7

OK、それで私はDirectXゲームのいくつかの余分なボタンのためにいくつかのオーバーレイをしようとしています。

ここで非常にうまくオーバーレイするc++サンプルを見つけました:http://www.gamedev.net/topic/359794-c-direct3d-hooking-sample/

それで私はそれをDelphiに変換し始めました。いくつかのログを記録すると、正しいプロセスでフックが開始され、Direct3DCreate9()が正しくフックされていることがわかります。

次に、TMyDirect3D9が正常に作成されます。しかし、プロセスはここからクラッシュします。

MyDirect3D9をフックされたDirect3DCreate9()を介して元のプロセスに戻し、クラス(インターフェイス)関数の1つを呼び出そうとすると、失敗するという私の知識に基づく推測(Ollydbgでのデバッグに基づく)。

コードは次のとおりです。私が私に知らせるのを助けるために他の情報を与えることができるならば。

メインDLL:

library LeagueUtilityBox;

{$R *.res}

{$DEFINE DEBUG}

uses
  Windows,
  APIHijack in 'APIHijack.pas',
  Direct3D9 in '..\DirectX 9.0\Direct3D9.pas',
  uSharedMem in '..\Misc\uSharedMem.pas',
  MyDirect3D9 in 'MyDirect3D9.pas',
  MyDirect3DDevice9 in 'MyDirect3DDevice9.pas',
  {$IFDEF DEBUG}
  SysUtils,
  uLog in '..\Misc\uLog.pas',
  {$ENDIF}
  uMisc in 'uMisc.pas';

var
  SharedMem : TSharedMem;
  D3DHook: SDLLHook;
  hHook : DWORD;
  MyDirect3D9 : TMyDirect3D9;

function GetTargetProcess: String;
const
  KeyBase : DWORD = HKEY_CURRENT_USER;
  KeyLocation : String = 'Software\LeagueUtilityBox';
var
  RegKey : HKEY;
  TargetProcess : Array[0..511] Of Char;
  Count : DWORD;
begin
  Result := '';
  If RegOpenKeyEx(KeyBase, PChar(KeyLocation), 0, KEY_QUERY_VALUE, RegKey) = ERROR_SUCCESS Then
    begin
    Count := 512;
    If RegQueryValueEx(RegKey, nil, nil, nil, @TargetProcess[0], @Count) = ERROR_SUCCESS Then
      begin
      Result := String(TargetProcess);
    end;
  end;
end;

type
  TDirect3DCreate9 = function(SDKVersion: LongWord): Pointer; stdcall;

function MyDirect3DCreate9(SDKVersion: LongWord): Pointer; stdcall;
var
  OldFunc : TDirect3DCreate9;
  D3D : PIDirect3D9;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'MyDirect3DCreate9 called');
  {$ENDIF}
  Result := nil;
  OldFunc := TDirect3DCreate9(D3DHook.Functions[0].OrigFn);
  D3D := OldFunc(SDKVersion);
  If D3D <> nil Then
    begin
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'D3D created: 0x' + IntToHex(DWORD(Pointer(D3D)), 8));
    {$ENDIF}
    New(MyDirect3D9);
    MyDirect3D9 := TMyDirect3D9.Create(D3D);
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'MyDirect3D9 Created');
    {$ENDIF}
    Result := @MyDirect3D9;
  end;
end;

procedure InitializeHook;
var
  Process : String;
  I : Integer;
begin
  SetLength(Process, 512);
  GetModuleFileName(GetModuleHandle(nil), PChar(Process), 512);
  For I := Length(Process) DownTo 1 Do
    begin
    If Process[I] = '\' Then Break;
  end;
  Process := Copy(Process, I + 1, Length(Process));
  If CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, PChar(GetTargetProcess), -1, PChar(Process), -1) = 2 Then
    begin
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'Found target: ' + GetTargetProcess);
    {$ENDIF}
    With D3DHook Do
      begin
      Name := 'D3D9.DLL';
      UseDefault := False;
      DefaultFn := nil;
      SetLength(Functions, 1);
      Functions[0].Name := 'Direct3DCreate9';
      Functions[0].HookFn := @MyDirect3DCreate9;
      Functions[0].OrigFn := nil;
    end;
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'About to hook: ' + String(AnsiString(D3DHook.Name)));
    {$ENDIF}
    HookAPICalls(@D3DHook);
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'Hook completed: ' + String(AnsiString(D3DHook.Name)));
    {$ENDIF}
  end;
end;

procedure InitializeDLL;
begin
  SharedMem := TSharedMem.Create('LeagueUtilityBox', 1024);
  Try
    hHook := PDWORD(SharedMem.Buffer)^;
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'Initializing DLL: ' + IntToStr(hHook));
    {$ENDIF}
  Finally
    SharedMem.Free;
  end;
