能力值:
( LV2,RANK:10 )
|
-
-
2 楼
获取test函数的地址“mov eax,offset test”可以的啊。还可以用“lea eax,dword ptr test”。
但是你的“__asm{ add esp,12 }“使堆栈不平衡,导致程序崩溃。内嵌汇编要注意函数调用时参数入栈和堆栈平衡。
|
能力值:
( LV2,RANK:10 )
|
-
-
3 楼
@2
噢。。
我看错了 mov eax,offset test是个warning 不是error
另外 我的add esp,12 是使堆栈平衡 因为ret指令使eip直接跳到了test函数入口处 __cdecl需要主调函数平衡堆栈 但是直接跳到了test函数入口处了 所以没能平衡堆栈 所以我家了 add esp,12 自己手动平衡了堆栈
程序崩溃另有原因 要怎么才能正常返回的mainCRTstartup函数执行exit 这个要等看雪的大虾出来解释了。。
|
能力值:
( LV2,RANK:10 )
|
-
-
4 楼
你还挺固执的。不信你自己调试一下就知道了。你根本就没调用test函数体,你平衡什么堆栈?平衡main函数体内的堆栈你只用pop就可以了。
|
能力值:
( LV2,RANK:10 )
|
-
-
5 楼
两位mm莫要吵架 莫要吵架 虽然没有调用test函数 但楼主mm的代码里依靠修改堆栈使ret返回到了test的入口处 test函数里的add esp,12 是想平衡main函数的堆栈吧?
不过楼主mm忘记了一个问题 ret指令使你直接跳到test函数的入口点了 main函数里面你曾经push ebx push edi push esi 这些都没平衡。。
|
能力值:
( LV2,RANK:10 )
|
-
-
6 楼
小虾路过围观
话说我点进来才知道这贴不欢迎我这个虾米。。
|
能力值:
( LV2,RANK:10 )
|
-
-
7 楼
刚才经调试 问题已解决 谢谢memspirit的提醒 不过我不是想平衡main函数的堆栈 只要能返回mainCRTStartup 那就让mainCRTStartup去平衡调用main函数的堆栈 我add esp,12 漏掉一个相应的push 应该是add esp,16 这样栈顶就是返回mainCRTStartup的地址了
问题已解决 完整代码和注释在下面 大家编译后调试一下 注意栈的变化 一看便知
#include "stdio.h"
void __cdecl test(){
__asm{
add esp,16 //恢复main函数入口处C编译器自动生成的 ebp ebx esi edi 寄存器所占堆栈
}
printf("hallo");
//因为从来没有调过test 而是从main函数ret到test函数的入口处的 所以这时候栈顶是返回到mainCRTStartup
}
int __cdecl main(int __argc,char** __argv, char** __environ){
//函数入口处C编译器将自动生成代码用来保护 ebp ebx esi edi四个寄存器
// push ebp
// mov ebp,esp
// push ebx
// push esi
// push edi
__asm{
mov eax,offset test
push eax //ret指令将使用这个地址跳转
push ebp //实际上这里有个错误 这个ebp已经被修改过了
push ebx
push esi
push edi
}
return 0; //xor eax, eax
//函数末尾处C编译器也将自动生成代码用来恢复 edi esi ebx ebp四个寄存器
// pop edi
// pop esi
// pop ebx
// pop ebp
}
|
能力值:
( LV9,RANK:610 )
|
-
-
8 楼
看不懂你们的代码 不过我写了一个 Release版本不崩溃 且能打印出来 Hello的代码
#include "stdafx.h"
#include "stdio.h"
void __cdecl test()
{
printf("hello \n");
}
int __cdecl main(int __argc,char** __argv, char** __environ){
__asm
{
mov eax,offset test
push eax
}
}
|
能力值:
( LV2,RANK:10 )
|
-
-
9 楼
push ebp
mov ebp, esp
push ebx
push esi
push edi
__asm{
mov eax, offset test
push eax
}
pop edi
pop esi
pop ebx
pop ebp
楼上你的c编译器没有生成保护ebx esi edi的代码么 即使没有 那ebp总该有的吧 你仅仅是一个push offset test,接下来会立即pop ebp 这时候ret就会用原始ebp中的值做返回地址了。。
|
能力值:
( LV9,RANK:610 )
|
-
-
10 楼
我用的是VC6.0的编译器 这种代码跟编译器有莫大的关系 研究这些东西不如 仔细读读汇编书 反正我的代码在Release版本下是没有问题的
解释也相当简单
-> mov eax,offset test 执行到这句的时候堆栈里面存的数据是main的返回地址 应该是内核级或者一些系统dll里
-> push eax 把test函数的地址入栈
-> ret 这句是隐藏的 反汇编才能看到
ps. return 0则不是这种情况 会加一个 xor eax,eax 的操作
这时候就从main来到了 test函数 执行完了一个 printf后
要执行ret 而此时 堆栈里只剩下原本main要返回的地址(另一个被main的ret '吃掉'了) 所以就能很完美的衔接好 不会崩溃
ps.做这样的练习 要用OD来配合比较好
|
能力值:
( LV2,RANK:10 )
|
-
-
11 楼
不同的编译器下生成的代码不同。讨论这些没有意义。重要的是学到知识。
|
能力值:
( LV2,RANK:10 )
|
-
-
12 楼
哈哈,这个也有取巧的成分,debug版本下VC编译器会添加
cmp esp,ebp
call _chkesp 来检查ebp和esp是否相等,release版本下没检查……esp明显比ebp小4,呵呵。
|
能力值:
( LV9,RANK:610 )
|
-
-
13 楼
[QUOTE=xiilin;769437]哈哈,这个也有取巧的成分,debug版本下VC编译器会添加
cmp esp,ebp
call _chkesp 来检查ebp和esp是否相等,release版本下没检查……esp明显比ebp小4,呵呵。[/QUOTE]
说要说嘛 11楼说的很对 讨论这些没什么意义
我认为所有的问题都可以放到OD下反汇编一下 就都一目了然了 都汇编知识 没啥意义
|
能力值:
( LV2,RANK:10 )
|
-
-
14 楼
事实上根本不必关心那些push ebx,push esi,push edi之类的指令,只记住一点:进入函数后第一句是push ebp,想从test()返回到mainCRTstartup就很简单了,在test()内当前ebp指向的栈单元内存放的是main()内的ebp值,而main()内的ebp+4指向的栈单元内存放的就是mainCRTstartup的返回地址,所以可以这样写:
#include <stdio.h>
void _cdecl test()
{
printf("Hello\n");
_asm
{
mov eax,[ebp] //取出main()内的ebp值
mov eax,[eax+4] //取出mainCRTstartup的返回地址
jmp eax //直接跳过去
}
}
int _cdecl main(int _argc,char **_argv,char **_env)
{
test();
return 0; //这一句执行不到了
}
|
能力值:
( LV9,RANK:610 )
|
-
-
15 楼
[QUOTE=xiilin;769475]事实上根本不必关心那些push ebx,push esi,push edi之类的指令, 只记住一点:进入函数后第一句是push ebp,想从test()返回到mainCRTstartup就很简单了,在test()内当前ebp指向的栈单元内存放的是main()内的ebp值,而main()内的ebp+4指...[/QUOTE]
你要我们记住的这点好像是错的
#include "stdafx.h"
void test(void)
{
printf("Hi!\n");
}
int main(int argc, char* argv[])
{
test();
return 0;
}
反汇编后的代码
00401000 /$ 68 30704000 push 00407030 ; ASCII "Hi!",LF
00401005 |. E8 16000000 call 00401020
0040100A |. 59 pop ecx
0040100B \. C3 retn
0040100C 90 nop
0040100D 90 nop
0040100E 90 nop
0040100F 90 nop
00401010 /$ E8 EBFFFFFF call 00401000
00401015 |. 33C0 xor eax, eax
00401017 \. C3 retn
00401018 90 nop
|
能力值:
( LV2,RANK:10 )
|
-
-
16 楼
[QUOTE=blueapplez;769494]你要我们记住的这点好像是错的
#include "stdafx.h"
void test(void)
{
printf("Hi!\n");
}
int main(int argc, char* argv[])
{
test();
return 0;
}
反汇编...[/QUOTE]
汗,你是用的哪个版本的VC?我在VC6下编译看了一下,debug和release版本都有push ebp,mov ebp,esp这两句
|
能力值:
( LV2,RANK:10 )
|
-
-
17 楼
这种方法能不能跳过上一层函数,只要写段代码试一下就知道了
#include <stdio.h>
void test2()
{
printf("test2\n");
_asm
{
mov eax,[ebp]
mov eax,[eax+4]
jmp eax
}
}
void test1()
{
test2();
printf("test1\n");
}
int main(int argc,char *argv[])
{
test1();
printf("main\n");
return 0;
}
把这个拿去编译运行一下,可以看到输出是"test2"和"main",没有输出"test1",因为内嵌的那段汇编跳过了test1,直接返回到了main函数里。
ps:用release模式编译,为了简短,只偷了ebp的值来获得main函数的返回地址,没有去偷esp的值,所以debug模式下执行call _chkesp时会出现一个断言错误。
|
能力值:
( LV9,RANK:610 )
|
-
-
18 楼
Microsoft Visual C++ 6.0(SP6) 这下清楚了吧
|
能力值:
( LV2,RANK:10 )
|
-
-
19 楼
那应该没问题啊,我也是这个版本,记得VC的有个编译优化选项是可以关闭用ebp访问局部变量的,关闭了那个选项就会全都用esp访问,如果是那样的话,可以用最右边的参数的地址+4来取得上一个函数的返回地址:-)
或者你编译一下我的17L发的那段代码试一下。
|
能力值:
( LV2,RANK:10 )
|
-
-
20 楼
很精彩...
|
|
|