Jump to content

RegisterSyncCallback


Go to solution Solved by Danyfirex,

Recommended Posts

Posted

RegisterSyncCallback from autohotkey forum by @lexikos
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=21223

/*
    RegisterSyncCallback
    
    A replacement for RegisterCallback for use with APIs that will call
    the callback on the wrong thread.  Synchronizes with the script's main
    thread via a window message.
    
    This version tries to emulate RegisterCallback as much as possible
    without using RegisterCallback, so shares most of its limitations,
    and some enhancements that could be made are not.
    
    Other differences from v1 RegisterCallback:
      - Variadic mode can't be emulated exactly, so is not supported.
      - A_EventInfo can't be set in v1, so is not supported.
      - Fast mode is not supported (the option is ignored).
      - ByRef parameters are allowed (but ByRef is ignored).
      - Throws instead of returning "" on failure.
*/
RegisterSyncCallback(FunctionName, Options:="", ParamCount:="")
{
    if !(fn := Func(FunctionName)) || fn.IsBuiltIn
        throw Exception("Bad function", -1, FunctionName)
    if (ParamCount == "")
        ParamCount := fn.MinParams
    if (ParamCount > fn.MaxParams && !fn.IsVariadic || ParamCount+0 < fn.MinParams)
        throw Exception("Bad param count", -1, ParamCount)
    
    static sHwnd := 0, sMsg, sSendMessageW
    if !sHwnd
    {
        Gui RegisterSyncCallback: +Parent%A_ScriptHwnd% +hwndsHwnd
        OnMessage(sMsg := 0x8000, Func("RegisterSyncCallback_Msg"))
        sSendMessageW := DllCall("GetProcAddress", "ptr", DllCall("GetModuleHandle", "str", "user32.dll", "ptr"), "astr", "SendMessageW", "ptr")
    }
    
    if !(pcb := DllCall("GlobalAlloc", "uint", 0, "ptr", 96, "ptr"))
        throw
    DllCall("VirtualProtect", "ptr", pcb, "ptr", 96, "uint", 0x40, "uint*", 0)
    
    p := pcb
    if (A_PtrSize = 8)
    {
        /*
        48 89 4c 24 08  ; mov [rsp+8], rcx
        48 89 54'24 10  ; mov [rsp+16], rdx
        4c 89 44 24 18  ; mov [rsp+24], r8
        4c'89 4c 24 20  ; mov [rsp+32], r9
        48 83 ec 28'    ; sub rsp, 40
        4c 8d 44 24 30  ; lea r8, [rsp+48]  (arg 3, &params)
        49 b9 ..        ; mov r9, .. (arg 4, operand to follow)
        */
        p := NumPut(0x54894808244c8948, p+0)
        p := NumPut(0x4c182444894c1024, p+0)
        p := NumPut(0x28ec834820244c89, p+0)
        p := NumPut(  0xb9493024448d4c, p+0) - 1
        lParamPtr := p, p += 8
        
        p := NumPut(0xba, p+0, "char") ; mov edx, nmsg
        p := NumPut(sMsg, p+0, "int")
        p := NumPut(0xb9, p+0, "char") ; mov ecx, hwnd
        p := NumPut(sHwnd, p+0, "int")
        p := NumPut(0xb848, p+0, "short") ; mov rax, SendMessageW
        p := NumPut(sSendMessageW, p+0)
        /*
        ff d0        ; call rax
        48 83 c4 28  ; add rsp, 40
        c3           ; ret
        */
        p := NumPut(0x00c328c48348d0ff, p+0)
    }
    else ;(A_PtrSize = 4)
    {
        p := NumPut(0x68, p+0, "char")      ; push ... (lParam data)
        lParamPtr := p, p += 4
        p := NumPut(0x0824448d, p+0, "int") ; lea eax, [esp+8]
        p := NumPut(0x50, p+0, "char")      ; push eax
        p := NumPut(0x68, p+0, "char")      ; push nmsg
        p := NumPut(sMsg, p+0, "int")
        p := NumPut(0x68, p+0, "char")      ; push hwnd
        p := NumPut(sHwnd, p+0, "int")
        p := NumPut(0xb8, p+0, "char")      ; mov eax, &SendMessageW
        p := NumPut(sSendMessageW, p+0, "int")
        p := NumPut(0xd0ff, p+0, "short")   ; call eax
        p := NumPut(0xc2, p+0, "char")      ; ret argsize
        p := NumPut((InStr(Options, "C") ? 0 : ParamCount*4), p+0, "short")
    }
    NumPut(p, lParamPtr+0) ; To be passed as lParam.
    p := NumPut(&fn, p+0)
    p := NumPut(ParamCount, p+0, "int")
    return pcb
}

