可以使用函数指针来运行“数据”吗?

这不是大多数人可能会使用的东西,但它只是浮现在脑海中并且困扰着我。

是否可以使用c-string中的某些机器代码,然后将其地址转换为函数指针,然后使用它来运行该机器代码?

从理论上讲,你可以按照Carl Norum的说法。 这称为“自修改代码”。

在实践中,通常会阻止你的是操作系统。 大多数主要的现代操作系统旨在区分“可读”,“可读写”和“可执行”存储器。 当这种操作系统内核加载一个程序时,它会将代码放入一个特殊的“可执行”页面,该页面被标记为只读,这样用户应用程序就无法对其进行修改。 同时,尝试GOTO不在“可执行”页面中的地址也会导致故障exception。 这是出于安全目的,因为许多种恶意软件和病毒以及其他黑客都依赖于使程序跳转到修改后的内存中。 例如,黑客可能会提供应用程序数据,这会导致某些function将恶意代码写入堆栈,然后运行它。

但从本质上讲,操作系统本身对加载程序的作用正是您所描述的 – 它将代码加载到内存中,将内存标记为可执行文件,然后跳转到内存中。

在嵌入式硬件领域,可能没有一个操作系统妨碍你,所以一些平台经常使用它。 在PlayStation 2上,我曾经一直这样做 – 如果有一些特定于沙漠级别的代码,并且在其他任何地方都没有使用过,我不会一直把它留在内存中 – 而是我将其与沙漠级别一起加载,并将我的函数指针修复为正确的可执行文件。 当用户离开关卡时,我会从内存中转储该代码,将所有这些函数指针设置为exception处理程序,并将下一级别的代码加载到同一空间中。

是的,你绝对可以这样做。 除非您的系统或编译器以某种方式阻止它(例如,您拥有哈佛架构),否则没有什么可以阻止您。 在跳跃之前,确保您的“数据”是有效的说明,否则您将面临灾难风险。

甚至不可能在C语言中尝试合法地做这样的事情,因为没有合法的方法来使函数指针指向“数据”。 C语言中的函数指针只能从其他函数指针初始化/赋值,即使您使用显式转换也是如此。 如果违反此规则,则行为未定义。

也可以使用实现定义的结果(与其他情况下的未定义结果相反)从整数(通过使用显式转换)初始化函数指针。 然而,通过以这种方式获得的指针进行调用来执行“数据”的尝试仍然导致未定义的行为。

如果您愿意忽略行为未定义的事实,那么该未定义行为的实际表现forms在不同平台上会有所不同。 在某些平台上,它甚至可能“工作”。

人们还可以想象一个超级优化器,它可以根据它优化的函数的规范来测试小的汇编程序。