"""
name: Remcos
category: config extractors
author: malcat

Decrypt CnC urls from a (unpacked) remcos sample
"""
import json
import struct
import malcat
malcat.setup()  # Add Malcat's data directories to sys.path when called in headless mode
from transforms.stream import RC4

############################ config extraction

CONFIG_KEYS = {
    0: "CnC",
    76: "audio_directory",
    34: "keylog_filename",
    98: "keylog_directory",
    52: "screenshots_directory",
    28: "mutex",
    20: "copy_filename",
}

def remcos_decrypt_config(a):
    res = {}
    for vf in a.vfiles:
        if vf.path.startswith("RCDATA/SETTING"):
            settings = vf.open()[:]
            break
    else:
        raise ValueError("Cannot locate SETTINGS resource")
    key_length = settings[0]
    key = settings[1:1 + key_length]
    decrypted = RC4().run(settings[1 + key_length:], key=key)

    for i, val in enumerate(decrypted.split(b"|")):
        keyname = CONFIG_KEYS.get(i, f"FIELD_{i}")
        if val in (b"\x1e\x1e\x1f", b"\x00"):
            continue
        if b"\x1e" in val:
            vals = val.split(b"\x1e")
        else:
            vals = [val]
        values = []
        for val in vals:
            if not val:
                continue
            value = None
            if value is None and not b"\x00" in val[:-1]:
                try:
                    if val.endswith(b"\x00"):
                        value = val[:-1].decode("ascii")
                    else:
                        value = val.decode("ascii")
                except: pass
            if value is None and len(val) > 4:
                try:
                    if val.endswith(b"\x00\x00"):
                        value = val[:-2].decode("utf-16-le")
                    else:
                        value = val.decode("utf-16-le")
                except: pass
            if value is None and len(val) == 4:
                value, = struct.unpack("<I", val)
            if value is None and len(val) == 2:
                value, = struct.unpack("<H", val)
            if value is None and len(val) == 1:
                value, = struct.unpack("<B", val)
            if value is None:
                value = repr(val)
            if value:
                values.append(value)
        if len(values) == 1:
            res[keyname] = values[0]
        else:
            res[keyname] = values
    return res

################################ MAIN
    
if __name__ == "__main__":

    configs = []
    if "analysis" in globals():
        # called from the gui, analysis object is already instanciated with the current file
        configs.append(remcos_decrypt_config(analysis))
    else:
        # called in headless mode, we need to analyse a file first
        import optparse
        usage = "usage: %prog <file1> [file2] ... [fileN]"
        parser = optparse.OptionParser(usage=usage, description="""Extract config for (unpacked) Remcos samples""")
        options, args = parser.parse_args()
        if len(args) < 1:
            parser.error("Please give path to a file")

        for fname in args:
            a = malcat.analyse(fname)
            configs.append(remcos_decrypt_config(a))

    for config in configs:
        print("\nREMCOS_CONFIG = ", end="")
        print(json.dumps(config, indent=4))
