我想不使用dlfcn.h
中的函数来动态加载库,但我有一个充满.so文件的文件夹,该文件使用以下命令编译:
gcc -Wall -shared -fPIC -o filename.so filename.c
所有这些都有一个名为:
的输入函数。void __start(int size, char** cmd);
(我知道,可能不是最好的名字)
然后我尝试在此之上调用open
,然后读取Elf64_Ehdr
的elf标头,并使用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;
}
让我进一步解释一下:
当我运行objdump -d filename.so
时,我得到_start
在0x6c0
处。 elf标头入口点返回0x5c0
(我用十进制的256补偿它)。
此外,pmap
显示了一个在假设0x7fdf94d0c000
处创建的可执行区域,因此我获得的函数指针方向(在0x7fdf94d0c6c0
处调用)应该是入口点并且可以调用通过函数指针。但是我得到的是,您猜对了:segfault。
我想指出的是,我有一个与(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");
}
答案 0 :(得分:3)
它要求我使用mmap技巧。我已经看到我的教授成功实现了它,但是我不知道如何实现。
要使其正常工作,您的__start
必须完全独立,并且不调用任何其他库(您通过调用strcmp
和{{1违反了该要求}}。
当您调用一个未解析的符号时,您询问动态加载程序以解析该符号,并且加载程序需要各种信息。该信息通常是在printf
调用期间设置的。由于您绕过了dlopen
,因此程序崩溃根本就不足为奇了。
您的第一步应该是更改代码,以使dlopen
根本不执行任何操作(为空),并确认您可以调用__start
。那将确认您正在正确计算其地址。
第二,在该代码中添加一个崩溃:
__start
,并验证您现在正在观察崩溃。这将确认您的代码正在被调用。
第三,删除崩溃的代码并仅使用 直接系统调用(例如void __start(...)
{
int *p = NULL;
*p = 42; // should crash here
}
(但不要调用write(1, "Hello\n", 6)
-您必须直接在库中实现它) ))实现您所需的一切。