如何从命令行获取参数(assembly nasm ubuntu 32bit)?

时间:2018-11-30 10:06:51

标签: assembly command arguments nasm

我想在命令行中“打印”参数, 例如, 当我在命令行输入“ $。/ sum3 10”时, 数字'10'必须在下一行显示

(像这样)

$。/ sum3 10
10

这是代码。

            segment .bss
     p_arv  resd 1
            segment .text
            global main
     main:
            enter 0,0
            pusha

            mov eax, dword [ebp+12] ; move pointer to argv to eax
            mov [p_argv], eax

            mov eax, [p_argv + 4] ; move number '10' to eax
            call print_int
            call print_nl

            popa
            mov eax, 0
            leave 
            ret

我希望堆栈会

--------------------------堆栈顶部
10(argv [1],[p_argv + 4])
./sum3(argv [0],[p_argv])

...

指向argv的指针(EBP + 12)
参数数量(EBP + 8)
main的返回地址
EBP

-----------------------堆栈底部

但是

结果是

$。/ sum3 10
0

我不明白为什么显示“ 0”。 那么我如何获得正确的数字(参数)?

1 个答案:

答案 0 :(得分:0)

该示例看起来像一个“汇编代码” C函数,例如C编译器会添加一些开始代码,并像函数一样对其进行调用。在此起始代码中,Linux命令行被处理为C参数,看起来像

int main(int argc, char *argv[]) resp. int main(int argc, char **argv)

在堆栈上仅得到两个参数:一个数字(argc)和一个指向字符串的指针列表的指针。您必须取消引用这些指针。

看看这个例子:

; Name:     get_argv_gcc.asm
; Assemble: nasm -felf32 get_argv_gcc.asm
; Link:     gcc -m32 -oget_argv_gcc get_argv_gcc.o
; Run:      ./get_argv_gcc arg1 arg2 arg3

SECTION  .data
    argcstr     db `argc = %d\n\0`      ; backquotes for C-escapes
    argvstr     db `argv[%u] = %s\n\0`

SECTION .text
global  main
extern printf
main:

    push ebp                    ; Prolog
    mov ebp, esp
    push ebx                    ; Callee saved registers
    push esi

    mov eax, [ebp + 8]          ; argc
    push eax
    push argcstr
    call printf                 ; Call libc
    add esp, (2*4)              ; Adjust stack by 2 arguments

    mov esi, [ebp + 12]         ; **argv
    mov ebx, 0                  ; Index of argv
    .J1:
    mov eax, [esi + ebx * 4]    ; *argv[ebx]
    test eax, eax               ; Null pointer?
    je .J2                      ; Yes -> end of loop
    push eax                    ; Pointer to string
    push ebx                    ; Integer
    push argvstr                ; Pointer to format string
    call printf                 ; Call libc
    add esp, (3*4)              ; Adjust stack by 3 arguments
    inc ebx
    jmp .J1                     ; Loop
    .J2:

    xor eax, eax                ; Returncode = return(0)
    pop esi                     ; Epilog
    pop ebx
    leave
    ret

shell调用的纯汇编程序在堆栈上获取的参数略有不同。有一篇很棒的文章:NASM - Linux Getting command line parameters。试试我的例子:

; Name:     get_argv.asm
; Assemble: nasm -felf32 get_argv.asm
; Link:     ld -m elf_i386 -o get_argv get_argv.o
; Run:      ./get_argv arg1 arg2 arg3

SECTION  .data
    LineFeed    dw  10
    nullstr     db '(null)',0
    argcstr     db 'argc = '
    argcstr1    db '---------------',0
    argvstr     db 'argv['
    argvstr1    db '---------------',0
    argvstr2    db '] = ',0

SECTION .text
global  _start
_start:

    push    ebp
    mov     ebp, esp

    mov eax, [ebp + 4]          ; argc
    mov edi, argcstr1
    call EAX_to_DEC             ; Convert EAX to a string pointed by EDI

    mov esi, argcstr
    call PrintString
    mov esi, LineFeed
    call PrintString

    xor ecx, ecx

    .J1:
    mov eax, ecx
    mov edi, argvstr1
    call EAX_to_DEC             ; Convert EAX to a string pointed by EDI

    mov esi, argvstr
    call PrintString
    mov esi, argvstr2
    call PrintString
    mov esi, [ebp+8+4*ecx]      ; argv[ECX]
    call PrintString
    test esi, esi
    jz .J2
    mov esi, LineFeed
    call PrintString
    add ecx, 1
    jmp .J1
    .J2:

    .exit:
    mov esi, LineFeed
    call PrintString

    mov     esp, ebp
    pop     ebp

    mov     eax, 1              ; SYS_EXIT
    xor     ebx, ebx            ; Exit code = 0 = no error
    int     0x80                ; Call Linux

PrintString:                    ; ARG: ESI Pointer to ASCIZ string
    pusha
    test esi, esi
    jne .J0
    mov esi, nullstr
    .J0:
    mov eax, 4                  ; SYS_WRITE
    mov ebx, 1                  ; STDOUT
    mov ecx, esi
    xor edx, edx                ; Count of bytes to send
    .J1:
    cmp byte [esi], 0           ; Look for the terminating null
    je .J2
    add edx, 1
    add esi, 1
    jmp .J1
    .J2:
    int 0x80                    ; Call Linux
    popa
    ret

EAX_to_DEC:                     ; ARG: EAX integer, EDI pointer to string buffer
    push ebx
    push ecx
    push edx

    mov ebx, 10                 ; Divisor = 10
    xor ecx, ecx                ; ECX=0 (digit counter)
    .J1:                        ; First Loop: store the remainders
    xor edx, edx                ; Don't forget it!
    div ebx                     ; EDX:EAX / EBX = EAX remainder EDX
    push dx                     ; Push the digit in DL (LIFO)
    add cl, 1                   ; = inc cl (digit counter)
    or eax, eax                 ; AX == 0?
    jnz .J1                     ; No: once more
    mov ebx, ecx                ; Store count of digits
    .J2:                        ; Second loop: load the remainders in reversed order
    pop ax                      ; get back pushed digits
    or al, 00110000b            ; to ASCII
    mov [edi], al               ; Store AL to [EDI] (EDI is a pointer to a buffer)
    add edi, 1                  ; = inc edi
    loop .J2                    ; until there are no digits left
    mov byte [edi], 0           ; ASCIIZ terminator (0)
    mov eax, ebx                ; Restore Count of digits

    pop edx
    pop ecx
    pop ebx
    ret                         ; RET: EAX length of string (w/o last null)