首页
社区
课程
招聘
[原创]RWhackA远程线程注入式病毒分析(H&NCTF2024)
发表于: 2024-6-6 00:51 8227

[原创]RWhackA远程线程注入式病毒分析(H&NCTF2024)

2024-6-6 00:51
8227

wp拜读:[原创]H&NCTF RE 部分题解-CTF对抗-看雪-安全社区|安全招聘|kanxue.com

涉及到的知识点:
IDA动态调试
IDAPython脚本
用快照对进程、模块、线程进行遍历(代码段)
WindowsAPI
病毒感染实现(PE infector)
DLL文件分析


发现保护器:保护器: Enigma Virtual Box
工具下载:【更新】Enigma Virtual Box(单文件封装工具) v10.50 - 『精品软件区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
该保护器介绍:Enigma Virtual Box用法 exe封包工具介绍-CSDN博客
这个保护器主要的功能就是把一堆文件合成为一个EXE!

脱壳工具:mos9527/evbunpack: Enigma Virtual Box Unpacker / 解包、脱壳工具 --- mos9527/evbunpack: Enigma Virtual Box Unpacker / 解包、脱壳工具 (github.com)
脱壳失败:

开始手动静态分析:

静态分析发现有很多內存访问异常点,均出自函数调用上,可能是程序重定位时产生的错误,没办法只能动态调试了。

我想知道为什么会动态调试才出现真实的地址:
找到存放地址的变量去下一个断点:

下个读写断点:

再去看是被哪个函数写入的值!

成功找到!

毫无疑问这个正确的地址是由保护器Enigma Virtual Box动态写入的!!!


发现存在一个非官方库的dll文件,猜这其实是题目要给的附件只是被打包进了exe!

继续动态调试,成功加载出myDLL1.dll文件,直接用ida插件dump出来:

成功:

剩下的就是动态调试dll文件里面的函数了!

这段代码的逻辑:
文件有三个:原程序、svchsst.exe、9434d49b-56e1-34d4-9434-0245943434d4.txt

继续分析main函数:

这里会检测程序是否运行再虚拟机上!

这段代码的主要逻辑就是运行vbs代码删除文件!
发现这段vbs的作用就是用来删除文件和隐藏窗口的!

批处理文件首先检查是否有参数传递。如果没有参数,它使用 mshta 命令重新运行自己,并传递 hide 参数,同时隐藏窗口。然后在重新运行时,由于有了 hide 参数,实际的批处理文件逻辑开始执行。

这段代码通过遍历进程查找电脑中是否存在vm必备进程!来检查是否是虚拟机!

直接手动绕过前面的虚拟机检查就来到了下面代码的位置!

遍历程序的所有模块来寻找特点hash值的模块,我们直接下个断点再来看看它找的是哪个模块!

再去看地址发现找到了kernel32

解决了这段代码就来学习一下这个结构体(结构体来至chatgpt)

继续向下分析

这段代码的核心函数就是sub_140001320();,前面查找"ZhuDongFangYu.exe" 和 "360Tray.exe" 这两个进程并不会阻止程序的运行!他还会检测计算机所使用的操作系统!

RtlGetVersion 函数所返回的结构体:

解析一下代码:

这段代码的主要功能是:

这段代码会输出最终的shellcode,也就是我们最终要分析的目标!直接在这个位置下个断点!!!dump一下数据就好了!!

利用idapython将数据dump出来!

将内存dump出来之后就直接拖入ida看看这段shellcode的作用,当然也可以直接在ida里将shellcode转为指令直接分析不需要dump!

但是我还是dump出来更加清晰:
在shellcode的最后就隐藏这flag:

