无法通过函数为全局变量赋值(Linux 上的共享库注入)

Unable to assign value to global variable through function (shared library injection on Linux)

我正在搞乱共享库注入并设法让它工作。但是没过多久,第一期就出现了。如果你看一下下面的代码块,你会发现它们之间的唯一区别是在第一个代码块中,“test”函数返回的值被分配给一个局部变量,而在第二个代码块中,它被分配给一个全局变量。

//main.cpp
#include <iostream>
#include "mem/mem.hpp"
#define PROCESS_MOD_NAME MEM_STR("/target\n")
#define LIBC_MOD_NAME    MEM_STR("/libc.so")

mem::moduleinfo_t test()
{
    mem::moduleinfo_t _modinfo;
    _modinfo.name = "testing";
    _modinfo.path = "testing";
    _modinfo.base = (mem::voidptr_t)10;
    _modinfo.end  = (mem::voidptr_t)20;
    _modinfo.size = 10;

    return _modinfo;
}

__attribute__((constructor))
void  libmain()
{
    mem::moduleinfo_t modinfo = test(); //local variable
    std::cout << "name: " << modinfo.name << std::endl;
    std::cout << "path: " << modinfo.path << std::endl;
    std::cout << "base: " << modinfo.base << std::endl;
    std::cout << "size: " << modinfo.size << std::endl;
    std::cout << "end:  " << modinfo.end  << std::endl;
}

这个有效,没有崩溃。

//main.cpp
#include <iostream>
#include "mem/mem.hpp"
#define PROCESS_MOD_NAME MEM_STR("/target\n")
#define LIBC_MOD_NAME    MEM_STR("/libc.so")

mem::moduleinfo_t test()
{
    mem::moduleinfo_t _modinfo;
    _modinfo.name = "testing";
    _modinfo.path = "testing";
    _modinfo.base = (mem::voidptr_t)10;
    _modinfo.end  = (mem::voidptr_t)20;
    _modinfo.size = 10;

    return _modinfo;
}

mem::moduleinfo_t modinfo; //global variable

__attribute__((constructor))
void  libmain()
{
    modinfo = test();
    std::cout << "name: " << modinfo.name << std::endl;
    std::cout << "path: " << modinfo.path << std::endl;
    std::cout << "base: " << modinfo.base << std::endl;
    std::cout << "size: " << modinfo.size << std::endl;
    std::cout << "end:  " << modinfo.end  << std::endl;
}

在这段代码中,目标程序直接崩溃了。 在 GDB 上调用“dlopen”时,输出为:

Program received signal SIGSEGV, Segmentation fault.
0xf7c19404 in __memcpy_sse2_unaligned () from /usr/lib32/libc.so.6
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(dlopen) will be abandoned.

目标程序代码为:

//target.cpp
#include <iostream>
#include <dlfcn.h>

int main()
{
    while(true);
    return 0;
}

编译此代码的命令行:

clang++ -m32 target.cpp -o build/target -ldl
clang++ -m32 -shared -fPIC main.cpp mem/mem.cpp -o build/libinject.so -ldl

不能使用全局变量是个大问题,那么是什么导致了这次崩溃,我该如何解决?

我不认为你的问题与全局变量有关(对我来说这两个例子都是段错误)。

核心问题是 C++ 全局构造函数 运行 在 C 构造函数(即标有 __attribute__((constructor)) 的函数)之后,因此当您在 [= 中写入时 std::cout 尚未初始化12=].

正如 yugr 所指出的,“C++ 全局构造函数 运行 在 C 构造函数之后(即标有 属性 ((构造函数)) 的函数”。考虑到这一点,我想到了解决这个问题的方法:创建一个 C 线程,在使用 PThread 加载 C++ 内容后,它会在后台 运行。然后我这样做来测试:

//main.cpp (shared library)
#include <iostream>
#include <pthread.h>

typedef struct
{
    std::string name;
    std::string path;
    void*       base;
    void*       end;
}example_t;

example_t test()
{
    example_t example;
    example.name = "hello";
    example.path = "test";
    example.base = (void*)0;
    example.end = (void*)0;
    return example;
}

example_t global_var;

void* thread_init(void* _args)
{
    global_var = test();
    std::cout << "global_var name: " << global_var.name << std::endl;
    std::cout << "global_var path: " << global_var.path << std::endl;
    std::cout << "global_var base: " << global_var.base << std::endl;
    std::cout << "global_var end:  " << global_var.end  << std::endl;
    return (void*)NULL;
}

__attribute__((constructor))
void libmain()
{
    pthread_t thread;
    pthread_create(&thread, NULL, (void*(*)(void*))thread_init, (void*)NULL);
}
//target.cpp (target program)
#include <iostream>
#include <dlfcn.h>

int main()
{
    while(true);
    return 0;
}

...使用以下脚本编译:

if [ ! -d build ]; then
    mkdir build
fi

clang++ -g -m32 -shared -fPIC main.cpp -o build/libinject.so -pthread
clang++ -g -m32 target.cpp -o build/target -ldl

...这个注入共享库:

#/bin/bash

if [ "$EUID" -ne 0 ]; then
    echo "[!] Run as root"
    exit 0
fi

if [ ! command -v gdb &> /dev/null ]; then
    echo "[!] Unable to find GDB, make sure you have it installed"
    exit 0
fi

proc_name="target"
proc_id=""
libpath="$(pwd)/build/libinject.so"

if ! proc_id=$(pgrep $proc_name) > /dev/null 2>&1; then
    echo "[!] The target process is not running"
    exit 0
fi

if [ ! -f $libpath ]; then
    echo "[!] Invalid shared library file"
fi

gdb -n -q -batch \
  -ex "attach $proc_id" \
  -ex "set $dlopen = (void*(*)(char*, int)) dlopen" \
  -ex "call $dlopen(\"$libpath\", 1)" \
  -ex "detach" \
  -ex "quit" > /dev/null 2>&1

echo "[*] done"

exit 1

你猜怎么着?工作得很好