x86-64 C 代码反汇编解读笔记2

周梦康 2019-11-30

函数 github

内核中程序执行的栈具有以下特点

  • 每一个进程在用户态对应一个调用栈结构(call stack)
  • 程序中每一个未完成运行的函数对应一个栈帧(stack frame),栈帧中保存函数局部变量、传递给被调函数的参数等信息
  • 栈底对应高地址,栈顶对应低地址,栈由内存高地址向低地址生长
pushq   %rbp        ; Save address of previous stack frame
movq    %rsp, %rbp  ; Address of current stack frame
subq    $16, %rsp   ; Reserve 16 bytes for local variables

; ... function ...

movq    %rbp, %rsp  ; \ equivalent to the
popq    %rbp        ; / 'leave' instruction
ret

实验

因为 C 程序默认的入口是_start,会有一些其他代码干扰,所以这里我做了一些简化

#include <stdio.h>

int _start(void) {
    int a = 1;
    int b = 2;
    printf("%d",a+b);
    return 0;
}

编译

gcc main.c -nostartfiles -save-temps -o main
[vagrant@10 01]$ cat main.s
    .file    "main.c"
    .section    .rodata
.LC0:
    .string    "%d"
    .text
    .globl    _start
    .type    _start, @function
_start:
.LFB0:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $1, -4(%rbp)
    movl    $2, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    -4(%rbp), %edx
    addl    %edx, %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size    _start, .-_start
    .ident    "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
    .section    .note.GNU-stack,"",@progbits
[vagrant@10 01]$ objdump -d main.o

main.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:    55                       push   %rbp
   1:    48 89 e5                 mov    %rsp,%rbp
   4:    48 83 ec 10              sub    $0x10,%rsp
   8:    c7 45 fc 01 00 00 00     movl   $0x1,-0x4(%rbp)
   f:    c7 45 f8 02 00 00 00     movl   $0x2,-0x8(%rbp)
  16:    8b 45 f8                 mov    -0x8(%rbp),%eax
  19:    8b 55 fc                 mov    -0x4(%rbp),%edx
  1c:    01 d0                    add    %edx,%eax
  1e:    89 c6                    mov    %eax,%esi
  20:    bf 00 00 00 00           mov    $0x0,%edi
  25:    b8 00 00 00 00           mov    $0x0,%eax
  2a:    e8 00 00 00 00           callq  2f <_start+0x2f>
  2f:    b8 00 00 00 00           mov    $0x0,%eax
  34:    c9                       leaveq
  35:    c3                       retq
[vagrant@10 01]$ objdump -d main

main:     文件格式 elf64-x86-64


Disassembly of section .plt:

0000000000400330 <printf@plt-0x10>:
  400330:    ff 35 d2 0c 20 00        pushq  0x200cd2(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400336:    ff 25 d4 0c 20 00        jmpq   *0x200cd4(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40033c:    0f 1f 40 00              nopl   0x0(%rax)

0000000000400340 <printf@plt>:
  400340:    ff 25 d2 0c 20 00        jmpq   *0x200cd2(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400346:    68 00 00 00 00           pushq  $0x0
  40034b:    e9 e0 ff ff ff           jmpq   400330 <printf@plt-0x10>

Disassembly of section .text:

0000000000400350 <_start>:
  400350:    55                       push   %rbp
  400351:    48 89 e5                 mov    %rsp,%rbp
  400354:    48 83 ec 10              sub    $0x10,%rsp
  400358:    c7 45 fc 01 00 00 00     movl   $0x1,-0x4(%rbp)
  40035f:    c7 45 f8 02 00 00 00     movl   $0x2,-0x8(%rbp)
  400366:    8b 45 f8                 mov    -0x8(%rbp),%eax
  400369:    8b 55 fc                 mov    -0x4(%rbp),%edx
  40036c:    01 d0                    add    %edx,%eax
  40036e:    89 c6                    mov    %eax,%esi
  400370:    bf 86 03 40 00           mov    $0x400386,%edi
  400375:    b8 00 00 00 00           mov    $0x0,%eax
  40037a:    e8 c1 ff ff ff           callq  400340 <printf@plt>
  40037f:    b8 00 00 00 00           mov    $0x0,%eax
  400384:    c9                       leaveq
  400385:    c3                       retq

为什么最后反汇编main第一个参数传入的是0x400386呢?

movl    $.LC0, %edi  # main.s
mov    $0x0,%edi  # main.o (不知道为什么是这样)
mov    $0x400386,%edi  # main

因为printf的原型是

int printf(const char *format, ...);

第一个参数就是字符串,也就是一个地址,而400386正好比main函数的地址要大,在只读数据段中。
通过

[vagrant@10 01]$ objdump -j .rodata -d main

main:     文件格式 elf64-x86-64


Disassembly of section .rodata:

0000000000400386 <.rodata>:
  400386:    25                       .byte 0x25
  400387:    64                       fs

我们查看ascii表,25对应的是%64对应的是d

http://www.renyujie.net/articles/article_ca_x86_3.php
https://github.com/0xAX/0xAX.github.io/blob/source/content/stack_layout_x86_64.md
https://www.cnblogs.com/bangerlee/archive/2012/05/22/2508772.html
https://blog.csdn.net/poject/article/details/84031102

登录 后评论
下一篇
云栖号资讯小编
1595人浏览
2020-03-31
相关推荐
每天学点GDB(七)
1322人浏览
2016-09-14 14:50:10
CLR执行模型
727人浏览
2012-07-06 11:22:00
CPU知识
1126人浏览
2016-05-17 11:13:24
testdbg-测试调试器
688人浏览
2017-11-07 13:15:00
0
0
0
595