Last active
January 10, 2020 17:53
-
-
Save BinaryResearch/9bca1b5b0b4b03c8d0a3ec01696cf650 to your computer and use it in GitHub Desktop.
Assemble and emulate snippets of x86-64 assembly code using Keystone and Unicorn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
from keystone import * | |
from capstone import * | |
from unicorn import * | |
from unicorn.x86_const import * | |
# GCC - 9 divided by 5 | |
# from https://stackoverflow.com/questions/41183935/why-does-gcc-use-multiplication-by-a-strange-number-in-implementing-integer-divi | |
ASM = "mov rax, 9; \ | |
movabs rdx, -3689348814741910323; \ | |
mul rdx; \ | |
mov rax, rdx; \ | |
shr rax, 2;" | |
def assemble_snippet(): | |
try: | |
ks = Ks(KS_ARCH_X86, KS_MODE_64) # initialize assembler object | |
encoding, count = ks.asm(ASM) # save results of assembly | |
except KsError as e: | |
print("ERROR: %s" %e) | |
return bytes(encoding) # return assembled object code | |
# callback for tracing instructions | |
def hook_code(uc, address, size, user_data): | |
# print contents of registers of interest | |
print("RAX = 0x%x" % uc.reg_read(UC_X86_REG_RAX)) | |
print("RDX = 0x%x" % uc.reg_read(UC_X86_REG_RDX)) | |
print("=================================================") | |
# print disassembly of intruction stream | |
instruction = uc.mem_read(address, size) | |
md = user_data | |
for i in md.disasm(instruction, address): | |
print(">>> 0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str)) | |
# from https://github.com/unicorn-engine/unicorn/blob/8621bca53758532ad6a4ec5d17684fcdb9923cc6/bindings/python/sample_x86.py#L475 | |
def emulate(): | |
ADDRESS = 0x1000000 # memory address where emulation starts | |
CODE = assemble_snippet() # object code to emulate | |
mu = Uc(UC_ARCH_X86, UC_MODE_64) # Initialize emulator in X86-64bit mode | |
mu.mem_map(ADDRESS, 2 * 1024 * 1024) # map 2MB memory for this emulation | |
mu.mem_write(ADDRESS, CODE) # map machine code to be emulated to memory | |
mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000) # set up stack | |
md = Cs(CS_ARCH_X86, CS_MODE_64) # initialize disassembly engine | |
mu.hook_add(UC_HOOK_CODE, hook_code, md) # pass diassembly engine to hook | |
print("Initial state:") | |
try: | |
# emulate machine code in infinite time | |
mu.emu_start(ADDRESS, ADDRESS + len(CODE)) | |
except UcError as e: | |
print("ERROR: %s" % e) | |
# final state | |
print("RAX = 0x%x" % mu.reg_read(UC_X86_REG_RAX)) | |
print("RDX = 0x%x" % mu.reg_read(UC_X86_REG_RDX)) | |
print("\n>>>Emulation complete.") | |
if __name__ == "__main__": | |
emulate() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment