首页
社区
课程
招聘
[原创]恶意代码分析之 API hash 反静态分析
发表于: 2019-11-11 13:38 7355

[原创]恶意代码分析之 API hash 反静态分析

2019-11-11 13:38
7355
在恶意代码分析中,Windows 平台的 API 函数是非常重要的信息来源,比如通过分析恶意代码中使用的 API 函数,我们不需要对已加壳的文件进行逆向分析,因为我们只需要对恶意代码所执行的 API 调用来进行动态分析,就可以知道某个特定文件具体的功能了。通过这样的方法(分析 API 调用),我们可以确定一个文件是否具有恶意性,而有些 API 调用只有某些特殊类型的恶意软件才会去使用。比如说,常用的恶意 payload 下载 API是 URLDownloadToFile,而GetWindowDC 这个 API 一般用于间谍软件或键盘记录器等恶意工具(用于屏幕截取),因此恶意软件作者会通过使用动态导入的方式隐藏 API 函数名称,避免直接使用静态导入而完全显示出来,动态导入在 shellcode 编写中也非常常见,而本次的 Sodinokibi/REvi 勒索软件隐藏 API 函数案例为 API hash 匹配技巧,完全避免了包含函数名,仅显示函数名称的固定大小的哈希,也节省了恶意代码的存储空间。
近期分析多个勒索软件时,会遇到使用计算 hash 来获取所需 API 函数的流程,这是一种非常常用的反静态分析技巧,本文对此进行学习记录。
静态导入是在 Windows 下由 PE 文件的导入地址表(IAT)中列出的,动态导入是使用 Windows API 提供的 LoadLibrary、GetProcAddress 函数进行导入的。而 API hash 匹配导入,指为每个函数名称计算哈希值,后续通过计算哈希值后经过匹配得到所需的函数地址。
IDA 载入该样本后,整体框架只有两个函数,查询后发现没有任何导入函数,如下:



双击进入第一个函数 sub_40369D,查看如下:


一步一步分析,再接着进入 sub_406A4D,发现有跳转,跟随跳转,如下:



函数一开头就是一个循环,发现 dword_41C9F8[esi]有循环赋值的操作,而 sub_405DCF 则是关键的重建 API 函数地址的函数(后续动态调试会发现),所以首要分析该函数。该处循环每次累加 0x4,直到累加和为 0x230大小时结束,也就是在 dword_41CC24地址处结束。每个 DWORD 值对应一个 API 函数,Sodinokibi/REvi 勒索软件会使用 API hash 解析成相应的 API 函数地址。进入该数组后,发现硬编码的 hash 值内容如下,每个值为 DWORD 类型。
函数一开头就是一个循环,发现 dword_41C9F8[esi]有循环赋值的操作,而 sub_405DCF 则是关键的重建 API 函数地址的函数(后续动态调试会发现),所以首要分析该函数。该处循环每次累加 0x4,直到累加和为 0x230大小时结束,也就是在 dword_41CC24地址处结束。每个 DWORD 值对应一个 API 函数,Sodinokibi/REvi 勒索软件会使用 API hash 解析成相应的 API 函数地址。进入该数组后,发现硬编码的 hash 值内容如下,每个值为 DWORD 类型。


进入上述的 API 构建函数 sub_405DCF 内部,如下:


可以发现存在很多分支结构,有很多判断条件,为了方便,转换成伪 C 代码看看。


函数开头的就对输入参数进行了处理,该函数的输入参数是刚刚硬编码的 DWORD 大小的值,也就是 API hash 值,为了便于分析,对其进行重命名为API_hash_a1。


输入的值首先会与 0x76C7 异或,得到的结果左移 16 位,然后再与 0xAFB9 异或,最后的结果再与之前输入的值进行异或,最终得到 v1。


接着由 v1 右移 21 位得到的 v2,在后续流程里会跟进 v2 的值来到不同的分支,如下:


跟进其中一个分支的函数里,发现又会调用 sub_405DCF,输入参数为硬编码的 DWORD 数值,同样是解码操作。


接着往下分析,遇到了 0x3C 与 0x78(0x18 与 0x60 的和),如果对 PE 文件结构熟悉,就会发现这与寻找导出表有关,因为 dll 文件从加载基址算起,偏移 0x3C 的地方就是其 PE 头,PE 头偏移 0x78 的地方存放着指向函数导出表的指针,如下图,就是寻找 PE 文件的导出表的过程。
接着往下分析,遇到了 0x3C 与 0x78(0x18 与 0x60 的和),如果对 PE 文件结构熟悉,就会发现这与寻找导出表有关,因为 dll 文件从加载基址算起,偏移 0x3C 的地方就是其 PE 头,PE 头偏移 0x78 的地方存放着指向函数导出表的指针,如下图,就是寻找 PE 文件的导出表的过程。


