如何在保持标准c++函数功能的同时绕过它

How to Bypass a Standard C++ Function While Maintaining Its Functionality

本文关键字:功能 函数 c++ 标准      更新时间:2023-10-16

我正在寻找一种能够重新定义一组POSIX函数的方法,但随后通过调用原始函数结束重新定义。我的想法是,我试图创建一个层,可以限制什么操作系统的API可以调用取决于哪个"配置文件"是活跃的。这个"配置文件"决定了哪些函数集是允许的,任何未指定的都不应该使用。

例如,如果在一个配置文件中我不允许使用strcpy,我希望能够导致编译时错误(通过static_assert)或打印一些东西到屏幕上说"strcpy不允许在此配置文件中",如下面:

MY_string.h

#include <string.h>
char *strcpy(char *restrict s1, const char *restrict s2)
{
#if defined(PROFILE_PASS_THROUGH)
    printf("strcpy is not allowed in this profilen");
    return strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
    static_assesrt(0, "strcpy is not allowed in this profilen");
    return 0;
#else
    return strcpy(s1, s2);
#endif
}

这样在main。cpp中我就可以使用my_string。h

#define PROFILE_PASS_THROUGH
#include "MY_string.h"
int main()
{
    char temp1[10];
    char temp2[10];
    sprintf(temp2, "Testing");
    if (0 = strcpy(temp1, temp2))
    {
        printf("temp1 is %sn", temp1);
    }
    return 0;
}

现在我意识到我上面写的代码将不能正确编译,由于strcpy的重新定义,但是有没有一种方法来允许这种功能,而不玩宏或创建我自己的标准c和c++库?

  1. 你可以编写一个预处理器,将对标准例程的调用更改为对你自己例程的调用。这样的预处理器可能很复杂,这取决于您是否需要识别完整的c++语法来使用名称空间等来区分调用,或者您可以更随意地识别调用。

  2. 你可以链接到你自己的库,生成一个可重定位的对象模块,去掉已解析的名称。您的库将包含具有标准名称的例程,例如strcpy,它们执行您想要的任何代码并调用其他名称,例如Mystrcpy。由它生成的对象模块然后与第二个库和标准库链接。第二个库包含具有这些名称的例程,例如Mystrcpy,调用原始库名称strcpy。当然,这样做的细节取决于您的链接器。目标是有这样一个链:原始代码调用strcpy。这被解析为第一个库中的strcpy版本。该版本调用MystrcpyMystrcpy调用标准库strcpy

  3. 您可以编译成汇编并编辑汇编中的名称,以便调用您的例程而不是标准库例程。

  4. 在某些系统上,您可以使用dlsym<dlfcn.h>中定义的其他函数来加载包含标准实现的动态库,并通过dlsym返回的指针来调用它们,而不是使用源代码中的常用名称。

  5. GCC连接器有一个--wrap开关,它将对foo的调用解析为您的例程__wrap_foo,并将对__real_foo的调用(您将在实现中使用)解析为实际的foo

参见在Windows、UNIX和Macintosh OS X平台上拦截任意函数。

不,不能在c++中完成。您想要的更类似于LISP(或派生)语言,在那里您可以抓住现有函数的插槽并"在适当的位置覆盖它",可能会退回到原始实现。

典型的做法是在Unix上通过LD_PRELOAD,示例(Unix)下面代理一个函数调用,特别是malloc(完整示例):

/**
 * malloc() direct call
 */
inline void * libc_malloc(size_t size)
{
  typedef void* (*malloc_func_t)(size_t);
  static malloc_func_t malloc_func = (malloc_func_t) dlsym(RTLD_NEXT, "malloc");
  return malloc_func(size);
}

在你MY_String.h:

... blah blah
using mynamespace::strcpy;
#endif // header guard or maybe not there if using pragma

则所有没有以std::为前缀的strcpys将使用您的。如果你真的想禁止他们,当你找到使用霰弹枪的人时,带上一把霰弹枪。

如果使用一些最新的GCC(例如4.7或更新的版本),您也可以在MELT中编写一个GCC 插件GCC扩展来替换每个对strcpy的调用到您自己的mystrcpy。这可能会花费您一些工作(可能是几天,而不是几个小时),但是在编译器内部,在GCC编译器的内部表示(Gimple)上工作具有巨大的优势。因此,即使在内联之后也会这样做。并且由于您扩展了编译器,您可以根据需要定制它的行为。

MELT是扩展GCC的领域特定语言。

您无法避免调用这些函数。

C++程序可以做任何它想做的事情,它可以有一些代码从libc加载strcpy符号并运行它。如果恶意的开发人员想要调用该函数,您将无法避免。要做到这一点,您需要在一些特殊的环境中运行c++代码(在沙盒或虚拟机中),但恐怕这样的技术是不可用的。

如果你信任开发人员,并且你只是在寻找一种方法来提醒他们不要调用某些函数,那么可能会有一些解决方案。

一种解决方案可能是避免使用#include libc头文件(如cstring),并且只包含您自己的头文件,仅声明所需的函数。

另一个解决方案可能是查看编译后的可执行文件,以便找出调用了哪些函数,或者查看LD_PRELOAD——一个重新定义(并因此覆盖)标准函数以使其在运行时打印警告的库。

如何修改MY_string.h

#include <cstring>
namespace my_functions{
    char *strcpy(char *s1, const char *s2)
    {
#if defined(PROFILE_PASS_THROUGH)
        printf("strcpy is not allowed in this profilen");
        return std::strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
        static_assert(0, "strcpy is not allowed in this profilen");
        return 0;
#else
        return std::strcpy(s1, s2);
#endif
     }
}
using namespace my_functions;

要使其工作,您不能包含或使用命名空间std;

相关文章: