quinta-feira, 27 de agosto de 2015

Passagem de Parâmetros via linha de comando (asm x86_64).

Usar os parâmetros passados via linha de comando em asm x86_64 é bem simples, quando você inicia o programa os primeiros 8 bytes no rsp contem o numero de argumentos passados via linha de comando, que sempre irá ser no minimo 1, os qword's (Quad Word = 8 Bytes) seguintes são os endereços de memoria onde os argumentos estão armazenados.

O primeiro endereço contem o nome do programa, o restante são os parâmetros passados via linha de comando, depois temos 8 bytes nulos e ai vem variáveis de ambientes e outras coisas.

vamos testar isso usando esse código:

section .text
 global _start
_start:
  nop

esse programa não vai fazer nada, vamos executa-lo usando o gdb, criar um breakpoint ( por isso esse nop), e olhar os valores armazenados no rsp, tudo isso para ver se realmente o que foi escrito acima é verdade.

[mmxm@hc0d3r tmp]$ nasm -f elf64 args_tut.asm 
[mmxm@hc0d3r tmp]$ ld -o args_tut args_tut.o
[mmxm@hc0d3r tmp]$ gdb ./args_tut
GNU gdb (GDB) Fedora 7.8.2-39.fc21
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./args_tut...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400080
(gdb) r
Starting program: /tmp/args_tut 

Breakpoint 1, 0x0000000000400080 in _start ()
(gdb) x/3xg $rsp
0x7fffffffe020: 0x0000000000000001 0x00007fffffffe32e
0x7fffffffe030: 0x0000000000000000
(gdb) x/3xg $rsp

Como executamos o programa sem passar nenhum parametro, os primeiros 8 bytes armazenam o valor 1, agora vamos ver qual é o valor armazenado no endereço 0x00007fffffffe32e

(gdb) x/s 0x00007fffffffe32e
0x7fffffffe32e: "/tmp/args_tut"

como dito anteriormente, e agora comprovado, o valor do primeiro endereço é nome do executável, agora vamos executar o programa novamente, mas dessa vez passando alguns argumentos:

(gdb) r arg1 arg2 arg3
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/args_tut arg1 arg2 arg3

Breakpoint 1, 0x0000000000400080 in _start ()
(gdb) x/6xg $rsp
0x7fffffffdff0: 0x0000000000000004 0x00007fffffffe31f
0x7fffffffe000: 0x00007fffffffe32d 0x00007fffffffe332
0x7fffffffe010: 0x00007fffffffe337 0x0000000000000000

como podem observar, os primeiros 8 bytes tiveram seu valor alterado, agora totalizam o numero 4, porque 3 argumentos foram passados, e mais 3 endereços de memoria surgiram, vamos dar uma olhada nos valores armazenador nesses endereços:

(gdb) x/4s 0x00007fffffffe31f
0x7fffffffe31f: "/tmp/args_tut"
0x7fffffffe32d: "arg1"
0x7fffffffe332: "arg2"
0x7fffffffe337: "arg3"

Os argumentos passados estão la, cada argumento é separado por um byte nulo, agora que já sabemos isso vamos programar :D

Exemplo de como listar os parâmetros passados:

sys_write equ 1
sys_exit equ 60

stdout equ 1

section .data
  break_line db 10

section .text
  global _start
_start:
  pop r10 ; armazena o numero de argumentos


  print_args_loop:
    pop rsi ; armazena o endereço do proximo argumento

    xor rdx, rdx
    xor rax, rax

    len_loop:
      cmp [byte rsi+rdx], al ; checa se encontrou um byte nulo, se sim sai do len_loop
      je exit_x
      inc rdx
      jmp len_loop

    exit_x:

    mov rax, sys_write ; rsi ja esta com o endereço da string, e rdx contem o tamanho da string
    mov rdi, stdout    ; so executar a syscall que vai printar

    syscall

    mov rax, sys_write
    mov rdi, stdout
    mov rsi, break_line
    mov rdx, 1

    syscall

    dec r10
    cmp r10, 0
  jg print_args_loop

 mov rax, sys_exit
 xor rdi, rdi
 syscall

Testando o programa:

[mmxm@hc0d3r tmp]$ nasm -f elf64 print_args.asm
[mmxm@hc0d3r tmp]$ ld -o print_args print_args.o
[mmxm@hc0d3r tmp]$ ./print_args 
./print_args
[mmxm@hc0d3r tmp]$ ./print_args 5
./print_args
5
[mmxm@hc0d3r tmp]$ ./print_args 5 abc
./print_args
5
abc
[mmxm@hc0d3r tmp]$ ./print_args 5 abc 123
./print_args
5
abc
123
[mmxm@hc0d3r tmp]$ /tmp/print_args x-men vs liga-da-justiça
/tmp/print_args
x-men
vs
liga-da-justiça

e é isso !~/^.;

2 comentários: