1、双向链表上有a、b、c一共3个连续的堆块,a、b、c三者之间的实际物理地址可能相差很大,但是绝对不会三者之间无其他字节,如果无其他字节,那表示他们三个可以合并成一个物理连接起来的大块,堆管理系统很快会让他们三者搞基成一个大个子堆块,拆下来重新按照其尺寸重新链接到该去的链表位置去。。。而不会放纵其在链表上物理地址紧密链接的同时还分成3个堆块串联在链表上。
2、返回正题,拆下其中一个堆块b时,会产生a、c之间的空缺,导致a、c无法相互知晓。所以堆管理系统会在拆b时,把a和c缝缝补补,连在一起,成为新链表,等待下次拆分、合并等等。
关键来了,
【1】双向链表上a与b之间有从左往右的一条线(这条线体现在:每个堆块都在距离自身堆块块首的相对第9~12字节保存他前方那个堆块(远离链表头的方向)的块数据的地址)连起来.
【2】又有从右往左的一条线连起来(这条线体现在:每个堆块都在距离自身堆块块首相对13~16字节处保存他屁股后面那个堆块(当前堆块屁股位置是靠近链表头地址的那边)的块数据的地址);
以此类推每2个相邻堆块之间由于双向可循,由于每个堆块上都记载着往该块左边或右边方向走的下一个地址(即上述9~12、13~16字节数据均为地址值),所以断掉一个块(暂称为块b)时,该断掉的块的左右两边欲形成新连接,必须知道彼此的地址。这个重新知晓相互地址的任务由即将被拆下来的堆块去处理,因为它才同时熟悉两边的邻居,知道他们地址,知道他们联系电话,QQ号,微信号。。。于是该堆块(又叫节点)把他身上距离头顶第9~12字节的4个字节的数据(实际上是他右邻居堆块的门牌号地址,举例0x001A0038)抄下来,给到左邻居。怎么给?在他身上距离头顶第13~16字节处的4个字节,就是他左邻居的门牌号地址(这个数据肯定比他右邻居的门牌号地址小,假设0x001A000A),我们于是按照这个左邻居地址,在堆管理系统帮助下,把0x001A0038这个数值给到左邻居家去:mov [0x001A000A],0x001A0038 。。。。都知道[]方括号的意思吧。等右邻居的地址给了左邻居后,再把左邻居的地址按此方法给到右邻居,他此时也就从链表上断下来了,因为空表链表上已经没有他存在的痕迹了。
而这个断掉块b过程之前,我们先通过字符串变量溢出,将字符串从其他块溢出到块b中,即覆盖了他的第9~16字节数据,效果就是改了他身上所保管的左、右邻居门牌号地址,一般就是右邻门牌号覆盖为shellcode入口地址,左邻门牌号覆盖为各种特殊地址,比如重要函数调用地址,栈帧中函数返回地址,栈帧中SEH的句柄。这样,在块b断下来时,他会把shellcode地址赋值到某个经常调用的函数的地址上,或栈帧中当前函数返回地址上,或栈帧中距离当前栈最近的SEH异常处理最近的那个句柄。
然后堆块这边就没他们什么事了,就等着某个函数被调用,错误跳转到shellcode地址去执行我们构造的code;等着栈帧中当前函数返回时执行的却是跳转到shellcode地址去执行我们构造的code;溢出发生了导致系统启动异常处理,执行了SEH异常处理相关函数,跳转到shellcode地址去执行我们构造的code。。。。。。
溢出并恶意执行了我们的代码。
现在回头去看,堆块断下来时,把错误的右邻居门牌号(子弹,shellcode入口地址,共计4个字节,即为DWORD双字),抄给左邻居时(左邻居的地址已经被改为目标地址,任何能被调用的地址),就是发生了子弹射击,DWORD Shoot是也。
--至于后面的把左邻居地址给右邻居地址的操作,会导致反射,影响shellcode,这个以后再分享了。