Assembly 汇编语言帮助查找agrv[1][0]

Assembly 汇编语言帮助查找agrv[1][0],assembly,x86,nasm,argv,Assembly,X86,Nasm,Argv,我试图获取x86汇编语言中存储在argv[1]中的第一个元素。 最初我将堆栈弹出两次到eax,因为我想要argc,这样我可以计算argc的数量。然后弹出argv到ebx。我想把[ebx]放到bl里。从这里我迷路了。我在组装方面几乎没有经验,我只是想了解它 main: ; mov ecx, 0 ;count output characters pop eax ;reject this 32 bits pop eax ;get argc ; pop ebx ;

我试图获取x86汇编语言中存储在
argv[1]
中的第一个元素。 最初我将堆栈弹出两次到
eax
,因为我想要
argc
,这样我可以计算
argc
的数量。然后弹出
argv
ebx
。我想把
[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

我有三项建议:

  • 如果你还没有
  • 尽量减少试图解决眼前问题的代码,以及
  • 如果可能的话,在卡住时检查C编译器生成的程序集
  • 为了获取argv,我可以简单地从程序返回argv[1]中第一个字符的ASCII码,以避免系统调用。系统调用与获取argv是不同的问题,因此避免系统调用会将注意力集中在手头的问题上

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

    程序只返回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$ 
    
    tmp$cat main.s
    .file“main.c”
    .文本
    格洛博梅因酒店
    .type main,@函数
    主要内容:
    pushl%ebp;将调用者基指针推送到堆栈上
    移动百分比esp,%ebp;将esp保存到基指针中
    低于8美元,特别是;在堆栈上留出一些空间用于主。。。
    andl$-16%,esp;但将pad移到对齐的堆栈指针
    movl$0,%eax
    次级%eax,%esp;不知道为什么gcc会从堆栈指针中减去0
    cmpl$1,8%(ebp);比较1和argc,argc超过基指针8
    
    jle.L2;如果argc我有三个建议,请跳到.L2:

  • 如果你还没有
  • 尽量减少试图解决眼前问题的代码,以及
  • 如果可能的话,在卡住时检查C编译器生成的程序集
  • 为了获取argv,我可以简单地从程序返回argv[1]中第一个字符的ASCII码,以避免系统调用。系统调用与获取argv是不同的问题,因此避免系统调用会将注意力集中在手头的问题上

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

    程序只返回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$ 
    
    tmp$cat main.s
    .file“main.c”
    .文本
    格洛博梅因酒店
    .type main,@函数
    主要内容:
    pushl%ebp;将调用者基指针推送到堆栈上
    移动百分比esp,%ebp;将esp保存到基指针中
    低于8美元,特别是;在堆栈上留出一些空间用于主。。。
    andl$-16%,esp;但将pad移到对齐的堆栈指针
    movl$0,%eax
    次级%eax,%esp;不知道为什么gcc会从堆栈指针中减去0
    cmpl$1,8%(ebp);比较1和argc,argc超过基指针8
    jle.L2;如果argc请跳转到.L2,请查看此处:

    以下是它的工作原理:

    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
    
    为了保持简单,这里没有错误检查。您可以检查arg的数量是否超过预期值或其他值

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

    看看这里:

    以下是它的工作原理:

    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
    
    为了保持简单,这里没有错误检查。您可以检查arg的数量是否超过预期值或其他值


    @Ed Cashin,如果OP正在学习英特尔语法,为什么要将其与AT&T混淆?

    请不要忘记使用代码块。我帮你修好了。请别忘了使用代码块。“我帮你修好了。”Gunner问我为什么在OP明显使用Intel语法的情况下使用AT&T语法。原因是,我相信达到双语熟练程度要比完全避免AT&T语法困难得多。检查“-save temps”留下的输出的能力弥补了任何暂时的混乱。为了进一步缓解这种不适,我提供了一个助记符。谢谢你的提问。@Gunner问我为什么在OP明显使用Intel语法的情况下使用AT&T语法。原因是,我相信达到双语熟练程度要比完全避免AT&T语法困难得多。检查“-save temps”留下的输出的能力弥补了任何暂时的混乱。缓和