[英]Writing a custom loader in C and assembly for x64 on Linux
我想在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驗證了這一點。
以下是我的裝載機。 目前還沒有錯誤處理,並且代碼非常丑陋。 但是,它應該演示我想要實現的目標:
#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會執行各種很酷的技巧,例如僅加載您最終最終訪問的文件部分(如果文件很大,則延遲加載也將使程序非常流暢),並且如果全部包含多個程序訪問相同的文件,則它們將共享相同的物理內存位置。
這是我的“加載程序”的最終版本,該版本基於Nicholas Pipiton的回答 。 再說一次:沒有錯誤處理,沒有簡化,沒有考慮到現實中的場景要困難得多,等等。
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
typedef void(*voidFunc)(void);
int main(int argc, char* argv[])
{
char* buffer;
voidFunc mainFunc;
struct stat myfilestats;
int fd;
fd = open(argv[1], O_RDONLY);
fstat(fd, &myfilestats);
buffer = mmap(NULL, myfilestats.st_size, PROT_EXEC, MAP_PRIVATE, fd, 0);
close(fd);
mainFunc = ((voidFunc)(buffer + 0x1125));
mainFunc();
munmap(buffer, myfilestats.st_size);
return EXIT_SUCCESS;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.