RegisterSyncCallback_Msg(wParam, lParam)
{
    if (A_Gui != "RegisterSyncCallback")
        return
    fn := Object(NumGet(lParam + 0))
    paramCount := NumGet(lParam + A_PtrSize, "int")
    params := []
    Loop % paramCount
        params.Push(NumGet(wParam + A_PtrSize * (A_Index-1)))
    return %fn%(params*)
}

contain machine code which I don't understand.
Anyone knowledgeable about using machine code willing to convert it to Autoit :P :D

Posted (edited)

don't know if I translate it correct or not :baby: as I am also not familiar with AHK :D

__ExampleA()
Func __ExampleA()
#cs
    p := NumPut(0x68, p+0, "char")      ; push ... (lParam data)
    lParamPtr := p, p += 4
    p := NumPut(0x0824448d, p+0, "int") ; lea eax, [esp+8]
    p := NumPut(0x50, p+0, "char")      ; push eax
    p := NumPut(0x68, p+0, "char")      ; push nmsg
    p := NumPut(sMsg, p+0, "int")
    p := NumPut(0x68, p+0, "char")      ; push hwnd
    p := NumPut(sHwnd, p+0, "int")
    p := NumPut(0xb8, p+0, "char")      ; mov eax, &SendMessageW
    p := NumPut(sSendMessageW, p+0, "int")
    p := NumPut(0xd0ff, p+0, "short")   ; call eax
    p := NumPut(0xc2, p+0, "char")      ; ret argsize
    p := NumPut((InStr(Options, "C") ? 0 : ParamCount*4), p+0, "short")
#ce

    $sMsg = '0x8000'                ;~ WM_APP
    $sHwnd = '0x000203B8'           ;~ Dummy Gui Hwnd
    $sSendMessageW = '0x76695539'   ;~ ProcAddress SendMessageW
    $ParamCount = 2
    $TParam = $ParamCount*4

    $bytecode = '0x' & '68' & '0824448d' & '50' & '68' & StringTrimLeft($sMsg, 2) & '68' & _
                StringTrimLeft($sHwnd, 2) & 'b8' & StringTrimLeft($sSendMessageW, 2) & 'd0ff' & 'c2' & $TParam
    ConsoleWrite($bytecode & @crlf)
    ConsoleWrite(BinaryLen($bytecode) & @crlf)  ;~ BinaryLen = 47
EndFunc

 

what confuse me is if BinaryLen = 47 then why the number of bytes allocate is 96 :unsure:

DllCall("GlobalAlloc", "uint", 0, "ptr", 96, "ptr")

 

Edited by jugador
Posted (edited)

Just guess :sweating: maybe we could solved this type of problem using such function(RegisterSyncCallback) :D

 

Func __Dummy_Logic()
    $pcb = DllCall("GlobalAlloc", "uint", 0, "ptr", 96, "ptr")[0]
    $sMsg = 0x8000                ;~ WM_APP
    $sHwnd = 0x000203B8           ;~ Dummy Gui Hwnd
    $sSendMessageW = 0x76695539   ;~ ProcAddress SendMessageW
    $ParamCount = 2
    $TParam = $ParamCount*4

    $lParamPtr = $pcb
    Local $tSTRUCT1 = DllStructCreate("Char[5];uint_ptr", $pcb)
    DllStructSetData($tSTRUCT1, 1, 0x68)
    DllStructSetData($tSTRUCT1, 2, $lParamPtr)

    Local $tSTRUCT2 = DllStructCreate("Int;Char[5];Char[5];Int;Char[5];Int;Char[5];Int;Short;Char[5];Short", $pcb + 4)
    DllStructSetData($tSTRUCT2, 1, 0x0824448d)
    DllStructSetData($tSTRUCT2, 2, 0x50)
    DllStructSetData($tSTRUCT2, 3, 0x68)
    DllStructSetData($tSTRUCT2, 4, $sMsg)
    DllStructSetData($tSTRUCT2, 5, 0x68)
    DllStructSetData($tSTRUCT2, 6, $sHwnd)
    DllStructSetData($tSTRUCT2, 7, 0xb8)
    DllStructSetData($tSTRUCT2, 8, $sSendMessageW)
    DllStructSetData($tSTRUCT2, 9, 0xd0ff)
    DllStructSetData($tSTRUCT2, 10, 0xc2)
    DllStructSetData($tSTRUCT2, 11, $TParam)
