用IDA Pro打开一个缺少调试符号的程序,函数列表里经常会出现一排sub_开头的名字。其中有些来自静态链接的运行库,有些才是程序自己实现的函数。FLIRT签名可以从现有机器码中识别一部分已知库函数,类型库则能为分析提供函数原型、结构体和枚举。两者都能让反汇编和伪代码更容易读,但加载入口不同,加载后的结果也要分别检查。
一、IDA Pro怎么加载FLIRT签名
IDA会尝试自动选用适合当前程序的签名。需要手动补充时,可以从签名窗口选择。这个窗口还会列出签名的处理状态和识别到的函数数量,便于对照加载结果。
1、在签名窗口选择并应用签名
①、在IDA Pro中打开待分析文件,等待【自动分析】完成,确认当前文件的处理器架构和位数。
②、按【Shift+F5】打开【Signatures】窗口,查看当前数据库已经应用的签名。
③、将焦点放在【Signatures】窗口,按【Insert】打开选择列表,选中与目标处理器、运行库及工具链相符的签名。
④、等待IDA处理签名,在【Signatures】中查看识别数量,再到【Functions】窗口核对新增的库函数名。
选择签名时,目标的位数只是一个条件。同为Windows x64程序,也可能使用不同编译器或不同版本的运行库。如果知道程序的构建环境,可以先从对应的运行库签名着手。识别数量表示有多少函数匹配,无法单凭这个数字判断整个程序用了哪些库。
2、从文件菜单添加额外签名
手里已有适用的签名时,也可以从文件菜单进入加载流程。自备签名没有出现在选择列表里时,可以先核对IDA查找签名的位置。
①、将自备的【.sig】文件放到IDA的【sig】目录或对应的处理器子目录。使用自定义目录时,检查【IDASGN】环境变量的配置。
②、打开【File】→【Load file】→【FLIRT signature file…】,选择对应签名。
③、回到【Signatures】查看签名是否已加入处理队列,待分析结束后核对识别结果。
二、IDA Pro的FLIRT签名与类型库有何区别
FLIRT签名回答的是“这段机器码可能对应哪个已知函数”,类型库回答的是“这个函数或数据应当按什么类型来读”。同一个分析数据库可以同时使用两者。
1、用FLIRT识别已有的库函数代码
①、在【Functions】里记下一处待核对函数的地址和当前名称。
②、在【Signatures】应用与目标构建环境相符的【.sig】签名。
③、回到同一地址,核对【函数名】和库函数标记,并结合该处的调用位置检查识别结果。
静态链接会把库函数的实现编进目标文件,FLIRT就有机会从其中识别已知代码模式。比如一个原先叫sub_的函数被识别为某个运行库函数,函数列表和调用处就能用这个名字来显示。原始程序里的机器码没有因此改写,变化发生在IDA的分析数据库中。
2、加载类型库并查看可用声明
类型库文件的扩展名是.til,可提供函数原型、结构体、联合体、枚举等声明。即使目标文件里没有某个库函数的实现,只要需要解释接口参数或数据布局,对应的类型声明也能派上用场。
①、按【Shift+F11】打开【Type Libraries】,查看已经加载的类型库。
②、在列表中按【Insert】,或右键选择【Load type library…】,选中与目标平台和接口相符的类型库。
③、打开【Local Types】,按需导入所需的结构体或枚举。核对结构体成员、字段偏移和类型大小。
3、在同一函数上对照名称与原型
①、在【Functions】中选中已识别的库函数,打开反汇编或【伪代码】视图。
②、按【Y】打开【Set type】,核对当前函数的参数类型、返回类型和调用约定。
③、如需补充原型,按目标库的实际声明设置类型,再回到【伪代码】查看调用处的变化。
函数名已经出来,参数仍可能显示为通用指针或整数。加载类型库后,声明可以供当前数据库使用,但仍要确认目标函数是否取得了合适的原型。尤其是指针指向结构体时,原型是否正确,会影响后续对字段访问的解读。
三、IDA Pro加载FLIRT签名后没有识别出函数怎么检查
签名出现在列表里,说明它已进入当前数据库的应用流程。处理结束后识别数量仍为零,可以先检查签名是否适用,再确认目标文件中是否有对应的代码。
1、核对签名与构建条件
①、在【Signatures】里查看签名状态及识别数量,确认本轮【自动分析】已经完成。
②、核对目标的【处理器架构】、位数和可查到的编译器信息,再检查所选签名对应的运行库版本。
③、如果同类库有多个构建版本的签名,在分析数据库副本中分别应用,记录每次新增的函数名称和地址。
库名相同,编译版本或优化设置不同,生成的机器码也可能不同。用副本分别核对,能看清某份签名单独识别了哪些位置,避免把此前已有的名称算进这次结果。
2、确认目标文件里有没有库函数实现
①、打开【Imports】,查看待核对的函数是否作为外部符号导入。
②、从调用位置查看【交叉引用】,确认它指向导入项,还是指向当前文件中的函数体。
③、如果调用指向导入项,打开【Type Libraries】核对是否有相应接口的类型库,再到调用处查看参数声明。
通过动态链接导入的函数,其实现代码在其他模块中。此时当前文件可以保留导入名称,也可以用类型声明解释参数。若函数体确实编进当前文件,再检查该处代码是否已被IDA定义为函数,以及所选签名是否覆盖该构建版本。
总结
分析时可以把函数名称和类型声明当作两条线索。签名命中后,有了可以沿调用关系核对的函数名。类型补齐后,指针、返回值和结构体字段也有了更具体的含义。交接分析数据库时,把用过的签名和手动补过的类型记下来,后来接手的人就能判断哪些名称来自自动匹配,哪些声明还需要对照程序版本确认。
