首页
社区
课程
招聘
[原创]重载内核全程分析笔记
发表于: 2013-8-20 20:19 101261

[原创]重载内核全程分析笔记

2013-8-20 20:19
101261

还记得七夕的那几天,老V率先把AGP的源码发布出来,然后是EasyDebugger的源码出土,后面陆续有很多大牛把珍藏已久的代码拿出来晒太阳,那段疯狂的日子,让看雪论坛都面临崩溃的边缘。折腾了大半天,终于把代码都下载下来了,可是下载下来做什么呢,自己连看都看不懂。
于是,带着激动而又郁闷的心情继续学习内核编程。
由于是初学者,很多时候也有不清楚的地方,若下文中有什么地方理解有误,还请大牛多多指正,
文中所写代码参考自      
看雪论坛大牛  和  梦织未来论坛大牛  
好了,进入正题吧,继续看我的废话。

重载内核内容:
1、        将内核文件加载到内存
2、        进行基址重定位
3、        重定位ssdt结构
4、        Hook KiFastCallEntry,让RING3进程调用走新内核

下面一步一步的进行分析
1、        加载内核文件
我们要加载哪一个文件呢?答案是:ntkrnlpa.exe,我测试的系统的XP sp3,该文件所在目录为C:\WINDOWS\system32\ntkrnlpa.exe。加载文件要一部分一部分的加载,因为硬盘上的文件对齐方式与在内存中文件的对齐方式不同,所以如果你直接申请一块内存,然后把整个磁盘文件加载进去,那肯定是不对的,我们要按照PE结构一块块的进行加载。具体加载顺序是:IMAGE_DOS_HEADER,IMAGE_NT_HEADER,IMAGE_SECTION_HEADER,最后是把各区段的内容加载进去。加载过程中用到的内核函数有:打开文件:ZwCreateFile,初始化ZwCreateFile中的一个参数:IninializeObjectAttributes,读取文件内容:ZwReadFile,分配内存空间:ExAllocatePool,释放内存空间:ExFreePool,关闭句柄:ZwClose。
由于这段代码比较简单,又有很多地方是做重复性的工作,这里就不贴上来讲解了,具体请大家下载源码进行分析吧。如有不懂的请回帖交流。
这段代码写完了,有什么用呢?加载PE文件这事,不像写helloworld,写Helloworld,写完了一运行,至少能看到一句话吧,可是LoadPe这事,在哪去看效果呢?如果代码中有错误,我们怎么知道有没有正确运行,光凭代码中的Kdprint打印信息是不够的。既然是内核程序,我们就可以通过windbg来查看,方法是:
随便找一个不是ssdt的函数(ssdt后面再说),如:IoCreateFile,打开windbg,输入lkd>u IoCreateFile l a,会看到一段汇编代码,获取第一句汇编代码的地址,如我这里是0x8056cc7e,再用xuetr看到的模块加载地址是0x804D8000,偏移=0x8056cc7e-0x804D8000=0x94C7E;再将debugview上打印出来的我们新加载的内核的首地址找到,加上这个偏移,我得到的是0x85BB0000,用windbg查看 lkd>u 85C44C7E l a
效果如图:

如果看到与图中相似的画面,也就是说和原来内核的代码几乎一样,那就说明加载内核文件成功了。
为什么说几乎一样呢?请看图中用红色线框标记的部分,原来的内核直接能够识别出一个全局变量名,而我们新载入的内核却只能看到一个数字。这是什么原因呢?要解决这个问题,就需要进行重载内核第二步,基址重定位。
2、        基址重定位
基址重定位的目的我们已经知道了,那具体该怎么做呢?微软在PE结构中已经有一个表定义了需要重定位的一些数据,就是重定位表,我们只需要修改里面的数据就可以了。现在的问题是如何修改。我们修改的目的是让新内核使用到正确的数据,来看看重定位表的数据结构:
typedef struct _IMAGE_BASE_RELOCATION {
    ULONG   VirtualAddress;
    ULONG   SizeOfBlock;
        USHORT  TypeOffset[1];
} IMAGE_BASE_RELOCATION;
第一个数据是相对地址,第二个数据是整个数据结构的大小,第三个数据就是要修改的地方了。
第三个数据是一个USHORT结构,包含两字节,高四位用于表示该数据的属性,我们这里要判断是否等于3,因为只有等于3的数据才是需要重定位的地址。我们将后12位取出来,再与新地址的首地址相加,得到一个实际的地址。得到这个实际地址后,这个地址的数据还并不是想要得到的数据,我们还要加上一个偏移,这个偏移就得用原内核的模块首地址减去imagebase,这样就可以正确定位了。
参考代码如下:

