先看看保护机制
然后拖进IDA分析下:
输入名字到一个全局变量,然后接下来是菜单:里面有指示对应的功能!下边还有个sub_400e10的函数,有点奇怪:联想强网杯的一道题,这个应该是用来泄露的。因为条件不满足,正常条件怎么也不会触发。
输入lenth,然后这个公式变形然后调用传入的函数指针的函数(分配作用)),传入这个函数的参数也是刚才变形之后的结果。然后来看看分配的函数的功能:申请以24字节为单位的块,然后每个24字节的块清零后两个8字节。相当于,分配内存初始化 ,有点构造函数的味道,但构造函数不分配内存。然后返回分配的内存指针,然后记录在ptr数组中。然后就是读入数据:也是以24字节为单位只往后16字节读入数据。
输入索引然后,查看ptr对应索引是否有值,然后以索引为参数调用函数指针传入的函数,sub_400ce0:free对应内存,然后填0,没有UAF
和malloc的功能差不多,最主要的区别在于调用传入的函数,这个函数功能不一样:判断一下传入a1,然后以24*a1+8为参数new对应大小的内存,也就是多了8字节,然后在new的内存开始处8字节存入size(以24为单位),以它往后都是以24字节为单位,第一个8字节存一个函数地址,然后后面两个清0。其实就是多的8字节用来存size(以24为单位)。
和free功能一样,然后区别在于调用的传入的函数。先从这篇内存中取出第一个8字节中存的size,然后计算出分配的这块内存的结束地址。这个函数是关键,如果看伪码看不出,所以我调试下给大家看用Python计算了当输入是16的时候申请的是2*24+8大小的块:所以我申请输入16:然后断下来后,看申请的内存:深红圈住的是size:2看执行情况rdx里边存的就是ptr数组中存的地址,看看寄存器:存的是0x614C28mov rax,[rdx-8]其实就是访问size了,执行下:然后:其实就是rdx+48,指向当前分配的内存的结束。经过调试可以相信应该看得很清楚了吧!
然后就是从分配的内存的最后往上0x18个字节,就是跳过最后一个24字节,然后取里边的内容,其实取到了那个函数地址,然后访问里边的内容,与目标进行比较。然后看里边的内容是不是和之前填的那个函数地址的内容一样,其实就是这里默认比较的东西。如果不一样就会传入对应的内存地址v2(也就是当前的存有函数地址的内存地址)作为参数,调用它,关键就在这里,如果我们该掉它?或者错误的引导程序让他觉得别的地方填的内容就是它要比较的,如果不匹配然后就执行。其实这里有个二重指针解引用试想,我们把程序取出函数地址的地方指向一个存有我们想要执行的函数的内存,当发生这个二重指针解引用的时候不就取到了我们想要执行的函数了么?然后开始执行。这个程序没有Pie,全局段完全可以使用,所以你懂得!并且这里还有个循环:可造成多次检查和执行,可以构造出rop的效果。如果和原来一样就delete掉原来的内存,然后填0.
输入索引,然后检查数组,然后执行传入的函数:伪码有问题,直接看汇编吧:其实就是printf("%s",ptr[idx]+8+24*i)用来打印每个24字节块的后16个字节.
自此程序的功能都弄清楚了!
前面说了,delete功能会首先获得内存块的size,然后跳到根据size计算出来的这个块的地方,然后获取指针,检查那个填入块的函数指针,然后根据它是不是默认的那个来决定是否执行而malloc功能的块比new功能的块少8个字节,当malloc功能的块拿去delete的时候,就会将对应的chunk的size段当做那个size,然后去取,就会造成错误。调试检验下:malloc一个24的块,输入1的时候那个公式算出来是1,这里我就不截图了,然后拿去delete,断下来:看下分配的chunk:然后进入流程:rdx指向的分配的那个开始,然后取rdx-8的内容:然后取到了size字段的0x21,然后经过那样去计算之后,会发生越界:看看我们之前分配的内存:已经越界到很下面去了。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
endingc 大师傅,你能告诉我你的main地址为啥设在0x4009ce而不是在0x4009a0,我设在0x4009a0crash掉了
endingc 还有我的onegadget有点问题你知道怎么回事吗