Skip to content

Instantly share code, notes, and snippets.

@Barakat
Last active May 4, 2022 10:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Barakat/ffdaaabe4ba3a5e23cc633450469a60f to your computer and use it in GitHub Desktop.
Save Barakat/ffdaaabe4ba3a5e23cc633450469a60f to your computer and use it in GitHub Desktop.
Emulating x64 machine code using Unicorn (A CPU scriptable emulator)
#!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()
#!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