我希望能够在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。我使用十六进制编辑器进行了验证。以下是我的加载器。目前还没有错误处理,代码非常丑陋。但是,它应该可以演示我想要实现的内容:
#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
进行了验证。
堆上存储操作码是否有问题?实际上,我决定让我想调用的函数尽可能简单(不需要使用栈或寄存器来传递参数等副作用)。但是,仍然有一些我真正不理解的问题。
请问有人可以指点我正确的方向吗?对于这方面有用的文献提示也将不胜感激!
.so
结尾的动态库? - Some programmer dudemmap
,而不是malloc
/fread
。此外,如果您无法保证加载地址(这个是可以的),则代码应该是位置无关的。您可能还想看一下dlopen
。 - Jesterfoo
在代码段中的偏移量为0x1125。但是可执行文件不仅仅是程序或其代码在内存中的形象,它是一个结构化文件。在开头有说明文件中包含了什么内容,以及有多个节(section)。每个节都有说明它是什么类型、长度等信息。还有关于可重定位符号等的信息。要加载可执行文件,必须解析和处理文件内容。 - Eric Postpischil