 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| advice is best for end user programming |
| yes |
|
66% |
[ 2 ] |
| no |
|
33% |
[ 1 ] |
|
| Total Votes : 3 |
|
| Author |
Message |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Sat Jan 24, 2009 8:58 pm Post subject: advise after functions |
|
|
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
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 |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Sun Jan 25, 2009 4:29 pm Post subject: understand mcode instructions |
|
|
| Lexikos wrote: |
| 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 |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Sun Jan 25, 2009 5:35 pm Post subject: Re: understand mcode instructions |
|
|
| 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.
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 |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Sun Apr 19, 2009 4:35 am Post subject: UDF hooks for 1.0.48 |
|
|
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 |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|