mmap 加载共享 object 并获取函数指针
mmap load shared object and get function pointer
我想在不使用 dlfcn.h
中的函数的情况下动态加载库 我有一个充满 .so 文件的文件夹:
gcc -Wall -shared -fPIC -o filename.so filename.c
并且它们都有一个名为:
的入口函数
void __start(int size, char** cmd);
(我知道,可能不是最好的名字)
然后我尝试通过 so 调用 open
,然后读取小精灵 header 作为 Elf64_Ehdr
使用 mmap
将其加载到内存中(我知道我应该使用 mprotect
但我想让它先工作,然后添加安全性),最后将 elf header 条目添加到 mmap 返回的指针(加上偏移量 0x100)。
测试代码如下:
#include <elf.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
typedef void (*ptr_t) (int, char**);
void* alloc_ex_memory(int fd) {
struct stat s;
fstat(fd, &s);
void * ptr = mmap(0, s.st_size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
if (ptr == (void *)-1) {
perror("mmap");
return NULL;
}
return ptr;
}
void* load_ex_file(const char* elfFile) {
Elf64_Ehdr header;
void * ptr;
FILE* file = fopen(elfFile, "rb");
if(file) {
fread(&header, 1, sizeof(header), file);
if (memcmp(header.e_ident, ELFMAG, SELFMAG) == 0) {
ptr = alloc_ex_memory(fileno(file));
printf("PTR AT -> %p\n", ptr);
printf("Entry at -> %lx\n", header.e_entry+256);
ptr = (header.e_entry + 256);
} else {
ptr = NULL;
}
fclose(file);
return ptr;
}
return NULL;
}
int main(int argc, char** argv) {
ptr_t func = load_ex_file(argv[1]);
printf("POINTER AT: %p\n", func);
getchar();
func(argc, argv);
return 0;
}
让我进一步解释一下:
当我 运行 objdump -d filename.so
我得到 _start
在 0x6c0
。小精灵 header 入口点 returns 0x5c0
(我补偿它在 dec 中添加 256)。
另外 pmap
显示正在创建的可执行区域可以说 0x7fdf94d0c000
所以我得到和调用的函数指针方向在 0x7fdf94d0c6c0
这应该是入口点并可通过函数指针调用。但我得到的是,你猜对了:段错误。
我想指出的是,我有相同的示例 运行(dlopen
、dlsym
、dlclose
),但它要求我使用 mmap 技巧。我已经看到我的教授成功实施了它,但我不知道如何实施。 (也许我缺少一种更简单的方法)。
我的代码基于 this jit example
提前致谢!
编辑:这是我编译成 .so 的 .c 代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void __start(int size, char** cmd) {
if (size==2) {
if (!strcmp(cmd[1], "-l")) {
printf("******.********.*****@udc.es\n");
return;
} else if (!strcmp(cmd[1], "-n")) {
printf("****** ******** *****\n");
return;
}
} else if (size==1) {
printf("****** ******** ***** (******.********.*****@udc.es)\n");
return;
}
printf("Wrong command syntax: autores [-l | -n]\n");
}
its required that i use the mmap trick. I have seen my professor implement it successfully but i cannot figure out how.
为此,您的 __start
必须完全独立,并且 不 调用任何其他库(您调用 [=12= 违反了该要求] 和 printf
).
当你调用一个未解析的符号时,你要求动态加载器解析那个符号,加载器需要各种信息。该信息通常在 dlopen
通话期间设置。由于您绕过了 dlopen
,因此您的程序崩溃 一点也不 令人惊讶。
您的第一步应该是更改您的代码,使 __start
根本不执行任何操作(为空),然后验证您是否可以调用 __start
。这将确认您正在正确计算其地址。
其次,为该代码添加崩溃:
void __start(...)
{
int *p = NULL;
*p = 42; // should crash here
}
并确认您现在正在观察崩溃。这将确认您的代码正在被调用。
第三,删除崩溃代码并使用仅直接系统调用(例如write(1, "Hello\n", 6)
(但不要调用write
——你必须实现它直接在你的库中))来实现你需要的任何东西。
我想在不使用 dlfcn.h
中的函数的情况下动态加载库 我有一个充满 .so 文件的文件夹:
gcc -Wall -shared -fPIC -o filename.so filename.c
并且它们都有一个名为:
的入口函数void __start(int size, char** cmd);
(我知道,可能不是最好的名字)
然后我尝试通过 so 调用 open
,然后读取小精灵 header 作为 Elf64_Ehdr
使用 mmap
将其加载到内存中(我知道我应该使用 mprotect
但我想让它先工作,然后添加安全性),最后将 elf header 条目添加到 mmap 返回的指针(加上偏移量 0x100)。
测试代码如下:
#include <elf.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
typedef void (*ptr_t) (int, char**);
void* alloc_ex_memory(int fd) {
struct stat s;
fstat(fd, &s);
void * ptr = mmap(0, s.st_size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
if (ptr == (void *)-1) {
perror("mmap");
return NULL;
}
return ptr;
}
void* load_ex_file(const char* elfFile) {
Elf64_Ehdr header;
void * ptr;
FILE* file = fopen(elfFile, "rb");
if(file) {
fread(&header, 1, sizeof(header), file);
if (memcmp(header.e_ident, ELFMAG, SELFMAG) == 0) {
ptr = alloc_ex_memory(fileno(file));
printf("PTR AT -> %p\n", ptr);
printf("Entry at -> %lx\n", header.e_entry+256);
ptr = (header.e_entry + 256);
} else {
ptr = NULL;
}
fclose(file);
return ptr;
}
return NULL;
}
int main(int argc, char** argv) {
ptr_t func = load_ex_file(argv[1]);
printf("POINTER AT: %p\n", func);
getchar();
func(argc, argv);
return 0;
}
让我进一步解释一下:
当我 运行 objdump -d filename.so
我得到 _start
在 0x6c0
。小精灵 header 入口点 returns 0x5c0
(我补偿它在 dec 中添加 256)。
另外 pmap
显示正在创建的可执行区域可以说 0x7fdf94d0c000
所以我得到和调用的函数指针方向在 0x7fdf94d0c6c0
这应该是入口点并可通过函数指针调用。但我得到的是,你猜对了:段错误。
我想指出的是,我有相同的示例 运行(dlopen
、dlsym
、dlclose
),但它要求我使用 mmap 技巧。我已经看到我的教授成功实施了它,但我不知道如何实施。 (也许我缺少一种更简单的方法)。
我的代码基于 this jit example
提前致谢!
编辑:这是我编译成 .so 的 .c 代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void __start(int size, char** cmd) {
if (size==2) {
if (!strcmp(cmd[1], "-l")) {
printf("******.********.*****@udc.es\n");
return;
} else if (!strcmp(cmd[1], "-n")) {
printf("****** ******** *****\n");
return;
}
} else if (size==1) {
printf("****** ******** ***** (******.********.*****@udc.es)\n");
return;
}
printf("Wrong command syntax: autores [-l | -n]\n");
}
its required that i use the mmap trick. I have seen my professor implement it successfully but i cannot figure out how.
为此,您的 __start
必须完全独立,并且 不 调用任何其他库(您调用 [=12= 违反了该要求] 和 printf
).
当你调用一个未解析的符号时,你要求动态加载器解析那个符号,加载器需要各种信息。该信息通常在 dlopen
通话期间设置。由于您绕过了 dlopen
,因此您的程序崩溃 一点也不 令人惊讶。
您的第一步应该是更改您的代码,使 __start
根本不执行任何操作(为空),然后验证您是否可以调用 __start
。这将确认您正在正确计算其地址。
其次,为该代码添加崩溃:
void __start(...)
{
int *p = NULL;
*p = 42; // should crash here
}
并确认您现在正在观察崩溃。这将确认您的代码正在被调用。
第三,删除崩溃代码并使用仅直接系统调用(例如write(1, "Hello\n", 6)
(但不要调用write
——你必须实现它直接在你的库中))来实现你需要的任何东西。