[英]Offset of global const variable in executable
希望有一個可執行文件通過修改自己的全局常量來保存其狀態。 只是為了擁有一個完全獨立的可執行文件。
我想到了一些解決方案/技巧:
鏈接器提供此信息會很方便。
鏈接器是否可以在可執行文件中提供只讀節的偏移量?
謝謝
您想要做的是棘手且難以攜帶的。
但是,您可以研究src / unexelf.c中的 GNU emacs unexec
函數(對於Linux;其他OS具有類似的文件)。
您也可以玩鏈接器技巧,例如使用自己的ld
腳本。
請注意,這些技巧可能是特定於處理器,編譯器,內核和libc版本的。
您實質上是在談論二進制重寫。 實現此目標而又不影響編譯過程的一種方法是將虛擬地址映射到物理地址,然后對其進行修補。 有趣的是,這是我在碩士論文中介紹的內容 。 以下圖像和文本是從該文檔中提取的:
請注意,我的原始項目背后的概念是在假定無法修改編譯過程的情況下,用其他二進制代碼重寫代碼。 如果您的要求和假設不同,那么這可能不是最簡單,最好的方法。
這里最重要的想法是,磁盤表示中的一部分映射到內存時會保留(而不是拆分)。 這意味着在磁盤表示形式的部分中具有一定偏移量的數據在加載到內存后將偏移相同的數量。
在libelf
,類似於libbfd
,可執行文件包含一組節,代碼和數據都可以駐留在其中。 當操作系統將可執行文件加載到內存中時,每個部分都基於某個基址。 我們可以將其反轉以將虛擬內存地址映射到物理文件偏移。 如果可以找到物理文件偏移,則可以將字節修補為常規文件。
libelf
解析可執行文件的節頭。 這使我們能夠獲得一組節,最重要的是,對於libelf
每個節,它可以告訴我們三件事:
(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.