下面进入到 Test过程代码分析段,
程序进入到了Test段,先得到最后两个参数(堆栈中的参数)
通过上面分析,此时必须要将当前的栈顶指针向后移动一个才可访问到最后一个参数
//初步写下如下试验代码
procedure Test
begin
Pop Eax
Pop Eax //此时则得到了最后一个参数的值了
end;
如果这样写的话,就存在一个很大的问题了,如果全部出栈,出栈,那么调用事件处理函数之前入栈的事件处理函数完成之后
的下条指令执行的位置丢失了!所以,在出栈其他参数之前,务必要先将调用事件处理函数完成之后的指令位置保存起来,才
可出栈!而且将入栈的参数全部出栈得到之后,再将刚刚出栈的下条指令地址入栈,就能使程序ret时候得到正确的指令执行
位置了。代码如下:
procedure Test;
begin
Pop EDX //指令执行位置保存到ESI
Pop EAX //最后一个参数3
Pop EAX //倒数第二个参数2
Push EDX //指令执行位置入栈,然后下面Delphi自动调用一个ret过程,也就是一个出栈的过程弹出下条执行指令位置
ret //该句可加可不加
end;
如此看来,通过这里可看出ret指令其实包含有两个过程,首先 出栈执行指令位置,然后跳转到那个位置,那么代码应该是
如下方式:
Pop NextAddr
Jmp NextAddr
分析透了,所以上面的Test函数还可再减化一下,都是跳到下个地方执行,所以直接使用跳转指令jmp来改变EIP位置
代码如下:
procedure Test;
begin
Pop EDX
Pop EAX
Pop EAX
Jmp EDX //这样就省去了他的一个入栈和一个出栈操作而直接跳转
end;
至于其他的几个参数还是在EAX,EDX,ECX中,如果你不动的话!
将传递的参数的值显示出来,那么如果要显示的话,就需要定义局部参数了!
定义了局部参数,则栈空间就会增加,然后将声明的局部变量分别入栈。
想想要使程序不报错!就必须得使得调用事件出来函数前后的栈平衡,所以就需要将前面入的参数
全部Pop弹出来,来改变堆栈栈顶指针。以达到堆栈平衡。
procedure Test
var
x,y,z: integer;
begin
//这里声明了三个变量x,y,z,一般做操作如下:
{Push Ebp //触发调用该函数的EBP值!也就是那个函数的入口栈基址。入栈保存
Mov Ebp,Esp //然后将当前的函数的入口栈基址给EBP保存
Add ESP,-12 //将堆栈扩充3个变量的位置
Push X
Push Y
Push Z //然后再将局部变量入栈
如此的话,则可通过基地址EBP来得到堆栈中的参数值了
}
asm
push esi //保存Esi值,注意了,此时又添加了一个栈变量了Esp在原来的基础上减4
mov esi,[ebp+8] //Ebp将自己入栈之后才是本入口栈的基地址,所以[Ebp+4]为程序返回跳转指令的位置,
mov z,esi
mov esi,[ebp+12] //同理类推
mov y,esi
mov x,ecx //由于x是第三个参数值,所以没有入栈
pop esi //返回上面的保存的esi内容的值
mov ecx,ebp
sub ecx,esp //此时ecx保存着基本地址和栈顶指针的差距,用来后面进行出栈循环用
end;
ShowMessage(IntToStr(x)); //得到的x值
ShowMessage(IntToStr(y)); //得到的y值
ShowMessage(IntToStr(z)); //得到的z值
//这个函数由于没有参数,而为了达到堆栈平衡,所以下面必须使用代码来手动达到目的
asm
@PopUp:
pop eax //根据基地址与当前地址的距离进行出栈,使堆栈到达基地址的位置。
sub ecx,4
jnz @PopUp //为0了则说明到达本函数的入口基栈地址了
pop ebp //由于在最开始初始化的时候压入了该函数的返回后地址,所以这里出栈该地址到ebp中
//此时已经回到了该函数的入口地址时候了,然后再由于传递过来的两个参数入栈占据了8大小的位置
//所以这里直接使用 ret 8则可返回到当前的堆栈位置+8的地方那就是原来触发函数的位置了。
ret 8
end;
end;
总体归纳说来的话,就是出栈到函数的入口基址,然后ret 到其入栈参数所占据的空间
type
TTestEvent = procedure(Sender: TObject;x,y,z,t: integer) of object;
TForm1 = class(TForm)
RzButton1: TRzButton;
RzButton2: TRzButton;
procedure RzButton1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure RzButton2Click(Sender: TObject);
private
FonTest: TTestEvent;
{ Private declarations }
procedure MyTest(Sender: TObject;x,y,z: integer);
public
{ Public declarations }
published
property OnTest: TTestEvent read FonTest Write FOnTest;
end;
var
Form1: TForm1;
implementation
uses typinfo;
{$R *.dfm}
procedure Test();
var
x,y,z: integer;
begin
asm
push esi
mov esi,[ebp+8]
mov z,esi
mov esi,[ebp+12]
mov y,esi
mov x,ecx
pop esi
mov ecx,ebp
sub ecx,esp
push ecx
end;
ShowMessage(IntToStr(x));
ShowMessage(IntToStr(y));
ShowMessage(IntToStr(z));
asm
pop ecx
@PopUp:
pop eax
sub ecx,4
jnz @PopUp
pop ebp
ret 12
end;
end;
function GetMax(x,y: integer): integer;
begin
if x > y then result := x
else result := y;
end;
procedure TForm1.RzButton1Click(Sender: TObject);
begin
GetMax(2,3);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
end;
procedure TForm1.MyTest(Sender: TObject; x, y, z: integer);
var
a,b,c: integer;
begin
a := x;
b:=y;
c:=z;
if a > b then
a := a+1;
//showmessage(inttostr(x));
end;
procedure TForm1.RzButton2Click(Sender: TObject);
var
Method: TMethod;
begin
MeThod.Data := Sender;
Method.Code := @Test;
SetMethodProp(self,'OnTest',Method)
end;