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基址的方法的更多相关文章
-
[转]SQL三种获取自增长的ID方法
最新更新请访问: http://denghejun.github.io SQL SERVER中的三种获得自增长ID的方法 这个功能比较常用,所以记下来以防自己忘掉. SCOPE_IDENTIT ...
-
Request三种获取数据的方式
今天在做ajax请求后台代码时,发现ajax的方法都对,但就是请求不了后台代码,后来在同事帮助下才发现前台定义了两个相同参数导致请求出错. 下面记录一下request三种获取数据的方式: 1. Req ...
-
Js之Dom学习-三种获取页面元素的方式、事件、innerText和innerHTML的异同
一.三种获取页面元素的方式: getElementById:通过id来获取 <body> <input type="text" value="请输入一个 ...
-
三种dedecms调用相关文章的方法
在文章的末尾或侧边栏添加相关文章可以提高用户的黏度,提高pv,增加se的好印象(哈哈),那么dedecms如何调用相关文章呢?有三种方法可以实现. 第一种dedecms调用相关文章的方法,用默认的li ...
-
MySQL、SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法
在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应 ...
-
三种计算c#程序运行时间的方法
三种计算c#程序运行时间的方法 第一种: 利用 System.DateTime.Now // example1: System.DateTime.Now method DateTime dt1 = S ...
-
三种将list转换为map的方法(传统方法、jdk8 Stream流、guava)
三种将list转换为map的方法 - jackyrong - ITeye博客:http://jackyrong.iteye.com/blog/2158009
-
jQuery 的三种获取值的方式
本节内容主要介绍的是如何使用jQuery中的.html(),.text()和.val()三种方法,用于读取,修改元素的html结构,元素的文本内容,以及表单元素的value值的方法.jQuery中为我 ...
-
jQuery ui autocomplete下拉列表样式失效解决,三种获取数据源方式,
jQuery有很多很多的已经实现,很漂亮的插件,autocomplete就是其中之一.jQuery ui autocomplete主要支持字符串Array.JSON两种数据格式,jQuery ui b ...
随机推荐
-
caffe python 接口设置
安装编译完成后, 运行 cd sudogedit ~/.bashrc 在打开的文件末尾加入 export PYTHONPATH=/home/caffe-master/python:$PYTHONPA ...
-
NDK相关以及同步相关博客收集
http://www.cnblogs.com/heiing/archive/2013/01/20/2868268.htmlhttp://blog.sina.com.cn/s/blog_461c24d5 ...
- Web 安全的短板
-
Cheatsheet: 2014 08.01 ~ 08.31
Web Slow Server? This is the Flow Chart You're Looking For A Strolll Through Node: Introduction .NET ...
-
D&;F学数据结构系列——二叉排序树
二叉排序树(Binary Sort Tree) 定义:对于树中的每个结点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值. 二叉查找树声明: #ifndef _ ...
-
python中去掉空行的问题
在python中处理空行时,经常会遇到一些问题.现总结经验如下: 1.遇到的空行如果只有换行符,直接使用=='\n'或者 len(line)==line.count('\n') 2.有多个空格+换行符 ...
-
python Unicode转ascii码的一种方法
缘起 看到这样的数据:Marek Čech.Beniardá怎样变成相对应的ascii码呢 解决 import unicodedata s = u"Marek Čech" #(u表 ...
-
nginx反向代理+tomcat域名绑定
今天在用nginx做反向代理时,由于一个tomcat下有多个应用,因此要在tomcat做域名绑定.tomcat启动后,通过域名+端口是可以访问到页面的,但是通过nginx转发后就不能访问了,因此tom ...
-
C# 语法特性 - 泛型(C#2.0)
泛型将 类型参数 的概念引入了 .NET Framework. 泛型主要有两个优点: 1.编译时可以保证类型安全. 2.不用做类型转换,获得一定的性能提升. 泛型方法.泛型委托.泛型接口 除了泛型类之 ...
-
vue中动态添加div
知识点:vue中动态添加div节点,点击添加,动态生成div,点击删除,删除对应的div,其中数组的长度是动态改变的,如在from表单中应用,直接在提交方法中,获得list,获取所填的元素即可 效果: ...