EndFunc

Op code out of my reach :no:
So it's upto the MVP of Autoit forum to decide to add such function(RegisterSyncCallback) or not ^_^

:bye:

Edited by jugador
Posted

Hello, I don't think it's going to help much but just convert it for fun.

 

#include <winapi.au3>
#include <Memory.au3>





MsgBox(0, "Main Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0])


Local $pCB = _RegisterSyncCallback(_MyFn)
;~ Local $pCB = _DllCallBackRegisterNormal(_MyFn)

;~ DllCallAddress("int", $pCB, "int", 123)

DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $pCB, "ptr", 124, "long", 0, "int*", 0)


MsgBox(0, "", "Click To Exit")

Exit


Func _MyFn($arg)
    MsgBox(0, "_MyFn - Child Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0] & @CRLF & "$arg:  " & Int($arg))
EndFunc   ;==>_MyFn


Func _DllCallBackRegisterNormal($Function)
    Local $hHandle = DllCallbackRegister($Function, "int", "ptr")
    Return DllCallbackGetPtr($hHandle)
EndFunc   ;==>_DllCallBackRegisterNormal


Func _RegisterSyncCallback($Function, $iOptions = 0, $iParamCount = 0)
    Local Static $hGUI
    Local Static $pSendMessage
    Local Static $iMsg

    If Not $hGUI Then
        $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200)
        $iMsg = 0x8000
        GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg)
        $pSendMessage = DllCall("Kernel32.dll", "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW")[0]
        ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF)
        ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF)
        ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    EndIf

    If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(")  ;add x64 code yourself

    Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(")
    Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode)
    Local $hHandle = DllCallbackRegister($Function, "int", "ptr") ;hardcode one parameter

    Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _
            "8D4424085068" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _
            "B8" & SwapEndian($pSendMessage) & "FFD0C20400" & _
            SwapEndian(DllCallbackGetPtr($hHandle)) & "01000000"

    DllStructSetData($tCodeBuffer, 1, $sOPCode)
    ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF)

    Return $pRemoteCode

EndFunc   ;==>_RegisterSyncCallback

Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam)
    ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF)
    ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF)
    ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF)
    ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF)
    Local $tStruct = DllStructCreate("ptr pFunction", $lParam)
    Local $tStructParameters = DllStructCreate("ptr NParameters", $lParam + 4)
    Local $tStructParametersValue = DllStructCreate("ptr Value", $wParam)

    ConsoleWrite("$tStruct.pFunction: " & $tStruct.pFunction & @CRLF)
    ConsoleWrite("$tStructParameters.NParameters: " & $tStructParameters.NParameters & @CRLF)
    ConsoleWrite("$tStructParametersValue.Value: " & $tStructParametersValue.Value & @CRLF)
    DllCallAddress("int", $tStruct.pFunction, "int", $tStructParametersValue.Value)
    Return 1
EndFunc   ;==>RegisterSyncCallback_Msg

Func SwapEndian($hex)
    Return Hex(BitOR(BitOR(BitOR(BitShift($hex, 24), _
            BitAND(BitShift($hex, -8), 0x00FF0000)), _
            BitAND(BitShift($hex, 8), 0x0000FF00)), _
            BitShift($hex, -24)), 8)
EndFunc   ;==>SwapEndian

 

 

Saludos

Posted (edited)

@Danyfirex Thanks for the code :)
I do not understand Assembly code so I will not be able to modify the code as per my need.

few question:
1) _DllCallBackRegisterNormal create 2 different thread then why no new thread created in case of _RegisterSyncCallback?

2) when use CreateThread in loop _DllCallBackRegisterNormal create 5 different thread but _RegisterSyncCallback run only once why?

For $i = 1 to 5
    DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $pCB, "ptr", 124, "long", 0, "int*", 0)
Next

3) If I want to change this from 1 param to 4 param what change should I made to Assembly code?

