kernel32基地址获得学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题

第一种方法

通过线程初始化时, 获得esp堆栈指针中的ExitThread函数的地址,然后通过搜索获得kernel32.dll的基地址。

线程在被初始化的时,其堆栈指针指向ExitThread函数的地址,windows这样做是为了通过ret返回时来调用ExitThread地址。所以一般我们可以在我们主线程的起始位置(也就是 程序入口点处)通过获得堆栈指针中ExitThread函数(当然你要想创建一个线程时候获得也可以)

我们直接通过 mov  edx, [esp] ;获得堆栈指针中ExitThread地址到edx寄存器。因为ExitThread地址在kernel32.dll空间中,所以我们可以通过它往上搜索来获得基地址。

分析过kernel32.dll的朋友应该都知道kernel32.dll的块对齐值是00001000h, 并且一般DLL以1M为边界,所以我们可以通过10000h (64k) 作为跨度,这样可以增加搜索速度。我们如何确定这个地址是基地址,我们都知道我们判断这个地址的前两个字节是否是"MZ",然后定位到PE头结构,然后判断是否是"PE",如果这两个都符合的话则表示我们的地址则是基地址了.

注意,程序要用汇编编写,至少我不会用VC来内联编写,应为这个方法需要在程序的一开始就获取,而用VC编写的会有启动函数,也就是mainCRTStartup这些

代码如下:

	.386
	.model flat, stdcall
	option casemap:none

include windows.inc
include kernel32.inc
include user32.inc
includelib user32.lib
includelib kernel32.lib

	.const
szFormat	db ‘kernel32.dll address is: %x‘, 0dh, 0ah, 0
szFormat1	db ‘LoadLibrary kernel32.dll address is: %x‘, 0dh, 0ah, 0
szKrnl32	db ‘kernel32.dll‘, 0

	.data?
hStdOut		dd ?
dwWriteBytes	dd ?
szBuffer	db 1024 (?)

	.code

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取字符串长度
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetStrLen proc _lpString

	mov edi, _lpString
	or ecx, 0ffffffffh	; ecx初值等于-1
	xor eax, eax
	repnz scasb		; 循环扫描,每循环执行一次,ecx-1,直到出现0为止,0也会减1
	; 一个公式(摘自《C++反汇编与逆向分析技术揭秘》P183):
	; ecx(终值) = ecx(初值) - (Len + 1)
	; ecx(终值) = -1 - (Len + 1) = -(Len + 2)    : 将-1代入公式
	; neg(ecx(终值)) = Len + 2    : neg为求补,ecx(终值) = -(Len + 2),求补后为正Len + 2
	; not(ecx(终值)) + 1 = Len + 2
	; not(ecx(终值)) = Len + 1
    ; Len = not(ecx(终值)) - 1
    not ecx
	dec ecx
	mov eax, ecx
	ret

GetStrLen endp 

start:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取kernel32的基址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	mov edx, [esp]
Next:
	cmp word ptr [edx], ‘ZM‘       ; 因为读取后edx的内容是高地址在高位,所以需要倒过来写MZ和PE
	jz IsPe
	dec edx
	xor dx, dx
	jmp Next

IsPe:
	mov eax, [edx + 3ch]
	cmp word ptr [edx + eax], ‘EP‘
	jnz Next

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 将自己获取的和LoadLibrary获取的地址显示出来用于对比
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	invoke GetStdHandle, STD_OUTPUT_HANDLE
	mov hStdOut, eax

	invoke wsprintf, addr szBuffer, addr szFormat, edx
	invoke GetStrLen, addr szBuffer
	invoke WriteConsole, hStdOut, addr szBuffer, ecx, addr dwWriteBytes, NULL

	invoke LoadLibrary, addr szKrnl32
	invoke wsprintf, addr szBuffer, addr szFormat1, eax
	invoke GetStrLen, addr szBuffer
	invoke WriteConsole, hStdOut, addr szBuffer, eax, addr dwWriteBytes, NULL

	invoke CloseHandle, hStdOut
	invoke ExitProcess, NULL

end start

第二种方法:

Note:这个方法适用于XP, win7上获得是ntdll.dll的地址。

这个方法是遍历遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。 搜索的方法在上面我已经说了,通过减去跨度,然后判断地址前两字节是否是"MZ",是的话,继续定位到PE头结构,然后判断前两个字节是否是"PE",不是的话继续减去跨度搜索。直到是为止。
  struct EXCEPTION_REGISTRATION          prev dd ?                                handler dd ?                      ends 
  遍历的方法也很简单,我们都知道[fs:0]的ExceptionList 指向EXCEPTION_REGISTRATION结构,所以通过[fs:0]获得EXCEPTION_REGISTRATION结构后,判断prev成员是否是-1,如果是的话则取
