delphi 打造MyGetProcAddress函数(Delphi源码)

2018-10-31

标 题: 打造MyGetProcAddress函数(Delphi源码)
作 者: 老王

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
阅读56