符号执行android,基于Miasm符号执行移除OLLVM虚假控制流

本帖最后由 coldnight 于 2019-7-23 21:29 编辑

背景

分析被OLLVM混淆后的样本,该样本开启了虚假控制流保护。编译器会在正常的控制流中会通过不透明谓词添加额外控制流块阻止静态分析。人工去除该保护较简单,本文期望自动化去除虚假控制流,减少人力。例如:

sub_27810

55fd2b2273b5a8b4531f72773c469d6e.gif

1561982954751.png (45.8 KB, 下载次数: 0)

2019-7-23 21:25 上传

代码块1中的y_149_ptr值为变量y_149的地址,还有其他以x_num或y_num为名称的变量:

55fd2b2273b5a8b4531f72773c469d6e.gif

1561983871211.png (40.22 KB, 下载次数: 0)

2019-7-23 21:11 上传

在程序运行时,这些变量(x_num)初始化为0。因此代码块1中[eax]的值为0,jl跳转永远成立,代码块2和3不会被执行。此外代码块2中的jz跳转也永远成立。

代码块3中的指令add esp, 2E68B6h阻碍了静态分析,使IDA F5功能失效:

55fd2b2273b5a8b4531f72773c469d6e.gif

1562748741700.png (22.8 KB, 下载次数: 0)

2019-7-23 21:12 上传

分析

手动修复:虚假控制流:代码块1中jl改为jmp,同时以NOP填充代码块2和3。自动修复:

对于仅使用虚假控制流混淆的样本,需要识别jl测试条件是否为不透明谓词,如果是则修改jl为jmp即可。对于jz也如此。在去除jx等测试条件后,遍历所有基本块,未被遍历的块即为虚假控制流生成的块,可以去除。

实现

Miasm 2.0是一款逆向工程框架,提供了基于IR的符号执行引擎。在Miasm中,jl的测试条件的IR可以表示为:(@32[@32[x_ptr]]

[Python] 纯文本查看 复制代码ptr_jk = ExprId("x_ptr", 32)

memop = ExprMem(ExprMem(ptr_jk, 32), 32)

j_succ_jk = ExprId("addr_succ", 32)

j_fail_jk = ExprId("addr_fail", 32)

to_match = ExprCond(ExprOp("

如果地址的名称以x.或y.开头,则认为是不透明谓词的变量:

[Python] 纯文本查看 复制代码def is_dummy_var(s): # type: (str) -> bool

return s.startswith("x.") or s.startswith("y.")

使用符号执行引擎执行基本块,匹配基本块的分支条件,如果属于不透明谓词,则修改跳转指为为JMP即可:

[Python] 纯文本查看 复制代码loc = todo.pop() # 获得未遍历的基本块nxt_addr = sb.run_block_at(ircfg, loc)

r = match_expr(nxt_addr, to_match, [mem_addr_jk, jl_dst_jk, njl_dst_jk])

if r and any(filter(is_dummy_var, loc_db.get_location_names(ircfg.get_loc_key(r[mem_addr_jk])))) \

and isinstance(r[jl_dst_jk], ExprInt):

# 不透明谓词 生成补丁

ablk = asmcfg.loc_key_to_block(loc_db.get_offset_location(offset)) # type: AsmBlock

j_asm = ablk.lines[-1] # type: instruction 汇编语句块最后一行即为跳转指令

j_offset = j_asm.offset

j_size = len(j_asm.b)

j_dst = int(r[jl_dst_jk]) # addr_succ

# 生成JMP addr_succ的op_code

new_j_asm = gen_j_ins("JMP", j_dst, j_offset, j_size, strict=True)

patches.append((j_offset, new_j_asm))

todo.add(ExprInt(j_dst, 32)) # 遍历下一基本块

else:

for dst in possible_values(nxt_addr):

value = dst.value

if value.is_mem():

logging.warning('Bad destination: %s', value)

continue

todo.add(value) # 添加所有应遍历的分支基本块遍历完成后未被未遍历的块属于虚假控制流,可以清空:

[Python] 纯文本查看 复制代码for ablk in asmcfg.blocks:

blk_off = loc_db.get_location_offset(ablk.loc_key)

if ExprInt(blk_off, 32) not in visited_asm_blk:

b_start, b_end = ablk.get_range()

patches.append((b_start, bytes(NOP_B * (b_end - b_start)))) # 以nop清空

效果

去除不透明谓词前(手动删除阻止F5的语句):

55fd2b2273b5a8b4531f72773c469d6e.gif

1562748940811.png (45.8 KB, 下载次数: 0)

2019-7-23 21:26 上传

去除后:

55fd2b2273b5a8b4531f72773c469d6e.gif

1562749056561.png (33.16 KB, 下载次数: 1)

2019-7-23 21:26 上传

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值