当前位置: 首页 > news >正文

应用安全 --- IDA FLIRT 原理

应用安全 --- IDA FLIRT 原理

以识别 strlen 为例


总览

┌─────────────────────────────────────────────────────────────┐
│                    完整识别流程                               │
│                                                             │
│  [静态库] → [提取特征] → [生成签名] → [IDA匹配] → [标注]    │
│   .lib        .pat          .sig        识别          函数名  │
└─────────────────────────────────────────────────────────────┘

第一阶段:准备签名库(离线完成)

1.1 原始函数机器码

; strlen 编译后的机器码
; 地址无关,只看字节内容

偏移  机器码          汇编
+00:  55              push ebp
+01:  8B EC           mov  ebp, esp
+03:  8B 45 08        mov  eax, [ebp+8]    ; eax = s (参数)
+06:  8A 08           mov  cl, [eax]       ; cl = *s
+08:  84 C9           test cl, cl
+0A:  74 09           jz   +09             ; ← 偏移可变!
+0C:  90              nop
+0D:  40              inc  eax
+0E:  8A 08           mov  cl, [eax]
+10:  84 C9           test cl, cl
+12:  75 FB           jnz  -05             ; ← 偏移可变!
+14:  2B 45 08        sub  eax, [ebp+8]
+17:  5D              pop  ebp
+18:  C3              ret
; 函数总长 = 0x19 = 25 字节

1.2 FLAIR工具提取特征 → 生成 .pat

Step1: 提取前32字节
55 8B EC 8B 45 08 8A 08 84 C9 74 09 90 40 8A 08
84 C9 75 FB 2B 45 08 5D C3
(本函数只有25字节,不足32字节则全取)

Step2: 标记可变字节(跳转偏移)
偏移+0A 的 09 → 可变 → 掩码为 ..
偏移+12 的 FB → 可变 → 掩码为 ..

Step3: 生成 pat 条目
55 8B EC 8B 45 08 8A 08 84 C9 74 .. 90 40 8A 08 84 C9 75 .. 2B 45 08 5D C3
                              ^^                        ^^
                           掩码                       掩码

pat完整一行:
55 8B EC 8B 45 08 8A 08 84 C9 74 .. 90 40 8A 08 84 C9 75 .. 2B 45 08 5D C3 19 A1B2 0019 :0000 _strlen
│                                                                           │  │    │    └─── 函数名
│                                                                           │  │    └──────── 函数总长(0x19=25)
│                                                                           │  └───────────── CRC16值
│                                                                           └────────────────可变区长度
└──────────────────────────────────────────────────────────────────────────────────────────── 掩码后的前缀

1.3 sigmake 生成 .sig

运行命令:
sigmake msvcrt.pat msvcrt.sig

内部构建 Trie 树:

└─[55]
  └─[8B EC]
    └─[8B 45 08]
      └─[8A 08 84 C9 74 ..]
        └─[90 40 8A 08 84 C9 75 ..]
          └─[2B 45 08 5D C3]
            └─ 叶子节点
               ├── crc_len : 0x05
               ├── crc16   : 0xA1B2
               ├── total   : 0x19
               └── name    : "_strlen"

第二阶段:IDA 加载分析(在线识别)

2.1 IDA 发现未知函数

IDA 反汇编视图:

.text:00401080 sub_401080:          ; ← 未识别,显示为sub_XXXXXX
.text:00401080     push    ebp
.text:00401081     mov     ebp, esp
.text:00401083     mov     eax, [ebp+8]
.text:00401086     mov     cl, [eax]
.text:00401088     test    cl, cl
.text:0040108A     jz      short loc_401095
.text:0040108C     nop
.text:0040108D loc_40108D:
.text:0040108D     inc     eax
.text:0040108E     mov     cl, [eax]
.text:00401090     test    cl, cl
.text:00401092     jnz     short loc_40108D
.text:00401094 loc_401095:
.text:00401094     sub     eax, [ebp+8]
.text:00401097     pop     ebp
.text:00401098     retn

2.2 加载 .sig 文件

IDA操作:
File → Load File → FLIRT Signature File
选择 msvcrt.sig

或快捷键: Shift+F5

2.3 IDA 提取函数字节

从地址 0x00401080 读取字节:

原始字节:
55 8B EC 8B 45 08 8A 08 84 C9 74 [09] 90 40 8A 08
84 C9 75 [FB] 2B 45 08 5D C3

对可变位置应用掩码:
55 8B EC 8B 45 08 8A 08 84 C9 74 [..] 90 40 8A 08
84 C9 75 [..] 2B 45 08 5D C3

掩码后用于Trie匹配的序列:
55 8B EC 8B 45 08 8A 08 84 C9 74 .. 90 40 8A 08 84 C9 75 .. 2B 45 08 5D C3

2.4 Trie 树匹配过程

逐字节在Trie中查找:

输入:  55  → 匹配根节点子节点[55] ✓
输入:  8B  → 匹配下一层[8B EC]   ✓
输入:  EC  →                      ✓
输入:  8B  → 匹配[8B 45 08]      ✓
输入:  45  →                      ✓
输入:  08  →                      ✓
...
输入:  C3  → 到达叶子节点!

叶子节点数据:
┌──────────────────────────────┐
│ 候选函数: _strlen            │
│ crc_len:  5                  │
│ crc16:    0xA1B2             │
│ total:    0x19               │
└──────────────────────────────┘

结果:找到1个候选 → 进入CRC验证

2.5 CRC16 精确验证

确定CRC计算范围:
函数起始地址:  0x401080
前缀长度:      25字节(函数总长不足32字节的情况)
CRC区域:       这里直接对非可变字节计算

实际参与CRC计算的字节(跳过可变字节):
偏移  字节  是否参与
+00:  55   ✓
+01:  8B   ✓
+02:  EC   ✓
+03:  8B   ✓
+04:  45   ✓
+05:  08   ✓
+06:  8A   ✓
+07:  08   ✓
+08:  84   ✓
+09:  C9   ✓
+0A:  74   ✓
+0B:  09   ✗ 跳过(可变)
+0C:  90   ✓
...
+12:  75   ✓
+13:  FB   ✗ 跳过(可变)
+14:  2B   ✓
...

计算 CRC16 结果 = 0xA1B2

对比签名:
计算值 0xA1B2 == 存储值 0xA1B2  → ✓ 验证通过!

2.6 确认并标注函数名

验证通过!IDA 执行标注:

操作:
sub_401080  →  strlen

同时:
1. 函数注释自动添加
2. 参数名称恢复(如果sig包含类型信息)
3. 相关引用同步更新

第三阶段:识别结果

标注前

.text:00401080 sub_401080 proc near
.text:00401080     push    ebp
.text:00401081     mov     ebp, esp
.text:00401083     mov     eax, [ebp+arg_0]
...
.text:00401098     retn
.text:00401098 sub_401080 endp

标注后

.text:00401080 strlen proc near          ; ← 自动识别!
.text:00401080     push    ebp
.text:00401081     mov     ebp, esp
.text:00401083     mov     eax, [ebp+s]  ; ← 参数名也恢复了
...
.text:00401098     retn
.text:00401098 strlen endp

; 调用处也自动更新:
.text:00401200     push    eax
.text:00401201     call    strlen         ; ← 原来是 call sub_401080

完整流程一图总结

离线阶段                              在线阶段
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                      
msvcrt.lib                            待分析的 binary
    │                                      │
    ▼                                      ▼
[pelf工具]                          IDA 反汇编
提取每个函数字节                     发现 sub_401080
    │                                      │
    ▼                                      ▼
标记可变字节(..)                     读取函数字节
74 09 → 74 ..                        55 8B EC ...
    │                                      │
    ▼                                      ▼
生成 .pat 文件                       应用 .. 掩码
[前缀+CRC+函数名]                    74 09 → 74 ..
    │                                      │
    ▼                                      ▼
[sigmake工具]                        Trie 前缀匹配
构建压缩Trie树                       找到候选: strlen
    │                                      │
    ▼                                      ▼
生成 .sig 文件  ──────加载──────►    CRC16 验证
                                     0xA1B2 == 0xA1B2 ✓
                                    标注函数名
                                    sub_401080 → strlen ✓

关键点回顾

步骤核心操作目的
提取前缀 取函数前32字节 快速过滤
掩码处理 跳转偏移置.. 消除地址相关性
Trie匹配 逐字节查树 找到候选函数
CRC验证 计算非可变字节校验值 精确区分相似函数
标注结果 写入函数名/参数名 辅助逆向分析