Ассемблер x86 Linux получает параметры программы из _start

Я пытаюсь создать программу, чтобы просто написать параметр на экране. Я создал несколько программ, чтобы получить параметр функции C, или я использовал C, чтобы отправить параметр в мою программу asm. Есть ли способ получить параметр программы, используя только ассемблер

EX:

./Program "text"

Я использую как (Gnu Assembler)

Обычно я получаю эти параметры, используя

[esp+4]

Потому что esp - это указатель вызова программы/функции, но в чистом ассемблере он не получает параметр командной строки.

Есть ли способ сделать это?

Я гуглил, но не нашел много информации


person Lefsler    schedule 23.05.2013    source источник


Ответы (1)


В Linux знакомые переменные argc и argv из C всегда передаются в стеке ядром, доступным даже для программ сборки, которые полностью автономны и не связаны с кодом запуска в библиотеке C. Это задокументировано в i386 System V ABI, наряду с другими сведения о среде запуска процесса (значения регистров, выравнивание стека).

В точке входа ELF (также известной как _start) исполняемого файла x86 Linux:

  1. ESP указывает на argc
  2. ESP + 4 указывает на argv[0], начало массива. то есть значение, которое вы должны передать в main, поскольку char **argv равно lea eax, [esp+4], а не mov eax, [esp+4])

Как программа на минимальном ассемблере получает argc и argv

Я покажу, как читать argv и argc[0] в GDB.

cmdline-x86.S

#include <sys/syscall.h>

    .global _start
_start:
    /* Cause a breakpoint trap */
    int $0x03

    /* exit_group(0) */
    mov $SYS_exit_group, %eax
    mov $0, %ebx
    int $0x80

cmdline-x86.gdb

set confirm off
file cmdline-x86
run
# We'll regain control here after the breakpoint trap
printf "argc: %d\n", *(int*)$esp
printf "argv[0]: %s\n",  ((char**)($esp + 4))[0]
quit

Пример сеанса

$ cc -nostdlib -g3 -m32 cmdline-x86.S -o cmdline-x86
$ gdb -q -x cmdline-x86.gdb cmdline-x86
<...>  
Program received signal SIGTRAP, Trace/breakpoint trap.
_start () at cmdline-x86.S:8
8   mov $SYS_exit_group, %eax
argc: 1
argv[0]: /home/scottt/Dropbox/stackoverflow/cmdline-x86

Объяснение

  • Я установил программную точку останова (int $0x03), чтобы программа возвращалась в отладчик сразу после точки входа ELF (_start).
  • I then used printf in the GDB script to print
    1. argc with the expression *(int*)$esp
    2. argv с выражением ((char**)($esp + 4))[0]

версия x86-64

Отличия минимальны:

  • Замените ESP на RSP.
  • Изменить размер адреса с 4 на 8
  • Соответствовать различным соглашениям о вызовах системных вызовов Linux, когда мы вызываем exit_group(0) для правильного завершения процесса.

cmdline.S

#include <sys/syscall.h>

    .global _start
_start:
    /* Cause a breakpoint trap */
    int $0x03

    /* exit_group(0) */
    mov $SYS_exit_group, %rax
    mov $0, %rdi
    syscall

cmdline.gdb

set confirm off
file cmdline
run
printf "argc: %d\n", *(int*)$rsp
printf "argv[0]: %s\n",  ((char**)($rsp + 8))[0]
quit

Как обычные программы на C получают argc и argv

Вы можете разобрать _start из обычной программы на C, чтобы увидеть, как она получает argc и argv из стека и передает их при вызове __libc_start_main. Используя программу /bin/true на моей машине x86-64 в качестве примера:

$ gdb -q /bin/true
Reading symbols from /usr/bin/true...Reading symbols from /usr/lib/debug/usr/bin/true.debug...done.
done.
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x0000000000401580 <+0>: xor    %ebp,%ebp
   0x0000000000401582 <+2>: mov    %rdx,%r9
   0x0000000000401585 <+5>: pop    %rsi
   0x0000000000401586 <+6>: mov    %rsp,%rdx
   0x0000000000401589 <+9>: and    $0xfffffffffffffff0,%rsp
   0x000000000040158d <+13>:    push   %rax
   0x000000000040158e <+14>:    push   %rsp
   0x000000000040158f <+15>:    mov    $0x404040,%r8
   0x0000000000401596 <+22>:    mov    $0x403fb0,%rcx
   0x000000000040159d <+29>:    mov    $0x4014c0,%rdi
   0x00000000004015a4 <+36>:    callq  0x401310 <__libc_start_main@plt>
   0x00000000004015a9 <+41>:    hlt    
   0x00000000004015aa <+42>:    xchg   %ax,%ax
   0x00000000004015ac <+44>:    nopl   0x0(%rax)

Первые три аргумента __libc_start_main():

  1. RDI: указатель на main()
  2. RSI: argc, вы можете видеть, что это первое, что выскочило из стека
  3. RDX: argv, значение RSP сразу после извлечения argc. (ubp_av в источнике GLIBC)

_start для x86 очень похож:

Dump of assembler code for function _start:
   0x0804842c <+0>: xor    %ebp,%ebp
   0x0804842e <+2>: pop    %esi
   0x0804842f <+3>: mov    %esp,%ecx
   0x08048431 <+5>: and    $0xfffffff0,%esp
   0x08048434 <+8>: push   %eax
   0x08048435 <+9>: push   %esp
   0x08048436 <+10>:    push   %edx
   0x08048437 <+11>:    push   $0x80485e0
   0x0804843c <+16>:    push   $0x8048570
   0x08048441 <+21>:    push   %ecx
   0x08048442 <+22>:    push   %esi
   0x08048443 <+23>:    push   $0x80483d0
   0x08048448 <+28>:    call   0x80483b0 <__libc_start_main@plt>
   0x0804844d <+33>:    hlt    
   0x0804844e <+34>:    xchg   %ax,%ax
End of assembler dump.
person scottt    schedule 23.05.2013
comment
Один вопрос: Как сделать dword [ebp+4*ebx] в газе? - person Lefsler; 24.05.2013
comment
(%ebp, %ebx, 4) на самом деле. Но вы всегда можете просто ассемблировать с помощью nasm -felf32 и дизассемблировать с помощью objdump -d, чтобы увидеть, как что-то написано в синтаксисе AT&T. - person Peter Cordes; 09.10.2016
comment
Также хорошая идея: ссылка на стандарт ABI, который определяет начальную среду процесса (т.е. что находится в регистрах и памяти до запуска первой инструкции _start). github.com/hjl-tools/x86-psABI/wiki/X86- psABI в настоящее время ссылается на версию 252. x86-64 SystemV ABI. - person Peter Cordes; 09.10.2016