1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
#!/usr/bin/env python
# Sample code for X86 of Unicorn.
# Nguyen Anh Quynh <aquynh@gmail.com>
# KaiJern Lau <kj@theshepherdlab.io>
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()
# 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)
|