异常处理过程地址,然后进行搜索。

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwKrnlAddr = 0;

	__asm
	{
		mov edx, fs:[0]		// 获得EXCEPTION_REGISTRATION结构地址
Next:
		inc dword ptr [edx]	// 将prev+1,如果是-1为0
		jz Krnl
		dec dword ptr [edx]	// 不为-1,还原
		mov edx, [edx]		// 获得prev指向的地址
		jmp Next

Krnl:
		dec dword ptr [edx]	// 恢复
		mov edx, [edx + 4]	// 获得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
	}
	_tprintf(TEXT("Kernel32.dll address: %x\r\n"), dwKrnlAddr);
	_tprintf(TEXT("GetModuleHandle Kernel32.dll address: %x\r\n"),
		GetModuleHandle(TEXT("kernel32.dll")));

	return 0;
}

第三种方法:

此方法是通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA结构地址,然后遍历模块列表,查找kernel32.dll模块的基地址。
  TEB是线程环境块(Thread Environment Block)结构, 我们的fs段选择子所对应的段指向TEB,也就是fs:[0]指向TEB.那么TEB的ProcessEnvironmentBlock结构成员指 向我们的PEB进程环境块结构(Process Environment Block),然后通过PEB结构来获得PEB_LDR_DATA。 接下来我们通过windbg来查看下相关结构。
我们首先来看下TEB结构,通过windbg的dt命令。
lkd> dt _TEB nt!_TEB    +0x000 NtTib            : _NT_TIB    +0x01c EnvironmentPointer : Ptr32 Void    +0x020 ClientId         : _CLIENT_ID    +0x028 ActiveRpcHandle  : Ptr32 Void    +0x02c ThreadLocalStoragePointer : Ptr32 Void    +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
   ......省略
我们可以看到TEB结构的0x30偏移处存储的我们的PEB结构的地址。。
   然后接下来我们来看PEB结构。
