[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

感谢2023年的陪伴,2024年继续加油^_^

该系列文章将系统整理和深入学习系统安全、逆向分析和恶意代码检测,文章会更加聚焦,更加系统,更加深入,也是作者的慢慢成长史。漫漫长征路,偏向虎山行。享受过程,一起奋斗~

前文介绍了软件来源分析,结合网络攻击中常见的判断方法,利用Python调用扩展包进行区域溯源。这篇文章将开启IDA Python学习,首先介绍IDA Python配置过程和基础用法,然后尝试地区恶意软件的控制流图(CFG),再为后续的恶意软件家族分类或溯源提供帮助。由于作者是初学者,因此会遇到很多问题,欢迎各位大佬和读者指导。基础性基础,且看且珍惜。

文章目录:

  • 一.测试样本生成

  • 二.IDA手动提取CFG

    • 1.IDA概述

    • 2.IDA手动保存CFG

    • 3.GDL转换为图片格式

  • 三.IDA Python基础用法

    • 1.官方文档介绍

    • 2.IDA如何运行Python代码

    • 3.基础用法

    • 4.他山之石

  • 四.IDA Python自动生成控制流图

  • 五.总结


作者的github资源:

  • 逆向分析:

    • https://github.com/eastmountyxz/

      SystemSecurity-ReverseAnalysis

  • 网络安全:

    • https://github.com/eastmountyxz/

      NetworkSecuritySelf-study


作者作为网络安全的小白,分享一些自学基础教程给大家,主要是关于安全工具和实践操作的在线笔记,希望您们喜欢。同时,更希望您能与我一起操作和进步,后续将深入学习网络安全和系统安全知识并分享相关实验。总之,希望该系列文章对博友有所帮助,写文不易,大神们不喜勿喷,谢谢!如果文章对您有帮助,将是我创作的最大动力,点赞、评论、私聊均可,一起加油喔!

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]


声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。(参考文献见后)


一.测试样本生成

首先,我们编写一段C语言程序并生成对应的可执行文件。如下图所示:

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

该程序逻辑比较简单,包含一个条件语句、一个循环语句和一个函数调用。

#include <stdio.h>
int sub_num(int a, int b) { int s; s = a + b; printf("函数运算结果: %dn",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("数字大于10n"); } else { printf("数字小于等于10n"); }
//循环语句 for (i=0; i<=10; i++) { result += i; i++; } printf("1 + 2 + ... + 10 = %dn",result); //函数 result = result + sub_num(m,n); printf("最终输出结果: %dn",result); return 0;}

输出结果如下图所示:

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

编译生成的中间文件如下所示:

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]


二.IDA手动提取CFG

接着介绍IDA Python配置过程,并讲解如何实现IDA手动提取控制流图(CFG)。

1.IDA概述

IDA Pro(Interactive Disassembler Professional)简称“IDA”,是Hex-Rays公司出品的一款交互式反汇编工具,是目前最棒的一个静态反编译软件,为众多0day世界的成员和ShellCode安全分析人士不可缺少的利器。IDA Pro具有强大的功能,但操作较为复杂,需要储备很多知识,同时,它具有交互式、可编程、可扩展、多处理器等特点,可以通过Windows或Linux、MacOS平台来分析程序, 被公认为最好的逆向工程利器之一。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第一步,打开IDA Pro32软件选择一个exe文件载入,它将是我们要进行分析的程序,所分析的程序时上面撰写的C语言代码。

  • 在“Load a new file”窗口中选择装载PE文件,包括text(代码块)、data(数据块)、rsrc(资源块)、idata(输入表)和edata(输出表)等,也可以载入二进制文件。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

IDA反汇编包括两个阶段,首先将程序的代码和数据分开,分别标记函数并分析参数调用、跳转、指令关系等;接着如果IDA能识别文件的编译类型,就装载对应的编译器特征文件,给各函数赋名。同时,IDA会创建一个数据库,其组件分别保存在“.id0”、“.id1”、“.nam”和“.til”的文件里。

