我的思绪今天在关于函数指针的主题上徘徊,我想出了以下场景:
__stdcall int function (int)
{
return 0;
}
int main()
{
(*(int(*)(char*,char*))function)("thought", "experiment");
return 0;
}
AFAIK此代码会破坏堆栈,因此如果我运行此代码,我可以查看哪些类型的问题?
我会自己调查,但是我离开我的开发机器一周。
编辑:等一下,我一直在思考。正如在评论中观察到的那样,这段代码的意图是在完成所有操作后在堆栈上留下一个参数(调用者将两个参数放在堆栈上,被调用者 - 期望只有一个参数 - 只弹出一个参数)。但是,由于我的演员没有提到调用约定,我是否抛弃了stdcall,至少从调用者的角度来看? int function(int)仍然会从堆栈中弹出一个参数,但调用者是否会因为强制转换而认为该函数是__cdecl(默认值)? (即三个总参数弹出?)EDIT2:正如Rob所证实的那样,第二个问题的答案是肯定的。如果我想在堆栈上留下一个参数,我将不得不重述__stdcall:
(*(__stdcall int(*)(char*,char*))function)("thought", "experiment");
答案 0 :(得分:2)
您正在调用该函数,就好像它是_cdecl一样,这意味着调用者会推送参数并清理堆栈。
接收函数是_stdcall,这意味着被调用者清理堆栈。被调用者期望一个参数,因此将从堆栈中弹出4个字节。
当函数返回时,调用者将弹出两个指针(之前已推送两个指针),因此您的堆栈被4个字节损坏。
两个调用约定都使用相同的返回机制,并且具有相同的寄存器规则(不保留eax,ecx和edx)。有关详细信息,请参阅wikipedia。
根据堆栈框架布局和对齐情况,这种不匹配可能会导致许多影响。如果你很幸运,那么你就可以逃脱它。如果不是,您可能会搞乱主函数的返回地址,导致程序在分支到who-know-where时崩溃。如果编译器已注入某种堆栈保护来捕获损坏,那么它可能会检测到并中止该程序。
答案 1 :(得分:1)
不,它肯定不会导致蓝屏。没有用户模式进程能够做到这一点。即使这样的bug存在于内核模式代码中,只有在访问无效内存或向函数传递错误参数后才会发生BSOD。
您只是破坏了进程的私有内存,并且损坏可能(或可能不会)导致无效操作(例如,取消引用指向无效内存的指针)。发生这种情况时,操作系统会终止您的流程,但不久就会。
答案 2 :(得分:0)
我认为在这种情况下你会有'未定义的行为'。
来自C standard :(我认为它在C ++中是相同的)
768如果 转换后的指针用于调用a 类型不兼容的功能 用尖头的类型,行为 未定义。
编辑:在大多数操作系统上,此类错误不会导致整个操作系统出现问题。但它会导致程序中出现未定义的问题。用户模式程序很难能够产生蓝屏。