标题: IoT设备逆向工程中的函数识别 创建: 2019-05-08 17:56 更新: 2023-06-26 14:28 链接: https://scz.617.cn/misc/201905081756.txt -------------------------------------------------------------------------- 目录: ☆ 前言 ☆ 函数识别/符号迁移 1) FLIRT 1.1) FLIRT简介 1.2) 签名文件 1.3) 应用签名 1.4) FLAIR简介 1.5) EXC文件 2) IDB2PAT 3) lscan 4) rizzo 5) 利用IDC迁移符号 6) syms2elf 7) 静态库 7.1) 从源码编译OpenSSL 7.2) 生成SIG 7.4) SIG/RIZ实验 7.5) 其他 8) 社区维护的SIG库 vs 自编译源码 9) IDA Signsrch 9.1) signsrch.exe 10) 静态识别 vs 动态调试 ☆ 反编译 1) Snowman 2) HexRaysCodeXplorer 3) HexRaysPyTools ☆ 二进制比较 1) Diaphora 2) BinDiff ☆ 其他 1) IdaRef 2) localxrefs ☆ 参考资源 -------------------------------------------------------------------------- ☆ 前言 本文主要讨论IoT设备逆向工程中的函数识别、符号迁移问题,不考虑BinDiff、 PatchDiff2等重型工具,有些场景也不太适用。函数识别技术主要涉及两大类,一是 IDA自身的FLIRT技术,二是Craig的rizzo技术,本文介绍它们的基本原理、工程实践 细节。另外附赠几个其他类型IDA插件的使用说明。 ☆ 函数识别/符号迁移 1) FLIRT 1.1) FLIRT简介 考虑如下场景,分析一个被strip过的、不包含调试信息、符号信息的ELF,此ELF静 态链接过OpenSSL的libcrypto.a,而你关心ELF所用加密算法、HASH算法。findcrypt 插件可用于这种需求,但不够。 FLIRT是"Fast Library Identification and Recognition Technology"的缩写。这 是IDA自带的一种函数识别技术,用于前述需求。 参[1],作者介绍IDA 3.6中FLIRT技术的基本原理: -------------------------------------------------------------------------- Each function is represented by a pattern. Patterns are first 32 bytes of a function where all variant bytes are marked. A special signature file, called startup signature file is applied to the entry point of the disassembled program to determine the generating compiler. For the sake of user's convenience we attempted to recognize the main() function as often as it was possible. The algorithm for identifying this function differs from compiler to compiler and from program to program. The signature file contains no byte from the original libraries, except for the names of the functions. -------------------------------------------------------------------------- 简单点说,为静态库中每个函数确定其特征字节流、特定调用关系、特定检查项等等, 以此识别出对应函数。将各函数名及其识别特征按特定格式组织并存放在签名文件中, 形成扩展名为.sig的签名文件。IDA在分析ELF时,可以加载.sig,自动识别ELF中静 态链接进来的库函数。 签名文件对函数的识别标准比较复杂,并不是函数入口32字节这么简单,细节参[1]。 1.2) 签名文件 IDA自带一些签名文件: $ cd "C:\Program Files\IDA" $ tree /F /A sig | list | +---arm | android_arm.sig | armlibb.sig | armlibl.sig | armlib_l.sig | autoload.cfg | elf.sig | mfc.sig | pe.sig | vc_atl.sig | vc_extra.sig | vc_rtf.sig ... | +---mips | mfc.sig | pe.sig | psx.sig | psyq.sig | +---pc | autoload.cfg ... IDA自带签名文件存放在: \sig\\*.sig 是CPU架构,IoT设备逆向工程很可能面对arm。.sig的名字已经提供了一定信 息,可以从中猜测该.sig源自什么,.sig中可能含有一条说明信息,用如下办法查看: $ strings -n 5 armlibl.sig | head -2 IDASGN ARM library little endian 第二行输出即可能存在的说明信息,不是所有的.sig都有说明信息。 1.3) 应用签名 View->Open subviews->Signatures(Shift-F5) 此处看到当前使用的签名文件及靠它识别出来的函数个数 View->Open subviews->Signatures(Shift-F5)->右键菜单->Apply new signature(Ins) 或 File->Load file->FLIRT signature file 此处看到"\sig\\"下所有签名文件 《The IDA Pro Book》第2版第12章介绍FLIRT、FLAIR的使用。 -------------------------------------------------------------------------- Only functions named with an IDA dummy name can be automatically renamed. In other words, if you have renamed a function, and that function is later matched by a signature, then the function will not be renamed as a result of the match. Therefore, it is to your benefit to apply signatures as early in your analysis process as possible. -------------------------------------------------------------------------- "dummy name"形如"sub_..."。这即是说,binary被反汇编后尽可能早地应用签名文 件,以免出现人工重命名干挠签名文件带来的重命名。 1.4) FLAIR简介 FLAIR是"Fast Library Acquisition for Identification and Recognition"的缩写。 这是IDA自带工具集,用于定制自己的签名文件。 FLAIR工具集的基本使用方式: ELF -- (pelf) --> PAT -- (sigmake) +--> SIG -- (zipsig) --> 压缩格式的SIG | +--> EXC .a通过pelf得到.pat,后者是静态库中各函数名及其识别特征的文本文件格式。 .pat通过sigmake得到.sig。一般情况下第一次执行sigmake会碰上冲突,就是说多个 函数的识别特征是一样的,此时会产生.exc文件,必须手工处理.exc,解决冲突,再 次执行sigmake得到.sig。 sigmake得到的.sig已经可以为IDA所用,可用zipsig对之压缩,得到扩展名仍然是 .sig的压缩版,IDA同时支持非压缩、压缩版.sig。 FLAIR自带帮助文件: -------------------------------------------------------------------------- readme.txt FLAIR工具集概述 pat.txt 详细介绍PAT文件格式 sigmake.txt sigmake.exe的帮助文件,介绍EXC文件格式及处理方式 plb.txt plb.exe的帮助文件 -------------------------------------------------------------------------- 1.5) EXC文件 $ pelf libcrypto.a libcrypto_arm.pat $ sigmake -n"openssl-1.0.1l libcrypto.a for arm" libcrypto_arm.pat libcrypto_arm.sig libcrypto_arm.sig: modules/leaves: 497/595, COLLISIONS: 9 这表示.pat中存在冲突,多个函数的识别特征一样;自动生成libcrypto_arm.exc, 比如: -------------------------------------------------------------------------- ;--------- (delete these lines to allow sigmake to read this file) ; add '+' at the start of a line to select a module ; add '-' if you are not sure about the selection ; do nothing if you want to exclude all modules X509_get_default_private_dir 00 0000 00001FE51EFF2FE1........00001FE51EFF2FE1........00001FE51EFF2FE1 EVP_dss 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 EVP_dss1 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 EVP_ecdsa 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 NETSCAPE_SPKI_free 00 0000 00101FE5......EA........00101FE5......EA........00001FE5......EA PROXY_CERT_INFO_EXTENSION_free 00 0000 00101FE5......EA........00101FE5......EA........00001FE5......EA -------------------------------------------------------------------------- .exc最前面4行内容是固定的,它告诉你可以在后面的每行打头处增加+/-号,其意义 是: + 使用该函数名 - 让该函数名以注释形式出现 后面是空行分隔的冲突分组。 手工处理.exc示例: -------------------------------------------------------------------------- X509_get_default_private_dir 00 0000 00001FE51EFF2FE1........00001FE51EFF2FE1........00001FE51EFF2FE1 +EVP_dss 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 EVP_dss1 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 EVP_ecdsa 00 0000 00001FE51EFF2FE1........0C3090E50100A0E10310A0E1......EA0C0090E5 NETSCAPE_SPKI_free 00 0000 00101FE5......EA........00101FE5......EA........00001FE5......EA -PROXY_CERT_INFO_EXTENSION_free 00 0000 00101FE5......EA........00101FE5......EA........00001FE5......EA -------------------------------------------------------------------------- 必须删掉.exc最前面4行,表示这个.exc已被手工处理过,否则第二次sigmake不会做 动作。 对第1冲突分组(从1计)不做任何动作,该函数名被废弃。 对第2冲突分组采用函数名"EVP_dss",其余两个函数名被废弃,这导致误报。 对第3冲突分组在匹配函数处增加注释"PROXY_CERT_INFO_EXTENSION_free"。 处理EXC文件时的三条基本原则: -------------------------------------------------------------------------- a) 最简处理方式是删除EXC文件最前面(以分号打头的)4行内容,其余内容保持不变。这 表示冲突分组中所有函数名被废弃,重命名时不会用到它们。 b) 不要在同一个冲突分组内同时使用+/-,要么+要么-,且只出现一次。 c) 如果某冲突分组只有一个函数,不要对之使用+/-,保持原样,表示废弃该函数名。 我不清楚为什么会出现这种情形。 -------------------------------------------------------------------------- 2) IDB2PAT 定制签名文件的一般套路是: ELF -> PAT -> SIG 有可能出现: IDB -> PAT -> SIG 考虑如下场景,分析过一个裸格式的IoT固件,已经重命名一批关键函数,某天需要 分析另一个与前者高度近似的新版固件,此时可以用BinDiff、PatchDiff2进行二进 制比较,但那过于重型,有无其他办法快速迁移函数名? IDB2PAT是个IDA插件,顾名思义,从当前IDB导出PAT文件。然后sigmake生成SIG并应 用到新IDB中。 最早IDB2PAT是C版插件,需要IDA SDK编译,IDA一升级就得重新编译。FireEye提供 了Python版插件,参[6]。 Alt-F7加载idb2pat.py,提示选择输出PAT文件到哪里,确定即可。 idb2pat.py可用于IDA 7.2。为了在IDA 7.1中使用,需要做些修改。把所有的 "get_name("全文替换成"get_name(BADADDR,",7.2版get_name()函数原型是 "get_name(ea)",7.1版get_name()函数原型是"get_name(from,ea)"。原实现中 Config()使用缺省参数"mode=ALL_FUNCTIONS",改成"NON_AUTO_FUNCTIONS",否则会 将"sub_..."一并导出到PAT文件,这没有意义。 3) lscan 参[7]。 考虑如下场景,用IDA分析某strip过的ELF,发现其中存在被静态链接进来的库函数, 想快速知道该ELF用到哪些静态库,想快速重命名相应的静态库函数。假设你有大量 现成的.sig,一个个试过去太累。 lscan可以解决上述需求。它不是IDA插件,是独立使用的Python脚本。lscan检查SIG 与ELF的匹配程度,匹配百分比越高,ELF用到相应静态库的可能性越大。lscan可以 一次性检查多个SIG与单个ELF的匹配度,然后在IDA中优先应用匹配度最高的SIG。 lscan不能使用经zipsig压缩过的SIG,只认非压缩版SIG。 [scz@ /home/scz/src]> pip2 install pyelftools pefile [scz@ /home/scz/src]> git clone https://github.com/maroueneboubakri/lscan.git [scz@ /home/scz/src]> du -hs lscan 406M lscan "git clone"下回来406M,真正有用的就lscan.py一个文件,其他的都算测试集,其 自带SIG没啥大用,所以不必"git clone"下载。 [scz@ /home/scz/src/lscan]> python2.7 lscan.py -h Usage: lscan.py [options] Options: -h, --help show this help message and exit -v, --versbose Verbose mode -s SIGFILE, --sig=SIGFILE Signature file -S SIGDIR, --sigs=SIGDIR Signature folder -f BINFILE, --file=BINFILE ELF file -d, --dump Dump signature filre 用lscan自带"arm/sig/"下的所有SIG去匹配FLIRTTarget: [scz@ /home/scz/src/lscan]> python2.7 lscan.py -S arm/sig -f /tmp/FLIRTTarget arm/sig/libssl-1.0.1e.sig 15/573 (2.62%) arm/sig/libpthread-2.13.sig 0/260 (0.00%) arm/sig/libssl-1.1.0.sig 0/863 (0.00%) arm/sig/libc-2.22.sig 0/2841 (0.00%) arm/sig/libc-2.23.sig 0/2853 (0.00%) arm/sig/libm-2.13.sig 0/318 (0.00%) arm/sig/libcrypto-1.0.1e.sig 27/4518 (0.60%) arm/sig/libcrypto-1.1.0.sig 0/37 (0.00%) arm/sig/libm-2.22.sig 0/339 (0.00%) arm/sig/libpthread-2.22.sig 0/273 (0.00%) arm/sig/libc-2.13.sig 0/2816 (0.00%) lscan自带SIG没有适用于FLIRTTarget的。 用自制SIG去匹配FLIRTTarget: [scz@ /home/scz/src/lscan]> ls /tmp/sig fromidb.sig libcrypto_arm.sig libssl_arm.sig fromidb.sig源自IDB2PAT,其余两个.sig源自自编译库文件。 [scz@ /home/scz/src/lscan]> python2.7 lscan.py -S /tmp/sig -f /tmp/FLIRTTarget /tmp/sig/fromidb.sig 4358/2803 (155.48%) /tmp/sig/libcrypto_arm.sig 107955/4771 (2262.73%) /tmp/sig/libssl_arm.sig 247/600 (41.17%) 从这个输出看出lscan有BUG,匹配百分比都超出100%了,我懒得去找原因。 4) rizzo FLIRT的函数识别特征主要是机器码序列,而静态库函数的机器码序列受编译器、编 译选项、源代码版本影响太大。Craig提出另一套启发式函数识别标准,参[8],他检 查函数如下项: a) 对特征字符串的引用 b) 对特征常量的引用 c) Call Graph d) CFG (Control Flow Graph) e) ... 这些检查项受编译器、编译选项、源代码版本影响较小。 rizzo的工作流程是: IDB A -> RIZ -> IDB B rizzo和IDB2PAT都是从IDB出发。在IDB A中利用rizzo导出.riz文件,这相当于FLIRT 的.sig文件。然后在IDB B中利用rizzo导入.riz文件,这相当于应用签名。 考虑如下场景,用IDA反汇编静态库libsome.a,用rizzo导出libsome.riz,用IDA反 汇编怀疑用到libsome.a的ELF,用rizzo导入libsome.riz。 rizzo.py是IDA插件,将之复制到plugins目录,重启IDA即可。 导出.riz: File->Produce file->Rizzo signature file 加载.riz: File->Load file->Rizzo signature file 如果没看到这两项,说明插件有BUG。 .riz文件随便在哪儿,不要求放到"\sig\\"下。 5) 利用IDC迁移符号 如果两个IDB对应同一个ELF,通过导出IDC迁移符号、结构、注释即可。为什么会有 这种需求,自己想去。 导出.idc: File->Produce file->Dump database to IDC file 编辑some.idc,修改main(),只保留Bytes()、Functions()相关的函数。 加载.idc: File->Script file (Alt-F7) 6) syms2elf 参[10]。 假设某ELF是strip过的,用IDA对之进行分析,利用FLIRT、rizzo等技术重命名过一 批函数,此时利用syms2elf生成一个包含符号信息的新ELF。IDA反汇编新ELF时、gdb 调试新ELF时,符号信息自动生效。 将syms2elf.py复制到plugins目录,重启IDA即可。可用于IDA 6.95至7.2,x86/x64 均可。 Edit->Plugins->sym2elf 假设FLIRTTarget是旧ELF,FLIRTTarget_new是新ELF: $ file -b FLIRTTarget ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped $ file -b FLIRTTarget_new ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped $ nm -C FLIRTTarget_new | grep Private nm可以看到新ELF中已经包含重命名过的函数名。 $ nm -C FLIRTTarget_new | grep sub_ syms2elf会将"sub_..."当成有效函数名写入新ELF,这不好,应该只写"sub_..."之 外的其他函数名,我懒得改。 可用objcopy从新ELF中导出符号文件: $ objcopy --only-keep-debug FLIRTTarget_new FLIRTTarget_new.sym 显然,syms2elf只能用于ELF格式的binary,不能用于裸格式的固件。 syms2elf构造新ELF时会去找旧ELF,如果旧ELF不在相应路径上,syms2elf会失败。 检查binary所在路径: idaapi.get_input_file_path() 修改binary所在路径: idaapi.set_root_filename( "..." ) 修改成相对路径,比如只保留ELF文件名,不要目录结构,这样ELF与IDB在同一目录 即可。 7) 静态库 FLIRT、rizzo技术都依赖一个匹配度较高的静态库,有它才有匹配良好的SIG、RIZ。 经常看到一些人说FLIRT效果不佳,那更可能是你签名文件的问题,而不是FLIRT技术 本身的问题。SIG不匹配,FLIRT识别效果当然不佳,SIG不匹配源自静态库不匹配, 你得找到最接近的静态库,这是FLIRT技术最困难的点。rizzo技术也是一样的。 一般情况下分析ELF时很难知道它用到哪些静态库,更不要说获取编译时所用静态库 文件本身。运气好的话,可能在ELF中看到一些相关字符串信息。 $ file -b FLIRTTarget ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped $ strings -n 5 FLIRTTarget | grep -i gcc | sort -u GCC_3.5 libgcc_s.so.1 这可能表示编译FLIRTTarget时用的是gcc 3.5。在没有ldd的环境中查看ELF所依赖的 动态库: $ LD_TRACE_LOADED_OBJECTS=1 ./FLIRTTarget libdl.so.0 => /lib/libdl.so.0 (0x4000f000) libstdc++.so.6 => /lib/libstdc++.so.6 (0x4001a000) librt.so.0 => /lib/librt.so.0 (0x400fe000) libc.so.0 => /lib/libc.so.0 (0x40109000) libcrypt.so.0 => /lib/libcrypt.so.0 (0x401a3000) libm.so.0 => /lib/libm.so.0 (0x401bf000) libpthread.so.0 => /lib/libpthread.so.0 (0x401d7000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x401f5000) ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x40000000) $ strings -n 5 FLIRTTarget | grep -i rsa | sort -u | less ... Eric Young's PKCS#1 RSA ... rsa_ameth.c RSA_blinding ... rsa_eay.c rsaEncryption rsaesOaep ... rsa_keygen_bits rsa_keygen_pubexp ... rsa_lib.c ... RSA_new faild ... rsa_oaep.c ... RSA part of OpenSSL 1.0.1l 15 Jan 2015 rsa_pk1.c rsa_pmeth.c ... rsa_pss.c ... rsa_saos.c ... rsa_sign.c ... ssl_rsa.c ... FLIRTTarget可能用到"OpenSSL 1.0.1l"ARM版静态库(libcrypto.a)。 如下命令查看libcrypto.a中的符号: $ nm -C libcrypto.a | grep RSA_new 如下命令可知libcrypto.a的版本号: $ strings -n 5 libcrypto.a | grep -E "^OpenSSL\s+[0-9]\." 如果有条件,还可以这样确定版本号及编译选项: $ openssl version -a 上哪儿去找FLIRTTarget所用libcrypto.a呢? 7.1) 从源码编译OpenSSL https://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1l.tar.gz 版本是"openssl-1.0.1",后面跟着小写的字母"l"。 交叉编译ARM版本: [scz@ /tmp/arm]> tar xfz /tmp/openssl-1.0.1l.tar.gz [scz@ /tmp/arm/openssl-1.0.1l]> ./Configure --prefix=/usr/local --openssldir=/usr/local/openssl threads no-zlib no-shared linux-armv4 make CC=arm-gcc arm-gcc是个示意写法,你得设法分析FLIRTTarget,找出一个最接近的交叉编译工具 链。 我们只关心加密算法相关的函数,比如RSA_new(),指定no-zlib表示不关心zlib,否 则还得交叉编译zlib,麻烦。 从源码编译OpenSSL是被逼无奈的选择,不知道当时所用编译器、编译选项等信息, 只能瞎猜将就。 7.2) 生成SIG $ nm -C libcrypto.a | grep RSA_new $ pelf libcrypto.a libcrypto_arm.pat $ sigmake -n"openssl-1.0.1l libcrypto.a for arm" libcrypto_arm.pat libcrypto_arm.sig libcrypto_arm.sig: modules/leaves: 497/595, COLLISIONS: 9 sigmake很少能一次性成功,必须手工处理.exc解决冲突。 二次执行sigmake: $ sigmake -n"openssl-1.0.1l libcrypto.a for arm" libcrypto_arm.pat libcrypto_arm.sig 可用zipsig对.sig进行压缩,减少空间占用,但这不是必须的: $ zipsig libcrypto_arm.sig 可用dumpsig查看.sig: $ dumpsig libcrypto_arm.sig libcrypto_arm.txt $ dumpsig -f -X libcrypto_arm.sig libcrypto_arm.txt dumpsig不能还原出.pat,由.pat到.sig的过程有信息丢失,是单向过程,但dumpsig 得到的.txt中仍能看到特征字节流信息。 新生成的.sig应该放到"\sig\\"下。 用IDA反汇编libcrypto.a,用rizzo导出libcrypto.riz。 7.4) SIG/RIZ实验 个人经验是优先应用RIZ,再应用SIG。 先在FLIRTTarget.idb中应用libcrypto.riz,成功识别出: 00AF2A60 RSA_new 注意到FLIRTTarget.idb中RSA_new之后的函数未被识别。接着应用SIG,先Shift-F5, 再Ins,依次应用: libcrypto_arm.sig libssl_arm.sig fromidb.sig 测试发现反复应用SIG可以多识别出一些函数,比如: File State #func Library name ---- ----- ----- ------------ armlibl Applied 0 ARM library little endian libcrypto_arm Applied 2403 openssl-1.0.1l libcrypto.a for arm libssl_arm Applied 111 openssl-1.0.1l libssl.a for arm libcrypto_arm Applied 715 openssl-1.0.1l libcrypto.a for arm libssl_arm Applied 37 openssl-1.0.1l libssl.a for arm libcrypto_arm Applied 256 openssl-1.0.1l libcrypto.a for arm libssl_arm Applied 0 openssl-1.0.1l libssl.a for arm libcrypto_arm Applied 172 openssl-1.0.1l libcrypto.a for arm libcrypto_arm Applied 172 openssl-1.0.1l libcrypto.a for arm 第二行表示该轮应用libcrypto_arm.sig时有2403处函数重命名发生。 从FLIRT技术原理看,应该多次应用SIG,直至不再新增被识别函数。 7.5) 其他 静态链接时,.o中所有函数都被链接到binary中。假设在FLIRTTarget.idb中识别出 .o中某函数,其前后函数很可能对应.o中的布局,如果前后函数未被重命名,可手工 检查之。 8) 社区维护的SIG库 vs 自编译源码 参[7],有一些社区维护的SIG库,不过没啥用,用脚趾头都能想像。实践表明,还得 想办法自编译源码获取SIG/RIZ,从最终效果看,这样做有意义。 9) IDA Signsrch 参[4]。 Tool for searching signatures inside files, extremely useful as help in reversing jobs like figuring or having an initial idea of what encryption/ compression algorithm is used for a proprietary protocol or file. It can recognize tons of compression, multimedia and encryption algorithms and many other things like known strings and anti-debugging code which can be also manually added since it's all based on a text signature file read at run-time and easy to modify "IDA Signsrch"功能类似过去的findcrypt.plw,能识别的算法更多,可用于IDA 7.2。 将IDA_Signsrch.plw、IDA_Signsrch.p64、signsrch.xml、IDA_Signsrch.dll、 IDA_Signsrch64.dll分别放入相应版本plugins目录,重启IDA即可。 Edit->Plugins->Signsrch->Continue 匹配结果在"Signsrch matches"窗口展示。 "IDA Signsrch"和findcrypt不能说没用,但用处有限,很多时候还不如自己肉眼识 别特征常数并Google之。 9.1) signsrch.exe $ signsrch.exe -e md5sum Signsrch 0.2.4 by Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org optimized search function by Andrew http://www.team5150.com/~andrew/ disassembler engine by Oleh Yuschuk - open file "md5sum" - 41592 bytes allocated - load signatures - open file X:\Green\CLI\signsrch.sig - 3075 signatures in the database - start 8 threads - start signatures scanning: offset num description [bits.endian.size] -------------------------------------------- 00402c32 1018 MD5 digest [32.le.272&] 00402c47 2053 RIPEMD-128 InitState [32.le.16&] 00406b40 1038 padding used in hashing algorithms (0x80 0 ... 0) [..64] - 3 signatures found in the file in 0 seconds - done signsrch.exe找到MD5算法特征常量。指定"-e"时,把目标binary当成PE/ELF,给出 的offset是RVA而不是文件偏移,0x402c32对应md5_init_ctx()。 10) 静态识别 vs 动态调试 用IDA静态识别加密函数只是手段之一,如能动态调试,可以通过观察in/out合理猜 测算法并验证之,二者可以结合着来。 ☆ 反编译 1) Snowman 参[2]。 Snowman类似Hex-Rays,但远不如Hex-Rays。简单的"Hello World"反编译结果都惨不 忍睹。号称支持x86、x64、ARM、ARM64。官方声称支持到IDA 7.0,但实际上7.2也可 以用。 将snowman.plw、snowman.p64、snowman.dll、snowman64.dll分别放入相应版本 plugins目录,重启IDA即可。在光标处按F3展示当前函数C代码。F3的展示窗口有两 块区域,左侧是汇编,右侧是C/C++,可以在汇编区域选中代码片段,右键菜单中有 "Decompile (Ctrl-E)",对所选代码片段进行反编译,但这没有什么意义。 2) HexRaysCodeXplorer 参[5]。 将HexRaysCodeXplorer.plw、HexRaysCodeXplorer.p64、HexRaysCodeXplorer32.dll、 HexRaysCodeXplorer64.dll分别放入相应版本plugins目录,重启IDA即可。 这是Hex-Rays的插件,在Hex-Rays的反编译窗口中通过右键菜单使用。插件生效时, 右键菜单多出如下项: Display Ctree Graph T Object Explorer O REconstruct Type R Extract Types to File S Extract Ctrees to File C Ctree Item View V Jump to Disam J Show/Copy item offset Q Rename vars E IDA可以在"Local types (Shift-F1)"中自定义结构,HexRaysCodeXplorer的 "REconstruct Type"就是自动干这事,这是该插件最有用的功能。基本用法是: a) 在Hex-Rays反编译窗口中选中对象(结构)指针 b) 右键"Reset pointer type",或手工将变量由指针类型变成整型 c) 右键"REconstruct Type" d) 检查"Local types (Shift-F1)" HexRaysCodeXplorer-2.1.zip与IDA 7.2 SDK不兼容,目前不能在IDA 7.2中使用。 3) HexRaysPyTools 参[15],该插件可以自动分析重建结构定义,支持IDA 8.3。 git clone git@github.com:igogo-x86/HexRaysPyTools.git 复制HexRaysPyTools.py、HexRaysPyTools子目录到IDA\plugins\。 插件官方文档介绍了如何使用,建议直接看英文版。总的来说,HexRaysPyTools能用, 但就那么回事,聊胜于无。 ☆ 二进制比较 1) Diaphora 参[12]。 Diaphora类似BinDiff,支持在新旧IDB之间迁移符号,支持IDA 7.x。这是用Alt-F7 加载的Python版插件。Diaphora是希腊语中difference的意思。 帮助中说它用到了快捷键F3,不要跟Snowman混着用。 2) BinDiff 参 《浅析Nessus、Nmap针对MS17-010的远程精确扫描方案》 https://scz.617.cn/windows/201706221521.txt 《BinDiff二进制比较简介》 https://scz.617.cn/misc/202109131335.txt 用BinDiff迁移符号的要点是,先打开new,再比较old,最后右键Import符号。从前 hume的nsdiff可以双向迁移符号,所以一直习惯性先打开old,但这个习惯会导致 BinDiff迁移符号失败。 ☆ 其他 1) IdaRef 参[3]。 IdaRef的效果是,当光标停留在某条汇编指令上时,在"Instruction Reference"窗 口实时显示该汇编指令的帮助信息。支持x86、x64、ARM、MIPS。 | idaref.py | \---archs arm.sql mips32.sql x86-64.sql x86-64_old.sql xtensa.sql 将idaref.py和archs目录复制到plugins目录,重启IDA即可。 Edit->idaref->Stop Idaref/Start Idaref 这是个鸡肋插件,食之无味、弃之可惜。 Options->General->Disassembly->Auto comments 一般来说,打开IDA的自动注释就够了。 2) localxrefs 参[13]。 考虑如下场景,在IDA中以汇编模式查看某函数,在该函数体中想知道所有操作R5寄 存器的指令所在。为达此目的,在汇编窗口选中R5寄存器,IDA自动高亮所有R5寄存 器所在(不局限在目标函数中),鼠标点击它处之后,前述高亮自动失焦;还可以将目 标函数汇编代码复制到文本编辑器中搜索R5寄存器。localxrefs正是为此目的而生, 可以在"Output window"中显示所有操作R5寄存器的指令,可以双击指令地址自动跳 转过去。最初看到localxrefs,以为跟字符串交叉引用相关,实际不是。 localxrefs.py是IDA插件,将之复制到plugins目录,重启IDA即可,支持7.x。用法 如下: a) 在目标函数内选中R5寄存器,会黄色高亮之 b) Jump->List local xrefs 在"Output window"中会看到如下输出: Xrefs to R5 from sub_863DC8: -------------------------------------------------------------------------------------- Up w sub_863DC8+14 MOV R5, R0 Up r sub_863DC8+6C MOV R0, R5; s Up r sub_863DC8+74 MOV R1, R5 Up w sub_863DC8+8C LDR R5, [SP,#0xD30+s+8] Up r sub_863DC8+90 CMP R5, #0 Up r sub_863DC8+98 LDR R0, [R5,#0xC]; s1 Up w sub_863DC8+A8 LDREQ R5, [R5,#0x38] Up r sub_863DC8:loc_863E7C LDR R0, [R5,#0xC]; s1 Up r sub_863DC8+C0 LDR R6, [R5,#0x10] Up w sub_863DC8:loc_863EA4 LDR R5, [R5,#0x3C] Up r sub_863DC8:loc_863EA8 CMP R5, #0 Up r sub_863DC8+F8 MOV R2, R5; n - r sub_863DC8+104 STR R5, [R8,#(len_1070164 - 0x106F928)] Down w sub_863DC8+12C ADD R5, SP, #0xD30+var_130 Down w sub_863DC8+134 ADD R5, R5, #0xC Down r sub_863DC8+150 MOV R0, R5; buf Down r sub_863DC8+160 MOV R0, R5 Down r sub_863DC8+16C MOV R0, R5; buf Down r sub_863DC8+17C MOV R0, R5; buf Down r sub_863DC8+18C MOV R0, R5; buf Down w sub_863DC8+1B4 ADD R5, R4, #0x21 ; '!' Down r sub_863DC8+1C8 CMP R4, R5 Down w sub_863DC8+214 ADD R5, SP, #0xD30+var_930 Down w sub_863DC8+218 ADD R5, R5, #0xC Down r sub_863DC8+224 MOV R2, R5 Down r sub_863DC8+250 MOV R0, R5; in Down w sub_863DC8+344 MOV R5, R0 -------------------------------------------------------------------------------------- 第1列中"-"表示当前地址,"Up"表示位于当前地址低址方向,"Down"表示位于当前地 址高址方向。 第2列中"r/w"表示"读/写"目标寄存器(R5)。 第3列是地址,后面是汇编指令。 在Python命令行中执行: localxrefs.highlight() 在汇编窗口高亮当前函数中所有操作R5寄存器的指令 localxrefs.unhighlight() 撤销当前函数中指令高亮效果 ☆ 参考资源 [1] IDA F.L.I.R.T. Technology: In-Depth https://www.hex-rays.com/products/ida/tech/flirt/in_depth.shtml [2] Snowman https://github.com/yegord/snowman [3] IdaRef https://github.com/nologic/idaref [4] IDA Signsrch https://sourceforge.net/projects/idasignsrch/ http://aluigi.altervista.org/mytoolz/signsrch.zip http://aluigi.altervista.org/mytoolz/signsrch.sig.zip (特征数据库) [5] HexRaysCodeXplorer https://github.com/REhints/HexRaysCodeXplorer [6] IDB2PAT https://github.com/fireeye/flare-ida/blob/master/python/flare/idb2pat.py http://www.openrce.org/downloads/details/26/IDB_2_PAT (C版本) [7] lscan https://github.com/maroueneboubakri/lscan Community driven collection of IDA FLIRT signature https://github.com/Maktm/FLIRTDB https://github.com/push0ebp/sig-database (没有arm的) [8] rizzo https://github.com/devttys0/ida/tree/master/plugins/rizzo A Code Signature Plugin for IDA - Craig [2014-10-11] http://www.devttys0.com/2014/10/a-code-signature-plugin-for-ida/ [10] syms2elf https://github.com/danigargu/syms2elf [12] Diaphora - Joxean Koret https://github.com/joxeankoret/diaphora Analyzing MS15-050 With Diaphora - Alex Ionescu [2015-05-14] http://www.alex-ionescu.com/?p=271 [13] localxrefs https://github.com/devttys0/ida/tree/master/plugins/localxrefs [15] HexRaysPyTools https://github.com/igogo-x86/HexRaysPyTools