Assassin_ 发表于 2020-8-11 20:04

一个botnet的样本分析

本帖最后由 Assassin_ 于 2020-8-11 20:04 编辑

# 一个botnet的样本分析

## 前言

样本涉及的网络结构和设计模式比较陌生。对于我来说值得一看,可能有些地方理解不准确请海涵轻锤,但是深入研究,会发现这个样本越看越有意思,并且会有不少收获。

## 环境与工具

win 7 64位、IDA 7.0、exeinfo、OD

## 分析

其样本整体行为如下,中间有涉及hook和隐藏自身模块等操作还是可以学习的,其中隐藏模块自己实现了一下,但样本本身最有意思的地方还是网络通信。



### 静态分析

样本基本信息

| 文件名称      | crashreporter.exe                                          |
| --------------- | ------------------------------------------------------------ |
| MD5             | 41a867c465464efa23b2451ae1367396                           |
| SHA256          | 6c362198a8879579c074ee8b0b14e712e059ff7f6037305e26f6d9ed47c6d39b |
| 大小            | 116.00 KB                                                    |
| 类型            | Win32 EXE                                                    |
| 作用(下载/释放) | 释放执行                                                   |
| 打包/编译语言   | Microsoft Visual C++ ver. 8.0 / Visual Studio 2005 [ Debug:02 ] |
| 编译/保存日期   | 2015:05:20 12:48:00+01:00                                    |
| pdb             | C:\cc694wkw\x\4wiefw4\bv7mmx\67nqxrcs\g\e0q09q\2qnp\p4gxr\l9e7\xl4n6aq.pdb |



静态拖入IDA,发现代码加密



查壳,发现无壳,不过代码应该还是被压缩了





### 动态分析

首先利用rdtsc进行反调试





调用VirtualProtectEx修改内存属性



代码解密



利用jmp指令跳转到解密代码



利用fs:获取指定模块基址,通过获取Loadlibrary和Getprocaddress,然后利用GetProcAddress获取指定API



申请空间,并拷贝相应数据到该空间



申请空间并解压数据



解压之后发现该段数据为一个PE文件



接下来就是样本自身编写的一个PE Loader

修改自身PE内存属性,并将解密后的PE拷贝到原PE内存



重定位



获取API



修复内存属性



最后跳转到修改后的PE入口点



我们将其dump下来,此时就是恶意软件的本体了,对其进行分析

首先判断是否存在参数 /pid



对 ZwQueryVirtualMemory 进行Hook





我们查看该段代码



对其进行分析可以发现该段代码是对付沙箱的内存扫描,调用该函数会返回一段虚假的内存以欺骗沙箱



返回来,继续分析,通过申请空间,拷贝映像,并利用UnmapViewOfFile释放之前的映射的本体内存,这里可以自己写代码实现



发现找不到自身模块了




我们自己把代码抠出来,以后可以自己用

```
#include <iostream>
#include <Windows.h>

void My_UnmapViewOfFile()
{
    DWORD Imagebase;
    DWORD dwsize;
    DWORD lpaddr;
    char* result;
    LPVOID (*DwVirtualAlloc)(LPVOID lpAddress,SIZE_T dwSize,DWORDflAllocationType,DWORDflProtect);
    __asm
    {
      
      call $+5
      mov lpaddr,eax
      mov   eax, fs:
      mov eax,
      mov Imagebase, eax
      mov eax,
      add eax, Imagebase
      mov eax,
      mov dwsize, eax
      mov eax, ds:VirtualAlloc
      mov DwVirtualAlloc, eax
    }
   
    if (UnmapViewOfFile((LPVOID)Imagebase))
    {
      if (DwVirtualAlloc((LPVOID)Imagebase, dwsize, 0x3000u, 0x40u) == (LPVOID)Imagebase)
      {
            __asm
            {
                mov ecx, dwsize
                mov esi, lpaddr
                mov edi, Imagebase
                rep movsb
            }
      }
    }
    return;
}



int main()
{
    DWORD Imagebase = 0;
    DWORD dwsize = 0;
    char* result;
    void (*lpAddress)(void);
    __asm
    {
      mov   eax, fs:
      mov eax,
      mov Imagebase,eax
      mov eax,
      add eax, Imagebase
      mov eax,
      mov dwsize, eax
    }
    result = (char*)VirtualAlloc(NULL, dwsize + 0x200, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    lpAddress = (void (*)(void))result;
    if (result)
    {
      memcpy(result, My_UnmapViewOfFile, 0x200u);
      memcpy(result + 0x200, (const void*)Imagebase, dwsize);
      lpAddress();
      VirtualFree(lpAddress, 0, 0x8000u);
      result = (char*)1;
    }
    ShellExecute(NULL, NULL, L"calc.exe", NULL, NULL, NULL);
}
```
设置Token