为了便于查看,这里的话我们可以在 IDA 中自建本地类型,构建结构体,如下是 winnt.h 中导出表的定义。
typedef struct _IMAGE_EXPORT_DIRECTORY {
  DWORD Characteristics; //offset 0x0
  DWORD TimeDateStamp; //offset 0x4
  WORD MajorVersion;  //offset 0x8
  WORD MinorVersion; //offset 0xa
  DWORD Name; //offset 0xc
  DWORD Base; //offset 0x10
  DWORD NumberOfFunctions;  //offset 0x14
  DWORD NumberOfNames;  //offset 0x18
  DWORD AddressOfFunctions; //offset 0x1c
  DWORD AddressOfNames; //offset 0x20
  DWORD AddressOfNameOrdinals; //offset 0x24
 }

typedef struct _IMAGE_EXPORT_DIRECTORY {
  DWORD Characteristics; //offset 0x0
  DWORD TimeDateStamp; //offset 0x4
  WORD MajorVersion;  //offset 0x8
  WORD MinorVersion; //offset 0xa
  DWORD Name; //offset 0xc
  DWORD Base; //offset 0x10
  DWORD NumberOfFunctions;  //offset 0x14
  DWORD NumberOfNames;  //offset 0x18
  DWORD AddressOfFunctions; //offset 0x1c
  DWORD AddressOfNames; //offset 0x20
  DWORD AddressOfNameOrdinals; //offset 0x24
 }

在 IDA 中加入 Local Types,如下:





右键选中需要转换的变量进行转换,如下:




上图可以很清楚的显示出导出表结构体里的相应字段,在下面的 while 循环里,通过比较之前获取的 v1 经过计算后的值 v15 与 sub_405BAE 生成的结果,看是否一致,如果一致就会获取到当前的 API 函数的地址(由最后的 return v14 + *(_DWORD *)(v21 + 4 * *(unsigned __int16 *)(v22 + 2 * v16));返回),进入 405BAE 查看,如下:
上图可以很清楚的显示出导出表结构体里的相应字段,在下面的 while 循环里,通过比较之前获取的 v1 经过计算后的值 v15 与 sub_405BAE 生成的结果,看是否一致,如果一致就会获取到当前的 API 函数的地址(由最后的 return v14 + *(_DWORD *)(v21 + 4 * *(unsigned __int16 *)(v22 + 2 * v16));返回),进入 405BAE 查看,如下:


发现存在跳转,双击进入反编译失败。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 2
支持
分享
最新回复 (10)
雪    币: 26588
活跃值: (63257)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
楼主高产啊!感谢分享!
2019-11-11 16:14
0
雪    币: 722
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
楼主, 有没有那个样本呢?
2019-11-11 17:49
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
4
think8765 楼主, 有没有那个样本呢?
勒索软件不宜传播,但文末给了hash,可以搜索下。
2019-11-11 20:54
0
雪    币: 279
活跃值: (123)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习了
2019-11-22 10:18
0
雪    币: 119
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
好巧,这个跟你一样,,,并且引用文章、样本HASH。。。、都一样。。。“他抄你原创。。。。
https://www.youtube.com/watch?v=R4xJou6JsIE
2019-12-12 17:35
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
7
wx_菠萝 好巧,这个跟你一样,,,并且引用文章、样本HASH。。。、都一样。。。“他抄你原创。。。。[em_19]” https://www.youtube.com/watch?v=R4xJou6JsIE
不是,是我观看过这个国外的视频,然后自己动手学习了一遍。
2019-12-12 18:58
0
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
8
怎么去掉掉 ASLR   改成0080干啥
2019-12-13 10:34
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
9
killpy 怎么去掉掉 ASLR 改成0080干啥
看下这个文章  https://www.jianshu.com/p/91b2b6665e64
2019-12-13 16:30
0
雪    币: 159
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
请问哪里能找到这个恶意样本,刚刚搜了一阵子,没搜到。
2019-12-20 18:04
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
11
xiaozhu头 请问哪里能找到这个恶意样本,刚刚搜了一阵子,没搜到。
https://bbs.pediy.com/thread-254296.htm 试试这个
2019-12-20 18:11
0
游客
登录 | 注册 方可回帖
返回
//