我目前正在关注Dennis Andriesse撰写的关于二进制分解和检测的书。
在下一章中,我们为ELF类型文件编写了注射器。 注入程序将代码段放置在位置0x80000 [+ offset%16]处,覆盖.note.ABI-tag部分(该段仅包含与执行无关的信息,因此可以安全地覆盖它)。
我要修改的程序是一个简单的hello_username程序,如下所示:
list comprehension
我在此处放置的代码如下:
#include <iostream>
using namespace std;
main()
{
cout << "hello Lucky" << endl;
return 0;
}
尽管我相信我们不需要推送rcx和r11,但是代码很简单明了, 我用
编译 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
在那之后,我将其与我们编写的程序一起注入,除了注入该部分之外,我还覆盖了入口点。这是操作前的readelf输出:
nasm -f bin -o pranked.bin hi_there.asm
您可以看到入口点是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: 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
[ .... ]
您可以看到从0x80370开始的新部分在那里,并且ELF标头中的新入口点指向该部分(使用十六进制编辑器对其进行了仔细检查)。
现在,问题开始了:
如果我尝试执行该程序,它将正确打印出来 “ Yikes!您被恶作剧!\”紧接着是SEGMENTATION FAULT。
这让我发疯了,因为我现在已经纠缠了一个多星期! 不幸的是,gdb在这里没有太大帮助,但至少我可以在SIGSEV之前抢到堆栈框架:
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
那就是我被困的地方。为什么说1080英寸? ()。 我猜我对入口点的计算也应该是正确的。 裂痕也指向_start函数。...
我如何才能再次跳转到正确的入口点? 我只想为世界打招呼程序编写一个mod,这不应该太多黑魔法...
答案 0 :(得分:3)
我猜我对入口点的计算也应该是正确的。关键是指向_start函数,
不可能将_start
位于0x1080
(至少在Linux上不是)。
如果readelf
告诉您那是_start
的地址,则您有一个与位置无关的可执行文件。
PIE可执行文件直到几年前才出现,因此本书可能没有提到您需要一个非PIE的文件(因为在编写非PIE二进制文件时,您就可以构建/运行所有二进制文件)。
要构建非PIE二进制文件,请使用g++ -fno-pie -no-pie hello.cc
。完成此操作后,其余的代码注入应该像本书中那样工作。