【问题标题】:system() function not called from LD_PRELOAD'ed library未从 LD_PRELOAD 库调用的 system() 函数
【发布时间】:2017-06-15 07:24:01
【问题描述】:

我正在尝试在 linux 上使用 LD_PRELOAD 来包装对 system 函数的调用,以便为参数添加一些预处理。这是我的system.cpp

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string>
#include <iostream>

typedef int (*orig_system_type)(const char *command);

int system(const char *command)
{
    std::string new_cmd = std::string("set -f;") + command;
    // next line is for debuggin only
    std::cout << new_cmd << std::endl;

    orig_system_type orig_system;
    orig_system = (orig_system_type)dlsym(RTLD_NEXT,"system");
    return orig_system(new_cmd.c_str());
}

我用它构建它

g++ -shared -fPIC -ldl -o libsystem.so system.cpp

生成 .so 对象。然后我运行我的程序

$ LD_PRELOAD=/path/to/libsystem.so ./myprogram

我没有收到任何错误 - 但似乎我的 system 函数没有被调用。使用LD_DEBUG=libs 运行,我可以看到我的.so 正在加载,但是我的system 函数没有被调用,而是调用了标准库中的函数。

我需要对代码/构建进行哪些更改才能使其正常工作?

【问题讨论】:

    标签: c++ linux ld-preload


    【解决方案1】:

    你需要

    extern "C" int system ...
    

    因为它是由 C 函数调用的。 C++ 版本的名称被破坏,因此无法识别。

    【讨论】:

    • 是的!就是这样。我确实想知道名称修改,因为strings libsystem.so | grep systemsystem 函数名称周围有一些修改。使用extern "C",它现在可以正常工作了!
    【解决方案2】:

    您可能还需要考虑保存“orig_system”指针,以避免每次都调用 dlsym。您可以在 constructor/init 函数中执行此操作,因此您将拥有类似

    extern "C" {
    
    typedef int (*orig_system_type)(const char *command);
    
    static orig_system_type orig_system;
    
    static void myInit() __attribute__((constructor));
    
    void myInit()
    {
        orig_system = (orig_system_type)dlsym(RTLD_NEXT,"system");
    }
    
    int system(const char *command)
    {
        std::string new_cmd = std::string("set -f;") + command;
        // next line is for debuggin only
        std::cout << new_cmd << std::endl;
        return orig_system(new_cmd.c_str());
    }
    
    }
    

    (此代码未经测试,但我过去曾使用过此技术)。

    另一种方法是使用 GNU ld 的 --wrap 选项。

    如果你编译你的共享库

    -Wl,--包装系统

    然后在你编写的代码中

    extern "C" {
    
    void* __real_system(const char* command);
    void* __wrap_system(const char* command)
    {
        std::string new_cmd = std::string("set -f;") + command;
        // next line is for debuggin only
        std::cout << new_cmd << std::endl;
        return __real_system(new_cmd.c_str());
    }
    
    }
    

    (请注意,我从未使用过这个)。

    【讨论】:

      【解决方案3】:

      代码应该可以正常工作。假设驱动程序是这样的:

      #include <cstdlib>
      
      int main() {
          system("find -name *.cpp");
      }
      

      然后env LD_PRELOAD=$PWD/libsystem.so ./a.out 给我这个输出:

      set -f;find -name *.cpp
      ./system.cpp
      ./test.cpp
      

      这表明不仅您的调试语句出现,而且该命令禁用了 glob。

      【讨论】:

      • 查看@rici 的答案 - 你是对的,如果驱动程序是 C 语言,它会是正确的,但在我的情况下,它是 C++ 并且名称修改会妨碍。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-02
      • 2016-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多