• [Linux]Making plain binary files using a C compiler (i386+)第一部分


    作者:Cornelis Frank                       April 10, 2000

    工具:
    GCC 2.7.2.3以上
    NASM Version 0.97以上

    一、开始
    1.什么都没有的C程序
    test.c
    ------------
    int main () {
    }

    编译:
    gcc -c test.c
    ld -o test -Ttext 0x0 -e main test.o
    objcopy -R .note -R .comment -S -O binary test test.bin
    或者
    gcc -c test.c
    ld test.o -o test.bin -Ttext 0x0 -e main -oformat binary

    然后可以用下面这个命令查看二进制文件的反编译结果(NASM语法):
    ndisasm -b 32 test.bin

    显示:
    00000000 55      push ebp
    00000001 89E5  mov ebp,esp
    00000003 C9     leave
    00000004 C3     ret

    很简单,第一列是指令地址,第二列是指令的byte code,第三列是反编译出来的指令
    GCC只能产生32位的代码,因此不能用来写引导程序(要用16位的AS86)。

    2.使用局部变量
    为什么叫局部变量呢?反编译一下就知道了,同上,写一个test.c:
    int main () {
    int i; /* declaration of an int */
    i = 0x12345678; /* hexadecimal */
    }

    编译:
    gcc -c test.c
    ld -o test -Ttext 0x0 -e main test.o
    objcopy -R .note -R .comment -S -O binary test test.bin

    gcc -c test.c
    ld -o test.bin -Ttext 0x0 -e main -oformat binary test.o

    反编译:
    00000000 55                           push ebp
    00000001 89E5                       mov ebp,esp
    00000003 83EC04                  sub esp,byte +0x4
    00000006 C745FC78563412 mov dword [ebp-0x4],0x12345678
    0000000D C9                         leave
    0000000E C3                          ret

    原来它先把栈指针ESP减4(sizeof(int)),然后把0x12345678放到栈中了,在这个函数中使用了寄存器EBP并且未改变过值。实际上,它指向在栈中的局部变量,其引用的内容就是0x12345678!所以这就叫局部变量了?嘿嘿。

    用于存放局部变量的栈空间通常称为local stack frame,而上文中的寄存器EBP就被称为the frame pointer.

    把test.c中的
    int i;
    i = 0x12345678;
    改为
    int i = 0x12345678;
    也能得到相同的结果。

    2.使用可读写的全局变量
    为什么叫全局变量呢?同样来一下反编译,再写一个test.c:
    int i; /* declaration of global variable */
    int main () {
    i = 0x12345678;
    }

    编译:
    gcc -c test.c
    ld -o test -Ttext 0x0 -e main test.o
    objcopy -R .note -R .comment -S -O binary test test.bin

    反编译:
    00000000 55                              push ebp
    00000001 89E5                          mov ebp,esp
    00000003 C705101000007856  mov dword [0x1010],0x12345678
                     -3412
    0000000D C9                             leave
    0000000E C3                             ret

    我们看到,这里有个:0x1010,它是内存中的某一地址,这是因为编译器LD缺省时page-aligns the
    data segment(数据段页对齐?)。给LD加上-N参数后就变这样了:

    00000000 55                              push ebp
    00000001 89E5                          mov ebp,esp
    00000003 C705100000007856 mov dword [0x10],0x12345678
                      -3412
    0000000D C9                            leave
    0000000E C3                             ret

    这里0x10指向了代码结束后的内存。

    当我们用以下shell命令编译时
    gcc -c test.c
    ld -o test -Ttext 0x0 -Tdata 0x1234 -e main -N test.o
    objcopy -R .note -R .comment -S -O binary test test.bin
    反编译结果又是这样子:

    00000000 55                              push ebp
    00000001 89E5                          mov ebp,esp
    00000003 C705341200007856 mov dword [0x1234],0x12345678
                     -3412
    0000000D C9                             leave
    0000000E C3                             ret

    现在全局变量被存放到指定的0x1234里去了。也就是说如果LD使用参数-Tdata的话,我们就给出了数据段的地址,否则代码段将 located right after the code.

    如果变量存放在main函数外面预留的可访问数据段,那么就叫它全局的了。

    4.使用只读全局变量
    再像上面那样建个test.c:

    const int c = 0x12345678;
    int main () {
    }

    编译:
    gcc -c test.c
    ld -o test.bin -Ttext 0x0 -e main -N -oformat binary test.o

    反编译:
    00000000 55          push ebp
    00000001 89E5      mov ebp,esp
    00000003 C9         leave
    00000004 C3         ret
    00000005 0000     add [eax],al
    00000007 007856 add [eax+0x56],bh
    0000000A 3412    xor al,0x12

    可以看到,它与可读写的全局变量不同,它被直接写在数据段了(5-A反编译的结量没有意义,因为它是数据段)。
    我们用如下shell命令查看test.o:
    objdump --disassemble-all test.o
    将会在屏幕上看到:

    test.o: file format elf32-i386
    Disassembly of section .text:
    00000000 <main>:
    0: 55 pushl %ebp
    1: 89 e5 movl %esp,%ebp
    3: c9 leave
    4: c3 ret
    Disassembly of section .data:
    Disassembly of section .rodata:
    00000000 <c>:
    0: 78 56 js 58 <main+0x58>
    2: 34 12 xorb $0x12,%al

    这里可以更加清楚地看到,<c>被放在了rodata里,是只读的。

    把test.c改一下:
    int i = 0x12345678;
    const int c = 0x12346578;
    int main () {
    }
    用objdump得到:
    test.o: file format elf32-i386
    Disassembly of section .text:
    00000000 <main>:
    0: 55 pushl %ebp
    1: 89 e5 movl %esp,%ebp
    3: c9 leave
    4: c3 ret
    Disassembly of section .data:
    00000000 <i>:
    0: 78 56 js 58 <main+0x58>
    2: 34 12 xorb $0x12,%al
    Disassembly of section .rodata:
    00000000 <c>:
    0: 78 56 js 58 <main+0x58>
    2: 34 12 xorb $0x12,%al
    可以看到:int i 在data section 而 constant c 在只读的 read-only data section.

  • 相关阅读:
    JavaScript链式调用
    Javascript设计模式(2)-单体模式
    Javascript设计模式(1)
    stm32结合产品学习01—产品的框架
    【目标检测-模型对比1】R-CNN、SPPnet、Fast R-CNN、Faster R-CNN的对比
    【目标检测-框架测试】mmdetection的安装与使用
    【机器学习-笔记1】吴恩达网课笔记1——机器学习策略
    【算法】P1004 方格取数
    【算法】UVa 11624, Fire! 解题心得
    vector
  • 原文地址:https://www.cnblogs.com/huqingyu/p/113759.html
Copyright © 2020-2023  润新知