第二步,在弹出确认窗口中选择“Don’t show this message again”选项,在“Check for Hex-Rays product updates”中点击“OK”。运行结果如下图所示,接着可以开始我们的逆向分析。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第三步,选择“_main_0”函数查看程序的控制流图,下图可以看到代码及部分注释。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第四步,按下F5能够查看对应的源代码,另一个自定义函数为,可以查看其加分操作。

  • v3 = sub_401005(v7, v6);

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第五步,可以在Graph View和Text View中切换。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

最后,关闭IDA Pro并保存数据库文件,下次载入时,可以直接加载数据库文件,获取之前分析的状态。


2.IDA手动保存CFG

函数调用图
在菜单栏中点击“view–>graphs–>Function calls”,查看函数调用图。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

显示结果如下图所示:

  • 为啥显示这么复杂呢?

  • 如何提取关键函数的调用关系呢?

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

函数流程图
在菜单栏中点击“view–>graphs–>flowt chart”,查看函数流程图,其显示效果与IDA自带的反汇编流程视图相似。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

在WinGraph中点击“file–>save as”,将调用关系另存为GDL(graph discription language)文本为test01.gdl。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

保存的文件结果显示如下:

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]


3.GDL转换为图片格式

读者可以尝试安装EasyGraph扩展包,将GDL转换为DOT格式,或利用GraphViz绘制图片。

  • EasyGraph是复旦大学MSN小组开发的一款基于Python语言的图计算开源包

示例代码:

from easygraph.datasets import get_graph_karateclubimport easygraph as egG = get_graph_karateclub()# Calculate five shs(Structural Hole Spanners) in Gshs = eg.common_greedy(G, 5)# Draw the Graph, and the shs is marked by red stareg.draw_SHS_center(G, shs)# Draw CDF curves of "Number of Followers" of SH spanners and ordinary users in G.eg.plot_Followers(G, shs)

如何实现如下效果呢?推荐文章:

  • [安全工具][原创]保存IDA Pro中生成的函数调用关系(图)- GreatDane

  • Genius 二进制文件函数特征提取的复现 – Erio

  • 恶意功能定位 – 刘师妹

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]


三.IDA Python基础用法

下面介绍IDA Python的基础用法。

1.官方文档介绍

推荐读者学习IDA Python官方文档,尤其是需要了解和查询某个函数的用法时。

  • https://github.com/idapython

  • https://www.hexrays.com/products/ida/support/idapython_docs/

  • 《The Beginner’s Guide to IDAPython》 by Alexander Hanel

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

下面官网中包含各种API以及返回值数据类型,都还蛮详细的,推荐大家学习。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]


2.IDA如何运行Python代码

IDA Python是IDA6.8后自带插件,可以使用Python做很多的辅助操作。作者所使用的IDA版本为7.5,其支持Python3(7.0往前只支持Python2)。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第一步,在IDA中下部的Output Window中有个终端界面,包含python终端和IDC终端。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

比如,我们输入Python代码即可打印,如下所示:

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

第二步,我们尝试调用IDA Python并输出代码对样本进行分析。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

关键代码如下:

Python>DataRefsTo(here())<generator object refs at 0x00000197D0679B48>Python>import idautilsPython>idautils.DataRefsTo(here())<generator object refs at 0x00000197D0679B48>

提问:如何在IDA中调用Python脚本呢?并且实现批量处理。


3.基础用法

IDAPython创建于2004年,其是Gergely Erdelyi和 Ero Carrera的共同努力结果。他们的目标是将Python的强大功能与IDA的IDC C类脚本语言的分析自动化结合起来。IDA Python由三个独立的模块组成。

  • idc:负责提供IDA中的所有函数功能
  • idautils:负责提供大量实用函数
  • idaapi:负责访问核心IDA API
Python>info = idaapi.get_inf_structure()Python>print(info)<ida_ida.idainfo; proxy of <Swig Object of type 'idainfo *' at 0x000001C3E0E48330> >

推荐大家阅读这部书《The Beginner’s Guide to IDAPython —— Alexander Hanel》 。比如:

  • idc.ScreenEA()函数或here()函数:获取当前光标所在地址

  • idc.SegName(ea):获取当前地址所在的段(segment)名称

  • idc.GetDisasm(ea):获取当前地址的反汇编语句

  • idc.GetMnem(ea):获取当前反汇编语句的操作符

  • idc.GetOpnd:获取操作数

  • idaapi.MinEA():获取载入程序的最小的有效地址

  • idaapi.MaxEA():获取载入的程序最大的有效地址