int LocalHide()
{
  char Src[304]; // [rsp+20h] [rbp-E0h] BYREF
  char Command[304]; // [rsp+150h] [rbp+50h] BYREF
  char FileName[304]; // [rsp+280h] [rbp+180h] BYREF
  CHAR Filename[304]; // [rsp+3B0h] [rbp+2B0h] BYREF
  CHAR Buffer[256]; // [rsp+4E0h] [rbp+3E0h] BYREF
  char v6[10240]; // [rsp+5E0h] [rbp+4E0h] BYREF
  DWORD pcbBuffer; // [rsp+2DF0h] [rbp+2CF0h] BYREF
  FILE *Stream; // [rsp+2DF8h] [rbp+2CF8h] BYREF
 
  memset(v6, 0, sizeof(v6));
  memset(Command, 0, 0x12Cui64);
  memset(Filename, 0, 0x12Cui64);
  memset(Src, 0, 0x12Cui64);
  memset(FileName, 0, 0x12Cui64);
  memset(Buffer, 0, sizeof(Buffer));
  pcbBuffer = 256;
  GetUserNameA(Buffer, &pcbBuffer);             // 获取当前用户名
  stdio_common_vsprintf_s(v6, "C:\\Users\\%s\\Videos", Buffer);
  stdio_common_vsprintf_s_0(Src, "%s\\svchsst.exe");// 拼接出文件路径
  GetModuleFileNameA(0i64, Filename, 0x104u);
  stdio_common_vsprintf_s_0(FileName, "%s\\9434d49b-56e1-34d4-9434-0245943434d4.txt");
  Stream = fopen(FileName, "r");
  if ( !Stream )                                // 在主程序中由于文件不存在导致打开失败
  {
    fopen(FileName, "r");                       // 打开文件会失败,C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
    ModifyAndRenameFile(Src, "txt", 0);         // 将src的路径后缀改名
    CopyFileContent(Filename, Src);             // 将主exe复制一份名为svchsst.txt的文本文件
    ModifyAndRenameFile(Src, "exe", 1);         // 目录出现exe,修改文件明
    stdio_common_vsprintf_s_0(Command, "start %s");// 拼接出命令start svchsst.exe
    system(Command);                            // 运行完之后会出现一个svchsst.txt
    Stream = 0i64;
    fopen_s(&Stream, FileName, "w");
    fwrite(Filename, 0x12Cui64, 1ui64, Stream); // 写入主文件路径到txt文本:C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
    fclose(Stream);
    puts("退出\n");
    exit(0);
  }
  printf("身在此山中\n");
  memset(Src, 0, 0x12Cui64);
  fgets(Src, 300, Stream);                      // 读取文本信息,得到主程序的位置C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  fclose(Stream);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  system(Command);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
  return system(Command);
}
int LocalHide()
{
  char Src[304]; // [rsp+20h] [rbp-E0h] BYREF
  char Command[304]; // [rsp+150h] [rbp+50h] BYREF
  char FileName[304]; // [rsp+280h] [rbp+180h] BYREF
  CHAR Filename[304]; // [rsp+3B0h] [rbp+2B0h] BYREF
  CHAR Buffer[256]; // [rsp+4E0h] [rbp+3E0h] BYREF
  char v6[10240]; // [rsp+5E0h] [rbp+4E0h] BYREF
  DWORD pcbBuffer; // [rsp+2DF0h] [rbp+2CF0h] BYREF
  FILE *Stream; // [rsp+2DF8h] [rbp+2CF8h] BYREF
 
  memset(v6, 0, sizeof(v6));
  memset(Command, 0, 0x12Cui64);
  memset(Filename, 0, 0x12Cui64);
  memset(Src, 0, 0x12Cui64);
  memset(FileName, 0, 0x12Cui64);
  memset(Buffer, 0, sizeof(Buffer));
  pcbBuffer = 256;
  GetUserNameA(Buffer, &pcbBuffer);             // 获取当前用户名
  stdio_common_vsprintf_s(v6, "C:\\Users\\%s\\Videos", Buffer);
  stdio_common_vsprintf_s_0(Src, "%s\\svchsst.exe");// 拼接出文件路径
  GetModuleFileNameA(0i64, Filename, 0x104u);
  stdio_common_vsprintf_s_0(FileName, "%s\\9434d49b-56e1-34d4-9434-0245943434d4.txt");
  Stream = fopen(FileName, "r");
  if ( !Stream )                                // 在主程序中由于文件不存在导致打开失败
  {
    fopen(FileName, "r");                       // 打开文件会失败,C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
    ModifyAndRenameFile(Src, "txt", 0);         // 将src的路径后缀改名
    CopyFileContent(Filename, Src);             // 将主exe复制一份名为svchsst.txt的文本文件
    ModifyAndRenameFile(Src, "exe", 1);         // 目录出现exe,修改文件明
    stdio_common_vsprintf_s_0(Command, "start %s");// 拼接出命令start svchsst.exe
    system(Command);                            // 运行完之后会出现一个svchsst.txt
    Stream = 0i64;
    fopen_s(&Stream, FileName, "w");
    fwrite(Filename, 0x12Cui64, 1ui64, Stream); // 写入主文件路径到txt文本:C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
    fclose(Stream);
    puts("退出\n");
    exit(0);
  }
  printf("身在此山中\n");
  memset(Src, 0, 0x12Cui64);
  fgets(Src, 300, Stream);                      // 读取文本信息,得到主程序的位置C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  fclose(Stream);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  system(Command);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
  return system(Command);
}
void __noreturn sub_140001070()
{
  __int64 len; // rdx
  char v1[304]; // [rsp+20h] [rbp-498h] BYREF
  char vscCode[304]; // [rsp+150h] [rbp-368h] BYREF
  char v3[304]; // [rsp+280h] [rbp-238h] BYREF
  char v4[264]; // [rsp+3B0h] [rbp-108h] BYREF
  int v5; // [rsp+4C0h] [rbp+8h] BYREF
  __int64 v6; // [rsp+4C8h] [rbp+10h] BYREF
 
  memeset();
  v5 = 256;
  (advapi32_GetUserNameA)(v4, &v5);
  memeset();
  memeset();
  (kernel32_GetModuleFileNameA)(0i64, v1, 260i64);
  stdio_common_vsprintf_s(v3, "C:\\Users\\%s\\Pictures\\WindowsRun.bat", v4);
  v6 = 0i64;
  (ucrtbase_fopen_s)(&v6, v3, "w");
  memeset();
  stdio_common_vsprintf_s(
    vscCode,
    "if \"%%1\"==\"hide\" goto CmdBegin\n"
    "start mshta vbscript:createobject(\"wscript.shell\").run(\"\"\"%%~0\"\" hide\",0)(window.close)&&exit\n"
    ":CmdBegin\n"
    "del %s",
    v1);
  len = -1i64;
  do
    ++len;
  while ( vscCode[len] );
  (ucrtbase_fwrite)(vscCode, len, 1i64, v6);
  (ucrtbase_fclose)(v6);
  stdio_common_vsprintf_s(v1, "start %s", v3);
  (ucrtbase_system)(v1);
  ucrtbase_exit(0i64);
}
void __noreturn sub_140001070()
{
  __int64 len; // rdx
  char v1[304]; // [rsp+20h] [rbp-498h] BYREF
  char vscCode[304]; // [rsp+150h] [rbp-368h] BYREF
  char v3[304]; // [rsp+280h] [rbp-238h] BYREF
  char v4[264]; // [rsp+3B0h] [rbp-108h] BYREF
  int v5; // [rsp+4C0h] [rbp+8h] BYREF
  __int64 v6; // [rsp+4C8h] [rbp+10h] BYREF
 
  memeset();
  v5 = 256;
  (advapi32_GetUserNameA)(v4, &v5);
  memeset();
  memeset();
  (kernel32_GetModuleFileNameA)(0i64, v1, 260i64);
  stdio_common_vsprintf_s(v3, "C:\\Users\\%s\\Pictures\\WindowsRun.bat", v4);
  v6 = 0i64;
  (ucrtbase_fopen_s)(&v6, v3, "w");
  memeset();
  stdio_common_vsprintf_s(
    vscCode,
    "if \"%%1\"==\"hide\" goto CmdBegin\n"
    "start mshta vbscript:createobject(\"wscript.shell\").run(\"\"\"%%~0\"\" hide\",0)(window.close)&&exit\n"
    ":CmdBegin\n"
    "del %s",
    v1);
  len = -1i64;
  do
    ++len;
  while ( vscCode[len] );
  (ucrtbase_fwrite)(vscCode, len, 1i64, v6);
  (ucrtbase_fclose)(v6);
  stdio_common_vsprintf_s(v1, "start %s", v3);
  (ucrtbase_system)(v1);
  ucrtbase_exit(0i64);
}
//使用 CreateToolhelp32Snapshot() 函数获取当前进程的进程快照
  Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
  //如果获取快照失败,打印错误码
  if ( Toolhelp32Snapshot == -1 )
  {
    v7 = (kernel32_GetLastError)();
    printf("CreateToolhelp32Snapshot:%d\n", v7);
  }
  // 初始化 PROCESSENTRY32W 结构体
  v51[0] = 568;
  //使用 Process32FirstW() 函数遍历进程快照
  if ( (kernel32_Process32FirstW)(Toolhelp32Snapshot, v51) )
  {
  //定义一些字符串,用于检查进程名是否包含这些字符串
    v40 = "Vmtoolsd.exe";
    v41 = "Vmwaretrat.exe";
    v42 = "Vmwareuser.exe";
    v43 = "Vmacthlp.exe";
    v44 = "vboxservice.exe";
    v45 = "vboxtray.exe";
    do
    {
      memeset();
      //将进程名从宽字符转换为多字节字符串
      (kernel32_WideCharToMultiByte)(0i64, 0i64, v52, 0xFFFFFFFFi64, v50, 260, 0i64, 0i64, v40, v41, v42, v43, v44, v45);
      //检查进程名是否包含虚拟机相关的字符串
      for ( i = 0i64; i < 6; ++i )
      {
        v10 = (&v40)[i];
        v11 = (v50 - v10);
        do
        {
          v12 = v11[v10];
          v13 = *v10 - v12;
          if ( v13 )
            break;
          ++v10;
        }
        while ( v12 );
        if ( !v13 )
        {
          printf("为虚拟机exe\n");
          sub_140001070();
        }
      }
    }
    //继续遍历下一个进程
    while ( (kernel32_Process32NextW)(Toolhelp32Snapshot, v51) );
  }
  else
  {
  //如果 Process32FirstW() 函数失败,打印错误码
    v8 = (kernel32_GetLastError)();
    printf("Process32First:%d\n", v8);
  }
  printf("为实体机\n");
