2012-08-27 40 views
6

假設我有一個沒有外部依賴關係的C文件,並且只有const數據部分。我想編譯這個文件,然後得到一個二進制blob,我可以在另一個程序中加載,其中函數將通過函數指針使用。將目標文件轉換爲二進制代碼

讓我們舉個例子,這裏是一個fictionnal二進制模塊,在f1.c

static const unsigned char mylut[256] = { 
    [0 ... 127] = 0, 
    [128 ... 255] = 1, 
}; 

void f1(unsigned char * src, unsigned char * dst, int len) 
{ 
    while(len) { 
     *dst++ = mylut[*src++]; 
     len--; 
    } 
} 

我想編譯它f1.o,然後f1.bin,並使用它像這樣 中前衛.c

int somefunc() { 
    unsigned char * codedata; 
    f1_type_ptr f1_ptr; 
    /* open f1.bin, and read it into codedata */ 

    /* set function pointer to beginning of loaded data */ 
    f1_ptr =(f1_type_ptr)codedata; 

    /* call !*/ 
    f1_ptr(src, dst, len); 
} 

我想從f1.c到f1.o涉及-fPIC來獲得位置獨立性。我可以使用 從f1.o到f1.bin使用哪些標誌或鏈接器腳本?

澄清:

我知道動態鏈接。動態鏈接在這種情況下是不可能的。如果可能的話,鏈接步驟必須是cast func指向加載數據的指針。

請假定沒有OS支持。如果可以的話,我會舉例如 在與PC相關的地址的彙編中寫入f1。

+0

你知不知道,你可以使用共享對象的文件?您將.c文件編譯爲.so文件,然後將'dlopen()'加載到您的程序中,並將函數指針dlsym()傳遞給函數。然後你可以打電話給它。 –

+0

讓我們忘記libc和動態鏈接 – shodanex

+0

你想動態地(即在運行時)加載'f1.bin'嗎?然後你必須建立一個共享庫,並使用ldopen()+ ldsym()或其他模塊加載器(如gmodule)。由於潛在的安全威脅(執行數據段等),嘗試以其他方式執行此操作可能會很困難並被拒絕。 –

回答

2

您應該考慮構建一個共享庫(用於Linux的.dll dll或用於linux的.so)。

生成LIB是這樣的:

gcc -c -fPIC test.c 
gcc -shared test.o -o libtest.so 

如果你想從你的代碼中動態加載庫,看看功能的dlopen(3)的dlsym(3)

或者,如果你想在庫在編譯時的程序與聯繫,建立

gcc -c main.c 
gcc main.o -o <binary name> -ltest 

編輯:

我真的不知道什麼,我會說在這裏,但這可以給你一個線索,在你的研究進展...

如果你不想使用的dlopen的dlsym,你可以嘗試從的.o文件中讀取符號表,以便找到函數地址,然後,MMAP對象在內存中的文件與讀取和執行權限。那麼你應該能夠在你找到的地址執行加載的代碼。但要小心在代碼中可能遇到的其他依賴關係。

您可以檢查手冊頁elf(5)所有的

+0

我完全不想使用動態鏈接,相應地編輯了問題 – shodanex

+0

** dlopen **和** dlsym **不暗示動態鏈接(因爲您的程序不會鏈接到庫,您的二進制文件不依賴於它,編譯期間不需要該庫)。函數** dlopen **可讓您加載庫,並且** dlsym **將根據您提供的符號名稱返回函數地址。 – phsym

+0

dlsym表示調用操作系統提供的動態鏈接器來分析庫文件,執行映射等...... – shodanex

6

首先,對方表示,你應該考慮使用一個DLL或SO。

這就是說,如果你真的想這樣做,你需要替換鏈接腳本。像這樣的東西(不是很好的測試,但我認爲它的工作原理):

$ gcc -c -fPIC test.c 

鏈接有:

ENTRY(_dummy_start) 
SECTIONS 
{ 
    _dummy_start = 0; 
    _GLOBAL_OFFSET_TABLE_ = 0; 
    .all : { 
     _all = .; 
     LONG(f1 - _all); 
     *(.text .text.* .data .data.* .rodata .rodata.*) 
    } 
} 

然後編譯

$ ld -T script.ld test.o -o test.elf 

,並提取與二進制BLOB :

$ objcopy -j .all -O binary test.elf test.bin 

可能是索姆e腳本的解釋是歡迎的:

  • ENTRY(_dummy_start)這只是避免關於程序沒有入口點的警告。
  • _dummy_start = 0;它定義了前一行中使用的符號。該值不被使用。
  • _GLOBAL_OFFSET_TABLE_ = 0;這可以防止另一個鏈接器錯誤。我不認爲你真的需要這個符號,所以它可以被定義爲0.
  • .all這就是將收集你的blob的所有字節的部分的名稱。在本示例中,它將全部是.text,.data.rodata部分。如果您的功能複雜,您可能需要更多,在這種情況下,objdump -x test.o是您的朋友。
  • LONG(f1 - _all)不是真的需要,但是你想知道你的函數偏移到blob中,不是嗎?你不能假設它將在偏移量爲0.利用這一行,blob中的前4個字節將是符號f1(你的函數)的偏移量。如果使用64位指針,則將LONG更改爲QUAD

UPDATE:而且現在quick'n'dirty測試(它的作品!):

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/mman.h> 

typedef void (*f1_t)(char *a, char *b, int len); 
f1_t f1; 

int main() 
{ 
    char *blob = (char*)valloc(4096); 
    FILE *f = fopen("test.bin", "rb"); 
    fread(blob, 1, 4096, f); 
    fclose(f); 

    unsigned offs = *(unsigned*)blob; 
    f1 = (f1_t)(blob + offs); 
    mprotect(blob, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); 
    char txt[] = "¡hello world!"; 
    char txt2[sizeof(txt)] = ""; 
    f1(txt, txt2, sizeof(txt) - 1); 
    printf("%s\n%s\n", txt, txt2); 
    return 0; 

} 
+0

如果引用其他符號(如標準庫),可能需要'_GLOBAL_OFFSET_TABLE_',製成。 – AProgrammer

+0

@AProgrammer:但OP特別說_no外部依賴關係_,所以可能不需要。如果他有權訪問任何圖書館,那麼他將不得不靜態鏈接blob中的所有圖書館,或者自己動態鏈接......並且這會很複雜。 – rodrigo