EAT hooking je často skloňovaný pojem spojený s tvůrci malware (hlavně rootkitů a virů). Mohlo by se zdát, že se jedná o něco magického a nadpřirozeného. Následující mini-článek má za úkol dokázat, že opak je pravdou.

Obsah:
——————————————
1. Úvod
2. Návrh kódu
3. Využití
4. Kód
——————————————

1. Úvod
Při studiu malware dříve nebo později každý narazí na slovo hooking. Tento termín vyjadřuje schopnost svázat svůj vlastní kód s kódem cizím (zavěsit se na něj) a mít tak možnost reagovat na událost, který se v režii cizího kódu vyvolá (stisknutí kombinace kláves, volání konkrétní funkce atd.). Hooků existuje celá řada. My se dnes seznámíme s EAT (Export Address Table) hookem, jež se používá v případech, kdy jsou adresy funkcí získávány za běhu programu pomocí funkce GetProcAddress.

2. Návrh kódu
Pokud chceme hookovat EAT, musíme nejdříve tuto tabulku najít v adresním prostoru procesu. Ve chvíli, kdy máme adresu EATu, zjistíme, zda se v ní nachází, ve formě řetězce, funkce, kterou chceme hookovat. Pokud se zde nenachází, skončíme. Pokud se zde tato funkce nachází, najdeme si její adresu. Zde můžou nastat dvě možnosti. Buď bude funkce forwardovaná nebo nebude. Pokud je funkce forwardovaná, dohledáme si skutečnou adresu této funkce. V obou případech pak následně uložíme originální adresu (to je důležité, protože bychom jinak přišli o možnost volat originální funkci!!), pravíme práva místa v paměti, kde budeme přepisovat adresu funkce na READWRITE, přepíšeme ji adresou naší upravené funkce a vrátíme práva paměti do původního stavu. Když se teď pokusíme pomocí GetProcAddress zjistit adresu dané funkce, dostaneme adresu hookované funkce, která může libovolně naložit s předanými parametry a následně buď volat nebo nevolat originální funkci. Těm, kteří četli jeden z mých předchozích článků o vlastní verzi GetProcAddress jistě neunikla podobnost obou kódů. To je důkazem, jak dvojsečnou zbraní může být takový kód. Protože považuji za nesmyslné přepisovat popis funkce GetProcAddress z předchozího článku, přiložím pouze demonstrační kód, který je vcelku výmluvný.

3. Využití
Programátoři malware tuto techniku využívají společně s dalšími pro skrytí své činnosti, špehování, jako náhodný spouštěcí mechanismus jejich kódu atd. Na straně druhé jsou tyto techniky používány antivirovými pracovníky při obraně před malware.

4. Kód

#include <windows.h>
#include <stdio.h>
 
typedef void (WINAPI *oldheapalloc)(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
 
oldheapalloc OldHeapAlloc = NULL, MyCallHeapAlloc = NULL;
 
void WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
 
int EatHook(HMODULE hModule, LPCSTR lpProcName, VOID *NewAddress, VOID **OldAddress){
  DWORD hMod = (DWORD)hModule, *pFuncName, *pAddress, i;
  DWORD dwOldProtect, ExportTableAddr, ExportTableEnd;
  PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule;
  PIMAGE_NT_HEADERS pNtHdrs;
  PIMAGE_EXPORT_DIRECTORY pExportDir;
  WORD *pOrdinal;
  char szDll[MAX_PATH], szFunc[MAX_PATH], *pDllName;
 
  memset(szDll, 0, MAX_PATH);
  memset(szFunc, 0, MAX_PATH);
 
  if(pDosHdr->e_magic != 0x5A4D)
    return 0;
 
  pNtHdrs = (PIMAGE_NT_HEADERS)(pDosHdr->e_lfanew + 
                                hMod);
 
  if(pNtHdrs->Signature != 0x00004550)
    return 0;
 
  ExportTableAddr = pNtHdrs->OptionalHeader.DataDirectory[0].VirtualAddress;
  ExportTableEnd = ExportTableAddr +
                   pNtHdrs->OptionalHeader.DataDirectory[0].Size;
  pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pNtHdrs->OptionalHeader.DataDirectory[0].VirtualAddress +
                                         hMod);
 
  for(i = 0; i < pExportDir->NumberOfFunctions; i++){
    pFuncName = (DWORD *)(pExportDir->AddressOfNames + 
                          (sizeof(DWORD) * i) + 
                          hMod);
 
    // najdi funkci, ktera bude zahookovana
    if(lstrcmpA((char *)(*pFuncName + hMod), lpProcName) == 0){
      pOrdinal = (WORD *)(pExportDir->AddressOfNameOrdinals + 
                          (sizeof(WORD) * i) + 
                          hMod);
      pAddress = (DWORD *)(pExportDir->AddressOfFunctions + 
                           (sizeof(DWORD) * (*pOrdinal)) + 
                           hMod);
 
      // je funkce forwardovana?
      if((*pAddress >= ExportTableAddr) && (*pAddress <= ExportTableEnd)){
        i = 0;
        pDllName = (char *)(*pAddress + hMod);
 
        // rozparsuj string formatu 'knihovna.funkce'
        while(*(pDllName + i) != '.'){
          *(szDll + i) = *(pDllName + i);
          i++;
        }
 
        lstrcatA(szDll, ".dll");
        lstrcpyA(szFunc, pDllName + i + 1);
 
        // uloz skutecnou adresu forwardovane funkce
        *OldAddress = (VOID *)GetProcAddress(LoadLibraryA(szDll), szFunc);
	  }else{
        // uloz adresu funkce
        *OldAddress = (VOID *)(*pAddress + hMod);
	  }
 
	  // uprav prava mista, kde budeme v EAT prepisovat adresu funkce
      if(VirtualProtect(pAddress, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect) == 0)
        return 0;
 
      // prepis/zahookuj adresu funkce
      *pAddress = ((DWORD)NewAddress - hMod);
 
      // obnov prava mista, kde jsme v EAT prepsali adresu funkce
      if(VirtualProtect(pAddress, sizeof(DWORD), dwOldProtect, &dwOldProtect) == 0)
        return 0;
 
      return 1;
	}
  }
  return 0;
}
 
// upravena verze funkce HeapAlloc
void WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes){
  MessageBoxA(NULL, "Win API function HeapAlloc was hooked", 
              "HeapAlloc hooked", MB_ICONINFORMATION);
  OldHeapAlloc(hHeap, dwFlags, dwBytes);
}
 
int main(){
  HMODULE hMod = LoadLibraryA("kernel32.dll");
  printf("Before overwriting: HeapAlloc = 0x%p\n", 
	     GetProcAddress(LoadLibraryA("kernel32.dll"), "HeapAlloc"));
  EatHook(hMod, "HeapAlloc", (VOID *)&MyHeapAlloc, (VOID **)&OldHeapAlloc);
  printf("After overwriting: HeapAlloc  = 0x%p\n", 
	     GetProcAddress(LoadLibraryA("kernel32.dll"), "HeapAlloc"));
  getchar();
  MyCallHeapAlloc = (oldheapalloc)GetProcAddress(hMod, "HeapAlloc");
  MyCallHeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x8);
  return 0;
}