"""
Simple script to emulate selected code using unicorn. Apis are not supported
"""

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

[url]https://github.com/unicorn-engine/unicorn[/url]
""", format=True)
    raise
from unicorn.x86_const import *
from malcat import FileType, Architecture

if len(analysis.sel) == 0:
	raise ValueError("no code is selected, please first select the code range to emulate")
code_start = analysis.map.to_virt(analysis.sel.start)
code_end = analysis.map.to_virt(analysis.sel.end)

############## setup

STACK = 0x1000000
LOCAL_SPACE = 0x100000
STACK_SPACE = 0x100000
if analysis.architecture == Architecture.X64:
	emu = Uc(UC_ARCH_X86, UC_MODE_64)
	emu.reg_write(UC_X86_REG_RSP, STACK + STACK_SPACE)
	emu.reg_write(UC_X86_REG_RBP, STACK + STACK_SPACE + LOCAL_SPACE)
	emu.reg_write(UC_X86_REG_RIP, code_start)
else:
	emu = Uc(UC_ARCH_X86, UC_MODE_32)
	emu.reg_write(UC_X86_REG_ESP, STACK + STACK_SPACE)
	emu.reg_write(UC_X86_REG_EBP, STACK + STACK_SPACE + LOCAL_SPACE)
	emu.reg_write(UC_X86_REG_EIP, code_start)

emu.mem_map(STACK, 0x300000, UC_PROT_ALL)

############## map program
emu.mem_map(analysis.map.base, len(analysis.map))
for region in analysis.map:
	if region.virt and region.phys:
		print("population region {}".format(region.name))
		data = analysis.file.read(region.phys, region.phys_size)
		emu.mem_write(region.virt, data)
	
############## hooks

def hook_block(uc, address, size, user_data):
    gui.print(">>> Tracing basic block at [va]0x{:08x}[/va]-[va]0x{:08x}[/va]".format(address, address+size), format=True)

emu.hook_add(UC_HOOK_BLOCK, hook_block)


############## hooks


gui.print("Emulating code in range [va]{:x}[/va]-[va]{:x}[/va]...".format(code_start, code_end), format=True)
try:
	emu.emu_start(code_start, code_end)

	print("Emulation finished! Opening stack")
	stack = emu.mem_read(STACK, LOCAL_SPACE + STACK_SPACE)
	gui.open_after(bytes(stack))

except unicorn.UcError as e:
	if analysis.architecture == Architecture.X64:
		rip = emu.reg_read(UC_X86_REG_RIP)
	else:
		rip = emu.reg_read(UC_X86_REG_EIP)
	print()
	ea = analysis.map.v2a(rip)
	if ea is not None:
		disasm = str(analysis.asm[ea])
	else:
		disasm = "??"
	gui.print("[error] /!\\ [/error] Crash at address [va]0x{:x}[/va] ({}) : {} !".format(rip, disasm.replace("[", "\\["), e), format=True)

if analysis.architecture == Architecture.X64:
	pass
else:
	for register in ["eax", "ebx", "ecx", "edx", "ebp", "esp", "esi", "edi", "eip"]:
		reg_value = emu.reg_read(getattr(unicorn.x86_const, "UC_X86_REG_{}".format(register.upper())))
		gui.print("{:s}: [va]{:08x}[/va]".format(register.upper(), reg_value), format=True)
