How to pass a method as a callback to a Windows API call (subsequent)?

This post is a continuation of the related question posted here by Ran ..

the accepted answer uses the usual plain old function.

This passage particularly attracts my attention:

The instance method has an additional, implicit parameter containing the instance of the instance, that is, Self.

With the firm conviction that there must be a way to use some sort of β€œparameter” adapters (to rephrase get rid of the raw self-supporting link and provide a pointer to the appropriate adapted callback function), I end up finding this article called Peter Morris Class Callback .

To summarize, he uses the thunking method as an adaptive trick. (Disclaimer: I have never tested the code).

I know that this is not a very clean solution, but it allows you to create OO with all the expected benefits.

My question is:

Knowing that TCallbackThunk is based on the signature of the callback function, what will be the response of the above link if you do this, as Peter Morris did?

.

+4
source share
1 answer

You do not have to go through all this work, since EnumWindows (the function in the question asked) provides a data parameter. You can put any value you want there, for example, a link to the object shown in the answer. The Morris method is better suited for callback functions that do not provide any general-purpose data parameter.

To adapt the response to use Morris code, you first need to make sure that the signature of the callback method matches the signature of the API callback function. Since we call EnumWindows , we need a two-argument function that returns Bool. The calling convention should be stdcall (because the Morris code suggests this, and it’s hard to ban any other agreement).

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

Then we create a TCallbackThunk data TCallbackThunk with all the machine code and the transition offset, referring to the intended callback method.

However, we do not use the method described by Morris. Its code pushes the data structure onto the stack. This means that we push the executable code onto the stack. Modern processors and operating systems no longer allow this - the OS will stop your program. We could get around this by calling VirtualProtect to change the permissions of the current stack page, allowing it to execute, but this makes the whole page executable, and we don’t want to leave the program open for attack. Instead, we allocate a block of memory, especially for thunk, separately 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; 

Please note that these are 32-bit x86 instructions in this entry. I have no idea what the corresponding x86_64 instructions will be.

+3
source

Source: https://habr.com/ru/post/1396215/


All Articles