你实际上不需要做那么多工作,因为
EnumWindows
(引用问题中的函数)提供了一个数据参数。你可以在那里放置任何想要的值,例如答案中演示的对象引用。Morris的技术更适用于不提供任何通用数据参数的回调函数。
要使答案适应使用Morris的代码,首先需要确保回调方法的签名与API的回调函数的签名匹配。由于我们调用的是
EnumWindows
,所以我们需要一个返回Bool的两个参数函数。调用约定需要是stdcall(因为Morris的代码假定它,并且很难 thunk 任何其他调用约定)。
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指令是什么。