使用unicorn模拟执行去除混淆

0. 前言

在分析某app的so时遇到了间接跳转类型的混淆,不去掉的话无法使用ida f5来静态分析,f5之后就长下面这样:

本文记录一下使用python+unicorn模拟执行来去掉混淆的过程。

1. 分析混淆的模式

混淆的汇编代码如下:

可以看到,这个代码块进行了一通运算,然后通过 br x8,跳转到寄存器x8中保存的地址,仔细分析这个x8的来源,可以观察到如下的固定模式:

|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 | CMP X19, ``#0 MOV W9, ``#0x40 ; '@' ; X9 = 0x40 MOV W10, ``#0x38 ; '8' ; X10 = 0x38 ADRP X25, ``#off_212C70@PAGE CSEL X9, X10, X9, EQ ; ``if X19 ``=``= 0 then X9 ``= X10 ADD X25, X25, ``#off_212C70@PAGEOFF ; X25 = 0x212C70 LDR X8, [X25,X9] ; X8 ``= qword[X25 ``+ X9] MOV W9, ``#0xFE53 ; X9 = 0xFE53 MOV W10, ``#0x82B4 ; X10 = 0x82B4 CSEL X9, X10, X9, EQ ; ``if X19 ``=``= 0 then X9 ``= X10 SUB X8, X8, X9 ; X8 ``= X8 ``- X9 BR X8 ; 跳转到X8 |

先看 LDR X8, [X25,X9],X25寄存器是一张偏移表的基址, 这条指令从偏移表+X9出取出8字节数据放到了X8中,而X9的值来源于csel指令是0x38或者是0x40,由cmp的结果决定,如果X19等于0,则X9此时等于0x38,负责等于0x40。

再看 SUB X8, X8, X9,从偏移表取出一个8字节数据到X8之后,用X8减X9,结果放到X8,X9的值也是来源于csel指令,0x82B4或者是0xFE53

最后,通过br X8跳转到目标地址

也就是说,根据X9值的不同,最终跳转的地址会有两个,把正常的分支指令混淆成了上面这种模式,手动还原混淆可以把br X8 patch成:

|-----|-----------------------|
| 1 2 | beq addr1 b addr2 |

2. 去混淆的方式

本来想的是直接用python来匹配这种模式,然后手动计算出两个分支的地址,最后patch,但是后面发现,一个被混淆的函数中只有第一个混淆块会给X25赋值偏移表的地址,其他的块就直接用X25的值了,不会再次赋值,比如下面这个:

这样的话就不能把这些被混淆的块单独的拿出来看,因为缺少计算分支地址的必要条件,必须从函数的第一个被混淆的块出发,获取到偏移表的地址才行。如果还想通过手动的计算出目的地址,那就需要手动去确定函数的边界,这样就太麻烦了。

所以最后还是选择使通过模拟执行的方式,从函数头开始执行,跑通每一个块,在执行到混淆块的时候,计算出分支地址,最后进行patch

这里模拟执行的框架选择unicorn,之前学习过无名侠大佬用unicorn去ollvm混淆的文章,这里借鉴一下思路

3. 写脚本

3.1 加载so

由于代码中有需要访问偏移表,这些偏移表是在so的第二个segment,这个segment的内存便宜和文件便宜不一样,跟windows加载pe一样存在一个拉伸的效果,所以为了模拟执行代码时可以正常访问到偏移表的数据,我们手动将so拉伸成内存视图:

|-------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def load_elf(filename): ``global img_size ``global out_data ``segs ``= [] ``with ``open``(filename, ``'rb'``) as f: ``out_data ``= f.read() ``for seg ``in ELFFile(f).iter_segments(``'PT_LOAD'``): ``print``(``'file_off:%s, va: %s, size: %s' %``(``hex``(seg[``'p_offset'``]), ``hex``(seg[``'p_vaddr'``]), ``hex``(seg[``'p_filesz'``]))) ``segs.append((seg[``'p_offset'``],seg[``'p_vaddr'``], seg[``'p_filesz'``], seg.data())) ``img_size ``= segs[``-``1``][``1``] ``+ segs[``-``1``][``2``] ``byte_arr ``= bytearray([``0``] ``* img_size) ``for seg ``in segs: ``vaddr ``= seg[``1``] ``size ``= seg[``2``] ``data ``= seg[``3``] ``byte_arr[vaddr: vaddr ``+ size] ``= bytearray(data) ``return byte_arr |

