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结构

 //
// Thread Environment Block (TEB)
//
typedef struct _TEB
{
NT_TIB Tib; /* 00h */
PVOID EnvironmentPointer; /* 1Ch */
CLIENT_ID Cid; /* 20h */
PVOID ActiveRpcHandle; /* 28h */
PVOID ThreadLocalStoragePointer; /* 2Ch */
struct _PEB *ProcessEnvironmentBlock; /* 30h */
ULONG LastErrorValue; /* 34h */
ULONG CountOfOwnedCriticalSections; /* 38h */
PVOID CsrClientThread; /* 3Ch */
struct _W32THREAD* Win32ThreadInfo; /* 40h */
ULONG User32Reserved[0x1A]; /* 44h */
ULONG UserReserved[]; /* ACh */
PVOID WOW32Reserved; /* C0h */
LCID CurrentLocale; /* C4h */
ULONG FpSoftwareStatusRegister; /* C8h */
PVOID SystemReserved1[0x36]; /* CCh */
LONG ExceptionCode; /* 1A4h */
struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
UCHAR SpareBytes1[0x28]; /* 1ACh */
GDI_TEB_BATCH GdiTebBatch; /* 1D4h */
CLIENT_ID RealClientId; /* 6B4h */
PVOID GdiCachedProcessHandle; /* 6BCh */
ULONG GdiClientPID; /* 6C0h */
ULONG GdiClientTID; /* 6C4h */
PVOID GdiThreadLocalInfo; /* 6C8h */
ULONG Win32ClientInfo[]; /* 6CCh */
PVOID glDispatchTable[0xE9]; /* 7C4h */
ULONG glReserved1[0x1D]; /* B68h */
PVOID glReserved2; /* BDCh */
PVOID glSectionInfo; /* BE0h */
PVOID glSection; /* BE4h */
PVOID glTable; /* BE8h */
PVOID glCurrentRC; /* BECh */
PVOID glContext; /* BF0h */
NTSTATUS LastStatusValue; /* BF4h */
UNICODE_STRING StaticUnicodeString; /* BF8h */
WCHAR StaticUnicodeBuffer[0x105]; /* C00h */
PVOID DeallocationStack; /* E0Ch */
PVOID TlsSlots[0x40]; /* E10h */
LIST_ENTRY TlsLinks; /* F10h */
PVOID Vdm; /* F18h */
PVOID ReservedForNtRpc; /* F1Ch */
PVOID DbgSsReserved[0x2]; /* F20h */
ULONG HardErrorDisabled; /* F28h */
PVOID Instrumentation[]; /* F2Ch */
PVOID SubProcessTag; /* F64h */
PVOID EtwTraceData; /* F68h */
PVOID WinSockData; /* F6Ch */
ULONG GdiBatchCount; /* F70h */
BOOLEAN InDbgPrint; /* F74h */
BOOLEAN FreeStackOnTermination; /* F75h */
BOOLEAN HasFiberData; /* F76h */
UCHAR IdealProcessor; /* F77h */
ULONG GuaranteedStackBytes; /* F78h */
PVOID ReservedForPerf; /* F7Ch */
PVOID ReservedForOle; /* F80h */
ULONG WaitingOnLoaderLock; /* F84h */
ULONG SparePointer1; /* F88h */
ULONG SoftPatchPtr1; /* F8Ch */
ULONG SoftPatchPtr2; /* F90h */
PVOID *TlsExpansionSlots; /* F94h */
ULONG ImpersionationLocale; /* F98h */
ULONG IsImpersonating; /* F9Ch */
PVOID NlsCache; /* FA0h */
PVOID pShimData; /* FA4h */
ULONG HeapVirualAffinity; /* FA8h */
PVOID CurrentTransactionHandle; /* FACh */
PTEB_ACTIVE_FRAME ActiveFrame; /* FB0h */
PVOID FlsData; /* FB4h */
UCHAR SafeThunkCall; /* FB8h */
UCHAR BooleanSpare[]; /* FB9h */
} TEB, *PTEB;

