C mmap加载共享对象并获取函数指针
我想动态加载一个库,而不使用C mmap加载共享对象并获取函数指针,c,elf,mmap,C,Elf,Mmap,我想动态加载一个库,而不使用dlfcn.h中的函数。我有一个文件夹,里面装满了编译时使用的.so文件: gcc -Wall -shared -fPIC -o filename.so filename.c 它们都有一个输入函数,名为: void __start(int size, char** cmd); (我知道,可能不是最好的名字) 然后,我尝试通过so调用open,然后将elf头作为Elf64_Ehdr读取,并使用mmap将其加载到内存中(我知道我应该使用mprotect,但我想先让它工
dlfcn.h
中的函数。我有一个文件夹,里面装满了编译时使用的.so文件:
gcc -Wall -shared -fPIC -o filename.so filename.c
它们都有一个输入函数,名为:
void __start(int size, char** cmd);
(我知道,可能不是最好的名字)
然后,我尝试通过so调用open
,然后将elf头作为Elf64_Ehdr
读取,并使用mmap
将其加载到内存中(我知道我应该使用mprotect
,但我想先让它工作,然后添加安全性),最后将elf头条目添加到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;
}
这要求我使用mmap技巧。我已经看到我的教授成功地实现了它,但我不知道如何实现
要使其工作,您的\u start
必须是完全独立的,并且不能调用任何其他库(您通过调用strcmp
和printf
违反了该要求)
在调用未解析的符号时,您要求动态加载程序解析该符号,加载程序需要各种信息。这些信息通常在dlopen
调用期间设置。由于您绕过了dlopen
,因此程序崩溃一点也不奇怪
您的第一步应该是更改代码,使\uuuu start
完全不起任何作用(为空),然后验证您是否可以调用\uu start
。这将确认您正确计算了它的地址
其次,将崩溃添加到该代码中:
void __start(...)
{
int *p = NULL;
*p = 42; // should crash here
}
并验证您现在正在观察崩溃。这将确认您的代码正在被调用
第三,删除崩溃代码,只使用直接系统调用(例如
write(1,“Hello\n”,6)
(但不要调用write
——您必须在库中直接实现它))来实现您需要的任何功能。printf(“PTR AT->%lx\n”,PTR);
是指针的错误格式。%p
用于打印指针。@AndrewHenle编辑了代码。感谢您的回答,很抱歉直到今天才回复:如果我需要加载复杂的代码怎么办?。实际上我作为示例上传的代码是其中最简单的。我无法自己实现每个系统调用。有什么想法吗?@tretraft“什么是我需要复杂的代码”--然后您需要重新实现动态加载程序的一部分。但是为什么您不能简单地dlopen
这个库呢?
void __start(...)
{
int *p = NULL;
*p = 42; // should crash here
}