更改 ELF 可执行文件中的入口点

逆向工程 linux C 数据库 小精灵 海湾合作委员会
2021-07-03 00:30:48

我写了一些代码来执行以下操作:

  1. 在二进制文件中搜索并找到偏移量以添加代码(查找我可以覆盖的 00 序列)。

  2. 然后,我将ELF的入口点更改为上述地址。

  3. 然后,我将代码写入该地址并jmp返回到原始入口点。

因此,当二进制文件启动时,它将在我将入口点更改为的地址处执行代码,我可以看到我的代码,gdb但除了nop会触发段错误之外的任何类型的指令

谁能帮我理解为什么会这样?

为了记录,在我使用的二进制文件中,我写代码的地方是我发现一堆00s的地方。它在.eh_frame分区中。

我将可执行标志添加到该部分以防万一这是原因,但它没有帮助。

我正在尝试向用 C 编写的“hello world”程序添加代码。下一节中的偏移量位于将代码写入的位置:

在任何修改之前,二进制文件具有以下内容:

Section: [17] .eh_frame
Addr: 00000000004005d0 Offset: 0005d0 Size: 0000f4 Flags: A
Entry point address:               0x400400

我的代码运行后:

Section: [17] .eh_frame
Addr: 00000000004005d0 Offset: 0005d0 Size: 0000f4 Flags: A**WX**
Entry point address:               **0x4006b6**

GDB内部:

 Breakpoint 1, 0x00000000004006b6 in ?? ()
(gdb) x/10i $rip
=> 0x4006b6:    nop
   0x4006b7:    nop
   0x4006b8:    nop
   0x4006b9:    add    0x1,%ebx

当我遇到add 0x1, %ebx程序段错误时。** 我也试过一个add 0x1, %rbx.

编辑

我很确定我发现了这个问题。

在编写汇编代码时,我使用 GCC,然后使用 eXamine 命令获取每条指令的字节,但我正在做这样的事情:

__asm__("movl 0x00, %rax") (trying to access 0x00 location..)

而不是__asm__("movl $0x00, %rax")(根据需要将0常量移入寄存器)。

所以,我是个白痴,但至少我自己意识到了 ;))

感谢所有看过并提供帮助的人。

2个回答

.eh_frame部分不是代码部分。因此,它不是一个可执行部分,分配给它的内存是不可执行的内存。您可以读取它,但 CPU 不会执行它。

一种解决方案是更改 ELF 标头中该部分的权限(在部分列表中)。另一种方法是在可执行部分中找到另一个代码洞穴(这就是您可以添加代码的区域通常被称为的方式)。如果找不到合适的代码洞,也可以创建自己的部分。

PS 充满空值的区域不一定是您可以编辑的区域,这些空字节可能仍会被使用或解析或其他任何内容。

.eh_header可能无法执行,但编辑节表无济于事。您需要编辑该段对应的段(程序头)。您可以使用例如readelf -a

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000230 0x0000000000000230  R E    8
  INTERP         0x0000000000000270 0x0000000000000270 0x0000000000000270
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000020a28 0x0000000000020a28  R E    200000
  LOAD           0x0000000000020ab8 0x0000000000220ab8 0x0000000000220ab8
                 0x0000000000002e10 0x0000000000a53d58  RW     200000
  DYNAMIC        0x0000000000020c30 0x0000000000220c30 0x0000000000220c30
                 0x0000000000000250 0x0000000000000250  RW     8
  NOTE           0x000000000000028c 0x000000000000028c 0x000000000000028c
                 0x0000000000000020 0x0000000000000020  R      4
  TLS            0x0000000000020ab8 0x0000000000220ab8 0x0000000000220ab8
                 0x0000000000000000 0x0000000000000040  R      8
  GNU_EH_FRAME   0x000000000001c3d0 0x000000000001c3d0 0x000000000001c3d0
                 0x0000000000000ae4 0x0000000000000ae4  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x0000000000020ab8 0x0000000000220ab8 0x0000000000220ab8
                 0x0000000000000548 0x0000000000000548  R      1
Section to Segment mapping:
Segment Sections...
00     
01     .interp 
02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .gcc_except_table 
03     .preinit_array .init_array .fini_array .jcr .data.rel.ro .dynamic .got .data .bss 
04     .dynamic 
05     .note.ABI-tag 
06     .tbss 
07     .eh_frame_hdr 
08
09     .preinit_array .init_array .fini_array .jcr .data.rel.ro .dynamic .got 

在这个例子中,.eh_frame实际上已经在可执行段中(第一个 LOAD 条目,RE),但是如果你打补丁,例如.data.rel.ro,你需要编辑条目 #3(第二个 LOAD 条目,RW-)。

请注意,OS加载程序只使用LOAD程序头映射文件,所以编辑其它条目(例如GNU_EH_FRAME)是没有必要的,即使它们覆盖你感兴趣的部分。