舊書重溫:0day2【2】 實驗:三種獲取kernel32.dll基址的方法


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動態調試,也證實了以上結論!

 

 

 

文章到這里就算結束了!!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM