

























PAT 文件是 IDA FLAIR/FLIRT 的“文本签名源文件”。它不是最终给 IDA 加载的 .sig,而是给 sigmake 编译成 .sig 的中间文本文件。官方/常见格式说明里说:PAT 是文本文件,每个目标模块一行,最后用 --- 结束。
一个 .pat 文件通常长这样:
最后的:
表示 PAT 文件结束。
也就是说,.pat 没有像 .sig 那样复杂的二进制文件头。它的主体就是一行一行的签名记录。
一条函数/模块签名行大致格式是:
官方示例把它拆成这些字段:PATTERN BYTES、ALEN、ASUM、TOTAL LENGTH OF MODULE、PUBLIC NAMES、REFERENCED NAMES、TAIL BYTES。
通俗说就是:
格式:
含义:
| 内容 | 解释 |
|---|---|
| 一共 64 个字符 | 代表前 32 个字节 |
49、89、D0 |
固定字节,匹配时必须完全一致 |
.. |
一个可变字节 |
........ |
4 个可变字节 |
例如:
表示:
后面的 4 字节是地址偏移,链接时可能变化,所以 PAT 里不能写死,只能写成:
Hex-Rays 官方说明也强调,函数签名首先记录函数前 32 字节,并把可变字节标出来;这些可变字节通常来自重定位、外部引用、链接器修正等。
格式:
或:
含义:从第 33 个字节开始,有多少个连续固定字节参与 CRC16 计算。
注意:PATTERN 已经包含了前 32 字节。ALEN 管的是 第 32 字节之后 的一段固定字节。
例如:
表示:
如果写成:
表示:
你的例子里是:
原因是函数前 32 字节之后紧接着是 jmp 的相对偏移,这种偏移是可变的,所以不能算 CRC。
Hex-Rays 官方说明中也提到:当多个函数前 32 字节相同时,会从第 33 字节开始,直到遇到第一个可变字节之前,计算 CRC16;如果第 33 字节就是可变字节,那么 CRC 长度就是 0。
格式:
或:
含义:上一段 ALEN 字节的 CRC16 校验值。
例如:
表示:
你的例子是:
意思是:
格式:
含义:这个模块/函数的总长度。
你的例子:
表示:
也就是说,真正参与这条签名描述的函数代码范围是:
注意:这不是整个 .obj 文件大小,也不是 .pat 行长度,而是这个函数模块本身的长度。
PAT 格式说明里也明确说,TOTAL LENGTH OF MODULE IN BYTES 就是模块总长度,并且模块长度应小于 0x8000。
格式:
例如你这条:
含义:
通俗说:
其中:
| 写法 | 含义 |
|---|---|
:0000 name |
模块偏移 0 处定义了公开名字 |
:0010 name |
模块偏移 0x10 处也定义了一个名字 |
:0000@ name |
本地名字/local name |
:-0004 name |
负偏移名字,少见 |
PAT 规定每条模块记录至少要有一个 public name;如果没有,预处理器通常会造一个 :0000 ?。
格式:
含义:当前模块在某个偏移位置引用了另一个符号。
你的例子:
通俗解释:
| 字段 | 意思 |
|---|---|
^0009 |
函数内部偏移 0x0009 处引用了一个符号 |
^0014 |
函数内部偏移 0x0014 处引用了另一个符号 |
^0020 |
函数内部偏移 0x0020 处引用了外部函数 |
这些引用位置通常对应:
这些 xxxx 是链接器/加载器修正的地址或偏移,所以对应字节在 PATTERN 或 TAIL 里一般会写成 ..。
PAT 格式说明也说,引用名字用 ^XXXX name 表示,XXXX 是引用位置相对模块起点的偏移,而且该位置的字节显然是可变字节。
格式:
或:
含义:CRC 块后面的剩余字节。
PAT 的前 32 字节已经放在 PATTERN 里了。第 32 字节之后,如果有一段固定字节,就用 ALEN + ASUM 表示;再往后剩下的内容,就放到 TAIL_BYTES。
尾部字节的写法和前 32 字节一样:
| 写法 | 含义 |
|---|---|
5D |
固定字节 |
C3 |
固定字节 |
.. |
一个可变字节 |
........ |
4 个可变字节 |
你的例子最后是:
对应的是:
中的 4 字节相对跳转偏移。
也就是说:
其中 E9 已经在前 32 字节模式最后出现了,后面的 4 字节跳转偏移放在 TAIL_BYTES 里。
你的 PAT 行核心结构是:
可以拆成:
| 部分 | 原始内容 | 通俗解释 |
|---|---|---|
PATTERN |
4989D0...C9E9 |
函数前 32 字节匹配模板 |
ALEN |
00 |
第 32 字节后没有固定 CRC 区 |
ASUM |
0000 |
CRC16 为 0 |
MODLEN |
0024 |
函数模块长度 36 字节 |
PUBLIC_NAME |
:0000 _ZN64... |
函数起点处的函数名 |
REF 1 |
^0009 switch.table... |
偏移 0x09 处引用字符串长度表 |
REF 2 |
^0014 switch.table...1.rel |
偏移 0x14 处引用字符串地址偏移表 |
REF 3 |
^0020 _ZN42...str...Display... |
偏移 0x20 处跳转到 Rust 字符串格式化函数 |
TAIL |
........ |
函数尾部 4 个可变字节 |
这是最容易搞错的地方。
PAT 里的:
都不是文件绝对偏移,而是:
如果这个函数在 IDA 里从 0x18 开始,那么:
| PAT 偏移 | 实际文件/IDA 偏移 |
|---|---|
0000 |
0x18 |
0009 |
0x21 |
0014 |
0x2C |
0020 |
0x38 |
所以 ^0009 的意思不是文件偏移 0x09,而是:
IDA 用 PAT/FLIRT 识别函数,大致是这样:
Hex-Rays 官方说明里也说,单靠前 32 字节不总是足够;如果多个函数前 32 字节相同,就继续用 CRC16、不同位置的字节、引用名称等方式区分。
| 符号/字段 | 含义 |
|---|---|
XX |
固定十六进制字节 |
.. |
一个可变字节 |
........ |
四个可变字节 |
PATTERN |
前 32 字节模板 |
ALEN |
CRC 块长度 |
ASUM |
CRC16 值 |
MODLEN |
模块总长度 |
:XXXX name |
当前模块定义的公开名字 |
:XXXX@ name |
当前模块定义的本地名字 |
^XXXX name |
当前模块在偏移 XXXX 处引用了外部/内部符号 |
TAIL_BYTES |
CRC 块之后的剩余字节 |
--- |
PAT 文件结束 |
PAT 文件每一行就是一个函数/模块的“指纹”:前 32 字节模式负责快速匹配,ALEN+ASUM 负责 CRC 校验,MODLEN 说明函数长度,:XXXX 记录本函数名字,^XXXX 记录它引用的符号,最后的尾部字节补充剩余机器码,整个文件最后用 --- 结束。
免责声明 本文档所有内容仅供安全研究、学术交流与技术学习使用,严禁用于任何未经授权的逆向破解、网络攻击、隐私窃取、恶意软件开发及其他违反《中华人民共和国网络安全法》《数据安全法》等法律法规的行为,使用者应确保已获得目标软件权利人的合法授权并自行承担因使用本文档内容所产生的一切法律责任与后果,作者不对任何直接或间接损害承担任何责任,继续阅读即视为您已知悉并同意上述全部条款。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。