Halaman

Select Language For You

Login

Blog Archive

Saha Wae

free counters

Your Properties

IP
Sabtu, 20 Maret 2010

Hooking With Import Table Patching in Delphi

1. Apa itu hooking ?

Hooking menurut saya adalah teknik yang digunakan untuk meredirect fungsi ke fungsi lain. Contoh teknik hooking dalam kehidupan sehari bisa diliat pada telpon. Ada apa dengan telpon ? Terkadang saat kita menelpon seseorang terdengar suara perempuan. Perempuan itu berkata seperti ini "Maaf telpon yang anda tuju sedang dialihkan" dan kitapun di redirect ke nomor lain. Process redirect inilah yang saya samakan dengan proces dari hooking, walaupun saya sendiri gak mudeng bagaimana cara kerja dari telpon itu :P. Tapi kalau suaranya seperti ini "Pulsa anda tidak mencukupi untuk melakukan panggilan ini" bagaimana tuh ??. Oh iya kira2 perempuan itu cantik gak yah ? kalau cantik dan masih mudah gue mau donk... hahahhaa
2. Kegunaan dari teknik hooking ?

Teknik hooking sama halnya dengan pisau yang dimana teknik ini bisa digunakan dengan baik dan tentu saja bisa juga digunakan untuk tujuan jahat. Adapun contoh kegunaan yang saya maksud seperti ini :

Security Aplikasi :

- Menggunakan teknik hooking untuk mengetahui apa yang dilakukan oleh file apabila dijalankan misalnya mengekseskusi program lain, membuat file baru dan banyak lagi.

- Menggunakan teknik hooking untuk mengetahui koneksi yang dilakukan oleh file (hook winsock) yang biasanya berada pada Aplikasi firewall dan sniffing.

- Dan lain2.

Virus :

- Menggunakan teknik hooking untuk mengetahui keystroke (keylogger).

- Menggunakan teknik hooking untuk menyembunyikan diri pada taskmanager.

- Dan Lain2.
3. Bagaimana Hooking Bekerja ?

Untuk dapat memahami betul bagaimana hooking bekerja maka saya mencoba memberikan gambaran yang lagi2 berkaitan dengan telpon (telpon rumah). Ada apa lagi dengan telpon rumah ? coba anda perhatikan telpon rumah itu, Apa yang membuat telpon rumah itu bisa saling terhubung. Kalau gue sih simple jawabnya "Karena ada kabel yang menghubungkan ". Disaat kita perhatikan kabel ini maka akan terlihat bahwa ada beberapa tahapan yang menurut saya mungkin dilalui oleh kabel ini sebelum sampai ke pusatnya.

Tahapan2 itu Menurut saya :

Telpon rumah << >> GateWay << >> Tiang << >> Center

>> itu berarti kabel yang membawa informasi ex : nomor telpon, dan alamat telpon yang dituju.
<< kebalikan dari >> atau bisa process balik

Muncul pertanyaan, bagaimana kalau keluaran informasi dari gateway ke tiang itu datanya diubah ? atau sebaliknya data yang dari tiang ke gateway diubah ? oh iya please gak usah pm gue tentang bagaimana cara mengubahnya karena tentu saja saya sendiri tidak tahu :P.

Contoh dari gambaran diatas hampir sama dengan fungsi2 yang terdapat pada windows. Pada tulisan saya sebelumnya tentang "Virus Make And Protect" saya sudah menerangkan sedikit tentang IAT (import adress table) dan apa yang terjadi pada Aplikasi apabila memanggil fungsi windows. Nah untuk tulisan ini sendiri, saya akan menjelaskan lebih lanjut tentang itu.

Apa yang terjadi disaat kita memanggil fungsi windows ex: FindFirstFile (FindFirstFileA) pada Aplikasi kita ? gambarannya begini :

1. Aplikasi kita mengambil alamat fungsi FindFirstFileA di IAT.

2. IAT.FindFirstFileA Mengacu ke alamat FindFirstFileA pada kernel32.dll yang dimana alamat ini tersimpan pada Export Table.

3. kernel32.dll.FindFirstFileA merupakan fungsi non unicode dari kernel32.dll.FindFirstFileW.

4. kernel32.dll.FindFirstFileW Adalah Fungsi yang dimana memanggil Fungsi NtQueryDirectoryFile yang dimana terletak pada IAT kernel32.dll.

