首页
社区
课程
招聘
[原创]使用unicorn对ollvm字符串进行解密
发表于: 2021-6-16 21:57 15985

[原创]使用unicorn对ollvm字符串进行解密

2021-6-16 21:57
15985

样本是一个比较初级的ollvm混淆,其字符串解密过程在init_array中,所以so文件正常加载到内存中后,其加密的字符串就已经解密后写回原始地址了。
所以我们可以通过unicorn加载so文件,手动将解密后的字符串写回so文件,经过简单的patch就可以静态分析了

本文参考了leadroyalsmartdone大佬的文章。

问题一:

偷懒使用unicorn直接将整个so文件load进memory,读取data的数据全是00,修复后也是错误的,我按照leadroyal大佬的脚本修改后是正确的

问题二:

有时候使用unicorn map后,代码看着没有问题,但是跑起来就报错,可能你需要看看你分配的内存是不是不是1024的倍数

脚本如下:

复制uEmu.py到IDA_Pro_7.5\plugins\下,重启ida

找到函数,设置起始与结束断点:

image-20210105145619016

在需要执行的汇编指令起点,右键-uEmu-start

image-20210105145728799

配置寄存器

image-20210105150533403

ctrl+shift+alt+s单步运行

image-20210105150649023

如果你需要多行执行,单击要执行到某一行,右键-uEmu-run

image-20210105150820536

会自动向下执行到你单击的那一行

查看内存,比如我们要查看0x1F0AD处的内存,看一下解密后的字符串内容:右键-uEmu-show memory range

image-20210105150917607

填写地址与长度,注意长度为10进制,选择add

image-20210105151102965

image-20210105151159124

 
 
 
 
 
import binascii
import math
 
import ida_bytes
import idaapi
from capstone import *
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
from keystone import *
from unicorn import *
from unicorn.arm_const import *
 
 
class FILFINIT(object):
    def __init__(self, file, mode):
        self.file = file
        self.fd = self.file_init(mode)
        self.elf = ELFFile(self.fd)
        self.file_size = self.file_size()
        self.data_divs = self.get_data_div()
 
    def file_init(self, mode='rb+'):
        fd = open(self.file, mode=mode)
        return fd
 
    def file_close(self):
        self.fd.close()
 
    def file_seek_to(self, seekto):
        self.fd.seek(seekto, 0)
 
    def file_read(self, size=-1):
        binary = self.fd.read(size)
        return binary
 
    def file_write(self, data):
        self.fd.write(data)
 
    def file_size(self):
        import os
        return os.path.getsize(self.file)
 
    def get_data_div(self):
        data_divs = []
        if self.elf:
            symtab = self.elf.get_section_by_name('.dynsym')
            assert isinstance(symtab, SymbolTableSection)
 
            for sym in symtab.iter_symbols():  # sym.name:str
                if sym.name.startswith('.datadiv'):
                    data_divs.append(sym.entry.st_value)
        return data_divs
 
 
class UCINIT(object):
    def __init__(self, file, mode, offset=0x10000, stack_size=0x10000, data_size=0x10000):
        self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
        self.fileinfo = FILFINIT(file, mode)
        self.data_divs = self.uc_filter()
        self.func_start_and_end = self.get_func_start_and_end()
 
        self.uc = unicorn.Uc(UC_ARCH_ARM, UC_MODE_THUMB)
        self.image_base = 0x10000
        self.offset = offset
        self.stack_size = stack_size
        self.data_size = data_size
        self.image_size = (math.ceil(self.fileinfo.file_size / self.offset) + 1) * self.offset
        self.uc.mem_map(self.image_base, self.image_size)
        self.init_seg()
        # self.uc.mem_write(self.image_base, self.fileinfo.file_read()) # 全文件导入是错误的,不知道为什么
        self.stack_base = self.image_base + self.image_size + self.offset
        self.stack_top = self.stack_base + self.stack_size - 0x1000
        self.uc.mem_map(self.stack_base, self.stack_size)
        self.uc.reg_write(UC_ARM_REG_SP, self.stack_top)
        self.data_base = self.stack_base + self.stack_size + self.offset
        self.uc.mem_map(self.data_base, self.data_size)
 
    def init_seg(self):
        segs = [seg for seg in self.fileinfo.elf.iter_segments() if seg.header.p_type == 'PT_LOAD']
        for seg in segs:
            self.uc.mem_write(self.image_base + seg.header.p_vaddr, seg.data())
 
    def init_reg(self, regs):
        index = 0
        for reg in regs:
            reg_num = 66 + index
            self.uc.reg_write(reg_num, reg)
 
    def uc_run(self, target, end):
        targ = target + self.image_base
        util = self.image_base + end
        self.uc.emu_start(targ, util)
 
    def uc_stop(self):
        self.uc.emu_stop()
        self.fileinfo.file_close()
 
    def register_hook_code(self, htype, callback):
        self.uc.hook_add(htype, callback)
 
    def uc_filter(self, size=0x2):
        data_divs = []
        for data_div in self.fileinfo.data_divs:
            self.fileinfo.file_seek_to(data_div - 1)
            data = self.fileinfo.file_read(size)
            for inis in self.cs.disasm(data, 0):
                if inis.mnemonic == 'bx' and inis.op_str == 'lr':
                    continue
                else:
                    data_divs.append(data_div)
        self.fileinfo.file_seek_to(0)
        return data_divs
 
    def get_func_start_and_end(self):
        data_divs = []
        for data_div in self.data_divs:
            func = idaapi.get_func(data_div)
            func_start_and_end = (data_div, func.end_ea - 0x2)
            data_divs.append(func_start_and_end)
        return data_divs
 
    def get_elf_data(self):
        data_section_header = self.fileinfo.elf.get_section_by_name('.data').header
        print('[+] .data addr {}-{}'.format(hex(data_section_header.sh_addr), hex(data_section_header.sh_size)))
        new_data = self.uc.mem_read(self.image_base + data_section_header.sh_addr, data_section_header.sh_size)
        print('[+] .data binary {}'.format(binascii.hexlify(new_data)))
        return new_data
 
    def patch_so_data(self):
        data_section_header = self.fileinfo.elf.get_section_by_name('.data').header
        data_addr = data_section_header.sh_addr
 
        # self.fileinfo.file_seek_to(data_addr)
        # self.fileinfo.file_write(self.get_elf_data())
        new_data = self.get_elf_data()  # new_data: bytearray
        print('[+] patch so data addr {}\n {}'.format(hex(data_addr), bytes(new_data)))
        ida_bytes.patch_bytes(data_addr, bytes(new_data))
 
    def patch_data_div(self):
        ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
        for data_div in self.data_divs:
            binary = ks.asm('bx lr')
            print(bytes(binary[0]))
            # print(struct.pack('B', binary[0]))
            print('[+] patch so text .datadiv {}\n {}'.format(hex(data_div - 1), bytes(binary[0])))
            ida_bytes.patch_bytes(data_div - 1, bytes(binary[0]))
 
 
