簡體   English   中英

可執行文件中全局const變量的偏移量

[英]Offset of global const variable in executable

希望有一個可執行文件通過修改自己的全局常量來保存其狀態。 只是為了擁有一個完全獨立的可執行文件。

我想到了一些解決方案/技巧:

  1. 使用libelf並使程序自行解析以查找偏移量。
  2. 添加一個特定的標記,然后在可執行文件中搜索它。 我想這甚至可能是跨平台的?
  3. 使用對象轉儲實用程序確定可執行文件中的地址。 這可能需要始終作為項目構建的后期處理來完成。

鏈接器提供此信息會很方便。

鏈接器是否可以在可執行文件中提供只讀節的偏移量?

謝謝

您想要做的是棘手且難以攜帶的。

但是,您可以研究src / unexelf.c中的 GNU emacs unexec函數(對於Linux;其他OS具有類似的文件)。

您也可以玩鏈接器技巧,例如使用自己的ld腳本。

請注意,這些技巧可能是特定於處理器,編譯器,內核和libc版本的。

也許您想要應用程序檢查點 特別地, BLCR對您可能有用。

您實質上是在談論二進制重寫。 實現此目標而又不影響編譯過程的一種方法是將虛擬地址映射到物理地址,然后對其進行修補。 有趣的是,這是我在碩士論文中介紹的內容 以下圖像和文本是從該文檔中提取的:

http://localhostr.com/file/hyB1iFuiL0nV/Loading_Binary.jpg

請注意,我的原始項目背后的概念是在假定無法修改編譯過程的情況下,用其他二進制代碼重寫代碼。 如果您的要求和假設不同,那么這可能不是最簡單,最好的方法。

這里最重要的想法是,磁盤表示中的一部分映射到內存時會保留(而不是拆分)。 這意味着在磁盤表示形式的部分中具有一定偏移量的數據在加載到內存后將偏移相同的數量。

libelf ,類似於libbfd ,可執行文件包含一組節,代碼和數據都可以駐留在其中。 當操作系統將可執行文件加載到內存中時,每個部分都基於某個基址。 我們可以將其反轉以將虛擬內存地址映射到物理文件偏移。 如果可以找到物理文件偏移,則可以將字節修補為常規文件。

  • 首先,使用libelf解析可執行文件的節頭。 這使我們能夠獲得一組節,最重要的是,對於libelf每個節,它可以告訴我們三件事:
    1. 區段大小區段的大小。
    2. 節基地址當磁盤上的可執行文件加載到內存中時,節將基於的地址。
    3. 節磁盤偏移量節的磁盤偏移量。
  • 通過遍歷上一步中提取的段信息,可以找出任意虛擬內存地址包含在哪個段中。在修補過程中,我們感興趣的內存地址是繞行代碼所在的地址被寫。 虛擬內存地址到段中的偏移量可以通過(virtual_memory_address - section_base_address)計算。
  • 因此,可以通過(section_disk_offset + (virtual_memory_address - section_base_address))計算虛擬內存地址的磁盤偏移量。

此過程允許將任意虛擬內存地址映射到其相應的磁盤文件偏移量。 然后可以使用常規的C文件IO功能(例如fopen / fseek / fwrite / fclose修補此偏移量。

這是我使用以上步驟將虛擬地址映射到物理文件偏移量的代碼:

/*
 * Returns the corresponding 32 bit executable file offset of a virtual memory
 * address.
 */
uint32_t vaddr32_to_file_offset(char * filepath, uint32_t vaddr)
{
    int      fd     = open(filepath, O_RDONLY);
    Elf *    e      = elf_begin(fd, ELF_C_READ, NULL);
    uint32_t offset = 0;

    Elf_Scn * scn = NULL;
    while((scn = elf_nextscn(e, scn)) != NULL) {
        Elf32_Shdr * shdr = elf32_getshdr(scn);
        if(vaddr >= shdr->sh_addr &&
                (vaddr <= (shdr->sh_addr + shdr->sh_size))) {
            offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
            break;
        }
    }

    elf_end(e);
    close(fd);
    return offset;
}

/*
 * Returns the corresponding 64 bit executable file offset of a virtual memory
 * address.
 */
uint64_t vaddr64_to_file_offset(char * filepath, uint64_t vaddr)
{
    int      fd     = open(filepath, O_RDONLY);
    Elf *    e      = elf_begin(fd, ELF_C_READ, NULL);
    uint64_t offset = 0;

    Elf_Scn * scn = NULL;
    while((scn = elf_nextscn(e, scn)) != NULL) {
        Elf64_Shdr * shdr = elf64_getshdr(scn);
        if(vaddr >= shdr->sh_addr &&
                (vaddr <= (shdr->sh_addr + shdr->sh_size))) {
            offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
            break;
        }
    }

    elf_end(e);
    close(fd);
    return offset;
}

這是在給定偏移量的情況下修補ELF可執行文件的代碼:

/*
 * Sets the bytes at an arbitrary offset of a file to the contents of buffer.
 */
static bool patch_file(char * filepath, uint64_t offset, void * buffer,
        size_t size)
{
    FILE * pFile = fopen(filepath, "r+");

    if(pFile == NULL) {
        return FALSE;
    }

    fseek(pFile, offset, SEEK_SET);
    fwrite(buffer, 1, size, pFile);
    fclose(pFile);
    return TRUE;
}

更詳細的信息可以在報告本身中找到,該報告可在此處公開獲得。

這是不可能的,因為如果可能的話,編譯器通常會替換全局和靜態常量作為機器代碼中的立即數值。 例如:

const int x=3;

int main()
{
 return x;
}

為main()(OSX / gcc -O3)生成以下代碼:

_main:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $3, %eax // <= immediate constant!
    popq    %rbp
    ret

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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