lkd> dt _PEB nt!_PEB    +0x000 InheritedAddressSpace : UChar    +0x001 ReadImageFileExecOptions : UChar    +0x002 BeingDebugged    : UChar    +0x003 SpareBool        : UChar    +0x004 Mutant           : Ptr32 Void    +0x008 ImageBaseAddress : Ptr32 Void    +0x00c Ldr              : Ptr32 _PEB_LDR_DATA    ..........省略
我们可以看到PEB结构的0x0c偏移处存储的我们的_PEB_LDR_DATA结构地址。
然后我们再来查看 _PEB_LDR_DATA结构
lkd> dt _PEB_LDR_DATA nt!_PEB_LDR_DATA    +0x000 Length           : Uint4B    +0x004 Initialized      : UChar    +0x008 SsHandle         : Ptr32 Void    +0x00c InLoadOrderModuleList : _LIST_ENTRY    +0x014 InMemoryOrderModuleList : _LIST_ENTRY    +0x01c InInitializationOrderModuleList : _LIST_ENTRY    +0x024 EntryInProgress  : Ptr32 Void
我们看到这个结构中的模块立标有3个_LIST_ENTRY结构,它们分别是 InLoadOrderModuleList (加载顺序模块列表) InMemoryOrderModuleList(内存顺序模块排列) InInitializationOrderModuleList(初始化顺序模块列表)
然后我们继续查看这个结构。
nt!_LIST_ENTRY    +0x000 Flink            : Ptr32 _LIST_ENTRY    +0x004 Blink            : Ptr32 _LIST_ENTRY    这个结构我们可以看到它是一个双向链表,Flink表示从前往后, Blink表示从后往前。
并且这三个链表的结点是均是指向此结构
typedef struct _LDR_MODULE {     LIST_ENTRY        InLoadOrderModuleList;            // +0x00     LIST_ENTRY        InMemoryOrderModuleList;          // +0x08     LIST_ENTRY        InInitializationOrderModuleList;  // +0x10     PVOID             BaseAddress;                      // +0x18     PVOID             EntryPoint;                       // +0x1c     ULONG             SizeOfImage;                      // +0x20     UNICODE_STRING    FullDllName;                      // +0x24     UNICODE_STRING    BaseDllName;                      // +0x2c     ULONG             Flags;                            // +0x34     SHORT             LoadCount;                        // +0x38     SHORT             TlsIndex;                         // +0x3a     LIST_ENTRY        HashTableEntry;                   // +0x3c     ULONG             TimeDateStamp;                    // +0x44                                                         // +0x48 } LDR_MODULE, *PLDR_MODULE;
我们一般取它的初始化顺序结构(InInitializationOrderModuleList)的Flink成员指向的_LDR_MODULE结构的BaseAddress成员则为我们需要的基地址,当然由于第一个是 ntdll,所以取第二个则为我们的Kernel32.dll。

注意,这里在Win7中为第3个, 第2个是kernelbase.dll,可以通过LDR_MODULE结构的BaseDllName比较dll名称后来获取,参见:

windows7平台下定位kernel32基址的方法

windows 7下的病毒感染

  1. #include <stdio.h>
  2. #include <tchar.h>
  3. #include <windows.h>
  4. int _tmain(int argc, _TCHAR* argv[])
  5. {
  6. DWORD dwKrnlAddr = 0;
  7. __asm
  8. {
  9. mov edx, fs:[30h]       // 取得PEB
  10. mov edx, [edx + 0ch]    // PEB_LDR_DATA
  11. mov edx, [edx + 1ch]    // 获得InInitializationOrderModuleList.Flink指向的地址,也即指向第一个LDR_MODULE
  12. mov edx, [edx]      // 因为edx当前为InInitializationOrderModuleList
  13. // 而它的第一成员Flink又指向下一个LDR_MODULE, 所以直接读取就是下一个
  14. mov edx, [edx + 8h]
  15. mov dwKrnlAddr, edx
  16. }
  17. _tprintf(TEXT("Kernel32.dll address: %x\r\n"), dwKrnlAddr);
  18. _tprintf(TEXT("GetModuleHandle Kernel32.dll address: %x\r\n"),
  19. GetModuleHandle(TEXT("kernel32.dll")));
  20. return 0;
  21. }
时间: 2024-10-09 09:03:21

kernel32基地址获得学习笔记的相关文章

hash扫描获得api函数地址学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题 搜索获得api函数地址的实现 我们的程序能正常的调用函数.那么这个动态链接库是如何输出函数来供我们的用户程序调用呢?它实际上是采用输出表结构来描述本dll需要导出哪些函数来供其他的程序调用,这样其他的用户程序才能正常的调用此动态链接库的输出函数. 导出表结构: IMAGE_EXPORT_DIRECTORY struct Characteristics D

搜寻节空隙感染学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题 那么搜寻节空隙感染,最重要的就是找到我们节中存在的空隙.一般在病毒技术中,有两种方法. 循环读取节表,然后分别在每个节中搜寻00机器码(因为默认编译器是用00机器码填充的),如果此00机器码区域的大小大于病毒的体积.则取这段区域的偏移. 循环读取节表,通过节表结构中的物理文件大小 - 节映射大小 取得 节后面的物理空隙,然后判断此段空隙大小是否大于我们病

扩展节形式感染学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题 今天我们的感染方式是扩展末尾节,因为它很简单.稳定.快捷.那么扩展末尾节顾名思义就是针对被感染对象的最后一个节的扩展.将尾部节的大小扩充,然后将我们的病毒代码Write进去,修改若干的PE结构成员. 知道这些,你肯定会问修改哪些若干成员,为了给大家更直白的感觉,下面我列出了感染中需要修改的结构成员. 1.  SizeOfImage        50h

【转】MongoDB学习笔记(查询)

原文地址 MongoDB学习笔记(查询) 基本查询: 构造查询数据. > db.test.findOne() { "_id" : ObjectId("4fd58ecbb9ac507e96276f1a"), "name" : "stephen", "age" : 35, "genda" : "male", "email" : "[em

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承、虚继承与虚基类

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承.虚继承与虚基类 一.多重继承 在C++语言中,一个派生类可以从一个基类派生,称为单继承:也可以从多个基类派生,称为多继承. 多重继承--一个派生类可以有多个基类 class <派生类名> : <继承方式1> <基类名1>,<继承方式2> <基类名2>,... { <派生类新定义成员> }; 可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr)、C++对象模型

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr).C++对象模型 一.虚函数表指针(vptr)及虚基类表指针(bptr) C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括: virtual function机制:用以支持一个有效率的"执行期绑定": virtual base class:用以实现多次在继承体系中的基类,有一个单一而被共享的实体. 1.虚函数表指针 C++中,有两种数据

qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)

原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516201 qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等) 本学章节笔记主要详解Item元素(上半场主要涉及anchors锚),因为所有可视化的界面元素都继承于Item,熟悉Item后,不同的继承子类,有其定制的属性(从几个到几十个不等). <Qt实用技巧:在Qt Gui程

C++ Primer 学习笔记_66_面向对象编程 --定义基类和派生类[续]

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

C++ Primer 学习笔记_65_面向对象编程 --概述、定义基类和派生类

面向对象编程 --概述.定义基类和派生类 引言: 面向对象编程基于的三个基本概念:数据抽象.继承和动态绑定. 在C++中,用类进行数据抽象,用类派生从一个类继承另一个:派生类继承基类的成员.动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数. 继承和动态绑定在两个方面简化了我们的程序:[继承]能够容易地定义与其他类相似但又不相同的新类,[派生]能够更容易地编写忽略这些相似类型之间区别的程序. 面向对象编程:概述 面向对象编程的关键思想是多态性(polymorphism)