注入部分后出现分段错误

Segmentation Fault after injecting section

我目前正在关注 Dennis Andriesse 的一本关于二进制反汇编和检测的书。

在后面的章节中,我们写了一个ELF类型文件的注入器。 注入器在位置 0x80000 [+offset %16] 放置一个代码段,覆盖 .note.ABI-tag 段(它只包含与执行无关的信息,所以覆盖它是安全的)。

我正在 modding 的程序是一个简单的 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

然后我用我们写的程序注入,除了注入section,我还override了入口点。这是操作前的 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 中的新入口点 header 指向它(用十六进制编辑器仔细检查它)。

所以,现在问题开始了:

如果我尝试执行该程序,它会正确打印出来 "Yikes!You got pranked!\" 紧随其后的是 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,这个应该不会太黑魔法吧...

My calculation of the entry point should also be correct, I guess. And the rip is pointing to the _start function,

不可能 _start 位于 0x1080(至少不在 Linux)。

如果 readelf 告诉你那是 _start 的地址,那么你就有了一个与位置无关的可执行文件。

PIE 可执行文件直到几年前才存在,所以这本书可能没有提到您需要一个非 PIE 可执行文件(因为在编写非 PIE 二进制文件时您可以 build/run).

要构建非 PIE 二进制文件,请使用 g++ -fno-pie -no-pie hello.cc。这样做之后,其余的代码注入应该像书中那样工作。