PEB结构

 typedef struct _PEB
{
UCHAR InheritedAddressSpace; // 00h
UCHAR ReadImageFileExecOptions; // 01h
UCHAR BeingDebugged; // 02h
UCHAR Spare; // 03h
PVOID Mutant; // 04h
PVOID ImageBaseAddress; // 08h
PPEB_LDR_DATA Ldr; // 0Ch
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
PVOID SubSystemData; // 14h
PVOID ProcessHeap; // 18h
PVOID FastPebLock; // 1Ch
PPEBLOCKROUTINE FastPebLockRoutine; // 20h
PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
ULONG EnvironmentUpdateCount; // 28h
PVOID* KernelCallbackTable; // 2Ch
PVOID EventLogSection; // 30h
PVOID EventLog; // 34h
PPEB_FREE_BLOCK FreeList; // 38h
ULONG TlsExpansionCounter; // 3Ch
PVOID TlsBitmap; // 40h
ULONG TlsBitmapBits[0x2]; // 44h
PVOID ReadOnlySharedMemoryBase; // 4Ch
PVOID ReadOnlySharedMemoryHeap; // 50h
PVOID* ReadOnlyStaticServerData; // 54h
PVOID AnsiCodePageData; // 58h
PVOID OemCodePageData; // 5Ch
PVOID UnicodeCaseTableData; // 60h
ULONG NumberOfProcessors; // 64h
ULONG NtGlobalFlag; // 68h
UCHAR Spare2[0x4]; // 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG HeapSegmentReserve; // 78h
ULONG HeapSegmentCommit; // 7Ch
ULONG HeapDeCommitTotalFreeThreshold; // 80h
ULONG HeapDeCommitFreeBlockThreshold; // 84h
ULONG NumberOfHeaps; // 88h
ULONG MaximumNumberOfHeaps; // 8Ch
PVOID** ProcessHeaps; // 90h
PVOID GdiSharedHandleTable; // 94h
PVOID ProcessStarterHelper; // 98h
PVOID GdiDCAttributeList; // 9Ch
PVOID LoaderLock; // A0h
ULONG OSMajorVersion; // A4h
ULONG OSMinorVersion; // A8h
ULONG OSBuildNumber; // ACh
ULONG OSPlatformId; // B0h
ULONG ImageSubSystem; // B4h
ULONG ImageSubSystemMajorVersion; // B8h
ULONG ImageSubSystemMinorVersion; // C0h
ULONG GdiHandleBuffer[0x22]; // C4h
PVOID ProcessWindowStation; // ???
} 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) 确实有 非法访问的 异常

 #include "stdafx.h"
#include <stdio.h> int main()
{ _asm { jmp Start }
int ieax; _asm{
Start: GetKernelBase: ;查找 kernel地址
mov eax,7c800000h ;因为有非法访问我直接把我本机的kerne32.dll地址(7c800000h) 给eax 就可以了 Compare:
cmp eax,80000000h
jl SearchFinal
cmp word ptr[eax],'ZM'
je FindedKernelBase
add eax,010000h
jmp Compare }
FindedKernelBase:
{
_asm{ mov ieax,eax}
printf("kernel addr offset %x \n",ieax);
return ;
}
SearchFinal :
{ //;查找结束
printf("find kernel faild \n ");
return ;
}
return ;
}

我刚开始按照老罗的思路,从栈顶向下搜索,有问题

后来我就从8000000h搜索至70000000h处

发现有非法访问

为了确定我的发现

我从从7000000h搜索至80000000h处

还是有非法访问

那我直接把我电脑的kernel32.dll地址 替换 7000000h 为7c800000h

直接可以了

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

第二中办法 (SEH)异常处理链表搜索法

其中要先补习下基础知识

异常处理链表末端的处理结构体是系统最后为异常准备的处理(其中的下一个结构指针prev 为-1),就是咱们经常遇到的程序崩溃的提示。其地址是在kernel32 内存空间内部,我们只要找到最后的异常处理结构体,那么我们从

这个地址找下去一定能找到 ‘MZ’标志(kernel32的地址);

其中SEH链表位置:fs:[0]->线程信息块TIB,TIB.ExceptionList->SEH链表

 nt!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD ;SEH链表头
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB
 链表节点

 _EXCEPTION_REGISTRATION struc

     prev           dd ?                   ;下一个_EXCEPTION_REGISTRATION结构

     handler       dd ?                   ;异常处理函数地址

 _EXCEPTION_REGISTRATION ends
 #include <stdio.h>
#include <windows.h>
int main()
{
__asm
{ mov edx, fs:[] // 获得EXCEPTION_REGISTRATION结构地址
Next:
inc dword ptr [edx] // 将prev+1,如果是-1经过+1后等于0 (证明找到了 SEH链表的最后一项,也就达到了kernel的内存空间中了)
                  // 其中第一次时:fs:[0]->线程信息块TIB,TIB.ExceptionList->SEH链表
jz Krnl
dec dword ptr [edx] // 不为-1,还原
mov edx, [edx] // 获得prev指向的地址
jmp Next Krnl:
dec dword ptr [edx] // 恢复
mov edx, [edx + ] // 获得handle指向的地址 Looop:
cmp word ptr [edx], 'ZM'
jz IsPe
dec edx
xor dx, dx
jmp Looop IsPe:
mov eax, dword ptr [edx + 3ch]
cmp word ptr [edx + eax], 'EP'
jnz Next
mov dwKrnlAddr, edx
}
printf(TEXT("Kernel32.dll address: %x\r\n"), dwKrnlAddr);
printf(TEXT("GetModuleHandle Kernel32.dll address: %x\r\n"),
GetModuleHandle(TEXT("kernel32.dll")));
printf(TEXT("LoadLibrary Kernel32.dll address: %x\r\n"),
LoadLibrary(TEXT("kernel32.dll")));
return ;
}

          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 = ;