end;

procedure UninitializeDLL;
begin
  UnhookWindowsHookEx(hHook);
end;

function WindowsHookCallback(nCode: Integer; WPARAM: Integer; LPARAM: Integer): LRESULT; stdcall;
begin
  Result := CallNextHookEx(hHook, nCode, WPARAM, LPARAM);
end;

procedure EntryPoint(Reason: DWORD);
begin
  Case Reason Of
    DLL_PROCESS_ATTACH:
      begin
      InitializeDLL;
      InitializeHook;
    end;
    DLL_PROCESS_DETACH:
      begin
      UninitializeDLL;
    end;
  end;
end;

exports
  WindowsHookCallback;

begin
  DLLProc := @EntryPoint;
  EntryPoint(DLL_PROCESS_ATTACH);
end.

カスタムIDirect3D9:

unit MyDirect3D9;

interface

uses Direct3D9, Windows, uMisc, uLog;

type
  PMyDirect3D9 = ^TMyDirect3D9;
  TMyDirect3D9 = class(TInterfacedObject, IDirect3D9)
  private
    fD3D: PIDirect3D9;
  public
    constructor Create(D3D: PIDirect3D9);

    function QueryInterface(riid: REFIID; ppvObj: PPointer): HRESULT; stdcall;
    function _AddRef: DWORD; stdcall;
    function _Release: DWORD; stdcall;

    function RegisterSoftwareDevice(pInitializeFunction: Pointer): HResult; stdcall;
    function GetAdapterCount: LongWord; stdcall;
    function GetAdapterIdentifier(Adapter: LongWord; Flags: DWord; out pIdentifier: TD3DAdapterIdentifier9): HResult; stdcall;
    function GetAdapterModeCount(Adapter: LongWord; Format: TD3DFormat): LongWord; stdcall;
    function EnumAdapterModes(Adapter: LongWord; Format: TD3DFormat; Mode: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
    function GetAdapterDisplayMode(Adapter: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
    function CheckDeviceType(Adapter: LongWord; CheckType: TD3DDevType; AdapterFormat, BackBufferFormat: TD3DFormat; Windowed: BOOL): HResult; stdcall;
    function CheckDeviceFormat(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat: TD3DFormat; Usage: DWord; RType: TD3DResourceType; CheckFormat: TD3DFormat): HResult; stdcall;
    function CheckDeviceMultiSampleType(Adapter: LongWord; DeviceType: TD3DDevType; SurfaceFormat: TD3DFormat; Windowed: BOOL; MultiSampleType: TD3DMultiSampleType; pQualityLevels: PDWORD): HResult; stdcall;
    function CheckDepthStencilMatch(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat, RenderTargetFormat, DepthStencilFormat: TD3DFormat): HResult; stdcall;
    function CheckDeviceFormatConversion(Adapter: LongWord; DeviceType: TD3DDevType; SourceFormat, TargetFormat: TD3DFormat): HResult; stdcall;
    function GetDeviceCaps(Adapter: LongWord; DeviceType: TD3DDevType; out pCaps: TD3DCaps9): HResult; stdcall;
    function GetAdapterMonitor(Adapter: LongWord): HMONITOR; stdcall;
    function CreateDevice(Adapter: LongWord; DeviceType: TD3DDevType; hFocusWindow: HWND; BehaviorFlags: DWord; pPresentationParameters: PD3DPresentParameters; out ppReturnedDeviceInterface: IDirect3DDevice9): HResult; stdcall;
  end;

implementation

uses MyDirect3DDevice9;

constructor TMyDirect3D9.Create(D3D: PIDirect3D9);
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.Create');
  {$ENDIF}
  fD3D := D3D;
end;

function TMyDirect3D9.QueryInterface(riid: REFIID; ppvObj: PPointer): HRESULT; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.QueryInterface');
  {$ENDIF}
  Result := fD3D^.QueryInterface(riid, ppvObj);
end;

function TMyDirect3D9._AddRef: DWORD; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9._AddRef');
  {$ENDIF}
  Result := fD3D^._AddRef;
end;

function TMyDirect3D9._Release: DWORD; stdcall;
var
  count : DWORD;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9._Release');
  {$ENDIF}
  count := fD3D^._Release;
  If count = 0 Then
    begin
    Self.Free;
  end;
  Result := count;
end;

function TMyDirect3D9.RegisterSoftwareDevice(pInitializeFunction: Pointer): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.RegisterSoftwareDevice');
  {$ENDIF}
  Result := fD3D^.RegisterSoftwareDevice(pInitializeFunction);
end;

function TMyDirect3D9.GetAdapterCount: LongWord; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterCount');
  {$ENDIF}
  Result := fD3D^.GetAdapterCount;
end;

function TMyDirect3D9.GetAdapterIdentifier(Adapter: LongWord; Flags: DWord; out pIdentifier: TD3DAdapterIdentifier9): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterIdentifier');
  {$ENDIF}
  Result := fD3D^.GetAdapterIdentifier(Adapter, Flags, pIdentifier);
end;

function TMyDirect3D9.GetAdapterModeCount(Adapter: LongWord; Format: TD3DFormat): LongWord; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterModeCount');
  {$ENDIF}
  Result := fD3D^.GetAdapterModeCount(Adapter, Format);
end;

function TMyDirect3D9.EnumAdapterModes(Adapter: LongWord; Format: TD3DFormat; Mode: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.EnumAdapterModes');
  {$ENDIF}
  Result := fD3D^.EnumAdapterModes(Adapter, Format, Mode, pMode);
end;

function TMyDirect3D9.GetAdapterDisplayMode(Adapter: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterDisplayMode');
  {$ENDIF}
  Result := fD3D^.GetAdapterDisplayMode(Adapter, pMode);
end;

function TMyDirect3D9.CheckDeviceType(Adapter: LongWord; CheckType: TD3DDevType; AdapterFormat, BackBufferFormat: TD3DFormat; Windowed: BOOL): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceType');
  {$ENDIF}
  Result := fD3D^.CheckDeviceType(Adapter, CheckType, AdapterFormat, BackBufferFormat, Windowed);
end;

function TMyDirect3D9.CheckDeviceFormat(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat: TD3DFormat; Usage: DWord; RType: TD3DResourceType; CheckFormat: TD3DFormat): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceFormat');
  {$ENDIF}
  Result := fD3D^.CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat);
end;

function TMyDirect3D9.CheckDeviceMultiSampleType(Adapter: LongWord; DeviceType: TD3DDevType; SurfaceFormat: TD3DFormat; Windowed: BOOL; MultiSampleType: TD3DMultiSampleType; pQualityLevels: PDWORD): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceMultiSampleType');
  {$ENDIF}
  Result := fD3D^.CheckDeviceMultiSampleType(Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels);
end;

function TMyDirect3D9.CheckDepthStencilMatch(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat, RenderTargetFormat, DepthStencilFormat: TD3DFormat): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDepthStencilMatch');
  {$ENDIF}
  Result := fD3D^.CheckDepthStencilMatch(Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat);
end;

function TMyDirect3D9.CheckDeviceFormatConversion(Adapter: LongWord; DeviceType: TD3DDevType; SourceFormat, TargetFormat: TD3DFormat): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceFormatConversion');
  {$ENDIF}
  Result := fD3D^.CheckDeviceFormatConversion(Adapter, DeviceType, SourceFormat, TargetFormat);
end;

function TMyDirect3D9.GetDeviceCaps(Adapter: LongWord; DeviceType: TD3DDevType; out pCaps: TD3DCaps9): HResult; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetDeviceCaps');
  {$ENDIF}
  Result := fD3D^.GetDeviceCaps(Adapter, DeviceType, pCaps);
end;

function TMyDirect3D9.GetAdapterMonitor(Adapter: LongWord): HMONITOR; stdcall;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterMonitor');
  {$ENDIF}
  Result := fD3D^.GetAdapterMonitor(Adapter);
end;

function TMyDirect3D9.CreateDevice(Adapter: LongWord; DeviceType: TD3DDevType; hFocusWindow: HWND; BehaviorFlags: DWord; pPresentationParameters: PD3DPresentParameters; out ppReturnedDeviceInterface: IDirect3DDevice9): HResult; stdcall;
var
  hr : HRESULT;
begin
  {$IFDEF DEBUG}
  WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CreateDevice');
  {$ENDIF}
  hr := fD3D^.CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
  If Succeeded(hr) Then
    begin
    {$IFDEF DEBUG}
    WriteToLog('C:\LeagueUtilityBox.log', 'fD3D^.CreateDevice Succeeded');
    {$ENDIF}
    ppReturnedDeviceInterface := TMyDirect3DDevice9.Create(PIDirect3D9(@Self), @ppReturnedDeviceInterface);
  end;
  Result := hr;
end;

end.

更新: つまり、Delphiインターフェイスは実際のインターフェイスとは異なる動作をするように見えるためです(Delphiには、他のインターフェイスと正常に通信するための中間があります)。そのため、インターフェイスをポインタの配列に変換しました。

これで、プログラムはCreateDevice()を正常に呼び出します。これは、ログとOllydbgのステップスルーの両方で確認できます。

ここで、CreateDeviceが元のIDirect3D9.CreateDevice()を呼び出すと、再びクラッシュします。Ollydbgでデバッグすると、ポインターの間接参照が多すぎることに気付きます。

更新2: わかりました。さまざまな場所でのPIDirect3D9とIDirect3D9のポインターの問題を修正しました。したがって、元のIDirect3D9.CreateDevice()が呼び出されます。しかし、それはD3DERR_INVALIDCALLでエラーになります!!

とても紛らわしい。

更新3: わかりました。さらにデバッグを行うと、関数を呼び出すと、追加のパラメーターがスタックにプッシュされるようです。これにより、最初のパラメータが無効になります。これは、iAdapterパラメーターが無効(最初のパラメーター)であると言うDirectXデバッグによってさらに証明されます。

更新4: IntRefToMethPtr()を使用して、元のCreateDevice呼び出しへの直接ポインターを取得することで、同じスタックで呼び出すことができました。同じ結果。Delphiに接続しようとして、間違ったルートをたどったようです。

更新5: フッキングメソッドを書き直しました。今、私は本質的にEndScene()をフックしています。フックは、テストプログラム(この投稿の最初のURLにあるフックデモに付属のVertices.exe)で正常に動作するようになりました。しかし、メインゲームではゲームがクラッシュします。いずれにせよ、私はたくさんのことを学びました。

4

3 に答える 3

3

私はこれを数回行いましたが、詳細を回答に入れるには少し広範ですが、いくつかの一般的な落とし穴と、取り組む必要があるいくつかの特定の落とし穴があります.

まず、IDirect3D9 および IDirect3DDevice9 インターフェイスを (少なくとも)ライブラリで行われるのとまったく同じように、またはバイナリ互換で実装する必要があります。インターフェイスは仮想関数呼び出しに基づいているため (Pascal に相当するかどうかはわかりません)、すべてのメソッドは仮想でなければならず、メソッドは同じ順序で同じ引数を取る必要があります (これも同じ順序である必要があります)。

__thiscall私が詳しく調べたい部分は、pascal が関数を処理する方法です。関数は、Visual C++ でビルドされたコード ( 、仮想など)とバイナリ互換である必要があります。

さらに、いくつかの単純な正規表現を使用して、通常、既存の D3D9 ヘッダーからカスタム ヘッダーとコード ファイルのスケルトンを生成できます。これはばかげているように聞こえるかもしれませんが、本格的な D3D9 ラッパーは (IDirect3DDevice9 だけで) 2300 行になることに注意してください。ヘッダーから基本を生成すると、エラーの原因となる可能性のある多くの入力が省略されます。

ボタンを処理するには、a) 既存のレンダリングの上に描画し、b) 入力をキャッチする必要もあります。

a) 些細なことです:device->Present()本当のプレゼントを呼び出す前に、ただ待って絵を描くだけです。唯一の本当の落とし穴は、デバイスの状態です。既存の状態を保存し、オーバーレイ描画用に状態を設定してから、デバイスの状態をリセットする必要があります。それらを適切にリセットしないと、あらゆる種類の楽しい問題が発生します。設定が必要な状態はアプリによって異なりますが、通常はカリング、深度ソート/テストなどを無効にする必要があります。

