打造MyGetProcAddress函式(Delphi原始碼)

看雪資料發表於2004-05-06

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

相關文章