“
2024年4月28日是Eastmount的安全星球 —— 『网络攻防和AI安全之家』正式创建和运营的日子,该星球目前主营业务为 安全零基础答疑、安全技术分享、AI安全技术分享、AI安全论文交流、威胁情报每日推送、网络攻防技术总结、系统安全技术实战、面试求职、安全考研考博、简历修改及润色、学术交流及答疑、人脉触达、认知提升等。下面是星球的新人券,欢迎新老博友和朋友加入,一起分享更多安全知识,比较良心的星球,非常适合初学者和换安全专业的读者学习。
”
该系列文章将系统整理和深入学习系统安全、逆向分析和恶意代码检测,文章会更加聚焦,更加系统,更加深入,也是作者的慢慢成长史。漫漫长征路,偏向虎山行。享受过程,一起奋斗~
前文介绍了IDA Python配置过程和基础用法。这篇文章将尝试利用IDA Python提取恶意软件的控制流图(CFG),再为后续的恶意软件家族分类或溯源提供帮助。由于作者是初学者,因此会遇到很多问题,欢迎各位大佬和读者指导,同时感谢李师弟的指导和交流。基础性基础,且看且珍惜。
文章目录:
一.测试样本生成
二.IDA手动提取CFG
1.IDA概述
2.IDA手动保存CFG
三.IDA Python提取CFG
1.函数提取
2.样本提取
四.CFG提取问题
五.总结
作者的github资源:
逆向分析:
https://github.com/eastmountyxz/
SystemSecurity-ReverseAnalysis
网络安全:
https://github.com/eastmountyxz/
NetworkSecuritySelf-study
声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。(参考文献见后)
首先,我们编写一段C语言程序并生成对应的可执行文件。如下图所示:
该程序逻辑比较简单,包含一个条件语句、一个循环语句和一个函数调用。
`#include <stdio.h>`` ``int sub_num(int a, int b) {` `int s;` `s = a + b;` `printf("函数运算结果: %d\n",s);` `return s;``}`` ``int main() {` `int i,m,n;` `int result=0;`` ` `scanf("%d %d",&m,&n);` `printf("输入的数字为:%d %d",m,n);`` ` `//条件语句` `if (m>10) {` `printf("数字大于10\n");` `}` `else {` `printf("数字小于等于10\n");` `}`` ` `//循环语句` `for (i=0; i<=10; i++) {` `result += i;` `i++;` `}` `printf("1 + 2 + ... + 10 = %d\n",result);` ` //函数` `result = result + sub_num(m,n);` `printf("最终输出结果: %d\n",result);` `return 0;``}`
输出结果如下图所示:
编译生成的中间文件如下所示:
接着介绍IDA Python配置过程,并讲解如何实现IDA手动提取控制流图(CFG)。
IDA Pro(Interactive Disassembler Professional)简称“IDA”,是Hex-Rays公司出品的一款交互式反汇编工具,是目前最棒的一个静态反编译软件,为众多0day世界的成员和ShellCode安全分析人士不可缺少的利器。IDA Pro具有强大的功能,但操作较为复杂,需要储备很多知识,同时,它具有交互式、可编程、可扩展、多处理器等特点,可以通过Windows或Linux、MacOS平台来分析程序, 被公认为最好的逆向工程利器之一。
第一步,打开IDA Pro32软件选择一个exe文件载入,它将是我们要进行分析的程序,所分析的程序时上面撰写的C语言代码。
IDA反汇编包括两个阶段,首先将程序的代码和数据分开,分别标记函数并分析参数调用、跳转、指令关系等;接着如果IDA能识别文件的编译类型,就装载对应的编译器特征文件,给各函数赋名。同时,IDA会创建一个数据库,其组件分别保存在“.id0”、“.id1”、“.nam”和“.til”的文件里。
第二步,在弹出确认窗口中选择“Don’t show this message again”选项,在“Check for Hex-Rays product updates”中点击“OK”。运行结果如下图所示,接着可以开始我们的逆向分析。
第三步,选择“_main_0”函数查看程序的控制流图,下图可以看到代码及部分注释。
第四步,按下F5能够查看对应的源代码,另一个自定义函数为,可以查看其加分操作。
第五步,可以在Graph View和Text View中切换。
最后,关闭IDA Pro并保存数据库文件,下次载入时,可以直接加载数据库文件,获取之前分析的状态。
函数调用图
在菜单栏中点击“view–>graphs–>Function calls”,查看函数调用图。
显示结果如下图所示:
为啥显示这么复杂呢?
如何提取关键函数的调用关系呢?
函数流程图
在菜单栏中点击“view–>graphs–>flowt chart”,查看函数流程图,其显示效果与IDA自带的反汇编流程视图相似。
在WinGraph中点击“file–>save as”,将调用关系另存为GDL(graph discription language)文本为test01.gdl。
保存的文件结果显示如下:
该部分感谢李师弟的帮助,通过IDA Python批量提取样本的CFG,并生成gdl文件。整个工作目录如下所示:
`- get_cfg.py``- get_cfg_main.py``- get_sample_cfg.py``- sample` `- test01.exe`
首先定义主函数,通过主函数调用IDA软件和样本的路径,并构建analyse_module函数分析样本的CFG。关键代码如下所示:
`# coding: utf-8``# By:Eastmount & LJC 2024-05-10``import os`` ``#完整路径跨目录调用``IDA_PATH = r"C:\Software\IDAPro7.5\ida.exe"``IDA64_PATH = r"C:\Software\IDAPro7.5\ida64.exe"``analyser = r"D:\test_cfg\get_cfg.py"`` ``#命令行脚本批量获取样本cfg``def analyse_module(sample_list, sample_name):` `ida_exe = IDA_PATH` `for i in range(len(sample_list)):` `sample = sample_list[i]` `name = sample_name[i]` `cmd = " ".join([ida_exe, f"-c -A -S" + '"' + analyser + ' ' + name + '"', sample])` `print(cmd)` `os.system(cmd)`` ` `return True`` ``if __name__ == '__main__':` `#批量样本地址` `file_path = r'D:\test_cfg\sample'` `sample_name = os.listdir(file_path)` `sample_list = []` `for i in range(len(sample_name)):` `sample = file_path + '\\' + sample_name[i]` `print(sample)` `sample_list.append(sample)`` ` `analyse_module(sample_list, sample_name)` `print("over!!!")`
接着调用get_cfg.py文件分别提取函数CFG信息,关键代码如下所示:
`#coding: utf-8``# By:Eastmount & LJC 2024-05-10``import idaapi``import ida_gdl``import idautils``import idc`` ``def main():`` ` `functions = idautils.Functions()` ` for func_ea in functions:` `func_name = idc.get_func_name(func_ea)` `func = idaapi.get_func(func_ea)` `start_ea = idc.get_func_attr(func_ea, FUNCATTR_START)` `end_ea = idc.get_func_attr(func_ea, FUNCATTR_END)` `gdl_file_name = "CFG_{}.gdl".format(func_name)` `flowchart = idc.gen_flow_graph(gdl_file_name, "cfg", start_ea, end_ea, CHART_GEN_GDL)` `if flowchart:` `print("CFG for function {} has been saved as {}".format(func_name, gdl_file_name))` `else:` `print("Failed to save CFG for function {}".format(func_name))`` ``if __name__ == "__main__":` `idaapi.auto_wait()` `main()` `idc.qexit(0)`
运行结果如下图所示:
该程序成功提取了各函数的CFG并生成gdl文件,然后部分函数是系统自带(未被使用)也被提取。
接着调用get_sample_cfg.py文件分别提取样本的CFG信息,核心思想是捕获最大地址和最小地址之间的内容,关键代码如下所示:
`#coding: utf-8``# By:Eastmount & LJC 2024-05-10``import idautils``import ida_gdl``import idaapi``import idc``import time`` ``#根据样本最小地址和最大地址生成整个样本的cfg``def main(name):` `start_ea = ida_ida.inf_get_min_ea()` `end_ea = ida_ida.inf_get_max_ea()` `print(start_ea)` `print(end_ea)` `filename = '{}_cfg.gdl'.format(name)` `flowchart = idc.gen_flow_graph(filename, "cfg", start_ea, end_ea, CHART_GEN_GDL)` `print(flowchart)` `time.sleep(5)`` ``if __name__ == "__main__":` `idaapi.auto_wait()` `main(idc.ARGV[1])` `idc.qexit(0)`
运行结果如下图所示:
并且生成和保存两个文件,文件内容如下所示。
test01.exe.idb
test01.exe_cfg.gdl
至此,成功利用IDA Python提取样本的CFG。
然后当前代码存在几个问题:
如下图所示,IDA Python提取并生成的gdl文件远远大于导出的文件,并且包含大量系统函数,这些冗余信息会干扰实验分析。
test01.exe.idb
test01.exe_cfg.gdl(929KB):本文Python代码提取
test01.gdl(3.92KB):IDA手工导出
手工导出gdl文件。
写到这里,这篇文章就介绍完毕,希望对您有所帮助。
一.测试样本生成
二.IDA手动提取CFG
1.IDA概述
2.IDA手动保存CFG
三.IDA Python提取CFG
1.函数提取
2.样本提取
四.CFG提取问题
五.总结
『网络攻防和AI安全之家』目前收到了很多博友、朋友和老师的支持和点赞,尤其是一些看了我文章多年的老粉,购买来感谢,真的很感动,类目。未来,我将分享更多高质量文章,更多安全干货,真心帮助到大家。虽然起步晚,但贵在坚持,像十多年如一日的博客分享那样,脚踏实地,只争朝夕。继续加油,再次感谢!
(By:Eastmount 2024-05-13 夜于贵阳 )
参考文献如下,感谢师弟师妹们的帮助和耐心解答。
[1] 《The Beginner’s Guide to IDAPython》 by Alexander Hanel
[2] 项目地址:https://github.com/idapython
[3] 官方IDA Python手册:https://www.hexrays.com/products/ida/support/idapython\_docs/
[4] 《IDA pro权威指南》
[5] [安全工具][原创]保存IDA Pro中生成的函数调用关系(图)- GreatDane
[6] Easygraph:全面高效的图分析与社会计算开源工具 - PeppaRan
[7] Genius 二进制文件函数特征提取的复现 - Erio
[8] IDA Python安装与使用 - 17bdw
[9] ida运行python脚本 - 51cto
[10] 恶意功能定位 - 刘师妹
[11] 比较简单的IDAPython脚本 - 知乎
前文回顾(下面的超链接可以点击喔):
[系统安全] 五十六.恶意软件分析 (8)IDA Python基础用法及CFG控制流图提取详解[下]