DLLにおいて呼び出し元のモジュール名を得る方法

昔、Delphi-MLに投稿した、DLLにおいて呼び出し元のモジュール名を得るためのサンプル。
自作DLLの利用を制限させたい場合などに利用できるかも知れない。過去ログから探すのに苦労したため、今後のためにここに貼っておく。

まず、スタックをさかのぼり、DLLの呼び出し元(リターンアドレス)を取得する。(asm〜endの、アセンブラ記述部)。次に、Toolhelp32を利用してロードされているモジュール一覧とそのアドレスを列挙し、リターンアドレスがどのモジュールの範囲に含まれているかを検索している。
自作DLLに組み込む場合、関数のネストする段数によってスタック内でのリターンアドレスの位置が変わることに注意。


以下、呼び出し元の EXE/DLL のモジュール名を返す DLL のサンプルです。モジュールごとのアドレスを取得するのに Toolhelp32 を使っていますので、NT4 では動きません。

foo.exe --> Sample.dll という呼び出しであれば、'path\foo.exe' を返し、foo.exe --> bar.dll --> Sample.dll という呼び出しであれば、'path\bar.dll'を返します。

動作確認は Delphi 5 on Windows 98SE ですので、他ではもしかすると動作しないかもしれません。

library Sample;
uses SysUtils, Windows;

const
  TH32CS_SNAPMODULE = $8;
  MAX_MODULE_NAME32 = 255;

type
  TMODULEENTRY32 = record
    dwSize:        DWORD;
    th32ModuleID:  DWORD;
    th32ProcessID:  DWORD;
    GlblcntUsage:  DWORD;
    ProccntUsage:  DWORD;
    modBaseAddr:    DWORD;
    modBaseSize:    DWORD;
    hModule:        HMODULE;
    szModule:      array [0..MAX_MODULE_NAME32] of Char;
    szExePath:      array [0..MAX_PATH] of Char;
  end;
  LPMODULEENTRY32 = ^TMODULEENTRY32;

function CreateToolhelp32Snapshot( dwFlags:DWORD; th32ProccessID:DWORD ):THANDLE stdcall; external 
'kernel32.dll';
function Module32First( hSnapshot:THandle; lpme:LPMODULEENTRY32 ):LongBool stdcall; external 
'kernel32.dll';
function Module32Next( hSnapshot:THandle; lpme:LPMODULEENTRY32 ):LongBool stdcall; external 
'kernel32.dll';

function GetCallerModuleName( P:PChar; dwSize:DWORD ):LongBool; stdcall;
var
  h: DWORD;    
  Ent: TMODULEENTRY32;
  Caller: DWORD;
begin
  Result := False;
  asm
    push  ebx
    mov  ebx, ebp
    add  ebx, 4
    mov  eax, [ebx]
    mov  Caller, eax
    pop  ebx
  end;
  h := CreateToolhelp32snapshot( TH32CS_SNAPMODULE, GetCurrentProcessID );
  if h <> $FFFFFFFF then
  try
    Ent.dwSize := SizeOf( Ent );
    if Module32First( h, @Ent ) then
    repeat
      if ( Ent.modBaseAddr <= Caller ) and
        ( Caller <= Ent.modBaseAddr + Ent.modBaseSize ) then
      begin
        Result := True;
        StrLCopy( P, Ent.szExePath, dwSize );
        break;
      end;
    until not Module32Next( h, @Ent );
  finally
    CloseHandle( h );
  end;
end;

exports GetCallerModuleName;

begin
end;