0x01 找kernel32基地址的方法一般有三種:
暴力搜索法、異常處理鏈表搜索法、PEB法。
0x02 基本原理
暴力搜索法是最早的動態查找kernel32基地址的方法。它的原理是幾乎所有的win32可執行文件(pe格式文件)運行的時候都加載kernel32.dll,可執行文件進入入口點執行后esp
存放的一般是Kernel32.DLL 中的某個地址,所以沿着這個地址向上查找就可以找到kernel32的基地址。
那么如何知道我們找到的地址是kernel32的基地址呢?
因為kernel32.dll也是標准的pe結構文件,pe結構文件的開始是IMAGE_DOS_HEADER結構,IMAGE_DOS_HEADER結構的第一個字段是e_magic,它的值為’MZ’用於證明這是DOS兼容的
文件類型,所以如果我們找到的地址所指向的字符串為’MZ’,那么我們可以確信這是kernel32的基地址
所謂異常處理鏈表就是系統提供的處理異常的機制,當系統
遇到一個不知道如何處理的異常時就會查找異常處理鏈表,找到對應的異常處理程序,把保存的處理程序地址賦給eip,並執行處理程序,避免系統崩潰,異常處理鏈表的最后一項
是默認異常處理函數UnhandledExceptionFilter,因為UnhandledExceptionFilter在kernel32中,所以從UNhandledExceptionFilter地址向上搜索即可找到kernel32的基地址
PEB法
TEB結構
1 // 2 // Thread Environment Block (TEB) 3 // 4 typedef struct _TEB 5 { 6 NT_TIB Tib; /* 00h */ 7 PVOID EnvironmentPointer; /* 1Ch */ 8 CLIENT_ID Cid; /* 20h */ 9 PVOID ActiveRpcHandle; /* 28h */ 10 PVOID ThreadLocalStoragePointer; /* 2Ch */ 11 struct _PEB *ProcessEnvironmentBlock; /* 30h */ 12 ULONG LastErrorValue; /* 34h */ 13 ULONG CountOfOwnedCriticalSections; /* 38h */ 14 PVOID CsrClientThread; /* 3Ch */ 15 struct _W32THREAD* Win32ThreadInfo; /* 40h */ 16 ULONG User32Reserved[0x1A]; /* 44h */ 17 ULONG UserReserved[5]; /* ACh */ 18 PVOID WOW32Reserved; /* C0h */ 19 LCID CurrentLocale; /* C4h */ 20 ULONG FpSoftwareStatusRegister; /* C8h */ 21 PVOID SystemReserved1[0x36]; /* CCh */ 22 LONG ExceptionCode; /* 1A4h */ 23 struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */ 24 UCHAR SpareBytes1[0x28]; /* 1ACh */ 25 GDI_TEB_BATCH GdiTebBatch; /* 1D4h */ 26 CLIENT_ID RealClientId; /* 6B4h */ 27 PVOID GdiCachedProcessHandle; /* 6BCh */ 28 ULONG GdiClientPID; /* 6C0h */ 29 ULONG GdiClientTID; /* 6C4h */ 30 PVOID GdiThreadLocalInfo; /* 6C8h */ 31 ULONG Win32ClientInfo[62]; /* 6CCh */ 32 PVOID glDispatchTable[0xE9]; /* 7C4h */ 33 ULONG glReserved1[0x1D]; /* B68h */ 34 PVOID glReserved2; /* BDCh */ 35 PVOID glSectionInfo; /* BE0h */ 36 PVOID glSection; /* BE4h */ 37 PVOID glTable; /* BE8h */ 38 PVOID glCurrentRC; /* BECh */ 39 PVOID glContext; /* BF0h */ 40 NTSTATUS LastStatusValue; /* BF4h */ 41 UNICODE_STRING StaticUnicodeString; /* BF8h */ 42 WCHAR StaticUnicodeBuffer[0x105]; /* C00h */ 43 PVOID DeallocationStack; /* E0Ch */ 44 PVOID TlsSlots[0x40]; /* E10h */ 45 LIST_ENTRY TlsLinks; /* F10h */ 46 PVOID Vdm; /* F18h */ 47 PVOID ReservedForNtRpc; /* F1Ch */ 48 PVOID DbgSsReserved[0x2]; /* F20h */ 49 ULONG HardErrorDisabled; /* F28h */ 50 PVOID Instrumentation[14]; /* F2Ch */ 51 PVOID SubProcessTag; /* F64h */ 52 PVOID EtwTraceData; /* F68h */ 53 PVOID WinSockData; /* F6Ch */ 54 ULONG GdiBatchCount; /* F70h */ 55 BOOLEAN InDbgPrint; /* F74h */ 56 BOOLEAN FreeStackOnTermination; /* F75h */ 57 BOOLEAN HasFiberData; /* F76h */ 58 UCHAR IdealProcessor; /* F77h */ 59 ULONG GuaranteedStackBytes; /* F78h */ 60 PVOID ReservedForPerf; /* F7Ch */ 61 PVOID ReservedForOle; /* F80h */ 62 ULONG WaitingOnLoaderLock; /* F84h */ 63 ULONG SparePointer1; /* F88h */ 64 ULONG SoftPatchPtr1; /* F8Ch */ 65 ULONG SoftPatchPtr2; /* F90h */ 66 PVOID *TlsExpansionSlots; /* F94h */ 67 ULONG ImpersionationLocale; /* F98h */ 68 ULONG IsImpersonating; /* F9Ch */ 69 PVOID NlsCache; /* FA0h */ 70 PVOID pShimData; /* FA4h */ 71 ULONG HeapVirualAffinity; /* FA8h */ 72 PVOID CurrentTransactionHandle; /* FACh */ 73 PTEB_ACTIVE_FRAME ActiveFrame; /* FB0h */ 74 PVOID FlsData; /* FB4h */ 75 UCHAR SafeThunkCall; /* FB8h */ 76 UCHAR BooleanSpare[3]; /* FB9h */ 77 } TEB, *PTEB;
PEB結構
1 typedef struct _PEB 2 { 3 UCHAR InheritedAddressSpace; // 00h 4 UCHAR ReadImageFileExecOptions; // 01h 5 UCHAR BeingDebugged; // 02h 6 UCHAR Spare; // 03h 7 PVOID Mutant; // 04h 8 PVOID ImageBaseAddress; // 08h 9 PPEB_LDR_DATA Ldr; // 0Ch 10 PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h 11 PVOID SubSystemData; // 14h 12 PVOID ProcessHeap; // 18h 13 PVOID FastPebLock; // 1Ch 14 PPEBLOCKROUTINE FastPebLockRoutine; // 20h 15 PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h 16 ULONG EnvironmentUpdateCount; // 28h 17 PVOID* KernelCallbackTable; // 2Ch 18 PVOID EventLogSection; // 30h 19 PVOID EventLog; // 34h 20 PPEB_FREE_BLOCK FreeList; // 38h 21 ULONG TlsExpansionCounter; // 3Ch 22 PVOID TlsBitmap; // 40h 23 ULONG TlsBitmapBits[0x2]; // 44h 24 PVOID ReadOnlySharedMemoryBase; // 4Ch 25 PVOID ReadOnlySharedMemoryHeap; // 50h 26 PVOID* ReadOnlyStaticServerData; // 54h 27 PVOID AnsiCodePageData; // 58h 28 PVOID OemCodePageData; // 5Ch 29 PVOID UnicodeCaseTableData; // 60h 30 ULONG NumberOfProcessors; // 64h 31 ULONG NtGlobalFlag; // 68h 32 UCHAR Spare2[0x4]; // 6Ch 33 LARGE_INTEGER CriticalSectionTimeout; // 70h 34 ULONG HeapSegmentReserve; // 78h 35 ULONG HeapSegmentCommit; // 7Ch 36 ULONG HeapDeCommitTotalFreeThreshold; // 80h 37 ULONG HeapDeCommitFreeBlockThreshold; // 84h 38 ULONG NumberOfHeaps; // 88h 39 ULONG MaximumNumberOfHeaps; // 8Ch 40 PVOID** ProcessHeaps; // 90h 41 PVOID GdiSharedHandleTable; // 94h 42 PVOID ProcessStarterHelper; // 98h 43 PVOID GdiDCAttributeList; // 9Ch 44 PVOID LoaderLock; // A0h 45 ULONG OSMajorVersion; // A4h 46 ULONG OSMinorVersion; // A8h 47 ULONG OSBuildNumber; // ACh 48 ULONG OSPlatformId; // B0h 49 ULONG ImageSubSystem; // B4h 50 ULONG ImageSubSystemMajorVersion; // B8h 51 ULONG ImageSubSystemMinorVersion; // C0h 52 ULONG GdiHandleBuffer[0x22]; // C4h 53 PVOID ProcessWindowStation; // ??? 54 } PEB, *PPEB;
原理:在NT內核系統中fs寄存器指向TEB結構,TEB+0x30處指向PEB結構,PEB+0x0c處指向PEB_LDR_DATA結構,
PEB_LDR_DATA+0x1c處存放一些動態鏈接庫地址,第一個指向ntdl.dll,第二個就是kernel32.dll的基地址了
0x03 驗證以上辦法可行性
現在我們就來研究下第一中方法暴力搜索法
http://blog.csdn.net/syf442/article/details/4383254(更詳細的介紹)
ps:按照上面文章介紹不會觸發 非法訪問問題,實驗證明(環境xp sp2 + vc++6.0) 確實有 非法訪問的 異常
1 #include "stdafx.h" 2 #include <stdio.h> 3 4 int main() 5 { 6 7 8 _asm { jmp Start } 9 int ieax; 10 11 12 13 _asm{ 14 Start: 15 16 17 GetKernelBase: ;查找 kernel地址 18 mov eax,7c800000h ;因為有非法訪問我直接把我本機的kerne32.dll地址(7c800000h) 給eax 就可以了 19 20 Compare: 21 cmp eax,80000000h 22 jl SearchFinal 23 cmp word ptr[eax],'ZM' 24 je FindedKernelBase 25 add eax,010000h 26 jmp Compare 27 28 29 } 30 FindedKernelBase: 31 { 32 _asm{ mov ieax,eax} 33 printf("kernel addr offset %x \n",ieax); 34 return 0; 35 } 36 SearchFinal : 37 { //;查找結束 38 printf("find kernel faild \n "); 39 return 0; 40 } 41 return 0; 42 }
我剛開始按照老羅的思路,從棧頂向下搜索,有問題
后來我就從8000000h搜索至70000000h處
發現有非法訪問
為了確定我的發現
我從從7000000h搜索至80000000h處
還是有非法訪問
那我直接把我電腦的kernel32.dll地址 替換 7000000h 為7c800000h
直接可以了
論證 暴力搜索法 不是太通用呀
第二中辦法 (SEH)異常處理鏈表搜索法
其中要先補習下基礎知識
異常處理鏈表末端的處理結構體是系統最后為異常准備的處理(其中的下一個結構指針prev 為-1),就是咱們經常遇到的程序崩潰的提示。其地址是在kernel32 內存空間內部,我們只要找到最后的異常處理結構體,那么我們從
這個地址找下去一定能找到 ‘MZ’標志(kernel32的地址);
其中SEH鏈表位置:fs:[0]->線程信息塊TIB,TIB.ExceptionList->SEH鏈表
1 nt!_NT_TIB 2 +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD ;SEH鏈表頭 3 +0x004 StackBase : Ptr32 Void 4 +0x008 StackLimit : Ptr32 Void 5 +0x00c SubSystemTib : Ptr32 Void 6 +0x010 FiberData : Ptr32 Void 7 +0x010 Version : Uint4B 8 +0x014 ArbitraryUserPointer : Ptr32 Void 9 +0x018 Self : Ptr32 _NT_TIB
1 鏈表節點 2 3 _EXCEPTION_REGISTRATION struc 4 5 prev dd ? ;下一個_EXCEPTION_REGISTRATION結構 6 7 handler dd ? ;異常處理函數地址 8 9 _EXCEPTION_REGISTRATION ends
1 #include <stdio.h> 2 #include <windows.h> 3 int main() 4 { 5 __asm 6 { 7 8 9 10 mov edx, fs:[0] // 獲得EXCEPTION_REGISTRATION結構地址 11 Next: 12 inc dword ptr [edx] // 將prev+1,如果是-1經過+1后等於0 (證明找到了 SEH鏈表的最后一項,也就達到了kernel的內存空間中了)
// 其中第一次時:fs:[0]->線程信息塊TIB,TIB.ExceptionList->SEH鏈表 13 jz Krnl 14 dec dword ptr [edx] // 不為-1,還原 15 mov edx, [edx] // 獲得prev指向的地址 16 jmp Next 17 18 Krnl: 19 dec dword ptr [edx] // 恢復 20 mov edx, [edx + 4] // 獲得handle指向的地址 21 22 Looop: 23 cmp word ptr [edx], 'ZM' 24 jz IsPe 25 dec edx 26 xor dx, dx 27 jmp Looop 28 29 IsPe: 30 mov eax, dword ptr [edx + 3ch] 31 cmp word ptr [edx + eax], 'EP' 32 jnz Next 33 mov dwKrnlAddr, edx 34 } 35 printf(TEXT("Kernel32.dll address: %x\r\n"), dwKrnlAddr); 36 printf(TEXT("GetModuleHandle Kernel32.dll address: %x\r\n"), 37 GetModuleHandle(TEXT("kernel32.dll"))); 38 printf(TEXT("LoadLibrary Kernel32.dll address: %x\r\n"), 39 LoadLibrary(TEXT("kernel32.dll"))); 40 return 0; 41 }
xp sp2 的運行圖(圓滿的達到目標)
win7 x64 sp1 的運行結構(其中下邊使用DEPENDS.EXE發現的 DLL的基址)目測沒有達到預期目標,難道是win7有什么貓膩,下小節動態跟蹤下
但是論證了下發現此辦法在xp sp2上 可以達到目的,而win7 x64 sp1 目測沒有達到目的
進一步驗證 SEH異常鏈表搜索法 在 win7上失敗的原因
動態跟蹤了下發現確實找到了MZ的標志,也驗證存在PE標志,但是找的和 GetModuleHandle 、LoadLibrary 獲取到的不對
據推測可能是 異常鏈表最后一項的系統處理 (在win7 下)不再kernel32內存空間中,在其他dll內存空間中。。。
win7下的 異常鏈表最后一項的系統處理 在ADVAPI32地址空間下(或許這樣把)
接下來 我們實驗 第三種辦法PEB法
基礎知識必須要學習點,其中TEB和PEB結構看上面。
原理:在NT內核系統中fs寄存器指向TEB結構,TEB+0x30處指向PEB結構,PEB+0x0c處指向PEB_LDR_DATA結構,
PEB_LDR_DATA+0x1c處存放一些動態鏈接庫地址,第一個指向ntdl.dll,第二個就是kernel32.dll的基地址了
我先嘗試這寫下匯編代碼這次就直接在win7上調試(很久前,我調試過PEB找kernel32地址的代碼,確實可行)
下面代碼通過PEB獲取得到了kernel32地址,通過函數表 得到了 GetProcAddress函數地址,通過此函數地址
獲取Beep()函數地址,來證明 win7下 是可行的。。。
int (*pv)(HINSTANCE,char*); //pv = GetProcAdr; //pv = GetProcAdr; DWORD pBeep = 0; DWORD pGetProcAddress = 0; DWORD pKernel32 =0; HINSTANCE hK = GetModuleHandle("kernel32.dll"); //Beep printf(" Beep is %x \n",GetProcAddress(hK,"Beep")); _asm { push eax push esi push edx push ebp push esp sub esp,400h mov eax, fs:0x30 ;PEB的地址 mov eax, [eax + 0x0c] ;Ldr的地址 mov esi, [eax + 0x1c] ;Flink地址 lodsd mov eax, [eax + 0x08] ;eax就是kernel32.dll的地址 mov pKernel32,eax mov ebp,eax mov eax, [ebp+3Ch] ;eax = PE首部 mov edx,[ebp+eax+78h] add edx,ebp ;edx = 引出表地址 mov ecx , [edx+18h] ;ecx = 輸出函數的個數 mov ebx,[edx+20h] add ebx, ebp ;ebx =函數名地址,AddressOfName search: dec ecx mov esi,[ebx+ecx*4] add esi,ebp ;依次找每個函數名稱 ;GetProcAddress mov eax,0x50746547 cmp [esi], eax; 'PteG' jne search mov eax,0x41636f72 cmp [esi+4],eax; 'Acor' jne search ;如果是GetProcA,表示找到了 mov ebx,[edx+24h] add ebx,ebp ;ebx = 序號數組地址,AddressOf mov cx,[ebx+ecx*2] ;ecx = 計算出的序號值 mov ebx,[edx+1Ch] add ebx,ebp ;ebx=函數地址的起始位置,AddressOfFunction mov eax,[ebx+ecx*4] add eax,ebp ;利用序號值,得到出GetProcAddress的地址 add esp,400h pop esp pop ebp pop edx pop esi //mov ebx,[eax + 3ch ] //mov ebx,[eax + ebx + 78h] //add ebx,eax //mov ebx,[ebx+20h] //add ebx,eax mov pGetProcAddress,eax mov pv,eax pop eax // yan zheng han GetAddress 正確性 // beep //sub esp,90h //push 0x70656562 //push hK //call pv //add esp,90h ////add esp,8h //mov pBeep,eax } int a = (pv)(hK,"Beep") ; printf(" Beep is %x \n", a ); printf("kernel32 addr is %x , PEB get GetProcAddress addr is %x \n",pKernel32,pGetProcAddress); printf("kernel32 addr is %x , \r\n GetProcAddress() get GetProcAddress addr is %x \n",GetModuleHandle("kernel32.dll"),GetProcAddress(GetModuleHandle("kernel32.dll"),"GetProcAddress"));
(以上代碼 在 int a = (pv)(hK,"Beep") ; 存在 chkesp 提示,不知如何平衡堆棧,調試了好久 還請 高人指點 )
雖然在 win7 搜索得到的GetProcAddress地址 和 用 GetProcAddress()函數獲取得到的GetProcAddress地址不同;但是,通過搜索得到的 GetProcAddress地址 調用這個地址 獲取到的Beep()函數都是相同的,Ollydby動態調試,也證實了以上結論!
文章到這里就算結束了!!!!