"""
name: Speakeasy (shellcode)
category: emulation
author: malcat
icon: wxART_DISASM

Unpacks a raw shellcode file using Speakeasy emulator, starting at the first defined function. Make sure to force the right CPU architecture beforehand (only x86/x64 are supported).

Note: you need to install speakeasy and its dependancies for this script to work
On windows, make sure to check "Use system Python" in options dialog.

https://github.com/fireeye/speakeasy

"""

try:
    import speakeasy
except ImportError:
    gui.print("""
You need to install speakeasy and its dependancies for this script to work
On windows, make sure to check "Use system Python" in options dialog.

[url]https://github.com/fireeye/speakeasy[/url]
""", format=True)
    raise
import speakeasy.winenv.arch as e_arch
import malcat
from malcat import FileType


def code_hook(emu, addr, size, ctx):
    print("{:08x}".format(addr))
    return True

def api_hook(emu, api_name, func, params):
    print("[API] {}({})".format(api_name, ", ".join(map(hex, params))))
    return func(params)


unpacker = speakeasy.Speakeasy()
unpacker.config["timeout"] = 600
# Load the module
if analysis.architecture == malcat.Architecture.X64:
    arch = e_arch.ARCH_AMD64
else:
    arch = e_arch.ARCH_X86
address = unpacker.load_shellcode(analysis.file.path, arch, data=analysis.file.read(0, analysis.file.size))
ep = 0
start = 0
end = analysis.file.size
for fn in analysis.fns:
    ep = fn.start
    start = fn.end
    print("Emulating shellcode from address {:x}".format(fn.start))
    break
else:
    print("No function defined, assuming entry point is at address 0")

#unpacker.add_code_hook(code_hook)
unpacker.add_api_hook(api_hook, "*", "*")

# Emulate the module
unpacker.run_shellcode(address, offset=ep)
print("[*] Unpacking done")
#gui.write(unpacker.mem_read(address, analysis.file.size), "SC")
for dump in unpacker.get_memory_dumps():
    print(dump)
    if dump[0] is not None and not dump[0].startswith("emu.") and dump[5].startswith(b"MZ"):
        gui.open_after(dump[5], dump[0])
        break
else:
    gui.open_after(unpacker.mem_read(address, analysis.file.size), "SC")
    