解密字符串并拼接





与当前目录进行比较,若存在指定目录则直接返回,否则,继续执行



查看当前是否位admin用户,如果不是则创建两个线程





这两个线程执行以下操作

线程1 :

以admin用户进行启动



线程2:

虚拟键盘输入空格



根据时间创建目录,并再次拷贝文件到该目录





创建名为syshost的服务,如果存在该服务,则先删除该服务,然后再次创建



尝试开启服务,如果开启失败,则不在利用服务进行自启动,而是通过注册表进行自启动



移动源文件到临时目录



最后清理文件,结束该进程



我们可以看到该进程再驻留时存在两种启动方式,一种是加参数 \service的服务启动,还有一种是直接注册表不加参数再指定目录直接启动的方式,二者无论以什么方式启动,其最终的行为是类似的。

我们首先分析直接启动的方式,经过之前的分析,我们定位到比较指定目录的地方,即上文中的下图,我们可以看到此时该函数直接返回



返回之后执行如下代码



我们没有参数,所以执行的是函数 00405BAC这个函数

进入该函数,首先判断进程类型



设置安全级别到最低,并且创建事件。其事件名分别为之前的解密字符串 Global/NitrGB 和 Local_NitrGB





设置错误处理函数,查看异常所在线程及其原因



之后遍历进程,排除自身对其他进程进行代码注入



注入采用CreateRemoteThread的方式



利用特殊指令检查虚拟机





更改并设置防火墙规则



解密内置域名



解密内置ip,总共有32个



获取卷GUID



根据卷GUID查询相关注册表





解码样本内置数据并利用WinAPI进行加密





将加密后的数据写入临时文件中


并复制其数据到注册表中



利用p2p进行网络连接

首先获取随机端口



进入主函数

开放tcp和udp的随机端口,并创建一个线程



进入该线程,该线程功能为通过发送请求到其他bot,解析接收到的内容并更新自己的bot list或者C2或者ip list 或者发送内容到bot list中的任意bot,默认每5S发送一次,这段代码要好好看一下,如果能够理解,真的挺有意思的。



之后设置注册表,其值为随机端口进行加密后的值





其后再次查询并设置一系列注册表



开始进行网络连接, 利用C2进行数据的更新和恶意软件的分发



利用facebook.com和microsoft.com进行网络测试





创建4个线程



并4个线程中获取随机的域名迷惑分析者并通过对其进行DNS查询,将可以查询到的ip数据保存在一个列表中



对DNS查询的ip与之前获取facebook.com和microsoft.com的信息进行对比,确保不等于这两个ip的地址




然后连接硬编码域名,进行数据传输,获取当前精确时间





完成之后开始真正的C2连接

首先第一种就是硬编码的域名进行连接



第二种方式就是利用硬编码的Ip进行连接



如果C2连接成功之后,减慢bot之间的连接,变为每60S请求一次



如果以上两种连接不成功则加快bot之间的通信变为每一秒请求一次,利用第三种方法继续尝试C2的连接.




第三种方式,分为两部分拼接域名

第一部分





第二部分,获取后缀







最后进行数据的传输与交互



返回数据需要解密等复杂操作,故根据之前的分析,对其只能静态分析,不过我们可以看到后续的部分操作

如更新Botlist



代码执行



创建驱动文件等操作



## 结语

路漫漫其修远兮,吾将上下而求索。

lookerJ 发表于 2020-8-12 12:48

那张逻辑图总结的很棒。

CHILAS_LEE 发表于 2020-9-17 21:17

感谢楼主分享,我想请问一下楼主,我是在校的学生,实验室项目包含病毒分析方向,请问除了论坛上的帖子,还有什么地方可以学习相关技术吗,谢谢

arklearn 发表于 2020-8-11 22:47

感觉GPL2最无辜的,好好的版权协议,被利用了

boringdog 发表于 2020-8-11 23:54

看傻眼了,牛皮{:301_1003:}

Assassin_ 发表于 2020-8-12 08:51

VS2019编译,代码编译选项需禁用GS安全检查。

danyang0511 发表于 2020-8-12 08:53

谢谢分享{:1_919:}

chased 发表于 2020-8-12 09:54

感谢分享,很详细

sunnylds7 发表于 2020-8-12 10:03

感谢热心的分析

木子月 发表于 2020-8-12 12:38

楼主棒啊

guazi1990 发表于 2020-8-12 13:27

看着就很厉害(? ??_??)?
页: [1] 2 3 4 5 6
查看完整版本: 一个botnet的样本分析