ELF二进制注入

逆向工程 linux C 数据库 小精灵 注射
2021-06-29 04:04:24

我目前正在研究一个 ELF 注入器,我的方法是标准的:找到代码洞(足够长的 0 序列),用我想要执行的指令重写它,然后跳回原始程序的开始执行它作为它通常会。

为了在代码洞中实际执行代码,我尝试了两种不同的方法,这两种方法都会产生 sigsegv。

第一个是将入口点更改为代码洞的开始。第二个是从原始代码中“窃取”一些第一条指令并将跳转写入我的代码洞穴,然后在执行我注入的代码后,我将首先执行被盗指令,然后跳转到最后一个被盗指令之后的指令原始程序。

我还在更改代码洞所在部分的访问标志。

下面是在gdb中调试程序的一些截图:

入口点说明——0x555555556156为code Cave地址

代码洞中的指令——执行偷来的并跳回去

执行代码

以下是代码洞所在部分的标志:

[19] 0x555555556058->0x555555556160 at 0x00002058: .eh_frame ALLOC LOAD READONLY CODE HAS_CONTENTS

这是 Valgrind 的输出。

在此处输入图片说明

那么有没有办法真正允许执行本节中的代码。

我还考虑向二进制文件添加新部分并在那里编写我的代码。如果有人有这样做的经验,我将不胜感激。

编辑:我想我知道我的错误是什么 - 我为该部分设置了可执行标志,但它所在的段不是可执行的。但我发现的代码洞似乎也不属于任何部分,因为代码洞的开始实际上是一个部分的结束,而代码洞的结束是另一部分的开始。并且中间没有其他部分。

编辑 2:我将代码洞穴更改为.fini属于可执行段的部分。但是,我仍然对部分(和段)之间的空白空间感到困惑。

这是readelf输出的屏幕截图

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.propert NOTE             0000000000000338  00000338
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.build-i NOTE             0000000000000358  00000358
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003c8  000003c8
       00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000470  00000470
       0000000000000082  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           00000000000004f2  000004f2
       000000000000000e  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000500  00000500
       0000000000000020  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000520  00000520
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000005e0  000005e0
       0000000000000018  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000020  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001040  00001040
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001050  00001050
       0000000000000010  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001060  00001060
       0000000000000185  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000011e8  000011e8
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000012  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002014  00002014
       0000000000000044  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002058  00002058
       0000000000000108  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003db8  00002db8
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003dc8  00002dc8
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb8  00002fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004010  00003010
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003010
       000000000000002a  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040
       0000000000000618  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00003658
       0000000000000202  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  0000385a
       000000000000011a  0000000000000000           0     0     1


Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R E    0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R E    0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000005f8 0x00000000000005f8  R E    0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000001f5 0x00000000000001f5  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000160 0x0000000000000160  R E    0x1000
  LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000258 0x0000000000000260  RWE    0x1000
  DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8
                 0x00000000000001f0 0x00000000000001f0  RWE    0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R E    0x8
  NOTE           0x0000000000000358 0x0000000000000358 0x0000000000000358
                 0x0000000000000044 0x0000000000000044  R E    0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R E    0x8
  GNU_EH_FRAME   0x0000000000002014 0x0000000000002014 0x0000000000002014
                 0x0000000000000044 0x0000000000000044  R E    0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    0x10
  GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000248 0x0000000000000248  R E    0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   03     .init .plt .plt.got .plt.sec .text .fini 
   04     .rodata .eh_frame_hdr .eh_frame 
   05     .init_array .fini_array .dynamic .got .data .bss 
   06     .dynamic 
   07     .note.gnu.property 
   08     .note.gnu.build-id .note.ABI-tag 
   09     .note.gnu.property 
   10     .eh_frame_hdr 
   11     
   12     .init_array .fini_array .dynamic .got 


可以看出,.fini 节从 .fini 开始,11e8大小为d. 下一部分 - .rodata 从2000. 这是否意味着11e8 + d之间的空间2000不属于任何部分?该段也结束于11F5,这是属于它的最后一段的结束 - .fini

编辑 3:设法解决了问题 - 必须选择属于可执行段的部分。对节的大小仍然有点困惑。

实际上设法将代码注入二进制文件,但是,我从程序集中获得了指令,其中只有.text部分:

.text
.globl _start
_start:
  #save the base pointer
  pushq %rbp
  pushq %rbx
  mov %rsp,%rbp

  #write syscall = 1
  movq $1, %rax
  #print to stdout
  movq $1, %rdi
  #9 character long string
  movq $9, %rdx

  # push "INJECTED\n" to the stack  
  movq $0x0a, %rcx
  pushq %rcx
  movq $0x44455443454a4e49, %rcx
  pushq %rcx

  movq %rsp, %rsi

  syscall

  #remove the string
  pop %rcx
  pop %rcx

  movq $0, %rax
  movq $0, %rdi
  movq $0, %rdx

  
  pop %rbx
  pop %rbp
  ret

它在原始程序执行之前打印“INJECTED”。虽然这种有效载荷有效,但实现更好且实际可用的注入器的其他想法是什么?也许可以调用libc由我们的受害者二进制文件链接的函数或其他一些库函数?因为似乎从代码中获取实际指令,我们想注入,有点麻烦。

1个回答

在执行 ELF 文件时,OS 加载程序不关心节,而只关心段(又名程序头)。您需要确保您的代码属于一个可执行段。

可以看出,.fini 部分从 11e8 开始,大小为 d。下一部分 - .rodata 从 2000 开始。这是否意味着 11e8 + d 和 2000 之间的空间不属于任何部分?该段也在 11F5 处结束,这是属于它的最后一段的结尾 - .fini。

是的,11F5和2000之间的空间是一种“无人区”。理论上,那里的字节不会出现在内存中。然而,在实践中,内存保护和映射在页面粒度 (0x1000) 上工作,因此这些字节映射到内存中并使其可执行,以便您的补丁工作。为了使所有内容“合法”,最好将段长度扩展到 2000。