首页
论坛
课程
招聘
[原创]使用unicorn对ollvm字符串进行解密
2021-6-16 21:57 9389

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

2021-6-16 21:57
9389

题目出自3w班12月第一题。对抗字符串混淆

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

 

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

使用unicorn对字符串进行解密

问题一:

 

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

 

问题二:

 

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

 

脚本如下:

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
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!')

uemu对字符串进行解密" class="anchor" href="#使用uemu对字符串进行解密">使用uEmu对字符串进行解密

1
2
# 下载uEmu
$ git clonehttps://github.com/alexhude/uEmu

复制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


[培训] 优秀毕业生寄语:恭喜id咸鱼炒白菜拿到远超3W月薪的offer,《安卓高级研修班》火热招生!!!

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