You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中函数指针究竟是什么?栈上执行指令失败解惑

栈上执行机器码失败的原因及解决方案

核心原因:数据执行保护(DEP)

现代Windows系统默认开启数据执行保护(DEP),栈、堆这类数据区域默认被标记为不可执行。你把机器码放在栈上调用时,CPU会直接拒绝执行非可执行区域的代码,触发内存访问异常——这才是程序失败的根本原因,和函数指针的理解、机器码正确性无关。

关于函数指针的理解

你对函数指针的基本认知是对的:它本质是存储函数入口地址的指针,可直接调用。但标准C明确规定,对象指针(比如void*)和函数指针属于不同类型,两者转换属于未定义行为。不过多数编译器(包括Clang)为了兼容底层编程场景,提供了扩展支持,这也是开启-Wpedantic -Werror仍能编译通过的原因,但这个编译层面的允许和你程序运行失败完全无关。

Windows/MSVCRT的影响

MSVCRT本身不影响DEP的开启,DEP是Windows系统级的安全机制(从Vista开始默认启用),不管用哪个C运行时,栈默认都是不可执行的。要让栈上的机器码能执行,必须手动修改对应内存区域的权限。

解决方法:修改内存权限

使用Windows API VirtualProtect将存储机器码的栈区域标记为可执行。以下是示例代码(以x86架构为例):

#include <windows.h>
#include <stdio.h>

int main() {
    // x86下返回3的机器码:mov eax, 3; ret
    unsigned char code[] = {0xB8, 0x03, 0x00, 0x00, 0x00, 0xC3};
    int (*func)() = (int (*)())code;

    DWORD old_protect;
    // 将内存区域设置为可读写可执行
    if (!VirtualProtect(code, sizeof(code), PAGE_EXECUTE_READWRITE, &old_protect)) {
        printf("VirtualProtect failed: %d\n", GetLastError());
        return 1;
    }

    int result = func();
    printf("Result: %d\n", result);

    // 可选:恢复原内存权限,降低安全风险
    VirtualProtect(code, sizeof(code), old_protect, &old_protect);

    return 0;
}

如果是x64架构,机器码需要对应修改:

// x64下返回3的机器码:mov rax, 3; ret
unsigned char code[] = {0x48, 0xC7, 0xC0, 0x03, 0x00, 0x00, 0x00, 0xC3};

额外注意事项

  • 必须确保机器码和目标架构(x86/x64)完全匹配,否则即使权限正确,执行也会出错。
  • 手动修改内存权限后,建议恢复原权限,避免引入不必要的安全风险。

内容的提问来源于stack exchange,提问作者William Ryman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 02:20:04