5. IAT.NtQueryDirectoryFile mengacu ke NtQueryDirectoryFile pada ntdll. Yang dimana letaknya pada export table ntdll.

7. Ntdll merupakan batasan terakhir dari usermode yang dimana setelahnya sudah masuk ke kernel, jadi semua fungsi yang terdapat pada ntdll mengambil referensi dari kernel space melalui system call yang akan berakhir pada ntoskrnl.exe.


Pada kernel mode saya juga belom mengerti betul bagaimana cara kerjanya dan kekurangan dari delphi sendiri yang belum bisa untuk compile driver So mohon dimaapkan atas kekurangan informasi tentang itu .

kembali pada tahapan2 process pemangilan fungsi terlihat beberapa lompatan2 yang terjadi sebelum fungsi ini sampai ke ntdll (usermode = ntdll). Nah dari lompatan2 itu muncul pertanyaan bagaimana kalau alamat referensi dari tahapan diubah ke alamat fungsi lain ? tentu saja tiap pemanggilan dari fungsi itu maka yang dipanggil sebenarnya fungsi baru. seperti contoh pada telpon deh kejadiannya hehehe .

Mungkin sekian gambaran awal tentang bagaimana hooking bekerja lebih jelas tentang process dan implementasi kodenya anda bisa cari digoogle heuheue. Gak ding maksud saya pembahasan selanjutnya :P.
4. Structure PE

Untuk bisa mengubah IAT kita diharuskan untuk mengetahui structure dari PE. Pada tulisan ini, saya tidak menjelaskan secara gamblang structure pe ini tapi hanya terfocus ke IAT. Adapun bila anda ingin mempelajari lebih tentang Structure PE ini maka saya menganjurkan anda untuk membaca "Portable Executable File Format – A Reverse Engineer View by Goppit".

Windows mempunyai berbagai jenis file tipe yang dimana structure dari tiap file ini berbeda - beda. Executable file pada windows pun mempunya structurenya sendiri, yang dimana structure inilah yang sering kita sebut PE atau Portable Executable. Bagaimana Structure dari PE file ? PE file terdiri dari 3 structure penting yaitu Dos Header, PE Header , dan Section table. Tapi berhubung ini cuman sampai pada IAT so section table tidak dijelaskan :P.
1. DosHeader :

Code:

_IMAGE_DOS_HEADER = record
e_magic: Word; // Magic number
e_cblp: Word; // Bytes on last page of file
e_cp: Word; // Pages in file
e_crlc: Word; // Relocations
e_cparhdr: Word; // Size of header in paragraphs
e_minalloc: Word; // Minimum extra paragraphs needed
e_maxalloc: Word; // Maximum extra paragraphs needed
e_ss: Word; // Initial (relative) SS value
e_sp: Word; // Initial SP value
e_csum: Word; // Checksum
e_ip: Word; // Initial IP value
e_cs: Word; // Initial (relative) CS value
e_lfarlc: Word; // File address of relocation table
e_ovno: Word; // Overlay number
e_res: array [0..3] of Word; // Reserved words
e_oemid: Word; // OEM identifier (for e_oeminfo)
e_oeminfo: Word; // OEM information; e_oemid specific
e_res2: array [0..9] of Word; // Reserved words
e_lfanew: Longint; // File address of new exe header
end;

Cukup diperhatikan pada e_magic dan e_lfanew. Maksudnya :

e_magic : Nilainya $5A4D (MZ) untuk valid dos header.
e_lfanew : Nilainya merupakan alamat dari PE Header.
2. PE Header :

Code:

_IMAGE_NT_HEADERS = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER32;
end;

* PE Header Signature : Nilainya $00004550 (PE00) untuk valid PE Header.
* FileHeader: Merupakan structure yang formatntya seperti ini :
Code:


_IMAGE_FILE_HEADER = record
Machine: WORD;
NumberOfSections: WORD;
TimeDateStamp: DWORD;
PointerToSymbolTable: DWORD;
NumberOfSymbols: DWORD;
SizeOfOptionalHeader: WORD;
Characteristics: WORD;
end;