DWORD pGetProcAddress = ;
DWORD pKernel32 =; 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*]
add esi,ebp ;依次找每个函数名称
;GetProcAddress
mov eax,0x50746547
cmp [esi], eax; 'PteG'
jne search
mov eax,0x41636f72
cmp [esi+],eax; 'Acor'
jne search
;如果是GetProcA,表示找到了
mov ebx,[edx+24h]
add ebx,ebp ;ebx = 序号数组地址,AddressOf
mov cx,[ebx+ecx*] ;ecx = 计算出的序号值
mov ebx,[edx+1Ch]
add ebx,ebp ;ebx=函数地址的起始位置,AddressOfFunction
mov eax,[ebx+ecx*]
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基址的方法的更多相关文章

  1. [转]SQL三种获取自增长的ID方法

     最新更新请访问: http://denghejun.github.io   SQL SERVER中的三种获得自增长ID的方法  这个功能比较常用,所以记下来以防自己忘掉. SCOPE_IDENTIT ...

  2. Request三种获取数据的方式

    今天在做ajax请求后台代码时,发现ajax的方法都对,但就是请求不了后台代码,后来在同事帮助下才发现前台定义了两个相同参数导致请求出错. 下面记录一下request三种获取数据的方式: 1. Req ...

  3. Js之Dom学习-三种获取页面元素的方式、事件、innerText和innerHTML的异同

    一.三种获取页面元素的方式: getElementById:通过id来获取 <body> <input type="text" value="请输入一个 ...

  4. 三种dedecms调用相关文章的方法

    在文章的末尾或侧边栏添加相关文章可以提高用户的黏度,提高pv,增加se的好印象(哈哈),那么dedecms如何调用相关文章呢?有三种方法可以实现. 第一种dedecms调用相关文章的方法,用默认的li ...

  5. MySQL、SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法

    在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应 ...

  6. 三种计算c#程序运行时间的方法

    三种计算c#程序运行时间的方法 第一种: 利用 System.DateTime.Now // example1: System.DateTime.Now method DateTime dt1 = S ...

  7. 三种将list转换为map的方法(传统方法、jdk8 Stream流、guava)

    三种将list转换为map的方法 - jackyrong - ITeye博客:http://jackyrong.iteye.com/blog/2158009

  8. jQuery 的三种获取值的方式

    本节内容主要介绍的是如何使用jQuery中的.html(),.text()和.val()三种方法,用于读取,修改元素的html结构,元素的文本内容,以及表单元素的value值的方法.jQuery中为我 ...

  9. jQuery ui autocomplete下拉列表样式失效解决,三种获取数据源方式,

    jQuery有很多很多的已经实现,很漂亮的插件,autocomplete就是其中之一.jQuery ui autocomplete主要支持字符串Array.JSON两种数据格式,jQuery ui b ...

随机推荐

  1. 动态修改 NodeJS 程序中的变量值

    如果一个 NodeJS 进程正在运行,有办法修改程序中的变量值么?答案是:通过 V8 的 Debugger 接口可以!本文将详细介绍实现步骤. 启动一个 HTTP Server 用简单的 Hello ...

  2. poj 3686

    The Windy's Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3791   Accepted: 1631 Descr ...

  3. CSS Animatie是一款在线制作CSS3动画的工具,可以在线直接制作CSS3动画效果,生成代码

    CSS Animatie是一款在线制作CSS3动画的工具,可以在线直接制作CSS3动画效果,生成代码 CSS Animatie 彩蛋爆料直击现场 CSS Animatie是一款在线制作CSS3动画的工 ...

  4. POJ 1573

    #include<iostream> #include<stdio.h> #define MAXN 15 using namespace std; char _m[MAXN][ ...

  5. 无线Ad-hoc网络的关键技术之路由(转)

    无线Ad-hoc网络的关键技术之路由http://network.51cto.com/art/201003/189719.htm

  6. hdu 4726 Kia's Calculation

    思路:刚开始想复杂了. 看解题报告后才知道这题挺简单的,看来还是要多训练啊!!! 单独处理首位的数字,不能为0.其他的就好处理了,从大到小依次找下去就可以了…… 代码如下: #include<i ...

  7. C Primer Plus之存储类、链接和内存管理

    存储时期即生存周期——变量在内存中保留的时间 变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量. 注意:生存期和作用域是两个不同的概念. 作用域    作用域描述了程序中可以访问一个 ...

  8. 两台笔记本搭建openvswitch网络

    环境说明: 笔记本A.B均运行Ubuntu 14.04,两台笔记本通过无线网卡上网,用一根网线连接两台笔记本的有线网卡. 网络拓扑: 其中,vm1 vm2 S1位于笔记本A,vm3 vm4 S2位于笔 ...

  9. converntion

    One convention that we have is to use the names of fruits and vegetables for variables(only in small ...

  10. 使用yum安装CDH Hadoop集群

    使用yum安装CDH Hadoop集群 2013.04.06 Update: 2014.07.21 添加 lzo 的安装 2014.05.20 修改cdh4为cdh5进行安装. 2014.10.22  ...