抵消可执行全球常量变量(Offset of global const variable in exe

2019-07-31 19:42发布

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

一些解决方案/浮现在脑海黑客:

  1. 使用了libelf并有计划解析本身找到抵消。
  2. 添加特定的标记,只是寻找它的可执行文件。 我想这甚至有可能有些跨平台?
  3. 使用对象倾销utils的确定可执行文件的地址。 这可能需要一个后处理项目建设要始终做到..

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

是否有可能有连接器提供可执行文件只读段的偏移?

谢谢

Answer 1:

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

需要注意的是背着我最初的项目设计理念是要重写其他二进制文件假定编译过程无法修改代码。 如果您的要求和假设是不同的,这很可能不是最简单有效的方法。

这里最重要的想法是,在磁盘上的保存一个部分被保留(不分裂),当它被映射到内存中。 这意味着数据是在一定的偏移量,在盘表示的部分将通过加载到存储器中之后的相同的量来抵消。

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

更详细的信息可以在报告本身,这是公开的可以找到这里 。



Answer 2:

什么,你想要做的是棘手的,不可移植。

但是你可以学习的GNU emacs unexec的功能,例如在SRC / unexelf.c (对于Linux,其他操作系统也有类似的文件)。

你也可以发挥连接的招数,例如用自己的ld脚本。

要注意的是,这些技巧可能是处理器,反编译,内核级和libc-特定版本。

也许你想要的应用程序检查点 。 特别是, BLCR可能对你有用。



Answer 3:

这将是不可能的,因为编译器通常如果有可能取代全局和静态常量的立即值机代码。 例如:

const int x=3;

int main()
{
 return x;
}

产生用于主()(OSX / GCC -O3)此代码:

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


文章来源: Offset of global const variable in executable