Yang penting dari FileHeader ini adalah NumberOfSections (banyaknya section pada file) dan Characteristics ( untuk mengetahui characteristic dai PE file misalnya apakah file itu Exe atau dll).
* OptionalHeader : Sama seperti fileheader, optional header juga merupakan structure tapi yang perlu digaris bawahi disini adalah terdapat 2 jenis optionalHeader yang dimana 32 dan 64 . Untuk mengetahui tipe dari optional header ini bisa dilihat dari magicnya, adapun magicnya yaitu $10b untuk yang 32 dan $20b untuk yang 64.

32 :
Code:

_IMAGE_OPTIONAL_HEADER = record
//
// Standard fields.
//
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
BaseOfData: DWORD;
//
// NT additional fields.
//
ImageBase: DWORD;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: DWORD;
SizeOfStackCommit: DWORD;
SizeOfHeapReserve: DWORD;
SizeOfHeapCommit: DWORD;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;

64:
Code:

_IMAGE_OPTIONAL_HEADER64 = record
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
ImageBase: Int64;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: Int64;
SizeOfStackCommit: Int64;
SizeOfHeapReserve: Int64;
SizeOfHeapCommit: Int64;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;

Pada Optional Header ini banyak informasi penting tersimpan didalamnya seperti imagebase, AddressOfEntryPoint, SectionAlignment, FileAlignment, SizeOfImage, SizeOfHeaders dan DataDirectory.


Kita telah sampai pada DataDirectory yang dimana merupakan tempat letak dari IAT dan tentu saja telah masuk pada akhir penjelajahan PE Structure.

DataDirectory :

DataDirectory merupakan array dari IMAGE_DATA_DIRECTORY yang dimana berjumlah 16 adapun kita cuman berfokus pada IMAGE_DIRECTORY_ENTRY_IMPORT. Oh iya hampir kelupa structure dari IMAGE_DATA_DIRECTORY adalah sebagai berikut :
Code:

_IMAGE_DATA_DIRECTORY = record
VirtualAddress: DWORD;
Size: DWORD;
end;

VirtualAddress pada IMAGE_DATA_DIRECTORY adalah alamat dari data sesuai image_data_directorynya jadi misalnya kita mengakses IMAGE_DIRECTORY_ENTRY_IMPORT maka virtualaddress akan berpacu ke data yang dimana data ini adalah structure dari _IMAGE_IMPORT_DIRECTORY.

IMAGE_IMPORT_DIRECTORY :

Sama Halnya DataDirectory, IMAGE_IMPORT_DIRECTORY juga merupakan sebuah array yang dimana array dari IMAGE_IMPORT_DESCRIPTOR. Misalnya terdapat 10 fungsi dari dll yang diload maka terdapat 10 IMAGE_IMPORT_DESCRIPTOR juga.

IMAGE_IMPORT_DESCRIPTOR :

IMAGE_IMPORT_DESCRIPTOR juga merupakan sebuah structure yang menyimpan informasi nama dll dan kumpulan fungsi yang diload dari dll itu.

Code:

TIIDUnion = record
case Integer of
0: (Characteristics: DWORD); // 0 for terminating null import descriptor
1: (OriginalFirstThunk: DWORD); // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
end;

_IMAGE_IMPORT_DESCRIPTOR = record
Union: TIIDUnion;
TimeDateStamp: DWORD; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)

ForwarderChain: DWORD; // -1 if no forwarders
Name: DWORD;
FirstThunk: DWORD; // RVA to IAT (if bound this IAT has actual addresses)
end;

Name merupakan rva dari nama dll sedangkan FirstThunk adalah rva dari awal array ImageThunk yang dimana ImageThunk menyimpan informasi tentang fungsi. Jadi apabila terdapat 10 fungsi yang diload dari ex kernel32 maka akan terdapat 10 imagethunk juga.
Code:

_IMAGE_THUNK_DATA32 = record
case Integer of
0: (ForwarderString: DWORD); // PBYTE
1: (Function_: DWORD); // PDWORD
2: (Ordinal: DWORD);
3: (AddressOfData: DWORD); // PIMAGE_IMPORT_BY_NAME
end;

Code:

PIMAGE_THUNK_DATA64 = ^IMAGE_THUNK_DATA64;
_IMAGE_THUNK_DATA64 = record
case Integer of
0: (ForwarderString: ULONGLONG); // PBYTE
1: (Function_: ULONGLONG); // PDWORD
2: (Ordinal: ULONGLONG);
3: (AddressOfData: ULONGLONG); // PIMAGE_IMPORT_BY_NAME
end;
IMAGE_THUNK_DATA64 = _IMAGE_THUNK_DATA64;

