文章
https://www.cnblogs.com/LittleHann/p/13451724.html
函数指纹匹配
在逆向分析时,一般都遇不到有符号表的程序,但它们可能使用了开源项目,此时可使用开源项目生成特征来识别符号,此类插件评判主要看两点:
- 效率高:内存占用要低,时间要快,算法不好时程序稍微变大(10M以上)就无法工作了。
- 准确:尽可能多的匹配,尽可能少的误报,当然后者更重要,因为我们会假定有名称的函数名称代表了它的含义,就不会再分析它了。
Flirt
此时可直接先将程序编译为静态库文件,之后先使用pelf等生成pat文件,再使用sigmake生成sig文件,将其命名后放入ida的ids/pc
目录后,使用File->load file->flirt
选择即可,详细步骤如下:
# 根据目标类型选择工具生成pat文件,如.a文件用pelf
betamao@DESKTOP:~/flair75/bin/linux$ ./pelf ~/myapache/lib/libapr-1.a libapr.pat
~/myapache/lib/libapr-1.a: skipped 0, total 76
# 再将pat转换为.sig
betamao@DESKTOP:~/flair75/bin/linux$ ./sigmake libapr.pat libapr.sig
如果顺利的话这就结束了,但一般都会报错,那是因为有多个函数有同样的签名,此时它会生成一个exc文件,需要通过编辑该文件进行取舍,之后再重新执行sigmake,详见帮助文档。从它的官方文档可见,它使用通过如下算法生成的函数签名:
- 取函数前32字节,若里面包含可变数据(如重定位数据)将以.进行通配
- 之后是CRC32校验和
- 接下来会以^XXXX FuncName表示它在偏移XXXX处调用了函数FuncName,这个可以重复多次
此处推荐个库sig-database,它里面有一些ubuntu和windows下的C库,打CTF分析静态链接文件时可用(逃
idb2pat
有些项目比较复杂,不便于修改为库,此时依然可以先生成普通文件,使用ida
生成idb
信息,再由idb2pat.py生成pat
,之后的方法同时,只是需要注意,idb2pat
使用python2编写,若需要在ida7.5(默认使用python3.8)上使用,需要做如下修改:
diff --git a/python/flare/idb2pat.py b/python/flare/idb2pat.py
index c1633f0..300a4c8 100644
--- a/python/flare/idb2pat.py
+++ b/python/flare/idb2pat.py
@@ -35,7 +35,8 @@ def zrange(*args):
raise RuntimeError("Invalid arguments provided to zrange: {:s}".format(str(args)))
if end < start:
raise RuntimeError("zrange only iterates from smaller to bigger numbers only: {:d}, {:d}".format(start, end))
- return iter(itertools.count(start).next, end)
+ return itertools.takewhile(lambda x:x<end, itertools.count(start, 1))
def get_ida_logging_handler():
@@ -255,7 +256,7 @@ def make_func_sig(config, func):
else:
sig += "%02X" % (get_byte(ea))
- sig += ".." * (32 - (len(sig) / 2))
+ sig += ".." * (32 - (len(sig) // 2))
if func.end_ea - func.start_ea > 32:
crc_data = [0 for i in zrange(256)]
@@ -292,7 +293,7 @@ def make_func_sig(config, func):
sig += public_format % (public - func.start_ea, name)
- for ref_loc, ref in refs.iteritems():
+ for ref_loc, ref in refs.items():
# TODO: what is the first arg?
name = get_true_name(0, ref)
if name is None or name == "":
@@ -460,14 +461,14 @@ def main():
sigs = make_func_sigs(c)
if c.pat_append:
- with open(filename, "ab") as f:
+ with open(filename, "a", encoding='ascii') as f:
for sig in sigs:
f.write(sig)
f.write("\r\n")
f.write("---")
- f.write("\r\n")
+ f.write("\n")
else:
- with open(filename, "wb") as f:
+ with open(filename, "w", encoding='ascii') as f:
for sig in sigs:
f.write(sig)
- f.write("\r\n")
+ f.write("\n")
另外它生成的部分数据可能有误,导致sigmake
运行失败,此时可使用-vvvv
参数增加输出详细度,这样能快速定位到错误行。
Diaphora
没有仔细分析过它的原理,看它的官网和代码量感觉应该很厉害,使用时,先对开源库生成一个sqlite数据库文件,它不是插件,直接把下载的源码包解压后,按diaphora.py
并执行:
选择导出的地址(1)确认即可。之后在待分析的目标文件里按同样的方式打开,选择导出的sqlite文件地址(1),以及上一步开源库导出的sqlite文件的地址(2)后就会开始比较,比较后结果会按相似度排列,此时可以选择一个阈值右键直接进行函数名迁移。
rizzo
该项目下有多个插件和脚本,主要关注的就是rizzo,它能生成函数的签名,并在另一个项目中导入此签名,其实它和idb2pat作用类似,都是通过IDB生成签名信息,之后在另一个项目中用签名识别函数并为其重命名,但是它们实现的方式不同,Rizzo没有使用Flirt机制,它完全用idapython实现,并使用了如下几种识别方式:
o "Formal" signatures, where functions must match exactly o "Fuzzy" signatures, where functions must only resemble each other in terms of data/call references. o String-based signatures, where functions are identified based on unique string references. o Immediate-based signatures, where functions are identified based on immediate value references.
安装时,可以直接下载整包,之后执行plugins里的install.py
脚本进行安装:
python ./install.py --install -d /path/to/your/ida/install/directory
不过它有很多插件可能实际上并不想用,所以这里手动把它里面的rizzo.py文件和shims文件夹移到插件目录即可:
bm@Desktop: ~/IDA7.5SP3/plugins$ tree
├── rizzo.py
├── shims
│ └── ida_shims.py
使用时,先打开已分析过的文件,打开File->Produce File-> Rizzo signature file...
生成签名文件,再打开待分析的文件,打开File->Load File-> Rizzo signature file...
:
在运行结束后,会在Output窗口输出统计信息,并把识别成功的函数属性改为FUNC_LIB(函数窗口看,函数名称变为浅蓝色)。事实上它的匹配效果较差,适合用在升级文件的符号迁移,而在同文件间做迁移,直接写脚本效率更高,可写如下脚本:
from ida_funcs import get_func_name, get_func
from ida_nalt import get_input_file_path
from ida_kernwin import ask_file, ask_buttons
from ida_name import set_name
from idautils import Functions
from json import dump, load
input_file_path = get_input_file_path()
def export_map():
out_path = ask_file(True, input_file_path + '.map', '要导出的文件路径')
func_addr_name = {}
for func_addr in Functions():
func_name = get_func_name(func_addr)
if func_name.startswith(('sub_',)):
continue
func_addr_name[func_addr] = func_name
with open(out_path, 'w', encoding='utf8') as f:
dump(func_addr_name, f, indent=4)
def import_map():
in_path = ask_file(False, input_file_path + '.map', '要导入的文件路径')
with open(in_path, 'r', encoding='utf8') as f:
func_addr_name = load(f, object_hook=lambda x:{int(k):v for k,v in x.items()})
for func_addr in Functions():
func_name = get_func_name(func_addr)
if not func_name.startswith(('sub_',)): # 已有的名字就不处理了
continue
if func_addr not in func_addr_name: # 原项目也没有名字
continue
func = get_func(func_addr)
new_func_name = func_addr_name[func_addr]
print('ori name: ', func_name, 'new name:', new_func_name)
set_name(func.start_ea, new_func_name)
if __name__ == '__main__':
answer = ask_buttons('export', 'import', 'cancel', 1, '你要干啥?')
if answer == 1:
export_map()
elif answer == 0:
import_map()
else:
...
bindiff
作为炒鸡牛批的二进制对比工具,能进行函数识别很合情合理吧!它提供了很多种匹配算法,但是缺少文档看不懂是啥意思...它提供了一篇论文,描述了其中一种基于调用关系的匹配方式。
finger
阿里云开发的,没看过代码,按描述是云端收集了大量样本,建立了一个很庞大的指纹库,使用时它会把本地数据(至少是签名)上传到云端进行匹配,按描述在识别公共库时应该很有用,我自己试了下某特殊应用效果不理想,安装时,先安装sdk:
pip install finger-sdk
之后再把插件下载到plugins目录重启,在函数窗口右键选择Finger可尝试识别函数:
Karta
用于识别程序中的开源组件
Pigaios
源码与二进制间比较,并将源码的信息移植到分析数据库中
其他
fingermatch还没看过不知道效果如何...