4.他山之石

下面补充CFT师弟的IDA笔记供大家学习,非常不错的记录,在此感谢。

(1) idapython python3 关键函数所在库

################################### idaapi##################################get_inf_structure() # info = get_inf_structure(), info.is32bit(), info.procName是处理器架构FlowChart(get_func(ea)) #################################### idautils##################################Functions() # 列出所有的函数,返回的是一个迭代器,其中的所有元素都是函数起始地址Strings() # 列出所有字符串,每个字符串都有ea, length和strtype属性Names() # 所有的name,包括函数名,字符串名################################### ida_auto##################################auto_wait() # 批量处理脚本时用的,等待自动分析完成再进行分析,尽可能加,否则脚本可能无法完整执行################################### ida_bytes##################################ida_bytes.get_byte(ea) # 获取一个字节ida_bytes.get_word(ea) # 获取两个字节ida_bytes.get_dword(ea) # 获取四个字节# 修改数据类型,相当于手动操作时在对应位置按"D"# 这里也可以循环操作,比如一大块内存都是通过bytes表示的,需要换成8bit、16bit或32bit表示可以这样create_byte(ea, 1)create_16bit_data(ea, 2) # 第二个参数搞不懂有啥用,就按字节数保持不变通过循环实现吧create_32bit_data(ea, 4)################################### ida_funcs##################################get_func(ea) # 返回当前地址对应的函数的一些信息,其中start_ea是起始地址,end_ea是结束地址get_func_name(ea) # 获取这个地址对应的函数名################################### ida_nalt##################################get_root_filename() # 获取当前二进制文件的名字get_imagebase() # 获取加载基址################################### ida_pro##################################qexit(code) # 退出当前界面,相当于exit################################### ida_search##################################find_text(ea, 0, 0, string, ida_search.SEARCH_DOWN) # ea是地址,string是要找的字符,其它的不动就行# 要跳到下一个位置需要如此更新: ea = idc.next_head(ea)################################### ida_ua##################################create_insn(ea) # 将ea开始的数据转化为代码,注意得先用create_byte把所有数据都转成byte,否则不能用################################### ida_xref##################################get_first_fcref_to(addr) # 找到第一个引用addr的地址,没有则返回-1(也可能是BADADDR=2^32 - 1)get_next_fcref_to(addr)  # 调用上面的函数之后可以一直调用直到返回-1get_first_cref_to(addr)  # 找第一个代码引用get_next_cref_to(addr)get_first_dref_to(addr)  # 找第一个数据引用get_next_dref_to(addr)################################### idc##################################find_func_end(ea)         # 给定一个IDA能够识别的函数的开头,返回函数结尾prev_head(ea)             # ea位置上一条指令的地址(只能看相邻的地址,不能看跳转过来的位置)next_head(ea)             # 下一条指令的地址get_operand_type(ea, n)   # ea是指令地址,n代表第几个参数,感觉这个没啥用get_operand_value(ea, n)  # 获取参数的值get_strlit_content(ea)    # 获取ea位置的字符串,估计这个库在写的时候漏了一个lprint_insn_mnem(ea)       # 打印操作码,实用的多,要是一个地址没有数据,那么就会返回空字符串(即'',而不是None)print_operand(ea, n)      # 打印操作数,n从0开始(第0个不是操作码,而是第一个操作数),超出下标的是空字符串''

(2) 获取所有函数的名字

from idautils import Names, Strings, Functionsfor name in Names():  for string in Strings():    if int(name[0]) = int(string.ea):      print(hex(name[0]),str(name[1]),str(string))

(3) patch程序

