BMCTool

Parse RDP bitmap cache data into a single collage image file.

python · Common Scripts

Source

# This tool was developed by "https://github.com/ANSSI-FR/bmc-tools" - Thank you for your hardwork!
from struct import pack, unpack
from typing import AnyStr, Generic

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401


class BMCContainer(Generic[AnyStr]):
    BIN_FILE_HEADER = b"RDP8bmp\x00"
    BIN_CONTAINER = b".BIN"
    BMC_CONTAINER = b".BMC"
    TILE_HEADER_SIZE = {BMC_CONTAINER: 0x14, BIN_CONTAINER: 0xC}
    STRIPE_WIDTH = 64
    LOG_TYPES = ["[===]", "[+++]", "[---]", "[!!!]"]
    PALETTE = bytes(
        bytearray(
            (
                0,
                0,
                0,
                0,
                0,
                0,
                128,
                0,
                0,
                128,
                0,
                0,
                0,
                128,
                128,
                0,
                128,
                0,
                0,
                0,
                128,
                0,
                128,
                0,
                128,
                128,
                0,
                0,
                192,
                192,
                192,
                0,
                192,
                220,
                192,
                0,
                240,
                202,
                166,
                0,
                0,
                32,
                64,
                0,
                0,
                32,
                96,
                0,
                0,
                32,
                128,
                0,
                0,
                32,
                160,
                0,
                0,
                32,
                192,
                0,
                0,
                32,
                224,
                0,
                0,
                64,
                0,
                0,
                0,
                64,
                32,
                0,
                0,
                64,
                64,
                0,
                0,
                64,
                96,
                0,
                0,
                64,
                128,
                0,
                0,
                64,
                160,
                0,
                0,
                64,
                192,
                0,
                0,
                64,
                224,
                0,
                0,
                96,
                0,
                0,
                0,
                96,
                32,
                0,
                0,
                96,
                64,
                0,
                0,
                96,
                96,
                0,
                0,
                96,
                128,
                0,
                0,
                96,
                160,
                0,
                0,
                96,
                192,
                0,
                0,
                96,
                224,
                0,
                0,
                128,
                0,
                0,
                0,
                128,
                32,
                0,
                0,
                128,
                64,
                0,
                0,
                128,
                96,
                0,
                0,
                128,
                128,
                0,
                0,
                128,
                160,
                0,
                0,
                128,
                192,
                0,
                0,
                128,
                224,
                0,
                0,
                160,
                0,
                0,
                0,
                160,
                32,
                0,
                0,
                160,
                64,
                0,
                0,
                160,
                96,
                0,
                0,
                160,
                128,
                0,
                0,
                160,
                160,
                0,
                0,
                160,
                192,
                0,
                0,
                160,
                224,
                0,
                0,
                192,
                0,
                0,
                0,
                192,
                32,
                0,
                0,
                192,
                64,
                0,
                0,
                192,
                96,
                0,
                0,
                192,
                128,
                0,
                0,
                192,
                160,
                0,
                0,
                192,
                192,
                0,
                0,
                192,
                224,
                0,
                0,
                224,
                0,
                0,
                0,
                224,
                32,
                0,
                0,
                224,
                64,
                0,
                0,
                224,
                96,
                0,
                0,
                224,
                128,
                0,
                0,
                224,
                160,
                0,
                0,
                224,
                192,
                0,
                0,
                224,
                224,
                0,
                64,
                0,
                0,
                0,
                64,
                0,
                32,
                0,
                64,
                0,
                64,
                0,
                64,
                0,
                96,
                0,
                64,
                0,
                128,
                0,
                64,
                0,
                160,
                0,
                64,
                0,
                192,
                0,
                64,
                0,
                224,
                0,
                64,
                32,
                0,
                0,
                64,
                32,
                32,
                0,
                64,
                32,
                64,
                0,
                64,
                32,
                96,
                0,
                64,
                32,
                128,
                0,
                64,
                32,
                160,
                0,
                64,
                32,
                192,
                0,
                64,
                32,
                224,
                0,
                64,
                64,
                0,
                0,
                64,
                64,
                32,
                0,
                64,
                64,
                64,
                0,
                64,
                64,
                96,
                0,
                64,
                64,
                128,
                0,
                64,
                64,
                160,
                0,
                64,
                64,
                192,
                0,
                64,
                64,
                224,
                0,
                64,
                96,
                0,
                0,
                64,
                96,
                32,
                0,
                64,
                96,
                64,
                0,
                64,
                96,
                96,
                0,
                64,
                96,
                128,
                0,
                64,
                96,
                160,
                0,
                64,
                96,
                192,
                0,
                64,
                96,
                224,
                0,
                64,
                128,
                0,
                0,
                64,
                128,
                32,
                0,
                64,
                128,
                64,
                0,
                64,
                128,
                96,
                0,
                64,
                128,
                128,
                0,
                64,
                128,
                160,
                0,
                64,
                128,
                192,
                0,
                64,
                128,
                224,
                0,
                64,
                160,
                0,
                0,
                64,
                160,
                32,
                0,
                64,
                160,
                64,
                0,
                64,
                160,
                96,
                0,
                64,
                160,
                128,
                0,
                64,
                160,
                160,
                0,
                64,
                160,
                192,
                0,
                64,
                160,
                224,
                0,
                64,
                192,
                0,
                0,
                64,
                192,
                32,
                0,
                64,
                192,
                64,
                0,
                64,
                192,
                96,
                0,
                64,
                192,
                128,
                0,
                64,
                192,
                160,
                0,
                64,
                192,
                192,
                0,
                64,
                192,
                224,
                0,
                64,
                224,
                0,
                0,
                64,
                224,
                32,
                0,
                64,
                224,
                64,
                0,
                64,
                224,
                96,
                0,
                64,
                224,
                128,
                0,
                64,
                224,
                160,
                0,
                64,
                224,
                192,
                0,
                64,
                224,
                224,
                0,
                128,
                0,
                0,
                0,
                128,
                0,
                32,
                0,
                128,
                0,
                64,
                0,
                128,
                0,
                96,
                0,
                128,
                0,
                128,
                0,
                128,
                0,
                160,
                0,
                128,
                0,
                192,
                0,
                128,
                0,
                224,
                0,
                128,
                32,
                0,
                0,
                128,
                32,
                32,
                0,
                128,
                32,
                64,
                0,
                128,
                32,
                96,
                0,
                128,
                32,
                128,
                0,
                128,
                32,
                160,
                0,
                128,
                32,
                192,
                0,
                128,
                32,
                224,
                0,
                128,
                64,
                0,
                0,
                128,
                64,
                32,
                0,
                128,
                64,
                64,
                0,
                128,
                64,
                96,
                0,
                128,
                64,
                128,
                0,
                128,
                64,
                160,
                0,
                128,
                64,
                192,
                0,
                128,
                64,
                224,
                0,
                128,
                96,
                0,
                0,
                128,
                96,
                32,
                0,
                128,
                96,
                64,
                0,
                128,
                96,
                96,
                0,
                128,
                96,
                128,
                0,
                128,
                96,
                160,
                0,
                128,
                96,
                192,
                0,
                128,
                96,
                224,
                0,
                128,
                128,
                0,
                0,
                128,
                128,
                32,
                0,
                128,
                128,
                64,
                0,
                128,
                128,
                96,
                0,
                128,
                128,
                128,
                0,
                128,
                128,
                160,
                0,
                128,
                128,
                192,
                0,
                128,
                128,
                224,
                0,
                128,
                160,
                0,
                0,
                128,
                160,
                32,
                0,
                128,
                160,
                64,
                0,
                128,
                160,
                96,
                0,
                128,
                160,
                128,
                0,
                128,
                160,
                160,
                0,
                128,
                160,
                192,
                0,
                128,
                160,
                224,
                0,
                128,
                192,
                0,
                0,
                128,
                192,
                32,
                0,
                128,
                192,
                64,
                0,
                128,
                192,
                96,
                0,
                128,
                192,
                128,
                0,
                128,
                192,
                160,
                0,
                128,
                192,
                192,
                0,
                128,
                192,
                224,
                0,
                128,
                224,
                0,
                0,
                128,
                224,
                32,
                0,
                128,
                224,
                64,
                0,
                128,
                224,
                96,
                0,
                128,
                224,
                128,
                0,
                128,
                224,
                160,
                0,
                128,
                224,
                192,
                0,
                128,
                224,
                224,
                0,
                192,
                0,
                0,
                0,
                192,
                0,
                32,
                0,
                192,
                0,
                64,
                0,
                192,
                0,
                96,
                0,
                192,
                0,
                128,
                0,
                192,
                0,
                160,
                0,
                192,
                0,
                192,
                0,
                192,
                0,
                224,
                0,
                192,
                32,
                0,
                0,
                192,
                32,
                32,
                0,
                192,
                32,
                64,
                0,
                192,
                32,
                96,
                0,
                192,
                32,
                128,
                0,
                192,
                32,
                160,
                0,
                192,
                32,
                192,
                0,
                192,
                32,
                224,
                0,
                192,
                64,
                0,
                0,
                192,
                64,
                32,
                0,
                192,
                64,
                64,
                0,
                192,
                64,
                96,
                0,
                192,
                64,
                128,
                0,
                192,
                64,
                160,
                0,
                192,
                64,
                192,
                0,
                192,
                64,
                224,
                0,
                192,
                96,
                0,
                0,
                192,
                96,
                32,
                0,
                192,
                96,
                64,
                0,
                192,
                96,
                96,
                0,
                192,
                96,
                128,
                0,
                192,
                96,
                160,
                0,
                192,
                96,
                192,
                0,
                192,
                96,
                224,
                0,
                192,
                128,
                0,
                0,
                192,
                128,
                32,
                0,
                192,
                128,
                64,
                0,
                192,
                128,
                96,
                0,
                192,
                128,
                128,
                0,
                192,
                128,
                160,
                0,
                192,
                128,
                192,
                0,
                192,
                128,
                224,
                0,
                192,
                160,
                0,
                0,
                192,
                160,
                32,
                0,
                192,
                160,
                64,
                0,
                192,
                160,
                96,
                0,
                192,
                160,
                128,
                0,
                192,
                160,
                160,
                0,
                192,
                160,
                192,
                0,
                192,
                160,
                224,
                0,
                192,
                192,
                0,
                0,
                192,
                192,
                32,
                0,
                192,
                192,
                64,
                0,
                192,
                192,
                96,
                0,
                192,
                192,
                128,
                0,
                192,
                192,
                160,
                0,
                240,
                251,
                255,
                0,
                164,
                160,
                160,
                0,
                128,
                128,
                128,
                0,
                0,
                0,
                255,
                0,
                0,
                255,
                0,
                0,
                0,
                255,
                255,
                0,
                255,
                0,
                0,
                0,
                255,
                0,
                255,
                0,
                255,
                255,
                0,
                0,
                255,
                255,
                255,
                0,
            )
        )
    )
    COLOR_BLACK = b"\x00"
    COLOR_WHITE = b"\xff"

    def __init__(self, verbose=False, count=0, old=False, big=False, width=64):
        self.bdat = b""
        self.o_bmps = []
        self.bmps = []
        self.btype = b""
        self.cnt = count
        self.fname: str = ""
        self.oldsave = old
        self.pal = False
        self.verb = verbose
        self.big = big
        self.STRIPE_WIDTH = width
        if count > 0:
            self.b_log("debug", True, f"At most {count} tiles will be processed.")
        if old:
            self.b_log("debug", True, "Old data will also be saved in separate files.")

    def b_log(self, ltype, verbose, lmsg):
        log_type = {"info": demisto.info, "debug": demisto.debug, "error": demisto.error}

        if verbose or self.verb:
            log_type[ltype](lmsg)

        return True

    def b_import(self, fname):
        if len(self.bdat) > 0:
            self.b_log("error", False, "Data is already waiting to be processed; aborting.")
            return False

        with open(fname, "rb") as f:
            self.bdat = f.read()

        if len(self.bdat) == 0:
            self.b_log("error", False, "Unable to retrieve file contents; aborting.")
            return False

        self.fname = fname
        self.btype = self.BMC_CONTAINER

        if self.bdat[: len(self.BIN_FILE_HEADER)] == self.BIN_FILE_HEADER:
            unpacked_bytes = unpack("<L", self.bdat[len(self.BIN_FILE_HEADER) : len(self.BIN_FILE_HEADER) + 4])[0]
            self.b_log("info", True, f"Subsequent header version: {unpacked_bytes}.")
            self.bdat = self.bdat[len(self.BIN_FILE_HEADER) + 4 :]
            self.btype = self.BIN_CONTAINER

            self.b_log("debug", True, f"Successfully loaded '{self.fname}' as a {self.btype.decode()} container.")

            return True

        else:
            return False

    def b_process(self):
        """Extracts tiles from bmc container

        Returns:
            boolean: Result of tiles extraction
        """
        if len(self.bdat) == 0:
            self.b_log("error", False, "Nothing to process.")
            return False
        bl = 0
        while len(self.bdat) > 0:
            o_bmp = b""
            t_hdr = self.bdat[: self.TILE_HEADER_SIZE[self.btype]]
            key1, key2, t_width, t_height = unpack("<LLHH", t_hdr[:0xC])
            if self.btype == self.BIN_CONTAINER:
                bl = 4 * t_width * t_height
                t_bmp = self.b_parse_rgb32b(self.bdat[len(t_hdr) : len(t_hdr) + bl])
            elif self.btype == self.BMC_CONTAINER:
                t_bmp = ""
                t_len, t_params = unpack("<LL", t_hdr[-0x8:])
                if t_params & 0x08:  # This bit is always ONE when relevant data is smaller than expected data,
                    # thus it is most likely the "compression" bit flag.
                    if bl == 0:
                        if "22.bmc" in self.fname:
                            bl = 64 * 64 * 2
                        elif "24.bmc" in self.fname:
                            bl = 64 * 64 * 4
                        elif "2.bmc" in self.fname:
                            bl = 64 * 64
                        else:
                            for b in [1, 2, 4]:
                                if len(self.bdat) < len(t_hdr) + 64 * 64 * b + 8:
                                    break
                                if unpack("<H", self.bdat[len(t_hdr) + 64 * 64 * b + 8 :][:2])[0] == 64:
                                    bl = 64 * 64 * b
                                    break
                            if bl == 0:
                                self.b_log(
                                    "error", False, "Unable to determine data pattern size; exiting before throwing any error!"
                                )
                                return False
                    o_bmp = b""
                    t_bmp = self.b_uncompress(self.bdat[len(t_hdr) : len(t_hdr) + t_len], bl // (64 * 64))
                    if len(t_bmp) > 0:
                        if len(t_bmp) != t_width * t_height * bl // (64 * 64):
                            self.b_log(
                                "error",
                                False,
                                (
                                    f"Uncompressed tile data seems bogus (uncompressed {len(t_bmp)} bytes "
                                    f"while expecting {t_width * t_height * bl // (64 * 64)}). "
                                    f"Discarding tile."
                                ),
                            )
                            t_bmp = b""
                        else:
                            t_bmp = self.b_parse_rgb565(t_bmp)
                else:
                    cf = t_len // (t_width * t_height)
                    funcdict = {
                        "4": self.b_parse_rgb32b,
                        "3": self.b_parse_rgb24b,
                        "2": self.b_parse_rgb565,
                    }
                    try:
                        t_bmp = funcdict[str(cf)](self.bdat[len(t_hdr) : len(t_hdr) + cf * t_width * t_height])
                        if t_height != 64:
                            o_bmp = self.PALETTE + self.bdat[len(t_hdr) + cf * t_width * t_height : len(t_hdr) + cf * 64 * 64]
                    except KeyError:
                        if cf == 1:
                            self.pal = True
                            t_bmp = self.PALETTE + self.bdat[len(t_hdr) : len(t_hdr) + cf * t_width * t_height]
                        else:
                            self.b_log("error", False, f"Unexpected bpp {8 * cf} found during processing; aborting.")
                    bl = cf * 64 * 64
            if len(t_bmp) > 0:
                self.bmps.append(t_bmp)
                if len(o_bmp) > 0:
                    self.o_bmps.append(o_bmp)
                if len(self.bmps) % 100 == 0:
                    self.b_log("debug", True, f"{len(self.bmps)} tiles successfully extracted so far.")
            self.bdat = self.bdat[len(t_hdr) + bl :]
            if self.cnt != 0 and len(self.bmps) == self.cnt:
                break
        self.b_log("debug", False, f"{len(self.bmps)} tiles successfully extracted in the end.")
        return True

    def b_parse_rgb565(self, data):
        d_out = b""
        while len(data) > 0:
            pxl = unpack("<H", data[:2])[0]
            bl = ((pxl >> 8) & 0xF8) | ((pxl >> 13) & 0x07)
            gr = ((pxl >> 3) & 0xFC) | ((pxl >> 9) & 0x03)
            re = ((pxl << 3) & 0xF8) | ((pxl >> 2) & 0x07)
            d_out += bytearray((re, gr, bl, 255))
            data = data[2:]
        return bytes(d_out)

    def b_parse_rgb32b(self, data):
        d_out = b""
        d_buf = b""
        while len(data) > 0:
            if self.btype == self.BIN_CONTAINER:
                d_buf += data[:3] + b"\xff"
                if len(d_buf) == 256:
                    d_out = d_buf + d_out
                    d_buf = b""
            else:
                d_out += data[:3] + b"\xff"
            data = data[4:]
        return d_out

    def b_parse_rgb24b(self, data):
        d_out = b""
        d_buf = b""
        while len(data) > 0:
            if self.btype == self.BIN_CONTAINER:
                d_buf += data[:3] + b"\xff"
                if len(d_buf) == 256:
                    d_out = d_buf + d_out
                    d_buf = b""
            else:
                d_out += data[:3] + b"\xff"
            data = data[3:]
        return d_out

    def b_unrle(self, data):
        if len(data) == 0:
            return (-1, 1, 0)
        x = ord(data[0:1])
        if (x & 0xF0) == 0xF0:
            if x in [0xF5, 0xFB, 0xFC, 0xFF]:
                return (-1, 2, x)
            elif x in [0xFD, 0xFE]:
                return (x, 0, 1)
            elif x in [0xF9, 0xFA]:
                return (x, 8, 1)
            elif len(data) < 3:
                return (-1, 1, 0)
            else:
                c = unpack("<H", data[1:3])[0]
                return (x, c, 3)
        elif (x & 0xE0) == 0xA0:
            return (-1, 2, x)
        else:
            if (x & 0x80) == 0x00 or (x & 0xE0) == 0x80:
                c = x & 0x1F
                x = x & 0xE0
                o = 32
            else:
                c = x & 0x0F
                x = x & 0xF0
                o = 16
            if x in [0x40, 0xD0]:
                c *= 8
                o = 1
            if c == 0:
                if len(data) < 2:
                    return (-1, 1, 0)
                else:
                    c = ord(data[1:2]) + o
                o = 2
            else:
                o = 1
            return (x, c, o)
        return (-1, 3, 0)

    def b_uncompress(self, data, bbp):
        d_out = b""
        bro = -1
        fgc = self.COLOR_WHITE * bbp
        while len(data) > 0:
            cmd, rl, sz = self.b_unrle(data[:3])
            if cmd == -1:
                if rl == 1:
                    self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                elif rl == 2:
                    self.b_log("error", False, f"Unexpected decompression command encountered (0x{sz:02x}). Skipping tile.")
                else:
                    self.b_log("error", False, "Unhandled case in decompression routine. Skipping tile.")
                return b""
            data = data[sz:]
            if cmd in [0x00, 0xF0]:
                if len(d_out) < 64 * bbp:
                    if bro == 0:
                        d_out += fgc
                        rl -= 1
                    d_out += (self.COLOR_BLACK * bbp) * rl
                else:
                    if bro > 0:
                        c = d_out[-64 * bbp :][:bbp]
                        for i in range(bbp):
                            d_out += bytearray((ord(c[i : i + 1]) ^ ord(fgc[i : i + 1]),))
                        rl -= 1
                    while rl > 0:
                        d_out += d_out[-64 * bbp :][:bbp]
                        rl -= 1
                bro = len(d_out) // (64 * bbp)
            elif cmd in [0x20, 0xC0, 0xF1, 0xF6]:
                if cmd in [0xC0, 0xF6]:
                    if len(data) < bbp:
                        self.b_log("error", False, f"Unexpected end of compressed stream. Skipping tile. ({cmd}, {rl})")
                        return b""
                    fgc = data[:bbp]
                    data = data[bbp:]
                if len(d_out) < 64 * bbp:
                    d_out += fgc * rl
                else:
                    while rl > 0:
                        c = d_out[-64 * bbp :][:bbp]
                        for i in range(bbp):
                            d_out += bytearray((ord(c[i : i + 1]) ^ ord(fgc[i : i + 1]),))
                        rl -= 1
            elif cmd in [0xE0, 0xF8]:
                if len(data) < 2 * bbp:
                    self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                    return b""
                d_out += data[: 2 * bbp] * rl
                data = data[2 * bbp :]
            elif cmd in [0x60, 0xF3]:
                if len(data) < bbp:
                    self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                    return b""
                d_out += data[:bbp] * rl
                data = data[bbp:]
            elif cmd in [0x40, 0xD0, 0xF2, 0xF7, 0xF9, 0xFA]:
                if cmd in [0xD0, 0xF7]:
                    if len(data) < bbp:
                        self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                        return b""
                    fgc = data[:bbp]
                    data = data[bbp:]
                if cmd == 0xF9:
                    msk = b"\x03"
                    ml = 1
                elif cmd == 0xFA:
                    msk = b"\x05"
                    ml = 1
                else:
                    ml = (rl // 8) + 1 if rl % 8 != 0 else rl // 8
                    if len(data) < ml:
                        self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                        return b""
                    msk = data[:ml]
                    data = data[ml:]
                k = 0
                while rl > 0:
                    if (k % 8) == 0:
                        m = ord(msk[k // 8 :][:1])
                    else:
                        m = 0
                        demisto.debug(f"{k % 8} != 0 -> {m=}")
                    b = m & (0x1 << (k % 8))
                    if len(d_out) < 64 * bbp:
                        if b == 0:
                            d_out += self.COLOR_BLACK * bbp
                        else:
                            d_out += fgc
                    else:
                        c = d_out[-64 * bbp :][:bbp]
                        if b == 0:
                            d_out += c
                        else:
                            for i in range(bbp):
                                d_out += bytearray((ord(c[i : i + 1]) ^ ord(fgc[i : i + 1]),))
                    k += 1
                    rl -= 1
            elif cmd in [0x80, 0xF4]:
                if len(data) < bbp * rl:
                    self.b_log("error", False, "Unexpected end of compressed stream. Skipping tile.")
                    return b""
                d_out += data[: rl * bbp]
                data = data[rl * bbp :]
            elif cmd == 0xFD:
                d_out += self.COLOR_WHITE * bbp
            elif cmd == 0xFE:
                d_out += self.COLOR_BLACK * bbp
            else:
                self.b_log("error", False, f"Unhandled decompression command (0x{cmd:02x}). Skipping tile.")
                return b""
            if cmd not in [0x00, 0xF0]:
                bro = -1
        return d_out

    def b_export(self, name):
        self.fname = name

        # Removed tile creation from these lines

        self.b_log("info", False, f"Successfully exported {len(self.bmps)} files.")
        if self.big:
            pad: bytes = b"\xff"
            if not self.pal:
                pad *= 4
            for i in range(len(self.bmps)):
                if self.pal:
                    self.bmps[i] = self.bmps[i][len(self.PALETTE) :]
                while len(self.bmps[i]) != 64 * 64 * len(pad):
                    self.bmps[i] += pad * 64
            w: int = 64 * len(self.bmps)
            h = 64
            if len(self.bmps) // self.STRIPE_WIDTH > 0:
                m = len(self.bmps) % self.STRIPE_WIDTH
                if m != 0:
                    for _ in range(self.STRIPE_WIDTH - m):
                        self.bmps.append(pad * 64 * 64)
                w = self.STRIPE_WIDTH * 64
                h *= len(self.bmps) // self.STRIPE_WIDTH
            c_bmp = b"" if not self.pal else self.PALETTE
            if self.btype == self.BIN_CONTAINER:

                def collage_builder(x, a=self, PAD=len(pad), WIDTH=range(w // 64)):
                    return b"".join(
                        [
                            b"".join([a.bmps[a.STRIPE_WIDTH * (x + 1) - 1 - k][64 * PAD * j : 64 * PAD * (j + 1)] for k in WIDTH])
                            for j in range(64)
                        ]
                    )
            else:

                def collage_builder(x, a=self, PAD=len(pad), WIDTH=range(w // 64)):
                    return b"".join(
                        [
                            b"".join([a.bmps[a.STRIPE_WIDTH * x + k][64 * PAD * j : 64 * PAD * (j + 1)] for k in WIDTH])
                            for j in range(64)
                        ]
                    )

            c_bmp += b"".join(map(collage_builder, range(h // 64)))
            self.b_write(f"{self.fname[:-4]}_collage.bmp", self.b_export_bmp(w, h, c_bmp))
            self.b_log("info", False, "Successfully exported collage file.")

    def b_export_bmp(self, width, height, data):
        if not self.pal:
            return (
                b"BM"
                + pack("<L", len(data) + 122)
                + b"\x00\x00\x00\x00\x7a\x00\x00\x00\x6c\x00\x00\x00"
                + pack("<L", width)
                + pack("<L", height)
                + b"\x01\x00\x20\x00\x03\x00\x00\x00"
                + pack("<L", len(data))
                + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff niW"  # noqa: E501
                + (  # noqa: E501
                    b"\x00" * 36
                )
                + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
                + data
            )  # noqa: E126
        else:
            return (
                b"BM"
                + pack("<L", len(data) + 0x36)
                + b"\x00\x00\x00\x00\x36\x04\x00\x00\x28\x00\x00\x00"
                + pack("<L", width)
                + pack("<L", height)
                + b"\x01\x00\x08\x00\x00\x00\x00\x00"
                + pack("<L", len(data) - 0x400)
                + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
                + data
            )  # noqa: E501

    def b_write(self, fname, data):
        collage = fileResult(fname, data)
        return_results(collage)

    def b_flush(self):
        self.bdat = b""
        self.bmps = []
        self.o_bmps = []


def main():
    args = demisto.args()
    verbose = args.get("verbose", False)
    width = int(args.get("width", 64))
    source = args.get("EntryID", "")
    try:
        bmcc = BMCContainer(verbose=verbose, count=-1, old=False, big=True, width=width)

        if not source:
            # If the user didn't specify a path to a specific file(s) the script will grab .bin files from context

            sources = []

            files = argToList(demisto.context().get("File"))

            for bin_file in files:
                file = demisto.getFilePath(bin_file.get("EntryID"))
                if file.get("name").endswith(("bin", "bmc")):
                    sources.append(file)
        else:
            sources = [demisto.getFilePath(source)]

        for source in sources:
            if bmcc.b_import(source.get("path")):
                bmcc.b_process()
                bmcc.b_export(source.get("name"))
                bmcc.b_flush()

        del bmcc

        results = CommandResults(outputs_prefix="Collages", outputs=sources)

        return_results(results)
    except Exception as e:
        return_error(f"Failed to parse BMC cache file. Problem: {e!s}")


if __name__ in ("__builtin__", "builtins", "__main__"):
    main()

README

Parse RDP bitmap cache data into a single collage image file.

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 6.8.0

Inputs


Argument Name Description
EntryID The RDP cache file’s entryID to process.
verbose Should the command produce verbose output.
width Image width.

Outputs


Path Description Type
Collages Collage image of the tiles from the RDP session cache. Unknown