3.2 初始化unicorn

|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def init_unicorn(file_name): ``global bin_data ``global uc ``#装载一下so到内存 ``bin_data ``= bytes(load_elf(file_name)) ``uc ``= Uc(UC_ARCH_ARM64, UC_MODE_ARM) ``uc.mem_map(``0x80000000``, ``8 * 0x1000 * 0x1000``) ``uc.mem_map(``0``, ``8 * 0x1000 * 0x1000``) ``# 写入so数据 ``uc.mem_write(``0``, bin_data) ``#设置sp寄存器 ``uc.reg_write(UC_ARM64_REG_SP, ``0x80000000 + 0x1000 * 0x1000 * 6``) ``#设置指令执行hook,执行每条指令都会走hook_code ``uc.hook_add(UC_HOOK_CODE, hook_code) ``#设置非法内存访问hook ``uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_access) ``barr ``= uc.mem_read(``0x144320``, ``8``) ``print``(barr) |

3.3 如何跑通一个函数的所有路径

这里借鉴无名侠大佬的思路,先把入口节点放到队列中,然后不停从队列中取节点,以这个节点为起点模拟执行,直到下一个br reg,或者是ret。

一个节点包括地址和上下文环境(寄存器),在模拟执行之前,需要把寄存器的值设置好,同时在找到分支之后,也需要保存现场的上下文环境。

在找到下一个br reg之后,计算出分支地址,将分支地址放到队列中,继续循环即可。

|-------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | def deobf(): ``# 初始化unicorn ``filename ``= 'libxxxx.so' ``patched_filename ``= 'out.so' ``start_addr ``= 0x0103168 ``init_unicorn(filename) ``q ``= queue.Queue() ``q.put((start_addr, ``None``)) ``# 入口函数是第一个节点,放到队列中去,队列中是(地址,上下文) ``traced ``= {} ``# 跑过的节点 ``while not q.empty(): ``#一直循环,直到队列为空 ``addr, context ``= q.get() ``traced[addr] ``= 1 # 跑过了 ``s ``= run(addr, context) ``#开始模拟执行,找br reg ``if s ``is None``: ``continue ``if len``(s) ``=``= 2``: ``#单分支 ``if s[``0``] ``not in traced: ``q.put(s) ``#将分支节点放到队列中 ``else``: ``#双分支 ``if s[``0``] ``not in traced: ``q.put((s[``0``], s[``2``]))``#将分支节点放到队列中 ``if s[``1``] ``not in traced: ``q.put((s[``1``], s[``2``]))``#将分支节点放到队列中 def run(addr, context): ``global uc ``global is_success ``global block_flow ``#开始模拟执行,函数返回说明在hook_code中执行了emu_stop ``set_context(uc, context)``#设置寄存器环境 ``uc.emu_start(addr, ``0x10000``) ``if is_success ``=``= True``: ``is_success ``= False ``return block_flow[addr] ``#返回分支信息和context |

3.4 hook_code

hook_code是在unicorn中注册的指令执行回调,当执行uc.emu_start(addr, 0x10000)之后,就会开始模拟执行指令,同时调用hook_code,在hook_code中有很多重要的逻辑。

保存指令栈

进入hook_code之后,需要保存执行的汇编指令以及上下文环境,供后续判断是否到达混淆块使用:

|----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def hook_code(uc, address, size, user_data): ``global ins_stack ``global is_success ``if is_success ``=``= True``: ``uc.emu_stop() ``return ``ins_help ``= InsHelp() ``code ``= uc.mem_read(address, size) ``ins ``= list``(ins_help.disasm(code, address, ``False``))[``0``] ``print``(``"[+] tracing instruction\t0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) ``#记录指令和上下文环境 ``ins_stack.append((address, get_context(uc))) |

遇到ret或者bl .__stack_chk_fail需要停止

这两个是函数的边界,执行到这里就需要停下,我没有找到如何优雅的判断执行到了bl .__stack_chk_fail,所以就判断bl后面的地址了。

|-------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #遇到ret直接挺停止 if ins.mnemonic.lower() ``=``= 'ret'``: ``#uc.reg_write(UC_ARM64_REG_PC, 0) ``print``(``"[+] encountered ret, stop"``) ``ins_stack.clear() ``uc.emu_stop() ``return #遇到bl .__stack_chk_fail停止 if ins.mnemonic.lower() ``=``= 'bl' and ins.operands[``0``].imm ``=``= 0x237C0``: ``#uc.reg_write(UC_ARM64_REG_PC, 0) ``print``(``"[+] encountered bl .__stack_chk_fail, stop"``) ``ins_stack.clear() ``uc.emu_stop() ``return |

跳过函数调用、非栈或者so本身的内存访问、svc

需要跳过这些指令,并不影响寻路

|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | ``#跳过bl、非栈、so本身内存访问、svc ``if ins.mnemonic.lower().startswith(``'bl'``) ``or is_ref_ilegel_emm(uc, ins) ``or ins.mnemonic.lower().startswith(``'svc'``): ``print``(``"[+] pass instruction 0x%x\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) ``uc.reg_write(UC_ARM64_REG_PC, address ``+ size) ``return def is_ref_ilegel_emm(mu, ins): ``if ins.op_str.find(``'['``) !``= -``1``: ``if ins.op_str.find(``'[sp'``) ``=``= -``1``: ``# 不是通过sp访问内存 ``for op ``in ins.operands: ``if op.``type =``= ARM64_OP_MEM: ``addr ``= 0 ``if op.value.mem.base !``= 0``: ``addr ``+``= mu.reg_read(reg_ctou(ins.reg_name(op.value.mem.base))) ``if op.value.mem.index !``= 0``: ``addr ``+``= mu.reg_read(reg_ctou(ins.reg_name(op.value.mem.index))) ``if op.value.mem.disp !``= 0``: ``addr ``+``= op.value.mem.disp ``if 0x0 <``= addr <``= img_size: ``# 访问so中的数据,允许 ``return False ``elif 0x80000000 <``= addr < ``0x80000000 + 0x1000 * 0x1000 * 8``: ``#访问栈中的数据,允许 ``return False ``else``: ``return True ``else``:``# 是通过sp的内存访问,允许 ``return False ``else``: ``return False |

特征是否到达混淆块、计算分支地址

先判断是否是br指令,如果是,调用get_double_branch尝试去进一步匹配特征

|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | if ins.mnemonic ``=``= "br"``: ``#判断是否到达间接跳转 ``is_success ``= True ``block_base ``= ins_stack[``0``][``0``] ``jmp_addr ``= ins_stack[``-``1``][``0``] ``ret ``= get_double_branch(uc, ins_stack) ``if ret !``= None``: ``print``(``'find double branch: %x => %x, %x' % (block_base, ret[``0``], ret[``1``])) ``block_flow[ins_stack[``0``][``0``]] ``= ret ``patch_double_branch(uc, jmp_addr, ret) ``else``: ``ret ``= get_single_branch(uc, ins_stack) ``if ret ``=``= None``: ``print``(``"[+] find dest failed 0x%x\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) ``is_success ``= False ``else``: ``print``(``'find single branch: %x => %x' % (block_base, ret[``0``])) ``block_flow[block_base] ``= ret ``patch_single_branch(jmp_addr, ret[``0``]) ``ins_stack.clear() ``uc.emu_stop() ``return |

进入get_double_branch,遍历指令栈,判断是否存在特定的指令,如果有则获取指定寄存器的值,最后计算出两个分支地址,这几个指令存在先后顺序,所以需要几个标志变量来控制。

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | def get_double_branch(uc, ins_stack): ``#变量声明略 ``ins_help ``= InsHelp() ``cond ``= '' ``for tup ``in ins_stack[::``-``1``]: ``addr ``= tup[``0``] ``context ``= tup[``1``] ``ins ``= list``(ins_help.disasm(bin_data[addr: addr``+``5``], addr, ``False``))[``0``] ``# BR X8 ``if ins.mnemonic.lower() ``=``= 'br' and flag_br ``=``= False``: ``flag_br ``= True ``br_reg ``= ins.operands[``0``].reg ``# SUB X8, X8, X9 ``if flag_br ``=``= True and (ins.mnemonic.lower() ``=``= 'add' or ins.mnemonic.lower() ``=``= 'sub'``) \ ``and ins.operands[``0``].reg ``=``= br_reg ``and flag_sub_add ``=``= False``: ``if ins.operands[``1``].``type =``= 1 and ins.operands[``2``].``type =``= 1``: ``op_reg1 ``= ins.operands[``1``].reg ``op_reg2 ``= ins.operands[``2``].reg ``flag_sub_add ``= True ``# CSEL X9, X10, X9, EQ ``if flag_sub_add ``=``= True and ins.mnemonic.lower() ``=``= 'csel' and ins.operands[``0``].reg ``=``= op_reg2 \ ``and flag_csel1 ``=``= False``: ``cond ``= ins.op_str.split(``', '``)[``-``1``] ``regname1 ``= ins.reg_name(ins.operands[``1``].reg) ``regname2 ``= ins.reg_name(ins.operands[``2``].reg) ``# index1 = reg_ctou(regname1) - arm64_const.UC_ARM64_REG_X0 ``# index2 = reg_ctou(regname2) - arm64_const.UC_ARM64_REG_X0 ``reg2_value1 ``= 0 if regname1.lower() ``=``= 'xzr' else context[reg_ctou(regname1) ``- arm64_const.UC_ARM64_REG_X0] ``reg2_value2 ``= 0 if regname2.lower() ``=``= 'xzr' else context[reg_ctou(regname2) ``- arm64_const.UC_ARM64_REG_X0] ``flag_csel1 ``= True ``# LDR X8, [X25,X9] ``if flag_sub_add ``=``= True and ins.mnemonic.lower() ``=``= 'ldr' and ins.operands[``0``].reg ``=``= op_reg1 \ ``and flag_ldr ``=``= False``: ``pattern ``= r``'\[(.*?)\]' ``matches ``= re.findall(pattern, ins.op_str) ``assert len``(matches) ``=``= 1``, ``'not find []: %x\t%s\t%s' % (addr, ins.mnemonic, ins.op_str) ``op2_str ``= matches[``0``] ``regs ``= op2_str.split(``', '``) ``assert len``(regs) ``=``= 2``, ``'ins invalid!: %x\t%s\t%s' % (addr, ins.mnemonic, ins.op_str) ``table_base ``= context[reg_ctou(regs[``0``]) ``- arm64_const.UC_ARM64_REG_X0] ``op_reg3 ``= reg_ctou(regs[``1``]) ``flag_ldr ``= True ``# CSEL X9, X10, X9, EQ ``if flag_ldr ``=``= True and ins.mnemonic.lower() ``=``= 'csel' and reg_ctou(ins.reg_name(ins.operands[``0``].reg)) ``=``= op_reg3 \ ``and flag_csel2 ``=``= False``: ``regname1 ``= ins.reg_name(ins.operands[``1``].reg) ``regname2 ``= ins.reg_name(ins.operands[``2``].reg) ``# index1 = reg_ctou(regname1) - arm64_const.UC_ARM64_REG_X0 ``# index2 = reg_ctou(regname2) - arm64_const.UC_ARM64_REG_X0 ``reg3_value1 ``= 0 if regname1.lower() ``=``= 'xzr' else context[reg_ctou(regname1) ``- arm64_const.UC_ARM64_REG_X0] ``reg3_value2 ``= 0 if regname2.lower() ``=``= 'xzr' else context[reg_ctou(regname2) ``- arm64_const.UC_ARM64_REG_X0] ``flag_csel2 ``= True ``if flag_csel1 ``=``= True and flag_csel2 ``=``= True``: ``# 满足条件时走的分支 ``barr1 ``= uc.mem_read(table_base ``+ reg3_value1, ``8``) ``#直接从文件中读数据,注意内存偏移和文件偏移的转换 ``base1 ``= struct.unpack(``'q'``,barr1) ``offset1 ``= base1[``0``] ``- reg2_value1 ``# 不满足条件时走的分支 ``barr2 ``= uc.mem_read(table_base ``+ reg3_value2, ``8``) ``base2 ``= struct.unpack(``'q'``,barr2) ``offset2 ``= base2[``0``] ``- reg2_value2 ``return (offset1, offset2, get_context(uc), cond) ``else``: ``return None |

除了双分支,还有单分支的情况:

这种只有一个固定的分支,所以直接patch成 b 0xxxxxxxxx即可

所以如果上述特征匹配不成功,则认为是单分支

3.5 patch

当遇到混淆块,计算出分支地址之后,就要进行patch了,双分支的patch需要两条指令的空间,但是有时候混淆块的倒数第二个指令是原来的指令,不能被覆盖,那么能用的就只有一条指令的空间。

那就只能找代码段中别的的空闲空间,调b跳转到空闲空间,然后在跳转到两个分支,找一个跳板。

当我在so中搜索nop时,居然发现了一段很长的nop,那用这里不就行了吗,反正去混淆也只是为了静态分析,不需要塞回去让so正常跑。

patch代码:

|----------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | def patch_double_branch(uc, addr, branch): ``global out_data ``nop_addr ``= find2nop(uc) ``assert nop_addr ``is not None``, ``'no find 2 nop' ``offset1 ``= branch[``0``] ``offset2 ``= branch[``1``] ``cond ``= branch[``3``] ``ks ``= keystone.Ks(keystone.KS_ARCH_ARM64, keystone.KS_MODE_LITTLE_ENDIAN) ``# 1. 把bx reg修改成跳转到nop_addr ``jmp1_asm ``= 'b ' + hex``(nop_addr) ``jmp1_bin ``= ks.asm(jmp1_asm, addr)[``0``] ``# 2. bcond addr1 ``jmp2_asm ``= 'b' + cond ``+ ' ' + hex``(offset1) ``jmp2_bin ``= ks.asm(jmp2_asm, nop_addr)[``0``] ``#3. b addr2 ``jmp3_asm ``= 'b ' + hex``(offset2) ``jmp3_bin ``= ks.asm(jmp3_asm, nop_addr ``+ 4``)[``0``] ``#print(jmp3_bin) ``#patching ``print``(``'patch code: %x\t%s => %s' % (addr, ``list``(out_data[addr: addr ``+ 4``]), jmp1_bin)) ``out_data ``= patch_bytes(out_data, bytearray(jmp1_bin), addr, ``4``) ``print``(``'patch code: %x\t%s => %s' % (nop_addr, ``list``(out_data[nop_addr: nop_addr ``+ 4``]), jmp2_bin)) ``out_data ``= patch_bytes(out_data, bytearray(jmp2_bin), nop_addr, ``4``) ``print``(``'patch code: %x\t%s => %s' % (nop_addr ``+ 4``, ``list``(out_data[nop_addr ``+ 4``: nop_addr ``+ 8``]), jmp3_bin)) ``out_data ``= patch_bytes(out_data, bytearray(jmp3_bin) , nop_addr ``+ 4``, ``4``) |

最后将so数据写回文件

|-------|-----------------------------------------------------------------------------------------|
| 1 2 3 | #保存patch后的so with ``open``(patched_filename, ``'wb'``) as f: ``f.write(out_data) |

4. 效果

还存在一些虚假控制流,但是已经不影响分析了

5. 问题

  1. 如果没有找到那一长串nop,就需要给代码段扩展一下放跳板,不知道咋搞
  2. 混淆块的特征可能还需要进一步测试修改,只测了几个函数
相关推荐
wearegogog1234 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
molaifeng4 小时前
Go 语言如何实现高性能网络 I/O:Netpoller 模型揭秘
开发语言·网络·golang
Drawing stars4 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间4 小时前
Matlab学习记录33
开发语言·学习·matlab
品克缤4 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
Evand J5 小时前
【2026课题推荐】DOA定位——MUSIC算法进行多传感器协同目标定位。附MATLAB例程运行结果
开发语言·算法·matlab
小二·5 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°5 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
jllllyuz5 小时前
基于MATLAB的二维波场模拟程序(含PML边界条件)
开发语言·matlab
忆锦紫5 小时前
图像增强算法:Gamma映射算法及MATLAB实现
开发语言·算法·matlab