有过程Sub1、Sub2、Sub3、.....,均要调用另外一个S_Sub。
我的问题是如何在S_Sub中确定是那会函数调用了它?当然可以通过在S_Sub中确定返回地址的方法判断调用宿主,也可以用形参实参传递标志的方法或设公用标志变量,但我不想用这些方法。
各位还有方法吗?
我的问题是如何在S_Sub中确定是那会函数调用了它?当然可以通过在S_Sub中确定返回地址的方法判断调用宿主,也可以用形参实参传递标志的方法或设公用标志变量,但我不想用这些方法。
各位还有方法吗?
begin
if Sub1 then ShowMessage('被Sub1调用!')
...
end;procedure Sub1;
begin
S_Sub(True)
end;...
procedure S_Sub();
begin
try
raise Exception.Create('故意的异常');
except
//这这里查询callstack delphi并未提供看堆栈的功能,但是你到网上找找,有一些牛人写的工具是很好用的
end;
end;
这个
http://www.dimusware.com/products/excmagic/index.html
var
f: word;
begin
asm
mov f, di
end; if f=1 then ShowMessage('被Sub1调用!')
end;procedure Sub1;
begin
asm
push di
mov di,1
call S_Sub
pop di
end;
end;
我想找个更好的方法,不用形实参和标志变量。这样不需在接口或程序中做任何设置。这一点对DLL中的S_Sub很有用。
实际上利用堆栈是一个好方法,在所有的调试工具中都是通过这种方法实现调用关系的显示。只是堆栈返回地址不能表示是那个主过程Sub?调用了S_Sub。
我的问题真的无解吗?期待ing......
//调用者的方法地址
mov eax, [ebp+$04] 依据调用者的方法地址,查询对应的名称等信息。所以要求程序需要带有调试信息、开启栈帧(Stack frame)。
第三方库如MadExcept、JCLDebug,应该都支持以上需求。
调用者(比如Sub1)内部是这样的:
...//相关参数设置
call S_Sub//调用S_Sub
...//调用完之后的代码其中call指令就是把“调用完之后的代码”的首地址入栈,再jmp到S_Sub。即相当于:
push xxxx
jmp S_Sub然后S_Sub里头(开启栈帧的前提下):
push ebp
mov ebp,esp
...//其他代码
mov eax,[ebp+4]//此时如果有这句,就取得本函数结束后返回到哪儿继续执行的地址
...//其他代码
pop ebp//恢复ebp
ret//相当于pop ip,就是把xxxx弄出栈并赋给ip,即跳到xxxx执行
ret相当于pop eip
http://liyiwen.iteye.com/blog/345525
#include <stdio.h>
void whocallme();
void fun1() {
whocallme();
}
void fun2() {
whocallme();
}
void fun3() {
fun1();
}
void whocallme() {
int *_esp,i;
__asm {
mov eax,esp
mov _esp,eax
}
printf("\nfun1,fun2,fun3,_esp=%08x,%08x,%08x,%08x\n",(int)fun1,(int)fun2,(int)fun3,(int)_esp);
for (i=0;i<100;i++) if (_esp[i]==(int)_esp) break;
if (i<100) {
printf("ret addr=%08x\n",_esp[i+2]);
if ((int)fun1<=_esp[i+2] && _esp[i+2]<(int)fun2) printf("fun1 callme\n");
if ((int)fun2<=_esp[i+2] && _esp[i+2]<(int)fun3) printf("fun2 callme\n");
}
}
void main() {
fun2();
fun1();
}
//C:\tmp\tmp\Debug>tmp
//
//fun1,fun2,fun3,_esp=00401000,00401020,00401040,0012fe84
//ret addr=0040102e
//fun2 callme
//
//fun1,fun2,fun3,_esp=00401000,00401020,00401040,0012fe84
//ret addr=0040100e
//fun1 callme
//
//C:\tmp\tmp\Debug>cd ..\release
//
//C:\tmp\tmp\Release>tmp
//
//fun1,fun2,fun3,_esp=00401000,0040100a,00401014,0012ff5c
//ret addr=00401012
//fun2 callme
//
//fun1,fun2,fun3,_esp=00401000,0040100a,00401014,0012ff5c
//ret addr=00401008
//fun1 callme
//
可以参考借鉴一下,只是接口都需要修改了
if ((int)fun2<=_esp[i+2] && _esp[i+2]<(int)fun3) printf("fun2 callme\n");这两句if的条件判断始终为false,所以后面的printf语句始终不能执行到。
我想43楼在看完前面提到的那篇blog没有很好的理解其中的意思,
因为
(int)fun1
指的是代码段地址,
而
_esp[i+2]
中存储的是栈地址。
同时,在那篇博客中的代码已经明确表示:只能通过返回地址获得主调函数的栈地址,但是不加debug信息,很难通过栈数据反过来获得代码段数据,而且你也无法仅仅只通过地址而获得函数的名称,因为名称信息在编译的时候已经被翻译成段地址了。我不知道在开debug时,运行时信息是怎么样的,但一定会丰富,不过可能不同编译器编译出的RTL会不一样,也许没有一个通用的解决方案。总的来说,我觉得这个问题其实是由于楼主程序结构设计不合理造成的。如果你一定要在被调函数中知道主调函数是谁,其实就是需要一个外部状态来决定函数的执行行为。
后期的分析可以借助很多工具来达到这样的效果,但是设计者如何将这些工具动态连接到自己的程序中?好象不可能,也不现实。
在自己的DLL中能用你的方法确定调用者?有现成的代码吗?期待中......
谢谢各位的帮助!