首页
社区
课程
招聘
[原创]利用unicorn分析固件中的算法
发表于: 2021-4-22 13:19 27848

[原创]利用unicorn分析固件中的算法

2021-4-22 13:19
27848

近年来,越来越多安全研究员开始使用QEMU以及unicorn这类虚拟化技术对固件进行模拟执行,甚至是FUZZ测试。模拟执行在嵌入式固件分析中应用越来越广泛,为了让大家能了解这一技术的使用方法,本文从实战出发,利用unicorn框架分析某个设备的加密算法。

本文的目的想要分析并调用该固件的一个魔改的MD5。

我们简单地对比了该算法和标准md5的区别,发现大量算法常数被修改了。

标准算法:

修改后的:

标准算法:

修改后的:

标准的MD5一般有三个函数,分别如下所示:

为了模拟执行,我们需要在IDA找到这三个函数的地址,如下所示:

用法如下,这里就不多介绍了

该固件是MIPS大端架构系统,初始化一些加载地址,栈地址之类的全局变量

解析ELF文件,把固件的代码段读取到模拟器中:

分配栈空间,以及变量空间,用于存放md5_ctx以及输入的变量字符串

首先,我们为了让模拟器调用完每个函数之后能够停止运行,必须将返回地址设置为一个指定的地址,当callback检测到运行到该地址,立刻停止下来了:

分别调用3个函数:

通过代码可以知道,最终的md5值在MD5Context偏移为88的地方:

所以在调用完成之后直接把MD5_CTX偏移为88的数据读取出来即为MD5运算结果:

当输入为12345678得到下面的MD5值

所有代码如下:

本文通过unicorn将固件中魔改的md5算法成功进行模拟执行,并输出了正确的值,说明使用unicorn对固件中的算法进行分析是非常有效的。这将会对使用了混淆的算法特别有用,逆向研究人员只需要分析关键函数以及参数,让unicorn执行模拟即可,当然还有些不足,比如有些CPU指令支持不完全,运行效率等问题。

https://bbs.pediy.com/thread-253868.htm

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()
 
 
#init var
 
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
 
    #MD5Init
    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)
 
    #MD5Update
    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)
    #MD5Final
 
    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))
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()
 
 
#init var
 
def my_md5(key):
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 8
支持
分享
最新回复 (6)
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持下
2021-4-23 11:24
0
雪    币: 13
活跃值: (410)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
3

你这个硬件的是ARM指令集么?直接用python调用ARM固件的接口函数么?

最后于 2021-6-9 15:44 被daiweizhi编辑 ,原因:
2021-6-9 15:36
0
雪    币: 10828
活跃值: (14609)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
4
daiweizhi 你这个硬件的是ARM指令集么?直接用python调用ARM固件的接口函数么?
是mips的,通过模拟器模拟执行关键函数
2021-6-9 18:59
0
雪    币: 1028
活跃值: (216)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
5
感谢分享,学习中,小白看的有点头大,哈哈
2021-6-12 08:28
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
前辈, 能否留下elf, 想复现一下233333
2022-8-22 23:01
0
雪    币: 5
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
如果函数中还有其他嵌套函数,每一层都要模拟吗?
2024-4-26 09:55
0
游客
登录 | 注册 方可回帖
返回
//