function MyGetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall;
function MyGetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC;
var
DataDirectory: TImageDataDirectory;
P1: ^Cardinal;
P2: ^Word;
Base, NumberOfNames, AddressOfFunctions, AddressOfNames, AddressOfNameOrdinals, i, Ordinal: Cardinal;
TempStr1, TempStr2: string;
begin
Result := nil;
DataDirectory := PImageNtHeaders(Cardinal(hModule) + Cardinal(PImageDosHeader(hModule)^._lfanew))^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
P1 := Pointer(hModule + DataDirectory.VirtualAddress + 16);
Base := P1^; //輸出函式的起始序號。一般為1。
P1 := Pointer(hModule + DataDirectory.VirtualAddress + 24);
NumberOfNames := P1^; //輸出函式名的指標的陣列中的元素個數,也是輸出函式名對應的序號的陣列中的元素個數。
P1 := Pointer(hModule + DataDirectory.VirtualAddress + 28);
AddressOfFunctions := P1^; //一個RVA,指向輸出函式入口地址的陣列。
P1 := Pointer(hModule + DataDirectory.VirtualAddress + 32);
AddressOfNames := P1^; //一個RVA,指向輸出函式名的指標的陣列。
P1 := Pointer(hModule + DataDirectory.VirtualAddress + 36);
AddressOfNameOrdinals := P1^; //一個RVA,指向輸出函式名對應的序號的陣列。
Ordinal := 0;
if Cardinal(lpProcName) > $0000FFFF then
begin
//lpProcName引數指向函式名
TempStr1 := PChar(lpProcName); //要找的函式名
for i := 1 to NumberOfNames do
begin
//按順序在輸出函式名中找
P1 := Pointer(hModule + AddressOfNames + (i - 1) * 4);
TempStr2 := PChar(hModule + P1^); //當前輸出函式名
if TempStr1 = TempStr2 then
begin
//找到輸出函式
P2 := Pointer(hModule + AddressOfNameOrdinals + (i - 1) * 2);
//獲得序號,不必減Base
Ordinal := P2^;
Break;
end;
end;
end
else
//lpProcName傳過來的是序號,需減Base
Ordinal := Cardinal(lpProcName) - Base;
P1 := Pointer(hModule + AddressOfFunctions + Ordinal * 4); //P1^為函式入口RVA
if (P1^ >= DataDirectory.VirtualAddress) and (P1^ <= DataDirectory.VirtualAddress + DataDirectory.Size) then
begin
//!!!入口RVA在輸出表中,指向另一DLL的某函式,這一點很容易被忽視,很少有教程提到,也許是沒仔細看!!!
TempStr1 := PChar(hModule + P1^); //DLL.函式
TempStr2 := TempStr1;
while Pos('.', TempStr2) > 0 do
TempStr2 := Copy(TempStr2, Pos('.', TempStr2) + 1, Length(TempStr2) - Pos('.', TempStr2));
TempStr1 := Copy(TempStr1, 1, Length(TempStr1) - Length(TempStr2) - 1); //TempStr1是DLL名,TempStr2是函式名
//遞迴呼叫獲取新的函式地址
Base := GetModuleHandle(PChar(TempStr1));
if Base = 0 then
Base := LoadLibrary(PChar(TempStr1));
if Base > 0 then
Result := MyGetProcAddress(Base, PChar(TempStr2));
end
else
//RVA+基址就是函式的真實入口了
Result := Pointer(hModule + P1^);
//結果Result := GetProcAddress(hModule, lpProcName);
end;
相關:
_IMAGE_DATA_DIRECTORY = record
VirtualAddress: DWORD;
Size: DWORD;
end;
{$EXTERNALSYM _IMAGE_DATA_DIRECTORY}
TImageDataDirectory = _IMAGE_DATA_DIRECTORY;
PImageExportDirectory = ^TImageExportDirectory;
_IMAGE_EXPORT_DIRECTORY = packed record
Characteristics: DWord;
TimeDateStamp: DWord;
MajorVersion: Word;
MinorVersion: Word;
Name: DWord;
Base: DWord;
NumberOfFunctions: DWord;
NumberOfNames: DWord;
AddressOfFunctions: ^PDWORD;
AddressOfNames: ^PDWORD;
AddressOfNameOrdinals: ^PWord;
end;
{$EXTERNALSYM _IMAGE_EXPORT_DIRECTORY}
TImageExportDirectory = _IMAGE_EXPORT_DIRECTORY;
這是我用來在EncryptPE新版殼中處理IAT的原始碼,98、2003下測試透過。如果你發現問題請告訴我!
假如你是Cracker,看到這段程式碼,你準備如何設斷,避開IAT加密呢?
EncryptPE作者老王(wfs)
2004.5.6