继续我们的主题—使用IDAPython 让逆向工程师的生活变得更美好。

这一部分,我们将着手处理一个非常常见的问题:shellcode和恶意软件使用hash算法混淆加载的函数和链接库,这项技术被广泛使用。使用IDAPython,我们能够很容易的解决这个很有挑战性的问题。

背景

逆向人员经常在shellcode里遇到名字被混淆的函数。 总体来说这个过程比较简单:代码运行之后会在初始化阶段加载kernel32.dll链接库,然后继续用这个加载的映像来标识和存储LoadLibraryA 的函数,而这个函数又会去加载附加的链接库和函数 。

这项特殊的技术使用hash算法来标识一个函数,该hash算法一般使用CRC32, 其他变种中ROR13也经常被使用。

在逆向恶意软件的过程中,我们找到下面这段代码:

在上面的例子中, 我们可以通过找到包含0xEDB88320来快速标识出使用CRC32算法的地方:

现在算法和函数已经被标识出来了,我们可以使用交叉引用来看看这个函数被调用了多少次。

在这个特殊的样本中,这个函数被调用了190次。而我们不想手动的去解码这些hash值,那么像上一部分一样,我们可以使用IDAPython来简化我们的工作。

编写IDAPython脚本

第一步其实没有使用到IDAPython, 但是用到了Python。 为了标识出hash值与函数的匹配关系,我们需要生成一张微软的windows操作系统最常使用函数的hash值表。为了达成这个目的,我们可以抓取windows操作系统常用的链接库列表,然后遍历里面的函数。

def get_functions(dll_path):
  pe = pefile.PE(dll_path)
  if ((not hasattr(pe, 'DIRECTORY_ENTRY_EXPORT')) or (pe.DIRECTORY_ENTRY_EXPORT is None)):
    print "[*] No exports for %s" % dll_path
    return []
  else:
    expname = []
    for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
      if exp.name:
        expname.append(exp.name)
    return expname

我们可以用下面代码来计算这些函数的CRC32的值。

def calc_crc32(string): 
  return int(binascii.crc32(string) & 0xFFFFFFFF)

最后,我们将结果写道JSON格式的文件中,命名为output.json。这些JSON数据包含了一个很大的字典,格式如下:

HASH => NAME

完成的脚本代码下载链接

https://github.com/pan-unit42/public_tools/blob/master/ida_scripts/gen_function_json.py

生成了这个文件之后,我们回到IDA中,剩下的脚本将会在这里完成。 首先我们的脚本会从之前生成的output.json文件中读取JSON数据。 不幸的是,JSON对象不支持整数型的键值,所以数据加载之后,我们将键值转换为整数类型来替代字符串类型。

for k,v in json_data.iteritems():
  json_data[int(k)] = json_data.pop(k)

