AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

function hooks
Goto page Previous  1, 2
 
Reply to topic    AutoHotkey Community Forum Index -> Wish List
View previous topic :: View next topic  

advice is best for end user programming
yes
66%
 66%  [ 2 ]
no
33%
 33%  [ 1 ]
Total Votes : 3

Author Message
tinku99



Joined: 03 Aug 2007
Posts: 513
Location: Houston, TX

PostPosted: Sat Jan 24, 2009 8:58 pm    Post subject: advise after functions Reply with quote

Thanks Lexikos.
I made a couple of minor changes.

AdviseUDF:
now supports types: "before" and "after"
although after really means - just before first return line Sad
if there is no return line in the advised function, unexpected things might happen.

Code:

advise(advice, type, advisee)
{
    static ACT_EXPRESSION:=3, _sizeof_FuncParam:=16, _Var_Name:=16
        , _JumpToLine:=4, _Param:=8, _ParamCount:=12, _MinParams:=16, _IsBuiltIn:=49
    LowLevel_init()
    ; Resolve functions and validate.
    if !(advice_func := __findFunc(advice))
        || !(advisee_func := __findFunc(advisee))
        || NumGet(advisee_func+_IsBuiltIn,0,"char")
        || (param_count := NumGet(advisee_func+_ParamCount)) < NumGet(advice_func+_MinParams)
        return false
    ; Begin generating code.
    cg := code_gen()
    ; Begin a standalone expression. Each standalone expression has only one arg.
    line := code_line(cg, ACT_EXPRESSION), code_arg(cg, 0, true)
    ; Begin function call by adding a function deref.
    code_arg_deref(cg, advice, advice_func, true, param_count)
    ; Write required text for a function call:
    code_arg_write(cg, "(")
    ; Get address of advisee's first parameter (FuncParam).
    param := NumGet(advisee_func+_Param)
    ; For each available parameter,
    Loop %param_count%
    {
        if A_Index > 1
            code_arg_write(cg, ",")
        ; Get Var of this FuncParam.
        param_var := NumGet(param+0)
        ; Add variable deref.
        code_arg_deref(cg, __str(NumGet(param_var+_Var_Name)), param_var, false)
        ; Move to next parameter. (OK if no next parameter; loop will terminate).
        param += _sizeof_FuncParam
    }
    code_arg_write(cg, ")")
    ; Finish generating code.


if (type = "after")
{
  line1 := Numget(advisee_func+_JumpToLine, 0, "UInt")
  loop
  {
  lineAct := NumGet(line1+0, 0, "UChar")
    if (lineAct != 102)
      line1 := Numget(line1+0, 20, "UInt")
    else
      break
  }
 
  {
    if (ch := code_finalize(cg))
    {
      ; Insert the code before the first return statement
      code_insert_before(ch, line1)
      code_delete_handle(ch)
    }
    code_gen_delete(cg)
    return !!ch
  }
}
else
{
    if (ch := code_finalize(cg))
    {
        ; Insert the code before the jump-to line of the function.
        code_insert_before(ch, NumGet(advisee_func+_JumpToLine))
        ; Re-target the function at the new line.
        NumPut(line, advisee_func+_JumpToLine)
        code_delete_handle(ch)
    }
    code_gen_delete(cg)
    return !!ch
  }
 
}



AdviseBIF: added paramter type := "before" | "after"

Code:
AdviseBIF(advisee, type, advice)
{
adviceInstruction := ""
. "B9" mcodeptr(RegisterCallback(advice,"Cdecl",4,1))    ; mov   ecx, ...
. "FFD1"          ; call  ecx
%type% := adviceInstruction
    __mcode(advisee, ""
    . "8B442404"      ; mov   eax,dword ptr [esp+4]
    . "8B08"          ; mov   ecx,dword ptr [eax]
    . "51"            ; push  ecx
    . "FF742410"      ; push  dword ptr [esp+10h]
    . "FF742410"      ; push  dword ptr [esp+10h]
    . "50"            ; push  eax
    . before
    . "B9" mcodeptr(NumGet(__findFunc(advisee)+4))    ; mov   ecx, ...
    . "FFD1"          ; call  ecx
    . after
    . "83C410"        ; add   esp,10h
    . "C3")           ; ret
}
Back to top
View user's profile Send private message Send e-mail Visit poster's website
tinku99



Joined: 03 Aug 2007
Posts: 513
Location: Houston, TX

PostPosted: Sun Jan 25, 2009 4:29 pm    Post subject: understand mcode instructions Reply with quote