b) ボタンを実行するには、何らかの方法でウィンドウの入力にもフックする必要があります。ここにあるのと同じ種類のラッパーを実装しますが、DInput (使用されている場合) に対しては、おそらく良い考えです。次に、I...Device9 ラッパーの Present メソッドで、入力チェック、レンダリング、およびロジックを実行できます。

これらのようなラッパー用のまともなコードもあります。私は完全な d3d 8-to-9 (ランタイム変換) と 9 を持っており、再利用できる他の 2 つの d3d9 ラッパーを知っています。コードをチェックするか、既存のコードを使用するために、検討する価値があるかもしれません。

これに関して興味のある情報や興味深い情報があれば、喜んでお手伝い/知りたいと思います。

于 2011-06-29T19:12:50.407 に答える
1

Delphi 用の既存の DirextX パッケージを調べて、使用されている構造が使用されているものと同じであることを (その例で) 確認することもできます。

私が最もよく知っているサイトは Clootie のサイトです : http://clootie.ru/delphi/index.html

DX9 と DX10 の両方の SDK とサンプルが含まれています。

于 2011-07-02T10:52:19.440 に答える
1

clootie の D3D9 SDK を使用すると、完全に機能する D3D9 Proxy DLL を取得できます。

http://sourceforge.net/projects/delphi-dx9sdk/

およびGDの「高度な」d3d9ベース

http://www.gamedeception.net/attachment.php?attachmentid=3035&d=1260299029

Delphi Architect XE3 で使用し、コンパイルして正常に動作します。

于 2012-11-01T17:33:25.717 に答える