打造MyGetProcAddress函数

本文介绍了一种自定义的GetProcAddress函数实现方法,该方法用于在Windows环境下查找DLL导出函数的地址,通过解析PE文件格式来定位函数名及其对应的序号,并处理了函数地址位于其他DLL的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--------------------------------------------------------------------------------
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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值