簡體   English   中英

在Linux上用C和匯編為x64編寫自定義加載程序

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM