4

この投稿は、 Ranによってここに投稿された関連する質問のフォローアップです。

受け入れられた答えは、通常の単純な古い関数の使用に固執します。

この抜粋は特に私の注意を引きます:

インスタンスメソッドには、インスタンス参照、つまりSelfを含む追加の暗黙的なパラメータがあります。

一種の「パラメーター」アダプターを使用する方法があるべきであるという確固たる信念を持って(言い換えると、不要な自己暗黙の参照を取り除き、準拠する適応コールバック関数へのポインターを提供するため)、私はCallbackというタイトルのこの記事を見つけることになりますピーターモリスによるクラス

要約すると、彼は適応トリックとしてサンクテクニックを使用しています。(免責事項:私はコードをテストしたことがありません)。

私はそれが解決策としてあまりきれいではないことを知っていますが、それはすべての想定される利点を備えたオブジェクト指向デザインを可能にします。

私の質問:

TCallbackThunkがコールバック関数のシグネチャに基づいていることを知っているので、Peter Morrisが行ったようにそれを行うことが道である場合、上記の投稿の答えは何でしょうか?

4

1 に答える 1

3

EnumWindows(参照されている質問の関数)はデータパラメーターを提供するため、実際にすべての作業を行う必要はありません。回答に示されているオブジェクト参照など、必要な値をそこに置くことができます。Morrisの手法は、汎用データパラメーターを提供しないコールバック関数に適しています。

Morrisのコードを使用するように回答を調整するには、最初に、コールバックメソッドのシグネチャがAPIのコールバック関数のシグネチャと一致することを確認する必要があります。を呼び出しているEnumWindowsので、Boolを返す2つの引数の関数が必要です。呼び出し規約はstdcallである必要があります(Morrisのコードはそれを想定しており、他の呼び出し規約をサンクするのは難しいため)。

function TAutoClickOKThread.cbEnumWindowsClickOK(
  Wnd: HWnd; Param: LParam): Bool; stdcall;
begin
  // ...
end;

TCallbackThunk次に、目的のコールバックメソッドを参照するすべてのマシンコードとジャンプオフセットを使用してデータ構造を設定します。

ただし、モリスが説明した方法は使用しません。彼のコードはデータ構造をスタックに置きます。これは、実行可能コードスタックに配置していることを意味します。最近のプロセッサとオペレーティングシステムはそれをもう許可していません—OSはあなたのプログラムを停止します。現在のスタックページのパーミッションを変更して実行できるようにすることでこれを回避できますがVirtualProtect、ページ全体が実行可能になり、プログラムを攻撃のために開いたままにしたくありません。代わりに、スタックとは別に、特にサンクレコードにメモリのブロックを割り当てます。

procedure TAutoClickOKThread.Execute;
var
  Callback: PCallbackThunk;
begin
  Callback := VirtualAlloc(nil, SizeOf(Callback^),
    Mem_Commit, Page_Execute_ReadWrite);
  try
    Callback.POPEDX := $5A;
    Callback.MOVEAX := $B8;
    Callback.SelfPtr := Self;
    Callback.PUSHEAX := $50;
    Callback.PUSHEDX := $52;
    Callback.JMP := $E9;
    Callback.JmpOffset := Integer(@TAutoClickOKThread.cbEnumWindowsClickOK)
      - Integer(@Callback.JMP) - 5;

    EnumWindows(Callback, 0);
  finally
    VirtualFree(Callback);
  end;
end;

それらはそのレコードの32ビットx86命令であることに注意してください。対応するx86_64命令がどうなるかわかりません。

于 2012-02-13T15:56:19.233 に答える