这学期开始学汇编,除了嵌入式之外,还在信息安全方面有大用,本次实验通过 radare2
(没钱买ida-pro
)进行汇编代码查看,调试工具为gdb
样例
样例在此下载
运行第一个样例,可以看到要求输入密码:1
2
3 ./crackme0x00
IOLI Crackme Level 0x00
Password:
当然是hack掉了
调用堆栈模型
在汇编中 call 一个函数时发生了什么呢
- 用约定好的方式,如将参数放入指定寄存器,如
eax
或ebx
,或者通过 push 指令压入栈 - 将call下一条指令的地址(
eip
)压入栈作为返回地址压栈 - 保存调用者的栈帧(
ebp
)压栈 - 向下修改
esp
指针为当前函数留出放置临时变量的空间
返回时就反着做一边
StackOverflow
众所周知堆栈是向下生长的,而输入是往高地址输入的,看起来没问题,但是一旦输入过头就会覆盖上面的数据,比如保存的栈帧甚至是返回的地址,这就给了我们hack的机会,让程序跑飞到我们希望的地方
样例反汇编程序
可以看到在 0x8048456
处调用了scanf
,可以尝试构造一定长度的字符串输入,直到覆盖栈里的返回地址。
尝试构造字符串
这里使用这个博主的方法:radare2逆向笔记,使用shell脚本试探出字符串的长度
1 | !/bin/bash |
运行结果如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32IOLI Crackme Level 0x00
Password: Invalid Password!
......
Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29128 已完成 echo $input
29129 段错误 (核心已转储)| ./crackme0x00
28 cracked the program with 9fp95h7p17dv0jey1wy5hl89hstd
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29141 已完成 echo $input
29142 段错误 (核心已转储)| ./crackme0x00
29 cracked the program with to55sai6xa1gvg3ularlj6pbl9die
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29155 已完成 echo $input
29156 段错误 (核心已转储)| ./crackme0x00
30 cracked the program with zjbz0n0sx5oqpoyzqyewxe2uah69z6
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29169 已完成 echo $input
29170 段错误 (核心已转储)| ./crackme0x00
31 cracked the program with r43qq2zbjt2b50jm8fngir0qnfve6gs
......
可以看到从长度为28的字符串开始造成了缓冲区溢出
用python
产生一个长度为28的字符串,并输入程序,这里直接在gdb
中操作
1 | python -c "print('A'*28)" > test.in |
运行run < test.in
之后gdb
有了如下输出
1 | (gdb) run < test.in |
可以看到程序在段错误之后暂停了,用 i
指令查看寄存器
1 | (gdb) i registers |
可以看到程序返回时,ebp被改为了0x41414141
,而 0x41
正是A
的十六进制值
按照之前的内容,在往上加字符串就会覆盖掉eip
,这里使用长为32的字符串:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17(gdb) i registers
eax 0x0 0
ecx 0x0 0
edx 0x0 0
ebx 0x0 0
esp 0xffffcd40 0xffffcd40
ebp 0x41414141 0x41414141
esi 0x1 1
edi 0x8048360 134513504
eip 0x41414141 0x41414141
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
只要将多出来的4个字符修改为想要的地址就行了,通过汇编可以看到,我们的目标地址是0x08048480
C语言构造字符串:1
2
3
4
5
6
7
8
int main()
{
char a[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAA\x080\x84\x04\x08\0";
printf("%s",a);
return 0;
}
运行
1 | gcc test.c |