我目前正在关注 Dennis Andriesse 的一本关于二进制反汇编和检测的书。
在后面的章节中,我们为 ELF 类型的文件编写了一个注入器。注入器将代码段放置在位置 0x80000 [+offset %16] 处,覆盖 .note.ABI-tag 段(其中仅包含与执行无关的信息,因此覆盖它是安全的)。
我正在修改的程序是一个简单的 hello_username 程序,如下所示:
#include <iostream>
using namespace std;
main()
{
cout << "hello Lucky" << endl;
return 0;
}
我放在那里的代码如下所示:
BITS 64
section .text
global main
main:
push rax
push rcx
push rdx
push rsi
push rdi
push r11
mov rax, 0x1 ;syscall to print (sys_write)
mov rdi, 0x1 ;stdout
lea rsi, [rel $+rankle-$] ;offset to prank
mov rdx, [rel $+size-$] ;length of prank
syscall
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
push 0x1080 ;find "entry_point" using readelf program
ret
rankle : db "Yikes! U got pranked!", 0
size : dd 22
虽然我相信我们不需要推送 rcx 和 r11,代码简单直接,我编译它
nasm -f bin -o pranked.bin hi_there.asm
之后,我将它注入我们编写的程序中,除了注入该部分之外,我还覆盖了入口点。这是操作前的readelf输出:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1080
Start of program headers: 64 (bytes into file)
Start of section headers: 15344 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 11
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
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 00000000000002a8 000002a8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.build-i NOTE 00000000000002c4 000002c4
0000000000000024 0000000000000000 A 0 0 4
[ 3] .note.ABI-tag NOTE 00000000000002e8 000002e8
0000000000000020 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 00000308
0000000000000028 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000330 00000330
0000000000000138 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000468 00000468
0000000000000163 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000005cc 000005cc
000000000000001a 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000005e8 000005e8
0000000000000040 0000000000000000 A 6 2 8
[ 9] .rela.dyn RELA 0000000000000628 00000628
0000000000000120 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000000748 00000748
0000000000000060 0000000000000018 AI 5 23 8
[11] .init PROGBITS 0000000000001000 00001000
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000001020 00001020
0000000000000050 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 0000000000001070 00001070
0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 0000000000001080 00001080
00000000000001e1 0000000000000000 AX 0 0 16
[ .... ]
您可以看到入口点是 0x1080,这是 .text 部分的开始位置。(_start函数在这个地方,用objdump查了一下)
现在,在注入发生后,同样的输出:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x80370
Start of program headers: 64 (bytes into file)
Start of section headers: 15344 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 11
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
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 00000000000002a8 000002a8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.build-i NOTE 00000000000002c4 000002c4
0000000000000024 0000000000000000 A 0 0 4
[ 3] .init PROGBITS 0000000000001000 00001000
0000000000000017 0000000000000000 AX 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 00000308
0000000000000028 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000330 00000330
0000000000000138 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000468 00000468
0000000000000163 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000005cc 000005cc
000000000000001a 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000005e8 000005e8
0000000000000040 0000000000000000 A 6 2 8
[ 9] .rela.dyn RELA 0000000000000628 00000628
0000000000000120 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000000748 00000748
0000000000000060 0000000000000018 AI 5 23 8
[11] .plt PROGBITS 0000000000001020 00001020
0000000000000050 0000000000000010 AX 0 0 16
[12] .plt.got PROGBITS 0000000000001070 00001070
0000000000000008 0000000000000008 AX 0 0 8
[13] .text PROGBITS 0000000000001080 00001080
00000000000001e1 0000000000000000 AX 0 0 16
[...]
[25] .bss NOBITS 0000000000004060 00003048
0000000000000118 0000000000000000 WA 0 0 32
[26] .pranked PROGBITS 0000000000080370 00004370
000000000000004a 0000000000000000 AX 0 0 16
[27] .symtab SYMTAB 0000000000000000 00003070
00000000000006f0 0000000000000018 28 49 8
[28] .strtab STRTAB 0000000000000000 00003760
0000000000000382 0000000000000000 0 0 1
[29] .shstrtab STRTAB 0000000000000000 00003ae2
0000000000000107 0000000000000000 0 0 1
您可以看到新部分在那里,从 0x80370 开始,并且 ELF 标头中的新入口点指向该部分(使用十六进制编辑器仔细检查)。
所以,现在问题开始了:
如果我尝试执行该程序,它会正确打印出“Yikes!你被恶作剧了!\”紧随其后的是 SEGMENTATION FAULT。
这让我发疯了,因为我已经玩了一个多星期了!不幸的是,gdb 在这里没有多大帮助,但至少我可以在 SIGSEV 之前获取堆栈帧:
#0 0x0000000000001080 in ?? ()
(gdb) info f
Stack level 0, frame at 0x7fffb20db438:
rip = 0x1080; saved rip = 0x1
called by frame at 0x7fffb20db440
Arglist at 0x7fffb20db428, args:
Locals at 0x7fffb20db428, Previous frame's sp is 0x7fffb20db438
Saved registers:
rip at 0x7fffb20db430
这就是我卡住的地方。为什么说是1080??()。我猜我对切入点的计算也应该是正确的。rip 也指向 _start 函数....
我怎样才能让它再次跳转到正确的入口点?我只是想为一个hello world程序写一个mod,这应该不是太多的黑魔法......