“2024年4月28日是Eastmount的安全星球 —— 『网络攻防和AI安全之家』正式创建和运营的日子,该星球目前主营业务为 安全零基础答疑、安全技术分享、AI安全技术分享、AI安全论文交流、威胁情报每日推送、网络攻防技术总结、系统安全技术实战、面试求职、安全考研考博、简历修改及润色、学术交流及答疑、人脉触达、认知提升等。下面是星球的新人券,欢迎新老博友和朋友加入,一起分享更多安全知识,比较良心的星球,非常适合初学者和换安全专业的读者学习。 ”
该系列文章将系统整理和深入学习系统安全、逆向分析和恶意代码检测,文章会更加聚焦,更加系统,更加深入,也是作者的慢慢成长史。漫漫长征路,偏向虎山行。享受过程,一起奋斗~
前文介绍了IDA Python配置过程和基础用法。这篇文章将尝试利用IDA Python提取恶意软件的控制流图(CFG),再为后续的恶意软件家族分类或溯源提供帮助。由于作者是初学者,因此会遇到很多问题,欢迎各位大佬和读者指导,同时感谢李师弟的指导和交流。基础性基础,且看且珍惜。
文章目录:
作者的github资源:
声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。(参考文献见后)
首先,我们编写一段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)
运行结果如下图所示:
并且生成和保存两个文件,文件内容如下所示。
至此,成功利用IDA Python提取样本的CFG。
然后当前代码存在几个问题:
如下图所示,IDA Python提取并生成的gdl文件远远大于导出的文件,并且包含大量系统函数,这些冗余信息会干扰实验分析。
手工导出gdl文件。
写到这里,这篇文章就介绍完毕,希望对您有所帮助。
『网络攻防和AI安全之家』目前收到了很多博友、朋友和老师的支持和点赞,尤其是一些看了我文章多年的老粉,购买来感谢,真的很感动,类目。未来,我将分享更多高质量文章,更多安全干货,真心帮助到大家。虽然起步晚,但贵在坚持,像十多年如一日的博客分享那样,脚踏实地,只争朝夕。继续加油,再次感谢!