近年来,越来越多安全研究员开始使用QEMU以及unicorn这类虚拟化技术对固件进行模拟执行,甚至是FUZZ测试。模拟执行在嵌入式固件分析中应用越来越广泛,为了让大家能了解这一技术的使用方法,本文从实战出发,利用unicorn框架分析某个设备的加密算法。
本文通过unicorn将固件中魔改的md5算法成功进行模拟执行,并输出了正确的值,说明使用unicorn对固件中的算法进行分析是非常有效的。这将会对使用了混淆的算法特别有用,逆向研究人员只需要分析关键函数以及参数,让unicorn执行模拟即可,当然还有些不足,比如有些CPU指令支持不完全,运行效率等问题。
from
unicorn
import
*
from
capstone
import
*
from
unicorn.mips_const
import
*
from
elftools.elf.elffile
import
ELFFile
from
elftools.elf.segments
import
Segment
import
ctypes
import
binascii
import
hexdump
filepath
=
'fw'
load_base
=
0
stack_base
=
0
stack_size
=
0x20000
var_base
=
load_base
+
stack_size
var_size
=
0x10000
stop_stub_addr
=
0x30000
stop_stub_size
=
0x10000
emu
=
Uc(UC_ARCH_MIPS,UC_MODE_MIPS32
+
UC_MODE_BIG_ENDIAN)
def
disasm(bytecode,addr):
md
=
Cs(CS_ARCH_MIPS,CS_MODE_MIPS32
+
CS_MODE_BIG_ENDIAN)
for
asm
in
md.disasm(bytecode,addr):
return
'%s\t%s'
%
(asm.mnemonic,asm.op_str)
def
align(addr, size, growl):
UC_MEM_ALIGN
=
0x1000
to
=
ctypes.c_uint64(UC_MEM_ALIGN).value
mask
=
ctypes.c_uint64(
0xFFFFFFFFFFFFFFFF
).value ^ ctypes.c_uint64(to
-
1
).value
right
=
addr
+
size
right
=
(right
+
to
-
1
) & mask
addr &
=
mask
size
=
right
-
addr
if
growl:
size
=
(size
+
to
-
1
) & mask
return
addr, size
def
hook_code(uc, address, size, user_data):
bytecode
=
emu.mem_read(address,size)
print
(
" 0x%x :%s"
%
(address,disasm(bytecode,address)))
if
address
=
=
stop_stub_addr:
emu.emu_stop()
def
my_md5(key):
with
open
(filepath,
'rb'
) as elffile:
elf
=
ELFFile(elffile)
load_segments
=
[x
for
x
in
elf.iter_segments()
if
x.header.p_type
=
=
'PT_LOAD'
]
for
segment
in
load_segments:
prot
=
UC_PROT_ALL
print
(
'mem_map: addr=0x%x size=0x%x'
%
(segment.header.p_vaddr,segment.header.p_memsz))
addr,size
=
align(load_base
+
segment.header.p_vaddr,segment.header.p_memsz,
True
)
emu.mem_map(addr, size, prot)
emu.mem_write(addr, segment.data())
emu.mem_map(stack_base, stack_size, UC_PROT_ALL)
emu.mem_map(var_base, var_size, UC_PROT_ALL)
md5_ctx
=
var_base
psw
=
var_base
+
0x5000
emu.mem_write(psw,key)
emu.mem_map(stop_stub_addr, stop_stub_size, UC_PROT_ALL)
emu.reg_write(UC_MIPS_REG_A0, md5_ctx)
emu.reg_write(UC_MIPS_REG_RA,stop_stub_addr)
emu.reg_write(UC_MIPS_REG_SP,stack_base
+
stack_size)
my_MD5Init_addr
=
0x0041FAA8
my_MD5Update_addr
=
0x0041FAE4
my_MD5Final_addr
=
0x0041FC18
code
=
emu.mem_read(my_MD5Init_addr,
8
)
emu.hook_add(UC_HOOK_CODE, hook_code)
emu.emu_start(my_MD5Init_addr, my_MD5Init_addr
+
0x1000
)
emu.reg_write(UC_MIPS_REG_A0, md5_ctx)
emu.reg_write(UC_MIPS_REG_A1, psw)
emu.reg_write(UC_MIPS_REG_A2,
len
(key))
emu.reg_write(UC_MIPS_REG_SP,stack_base
+
stack_size)
emu.reg_write(UC_MIPS_REG_RA,stop_stub_addr)
emu.emu_start(my_MD5Update_addr, my_MD5Update_addr
+
0x1000
)
emu.reg_write(UC_MIPS_REG_A0, md5_ctx)
emu.reg_write(UC_MIPS_REG_SP,stack_base
+
stack_size)
emu.reg_write(UC_MIPS_REG_RA,stop_stub_addr)
emu.emu_start(my_MD5Final_addr, my_MD5Final_addr
+
0x1000
)
return
emu.mem_read(md5_ctx
+
88
,
16
)
if
__name__
=
=
"__main__"
:
key
=
b
'12345678'
hexdump.hexdump(my_md5(key))