抵消全球可执行常数变量 [英] Offset of global const variable in executable

查看:164
本文介绍了抵消全球可执行常数变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想有一个可执行通过修改其自身的全局常量保存其状态。只是有一个完全独立的可执行文件的踢腿。

一个几个解决方案/黑客浮现在脑海中:


  1. 使用libelf函数并有计划解析自己找到的偏移量。

  2. 添加特定的标记,只搜索它的可执行文件。我想这甚至可能是有些跨平台?

  3. 使用对象倾销utils的确定可执行文件的地址。这可能需要一个后处理项目建设始终做到..

这将是整齐有连接器提供了这个信息。

是否有可能具有接头提供在可执行文件中的偏移量的只读部分的

感谢


解决方案

您基本上谈论的二进制重写。实现这种无需与编译过程摆弄一种方法是将虚拟地址映射到物理之一,然后打补丁。有趣的是,这是我盖在我的硕士论文。下面的图片和文字是从文档拉:

请注意,我的背后原来的项目概念是重写code在其他二进制文件假定编译过程不能修改。如果你的要求和假设是不同的,这很可能不是最简单有效的方法。

这里最重要的想法是,在磁盘重新presentation节是当它被映射到内存preserved(不分割)。这意味着它们是在一定的数据偏移量在盘重新presentation的部分将通过加载到存储器之后的相同量被抵消。

libelf函数,类似于 libbfd ,一个可执行文件包含了一组部分,其中两个code和数据可以驻留。当操作系统加载可执行文件到内存中,每个部分是基于一些基址。我们可以扭转这一到虚拟内存地址映射到物理文件偏移。如果能够找到一个物理文件偏移量,字节可以修补作为常规的文件。


  • 首先,可执行文件的节头解析与 libelf函数
    这使我们能够最重要的是获得一组切片,并且对于
    每节 libelf函数可以告诉我们三件事:

    1. 栏目大小部分的大​​小。

    2. 栏目基址,该部分将根据当磁盘上的可执行文件加载到内存中的地址。

    3. 磁盘部分抵消磁盘部分偏移。


  • 将通过在previous步骤中提取的部分信息迭代,就可以找出任意虚拟内存地址包含在哪个部门。在打补丁,内存地址,我们感兴趣的是的地址code。在这绕道将被写入。在虚拟内存地址到部分偏移可以通过来计算。(virtual_memory_address - section_base_address)

  • 因此,磁盘的虚拟内存地址偏移可以通过来计算(section_disk_offset +(virtual_memory_address - section_base_address))。

这个过程允许的任意的虚拟存储器地址映射到其相应的磁盘文件偏移量。此偏移,然后可以用常规的C文件IO功能,如的fopen / fseek的 / 修补FWRITE / FCLOSE

这是我的code的映射虚拟地址到物理文件中使用上述步骤偏移:

  / *
 *返回相应的32位可执行文件的虚拟内存的偏移
 * 地址。
 * /
uint32_t的vaddr32_to_file_offset(字符*文件路径,uint32_t的VADDR)
{
    INT FD =打开(文件路径,O_RDONLY);
    精灵* E = elf_begin(FD,ELF_C_READ,NULL);
    uint32_t的偏移= 0;    Elf_Scn * SCN = NULL;
    而((SCN = elf_nextscn(E,SCN))!= NULL){
        *个Elf32_Shdr SHDR = elf32_getshdr(SCN);
        如果(VADDR> = shdr-> sh_addr和放大器;&安培;
                (VADDR&下; =(shdr-> sh_addr + shdr-> sh_size))){
            偏移量= shdr-> sh_offset +(VADDR - shdr-> sh_addr);
            打破;
        }
    }    elf_end(E);
    关闭(FD);
    返回偏移;
}/ *
 *返回相应的64位可执行文件的虚拟内存的偏移
 * 地址。
 * /
