旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

时间:2020-12-16 14:44:33

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 

直接可以了

旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

 

论证 暴力搜索法 不是太通用呀

 

第二中办法 (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 }

旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

          xp sp2 的运行图(圆满的达到目标)

旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

      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动态调试,也证实了以上结论!

旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

 

 

旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法

 

文章到这里就算结束了!!!!