Ошибка сегментации после инъекции раздела

В настоящее время я слежу за книгой Денниса Андрисса о бинарном дизассемблировании и инструментарии.

В следующей главе мы написали инжектор для файлов типа 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 указывает на это (дважды проверил это с помощью шестнадцатеричного редактора).

Итак, теперь начинаются проблемы:

Если я попытаюсь запустить программу, она правильно распечатает «Ура! Вас разыграли! \», за которым сразу же следует ОШИБКА СЕГМЕНТАЦИИ.

Это сводит меня с ума, так как я возился с этим уже больше недели! К сожалению, 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 в ?? (). Думаю, мой расчет точки входа тоже должен быть правильным. И рип тоже указывает на функцию _start....

Как я могу заставить это снова перейти к правильной точке входа? Я просто хочу написать мод для программы hello world, в этом не должно быть слишком много черной магии...


person clockw0rk    schedule 02.10.2019    source источник
comment
push 0x1080 — 0x1080 кажется смещением от начала изображения в памяти, а не абсолютным виртуальным адресом.   -  person 500 - Internal Server Error    schedule 02.10.2019
comment
Я тоже так думал, на первый взгляд кажется очень низким. Но почему readelf и objdump указывают на это как на точку входа? Я бы не стал предполагать ошибку в readelf, хотя   -  person clockw0rk    schedule 02.10.2019
comment
Утилиты двоичного дампа не могут узнать фактический адрес загрузки модуля во время выполнения, поэтому адреса в дампах обычно являются смещениями от фактического адреса загрузки. Компоновщик создает запись в таблице перемещений для адресов, что позволяет загрузчику исправить адрес при загрузке модуля.   -  person 500 - Internal Server Error    schedule 02.10.2019
comment
Да, оф. Но: не должен ли интерпретатор правильно переводить ссылку на кодовую инструкцию? Я имею в виду, что это информация для указателя инструкций, верно? Может быть, я ошибся, поэтому, пожалуйста, поправьте меня, но вызовы и переходы обычно указывают на адреса в файле, а не в памяти, не так ли?   -  person clockw0rk    schedule 02.10.2019
comment
@ clockw0rk, откуда интерпретатору знать, что это push 0x1080 для точки входа или вообще для любого адреса?   -  person Paweł Łukasik    schedule 02.10.2019
comment
@ 500-InternalServerError: в исполняемом файле, отличном от PIE, заголовки ELF do указывают адрес загрузки во время выполнения. Это то, что делает позицию исполняемых файлов зависимой. См. также 32-битные абсолютные адреса больше не разрешены в x86-64 Linux?. Но да, в PIE базой изображения является 0 при дизассемблировании, если вы не используете параметры objdump для его установки.   -  person Peter Cordes    schedule 03.10.2019


Ответы (1)


Думаю, мой расчет точки входа тоже должен быть правильным. А рип указывает на функцию _start,

никоим образом _start не находится в 0x1080 (по крайней мере, не в Linux).

Если readelf говорит вам, что это адрес _start, то у вас есть исполняемый файл, не зависящий от позиции.

Исполняемые файлы PIE не существовали до нескольких лет назад, поэтому в книге, вероятно, не упоминается, что вам нужен не-PIE (поскольку на момент написания двоичные файлы, отличные от PIE, были всем, что вы могли собрать/запустить).

Чтобы создать двоичный файл, отличный от PIE, используйте g++ -fno-pie -no-pie hello.cc. После этого остальная часть внедрения кода должна работать, как в книге.

person Employed Russian    schedule 03.10.2019
comment
Это важная информация! Я попробую это как можно скорее и сообщу. Не могли бы вы рассказать нам, почему возникла необходимость в позиционно-независимых исполняемых файлах... - person clockw0rk; 04.10.2019
comment
зачем нужна... -- для предотвращения атак по известным адресам, т.е. именно той атаки, которую вы пытаетесь предпринять. - person Employed Russian; 04.10.2019
comment
Это сработало! Я был так расстроен. Большое спасибо, господин нанятый русский язык! Не могли бы вы, случайно, связать что-нибудь, что объясняет, как рассчитать точку входа для пирогов? - person clockw0rk; 06.10.2019