Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;删除函数_C++_Memory - Fatal编程技术网

C++ C++;删除函数

C++ C++;删除函数,c++,memory,C++,Memory,如果我有这样一个函数 void check(char *buffer) { //some stuff that only needs to be run once } 在我调用一次函数后,有没有办法从内存中“删除”该函数 我可以很容易地用纯粹的ASM来做这件事。但我想知道是否有更标准的方法。 < P> C++标准(甚至C语言)考虑程序的代码的整体性是不可变的。因此,“删除”一个函数是没有意义的 但让我们务实一点。大多数时候,你的C++程序编译成一些在某些操作系统中运行(我假设OS是Li

如果我有这样一个函数

void check(char *buffer)
{
    //some stuff that only needs to be run once
}
在我调用一次函数后,有没有办法从内存中“删除”该函数

我可以很容易地用纯粹的ASM来做这件事。但我想知道是否有更标准的方法。

< P> C++标准(甚至C语言)考虑程序的代码的整体性是不可变的。因此,“删除”一个函数是没有意义的

但让我们务实一点。大多数时候,你的C++程序编译成一些在某些操作系统中运行(我假设OS是Linux,但是你可以将我的答案适应其他OSES)。有关操作系统的概述,请阅读(可免费下载)

然后,在某些情况下,当您的程序启动时(可能是通过shell执行一个接一个的操作),将创建一个新的(并由内核进行管理,内核将配置并通过适当的数据(例如,来自磁盘的数据)进行处理)

通常,程序的可执行文件包含一个内存,该内存映射在虚拟地址空间的某个只读和可执行段中。因为该部分是只读的,所以不能“删除”或“写入”。虚拟地址空间更复杂,包含多个代码段(每个库可能有一个或多个)

我可以很容易地用纯粹的ASM来做这件事

对于在某些(现代)操作系统上运行的程序而言,是错误的。因为<强>代码段是只读< /强>,所以需要更改虚拟地址空间(唯一的方法是通过适当的系统调用,如:代码> MMAP<代码>,<代码> MunMAP< /COD>,<代码> McReule<代码>;您可以在C++、C或甚至ASM中调用它们。某些函数已经在汇编程序中编码,这一事实并没有改变这一点。即使是汇编代码也无法在不改变虚拟地址空间的情况下覆盖代码段(在这种情况下仍然会得到一些)

你可以玩低级技巧,例如使用来自某些C++代码或C代码或汇编代码的系统调用,来改变你的虚拟地址空间,并从中删除一些页面。我强烈建议不要这样做。但是,如果您这样做,您需要了解ELF可执行文件和共享对象以及虚拟地址空间的所有细节。注意。使用&&see了解ELF可执行文件的详细信息

您可以将代码(您的
检查
和其他几个函数)放入一些函数中,但在您的情况下,您可能不应该这样做。在这种情况下,您可以使用该插件,然后卸载它。您将使用加载插件,并使用
dlclose
卸载插件。插件的
dlopen
-ing执行多个操作(从而增加虚拟地址空间)和处理,而其
dlclose
执行多个操作(从而缩小虚拟地址空间)

在Linux上玩
/proc/
(请参阅)。在终端中尝试
cat/proc/self/maps
cat/proc/$$/maps
cat/proc/$$/status
。从C++程序中读取<代码> /PRO/BUI/MAP> <代码>(例如,参见)

例如,是用C++编码的shell。它现在在我的桌面上以pid 4735运行。它的虚拟地址空间有69个段,用

wc/proc/4735/maps
计算。其代码段已映射(只读和可执行):

我的C标准库的代码段可能位于

7fa96bcb3000-7fa96be4c000 r-xp 00000000 08:01 1704209  /lib/x86_64-linux-gnu/libc-2.25.so
我不理解您的安全顾虑(仅在注释中提及,应进入问题),因为您的代码段是只读的。实际上,恶意函数可能会(使用)更改某些代码段的保护,然后更改它。但是您首先需要解释(恶意函数的)代码注入是如何发生的(假设您的Linux内核是可信任的,您可以编写一个依赖并经验证的程序来避免这种情况发生)

也许你有隐私问题(但这是另一个问题)。显然,您不希望在代码中保留密码作为明文



可能(但不太可能)您正在编写一个独立的程序(没有任何操作系统),例如为嵌入式系统及其微控制器(例如类似)编写的程序。在这种情况下,您的代码在闪存中。而如何更改或删除它则取决于硬件。顺便说一句,多次覆盖闪存会损害硬件。

您可以这样做,只需稍微降低性能(如果您只想调用一次函数,则与此无关):

在.c文件中:

static void check_private(char *buffer)
{
    //some stuff that only needs to be run once
    check = NULL; // disable calling this ever again
}

// declare check as pointer to function (pointer to char) returning void
// and intialize to check_private
void (*check)(char*) = check_private;
在相应的.h文件中:

extern void (*check)(char*);
只要去除调试符号,并且不将其存储在程序的其他位置,
check
指针将是存储
check\u private
地址的唯一位置,因此一旦覆盖它,它就消失了


NULL
是一个不错的禁用值,如果您知道您运行的是一个具有内存保护的正常操作系统,因为它将产生的UB将是SEGFULT,并且很容易检查

如果不需要segfault,可以将其设置为指向另一个函数,例如:

static void check_disabled(char *buffer)
{
    (void)buffer; // disable compilation warning about unused parameter
    abort(); // maybe this
    exit(EXIT_FAILURE); // or this
    return; // or this for no-op
    for(;;); // or even this on some tiny embedded system
}

如果这是一个库,并且您需要一些基本的硬保护以防止无效使用,那么您可以使用以下导出函数代替指针:

void check(char *buffer)
{
    static int called = 0;
    if (!called) {
        called = 1;
        check_private(buffer);
    } else {
        // called 2nd time, do something, see above for suggestions
    }
}

同样,只要去掉调试符号,被调用的
变量的地址存储的唯一位置就是这个函数的代码中,所以无法访问它。好的,除了通过调用UB来猜测它的地址并使用指针来修改它之外,尤其是在现代优化编译器中,这实际上是UB而不仅仅是hack,因为无法保证汇编和数据编译器为C代码实际生成了什么。

为什么?你想实现什么?@Tas它将防止exe被双重“注入”。这是一种安全措施,您还可以执行
mprotect
来取消对页面的保护,并使用
nop
sled将函数体覆盖为最终
ret
。这样就避免了取消映射整个对象的需要
void check(char *buffer)
{
    static int called = 0;
    if (!called) {
        called = 1;
        check_private(buffer);
    } else {
        // called 2nd time, do something, see above for suggestions
    }
}