-
-
Save ilbaroni/9e6202036a365ea3279e6ccc2685829c to your computer and use it in GitHub Desktop.
Emulating x64 machine code using Unicorn (A CPU scriptable emulator)
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
#!python3 | |
# -*- coding: utf-8 -*- | |
# pip install unicorn | |
import unicorn | |
import unicorn.x86_const | |
import struct | |
def required_mapping_size(size): | |
page_size = 4096 | |
while page_size < size: | |
page_size *= 2 | |
return page_size | |
def code_hook(emulator, address, size, _user_data): | |
rsp = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RSP) | |
print(f'[!] 0x{address:016x} Executing an instruction at of size {size} (rsp = 0x{rsp:016x})') | |
def memory_hook(emulator, access_type, address, size, value, _user_data): | |
access_type = { | |
unicorn.UC_MEM_READ: 'READ', | |
unicorn.UC_MEM_WRITE: 'WRITE', | |
unicorn.UC_MEM_FETCH: 'FETCH', | |
unicorn.UC_MEM_READ_UNMAPPED: 'READ_UNMAPPED', | |
unicorn.UC_MEM_WRITE_UNMAPPED: 'WRITE_UNMAPPED', | |
unicorn.UC_MEM_FETCH_UNMAPPED: 'FETCH_UNMAPPED', | |
unicorn.UC_MEM_WRITE_PROT: 'WRITE_PROTECTED', | |
unicorn.UC_MEM_READ_PROT: 'READ_PROTECTED', | |
unicorn.UC_MEM_FETCH_PROT: 'FETCH_PROTECTED' | |
}[access_type] | |
rip = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RIP) | |
print(f'[!] 0x{rip:016x} Memory access at 0x{address:016x} of type {access_type}, size = {size}, value = {value}') | |
def interrupt_hook(emulator, interrupt_number, _user_data): | |
rip = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RIP) | |
print(f'[!] 0x{rip:016x} Interrupt requested {interrupt_number}') | |
def syscall_hook(emulator, _user_data): | |
rip = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RIP) | |
print(f'[!] 0x{rip:016x} Requesting a system call') | |
def main(): | |
# 0: 90 nop | |
# 1: 6a 01 push 0x1 | |
# 3: 48 c7 c0 02 00 00 00 mov rax,0x2 | |
# a: 50 push rax | |
# b: 6a 03 push 0x3 | |
# d: 58 pop rax | |
# e: 5b pop rbx | |
# f: 48 01 d8 add rax,rbx | |
# 12: 5b pop rbx | |
# 13: 48 01 d8 add rax,rbx | |
# 16: 0f 05 syscall | |
# 18: cd 7b int 0x7b | |
# 1a: 48 b9 00 00 00 00 00 movabs rcx,0x80000000000 | |
# 21: 08 00 00 | |
# 24: 48 c7 01 7b 00 00 00 mov QWORD PTR [rcx],0x7b | |
# 2b: 90 nop | |
code = b'\x90\x6a\x01\x48\xc7\xc0\x02\x00\x00\x00\x50\x6a\x03\x58\x5b\x48\x01\xd8\x5b\x48\x01\xd8\x0f\x05\xcd\x7b' \ | |
b'\x48\xb9\x00\x00\x00\x00\x00\x08\x00\x00\x48\xc7\x01\x7b\x00\x00\x00\x90' | |
code_size = len(code) | |
code_mapping_size = required_mapping_size(code_size) | |
code_address_start = 0x0000000010000000 | |
code_address_end = code_address_start + code_size | |
stack_size = 4096 | |
stack_mapping_size = required_mapping_size(stack_size) | |
stack_address_start = 0x0000000000000000 | |
stack_address_end = stack_address_start + stack_size | |
data_size = 4096 | |
data_mapping_size = required_mapping_size(data_size) | |
data_address_start = 0x0000080000000000 | |
emulator = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64) | |
emulator.mem_map(code_address_start, code_mapping_size) | |
emulator.mem_write(code_address_start, code) | |
# RSP points to the top of the stack, which grows downward (i.e. every time something is pushed into the stack the | |
# top of the stack is decremented). | |
emulator.mem_map(stack_address_start, stack_mapping_size) | |
emulator.reg_write(unicorn.x86_const.UC_X86_REG_RSP, stack_address_end - 1) | |
# Mapped memory to write to | |
emulator.mem_map(data_address_start, data_mapping_size) | |
emulator.hook_add(unicorn.UC_HOOK_CODE, code_hook) | |
emulator.hook_add(unicorn.UC_HOOK_MEM_VALID, memory_hook) | |
emulator.hook_add(unicorn.UC_HOOK_INTR, interrupt_hook) | |
emulator.hook_add(unicorn.UC_HOOK_INSN, syscall_hook, None, 1, 0, unicorn.x86_const.UC_X86_INS_SYSCALL) | |
emulator.emu_start(code_address_start, code_address_end) | |
rip = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RIP) | |
rsp = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RSP) | |
rax = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RAX) | |
memory = emulator.mem_read(data_address_start, 8) | |
memory = struct.unpack('<1Q', memory)[0] | |
print(f'[!] 0x{rip:016x} Emulation completed (rax = {rax}, rsp = 0x{rsp:016x}, memory = {memory})') | |
if __name__ == '__main__': | |
main() |
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
#!python3 | |
# -*- coding: utf-8 -*- | |
# pip install unicorn | |
import unicorn | |
import unicorn.x86_const | |
def code_tracer(emulator, address, _size, _user_data): | |
rax = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RAX) | |
rbx = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RBX) | |
rcx = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RCX) | |
print(f'[!] Executing instruction at 0x{address:016x} | rax = {rax} | rbx = {rbx} | rdx = {rcx}') | |
def main(): | |
emulation_address = 0x08000000 | |
# https://defuse.ca/online-x86-assembler.htm | |
# | |
# 0: 48 c7 c0 01 00 00 00 mov rax,0x1 | |
# 7: 48 c7 c3 02 00 00 00 mov rbx,0x2 | |
# e: 48 01 d8 add rax,rbx | |
# 11: 48 01 c8 add rax,rcx | |
# 14: 90 nop | |
code = b'\x48\xC7\xC0\x01\x00\x00\x00\x48\xC7\xC3\x02\x00\x00\x00\x48\x01\xD8\x48\x01\xC8\x90' | |
emulator = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64) | |
emulator.mem_map(emulation_address, 4 * 1024) | |
emulator.mem_write(emulation_address, code) | |
emulator.reg_write(unicorn.x86_const.UC_X86_REG_RCX, 3) | |
emulator.hook_add(unicorn.UC_HOOK_CODE, code_tracer, None, emulation_address, emulation_address + len(code)) | |
emulator.emu_start(emulation_address, emulation_address + len(code)) | |
rax = emulator.reg_read(unicorn.x86_const.UC_X86_REG_RAX) | |
print(f'[!] RAX value after emulation = {rax}') # expects 6 | |
# Output: | |
# [!] Executing instruction at 0x0000000008000000 | rax = 0 | rbx = 0 | rdx = 3 | |
# [!] Executing instruction at 0x0000000008000007 | rax = 1 | rbx = 0 | rdx = 3 | |
# [!] Executing instruction at 0x000000000800000e | rax = 1 | rbx = 2 | rdx = 3 | |
# [!] Executing instruction at 0x0000000008000011 | rax = 3 | rbx = 2 | rdx = 3 | |
# [!] Executing instruction at 0x0000000008000014 | rax = 6 | rbx = 2 | rdx = 3 | |
# [!] RAX value after emulation = 6 | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment