我试图了解如何在x86中进行系统调用。我正在阅读Smashing the stack for fun and profit。第7页提供了一个功能:

#include <stdio.h>
void main() {
    char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL;
    execve(name[0], name, NULL);
}


并在函数下方提供其程序集转储:

函数main的汇编代码转储:

0x8000130 : pushl %ebp
0x8000131 : movl %esp,%ebp
0x8000133 : subl $0x8,%esp
0x8000136 : movl $0x80027b8,0xfffffff8(%ebp)
0x800013d : movl $0x0,0xfffffffc(%ebp)
0x8000144 : pushl $0x0
0x8000146 : leal 0xfffffff8(%ebp),%eax
0x8000149 : pushl %eax
0x800014a : movl 0xfffffff8(%ebp),%eax
0x800014d : pushl %eax
0x800014e : call 0x80002bc <__execve>
0x8000153 : addl $0xc,%esp
0x8000156 : movl %ebp,%esp
0x8000158 : popl %ebp
0x8000159 : ret


函数__execve的汇编代码转储:

0x80002bc <__execve>: pushl %ebp
0x80002bd <__execve+1>: movl %esp,%ebp
0x80002bf <__execve+3>: pushl %ebx
0x80002c0 <__execve+4>: movl $0xb,%eax
0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx
0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx
0x80002cb <__execve+15>: movl 0x10(%ebp),%edx
0x80002ce <__execve+18>: int $0x80
0x80002d0 <__execve+20>: movl %eax,%edx
0x80002d2 <__execve+22>: testl %edx,%edx
0x80002d4 <__execve+24>: jnl 0x80002e6 <__execve+42>
0x80002d6 <__execve+26>: negl %edx
0x80002d8 <__execve+28>: pushl %edx
0x80002d9 <__execve+29>: call 0x8001a34 <__normal_errno_location>
0x80002de <__execve+34>: popl %edx
0x80002df <__execve+35>: movl %edx,(%eax)
0x80002e1 <__execve+37>: movl $0xffffffff,%eax
0x80002e6 <__execve+42>: popl %ebx
0x80002e7 <__execve+43>: movl %ebp,%esp
0x80002e9 <__execve+45>: popl %ebp
0x80002ea <__execve+46>: ret
0x80002eb <__execve+47>: nop


但是在我的机器上编写相同的代码并进行编译时


gcc test.c -m32 -g -o test -fno-stack-protector -static


并生成转储


objdump -S测试> test.dis


我得到以下主要转储:

void main(){
 8048e24:       55                      push   %ebp
 8048e25:       89 e5                   mov    %esp,%ebp
 8048e27:       83 e4 f0                and    $0xfffffff0,%esp
 8048e2a:       83 ec 20                sub    $0x20,%esp
        char *name[2];
        name[0] = "/bin/sh";
 8048e2d:       c7 44 24 18 e8 de 0b    movl   $0x80bdee8,0x18(%esp)
 8048e34:       08
        name[1] = NULL;
 8048e35:       c7 44 24 1c 00 00 00    movl   $0x0,0x1c(%esp)
 8048e3c:       00
        execve(name[0], name, NULL);
 8048e3d:       8b 44 24 18             mov    0x18(%esp),%eax
 8048e41:       c7 44 24 08 00 00 00    movl   $0x0,0x8(%esp)
 8048e48:       00
 8048e49:       8d 54 24 18             lea    0x18(%esp),%edx
 8048e4d:       89 54 24 04             mov    %edx,0x4(%esp)
 8048e51:       89 04 24                mov    %eax,(%esp)
 8048e54:       e8 17 34 02 00          call   806c270 <__execve>
}


对于__execve:

0806c270 <__execve>:
 806c270:       53                      push   %ebx
 806c271:       8b 54 24 10             mov    0x10(%esp),%edx
 806c275:       8b 4c 24 0c             mov    0xc(%esp),%ecx
 806c279:       8b 5c 24 08             mov    0x8(%esp),%ebx
 806c27d:       b8 0b 00 00 00          mov    $0xb,%eax
 806c282:       ff 15 f0 99 0e 08       call   *0x80e99f0
 806c288:       3d 00 f0 ff ff          cmp    $0xfffff000,%eax
 806c28d:       77 02                   ja     806c291 <__execve+0x21>
 806c28f:       5b                      pop    %ebx
 806c290:       c3                      ret
 806c291:       c7 c2 e8 ff ff ff       mov    $0xffffffe8,%edx
 806c297:       f7 d8                   neg    %eax
 806c299:       65 89 02                mov    %eax,%gs:(%edx)
 806c29c:       83 c8 ff                or     $0xffffffff,%eax
 806c29f:       5b                      pop    %ebx
 806c2a0:       c3                      ret
 806c2a1:       66 90                   xchg   %ax,%ax
 806c2a3:       66 90                   xchg   %ax,%ax
 806c2a5:       66 90                   xchg   %ax,%ax
 806c2a7:       66 90                   xchg   %ax,%ax
 806c2a9:       66 90                   xchg   %ax,%ax
 806c2ab:       66 90                   xchg   %ax,%ax
 806c2ad:       66 90                   xchg   %ax,%ax
 806c2af:       90                      nop


我了解该文章很旧,因此可能与当前标准不完全匹配。实际上,我能够理解大多数差异。这是困扰我的事情:

据我所知:要执行exec系统调用,我需要将参数放在特定的寄存器中并调用指令


整数0x80


发送中断。我可以在本文给出的转储中的地址0x80002ce处看到此指令。但是我在我的身上找不到相同的指令。我发现代替它


致电* 0x80e99f0


而且我的转储中甚至不存在地址0x80e99f0。我在这里想念什么? 0x80e99f0之前*的意义是什么?地址0x80e99f0是否在运行时动态加载?如果是真的,那么在编译过程中-static标志的用途是什么,如何使转储与本文相似?

我在Intel处理器上运行64位ubuntu 14.04

在获得建议以-DS标志运行objdump之后进行编辑:

我终于得到了隐藏地址:

080e99f0 <_dl_sysinfo>:
 80e99f0:       70 ed                   jo     80e99df <_dl_load_lock+0x7>
 80e99f2:       06                      push   %es
 80e99f3:       08 b0 a6 09 08 07       or     %dh,0x70809a6(%eax)


但仍然没有任何意义。

jo 80e99df中的地址再次指向隐藏在这些行之间的内容:

080e99d8 <_dl_load_lock>:
        ...
 80e99e4:       01 00                   add    %eax,(%eax)
        ...




从答案中可以明显看出,代码实际上跳到了内存位置0x80e99f0中存在的地址,该地址最终指向int $0x80指令。

最佳答案

尝试使用objdump -DSobjdump -sS在转储中包含地址0x80e99f0。

本地示例:

0806bf70 <__execve>:
...
806bf82:       ff 15 10 a3 0e 08       call   *0x80ea310


在地址0x80ea310(用objdump -sS显示):

80ea310 10ea0608 60a60908 07000000 7f030000


10ea0608是内存中的地址0x806ea10 little-endian。

然后,您将看到_dl_sysinfo_int80的地址位于此处:

0806ea10 <_dl_sysinfo_int80>:
 806ea10:       cd 80                   int    $0x80
 806ea12:       c3                      ret


它将调用软件中断0x80(执行syscall),然后返回到调用方。

因此,调用* 0x80ea310实际上是在调用0x806ea10(取消引用指针)

07-24 09:46
查看更多