-1

今天脑子里一直在想函数指针这个话题,脑子里想到了以下场景:

__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");
4

3 回答 3

2

您正在调用该函数,就好像它是 _cdecl 这意味着调用者推送参数并清理堆栈。

接收函数是 _stdcall,这意味着被调用者清理堆栈。被调用者需要一个参数,因此将从堆栈中弹出 4 个字节。

当函数返回时,调用者将弹出两个指针(之前已压入两个指针),因此您的堆栈被 4 个字节损坏。

两种调用约定使用相同的返回机制,并具有相同的寄存器规则(不保留 eax、ecx 和 edx)。有关更多详细信息,请参阅维基百科

根据堆栈帧布​​局和对齐方式,这种不匹配可能会导致许多影响。如果你很幸运,那么你就可以摆脱它。如果不是这样,您可能会弄乱主函数的返回地址,导致程序在分支到谁知道在哪里时崩溃。如果编译器注入了某种堆栈保护来捕获损坏,那么它可能会检测到这一点并中止程序。

于 2009-03-30T01:42:04.487 回答
1

不,它绝对不会导致蓝屏。没有用户模式进程能够做到这一点。即使这样的错误出现在内核模式代码中,蓝屏死机也只会在访问无效内存或将错误参数传递给函数后才会发生。

您只是在破坏进程的私有内存,并且该破坏可能(或可能不会)稍后导致无效操作(例如,取消引用指向无效内存的指针)。发生这种情况时,操作系统会终止您的进程,但不会很快。

于 2009-03-30T01:20:08.113 回答
0

我认为在这种情况下你会有“未定义的行为”。

来自C 标准:(我认为它在 C++ 中是相同的)

768 如果转换后的指针用于调用类型与指向的类型不兼容的函数,则行为未定义。

编辑:在大多数操作系统上,这种类型的错误不会导致整个操作系统出现问题。但这会在您的程序中导致未定义的问题。用户模式程序很难导致蓝屏。

于 2009-03-30T01:08:57.717 回答