gpt4 book ai didi

c - 在 Linux 上用 C 编写自定义加载器并为 x64 汇编

转载 作者:太空宇宙 更新时间:2023-11-04 06:50:48 27 4
gpt4 key购买 nike

我想在 x64 Linux 上为二进制代码编写自己的加载程序。将来我希望能够自己执行链接步骤,从而能够从 .o 目标文件中调用代码。但是现在,我想从已链接的可执行二进制文件中调用一个函数。

为了创建一些可以从“外部”调用的函数,我从以下源代码开始:

void foo(void)
{
int a = 2;
int b = 3;
a + b;
}

int main(void)
{
foo();
return 0;
}

这是我想使用加载器调用的 foo() 函数。使用以下命令链

gcc -o /tmp/main main.c
strip -s /tmp/main
objdump -D /tmp/main

我得到了foo()函数的汇编代码,如下所示:

...
0000000000001125 <foo>:
1125: 55 push %rbp
1126: 48 89 e5 mov %rsp,%rbp
1129: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
1130: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)
1137: 90 nop
1138: 5d pop %rbp
1139: c3 retq
...

这意味着,foo() 函数从 main 中的偏移量 0x1125 开始。我使用 hexeditor 验证了这一点。

下面是我的loader。目前还没有错误处理,代码非常难看。但是,它应该展示我想要实现的目标:

#include <stdio.h>
#include <stdlib.h>

typedef void(*voidFunc)(void);

int main(int argc, char* argv[])
{
FILE *fileptr;
char *buffer;
long filelen;
voidFunc mainFunc;

fileptr = fopen(argv[1], "rb"); // Open the file in binary mode
fseek(fileptr, 0, SEEK_END); // Jump to the end of the file
filelen = ftell(fileptr); // Get the current byte offset in the file
rewind(fileptr); // Jump back to the beginning of the file

buffer = (char *)malloc((filelen+1)*sizeof(char)); // Enough memory for file + \0
fread(buffer, filelen, 1, fileptr); // Read in the entire file
fclose(fileptr); // Close the file

mainFunc = ((voidFunc)(buffer + 0x1125));

mainFunc();

free(buffer);

return 0;
}

当执行此程序 objloader/tmp/main 时,它会导致 SEGFAULT。

mainFunc 变量指向正确的位置。我使用 gdb 验证了这一点。

操作码存在于堆上是个问题吗?实际上,我决定使我想调用的函数尽可能简单(副作用、函数参数所需的堆栈或寄存器,...)。但是,有些东西,我真的不明白。

任何人都可以在这里指出正确的方向吗?也非常感谢任何有关这方面有用文献的提示!

最佳答案

为了使buffer 内存区域可执行,您将不得不使用mmap。尝试

#include <sys/mman.h>
...
buffer = (char *)mmap(NULL, filelen /* + 1? Not sure why. */, PROT_EXEC | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);

这应该为内存区域提供您想要的权限,并让它与周围的代码一起工作。事实上,如果你想按预期的方式使用 mmap,请使用

int fd = open(argv[1], O_RDONLY);
struct stat myfilestats;
fstat(fd, &myfilestats);
buffer = (char*)mmap(NULL, myfilestats.st_size, PROT_EXEC, MAP_PRIVATE, fd, 0);
fclose(fd);
...
munmap(buffer, myfilestats.st_size);

使用MAP_ANONYMOUS 将使内存区域与文件描述符不相关联,但想法是如果它代表一个文件,文件描述符应该与它相关联。当你这样做时 Linux 会做各种很酷的技巧,比如只加载你实际最终访问的文件部分(延迟加载也会在文件很大时使程序非常流畅),如果多个程序都是访问同一个文件,那么它们将共享同一个物理内存位置。

关于c - 在 Linux 上用 C 编写自定义加载器并为 x64 汇编,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51654831/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com