DllCallbackRegister($Function, "int", "ptr") ;hardcode one parameter

 

test code:

#include <winapi.au3>
#include <Memory.au3>

MsgBox(0, "Main Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "dword", "GetCurrentThreadId")[0])

Local $pCB = _RegisterSyncCallback(_MyFn)
;Local $pCB = _DllCallBackRegisterNormal(_MyFn)

;~ DllCallAddress("int", $pCB, "int", 123)

For $i = 1 to 5
    DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $pCB, "ptr", 124, "long", 0, "int*", 0)
Next

MsgBox(0, "", "Click To Exit")
Exit



Func _MyFn($arg)
    MsgBox(0, "_MyFn - Child Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "dword", "GetCurrentThreadId")[0] & @CRLF & "$arg:  " & Int($arg))
EndFunc   ;==>_MyFn


Func _DllCallBackRegisterNormal($Function)
    Local $hHandle = DllCallbackRegister($Function, "int", "ptr")
    Return DllCallbackGetPtr($hHandle)
EndFunc   ;==>_DllCallBackRegisterNormal


Func _RegisterSyncCallback($Function, $iOptions = 0, $iParamCount = 0)
    Local Static $hGUI
    Local Static $pSendMessage
    Local Static $iMsg

    If Not $hGUI Then
        $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200)
        $iMsg = 0x8000
        GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg)
        $pSendMessage = DllCall("Kernel32.dll", "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW")[0]
        ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF)
        ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF)
        ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    EndIf

    If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(")  ;add x64 code yourself

    Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(")
    Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode)
    Local $hHandle = DllCallbackRegister($Function, "int", "ptr") ;hardcode one parameter

    Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _
            "8D4424085068" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _
            "B8" & SwapEndian($pSendMessage) & "FFD0C20400" & _
            SwapEndian(DllCallbackGetPtr($hHandle)) & "01000000"

    DllStructSetData($tCodeBuffer, 1, $sOPCode)
    ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF)

    Return $pRemoteCode

EndFunc   ;==>_RegisterSyncCallback

Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam)
    ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF)
    ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF)
    ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF)
    ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF)
    Local $tStruct = DllStructCreate("ptr pFunction", $lParam)
    Local $tStructParameters = DllStructCreate("ptr NParameters", $lParam + 4)
    Local $tStructParametersValue = DllStructCreate("ptr Value", $wParam)

    ConsoleWrite("$tStruct.pFunction: " & $tStruct.pFunction & @CRLF)
    ConsoleWrite("$tStructParameters.NParameters: " & $tStructParameters.NParameters & @CRLF)
    ConsoleWrite("$tStructParametersValue.Value: " & $tStructParametersValue.Value & @CRLF)
    DllCallAddress("int", $tStruct.pFunction, "int", $tStructParametersValue.Value)
    Return 1
EndFunc   ;==>RegisterSyncCallback_Msg

Func SwapEndian($hex)
    Return Hex(BitOR(BitOR(BitOR(BitShift($hex, 24), _
            BitAND(BitShift($hex, -8), 0x00FF0000)), _
            BitAND(BitShift($hex, 8), 0x0000FF00)), _
            BitShift($hex, -24)), 8)
EndFunc   ;==>SwapEndian

 

Edited by jugador
  • Solution
Posted

Here a sample for passing two parameters (You would need to make it more dinamically)

#include <winapi.au3>
#include <Memory.au3>





MsgBox(0, "Main Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0])


Local $pCB = _RegisterSyncCallback(_MyFn, 2)
;~ Local $pCB = _DllCallBackRegisterNormal(_MyFn)

Local $tParameters = DllStructCreate("int Value1;int Value2")
$tParameters.Value1 = 10
$tParameters.Value2 = 30
;~ DllCallAddress("int", $pCB, "ptr", DllStructGetPtr($tParameters))

ConsoleWrite("DllStructGetPtr($tParameters): " & DllStructGetPtr($tParameters) & @CRLF)
DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $pCB, "ptr", DllStructGetPtr($tParameters), "long", 0, "int*", 0)


MsgBox(0, "", "Click To Exit")

Exit


Func _MyFn($arg1,$arg2)
    MsgBox(0, "_MyFn - Child Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0] & @CRLF & "$arg1:  " & Int($arg1) & @CRLF & "$arg2:  " & Int($arg2))
EndFunc   ;==>_MyFn


Func _DllCallBackRegisterNormal($Function)
    Local $hHandle = DllCallbackRegister($Function, "int", "ptr")
    Return DllCallbackGetPtr($hHandle)
EndFunc   ;==>_DllCallBackRegisterNormal


Func _RegisterSyncCallback($Function, $iParamCount = 0, $iOptions = 0)
    Local Static $hGUI
    Local Static $pSendMessage
    Local Static $iMsg

    If Not $hGUI Then
        $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200)
        $iMsg = 0x8000
        GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg)
        $pSendMessage = DllCall("Kernel32.dll", "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW")[0]
        ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF)
        ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF)
        ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    EndIf

    If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(")  ;add x64 code yourself

    Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(")
    Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode)
    Local $hHandle = DllCallbackRegister($Function, "int", "int;int") ;hardcode one parameter

    Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _
            "8D4424085068" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _
            "B8" & SwapEndian($pSendMessage) & "FFD0C2" & StringLeft(SwapEndian($iParamCount * 4), 4) & _
            SwapEndian(DllCallbackGetPtr($hHandle)) & SwapEndian($iParamCount)

    DllStructSetData($tCodeBuffer, 1, $sOPCode)
    ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF)

    Return $pRemoteCode

EndFunc   ;==>_RegisterSyncCallback

Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam)
    ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF)
    ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF)
    ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF)
    ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF)
    Local $tStruct = DllStructCreate("ptr pFunction", $lParam)
    Local $tNParameters = DllStructCreate("ptr NParameters", $lParam + 4)
    Local $tStructParameters = DllStructCreate("ptr", $wParam)

    ConsoleWrite("$tStruct.pFunction: " & $tStruct.pFunction & @CRLF)
    ConsoleWrite("$tNParameters.NParameters: " & $tNParameters.NParameters & @CRLF)
    Local $aValues[$tNParameters.NParameters]
    For $i = 0 To $tNParameters.NParameters - 1
        $aValues[$i] = DllStructGetData(DllStructCreate("int", DllStructGetData($tStructParameters, 1) + ($i * 4)),1)
        ConsoleWrite($aValues[$i] & @CRLF)
    Next
    DllCallAddress("int", $tStruct.pFunction, "int", $aValues[0],"int",$aValues[1])
    Return 1
EndFunc   ;==>RegisterSyncCallback_Msg

Func SwapEndian($hex)
    Return Hex(BitOR(BitOR(BitOR(BitShift($hex, 24), _
            BitAND(BitShift($hex, -8), 0x00FF0000)), _
            BitAND(BitShift($hex, 8), 0x0000FF00)), _
            BitShift($hex, -24)), 8)
EndFunc   ;==>SwapEndian

1: Don't now ask to the owner of the function haha (just don't want to look deeply)

 

2:I seems to be it needs some code interaction  before run the other threads, sleep works

For $i = 1 to 5
    $tParameters.Value1=$i
    DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $pCB, "ptr",DllStructGetPtr($tParameters), "long", 0, "int*", 0)
    Sleep(100) ;need some autoit interaction
Next

3: Already added using to struct and checking in the structure parameters but AutoIt does not allow to push parameters as ahk so you need to probably add some if with @NumParams.  

 

Saludos

Posted (edited)

I tried @trancexx _WinHttpSetStatusCallback example using @Danyfirex _RegisterSyncCallback funcation it worked :)

 

next is to try @ProgAndy _WinHttpSetStatusCallback code to see if it work without crash :sweating:

Edit:
_WinHttpSetStatusCallback code by @ProgAndy run smoothly without crash using @Danyfirex _RegisterSyncCallback funcation :)
for testing try EnumWindows using _RegisterSyncCallback funcation that also worked :cheer:.

 

again Thanks @Danyfirex for helping me :) :) :) :)

Edited by jugador
Posted (edited)

@Danyfirex Hopefully one last question to understand code better :D

1) How you get value 30 ? => ($pRemoteCode + 30)        
what this 30 define :sweating:

Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _
.......

 

Edited by jugador
Posted (edited)

30 is the number of bytes in the opcode till 

 p := NumPut((InStr(Options, "C") ? 0 : ParamCount*4), p+0, "short")
"char"  1
  p+4   4
"int"   4
"char"  1
"char"  1
"int"   4
"char"  1
"int"   4
"char"  1
"int"   4
"short" 2
"char"  1
"short" 2

 

 