from idc import GetDisasm, print_insn_mnem, get_operand_valuefrom ida_bytes import next_headfrom ida_bytes import patch_bytestart = 0x798end = 0xf67ea = startwhile ea < end:  old_ea = ea  instr1 = GetDisasm(ea)  op1 = print_insn_mnem(ea)  ea = next_head(ea, end)  instr2 = GetDisasm(ea)  op2 = print_insn_mnem(ea)  if (op1 = 'jb' and op2 = 'jnb') or (op1 = 'jnb' and op2 = 'jb'):    value1 = get_operand_value(old_ea, 0)    value2 = get_operand_value(ea, 0)    if value1 = value2:      print(hex(old_ea), instr1)      print(hex(ea), instr2)      print(hex(value1), hex(value2))      print('-' * 30)      old_ea = next_head(ea, end)      ea = value1            for i in range(old_ea, ea):      patch_byte(i, 0x90) # 填充对应位置的数据

(4) ida脚本批量处理程序

import osimport subprocessIDA_PATH = "E:\Apps\IDAPro7.5\ida64.exe"PLUGIN_PATH = ".\get_end.py"
# 获取所有需要分析的二进制文件路径ELF_PATH = 'E:\feature-extractor\heart\'elfs = os.listdir(ELF_PATH)print('number of elfs:', len(elfs))for elf in elfs: elf_path = ELF_PATH + elf cmd = IDA_PATH + " -c -A -S" + PLUGIN_PATH + " " + elf_path subprocess.call(cmd)

(5) ida脚本传入参数

# 执行的时候按照下面的格式# 传入的参数应该是命令行能识别的数据,如果按照上面那个批量脚本传一些python数据结构似乎是不行的# ida -c -A -S"script_path argv[1] argv[2] argv[3]" filepath
# 在脚本文件中import idcdef main(A, B, C): pass if _ name _ = " _ main _ ": idc.Wait() main(idc.ARGV[1], idc.ARGV[2], idc.ARGV[3]) idc.Exit(0)


四.IDA Python自动生成控制流图

如何打开Python脚本进行批量处理呢?
在File中通过Script file打开指定脚本,比如获取当前程序的函数列表文件。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

其代码如下:

from idaapi import *
#获取当前程序的函数列表functions = [get_func_name(x) for x in Functions()]for function in functions: print(function)

运行结果如下图所示,函数名称被获取。

[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

接下来,如何利用IDA Python自动生成恶意样本的控制流图呢?我们将在下一篇博客中介绍,将继续探索。同时存在问题包括,感谢与师弟师妹们的探讨。

  • IDA Python如何批量将指定文件夹中样本转换为CFG,为后续家族分类提供支撑。
  • IDA能像IPython一样编辑代码的同时运行调试吗?dbg比较困难。
  • IDA提取的CFG存储究竟存储为什么格式,是json文件吗?又如何转换为图片呢?networkx库有啥用?
  • 如何恢复CFG中不联通的图?
  • 如何利用miasm提取CFG呢?据说其IR比较方便。
  • IDA和angr提取CFG的区别及优缺点?


    – angr适合大批量处理数据,但感觉angr没有ida准确
    – 构建cfg我用angr自带的有个函数——cfgfast或cfgemulate
    – angr的符号执行没有那么好用,更多的还是把他当一个分析工具,很容易就内存爆炸
    – angr可能会添加一些不存在的边,比如说你的一个函数a调用了一个函数b,那么函数b执行完之后会返回到a的函数体里面,然后如果同样的a也用这种方式调用了c的话,那c也会返回的a的函数体里面,这样就造成它就会添加一个b跟c之间的通路,但是b跟c之间可能本来是没有通路的,所以说这种路径你就要把它去掉


五.总结

写到这里,这篇文章就介绍完毕,希望对您有所帮助。

  • 一.测试样本生成

  • 二.IDA手动提取CFG
    1.IDA概述
    2.IDA手动保存CFG
    3.GDL转换为图片格式

  • 三.IDA Python基础用法
    1.官方文档介绍
    2.IDA如何运行Python代码
    3.基础用法
    4.他山之石

  • 四.IDA Python自动生成控制流图

  • 五.总结

感谢大家2023年的支持和关注,让我们在2024年继续加油!分享更多好文章,感恩,娜璋白首。

(By:Eastmount 2024-01-30 夜于火星)


参考文献如下,感谢师弟师妹们的帮助和耐心解答。

  • [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脚本 – 知乎

前文回顾(下面的超链接可以点击喔):

原文始发于微信公众号(娜璋AI安全之家):[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

相关文章