1

我正在尝试获取argv[1]以 x86 汇编语言存储的内容的第一个元素。我已经将堆栈弹出两次,eax 因为我想要argc这样我可以计算argc. 然后弹出argvebx。我正在考虑[ebx]投入bl. 从这里我迷路了。我在组装方面几乎没有经验,我只是想了解它。

main:
;
    mov ecx, 0 ;count output characters
    pop eax ;reject this 32 bits
    pop eax ;get argc
    ;
    pop ebx ; get argv
    ;mov bl, [ebx]
    ;
    add al, 30H ;convert integer to ascii
    mov edi, cline ;put char in output buffer
    mov byte [edi],al
    ;inc edi
    ;mov [edi], bl
    inc ecx ;increment output char count
    inc edi ;increment pointer to o/p buffer
    mov al, 0aH ;LF to end line
    mov byte[edi],al ;put it at end of output line
    inc ecx ;increment output char count
    push ecx ;save char count on stack
    mov edx,len ;length of string to write
    mov ecx,msg ;addr of string
    mov ebx,1 ;file descriptor 1 = stdout
    mov eax,4 ;"write" system call
    int 0x80 ;call the kernel
;
    pop edx ;restore char count into edx for system call
    mov ecx,cline ;address of string
    mov ebx,1 ;file descriptor 1 = stdout
4

2 回答 2

6

看看这里:NASM - Linux 获取命令行参数

下面是它的工作原理:

argc = [esp]
argv = [esp + 4 * ARG_NUMBER]

其中 ARG_NUMBER 是 argv 的基于 1 的索引

./test hello there
[esp] = 3
[esp + 4 * 1] = ./test (program path and name)
[esp + 4 * 2] = hello
[esp + 4 * 3] = there

我将使用 C 库中的 printf 使其更清晰:

extern printf, exit

section .data
fmtint  db  "%d", 10, 0
fmtstr  db  "%s", 10, 0

section .text
global main
main:

    push    dword[esp]
    push    fmtint      
    call    printf                      ; print argc
    add     esp, 4 * 2

    mov     ebx, 1  
PrintArgV:
    push    dword [esp + 4 * ebx]
    push    fmtstr
    call    printf                      ; print each param in argv
    add     esp, 4 * 2

    inc     ebx
    cmp     ebx, dword [esp]
    jng     PrintArgV

    call    exit

这里没有错误检查以保持简单。您可以检查 args 的数量是否超出您的预期或其他。

在此处输入图像描述

@Ed Cashin,如果 OP 正在学习 INTEL 语法,为什么要将它们与 AT&T 混淆?

于 2013-11-03T21:33:55.663 回答
0

我有三个建议:

  1. 如果您还没有,请查看http://www.nasm.us/doc/nasmdoc9.html ,
  2. 尽量减少试图解决您当前问题的代码,并且
  3. 遇到困难时,如果可能,请检查 C 编译器生成的程序集。

为了获取 argv,我可以简单地从我的程序中返回 argv[1] 中第一个字符的 ASCII 码,以避免系统调用。系统调用与获取 argv 是不同的问题,因此避免它会将注意力集中在手头的问题上。

然后我可以编译一个最小的 C 程序并检查生成的程序集。如果您还记得当您要去新泽西州的 AT&T 时,目的地在美国的右侧,那么阅读 AT&T 语法汇编并不是那么糟糕;)

tmp$ cat main.c
int main(int argc, char *argv[])
{
        if (argc > 1)
                return argv[1][0];
        return 0;
}
tmp$ gcc -Wall -save-temps main.c

该程序只返回 argv[1] 中第一个字符的 ASCII 码。“t”是 116。

tmp$ ./a.out test
tmp$ echo $?
116
tmp$ 

检查生成的程序集,我发现它不使用 pops,而只是根据堆栈参数相对于基指针 ebp 的位置加载寄存器。我发现我喜欢这种将 mov 与基指针一起使用的风格。

我不会像您尝试那样使用 pop,因此其他人可能会评论如何使用 pop 来做到这一点。

我已经用关于我认为正在发生的事情的评论对程序集进行了一些注释。欢迎指正。

tmp$ cat main.s
        .file   "main.c"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp         ; push the callers base pointer onto the stack
        movl    %esp, %ebp   ; save esp into the base pointer
        subl    $8, %esp     ; make some room on the stack for main ...
        andl    $-16, %esp   ; but pad to an aligned stack pointer
        movl    $0, %eax
        subl    %eax, %esp   ; dunno why gcc subtracts 0 from stack pointer
        cmpl    $1, 8(%ebp)  ; compare 1 and argc, which is 8 past the base pointer
        jle     .L2          ; jump to .L2 if argc <= 1
        movl    12(%ebp), %eax   ; fetch argv into eax
        addl    $4, %eax         ; skip the first 32 bits at that address
        movl    (%eax), %eax     ; fetch address from the resulting address
        movsbl  (%eax),%eax      ; load byte from that address into eax
        movl    %eax, -4(%ebp)   ; put that byte onto the stack (see note 1.)
        jmp     .L1
.L2:
        movl    $0, -4(%ebp)
.L1:
        movl    -4(%ebp), %eax   ; load return value from stack (see note 1.)
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .ident  "GCC: (GNU) 3.2.2"
tmp$ 

我在 32 位机器上没有方便的 nasm,而且 x86_64 调用约定与您正在处理的不同,所以我没有将这个程序集翻译成 nasm 语法。

  1. 编译器会做一些让你挠头想知道的事情,“这是聪明还是愚蠢?” 在这种情况下,我想我可能只使用 eax 本身而不是堆栈来保存返回值,但有时谷歌搜索是有教育意义的。我了解到为什么 gcc 有时喜欢使用“xor reg, reg”将寄存器归零,例如通过谷歌搜索。
于 2013-11-03T01:57:50.867 回答