Saludos

Edited by Danyfirex
  • 2 years later...
Posted

I'm looking back over this - and thought I'd try to explain some of the binary stuff if its helpful.

In case its not clear, we're directing callbacks to a bit of pre-compiled code.  That routine repackages the callback as a window message, and executes a sendMessage.

To make sense of the assembly, we need understand what's going on under the hood with a stdcall
. When you make a call in x86, the arguments must be placed on a stack. So take a SendMessage:

$iResult = SendMessage($hGUI, $iMsg, $wParam, $lParam)

Before we execute a CALL instruction, we first push all the params to our stack so it looks something like this:
(Note that the ($hGUI) is technically the "top" of the stack, but I'm showing it this way with the latest push at the bottom.)

ESP + 0x0C [ lParam ]
ESP + 0x08 [ wParam ]
ESP + 0x04 [ iMsg   ]
ESP + 0x00 [ hGUI   ] <-- TOP of Stack

And when we execute a CALL, an extra address automatically appears on top of stack.
This is to take note of where we executing, so we can resume our routine once the call finishes. 

+ 0x10 [ lParam  ]
+ 0x0C [ wParam  ]
+ 0x08 [ iMsg    ]
+ 0x04 [ hGUI    ] 
   ESP [ RET ADD ] ;from EIP

OK... So when a callback fires it firsts hits our "SynchronusCallback" pointer.  This is that binary code that's dumped into memory.
At this point the stack contains our arguments, and somewhere to go once we're done.

- CallbackArg2
- CallbackArg1
- Return Address

Now breaking down that binary..
The blob is in 3 parts - the executable bit at the start, some data in the middle, and a bunch of left over space. 

Executed bit:
0x681E003B048D44240850680080000068300B5800B8007D5776FFD0C20800

Data section: (pCallback & iParamCount) 
0x00003C0402000000

The machine code calls sendmessage. And now the assembly instructions start to make a bit more sense.
(EAX is basically just a register to hold 32bit data here.)

68 1E003B04 : PUSH 0x043B001E       - "Push lParam"       (A ptr to our "real" Callback address. Lives in the data part of the bin.)
8D 44 24 08 : LEA EAX, [ESP+0x08]   - "Get wParam"        (Get address where the args start - Already in the stack.)
50          : PUSH EAX              - "Push wparam"       
68 00800000 : PUSH 0x00008000       - "Push WM_APP"       (literal value)
68 300B5800 : PUSH 0x00580B30       - "Push hGui"         (hard coded in the bin)
B8 007D5776 : MOV EAX, 0x76577D00   - "Get pSendMsg"      (Load a ptr to the sendmsg func - again, hard coded in bin)
FF D0       : CALL EAX              - "Call SendMessage"
C2 08 00    : RET 8                 - "Return and clear the stck"   (8 will clear the 2 Callback args at the beggining)

Note the wParam value  (our initial callback arguments) is literally a pointer the stack.  We pull this out on the AutoIt side using  a "int CallbackAg1; int Callback2;" struct or equivalent.  Also note the "hard coded" addresses are only baked in when we generate the code at runtime.

Now focusing on the "RET 8" line... and teardown.
Our stack entering the sendmessage subroutine looks like this.

- CallbackArg2
- CallbackArg1
- RetAddress to OSCallback Mgr
- lParam (ptr to data bit of the bin)
- wParam (Ptr to CallbackArg1 above)
- iMsg   (Hard Coded)
- hGUI   (Hard Coded)
- RetAddress to Callback

With the stdcall convention, functions are responsible for clearing their own args. So after the CALL, our stack should be back to:

- CallbackArg2
- CallbackArg1
- RetAddress to OS Callback Mgr

And then we execute "RET 8":

  •  RetAddress pops back to EIP
  •  Then we need to clear our stack so. Hence the "8" bytes (or 2x 32bit arguments).

PS:  Where does the return value from SendMessage end up? It'll likely be waiting for us in EAX.  But we're not interested.

Posted

64 bit is a bit different. This is untested, but from reading up of things.

Once the callback fires, we have our parameters already loaded into CPU registers.
Where they are live depends of their type: floating-point or ints.  We will assume we have Ints to keep things simple.

Param 1 - RCX
Param 2 - RDX
Param 3 - R8
Param 4 - R9

The caller has already allocated some space on the stack for us to dump these values.
This is called the "shadow space". There must be 32 bytes there regardless of the parameter count.

So to do the dump:

MOV [RSP + 0x08], RCX
MOV [RSP + 0x10], RDX
MOV [RSP + 0x18], R8
MOV [RSP + 0x20], R9

If there are > 4 params, more space will be allocated for us to dump into - but the shadow space specifically refers to that 32-byte area.
We'll assume we have < 4 params for now. After the move instructions our stack looks like such:

------Shadow Space -----
RSP + 0x20  [Param 4]
RSP + 0x18  [Param 3]
RSP + 0x10  [Param 2]
RSP + 0x08  [Param 1]
RSP         [8-Byte Ret Address] <-Top of stack

Now we need to setup the sendmessage call.
We must allocate shadow space for the subroutine to use.

SUB RSP, 40

Note: the return address is 8 bytes. but the stack must be byte-aligned to 16bytes.
Hence we allocate 32bytes of shadow space + 8byte pad = 40 bytes (0x28 hex)

So our stack is now:

RSP + 0x48  [Param 4]
RSP + 0x40  [Param 3]
RSP + 0x38  [Param 2]
RSP + 0x30  [Param 1]
RSP + 0x28  [8-Byte Ret Address] 
-----------------------------------------------
RSP + 0x20  [8-Byte Alignment]
----------- Sendmsg shadow space  -------------
RSP + 0x18  
RSP + 0x10  
RSP + 0x08  
RSP + 0x00

And then we setup sendmessage's params.

MOV RCX, 0xXXXXXXXXXXXXXXXX         ; 1st param: Integer / hGUI
MOV RDX, 0x00008000                 ; 2nd param: Integer / iMsg ($WM_APP)
LEA R8,  [RSP + 0x30]               ; 3rd param: Pointer / wParam (ptr to parent args)
MOV R9,  0xYYYYYYYYYYYYYYYY         ; 4th param: Integer / lParam (ptr to our "real" autoit routine)

Now we can do the call by copying the grabbing the routine's ptr, and executing CALL

MOV RAX, 0xZZZZZZZZZZZZZZZZ      
CALL RAX

Unlike stdcall, we are responsible for clearing the callee's stack. 
So we should move the stack ptr forward 40 bytes (0x28 hex) to clear what we allocated earlier. Then we can finally return.

ADD RSP, 40
RET

 

All together we have:
where X = hGUI, Y = pCallback (lparam), Z = pSendMessage

MOV [RSP + 0x08], RCX
MOV [RSP + 0x10], RDX
MOV [RSP + 0x18], R8
MOV [RSP + 0x20], R9
SUB RSP, 40
MOV RCX, 0xXXXXXXXXXXXXXXXX         
MOX RXD, 0x00008000                
LEA R8,  [RSP + 0x30]              
MOV R9,  0xYYYYYYYYYYYYYYYY
MOV RAX, 0xZZZZZZZZZZZZZZZZ      
CALL RAX
ADD RSP, 40
RET

And if you trust an AI's conversion, here's the machine code: 
(EDX is basically RDX, but its 32bit.  The top bits of RDX are automatically cleared in the register)

48 89 4C 24 08          ; MOV [RSP + 0x08], RCX
48 89 54 24 10          ; MOV [RSP + 0x10], RDX
4C 89 44 24 18          ; MOV [RSP + 0x18], R8
4C 89 4C 24 20          ; MOV [RSP + 0x20], R9
48 83 EC 28             ; SUB RSP, 40
48 B9 XX XX XX XX XX    ; MOV RCX, 0xXXXXXXXXXXXXXXXX (hGUI)
      XX XX XX
BA 00 80 00 00          ; MOV EDX, 0x00008000         (iMsg - Optimized)
4C 8D 44 24 30          ; LEA R8,  [RSP + 0x30]
49 B9 YY YY YY YY YY    ; MOV R9,  0xYYYYYYYYYYYYYYYY (pCallback)
      YY YY YY
48 B8 ZZ ZZ ZZ ZZ ZZ    ; MOV RAX, 0xZZZZZZZZZZZZZZZZ (pSendMessage)
      ZZ ZZ ZZ
FF D0                   ; CALL RAX
48 83 C4 28             ; ADD RSP, 40
C3                      ; RET

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...