Ada 2 jenis Imagethunk yang mana 32 dan 64 (lihat optional header). Pada imagethunk 32 jarak antara nilainya adalah 4 byte (dword) sedangkan pada 64 jaraknya adalah 8 byte.

Sekian tentang structure pe yang mana hanya berfocus pada iat. Apabila anda merasa kurang jelas, saya minta maaf and silahkan lanjut ke pembahasan coding.
5. Coding

Untuk memulai menulis codenya maka dibutuhkan pseucode berdasarkan structure dari pe. Adapun pseucode sebagai berikut :
1. Load File yang dimana filenya adalah dll.
2. Masukkan nilai ke Dosheader serta melakukan validitasi Dos Header.
3. Menuju ke Dosheader__lfanew untuk mendapatkan PE Header.
4. Validitasi PE Header.
5. Mengisi IMAGE_IMPORT_DESCRIPTOR (array urutan pertama) yang dimana berada pada PEHeader>>OptionalHeader>>Datadirectory[1].virtualaddres
6. Perulangan 1 :
* Mengisi Nilai thunk yang berdasarkan alamat dari IMAGE_IMPORT_DESCRIPTOR.firstthunk
* Perulangan 2 :
o Mencari Target Function.
o Apabila Target Function ditemukan maka ganti alamat function Target Function dengan alamat new function.
o Menambahkan curent thunk sebanyak besarnya thunk untuk mendapatkan thunk selanjutnya.
o Apabila thunk bernilai nil menandakan tak ada lagi thunk pada current IMAGE_IMPORT_DESCRIPTOR maka keluar dari perulangan 2
* Menambahkan curent IMAGE_IMPORT_DESCRIPTOR sebanyak besarnya IMAGE_IMPORT_DESCRIPTOR untuk mendapatkan IMAGE_IMPORT_DESCRIPTOR selanjutnya
* Apabila IMAGE_IMPORT_DESCRIPTOR bernilai nil menandakan tak ada lagi IMAGE_IMPORT_DESCRIPTOR pada DataDirectory maka keluar dari perulangan 1


Dari pseudocode diatas codenya seperti ini :
Code:


Function HookIAT(Handle :THandle; oldfunction,Newfunction:pointer):boolean;
var
ppfn: ppointer;
IDH : PImageDosHeader absolute Handle;
INH32 : PIMAGE_NT_HEADERS32;
INH64 : PIMAGE_NT_HEADERS64;
IID : PIMAGE_IMPORT_DESCRIPTOR;
ITD32 : PIMAGE_THUNK_DATA32;
ITD64 :PIMAGE_THUNK_DATA64;
FileIs64 : bool;
num_written,SaveFlag : DWORD;
begin
result := false;
if (Handle = 0 ) or IsBadCodePtr(oldfunction) or IsBadCodePtr(Newfunction) then exit;
if IsBadReadPtrSize( IDH, SizeOf(TImageDosHeader) ) or
(IDH.e_magic <> IMAGE_DOS_SIGNATURE ) then exit;
INH32 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH32, SizeOf(PIMAGE_OPTIONAL_HEADER32)) or
(INH32.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH32.OptionalHeader.Magic = $10B) then FileIs64 := false
else if (INH32.OptionalHeader.Magic = $20B) then FileIs64 := false
else exit;
if (not FileIs64) then begin
if (INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD32 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD32 <> nil) and (ITD32.Function_ <> 0) do begin
ppfn := @(ITD32.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(dword), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, SizeOf(dword),num_written);
VirtualProtect(ppfn, sizeof(dword), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD32);
end;
inc(IID);
end;
end else begin
INH64 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH64, SizeOf(PIMAGE_OPTIONAL_HEADER64)) or
(INH64.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD64 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD64 <> nil) and (ITD64.Function_ <> 0) do begin
ppfn := @(ITD64.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(ULONGLONG), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, sizeof(ULONGLONG),num_written);
VirtualProtect(ppfn, sizeof(ULONGLONG), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD64);
end;
inc(IID);
end;
end;
end;

Code lengkap beserta tambahan function untuk pointer handlenya :
Code:


type
PIMAGE_OPTIONAL_HEADER32 = ^IMAGE_OPTIONAL_HEADER32;
_IMAGE_OPTIONAL_HEADER = record
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
BaseOfData: DWORD;
ImageBase: DWORD;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: DWORD;
SizeOfStackCommit: DWORD;
SizeOfHeapReserve: DWORD;
SizeOfHeapCommit: DWORD;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;
IMAGE_OPTIONAL_HEADER32 = _IMAGE_OPTIONAL_HEADER;

PIMAGE_OPTIONAL_HEADER64 = ^IMAGE_OPTIONAL_HEADER64;
_IMAGE_OPTIONAL_HEADER64 = record
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
ImageBase: Int64;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: Int64;
SizeOfStackCommit: Int64;
SizeOfHeapReserve: Int64;
SizeOfHeapCommit: Int64;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;
IMAGE_OPTIONAL_HEADER64 = _IMAGE_OPTIONAL_HEADER64;

PIMAGE_NT_HEADERS32 = ^IMAGE_NT_HEADERS32;
_IMAGE_NT_HEADERS = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER32;
end;
IMAGE_NT_HEADERS32 = _IMAGE_NT_HEADERS;

PIMAGE_NT_HEADERS64 = ^IMAGE_NT_HEADERS64;
_IMAGE_NT_HEADERS64 = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER64;
end;
IMAGE_NT_HEADERS64 = _IMAGE_NT_HEADERS64;

TIIDUnion = record
case Integer of
0: (Characteristics: DWORD); // 0 for terminating null import descriptor
1: (OriginalFirstThunk: DWORD); // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
end;

PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR;
_IMAGE_IMPORT_DESCRIPTOR = record
Union: TIIDUnion;
TimeDateStamp: DWORD; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)

ForwarderChain: DWORD; // -1 if no forwarders
Name: DWORD;
FirstThunk: DWORD; // RVA to IAT (if bound this IAT has actual addresses)
end;
IMAGE_IMPORT_DESCRIPTOR = _IMAGE_IMPORT_DESCRIPTOR;

PIMAGE_THUNK_DATA32 = ^IMAGE_THUNK_DATA32;
_IMAGE_THUNK_DATA32 = record
case Integer of
0: (ForwarderString: DWORD); // PBYTE
1: (Function_: DWORD); // PDWORD
2: (Ordinal: DWORD);
3: (AddressOfData: DWORD); // PIMAGE_IMPORT_BY_NAME
end;
IMAGE_THUNK_DATA32 = _IMAGE_THUNK_DATA32;

PIMAGE_THUNK_DATA64 = ^IMAGE_THUNK_DATA64;
_IMAGE_THUNK_DATA64 = record
case Integer of
0: (ForwarderString: ULONGLONG); // PBYTE
1: (Function_: ULONGLONG); // PDWORD
2: (Ordinal: ULONGLONG);
3: (AddressOfData: ULONGLONG); // PIMAGE_IMPORT_BY_NAME
end;
IMAGE_THUNK_DATA64 = _IMAGE_THUNK_DATA64;


{****PointerCheck*********************************************************************}
function MakePtr( AAddr: Pointer; Shift: Integer ): Pointer;
begin
Result := Pointer( Integer(AAddr)+Shift );
end;
{---------------}
function IsBadReadPtrSize( Ptr: Pointer; Size: DWORD): Boolean;
var
mbi: TMemoryBasicInformation;
const
PAGE_READABLE = PAGE_READONLY or PAGE_EXECUTE_READWRITE or
PAGE_READWRITE or PAGE_EXECUTE_READ;
begin
Result := VirtualQuery(Ptr, mbi, SizeOf(mbi)) <> 0;
if Result then begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_READABLE) = 0);
if Result then exit;
if Ptr<>mbi.BaseAddress then Inc(Size, DWORD(Ptr) - DWORD(mbi.BaseAddress) );
if (Size<= mbi.RegionSize) then exit;
while True do begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_READABLE) = 0);
if Result or (Size<= mbi.RegionSize) then break;
Dec(Size, mbi.RegionSize);
Ptr := Pointer(DWORD(mbi.BaseAddress) + mbi.RegionSize + 1);
if VirtualQuery(Ptr, mbi, SizeOf(mbi)) = 0 then begin
Result := True;
break;
end;
end;
end;
end;
{---------------}
function IsBadWritePtrSize( Ptr: Pointer; Size: DWORD): Boolean;
var
mbi: TMemoryBasicInformation;
const
PAGE_WRITABLE = PAGE_EXECUTE_WRITECOPY or PAGE_EXECUTE_READWRITE or
PAGE_READWRITE or PAGE_WRITECOPY;
begin
Result := VirtualQuery(Ptr, mbi, SizeOf(mbi)) <> 0;
if Result then begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_WRITABLE)= 0);
if Result then exit;
if Ptr<>mbi.BaseAddress then Inc(Size, DWORD(Ptr) - DWORD(mbi.BaseAddress) );
if (Size<= mbi.RegionSize) then exit;
while True do begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_WRITABLE)= 0);
if Result or (Size<= mbi.RegionSize) then break;
Dec(Size, mbi.RegionSize);
Ptr := Pointer(DWORD(mbi.BaseAddress) + mbi.RegionSize + 1);
if VirtualQuery(Ptr, mbi, SizeOf(mbi)) = 0 then begin
Result := True;
break;
end;
end;
end;
end;
{******************************************************************************}

