找回密码
 注册
查看: 976|回复: 6

这行汇编何意,有关堆栈

[复制链接]
发表于 2006-4-28 10:15:23 | 显示全部楼层 |阅读模式
[code:1]
.section .data
new_line_char:
        .byte 0x0a
        .section .text
        .globl _start
        .align 4
_start:
        movl %esp, %ebp        # store %esp in %ebp  //指向argc
again:
        addl $4, %esp        # %esp ---> next parameter on stack//指向程序名字本身
        movl (%esp), %eax        # move next parameter into %eax  //%esp所指向内存单元里的值(这个值是也是一个地址)放入eax中
        testl %eax, %eax        # %eax (parameter) == NULL pointer?
        jz end_again                # get out of loop if yes
        call putstring                # output parameter to stdout.
        jmp again                # repeat loop
end_again:
        xorl %eax, %eax                # %eax = 0
        incl %eax                # %eax = 1, system call _exit ()
        xorl %ebx, %ebx                # %ebx = 0, normal program exit.
        int $0x80                # execute _exit () system call

        ## prints string to stdout
putstring:        .type @function
        pushl %ebp
        movl %esp, %ebp //第一次执行到此时,我觉得%esp指向程序名字本身,现在ebp指向程序的名字所在的地址,在下一行中还要加个8然后放到ecx中,我就不解了
[/code:1]
movl 8(%ebp), %ecx //%ecx是write调用中第二个参数buf的地址,我觉得ebp现在就是指向参数的,为何还要把%ebx加8后才放入%ecx中
[code:1]
xorl %edx, %edx
count_chars:
        movb (%ecx,%edx,$1), %al
        testb %al, %al
        jz done_count_chars
        incl %edx
        jmp count_chars
done_count_chars:
        movl $4, %eax
        xorl %ebx, %ebx
        incl %ebx        //%ebx = 1, fd = stdout
        int $0x80//4号是write调用write(int fd,const void *buf, size_t count); %ebx=fd, %ecx=buf, %edx=count.
        movl $4, %eax      
        leal new_line_char, %ecx  //标号new_line_char处的字符是一个换行符,0x0a
        xorl %edx, %edx
        incl %edx
        int $0x80                         //调用write()写入换行符
        movl %ebp, %esp
        popl %ebp
        ret
[/code:1]
不解的是红色的那行代码:movl 8(%ebp), %ecx
这行将%ebp里的地址加8后移到ecx中,为什么要加8呢,我觉得%ebp现在就是指向程序的参数的地址了.

程序引自:
http://database.sarang.net/study/linux/asm/linux-asm.txt
 楼主| 发表于 2006-4-28 10:18:28 | 显示全部楼层
程序引自:
http://database.sarang.net/study/linux/asm/linux-asm.txt
昨晚想了一晚上,
回复

使用道具 举报

发表于 2006-4-28 15:24:47 | 显示全部楼层
call要保存一些信息在堆栈里,好像是函数的返回地址和什么来着,所以要加数跳过那段
信息。
回复

使用道具 举报

 楼主| 发表于 2006-4-28 23:22:01 | 显示全部楼层
... ... ...
addl $4, %esp
... ... ...
movl %esp, %ebp
movl 8(%ebp), %ecx #?
... ... ...

我再想想看,文中是这么说的.如果如3张大老K所说,那么在每一个参数前要放一些信息.从程序中来看也是这样,它每从esp取参数的地址时,总要跳过8BYTE.

So how  is this  data arranged on  the stack?  Quite  simple really.
    The  number of  command line  arguments (including  the name  of the
    program)  are stored as  an integer  at [esp].   Then, at  [esp+4] a
    pointer to the first command line argument (which is the name of the
    program)  is stored.
   If  there were  any  additional command  line
    parameters,  their pointers  would be  stored in  [esp+8], [esp+12],
    etc.  After  all the  command line argument  pointers, comes  a NULL
    pointer.   After  the NULL  pointer  are  all  the pointers  to  the
    environment variables,  and then finally a NULL  pointer to indicate
    the end of the environment variables have been reached.

    A summary of the initial ELF stack is shown below;

    (%esp)         argc, count of arguments (integer)
    4(%esp)         char *argv (pointer to first command line argument)
       ...         pointers to the rest of the command line arguments
    ?(%esp) NULL pointer
       ...         pointers to environment variables
    ??(%esp)         NULL pointer
回复

使用道具 举报

发表于 2006-5-2 01:50:25 | 显示全部楼层
call putstring 就是 push eip, 而push eip 仅仅更新了 esp, ebp 没变。

第一次进 putstring 后,那个 8(%ebp)  指向的是程序名,即第一个参数,
下一次,是下一个参数。

8 的意思是,跳过本函数开始push1次, 加上call 时系统push的一次,一共2次改写了 esp
回复

使用道具 举报

 楼主| 发表于 2006-5-3 15:17:16 | 显示全部楼层
cool,
thank u.
回复

使用道具 举报

 楼主| 发表于 2006-5-10 15:00:40 | 显示全部楼层
用gdb跟踪了一下堆栈的变化,终于搞清楚了.
call putstring时,压入了后面一行即 jmp again 的地址, 对应的,putstring函数结束时所调用的 ret 指令要从堆栈中弹出 call 时压入的地址,并到那里(jmp again)去接着执行.
至于putstring函数内部开头处的 pushl %ebp 则和 函数末尾处的 popl %ebp相对应.

这个程序的功能是打印出各个参数.

假设调用这个程序执行时的命令是:
[oatt@MagicLinux program-as]$./stack-param-error oatt dog param4 param5
调用及结果是:
[oatt@MagicLinux program-as]$ ./stack-param-error oatt dog param4 param5
./stack-param-error
oatt
dog
param4
param5

addl $4,%esp这行语句很重要,每次执行它时,都使$esp往高地址处走一步,从而跨过一个参数,保证了程序能够在堆栈中遍历所有的参数.

整个执行过程中,第一次要执行again标记后的 addl $4, %esp语句时,%esp指向堆栈顶端(设为A),即放参数个数的地方.
当第二次要执行 addl $4, %esp 语句时,%esp指向A+4,(里面放的是/home/oatt/program-as/stack-param-error字符串的地址),
第三次要执行 addl $4, %esp 语句时,%esp指向A+8,(里面放的是 oatt字符串的地址.)

... ... ... ...

以下执行过程类推...


到最后吧,又有了一点疑问:

用绝对路径调用时结果中显示的也是绝对路径.

但是使用相对路径调用时./stack-param-error oatt dog param4 param5
用gdb跟踪内存时,第一个参数是/home/oatt/program-as/stack-param-error, (怎么成了绝对路径!!!),当int 0x80调用write时,%edx是39,%ecx也指向 /home/oatt/program-as/stack-param-error 这个字符串的地址(这个串39个字符), 可是程序输出时,却是./stack-param-error,它和放在内存的字符串不太一致,这里我就不知道是怎么回事了,是不是write函数或者是shell对它进行了处理??

[  int $0x80//4号是write调用write(int fd,const void *buf, size_t count); %ebx=fd, %ecx=buf, %edx=count.  ]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2025-2-7 01:02 , Processed in 0.021384 second(s), 16 queries .

© 2001-2025 Discuz! Team. Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表