前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[系统安全] 五十五.恶意软件分析 (7)IDA Python基础用法及CFG控制流图提取详解[上]

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

作者头像
Eastmount
发布2024-02-01 17:02:34
3790
发布2024-02-01 17:02:34
举报

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

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

作者的github资源:

  • 逆向分析:
    • https://github.com/eastmountyxz/ SystemSecurity-ReverseAnalysis
  • 网络安全:
    • https://github.com/eastmountyxz/ NetworkSecuritySelf-study

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

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


一.测试样本生成

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

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

代码语言:javascript
复制
#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手动提取CFG

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

1.IDA概述

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

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

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

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

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

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

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

  • v3 = sub_401005(v7, v6);

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

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


2.IDA手动保存CFG

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

显示结果如下图所示:

  • 为啥显示这么复杂呢?
  • 如何提取关键函数的调用关系呢?

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

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

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


3.GDL转换为图片格式

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

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

示例代码:

代码语言:javascript
复制
from easygraph.datasets import get_graph_karateclub
import easygraph as eg
G = get_graph_karateclub()
# Calculate five shs(Structural Hole Spanners) in G
shs = eg.common_greedy(G, 5)
# Draw the Graph, and the shs is marked by red star
eg.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
  • 恶意功能定位 - 刘师妹

三.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

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


2.IDA如何运行Python代码

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

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

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

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

关键代码如下:

代码语言:javascript
复制
Python>DataRefsTo(here())
<generator object refs at 0x00000197D0679B48>
Python>import idautils
Python>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
代码语言:javascript
复制
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 关键函数所在库

代码语言:javascript
复制
##################################
# 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)  # 调用上面的函数之后可以一直调用直到返回-1
get_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位置的字符串,估计这个库在写的时候漏了一个l
print_insn_mnem(ea)       # 打印操作码,实用的多,要是一个地址没有数据,那么就会返回空字符串(即'',而不是None)
print_operand(ea, n)      # 打印操作数,n从0开始(第0个不是操作码,而是第一个操作数),超出下标的是空字符串''

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

代码语言:javascript
复制
from idautils import Names, Strings, Functions
for 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程序

代码语言:javascript
复制
from idc import GetDisasm, print_insn_mnem, get_operand_value
from ida_bytes import next_head
from ida_bytes import patch_byte
start = 0x798
end = 0xf67
ea = start
while 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脚本批量处理程序

代码语言:javascript
复制
import os
import subprocess
IDA_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脚本传入参数

代码语言:javascript
复制
# 执行的时候按照下面的格式
# 传入的参数应该是命令行能识别的数据,如果按照上面那个批量脚本传一些python数据结构似乎是不行的
# ida -c -A -S"script_path argv[1] argv[2] argv[3]" filepath

# 在脚本文件中
import idc
def 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打开指定脚本,比如获取当前程序的函数列表文件。

其代码如下:

代码语言:javascript
复制
from idaapi import *

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

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

接下来,如何利用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自动生成控制流图
  • 五.总结
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 娜璋AI安全之家 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.测试样本生成
  • 二.IDA手动提取CFG
    • 1.IDA概述
      • 2.IDA手动保存CFG
        • 3.GDL转换为图片格式
        • 三.IDA Python基础用法
          • 1.官方文档介绍
            • 2.IDA如何运行Python代码
              • 3.基础用法
                • 4.他山之石
                • 四.IDA Python自动生成控制流图
                • 五.总结
                相关产品与服务
                Elasticsearch Service
                腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档