//使用 CreateToolhelp32Snapshot() 函数获取当前进程的进程快照
  Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
  //如果获取快照失败,打印错误码
  if ( Toolhelp32Snapshot == -1 )
  {
    v7 = (kernel32_GetLastError)();
    printf("CreateToolhelp32Snapshot:%d\n", v7);
  }
  // 初始化 PROCESSENTRY32W 结构体
  v51[0] = 568;
  //使用 Process32FirstW() 函数遍历进程快照
  if ( (kernel32_Process32FirstW)(Toolhelp32Snapshot, v51) )
  {
  //定义一些字符串,用于检查进程名是否包含这些字符串
    v40 = "Vmtoolsd.exe";
    v41 = "Vmwaretrat.exe";
    v42 = "Vmwareuser.exe";
    v43 = "Vmacthlp.exe";
    v44 = "vboxservice.exe";
    v45 = "vboxtray.exe";
    do
    {
      memeset();
      //将进程名从宽字符转换为多字节字符串
      (kernel32_WideCharToMultiByte)(0i64, 0i64, v52, 0xFFFFFFFFi64, v50, 260, 0i64, 0i64, v40, v41, v42, v43, v44, v45);
      //检查进程名是否包含虚拟机相关的字符串
      for ( i = 0i64; i < 6; ++i )
      {
        v10 = (&v40)[i];
        v11 = (v50 - v10);
        do
        {
          v12 = v11[v10];
          v13 = *v10 - v12;
          if ( v13 )
            break;
          ++v10;
        }
        while ( v12 );
        if ( !v13 )
        {
          printf("为虚拟机exe\n");
          sub_140001070();
        }
      }
    }
    //继续遍历下一个进程
    while ( (kernel32_Process32NextW)(Toolhelp32Snapshot, v51) );
  }
  else
  {
  //如果 Process32FirstW() 函数失败,打印错误码
    v8 = (kernel32_GetLastError)();
    printf("Process32First:%d\n", v8);
  }
  printf("为实体机\n");