Function HookIAT(Handle :THandle; oldfunction,Newfunction:pointer):boolean;
var
ppfn: ppointer;
IDH : PImageDosHeader absolute Handle;
INH32 : PIMAGE_NT_HEADERS32;
INH64 : PIMAGE_NT_HEADERS64;
IID : PIMAGE_IMPORT_DESCRIPTOR;
ITD32 : PIMAGE_THUNK_DATA32;
ITD64 :PIMAGE_THUNK_DATA64;
FileIs64 : bool;
num_written,SaveFlag : DWORD;
begin
result := false;
if (Handle = 0 ) or IsBadCodePtr(oldfunction) or IsBadCodePtr(Newfunction) then exit;
if IsBadReadPtrSize( IDH, SizeOf(TImageDosHeader) ) or
(IDH.e_magic <> IMAGE_DOS_SIGNATURE ) then exit;
INH32 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH32, SizeOf(PIMAGE_OPTIONAL_HEADER32)) or
(INH32.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH32.OptionalHeader.Magic = $10B) then FileIs64 := false
else if (INH32.OptionalHeader.Magic = $20B) then FileIs64 := false
else exit;
if (not FileIs64) then begin
if (INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD32 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD32 <> nil) and (ITD32.Function_ <> 0) do begin
ppfn := @(ITD32.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(dword), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, SizeOf(dword),num_written);
VirtualProtect(ppfn, sizeof(dword), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD32);
end;
inc(IID);
end;
end else begin
INH64 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH64, SizeOf(PIMAGE_OPTIONAL_HEADER64)) or
(INH64.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD64 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD64 <> nil) and (ITD64.Function_ <> 0) do begin
ppfn := @(ITD64.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(ULONGLONG), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, sizeof(ULONGLONG),num_written);
VirtualProtect(ppfn, sizeof(ULONGLONG), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD64);
end;
inc(IID);
end;
end;
end;

Testing Hooking Messagebox
Code:


program Project1;

uses
windows;

type
PIMAGE_OPTIONAL_HEADER32 = ^IMAGE_OPTIONAL_HEADER32;
_IMAGE_OPTIONAL_HEADER = record
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
BaseOfData: DWORD;
ImageBase: DWORD;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: DWORD;
SizeOfStackCommit: DWORD;
SizeOfHeapReserve: DWORD;
SizeOfHeapCommit: DWORD;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;
IMAGE_OPTIONAL_HEADER32 = _IMAGE_OPTIONAL_HEADER;

PIMAGE_OPTIONAL_HEADER64 = ^IMAGE_OPTIONAL_HEADER64;
_IMAGE_OPTIONAL_HEADER64 = record
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
ImageBase: Int64;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: Int64;
SizeOfStackCommit: Int64;
SizeOfHeapReserve: Int64;
SizeOfHeapCommit: Int64;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;
IMAGE_OPTIONAL_HEADER64 = _IMAGE_OPTIONAL_HEADER64;

PIMAGE_NT_HEADERS32 = ^IMAGE_NT_HEADERS32;
_IMAGE_NT_HEADERS = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER32;
end;
IMAGE_NT_HEADERS32 = _IMAGE_NT_HEADERS;

