|
对于 CPU 来说,它对指令和数据本质上是不区分的。数据也可以当成是指令,这也就是在缓冲区溢出的利用中,塞进去的是数据,但这些数据时经过精心构造过的,CPU 拿它们当指令来执行了。
在硬编码的一个简单示例里,揭示了这一点。
下面是一个普通的汇编程序:
.section .text
.global _start
_start:
nop
nop
jmp start_of_setup
nop
nop
nop
start_of_setup:
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80 这里,我们只关心 JMP 这条指令,上面的应用显而易见的跳转到 start_of_setup 这条标号下执行指令。通过 objdump 查看编译后生成的目标文件:$ objdump -d hcode.o
hcode.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 90 nop
1: 90 nop
2: eb 03 jmp 7
4: 90 nop
5: 90 nop
6: 90 nop
00000007 :
7: ba 2a 00 00 00 mov $0x2a,%edx
c: cd 80 int $0x80
e: b8 01 00 00 00 mov $0x1,%eax
13: bb 00 00 00 00 mov $0x0,%ebx
18: cd 80 int $0x80 上面,可以看到 jmp start_of_setup 的指令码为两个字节:eb 03 。其中 eb 是 jmp 指令本身的指令码;03 则是跳转的偏移,也就是 jmp 的跳转地址是从紧随其后的那条 nop 指令处开始计算,偏移 3 个字节后的那条,即 mov $0x2a, %edx 。
现在我们用硬编码来实现这个跳转。所谓的硬编码,通俗的将,就是直接用指令码来表示。修改上面的程序:
.section .text
.global _start
_start:
nop
nop
.byte 0xeb
.byte start_of_setup-4
nop
nop
nop
start_of_setup:
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80 上面,直接将原来程序中的 jmp 指令处用两个 .byte 的定义来取代。对于一般场合来说,.byte 会用来指定一个字节数据,而它也就仅会当成数据来使用。但在这里,它就变成了指令。再用 objdump 看一下,发现 dump 出来的指令码和第一个程序中的是一模一样的,也就是我们在此处使用了硬编码。另外,.byte start_of_setup-4 实际上计算的也就是 start_of_setup 和 .byte start_of_setup-4 之间的距离(3个nop,3个字节)。 |
|