v40 = "Vmtoolsd.exe";
v41 = "Vmwaretrat.exe";
v42 = "Vmwareuser.exe";
v43 = "Vmacthlp.exe";
v44 = "vboxservice.exe";
v45 = "vboxtray.exe";
v40 = "Vmtoolsd.exe";
v41 = "Vmwaretrat.exe";
v42 = "Vmwareuser.exe";
v43 = "Vmacthlp.exe";
v44 = "vboxservice.exe";
v45 = "vboxtray.exe";
// 获取当前进程的环境块(PEB)
Blink = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Blink;
// 初始化链表遍历指针v16为链表头部
v16 = Blink;
// 遍历模块链表
do
{
    // 移动到下一个模块
    v16 = v16->Flink;
 
    // 检查当前模块是否有模块名称
    if (v16[3].Flink)
    {
        // 提取模块名称并存储在v46数组中
        Flink = v16[6].Flink;
        for (j = 0i64; *Flink; v46[j++] = v19)
        {
            // 最多只处理63个字符
            if (j >= 0x3F)
                break;
            v19 = *Flink;//字节存储在v19中
            Flink += 2;//移动两个字节
        }
        v46[j] = 0; // 添加字符串结束符
 
        // 将模块名称转换为小写
        v20 = (user32_CharLowerA)(v46);
 
        // 计算模块名称哈希值
        v21 = 53;
        v22 = -1i64;
        v14 = v20;
        do
            ++v22;
        while (*(v20 + v22));
        v23 = 0;
        if (v22)
        {
            do
            {
                v24 = *v14;
                ++v23;
                ++v14;
                v21 = v24 + 3 * v21;
            }
            while (v23 < v22);
            // 检查哈希值是否匹配目标值0x037C0B5E
            if (v21 == 0x037C0B5E)
                break;
        }
    }
}
// 循环直到遍历完整个模块链表
while (Blink != v16);
// 获取当前进程的环境块(PEB)
Blink = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Blink;
// 初始化链表遍历指针v16为链表头部
v16 = Blink;
// 遍历模块链表
do
{
    // 移动到下一个模块
    v16 = v16->Flink;
 
    // 检查当前模块是否有模块名称
    if (v16[3].Flink)
    {
        // 提取模块名称并存储在v46数组中
        Flink = v16[6].Flink;
        for (j = 0i64; *Flink; v46[j++] = v19)
        {
            // 最多只处理63个字符
            if (j >= 0x3F)
                break;
            v19 = *Flink;//字节存储在v19中
            Flink += 2;//移动两个字节
        }
        v46[j] = 0; // 添加字符串结束符
 
        // 将模块名称转换为小写
        v20 = (user32_CharLowerA)(v46);
 
        // 计算模块名称哈希值
        v21 = 53;
        v22 = -1i64;
        v14 = v20;
        do
            ++v22;
        while (*(v20 + v22));
        v23 = 0;
        if (v22)
        {
            do
            {
                v24 = *v14;
                ++v23;
                ++v14;
                v21 = v24 + 3 * v21;
            }
            while (v23 < v22);
            // 检查哈希值是否匹配目标值0x037C0B5E
            if (v21 == 0x037C0B5E)
                break;
        }
    }
}
// 循环直到遍历完整个模块链表
while (Blink != v16);
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
    LIST_ENTRY ForwarderLinks;
    LIST_ENTRY ServiceTagList;
    LIST_ENTRY StaticLinks;
    PVOID ContextInformation;
    ULONG_PTR OriginalBase;
    LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;

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

上传的附件:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//