PIMAGE_NT_HEADERS64 = ^IMAGE_NT_HEADERS64;
_IMAGE_NT_HEADERS64 = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER64;
end;
IMAGE_NT_HEADERS64 = _IMAGE_NT_HEADERS64;

TIIDUnion = record
case Integer of
0: (Characteristics: DWORD); // 0 for terminating null import descriptor
1: (OriginalFirstThunk: DWORD); // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
end;

PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR;
_IMAGE_IMPORT_DESCRIPTOR = record
Union: TIIDUnion;
TimeDateStamp: DWORD; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)

ForwarderChain: DWORD; // -1 if no forwarders
Name: DWORD;
FirstThunk: DWORD; // RVA to IAT (if bound this IAT has actual addresses)
end;
IMAGE_IMPORT_DESCRIPTOR = _IMAGE_IMPORT_DESCRIPTOR;

PIMAGE_THUNK_DATA32 = ^IMAGE_THUNK_DATA32;
_IMAGE_THUNK_DATA32 = record
case Integer of
0: (ForwarderString: DWORD); // PBYTE
1: (Function_: DWORD); // PDWORD
2: (Ordinal: DWORD);
3: (AddressOfData: DWORD); // PIMAGE_IMPORT_BY_NAME
end;
IMAGE_THUNK_DATA32 = _IMAGE_THUNK_DATA32;

PIMAGE_THUNK_DATA64 = ^IMAGE_THUNK_DATA64;
_IMAGE_THUNK_DATA64 = record
case Integer of
0: (ForwarderString: ULONGLONG); // PBYTE
1: (Function_: ULONGLONG); // PDWORD
2: (Ordinal: ULONGLONG);
3: (AddressOfData: ULONGLONG); // PIMAGE_IMPORT_BY_NAME
end;
IMAGE_THUNK_DATA64 = _IMAGE_THUNK_DATA64;


{****PointerCheck*********************************************************************}
function MakePtr( AAddr: Pointer; Shift: Integer ): Pointer;
begin
Result := Pointer( Integer(AAddr)+Shift );
end;
{---------------}
function IsBadReadPtrSize( Ptr: Pointer; Size: DWORD): Boolean;
var
mbi: TMemoryBasicInformation;
const
PAGE_READABLE = PAGE_READONLY or PAGE_EXECUTE_READWRITE or
PAGE_READWRITE or PAGE_EXECUTE_READ;
begin
Result := VirtualQuery(Ptr, mbi, SizeOf(mbi)) <> 0;
if Result then begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_READABLE) = 0);
if Result then exit;
if Ptr<>mbi.BaseAddress then Inc(Size, DWORD(Ptr) - DWORD(mbi.BaseAddress) );
if (Size<= mbi.RegionSize) then exit;
while True do begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_READABLE) = 0);
if Result or (Size<= mbi.RegionSize) then break;
Dec(Size, mbi.RegionSize);
Ptr := Pointer(DWORD(mbi.BaseAddress) + mbi.RegionSize + 1);
if VirtualQuery(Ptr, mbi, SizeOf(mbi)) = 0 then begin
Result := True;
break;
end;
end;
end;
end;
{---------------}
function IsBadWritePtrSize( Ptr: Pointer; Size: DWORD): Boolean;
var
mbi: TMemoryBasicInformation;
const
PAGE_WRITABLE = PAGE_EXECUTE_WRITECOPY or PAGE_EXECUTE_READWRITE or
PAGE_READWRITE or PAGE_WRITECOPY;
begin
Result := VirtualQuery(Ptr, mbi, SizeOf(mbi)) <> 0;
if Result then begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_WRITABLE)= 0);
if Result then exit;
if Ptr<>mbi.BaseAddress then Inc(Size, DWORD(Ptr) - DWORD(mbi.BaseAddress) );
if (Size<= mbi.RegionSize) then exit;
while True do begin
Result := (mbi.Protect = PAGE_NOACCESS) or ((mbi.Protect and PAGE_WRITABLE)= 0);
if Result or (Size<= mbi.RegionSize) then break;
Dec(Size, mbi.RegionSize);
Ptr := Pointer(DWORD(mbi.BaseAddress) + mbi.RegionSize + 1);
if VirtualQuery(Ptr, mbi, SizeOf(mbi)) = 0 then begin
Result := True;
break;
end;
end;
end;
end;
{******************************************************************************}

