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