Jak obiecalem tak zrobilem. Dzis chciałbym omowic IAT hooking i sposoby ich wykrywania.
Za jakis czas postaram sie napisac cos o hookach EAT i modyfikacjach kodu.
No wiec zaczynamy.
Najpierw kilka słow czym jest sam IAT hooking – jest to technika hookowania funkcji poprzez zamiane adresu funkcji umieszczonego w IAT na adres
funkcji umieszczonej w innej bibliotece dll albo poprostu gdzies w pamieci. O samym zakładaniu hookow pisal nie bede sa do tego dziesiatki gotowych
bibliotek, setki przykładowych kodów wiec mysle ze ci ktorzy jeszcze tego nie opanowali powinni jak najszybciej uzupełnic braki kozystajac z google.
Teraz cos na temat wykrywania tych hookow – Opisze w punktach co musimy zrobic:
1. Znajdujemy plik exe procesu w ktorym chcemy hookow poszukac.
2. Znajdujemy w tym pliku tablice IAT i sprawdzamy wszystkie funkcje znajdujace sie w IAT
3. Sprawdzamy w jakim module wg. IAT powinna znajdowac sie dana funkcja.
4. Sprawdzamy ImageBase i Size tego modułu i sprawdzamy czy adres w IAT miesci sie w przedziale
Jesli tak to wszystko OK jesli nie to sprawdzamy na jaki moduł wskazuje dany adres
To tyle teori.
Czas na praktyke.
Potrzebne naglowki:
1
2
3
4
5
6
7
8
9
| #include <windows.h>
#include <stdio.h>
#include <string>
#include <Tlhelp32.h>
#include <vector>
#include <Psapi.h>
#include <fstream>
#include "PE_class.h" //moja klasa do obslugi plikow PE(udostepnie razem z reszta kodu)
#pragma comment (lib,"psapi.lib"); |
Zmienne globalne:
1
2
3
| HANDLE hProc;//uchwyt procesu
DWORD pid;//ProcessID
DWORD MainImageBase;//ImageBase procesu |
Main:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| int main(int argc, CHAR* argv[])
{
printf("\n\nIAT Hook detector v. 1.0 by Grzonu\n\n");
if(argc!=2)//sprawdzamy czy podano parametr jesli nie to wychodzimy
{
printf("usage: %s <pid>\n\n",argv[0]);
return 0;
}
pid=atoi(argv[1]);//pobieramy PID
DWORD IB=0;
DWORD ImgSize=0;
FindImageBase(pid,&IB,&ImgSize);//Znajdujemy ImageBase i Size procesu
printf("ImageBase: 0x%.8x\nImageSize: 0x%.8x\n\n",IB,ImgSize);
hProc=OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION,FALSE,pid);//otwieramy go
if(hProc==0)//jesli sie nie udalo to wychodzimy
{
printf("can`t open process\n");
return 0;
}
char* buf=LoadMod(IB);//ladujemy plik
MainImageBase=IB;
PE_file PE((HMODULE)buf);
IAT(&PE);//szukamy hookow
free(buf);//zwalniamy bufor zaalokowany w funkcji LoadMod
return 0;
} |
Teraz postaram sie omowic kazda z funkcji na poczatek FindImageBase() czyli funkcja szukajaca ImageBase i Size procesu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| DWORD FindImageBase(DWORD pid,DWORD* IB,DWORD* ImgSize)//funkcja przyjmuje processID, wskazniki na bufory na dane
{
PROCESSENTRY32 lppe32;
char buf[260];
memset(buf,0,260);
HANDLE hSnapshot;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//robimy snapshot listy procesow
lppe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapshot, &lppe32);
do
{
if(lppe32.th32ProcessID==pid)//szukamy naszego procesu
{
strcpy(buf,lppe32.szExeFile);
break;
}
}
while(Process32Next(hSnapshot, &lppe32));
CloseHandle(hSnapshot);
if(buf[0]==0)//jesli nie udalo sie znalesc zwracamy 0;
{
return 0;
}
MODULEENTRY32 mod32;
std::string x;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);//robimy snapshot listy modolow znalezionego procesu
mod32.dwSize = sizeof(MODULEENTRY32);
Module32First(hSnapshot, &mod32);
do
{
x=mod32.szExePath;
if(x.find(buf)!=0xFFFFFFFF)//sprawdzamy czy to główna czesc programu
{//jesli tak to wypelniamy odpowiednie zmienne
*IB=(DWORD)mod32.modBaseAddr;
*ImgSize=mod32.modBaseSize;
}
}
while(Module32Next(hSnapshot, &mod32));
CloseHandle(hSnapshot);
return 1;
} |
Mamy wiec juz potrzebne ImageBase i ImageSize procesu. Teraz zajmiemy sie funkcja ladujaca kod modulu z pliku na ktory wskazuje ImageBase.
Dotyczyc to bedzie nie tylko glownego kodu programu ale i wszystkich bibliotek. Musimy pobrac ImageSize z pliku poniewaz ImageSize modulu
w pamieci bardzo latwo zmienic tak aby wygladalo ze kod jednej biblioteki pokrywa sie z druga poprzez zmiane ImageSize modulu w pamieci.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| char* LoadMod(DWORD IB)
{
char ProcName[260];
GetModuleFileNameEx(hProc,(HMODULE)IB,ProcName,260);//pobieramy pelna sciezke do binarki modulu
std::ifstream f(ProcName,std::ios::binary);//otwieramy plik
char* buf;
f.seekg(0,std::ios::end);
int size=f.tellg();
f.seekg(0,std::ios::beg);
buf=(char*)malloc(size);//alokujemy bufor(pamietajmy go pozniej zwolnic)
memset(buf,0,size);
int i=0;
char ch;
while(i<size)
{
f.get(ch);//wczytujemy
buf[i]=ch;
i++;
}
f.close();
return buf;//zwracamy bufor
} |
Teraz czas na glowna funkcje IAT() ktora ma za zadanie znalesc wszystkie funkcje importowane przez aplikacje i sprawdzic czy nie sa hookowane.
Funkcja ta kozysta z kilku innych funkcji ktore tez po drodze omowie.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
| void IAT(PE_file* PE)
{
HINSTANCE hInstance =(HINSTANCE)PE->buf;
PIMAGE_DOS_HEADER pdosheader = (PIMAGE_DOS_HEADER)hInstance;//pobieramy naglowki
PIMAGE_NT_HEADERS pntheaders = (PIMAGE_NT_HEADERS)((DWORD)hInstance + pdosheader->e_lfanew);//i dalej...
PIMAGE_SECTION_HEADER psectionheader = (PIMAGE_SECTION_HEADER)(pntheaders + 1);
PIMAGE_IMPORT_DESCRIPTOR pimportdescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hInstance + PE->RVA_to_RAW(pntheaders->OptionalHeader.DataDirectory[1].VirtualAddress));//znajdujemy adres IAT w naglowku PE ale jako ze szukamy w pliku a nie w procesie pamietamy o zamianie adresu z RVA na RAW
PIMAGE_THUNK_DATA pthunkdatain, pthunkdataout;
PIMAGE_IMPORT_BY_NAME pimportbyname;
DWORD dw;
PCHAR ptr;
DWORD IB;
char* buf;
DWORD ImgSize;
PIMAGE_OPTIONAL_HEADER32 opt;
DWORD address;
DWORD IAT_adr;
DWORD read;
int i=0;
while ( pimportdescriptor->TimeDateStamp != 0 ||pimportdescriptor->Name != 0)//pobieramy tak dlugo importy az sie skoncza :)
{
ptr = (PCHAR)((DWORD)hInstance+ PE->RVA_to_RAW((DWORD)pimportdescriptor->Name));//Nazwa biblioteki Dll
i=0;
IB=FindModule(ptr);//Funkcja szukajaca ImageBase na podstawie nazwy biblioteki(omowie dalej)
buf=LoadMod(IB);//opisana wczesniej funkcja ladujaca kod
PE_file PE2((HMODULE)buf);//ladujemy kod do klasy
opt=PE2.GetOptionalHeader();//Pobieramy odpowiedni header
ImgSize=opt->SizeOfImage;//pobieramy potrzebne nam pole z tego headera czyli ImageSize
free(buf);//zwalniamy bufor
pthunkdataout = (PIMAGE_THUNK_DATA)((DWORD)hInstance + PE->RVA_to_RAW((DWORD)pimportdescriptor->FirstThunk));//pobieramy adres gdzie zaczynaja sie adresy w IAT
if (pimportdescriptor->Characteristics == 0)
{
pthunkdatain = pthunkdataout;//pobieramy adres struktury z ktorej pobierzemy nazwy funkcji
}
else
{
pthunkdatain = (PIMAGE_THUNK_DATA)((DWORD)hInstance +PE->RVA_to_RAW((DWORD)pimportdescriptor->Characteristics));//to samo ;)
}
while ( pthunkdatain->u1.AddressOfData != NULL)//dopuki sa jeszcze jakies funkcje
{
if ((DWORD)pthunkdatain->u1.Ordinal & IMAGE_ORDINAL_FLAG)//jesli Ordinal
{
LPSTR x=MAKEINTRESOURCE(LOWORD(pthunkdatain->u1.Ordinal));//Ordinal
address=MainImageBase+(pimportdescriptor->FirstThunk+(i*4));//adres bufora na adres funkcji
ReadProcessMemory(hProc,(LPCVOID)address,&IAT_adr,4,&read);//Odczytujemy adres funkcji znajdujacej sie pod adresem pobranym wyzej
if(IAT_adr<IB || IAT_adr>(IB+ImgSize))//sprawdzamy czy adres miesci sie w granicach swojego modułu
{//jesli nie to sprawdzamy w jakim module znajduje sie funkcja
DWORD HookBase=FindHookModule(IAT_adr);//pobieramy ImageBase modułu w ktorym znajduje sie ta funkcja.(Ta funkcje omowie pozniej)
char modname[260];
if(HookBase==0)//jesli nie ma takiego modulu oznacza ze jest to poprostu zaalokowana pamiec np. przez VirtualAllocEx
{
strcpy(modname,"Virtual Memory");
}
else//jesli jest to pobieramy sciezke do tego modułu
{
GetModuleFileNameEx(hProc,(HMODULE)HookBase,modname,260);
}
printf("Ord: %x(%s) --- Hooked by %s(0x%.8x)\n",x,ptr,modname,IAT_adr);//printfujemy wynik :)
}
} else {
pimportbyname = (PIMAGE_IMPORT_BY_NAME)(PE->RVA_to_RAW((DWORD)pthunkdatain->u1.AddressOfData) + (DWORD)hInstance);
address=MainImageBase+(pimportdescriptor->FirstThunk+(i*4));//tutaj to samo co wyzej
ReadProcessMemory(hProc,(LPCVOID)address,&IAT_adr,4,&read);
if(IAT_adr<IB || IAT_adr>(IB+ImgSize))
{
DWORD HookBase=FindHookModule(IAT_adr);
char modname[260];
if(HookBase==0)
{
strcpy(modname,"Virtual Memory");
}
else
{
GetModuleFileNameEx(hProc,(HMODULE)HookBase,modname,260);
}
printf("%s(%s) --- Hooked by %s(0x%.8x)\n",(char*)pimportbyname->Name,ptr,modname,IAT_adr);
}
}
i++;//nastepna funkcja
pthunkdatain++;
pthunkdataout++;
}
pimportdescriptor++;//nastepna DLL`ka
}
} |
To teraz 2 funkcje ktore mialem omowic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| void str_tolower(char* str)//funkcja zamieniajaca string na male litery
{
int i=0;
int size=strlen(str);
while(i<size)
{
str[i]=tolower(str[i]);
i++;
}
}
DWORD FindModule(char* mod_name)
{
MODULEENTRY32 mod32;
std::string x;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);//o tym juz mowilem
mod32.dwSize = sizeof(MODULEENTRY32);
str_tolower(mod_name);
Module32First(hSnapshot, &mod32);
do
{
str_tolower(mod32.szExePath);
x=mod32.szExePath;
if(x.find(mod_name)!=0xFFFFFFFF)//jesli to szukany modul to zwracamy jego ImageBase
{
return (DWORD)mod32.modBaseAddr;
}
}
while(Module32Next(hSnapshot, &mod32));
CloseHandle(hSnapshot);
return 0;
}
DWORD FindHookModule(DWORD Address)//funkcja szukajaca ImageBase modulu w ktorym znajduje sie funkcja
{
MODULEENTRY32 mod32;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
mod32.dwSize = sizeof(MODULEENTRY32);
Module32First(hSnapshot, &mod32);
do
{
if(Address>=(DWORD)mod32.modBaseAddr && Address<=(DWORD)(mod32.modBaseAddr+mod32.modBaseSize))//jesli adres znajduje sie w przedziale <ImageBase ; ImageBase+ImageSize> to zwracamy jego ImageBase
{
return (DWORD)mod32.modBaseAddr;
}
}
while(Module32Next(hSnapshot, &mod32));
CloseHandle(hSnapshot);
return 0;
} |
To chyba tyle na dzis.
Przedstawiona metoda jest prosta a jednak skuteczna(choc oczywiscie ja tez mozna ominac)
Jesli bym o czyms zapomnial to dopisze wkrotce
Załączam kod pod VC++ i binarke 
Link