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



我正在处理共享库注入,并设法使其工作。但没过多久,第一期就出现了。如果你看一下下面的代码块,你会发现它们之间唯一的区别是在第一个代码块上;测试";函数被分配给局部变量,而在第二个代码块中,它被分配给全局变量。

//main.cpp
#include <iostream>
#include "mem/mem.hpp"
#define PROCESS_MOD_NAME MEM_STR("/targetn")
#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("/targetn")
#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;
}

在这个代码块上,目标程序只是崩溃。当呼叫";dlopen";在GDB上,输出为:

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

不能使用全局变量是一件大事,那么是什么原因导致了这次崩溃,我该如何解决呢?

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

核心问题是C++全局构造函数在C构造函数(即用__attribute__((constructor))标记的函数(之后运行,因此当您在libmain中写入std::cout时,它还没有初始化。

正如yugr所指出的,"C++全局构造函数在C构造函数之后运行(即用属性((constructor((";。考虑到这一点,我想了一种解决方法:创建一个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

你猜怎么着?运行良好

最新更新