Jump to content

Recommended Posts

  • 7 months later...
Posted (edited)

Here is a bit faster/cleaner version

It should be a bit easier to understand too

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

;Au3CallByName, Bilgus
Global $Au3_CallByName = 0
Global $hKernel32 = DllOpen("Kernel32.dll")
OnAutoItExitRegister(__CallByNameCleanup)


Func __CallByNameCleanup()
    Au3_CallByName_Init(False) ;Unload
    DllClose($hKernel32)
EndFunc   ;==>__CallByNameCleanup

; Takes a pointer to the v-table in a class and replaces specified member Id in it to a new one.
Func __HookVTableEntry($pVtable, $iVtableOffset, $pHook, ByRef $pOldRet)
    ;;https://www.autoitscript.com/forum/topic/107678-hooking-into-the-idispatch-interface/
    Local Const $PAGE_READWRITE = 0x04
    Local $tpVtable = DllStructCreate("ptr", $pVtable)
    Local $szPtr = DllStructGetSize($tpVtable)
    Local $pFirstEntry, $pEntry, $tEntry, $aCall, $flOldProtect, $bStatus

    ; Dereference the vtable pointer
    $pFirstEntry = DllStructGetData($tpVtable, 1)
    $pEntry = $pFirstEntry + ($iVtableOffset * $szPtr) ;vtable is a big array of pointers 

    ; Make the memory free for all. Yay!
    $aCall = DllCall($hKernel32, "int", "VirtualProtect", "ptr", $pEntry, "long", $szPtr, "dword", $PAGE_READWRITE, "dword*", 0)

    If @error Or Not $aCall[0] Then
        ConsoleWriteError("Error: Failed To hook vTable" & @CRLF)
        Return False
    EndIf
    $flOldProtect = $aCall[4]

    $tEntry = DllStructCreate("ptr", $pEntry)
    $pOldRet = DllStructGetData($tEntry, 1)
    If $pOldRet <> $pHook Then
        DllStructSetData($tEntry, 1, $pHook)
        $bStatus = True
    Else ;Already Hooked
        ConsoleWriteError("Error: vTable is already hooked" & @CRLF)
        $bStatus = False
    EndIf

    ;put the memory protect back how we found it
    DllCall($hKernel32, "int", "VirtualProtect", "ptr", $pEntry, "long", $szPtr, "dword", $flOldProtect, "dword*", 0)
    Return $bStatus
EndFunc   ;==>__HookVTableEntry

; Everytime autoit wants to call a method, get or set a property in a object it needs to go to
; IDispatch::GetIDsFromNames. This is our version of that function, note that by defining this ourselves
; we can fool autoit to believe that the object supports a lot of different properties/methods.
Func __IDispatch_GetIDsFromNames($pSelf, $riid, $rgszNames, $cNames, $lcid, $rgDispId)
    Local Const $DISP_E_UNKNOWNNAME = 0x80020006, $DISPID_UNKNOWN = -1
    Local Static $pGIFN = __Pointer_GetIDsFromNames()
    Local $hRes

    ;Call the original GetIDsFromNames
    $hRes = DllCallAddress("LRESULT", $pGIFN, "ptr", $pSelf, "ptr", $riid, _
            "struct*", $rgszNames, "dword", $cNames, "dword", $lcid, "ptr", $rgDispId)
    If @error Then
        ConsoleWriteError("Error: GetIDsFromNames: " & @error & @CRLF)
        Return $DISP_E_UNKNOWNNAME
    EndIf

    ; Autoit didnt find the name now its our turn
    If $DISPID_UNKNOWN = DllStructGetData(DllStructCreate("long[" & $cNames & "]", $rgDispId), 1, 1) Then
        ;$rgszNames is a pointer to an array[$cNames] of names -- Autoit only asks for one member $cNames = 1
        Local $tName = DllStructCreate("wchar[64]", DllStructGetData(DllStructCreate("ptr[" & $cNames & "]", $rgszNames), 1, 1))
        Local $sName = DllStructGetData($tName, 1)

        ;We just prepend CBN_CB_ to the function name and try to call it
        $hRes = Call("CBN_CB_" & $sName, $sName, $pGIFN, $pSelf, $riid, $rgszNames, $cNames, $lcid, $rgDispId)
        If Not @error And $hRes <> Default Then Return $hRes ; User handled the function

        ;Call the original GetIDsFromNames
        $hRes = DllCallAddress("LRESULT", $pGIFN, "ptr", $pSelf, "ptr", $riid, _
                "struct*", $rgszNames, "dword", $cNames, "dword", $lcid, "ptr", $rgDispId)
        If @error Then
            ConsoleWrite("Error: GetIDsFromNames: " & @error & @CRLF)
            Return $DISP_E_UNKNOWNNAME
        EndIf
    EndIf
    Return $hRes[0]
