打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
delphi

This post is a follow-up of a related question posted here by Ran.

The accepted answer sticks to the use of the usual a plain old function.

This excerpt particularly catch my attention:

An instance method has an extra, implicit, parameter containing the instance reference, i.e. Self.

With the firm conviction that there should be a way to use a kind of "parameters" adapter (to rephrase get rid of the uneeded Self implicit reference and provide a pointer to a complying adapted callback function), I end up finding this article entitled Callback a class by Peter Morris.

To sum up, he uses thunking technique as adaptation trick. (Disclaimer: I never tested the code).

I know it's not very clean as a solution but it allows OO design with all the supposed benefits.

My Question:

Knowing that TCallbackThunk is based on the callback function signature, what would be the answer of the above refered post if doing it as Peter Morris did is the way to go?

.

asked Feb 13 '12 at 14:23
menjaraz
4,48231031


 
I'd add what some API callbacks are allowing a workaround with lParam, but some are not (eg: WinNLS on the illustration). Also, this should be called StdcallThunk or something. –  OnTheFly Feb 13 '12 at 14:55

 
This won't work because the code that is produced will not reside in an executable page. You can fix that problem. But then you will have code that can only work on x86 and fails on x64. –  David Heffernan Feb 13 '12 at 15:02

 
@David Heffernan: Thank you. My box is a x86 and any code working in Delphi 5, 7, 2007, XE will do. I must edit the post. –  menjaraz Feb 13 '12 at 15:12

 
You will need to use VirtualAlloc to allocate an executable page. Don't do anything daft and make your stack executable! But really, I can't understand why such an approach would be better than a standard function. –  David Heffernan Feb 13 '12 at 15:19

 
what will cause an illustrated code to fail is DEP. –  OnTheFly Feb 13 '12 at 15:33
show 1 more comment

1 Answer

up vote 3 down vote accepted

You don't really need to go through all that work since EnumWindows (the function in the referenced question) provides a data parameter. You can put whatever value you want there, such as the object reference demonstrated in the answer. Morris's technique is better suited for callback functions that don't provide any general-purpose data parameter.

To adapt the answer to use Morris's code, you'll first need to make sure the signature of the callback method matches the signature of the API's callback function. Since we're calling EnumWindows, we need a two-argument function returning Bool. The calling convention needs to be stdcall (because Morris's code assumes it, and it's difficult to thunk any other calling convention).

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

Next, we set up the TCallbackThunk data structure with all the machine code and the jump offset referring to the intended callback method.

However, we don't use the way Morris described. His code puts the data structure on the stack. That means we're putting executable code on the stack. Modern processors and operating systems don't allow that anymore — the OS will halt your program. We could get around that by calling VirtualProtect to modify the permissions of the current stack page, allowing it to be executed, but that makes the whole page executable, and we don't want to leave the program open for attack. Instead, we'll allocate a block of memory especially for the thunk record, separate from the stack.

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;

Note that those are 32-bit x86 instructions in that record. I have no idea what the corresponding x86_64 instructions would be.

answered Feb 13 '12 at 15:56
Rob Kennedy
101k10123258


 
+1: Very instructive! Before I accept the answer I just want to ascertain 2 things given that Morris's code is aging. 1) Is the pointer adjustement still valid (VMT/TObject changes between version) 2) Same with the hardcoded ASM instruction. I must confess I know little of ASM and BASM. Thank you. –  menjaraz Feb 13 '12 at 16:43

 
I can see clearly now why doing code injection might compromise security. –  menjaraz Feb 13 '12 at 16:50

 
The pointer adjustment is to compensate for the size of a JMP instruction. The offset in a JMP instruction is relative to the next instruction (because EIP is advanced before address calculations occur). It has nothing to do with VMT sizes or objects. X86 instructions haven't changed. –  Rob Kennedy Feb 13 '12 at 17:03

 
Thank you very much. –  menjaraz Feb 13 '12 at 17:08
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
[VBA][高阶应用] DLL动态调用
Understanding how function call works
delphi心得
How the Java Virtual Machine (JVM) Works
BASM for beginners, lesson 1B
quaggaJS-条码扫描
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服