void FixBaseRelocTable(PVOID pNewImage)
{

	//将新内核地址作为一个PE文件头,依次向下,目的是寻找重定位表结构
	pImageDosHeader=(PIMAGE_DOS_HEADER)pNewImage;
	//定位到IMAGE_NT_HEADER
	pImageNtHeader=(PIMAGE_NT_HEADERS)((ULONG)pNewImage+pImageDosHeader->e_lfanew);
	//获取内核文件的imagebase,以便后面做偏移修改。
	OriginalImageBase=pImageNtHeader->OptionalHeader.ImageBase;
	//定位到数据目录
	ImageDataDirectory = pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
	//定位到重定位表结构
	pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(ImageDataDirectory.VirtualAddress + (ULONG)pNewImage);
	if (pImageBaseRelocation==NULL)
	{
		return;  
	}
	while (pImageBaseRelocation->SizeOfBlock)
	{   //计算需要修改的地址的个数
		uRelocTableSize=(pImageBaseRelocation->SizeOfBlock-8)/2;
		//循环遍历
		for (uIndex=0;uIndex<uRelocTableSize;uIndex++)
		{//判断高4位是否等于3
			Type=pImageBaseRelocation->TypeOffset[uIndex]>>12;
			if (Type==3)
			{
				//修改地址,相对地址加上一个新内核地址,使其成为一个实际地址
				uRelocAddress=(ULONG *)((ULONG)(pImageBaseRelocation->TypeOffset[uIndex]&0x0fff)+pImageBaseRelocation->VirtualAddress+(ULONG)pNewImage);
				//再加上内核首地址到imagebase的偏移
				*uRelocAddress=*uRelocAddress+(OrigImage-OriginalImageBase);
			}
		}
		//进行下一个重定位表的修改
		pImageBaseRelocation=(IMAGE_BASE_RELOCATION *)((ULONG)pImageBaseRelocation+pImageBaseRelocation->SizeOfBlock);
	}
}
VOID SetNewSSDT(PVOID pNewImage)
{
	ULONG							uIndex;
	ULONG							uNewKernelInc,uOffset;
	//新内核地址-老内核地址,得到相对偏移
	uNewKernelInc = (ULONG)pNewImage -OrigImage;
	//老内核的ssdt指针加上相对偏移,得到新内核的ssdt指针
	pNewSSDT = (ServiceDescriptorTableEntry_t *)((ULONG)&KeServiceDescriptorTable + uNewKernelInc);

	if (!MmIsAddressValid(pNewSSDT))
	{
		KdPrint(("pNewSSDT is unaviable!"));
		return;
	}
	//由于数量是一个数值,因此不必作相对偏移
	pNewSSDT->NumberOfServices = KeServiceDescriptorTable.NumberOfServices;
	//计算相对函数地址
	uOffset = (ULONG)KeServiceDescriptorTable.ServiceTableBase -OrigImage;
	//得到新的ssdt函数表地址
	pNewSSDT->ServiceTableBase = (unsigned int*)((ULONG)pNewImage + uOffset);
	if (!MmIsAddressValid(pNewSSDT->ServiceTableBase))
	{
		KdPrint(("pNewSSDT->ServiceTableBase: %X",pNewSSDT->ServiceTableBase));
		return;
	}
	//依次遍历
	for (uIndex = 0;uIndex<pNewSSDT->NumberOfServices;uIndex++)
	{//新的函数地址再加上相对加载地址,得到现在的ssdt函数地址
		pNewSSDT->ServiceTableBase[uIndex] += uNewKernelInc;
	}
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (152)
雪    币: 3032
活跃值: (3366)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
2
+1
2013-8-20 20:33
0
雪    币: 279
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不错
2013-8-20 20:44
0
雪    币: 19
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
看不懂和初学者都能分析的这么多,我看我还是别活了
2013-8-20 20:53
0
雪    币: 328
活跃值: (154)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
5
mark~一下,果断学习
2013-8-20 20:57
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
很好~~
2013-8-20 21:11
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
7
呵呵,说笑了,慢慢学也不是那么困难的。
2013-8-20 22:05
0
雪    币: 219
活跃值: (783)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
8
  ~~~
2013-8-20 22:15
0
雪    币: 46
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
老V发布的我怎么没看见 给个链接
2013-8-20 22:21
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
10
不错,不错,lz这是真的看代码了,而不是那些整天坐享其成的。
2013-8-20 22:22
0
雪    币: 135
活跃值: (63)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
学习,mark一下。
2013-8-20 22:52
0
雪    币: 27
活跃值: (127)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
12
很好,向楼主学习.
2013-8-20 22:52
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
13
老V都来了,感到莫大的荣幸
2013-8-20 22:53
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
14
呵呵,估计是后面精华太多,老v的帖子直接被挤下去了,不过大牛的帖子永远都不会沉http://bbs.pediy.com/showthread.php?t=177154
2013-8-20 23:31
0
雪    币: 114
活跃值: (180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
通俗易懂,必须支持楼主~~
2013-8-20 23:33
0
雪    币: 62
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
NB。。。学习。。。。。。。。
2013-8-21 00:02
0
雪    币: 12688
活跃值: (4294)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
这个真的是容易懂了~~一看就明白~~谢谢分享~~
2013-8-21 02:50
0
雪    币: 38
活跃值: (205)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
Mark.....
2013-8-21 04:33
0
雪    币: 43
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
不错 学习了
2013-8-21 07:29
0
雪    币: 133
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
分析的太棒了,非常感谢
2013-8-21 10:07
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
21
大牛见笑了
2013-8-21 12:59
0
雪    币: 219
活跃值: (783)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
22
很不错~ 动动手都是好事情~
2013-8-21 13:06
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
23
LZ很认真,辛苦了
2013-8-21 13:47
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
相对函数地址=老内核的函数基址指针-老内核的起始加载地址,
相对加载地址=新内核加载地址-老内核加载地址
新的Ssdt函数地址=新内核加载地址+相对函数地址+相对加载地址。
最后面为什么还要加上相对加载地址啊?
2013-8-21 14:00
0
雪    币: 253
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
不错的分析,加个优秀吧。。
2013-8-21 14:08
0
游客
登录 | 注册 方可回帖
返回
//