我目前正在研究一个 ELF 注入器,我的方法是标准的:找到代码洞(足够长的 0 序列),用我想要执行的指令重写它,然后跳回原始程序的开始执行它作为它通常会。
为了在代码洞中实际执行代码,我尝试了两种不同的方法,这两种方法都会产生 sigsegv。
第一个是将入口点更改为代码洞的开始。第二个是从原始代码中“窃取”一些第一条指令并将跳转写入我的代码洞穴,然后在执行我注入的代码后,我将首先执行被盗指令,然后跳转到最后一个被盗指令之后的指令原始程序。
我还在更改代码洞所在部分的访问标志。
下面是在gdb中调试程序的一些截图:
以下是代码洞所在部分的标志:
[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
由我们的受害者二进制文件链接的函数或其他一些库函数?因为似乎从代码中获取实际指令,我们想注入,有点麻烦。