uint64_t中vaddr64_to_file_offset(字符*文件路径,uint64_t中VADDR)
{
    INT FD =打开(文件路径,O_RDONLY);
    精灵* E = elf_begin(FD,ELF_C_READ,NULL);
    uint64_t中偏移= 0;    Elf_Scn * SCN = NULL;
    而((SCN = elf_nextscn(E,SCN))!= NULL){
        Elf64_Shdr * SHDR = elf64_getshdr(SCN);
        如果(VADDR> = shdr-> sh_addr和放大器;&安培;
                (VADDR&下; =(shdr-> sh_addr + shdr-> sh_size))){
            偏移量= shdr-> sh_offset +(VADDR - shdr-> sh_addr);
            打破;
        }
    }    elf_end(E);
    关闭(FD);
    返回偏移;
}

这是code修补给ELF可执行文件偏移:

  / *
 *设定在任意一个文件的偏移缓冲器的内容的字节。
 * /
静态布尔patch_file(字符*文件路径,uint64_t中抵消,无效*缓冲区,
        为size_t大小)
{
    FILE * PFILE = FOPEN(文件路径,R +);    如果(PFILE == NULL){
        返回FALSE;
    }    fseek的(PFILE,偏移,SEEK_SET);
    的fwrite(缓冲液,1,大小,PFILE);
    FCLOSE(PFILE);
    返回TRUE;
}

更详细的信息可以在报表本身,这是公开的发现的这里

Would like to have an executable save its state by modifying its own global constants. Just for the kicks of having a totally self-contained executable.

A few solutions/hacks that come to mind:

  1. Use libelf and have the program parse itself to find the offset.
  2. Add a specific marker and just search for it in the executable file. I guess this might even be somewhat cross-platform?
  3. Use object dumping utils to determine the address in the executable file. This probably needs to be always done as a post-process to project build..

It would be neat to have the linker provide this info.

Is it possible to have the linker provide the offset of a read-only section in the executable file?

Thanks

解决方案

You are essentially talking about binary rewriting. One method to achieve this without fiddling with the compile process is to map a virtual address to a physical one and then patch it. Interestingly, this is something I covered in my master's thesis. The following images and text are pulled from that document:

Note that the concept behind my original project was to rewrite code in other binaries assuming that the compile process could not be modified. If your requirements and assumptions are different, this may well not be the easiest and best approach.

The most important idea here is that a section in the disk representation is preserved (not split) when it is mapped into memory. This means data that is at a certain offset into the section in the disk representation will be offset by the same amount after loaded into memory.

In libelf, similarly to libbfd, an executable contains a set of sections in which both code and data can reside. When the operating system loads the executable into memory, each section is based at some base address. We can reverse this to map a virtual memory address to a physical file offset. If a physical file offset can be found, the bytes can be patched as a regular file.

  • First, the section header of the executable is parsed with libelf. This allows us to obtain a set of sections and most importantly, for each section libelf can tell us three things:

    1. Section size The size of the section.
    2. Section base address The address that the section will be based at when the executable on disk is loaded into memory.
    3. Section disk offset The disk offset of the section.

  • By iterating through the section information extracted in the previous step, it is possible to find out what section an arbitrary virtual memory address is contained in. During patching, the memory address we are interested in is the address of the code at which a detour is to be written. The offset of the virtual memory address into the section can be calculated by (virtual_memory_address - section_base_address).
  • Therefore, the disk offset of the virtual memory address can be calculated by (section_disk_offset + (virtual_memory_address - section_base_address)).

This process allows an arbitrary virtual memory address to be mapped to its corresponding disk file offset. This offset can then be patched with regular C file IO functions such as fopen/fseek/fwrite/fclose.

This is my code for mapping a virtual address to a physical file offset using the above steps:

/*
 * 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;
}

This is the code to patch an ELF executable given an offset:

/*
 * 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;
}

More detailed information can be found in the report itself which is publicly available here.

这篇关于抵消全球可执行常数变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