[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.

原文地址:https://www.cnblogs.com/huqingyu/p/113759.html