Почему ошибка сегмента при записи в записываемый раздел .data? Использование Ubuntu, x86, nasm, gdb, readelf - PullRequest
1 голос
/ 10 июля 2020

Я учусь писать простой шелл-код на ассемблере. Я получаю ошибку сегмента, когда код операции mov выполняет запись поверх данных db. Зачем? Любое руководство приветствуется! Отладка с помощью gdb подтверждает, что данные смежны с кодом во время выполнения, а повторный анализ программы подтверждает, что сегмент данных доступен для записи.

    section .text
    global _start
        _start:

          ; The following code calls execve("/bin/sh", argv, envp=0)
          jmp short two
    one:
          pop ebx
          xor eax, eax
          mov [ebx+12], eax
          mov [ebx+7], al
          mov [ebx+8], ebx
          lea ecx, [ebx+8]
          lea edx, [ebx+12]
          mov al, 11
          int 0x80
    two:
          call one
section .data align=1
          db '/bin/shzargvenvp'

Дополнительная информация после чтения комментариев:

It Segment сбои при автономном запуске в командной строке linux (./myshdb), а также когда я перехожу в инструкцию mov с помощью gdb (устанавливаю break на «один», выполняю, затем повторяю шаг).

Да, компиляция и запуск на 32-битной установке Ubuntu. Вот различные командные строки, которые я использую (все они отлично работают для варианта кода оболочки):

nasm -f elf32 -g -F stabs myshdb.s -o myshdb.o
objdump -Mintel --disassemble myshdb.o
ld myshdb.o -o myshdb
readelf -a myshdb
gdb myshdb

При использовании разных алгоритмов компиляция и команды работают нормально, и программа работает нормально. У меня проблемы с близостью данных сразу после кода и попытками записи в раздел данных. Первоначально это был весь раздел .text, но он явно предназначен только для чтения, поэтому я подумал, что объявление данных, выровненное по 1-байтовым границам, будет работать. 1-байтовая граница работает, но почему-то запись не работает, хотя readelf говорит, что она загружена с возможностью записи. Обратите внимание на 16 байтов (размер = 0x10) в сегменте данных с флагом «W».

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048080 000080 000025 00  AX  0   0 16
  [ 2] .data             PROGBITS        080490a5 0000a5 000010 00  WA  0   0  1
  [ 3] .stab             PROGBITS        00000000 0000b8 0000d8 0c      4   0  4
  [ 4] .stabstr          STRTAB          00000000 000190 00000a 00      0   0  1
  [ 5] .shstrtab         STRTAB          00000000 00029b 000036 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 00019c 0000d0 10      7   9  4
  [ 7] .strtab           STRTAB          00000000 00026c 00002f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Следуют ли данные сразу за кодом? вывод gdb ниже, остановлен непосредственно перед выполнением первого кода операции mov. Данные отображаются непрерывно после кода. EBX содержит адрес 0x80480a5, который указывает на действительные строковые данные сразу после кода. Проверка памяти (x 0x80480a5) также подтверждает непрерывное расположение.

[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x80480a5 ("/bin/shZargvenvp")
ECX: 0x0 
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x0 
ESP: 0xbfffeda0 --> 0x1 
EIP: 0x804808d (<one+3>:    mov    DWORD PTR [ebx+0xc],eax)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048088 <zero>:    jmp    0x80480a0 <two>
   0x804808a <one>: pop    ebx
   0x804808b <one+1>:   xor    eax,eax
=> 0x804808d <one+3>:   mov    DWORD PTR [ebx+0xc],eax
   0x8048090 <one+6>:   mov    DWORD PTR [ebx+0x8],ebx
   0x8048093 <one+9>:   mov    BYTE PTR [ebx+0x7],al
   0x8048096 <one+12>:  lea    ecx,[ebx+0x8]
   0x8048099 <one+15>:  lea    edx,[ebx+0xc]
   [------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804808d in one ()
gdb-peda$ x 0x80480a5
0x80480a5:  "/bin/shZargvenvp"

@ Работники русского языка запросили распечатку у reaelf -Wl. Вот информация, когда я перестраивал вещи с нуля:

---------- code snippet compiled with nasm, ld -----------------
zero: jmp short two
one:  pop ebx
      xor eax, eax
      mov [ebx+12], eax
      mov [ebx+8], ebx
      mov [ebx+7], al
      lea ecx, [ebx+8]
      lea edx, [ebx+12]
      mov al, 11
      int 0x80
two:  call one
section .data align=1
msg:   db '/bin/sh0argvenvp' 

-------- readelf output as requested --------
readelf -Wl myshdb

Elf file type is EXEC (Executable file)
Entry point 0x8048080
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x0009d 0x0009d R E 0x1000
  LOAD           0x00009d 0x0804909d 0x0804909d 0x00010 0x00010 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text 
   01     .data 

-------------- run with gdb and step to mov instructions ----------
---------------registers--------------
EAX: 0x0 
EBX: 0x804809d ("/bin/sh0argvenvp")

----------- memory address checks ------------
gdb-peda$ p zero
$15 = {<text variable, no debug info>} 0x8048080 <zero>
gdb-peda$ p one
$16 = {<text variable, no debug info>} 0x8048082 <one>
gdb-peda$ p two
$17 = {<text variable, no debug info>} 0x8048098 <two>
gdb-peda$ p $ebx
$18 = 0x804809d
gdb-peda$ p msg
$19 = 0x6e69622f
gdb-peda$ x 0x804809d
0x804809d:  "/bin/sh0argvenvp"
gdb-peda$ x msg
0x6e69622f: <error: Cannot access memory at address 0x6e69622f>

Другими словами, строковое сообщение доступно из области памяти сразу после кода (0x804809d). Тем не менее, метка msg сопоставляется с 0x6e69622f, как я могу увидеть там данные с помощью gdb? Что мне говорит вывод reaelf -Wl?

1 Ответ

3 голосов
/ 11 июля 2020

Отладка с помощью gdb подтверждает, что данные смежны с кодом во время выполнения, а повторный анализ программы подтверждает, что сегмент данных доступен для записи.

Вы ожидаете, что db '...' немедленно следовать за CALL one.

Этого не на самом деле , ваш раздел .data находится в другом сегменте (потому что ему нужны другие разрешения):

readelf -Wl myshdb
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00094 0x00094 R   0x1000
  LOAD           0x001000 0x08049000 0x08049000 0x0001d 0x0001d R E 0x1000
  LOAD           0x002000 0x0804a000 0x0804a000 0x00010 0x00010 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00
   01     .text
   02     .data

Обратите внимание, что .data находится во втором сегменте LOAD, и этот сегмент начинается на другой странице.

Что может сбить вас с толку, так это то, что ваш компоновщик может оставьте копию из .data следующего кода для two (в моей версии нет - это все 0 для меня).

В любом случае, ваш код as is пытается записать в первый LOAD сегмент, в место сразу после конца two, но этот сегмент (явно) не доступен для записи.

...