数据完全载入后,我们创建一个新的枚举类型(enumeration)来存储‘hash值-函数名’的映射。(更多关于的枚举类型的资料与工作原理,可以阅读下面的这个教程

http://www.cprogramming.com/tutorial/enum.html)

使用枚举类型,我们能够将一个整数值(如CRC32)与字符串(如函数名)建立映射关系。为了在IDA中建立新的枚举,我们将使用到AddEnum()函数。为了让脚本能处理更多的情况,我们先使用GetEnum()函数来校验枚举是否已经存在。

enumeration = GetEnum("crc32_functions")
if enumeration == 0xFFFFFFFF:
  enumeration = AddEnum(0, "crc32_functions", idaapi.hexflag())

这个枚举以后会被修改。 下一步就是找到hash转换函数的交叉引用。这个跟第一部分提到的很相似。当分析参数如何传递到函数中时, 我们可以看到CRC32格式的hash值被作为第二个参数。

我们将遍历函数调用前面的指令,找到第二个push指令的使用处。一旦找到,我们将会将CRC32的hash值与之前从output.json里面读取出来的JSON数据进行匹配,看是否有匹配的函数名。

for x in XrefsTo(load_function_address, flags=0):
    current_address = x.frm
    addr_minus_20 = current_address-20
    push_count = 0
    while current_address >= addr_minus_20:
      current_address = PrevHead(current_address)
      if GetMnem(current_address) == "push":
        push_count += 1
        data = GetOperandValue(current_address, 0)
        if push_count == 2:
          if data in json_data:
            name = json_data[data]

在下面这个步骤中,我们使用 AddConstEx()函数将CRC32 hash值与函数名加入到之前创建的枚举中。

AddConstEx(enumeration, str(name), int(data), -1)

一旦数据被加入到枚举中, 我们就能够将CRC32 hash值转换为对应的枚举名。 下面两个函数能够用来获取枚举的第一个实例,以及获取转换为枚举的数据的地址。

def get_enum(constant):
  all_enums = GetEnumQty()
  for i in range(0, all_enums):
    enum_id = GetnEnum(i)
    enum_constant = GetFirstConst(enum_id, -1)
    name = GetConstName(GetConstEx(enum_id, enum_constant, 0, -1))
    if int(enum_constant) == constant: return [name, enum_id]
    while True:
      enum_constant = GetNextConst(enum_id, enum_constant, -1)
      name = GetConstName(GetConstEx(enum_id, enum_constant, 0, -1))
      if enum_constant == 0xFFFFFFFF:
        break
      if int(enum_constant) == constant: return [name, enum_id]
  return None
  
def convert_offset_to_enum(addr):
  constant = GetOperandValue(addr, 0)
  enum_data = get_enum(constant)
  if enum_data:
    name, enum_id = enum_data
    OpEnumEx(addr, 0, enum_id, 0)
    return True
  else:
    return False

这个枚举转换发生之后,我们将会关注到函数后面DWORD加载的地址。

我们将会从函数开始往后遍历,去找到将eax的值移动到DWORD的指令。找到之后,我们会将DWORD值重命名为函数名。为了避免命名冲突,我们将会使用’d_’来作为名字前缀。

address = current_address
while address <= address_plus_30:
  address = NextHead(address)
  if GetMnem(address) == "mov":
    if 'dword' in GetOpnd(address, 0) and 'eax' in GetOpnd(address, 1):
      operand_value = GetOperandValue(address, 0)
      MakeName(operand_value, str("d_"+name))

将这几个步骤结合在一起,就可以将之前不可读的反汇编代码用更容易理解的形式替换。

当我们查看用来存储这些信息的DWORDs时,我们将会得到下面这样的函数名列表。这些数据可以被用来进行恶意软件的静态分析。

完整的IDAPython 脚本代码:

https://github.com/pan-unit42/public_tools/blob/master/ida_scripts/crc32_conversion.py

总结:

我们又一次用使用IDAPython解决了一个困难的问题(将190处CRC32 hash值转换为有意义的函数名)。枚举结构在这个问题的解决过程中起到了非常重要的作用。IDAPython能够很方便的操作枚举变量,进行创建修改,为我们节省大量的时间。另外的,当我们在逆向其他样本遇到相同问题的时候,这些枚举数据可以导出或导入到IDA项目中。

* 原文链接:researchcenter.paloaltonetworks,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

IDAPython教程(二)的更多相关文章

  1. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  2. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  3. C#微信公众号开发系列教程二(新手接入指南)

    http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...

  4. 无废话ExtJs 入门教程二十一[继承:Extend]

    无废话ExtJs 入门教程二十一[继承:Extend] extjs技术交流,欢迎加群(201926085) 在开发中,我们在使用视图组件时,经常要设置宽度,高度,标题等属性.而这些属性可以通过“继承” ...

  5. 无废话ExtJs 入门教程二十[数据交互:AJAX]

    无废话ExtJs 入门教程二十[数据交互:AJAX] extjs技术交流,欢迎加群(521711109) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3C ...

  6. 无废话ExtJs 入门教程二[Hello World]

    无废话ExtJs 入门教程二[Hello World] extjs技术交流,欢迎加群(201926085) 我们在学校里学习任何一门语言都是从"Hello World"开始,这里我 ...

  7. Android Studio系列教程二--基本设置与运行

    Android Studio系列教程二--基本设置与运行 2014 年 11 月 28 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处! 上面一篇博客,介绍了Studio的 ...

  8. Laravel教程 二:路由,视图,控制器工作流程

    Laravel教程 二:路由,视图,控制器工作流程 此文章为原创文章,未经同意,禁止转载. View Controller 上一篇教程我们走了那么长的路,终于把Laravel安装好了,这一篇教程我们就 ...

  9. Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!

      分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...

  10. NGUI系列教程二

    接下来我们创建一个Label,NGUI->Open the Widget Wizard,打开widgetTool对话框,在Template中选择Label,确定AddTo右侧选项为panel,点 ...

随机推荐

  1. bzoj3427小P的牧场(斜率优化dp)

    小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧 ...

  2. [ZJOI2007]时态同步(dfs+贪心)

    小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3.进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点,都存在且仅 ...

  3. 使用BlockQueue实现生产者和消费者模式

    数据 package cn.lonecloud.procum; /** * @author lonecloud * @version v1.0 * @date 上午11:00 2018/5/7 */ ...

  4. 解决SecureCRT无法用非root账号登录ssh

    原文: http://blog.csdn.net/zxx2403/article/details/46959047 链接失败,提示这个: --------------------------- Sec ...

  5. java ee wildfly 批处理 job 工作

    配置批处理job,同时启动两个并行任务processData,syncTableTask,执行往后执行第三个任务job:playDurationTask. xml配置如下: <job id=&q ...

  6. 2018.02.12 noip模拟赛T2

    二兵的赌注 Description游戏中,二兵要进入了一家奇怪的赌场.赌场中有n个庄家,每个庄家都可以猜大猜小,猜一次一元钱.每一次开彩前,你都可以到任意个庄家那里下赌注.如果开彩结果是大,你就可以得 ...

  7. 【UVA1194】Machine Schedule

    题目大意:给定 N 个任务和两台机器,每个任务可以在任意一台机器上执行,每台机器有 N 个启动状态,不同任务需要机器在不同的状态下执行,求执行所有任务需要多少个不同的状态. 题解:由于一个任务一定要被 ...

  8. 如何在疲劳的JS世界中持续学习

    作者简介 cnfi 蚂蚁金服·数据体验技术团队 本文翻译自<Stay updated in JS fatigue universe>,并对内容有所补充和修改. 部分内容参考<HOW ...

  9. struts2 二: 参数封装

    封装请求正文到对象中(非常重要) 1.静态参数封装 在struts.xml配置文件中,给动作类注入值.调用的是setter方法. struts.xml的编写: <action name=&quo ...

  10. selenium-server-standalone 和selenium-server, selenium-java的区别

    https://stackoverflow.com/questions/30865539/selenium-server-standalone-jar-vs-selenium-java-jar