Function HookIAT(Handle :THandle; oldfunction,Newfunction:pointer):boolean;
var
ppfn: ppointer;
IDH : PImageDosHeader absolute Handle;
INH32 : PIMAGE_NT_HEADERS32;
INH64 : PIMAGE_NT_HEADERS64;
IID : PIMAGE_IMPORT_DESCRIPTOR;
ITD32 : PIMAGE_THUNK_DATA32;
ITD64 :PIMAGE_THUNK_DATA64;
FileIs64 : bool;
num_written,SaveFlag : DWORD;
begin
result := false;
if (Handle = 0 ) or IsBadCodePtr(oldfunction) or IsBadCodePtr(Newfunction) then exit;
if IsBadReadPtrSize( IDH, SizeOf(TImageDosHeader) ) or
(IDH.e_magic <> IMAGE_DOS_SIGNATURE ) then exit;
INH32 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH32, SizeOf(PIMAGE_OPTIONAL_HEADER32)) or
(INH32.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH32.OptionalHeader.Magic = $10B) then FileIs64 := false
else if (INH32.OptionalHeader.Magic = $20B) then FileIs64 := false
else exit;
if (not FileIs64) then begin
if (INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD32 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD32 <> nil) and (ITD32.Function_ <> 0) do begin
ppfn := @(ITD32.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(dword), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, SizeOf(dword),num_written);
VirtualProtect(ppfn, sizeof(dword), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD32);
end;
inc(IID);
end;
end else begin
INH64 := MakePtr(IDH, IDH._lfanew);
if IsBadReadPtrSize( INH64, SizeOf(PIMAGE_OPTIONAL_HEADER64)) or
(INH64.Signature <> IMAGE_NT_SIGNATURE) then exit;
if (INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0) then exit;
IID := MakePtr(IDH,INH64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (IID.Name <> 0) do begin
ITD64 := MakePtr(IDH, Integer(IID.FirstThunk));
while (ITD64 <> nil) and (ITD64.Function_ <> 0) do begin
ppfn := @(ITD64.Function_);
if PDWORD(ppfn^) = PDWORD(oldfunction) then begin
VirtualProtect(ppfn, sizeof(ULONGLONG), PAGE_READWRITE, @SaveFlag);
WriteProcessMemory( GetCurrentProcess(), ppfn, @Newfunction, sizeof(ULONGLONG),num_written);
VirtualProtect(ppfn, sizeof(ULONGLONG), SaveFlag , @SaveFlag);
result := true;
end;
inc(ITD64);
end;
inc(IID);
end;
end;
end;

var
OldMessageBox: function (hWnd: HWND; lpText, lpCaption: PWidechar; uType: UINT): Integer; stdcall;

function HookMessageBox(hWnd: HWND; lpText, lpCaption: PWidechar; uType: UINT): Integer; stdcall;
begin
Result := OldMessageBox(hWnd,
PWidechar('HOOKING : '+lpText),
PWidechar('HOOKING : '+lpCaption), uType);
end;

procedure SetAPIHook;
begin
oldmessagebox := GetProcAddress(GetModuleHandle('User32.dll'), 'MessageBoxW');
HookIAT(hInstance,@oldmessagebox,@HookMessageBox);
end;

Procedure resethook;
begin
HookIAT(hInstance,@HookMessageBox,@oldmessagebox);
end;

begin
MessageBox(0, 'Test Message', 'Test Caption', 0);
SetAPIHook;
MessageBox(0, 'Test Message', 'Test Caption', 0);
resethook;
MessageBox(0, 'Test Message', 'Test Caption', 0);
end.

6. Penutup

Dengan berakhirnya bagian coding maka berakhir juga tulisan ini. Segala kekurangan yang terdapat pada tulisan itu kembali ke penulis yang mana juga adalah manusia, so mohon dimaapkan.


1. Thanks, Credit and reference

1. "Portable Executable File Format – A Reverse Engineer View by Goppit".

2. "Iczelion's Win32 Assembly Homepage".

3. Aphex For afxcodehook.

4. Uall For UallCollection.

5. Steve1028 from teachme about pe from zero

6. My Father and brother.

7. All My Friend.

8. All who read this tutorial.

Sumber : Here

0 komentar: