C++ 标准(甚至是 C 标准)认为程序代码的整体是不可变的。所以“删除”一个函数是没有意义的。
但让我们务实一点。大多数情况下,您的 C++ 程序被编译成在某些操作系统中运行的 executable(我假设操作系统是 Linux,但您可以将我的答案调整为其他操作系统)。有关操作系统的一般概述,阅读Operating Systems: Three Easy Pieces(可免费下载)
然后,当您的程序在某些process 中启动时(可能是通过您的shell 执行fork(2) 然后是execve(2)),会创建一个新的virtual address space(并由配置@987654327 的内核管理) @ 并通过paging 处理page faults 适当的数据,例如来自磁盘)。
一般来说,您的程序的ELF 可执行文件包含一个code segment,它在您的虚拟地址空间的一些只读和可执行段中进行内存映射。由于该部分是只读的,因此您不能“删除”或“写入”它。使用shared libraries,虚拟地址空间更加复杂,包含几个代码段(每个库可能一个或多个)。
我可以轻松地在纯 ASM 中执行此操作。
错误对于在某些(现代)操作系统上运行的程序。因为代码段是只读的,所以你需要改变你的虚拟地址空间(only方法是通过适当的系统调用,如mmap,@ 987654356@、mprotect;您可以在 C++、C 甚至 ASM 中调用它们)。某些功能已在汇编程序中编码的事实不会改变这一点。即使是汇编代码也无法覆盖代码段(在这种情况下,您仍然会得到一些segmentation fault)而不改变您的虚拟地址空间。
您可以玩一些低级技巧,例如使用munmap(2) 系统调用(来自某些 C++ 代码或 C 代码或汇编代码)来更改您的虚拟地址空间并从中删除一些页面。我强烈建议不要这样做。但是如果你走这条路,你需要了解你的 ELF 可执行文件和共享对象以及你的虚拟地址空间的所有细节。请注意ASLR、elf(5)、ld-linux(8)、vdso(7)。使用 objdump(1) 和 readelf(1) 并查看 ld(1) 了解您的 ELF 可执行文件的详细信息。
您可以将您的代码(您的check 和其他几个函数)放入一些plugin,但在您的情况下,您可能不应该这样做。在这种情况下,您可以dynamically load 该插件然后卸载它。您将使用dlopen(3) 加载插件,并使用dlclose 卸载它。插件的dlopen-ing 执行几个 mmap(2)(因此增加了虚拟地址空间)并处理relocations,它的dlclose 执行了几个munmap(2)(因此缩小了虚拟地址空间)。
在 Linux 上使用/proc/(请参阅proc(5))。在终端中尝试 cat /proc/self/maps 和 cat /proc/$$/maps 和 cat /proc/$$/status。并从您的 C++ 程序中读取/proc/self/maps(例如,参见this)。
例如,fish 是一个用 C++ 编码的 shell。它现在在我的桌面上以 pid 4735 的进程运行。它的虚拟地址空间有 69 个段,以wc /proc/4735/maps 计算。它的代码段被映射(只读和可执行):
556dad571000-556dad697000 r-xp 00000000 08:01 11272485 /usr/bin/fish
我的 C 标准库的代码段大概在
7fa96bcb3000-7fa96be4c000 r-xp 00000000 08:01 1704209 /lib/x86_64-linux-gnu/libc-2.25.so
我不了解您的安全问题(仅在评论中提到,应该进入问题),因为您的代码段是只读。实际上,恶意函数可以更改某些代码段的保护(使用mprotect(2)),然后对其进行更改。但是您首先需要解释(该恶意函数的)代码注入是如何发生的(您可以编写一个依赖于process isolation 并用formal methods 证明的程序来避免这种情况发生,假设您的 Linux 内核是可信任的) .
也许您有隐私方面的顾虑(但这是一个不同的问题)。显然,您不想在代码中以明文形式保留密码。
也许(但不太可能)您正在编写一个独立程序(没有任何操作系统),例如对于嵌入式系统及其微控制器(例如Arduino like)。在这种情况下,您的代码在闪存中。以及如何更改或删除它成为硬件特定的。顺便说一句,多次覆盖闪存会损害硬件。