要调用一个函数,得先分析它,弄明白它的全部参数
一般我们直接跳到(看)函数返回处(函数尾), 看它 ret 后面是否有数值
像这个函数是 ret 04 可以认定它有一个参数,是从堆栈传进来 ( push XX)
这个数值04 代表 一个参数的存储长度
推而广之
ret 08 = 2 个参数 即 调用该函数时,必须有 2个 push XX
ret 0C = 3 个参数 即 调用该函数时,必须有 3个 push XX
...
如此,我们确定调用它的 push 个数(实参)(压堆栈)
接着,我们回到函数头,查看寄存器的使用,以确定有没有通过寄存器的带进来数据的参数(隐形参数?)
即,是否有寄存器带进来的数据,有给其它寄存器传数据(赋值)吗?
像这个函数:
push ebp
...
mov esi,ecx
这一段就是这函数的"开头"部分.
这里要理解 push 的另一种作用,是存储寄存器的数据,并非是调用函数前的传参. 存储起来,是为了运行代码后,还原寄存器的数据,这也是调用函数时有使用寄存器,但调用结束后,部分寄存器数据不会变动的原因.
所以, push ebp 这条语句的结论是 ebp 没有赋值操作, ebp 暂时不能算是参数; 后面 push eax, push esi 的作用类似.
mov ebp,esp 这个是固定使用形式, esp 永是堆栈顶部的地址,肯定不是参数;这里是记录栈顶,方面后续寄存器还原与使用.
mov eax,fs:[00000000] 是读取新数据到eax里, 没考虑eax原来的数据, eax也不是参数
xor eax,ebp 这个也是固定的,由前面mov ebp,esp, ebp 已经是进入函数后的栈顶地址,它当然不是参数
mov esi,ecx ecx给esi赋值,而前面没有1条是给ecx赋值的语句,那么ecx的数据从哪里来? 当然是调用函数前带进来的,所以ecx是参数.
一般我们直接跳到(看)函数返回处(函数尾), 看它 ret 后面是否有数值
像这个函数是 ret 04 可以认定它有一个参数,是从堆栈传进来 ( push XX)
这个数值04 代表 一个参数的存储长度
推而广之
ret 08 = 2 个参数 即 调用该函数时,必须有 2个 push XX
ret 0C = 3 个参数 即 调用该函数时,必须有 3个 push XX
...
如此,我们确定调用它的 push 个数(实参)(压堆栈)
接着,我们回到函数头,查看寄存器的使用,以确定有没有通过寄存器的带进来数据的参数(隐形参数?)
即,是否有寄存器带进来的数据,有给其它寄存器传数据(赋值)吗?
像这个函数:
push ebp
...
mov esi,ecx
这一段就是这函数的"开头"部分.
这里要理解 push 的另一种作用,是存储寄存器的数据,并非是调用函数前的传参. 存储起来,是为了运行代码后,还原寄存器的数据,这也是调用函数时有使用寄存器,但调用结束后,部分寄存器数据不会变动的原因.
所以, push ebp 这条语句的结论是 ebp 没有赋值操作, ebp 暂时不能算是参数; 后面 push eax, push esi 的作用类似.
mov ebp,esp 这个是固定使用形式, esp 永是堆栈顶部的地址,肯定不是参数;这里是记录栈顶,方面后续寄存器还原与使用.
mov eax,fs:[00000000] 是读取新数据到eax里, 没考虑eax原来的数据, eax也不是参数
xor eax,ebp 这个也是固定的,由前面mov ebp,esp, ebp 已经是进入函数后的栈顶地址,它当然不是参数
mov esi,ecx ecx给esi赋值,而前面没有1条是给ecx赋值的语句,那么ecx的数据从哪里来? 当然是调用函数前带进来的,所以ecx是参数.