如果我有这样的功能
void check(char *buffer)
{
//some stuff that only needs to be run once
}
Run Code Online (Sandbox Code Playgroud)
在我调用一次后,有什么方法可以从内存中删除"删除"这个功能吗?
我可以轻松地在纯ASM中执行此操作.但我想知道是否有更标准的方法.
C++标准(甚至是C标准)将程序代码的总体视为不可变的.所以"删除"一个函数是没有意义的.
但是,让我们务实.大多数情况下,你的C++程序被编译成在某些操作系统中运行的某些可执行文件(我认为操作系统是Linux,但你可以调整我对其他操作系统的答案).有关操作系统的一般概述,请阅读操作系统:三个简易件(可免费下载)
然后,当你的程序启动时(可能是你的shell 在某个进程中执行fork(2)然后是execve(2)),会创建一个新的虚拟地址空间(由内核管理,配置MMU并处理页面错误)通过例如从磁盘分页适当的数据.
通常,程序的ELF可执行文件包含一个代码段,该代码段在虚拟地址空间的某些只读和可执行段中进行内存映射.由于该部分是只读的,因此您无法"删除"或"写入"它.对于共享库,虚拟地址空间更复杂,并包含多个代码段(每个库可能有一个或多个).
我可以轻松地在纯ASM中执行此操作.
在某些(现代)操作系统上运行的程序错误.由于代码段是只读的,所以你需要改变你的虚拟地址空间(只有做到这一点的方式是直通相应的系统调用一样mmap,munmap,mprotect,你可以调用它们在C++中,C,或者甚至在ASM).某些函数已在汇编程序中编码的事实不会改变它.甚至汇编代码也不能覆盖代码段(在这种情况下你仍然会遇到一些分段错误),而不会改变你的虚拟地址空间.
你可以玩低级技巧,例如使用munmap(2)系统调用 - 来自某些C++代码或C代码或汇编代码 - 来改变你的虚拟地址空间并从中删除一些页面.我强烈建议不这样做.但是,如果你走这条路,你需要了解ELF可执行文件和共享对象以及虚拟地址空间的所有细节.注意ASLR,elf(5),ld-linux(8),vdso(7).使用objdump(1)和readelf(1)并查看ld(1)以了解ELF可执行文件的详细信息.
您可以将您的代码(check以及其他几个函数)放入某个插件中,但在您的情况下,您可能不应该这样做.在这种情况下,您可以动态加载该插件,然后卸载它.您将使用dlopen(3)加载插件并dlclose卸载它.dlopen插件的-ing做了几个 mmap(2)(因此增加了虚拟地址空间)和进程重定位,并且它dlclose做了几个munmap(2)(因此缩小了虚拟地址空间).
/proc/在Linux上玩(参见proc(5)).尝试cat /proc/self/maps和cat /proc/$$/maps并cat /proc/$$/status在终端./proc/self/maps从您的C++程序中读取(例如,请参阅此内容).
例如,fish是用C++编码的shell.它现在在我的桌面上运行pid 4735.它的虚拟地址空间有69个段wc /proc/4735/maps.它的代码段被映射(只读和可执行):
556dad571000-556dad697000 r-xp 00000000 08:01 11272485 /usr/bin/fish
Run Code Online (Sandbox Code Playgroud)
我的C标准库的代码段可能在
7fa96bcb3000-7fa96be4c000 r-xp 00000000 08:01 1704209 /lib/x86_64-linux-gnu/libc-2.25.so
Run Code Online (Sandbox Code Playgroud)
我不理解您的安全问题(仅在评论中提及,这应该包含在问题中),因为您的代码段是只读的.实际上,恶意函数可以改变某些代码段的保护(使用mprotect(2))然后改变它.但是你需要解释代码注入(该恶意函数)是如何发生的(你可以编写一个依赖于进程隔离的程序,并通过正式方法证明可以避免这种情况发生,假设你的Linux内核是可信的).
也许您有隐私问题(但这会产生不同的问题).显然,您不希望在代码中将密码保留为明文.
也许(但不太可能)你正在编写独立程序(没有任何操作系统),例如嵌入式系统及其微控制器(例如Arduino).在这种情况下,您的代码在闪存中.如何更改或删除它将变为特定于硬件.顺便说一句,多次覆盖闪存会损害硬件.