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

Decrypt config from a (unpacked) XWorm sample
"""
import base64
from pprint import pp
import malcat
import hashlib
malcat.setup()  # Add Malcat's data directories to sys.path when called in headless mode
from transforms.block import AesDecrypt

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

def xworm_decrypt_config(a):
    fn_settings = a.symbols["Client.Settings.cctor"]
    config = {}
    config_encrypted = {}
    for bb in a.fns["Settings.cctor"]:
        prev_value = ""
        for instr in bb:
            if instr.mnemonic == "ldstr":
                prev_value = ""
                str_address = a.v2a(instr[0].value)
                if not str_address in a.strings:
                    continue
                prev_value = a.strings[str_address].text
            elif instr.mnemonic.startswith("ldc."):
                prev_value = instr[0].value
            elif instr.mnemonic == "stsfld" and prev_value:
                if instr[0].symbol == "Mutex":
                    config["Mutex"] = prev_value
                elif instr[0].symbol in ("Sleep", ):
                    config[instr[0].symbol] = int(prev_value)
                else:
                    prev_value = base64.b64decode(prev_value)
                    config_encrypted[instr[0].symbol] = prev_value
    if not "Mutex" in config:
        raise ValueError("Config key not found")
    md5 = hashlib.md5(config["Mutex"].encode("utf8")).digest()
    key = md5[:15] + md5 + b"\x00"
    for entry, encrypted in config_encrypted.items():
        decrypt = AesDecrypt().run(encrypted, mode="ecb", key=key, unpad=True)
        try:
            decrypt = decrypt.decode("ascii")
        except: pass
        config[entry] = decrypt
   
    return config

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

    configs = []
    if "analysis" in globals():
        # called from the gui, analysis object is already instanciated with the current file
        configs.append(xworm_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) XWorm 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(xworm_decrypt_config(a))

    for config in configs:
        for k, v in config.items():
            print(f"{k}: {v}")
