吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 711|回复: 5
上一主题 下一主题
收起左侧

[原创] 基于trace构建CFG找到关键点

  [复制链接]
跳转到指定楼层
楼主
SharsDela 发表于 2025-3-4 23:48 回帖奖励
本帖最后由 SharsDela 于 2025-3-5 00:03 编辑

本文章仅为技术交流目的而创作,旨在分享观点、经验及技术思路。
使用工具:       Intelpin, 生成trace记录
                      Python以及使用triton作为反汇编框架
                      IDA 9.0
目标程序: 加了壳的elf 程序. 带有花指令和控制流平坦化.壳本身带有花指令, 控制流混淆, 控制流平坦化
壳的功能: 1.对程序完整性进行校验
                                检测到 hash 值不匹配, rip 会执行到 0xdead 然后 crash               
                 2.检测 /proc/self/statustracer id进行反调试                             
                                检测到被调试会调用Kill函数结束自己
目的: 根据trace记录构建CFG, 并且根据执行流程回溯关键条件跳转
CFG构建的效果如下:
间接跳转使用红色, 一眼看到分发器
条件跳转使用绿色, 有助于快速找到关键点
直接跳转使用蓝色

原程序正常执行的CFG执行了4000多万条指令, 去重后,指令数量 18104 个

当程序被patch, 完整性校验不通过时的CFG

去花后的CFG


花指令分析:使用SSA分析过, 等于没有操作, 连续且可以直接移除的查看以下基本块
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
lea     rsp, [rsp+8]
pushfq
add     rbp, 64h
popfq
lea     rbp, [rbp-64h]
pushfq
push    rdi
mov     rdi, [rsp+8]
lea     rdi, [rdi+0Ah]
xchg    rdi, [rsp+8]
mov     [rsp+8], rdi
mov     rdi, [rsp]
lea     rsp, [rsp+8]
call    $+5
lea     rax, [rax]
add     qword ptr [rsp+0], 18C8Eh
xchg    rax, [rsp+0]
pushfq
add     rbp, 29h
popfq
lea     rbp, [rbp-29h]
xchg    rax, [rsp+8]
push    rdx
mov     rdx, rdi
pop     rdx
xchg    rax, [rsp+0]
push    rbp
mov     rbp, rsp
leave
popfq
push    rdx
mov     rdx, [rsp+8]
lea     rdx, [rdx+32h]
xchg    rdx, [rsp+8]
mov     [rsp+8], rdx
mov     rdx, [rsp]
lea     rsp, [rsp+8]
retn

====================
可以直接移除的, 可以是任意立即数
pushfq
add     rbp, 64h
popfq
lea     rbp, [rbp-64h]

====================

可以是任意寄存器

push    rdi
mov     rdi, [rsp+8]
lea     rdi, [rdi+0Ah]
xchg    rdi, [rsp+8]
mov     [rsp+8], rdi
mov     rdi, [rsp]
lea     rsp, [rsp+8]

===================

可以是任意寄存器

push    rdx
mov     rdx, rdi
pop     rdx

================

控制流混淆分析:

去掉之后剩下的就是控制流混淆, 没有实际功能

lea     rsp, [rsp+8]
pushfq
call    $+5
lea     rax, [rax]
add     qword ptr [rsp+0], 18C8Eh
xchg    rax, [rsp+0]
xchg    rax, [rsp+8]
xchg    rax, [rsp+0]
popfq
retn

关键在call $+5 , 和 add指令, 根据当前基本块内的地址加上偏移跳到下一个基本块, 可以替换成 一条 push imm指令, 都是固定套路,没有变化.

=============

还有一些花指令, 看代码就不说了.

CFG构建的思路就是合并一些花指令基本块,

上一个基本块

call imm1
下一个基本块
lea     rsp, [rsp+8]
jmp imm2
在CFG中看就是这样

像这种,两个基本块基本都没有实际功能的, 后继基本块入度为 1 的, 两个基本块 合并成 jmp imm2

优化bcf虚假控制流, 看似走了不同的分支, 实际上没有功能并最终走向同一个节点

手动标记,并进行优化.

找关键条件跳转的思路
根据trace的流程,打印基本块的指令,找到最后一个执行的基本块, 往上找到一个条件跳转,则是关键跳转

下面是代码,有的见附件
Pin instruction trace 代码就不贴了, 自带的例子修改几行代码就行
格式是
0x12345678
0x45678910


python代码, CFG构建部分大部分用Claude进行提示词工程完成的
流程是 : 加载二进制, 遍历trace指令, 构建基本块并执行优化, 构建CFG并执行优化
[Python] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def main():
    global gVecCleanBbl
    trace = load_trace(TRACE_FILE)
    # Set optimization
    ctx.setMode(MODE.ALIGNED_MEMORY, True)
    ctx.setMode(MODE.CONSTANT_FOLDING, True)
    ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True)
    ctx.setMode(MODE.AST_OPTIMIZATIONS, True)
 
    # Load the binary
    loadBinary()
 
    purify(ctx, trace)
    # 按顺序打印所有基本块, 根据此找到关键跳转
    printResult(gVecCleanBbl)
    # bbl_patch 手动patch的虚假控制流
    cfg, edge_counts, bbls = processAndOptimizeCFG(BblTraceOrder, gVecCleanBbl, ctx, bbl_patch.keys())
 
 
 
    # reDisassembly(bbls, cfg)
    printBasicBlock(bbls, "merged_bbl.txt")
    saveCFGDot(edge_counts, cfg, bbls, "cfg_colored.dot")
    pass



使用lief加载二进制报错不知道为啥,这里我使用gdb进行dump然后加载, 脚本见附件
[Python] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
def loadBinary():
    dump = ProcessDump(DUMP)
    for segment in dump.memory:
        if 'perms' not in segment:
            continue
        if 'start' not in segment:
            continue
        if 'end' not in segment:
            continue
        if segment['perms' == 'r-xp' and segment['start' < 0x7ffff7fbd000:
            ctx.setConcreteMemoryAreaValue(segment['start'], segment['memory'])
    for reg, value in dump.registers.items():
        if 'base' in reg:
            continue
        ctx.setConcreteRegisterValue(ctx.getRegister(reg), value)
    return




感觉应该有使用符号执行一把梭的方法, 但是太菜了,希望有大牛点拨, 继续学习.




代码附件:
tracePurify.zip (11.5 KB, 下载次数: 5)

的]


免费评分

参与人数 4吾爱币 +9 热心值 +4 收起 理由
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hello95271 + 1 + 1 我很赞同!
tianyu925 + 1 我很赞同!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
cdm210 发表于 2025-3-5 14:02
感觉还行
3#
huanglaoda 发表于 2025-3-5 15:37
4#
oscar4222001 发表于 2025-3-6 00:07
感谢大佬,虽然我本身能力不好看不懂!!!但是还是非常感谢!!!
5#
c271687388 发表于 2025-3-6 08:09
感谢大佬分享思路!!!
6#
HCl10 发表于 2025-3-14 09:18
太强了,学习一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-1 10:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表