HITCON ENT 惡意軟體新型API呼叫行為實作(Patten式GetProcAddress找碴呼叫API)

動機

這次HITCON Enterprise正好坐在R0場有幸聽到PK大神講的議程XD。不過本來想聽這場是想知道實際Google Drive C2 API呼叫過程的Olly分析,不過似乎整場議程重點不放在那Otz...

但正好聽到一件有趣的事情,議程內提到新型惡意軟體呼叫不再像以前是用GetProcAddress,而是自幹了一個叫做GetProcAddressEx呼叫方式會像是:

GetProcAddressEx( ModuleBase, "0=L;3=d;4=L;11=A");
//找尋一個API根據函數名,第0個為L、第3個為d、第4個為L、第11個為A

這手法聽起來像是在逃避正常靜態分析軟體(如IDA Pro)可以很容易根據Import Address Table得知呼叫過程或者透過對GetProcessAddress下斷點動態反查API過程,所以我想這個GetProcAddressEx內部應該:
  1. 不該使用到任何API來增加分析難度
  2. 暴力分析DLL模組結構體找碴外導函數
  3. 暴力列舉API過程中根據Pattern(也就是0=L;3=d...)找到第一筆合適的API名
根據這個原則,於是我開始實做惹XD

Visual C++實作

關於這種奇怪的要求,想到之前有研究過類似的發文 [Windows][PE][ASM]純組語手幹Dll Header解析外導函數表撈出函數地址(如LoadLibraryA動態地址)索性我就拿這個的部分Code來改成VC++函數版本了XD

首先Pattern檢測部份,我寫了一支簡單的函數
int StrVal(std::string Data)
{
 return std::stoi(Data);
}

bool ChkPatten(char* Str, char* Patten)
{
 bool retn = true;
 std::string n = "";
 for (int i = 0; Patten[i]; i++)
 {
  if (*(char*)(Patten + i) == '=')
  {
   int LPos = StrVal(n);
   char RChar = *(char*)(Patten + i + 1);
   if (Str[LPos] != RChar)
   {
    retn = false;
    break;
   }
   n = "";
   i += 2;
  }
  else
  {
   n += *(char*)(Patten + i);
  }
 }
 return retn;
}
這支ChkPatten寫法重點就是一直複製左邊的數字到n的String結構體內,直到遇到”=“時就解析右邊的一個Char看指定位置的字詞是否為該Char然後不停的確認Pattern,就這樣而已XD

那再來就是重點的DLL結構體解析,根據DLL結構體的定義可以寫出以下Code:
int GetProcAddrEx(int ModBase, char* Patten)
{
 int Data = 0;
 _asm
 {
 
  mov esi, ModBase
   mov edx, [esi + 0x3c]//e_ifanw
   add edx, esi//Point To NTHeader Address.
   add edx, 0x18//OptionalHeader
   add edx, 0x60//IMAGE_OPTIONAL_HEADER -> DataDirectory
   add edx, 0x00//Export Directory Offset Addr
   mov edx, [edx]//Export Directory Addr
   add edx, esi

   mov ecx, [edx + 0x18]//Number Of Names

  FindAddrOfNames:
   mov ebx, [edx + 0x20]//Addr Of Name Offset
   add ebx, esi//Point to Addr Of Name
   mov ebx, [ebx + ecx * 4]
   add ebx, esi//Current Name Addr
   dec ecx
   jl ExitFunc


   pushad
   push Patten
   push ebx
   call ChkPatten
   add esp, 0x08
   test al, al
   popad
   jz FindAddrOfNames

   GetAddr :
   inc ecx
   mov ebx, [edx + 0x24]//AddressOfNameOrdinals Offset
   add ebx, esi
   mov cx, [ebx + ecx * 2]//ECX = AddressOfNameOrdinals  + Index As WORD(2 BYTE)
   mov ebx, [edx + 0x1c]//AddressOfFunction Array Offset.
   add ebx, esi
   mov ebx, [ebx + ecx * 4]//Set EDX = Value Of AddressOfFunction[Index] = Offset.
   add esi, ebx
   mov[Data], esi
   ExitFunc :
 }
 return Data;
}
如果不懂的話可以去翻 [Windows][PE][ASM]純組語手幹Dll Header解析外導函數表撈出函數地址(如LoadLibraryA動態地址)來看一下,這邊主要就是改了一下那篇部落格文內的比對文字。

pushad
   push Patten
   push ebx
   call ChkPatten
   add esp, 0x08
   test al, al
   popad
   jz FindAddrOfNames

去呼叫我們前面寫好的ChkPatten函數來確認我們當前列舉的API ESI對應的文字名字是否為Patten所求的API,比較特別的是add esp, 0x08部分是為了平衡VC內函數寫法固定都得平衡參數使用的空間(我記得C++ Builder好像不用這樣平衡?看IDE)

最後就是Demo一下這兩個函數的猛猛效果了

DEMO

首先,為了做到完美的不使用API我還使用到了之前PO過的[Windows][PE][ASM][CE]從PE架構到模組架構到暴力列舉模組找模組位置(如Kernel32.dll)來找碴模組在當前記憶體中的位置。

int GetKernel32Mod()
{
 int dRetn = 0;
 _asm
 {
  Main:
   mov ebx, fs:[0x30] //PEB
   mov ebx, [ebx + 0x0c]//Ldr
   mov ebx, [ebx + 0x1c]//InInitializationOrderModuleList
  Search :
   mov eax, [ebx + 0x08]//Point to Current Modual Base.
   mov ecx, [ebx + 0x20]//Point to Current Name.
   mov ecx, [ecx + 0x18]
   cmp cl, 0x00//Test if Name[25] == \x00.
   mov ebx, [ebx + 0x00]
   jne Search
   mov[dRetn], eax
 }
 return dRetn;
}
那最後就是實作怎麼不用任何API(應該說IAT表、靜態工具分析看不出來)怎麼做到漂亮的呼叫API了,這邊以Console程式要呼叫MessageBoxA為例,因為MessageBoxA在User32.dll之下但是Console程式預設是不會載入這個模組的,所以實際程式碼我們得先透過LoadLibraryA把User32.dll給載入進記憶體在找碴MessageBoxA的實際記憶體地址然後做呼叫:

int _tmain(int argc, char* argv[])
{
 int Kernel32Base = GetKernel32Mod();
 HMODULE(WINAPI*MyLoadLibraryA)(LPCTSTR lpFileName) = (HMODULE(WINAPI *)(LPCTSTR))(GetProcAddrEx(Kernel32Base, "0=L;3=d;4=L;11=A"));
 
 int User32Base = (int)MyLoadLibraryA((LPCTSTR)"User32.dll");
 int(WINAPI*MyMessageBoxA)(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType) = (int(WINAPI *)(HWND, LPCTSTR, LPCTSTR, UINT))(GetProcAddrEx(User32Base, "0=M;2=s;3=s;7=B;10=A"));
 MyMessageBoxA(0,(LPCTSTR)"Adr Handsome!", (LPCTSTR)"Adr Say", 0);
 return 0;
}
實作效果:

效果拔擢啊!!
如果想下載完整的專案代碼,可以到我的GitHub:
https://github.com/aaaddress1/Virus-Patten-API-Call/tree/master

留言

這個網誌中的熱門文章

[C#] Lambda花式應用噁爛寫法(跨UI委派秒幹、多線程處理...etc)

[Windows] 逆向工程 C++ 中入口函數參數 main(argc, argv) 與如何正確的進行參數劫持

重建天堂之門:從 32 位元地獄一路打回天堂聖地(上)深度逆向工程 WOW64 設計