-
-
[原创]使用unicorn对ollvm字符串进行解密
-
2021-6-16 21:57 15050
-
题目出自3w班12月第一题。对抗字符串混淆
样本是一个比较初级的ollvm混淆,其字符串解密过程在init_array中,所以so文件正常加载到内存中后,其加密的字符串就已经解密后写回原始地址了。
所以我们可以通过unicorn加载so文件,手动将解密后的字符串写回so文件,经过简单的patch就可以静态分析了
本文参考了leadroyal与smartdone大佬的文章。
使用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
找到函数,设置起始与结束断点:
在需要执行的汇编指令起点,右键-uEmu-start
配置寄存器
ctrl+shift+alt+s单步运行
如果你需要多行执行,单击要执行到某一行,右键-uEmu-run
会自动向下执行到你单击的那一行
查看内存,比如我们要查看0x1F0AD处的内存,看一下解密后的字符串内容:右键-uEmu-show memory range
填写地址与长度,注意长度为10进制,选择add
[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课
最后于 2021-6-16 21:58
被新萌编辑
,原因: 修改样式
赞赏
谁下载
看原图