accepted answer坚持使用通常的普通旧功能。
这段摘录特别引起我的注意:
实例方法有一个额外的隐式参数,其中包含 实例引用,即Self。
坚信应该有一种方法可以使用一种“参数”适配器(重新定义摆脱不必要的Self隐式引用并提供指向适应的自适应回调函数的指针),我最终找到了这个article名为按Peter Morris回复课程。
总之,他使用thunking技术作为适应技巧。 (免责声明:我从未测试过代码)。
我知道它不是一个非常干净的解决方案,但它允许OO设计具有所有所谓的好处。
我的问题:
知道 TCallbackThunk 是基于回调函数签名的,如果像Peter Morris那样做的话,上面提到的帖子的答案是什么呢?
答案 0 :(得分:3)
由于EnumWindows
(引用问题中的函数)提供了数据参数,因此您不需要完成所有这些工作。你可以在那里放置你想要的任何值,例如答案中展示的对象参考。 Morris的技术更适合于不提供任何通用数据参数的回调函数。
要使答案适应使用Morris的代码,首先需要确保回调方法的签名与API的回调函数的签名相匹配。因为我们正在调用EnumWindows
,所以我们需要一个返回Bool的双参数函数。调用约定需要是stdcall(因为Morris的代码假设它,并且很难破坏任何其他调用约定。)
function TAutoClickOKThread.cbEnumWindowsClickOK(
Wnd: HWnd; Param: LParam): Bool; stdcall;
begin
// ...
end;
接下来,我们设置TCallbackThunk
数据结构,其中包含所有机器代码和跳转偏移,指的是预期的回调方法。
但是,我们不使用Morris描述的方式。他的代码将数据结构放在堆栈上。这意味着我们将可执行代码放在堆栈上。现代处理器和操作系统不再允许这样 - 操作系统将停止您的程序。我们可以通过调用VirtualProtect
来修改当前堆栈页面的权限,允许它被执行,但这使整个页面可执行,我们不想让程序保持打开以进行攻击。相反,我们将为thunk记录分配一块内存,与堆栈分开。
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指令是什么。