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

问题描述 投票:0回答:2

我正在尝试获取 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
assembly x86 nasm argv
2个回答
7
投票

看这里: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

为了简单起见,这里没有错误检查。您可以检查参数的数量是否超出您的预期或其他。

enter image description here

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


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$ 

检查生成的程序集,我发现它不使用 pop,而只是根据堆栈参数相对于基指针 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”将寄存器归零,例如通过谷歌搜索。
© www.soinside.com 2019 - 2024. All rights reserved.