Lexikos wrote:
Confused
Code:
LowLevel_init()
AdviseBIF("VarSetCapacity","Debugger")
{...
AdviseBIF(advisee, advice)
{
    __mcode(advisee, ""
    . "8B442404"      ; mov   eax,dword ptr [esp+4]
    . "8B08"          ; mov   ecx,dword ptr [eax]
    . "51"            ; push  ecx
    . "FF742410"      ; push  dword ptr [esp+10h]
    . "FF742410"      ; push  dword ptr [esp+10h]
    . "50"            ; push  eax

    . "B9" mcodeptr(RegisterCallback(advice,"Cdecl",4,0))    ; mov   ecx, ...
    . "FFD1"          ; call  ecx
    . "B9" mcodeptr(NumGet(__findFunc(advisee)+4))    ; mov   ecx, ...
    . "FFD1"          ; call  ecx
    . "B9" mcodeptr(RegisterCallback(advice,"Cdecl",4,1))    ; mov   ecx, ...
    . "FFD1"          ; call  ecx

    . "83C410"        ; add   esp,10h
    . "C3")           ; ret
}

It seems that you need to add a parameter to the 3 params from
BuiltInFunctionType(ResultToken, Param, ParamCount)
to fill the params for
Debugger(aResultToken, aParam, aParamCount, aName)
Is the aName in ecx here?:
. "51" ; push ecx
What is in eax? before the calls?
I am still a little lost after reading:
http://www.rorydriscoll.com/2008/05/19/mockitnow-redirecting-function-calls-in-c/ and the LowLevel Reference chm file.

Lexikos: could you please help me understand your asm wizardry here?

Can this technique be used to hook c functions directly if you have pointers to them, rather than hooking dllcall?

Also, I have no idea even how to begin trying to understand this part from your DebugBIF in here: http://www.autohotkey.com/forum/topic36665.html&highlight=debugbif

Code:
if !VarSetCapacity(DebugErrorLevel)
    {
        VarSetCapacity(DebugErrorLevel, 56), NumPut(0xC35D5E5F, NumPut(0x10C4830C, NumPut(0x55FF5756, NumPut(0x1C75FF20, NumPut(0x75FF0E74, NumPut(0x3038800C, NumPut(0xC4830840, NumPut(0x8B08458B, NumPut(0x1055FF56, NumPut(0x1C75FF3E, NumPut(0x8B2075FF, NumPut(0x5718758B, NumPut(0x56EC8B55, NumPut(&DebugErrorLevel+4, DebugErrorLevel))))))))))))))
        VarSetCapacity(DebugResult, 60), NumPut(0x0000C35D, NumPut(0x5E5F10C4, NumPut(0x830855FF, NumPut(0x57561875, NumPut(0xFF1C75FF, NumPut(0x0E750038, NumPut(0x80068B15, NumPut(0x7500087E, NumPut(0x830CC483, NumPut(0x0C55FF56, NumPut(0x1875FF3E, NumPut(0x8B1C75FF, NumPut(0x5714758B, NumPut(0x56EC8B55, NumPut(&DebugResult+4, DebugResult)))))))))))))))
    }
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 7299
Location: Australia

PostPosted: Sun Jan 25, 2009 5:35 pm    Post subject: Re: understand mcode instructions Reply with quote

tinku99 wrote:
Is the aName in ecx here?:
. "51" ; push ecx
Yes, ecx contains a pointer to the function's name. This is passed via aResultToken.marker.
Quote:
What is in eax? before the calls?
The first instruction, mov eax ... puts the value of the aResultToken parameter into eax. The value of eax before the function begins is irrelevant.
Quote:
Lexikos: could you please help me understand your asm wizardry here?
I am not qualified to teach assembly, being only a beginner myself. I will attempt to explain:
Code:
; Put aResultToken into eax.
mov   eax,dword ptr [esp+4]
; Put aResultToken.marker into ecx.
mov   ecx,dword ptr [eax]
; Push parameters, right to left.
push  ecx                   ; aName
push  dword ptr [esp+10h]   ; aParamCount
push  dword ptr [esp+10h]   ; aParam
push  eax                   ; aResultToken

; Calls to absolute addresses must be done indirectly, using a register.
; Because all three are CDECL, each call may use what we prepared above.
; In theory this works, HOWEVER, the compiler may optimize by using the
; parameters as local variables. In such a case, advice_callback1 would
; receive garbage parameters. Testing showed this happens with DllCall
; on beta builds in VC++ 2008. This is why DebugBIF does not use this method.

; Call first callback.
mov   ecx, advice_callback0
call  ecx
; Call built-in function.
mov   ecx, advisee_bif
call  ecx
; Call second callback.
mov   ecx, advice_callback1
call  ecx

; Pop parameters off stack.
add   esp,10h
ret

Quote:
Can this technique be used to hook c functions directly if you have pointers to them, rather than hooking dllcall?
Not exactly; in this case we can overwrite the pointer AutoHotkey uses to call the function. To redirect an arbitrary C function, you would need to overwrite the code of the function itself. This would have additional complications which I'm not interested in exploring.

Quote:
Also, I have no idea even how to begin trying to understand this part from your DebugBIF in here: http://www.autohotkey.com/forum/topic36665.html&highlight=debugbif
I'm not surprised. The inner-most call, NumPut(&Debug...+4, Debug...), stores a pointer to the actual machine code in the first 4 bytes of the variable. Working outward from there, machine code is written to the variable 4 bytes at a time. More importantly, I've updated the DebugBIF thread with the original C++ source code.
Back to top
View user's profile Send private message Visit poster's website
tinku99



Joined: 03 Aug 2007
Posts: 513
Location: Houston, TX

PostPosted: Sun Apr 19, 2009 4:35 am    Post subject: UDF hooks for 1.0.48 Reply with quote

I finally got around to try to update the user defined advice functions... here is a humble first attempt:
it is requiring an advising label...

Code:
; adviseUDF by Naveen Garg for 1.0.48+
;; modified from advise(...) by Lexikos for 1.0.47
;; http://www.autohotkey.com/forum/profile.php?mode=viewprofile&u=3754
;; copyright http://www.autohotkey.com/forum/topic39287.html?sid=241caa966a8fdc2921188706f5213cb0

adviseUDF(advice, type, advisee)  ; type = before or after
{
    static ACT_EXPRESSION:=3, _sizeof_FuncParam:=16, _Var_Name:=24
        , _JumpToLine:=4, _Param:=8, _ParamCount:=12, _MinParams:=16, _IsBuiltIn:=49
    ; advice uses same parameters advisee
    LowLevel_init()
    ; Resolve functions and validate.
    if !(advice_func := __findFunc(advice))
        || !(advisee_func := __findFunc(advisee))
        || NumGet(advisee_func+_IsBuiltIn,0,"char")
        || (param_count := NumGet(advisee_func+_ParamCount)) < NumGet(advice_func+_MinParams)
        return false
    ; Begin generating code.
    cg := code_gen()

    ; Begin a standalone expression. Each standalone expression has only one arg.
line := code_line(cg, ACT_GOSUB := 96) 
code_arg(cg, 0, false)
code_arg_write(cg, advice)

if (type = "after")
{
  line1 := Numget(advisee_func+_JumpToLine, 0, "UInt")
  loop
  {
  lineAct := NumGet(line1+0, 0, "UChar")
    if (lineAct != 102)
      line1 := Numget(line1+0, 20, "UInt")
    else
      break
  }
 
  {
    if (ch := code_finalize(cg))
    {
      ; Insert the code before the first return statement
      code_insert_before(ch, line1)
      code_delete_handle(ch)
    }
    code_gen_delete(cg)
    return !!ch
  }
}
else
{
    if (ch := code_finalize(cg))
    {
        ; Insert the code before the jump-to line of the function.
        code_insert_before(ch, NumGet(advisee_func+_JumpToLine))
        ; Re-target the function at the new line.
        NumPut(line, advisee_func+_JumpToLine)
        code_delete_handle(ch)
    }
    code_gen_delete(cg)
    return !!ch
  }
 
}





testscript:
Code:

myFunc("Apple","banana")
adviseUDF("myFuncAdvice","before","myFunc")
myFunc("Apple","banana")
return

myFunc(A,B) {
    MsgBox %A%, meet %B%.
return
}

SwapParameters(ByRef A, ByRef B) {
    C := A
    A := RegExReplace(B,"^\w","$U0")
    B := RegExReplace(C,"^\w","$l0")
}

myFuncAdvice:
myFuncAdvice("myFunc")
return

myFuncAdvice(advisee)
{
    static ACT_EXPRESSION:=3, _sizeof_FuncParam:=16, _Var_Name:=24
        , _JumpToLine:=4, _Param:=8, _ParamCount:=12, _MinParams:=16, _IsBuiltIn:=49
    ; advice uses same parameters advisee
    LowLevel_init()
    ; Resolve functions and validate.
    if  !(advisee_func := __findFunc(advisee))
        || NumGet(advisee_func+_IsBuiltIn,0,"char")
    return false
    param_count := NumGet(advisee_func+_ParamCount)
    param := NumGet(advisee_func+_Param)
    ; For each available parameter,
    Loop %param_count%
    {
        ; Get Var of this FuncParam.
        param_var := NumGet(param+0)
        ; Add variable deref.
   msgbox % __str(NumGet(param_var+_Var_Name))
   msgbox % __str(NumGet(param_var + 8))

        code_arg_deref(cg, __str(NumGet(param_var+_Var_Name)), param_var, false)
        ; Move to next parameter. (OK if no next parameter; loop will terminate).
        param += _sizeof_FuncParam
    }

msgbox, advised
}

!q::ExitApp
!r::Reload
#Include adviseUDF.ahk  ;v1.0.48
#Include ..\import
#Include Lowlevel.ahk  ; v1.0.48
#Include code.ahk  ; v1.0.48

Back to top
View user's profile Send private message Send e-mail Visit poster's website
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Wish List All times are GMT
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group