EndFunc   ;==>__IDispatch_GetIDsFromNames

Func __Pointer_GetIDsFromNames($ptr = 0)
    ;Stores pointer to the original 'GetIDsFromNames'
    Local Static $pOldGIFN = $ptr
    If $ptr <> 0 Then $pOldGIFN = $ptr
    Return $pOldGIFN
EndFunc   ;==>__Pointer_GetIDsFromNames

Func Au3_CallByName_Init($bHook = True, $classname = "shell.application")
    Local Const $iOffset_GetIDsFromNames = 5 ;vtable index

    Local Static $IDispatch_GetIDsFromNames_Callback = 0
    Local $oObject, $pObject, $pHook, $pOldGIFN

    If $bHook Then
        If $IDispatch_GetIDsFromNames_Callback = 0 Then
            $IDispatch_GetIDsFromNames_Callback = DllCallbackRegister("__IDispatch_GetIDsFromNames", _
                                                                      "LRESULT", "ptr;ptr;ptr;dword;dword;ptr")
        EndIf
        $pHook = DllCallbackGetPtr($IDispatch_GetIDsFromNames_Callback)
    Else
        $pHook = __Pointer_GetIDsFromNames()
        If $pHook <= 0 Then Return ;Already Unloaded
    EndIf

    $oObject = ObjCreate($classname)
    $pObject = DllStructSetData(DllStructCreate("ptr"), 1, $oObject)

    If __HookVTableEntry($pObject, $iOffset_GetIDsFromNames, $pHook, $pOldGIFN) Then
        __Pointer_GetIDsFromNames($pOldGIFN) ;Save the original pointer to GetIDsFromNames
        If Not $bHook Then
            DllCallbackFree($IDispatch_GetIDsFromNames_Callback)
            $IDispatch_GetIDsFromNames_Callback = 0
        EndIf
    Else
        ;Error
    EndIf

    $oObject = 0
EndFunc   ;==>Au3_CallByName_Init

;|||||||||===========|||||||||
;||||||||| CallBacks |||||||||
Func CBN_CB_Au3_CallByName($sName, $pGIFN, $pSelf, $riid, $rgszNames, $cNames, $lcid, $rgDispId)
    Local Const $DISP_E_UNKNOWNNAME = 0x80020006
    ConsoleWrite(">Call By Name: " & $sName &  " -> " & $Au3_CallByName & @crlf)
    Local Static $tpMember = DllStructCreate("ptr")
    If $Au3_CallByName Then
        Local $hRes, $tMember

        ;ConsoleWrite("CallByName: " & $Au3_CallByName & @CRLF)
        $tMember = DllStructCreate("wchar[" & StringLen($Au3_CallByName) + 1 & "]")
        DllStructSetData($tMember, 1, $Au3_CallByName)
        DllStructSetData($tpMember, 1, DllStructGetPtr($tMember))
        $rgszNames = $tpMember
        $Au3_CallByName = 0

        ;Call the original GetIDsFromNames
        $hRes = DllCallAddress("LRESULT", $pGIFN, "ptr", $pSelf, "ptr", $riid, _
                "struct*", $rgszNames, "dword", $cNames, "dword", $lcid, "ptr", $rgDispId)
        If @error Then
            ConsoleWrite("Error: Call By Name: " & @error & @CRLF)
            Return $DISP_E_UNKNOWNNAME
        EndIf
        Return $hRes[0]
    EndIf
    Return Default ;Default handler
EndFunc

;||||||||| CallBacks |||||||||
;|||||||||===========|||||||||

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;TESTS;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Global $oDictionary = ObjCreate("Scripting.Dictionary")

Au3_CallByName_Init()

Sleep(1000)

For $i = 1 To 3
    $Au3_CallByName = "Add"
    $oDictionary.Au3_CallByName("test1:" & $i, "Dictionary Item: " & $i)
Next

$Au3_CallByName = "keys"
For $sKey In $oDictionary.Au3_CallByName()
    For $j = 0 To 1
        $Au3_CallByName = ($j = 0) ? "Item" : "Exists"
        ConsoleWrite($sKey & "[" & $Au3_CallByName & "] -> " & $oDictionary.Au3_CallByName($sKey) & @CRLF)
    Next
Next
Au3_CallByName_Init(False) ;Unload (Not Strictly Needed, Done on Script Close)

Same Idea but now we only go looking for our function if Autoit indicates that it doesn't exist

 

Edited by Bilgus
  • 3 months later...
  • 1 year later...
Posted

Hi, instead of asking here i started a new topic because i think you would be offline for quite some time, but if you see this, please take a look. Thank you!

 

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...