def hook_code(uc: unicorn.Uc, address, size, user_data):
    cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
    me = uc.mem_read(address, size)
    for code in cs.disasm(me, size):
        print("[+] Tracing instructions at 0x%x, instructions size = ix%x, inst: %s %s" % (
            address, size, code.mnemonic, code.op_str))
 
 
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
        me = uc.mem_read(address, size)
        print('[+] Memory is being READ at 0x%x, data size = %u, data value = 0x%s' % (
            address, size, binascii.hexlify(me)))
 
 
if __name__ == '__main__':
    print('[+] So file parse starting!')
    so = UCINIT('libcrack.so', 'rb')
    # so.get_elf_data()
    for start, end in so.func_start_and_end:
        print('[+] Data div addr {}-{}'.format(hex(start), hex(end)))
        so.init_reg((0, 0, 0))
        # so.register_hook_code(UC_HOOK_CODE, hook_code)
        # so.register_hook_code(UC_HOOK_MEM_READ, hook_mem_access)
        # so.register_hook_code(UC_HOOK_MEM_WRITE, hook_mem_access)
        so.uc_run(start, end)
        so.patch_data_div()
    # so.patch_so_data()
    # so.get_elf_data()
    so.uc_stop()
    print('[+] So file run finished!')
import binascii
import math
 
import ida_bytes
import idaapi
from capstone import *
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
from keystone import *
from unicorn import *
from unicorn.arm_const import *
 
 
class FILFINIT(object):
    def __init__(self, file, mode):
        self.file = file
        self.fd = self.file_init(mode)
        self.elf = ELFFile(self.fd)
        self.file_size = self.file_size()
        self.data_divs = self.get_data_div()
 
    def file_init(self, mode='rb+'):
        fd = open(self.file, mode=mode)
        return fd
 
    def file_close(self):
        self.fd.close()
 
    def file_seek_to(self, seekto):
        self.fd.seek(seekto, 0)
 
    def file_read(self, size=-1):
        binary = self.fd.read(size)
        return binary
 
    def file_write(self, data):
        self.fd.write(data)
 
    def file_size(self):
        import os
        return os.path.getsize(self.file)
 
    def get_data_div(self):
        data_divs = []
        if self.elf:
            symtab = self.elf.get_section_by_name('.dynsym')
            assert isinstance(symtab, SymbolTableSection)
 
            for sym in symtab.iter_symbols():  # sym.name:str
                if sym.name.startswith('.datadiv'):
                    data_divs.append(sym.entry.st_value)
        return data_divs
 
 
class UCINIT(object):
    def __init__(self, file, mode, offset=0x10000, stack_size=0x10000, data_size=0x10000):
        self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
        self.fileinfo = FILFINIT(file, mode)
        self.data_divs = self.uc_filter()
        self.func_start_and_end = self.get_func_start_and_end()
 
        self.uc = unicorn.Uc(UC_ARCH_ARM, UC_MODE_THUMB)
        self.image_base = 0x10000
        self.offset = offset
        self.stack_size = stack_size
        self.data_size = data_size
        self.image_size = (math.ceil(self.fileinfo.file_size / self.offset) + 1) * self.offset
        self.uc.mem_map(self.image_base, self.image_size)
        self.init_seg()
        # self.uc.mem_write(self.image_base, self.fileinfo.file_read()) # 全文件导入是错误的,不知道为什么
        self.stack_base = self.image_base + self.image_size + self.offset
        self.stack_top = self.stack_base + self.stack_size - 0x1000
        self.uc.mem_map(self.stack_base, self.stack_size)
        self.uc.reg_write(UC_ARM_REG_SP, self.stack_top)
        self.data_base = self.stack_base + self.stack_size + self.offset
        self.uc.mem_map(self.data_base, self.data_size)
 
    def init_seg(self):
        segs = [seg for seg in self.fileinfo.elf.iter_segments() if seg.header.p_type == 'PT_LOAD']
        for seg in segs:
            self.uc.mem_write(self.image_base + seg.header.p_vaddr, seg.data())
 
    def init_reg(self, regs):
        index = 0
        for reg in regs:
            reg_num = 66 + index

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

最后于 2021-6-16 21:58 被新萌编辑 ,原因: 修改样式
上传的附件:
收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//