一、首先准备好一个程序,运行起来,用windbg进行附加调试,由于每个windows下的程序都会加载kernel32.dll,因此,找基址的过程是一样的;
二、查看PEB地址;
法一、r $peb
法二、通过TEB获取,r $teb
获取到teb地址后,对_TEB结构体解析dt _TEB 3ca000
法三、通过fs寄存器获取,我们知道fs:[0]就是TEB结构体的首地址,但是,在windbg里dd fs:[0]时,地址却做了隐藏:
那该怎么办呢,其实,这就要看下TEB的结构了
在TEB结构的0x18偏移处,存放的其实就是TEB的地址,和fs:[0]是一样的;
另外,在TEB结构的0x30偏移处,存放的就是PEB的地址,我们再来看下:
和上面两种方法,得到的结果都是一致的,这也验证了我们的想法;
三、接下来,既然PEB的地址找到了,就对PEB进行解析:
首先找到LDR:
接下来,解析LDR:
这里,也许有人会有疑问:那个_LIST_ENTRY后面,怎么有两个值,是什么含义呢?加个-b,就看出来了:
typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
其实,内核数据结构中,比较常见,使用的这个双向链表;
我们就选取InLoadOrderModuleList这个链;对它的Flink进行解析,
通过查阅MSDN,知道,这个Flink指向的具体的数据结构类型是:_LDR_DATA_TABLE_ENTRY
继续遍历InLoadOrderLinks的Flink字段:
还不是Kernel32.dll,继续走:
到此,通过遍历InLoadOrderLinks链,我们找到了KERNEL32.DLL,取出基址就比较容易了,在0x18偏移处;
取出这个基址,我们就可以解析PE导出表,找到我们需要的函数的地址了;
四、代码
int GetKernel32Base() { int nAddress = 0; _asm { push eax; mov eax, fs:[0x30]; // PEB mov eax, [eax + 0xC] // LDR mov eax, [eax + 0xC] // InLoadOrderModuleList, exe mov eax, [eax]; // nt.dll mov eax, [eax]; // kernel32.dll mov eax, dword ptr ds:[eax + 0x18]; // BaseAddr; mov nAddress, eax; pop eax; } return nAddress; }
附录:
参考MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/aa813708%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396