今天分析win7的一个64位的驱动,其中涉及到了win7 64位SSDT的知识,结果发现和xp系统的完全不一样,感慨自己out的同时,赶紧学习补上。
注:本文是自己的学习总结,如有不正确的地方欢迎拍砖指正。如果没有特殊说明下面说的都是64位的win7。另外,下文的汇编都是我随手写的或者直接从windbg或IDA上拷贝的,不保证用编译器能直接编译通过:不过即使编不过稍微一改应该就可以了。
windows的SSDT是类似于linux下的sys_call_table的东东,在windbg中它的符号名为KiServiceTable,下文也以KiServiceTable称呼之。下面主要说明KiServiceTable基址的获取方法和其格式。
给XP系统写驱动时当我们要用到KiServiceTable时,直接声明一下,然后就可以用了,因为XP系统中的nt导出了这个表。但在win7下这个导出去掉了,所以要想使用KiServiceTable的基址,就得另寻别的方法。网上以及我分析的文件,都是用的读取MSR,然后特征码匹配的方法。
读取MSR的第0xC0000082号寄存器,可以得到KiSystemCall64的基址。什么是KiSystemCall64呢?就是所有系统调用进入内核的第一站,相当于linux的system_call啦,网上有的是资料。要想得到KiServiceTable的基址,第一步就得获取KiSystemCall64的基址,方法如下:
- mov ecx, 0C000082h
- rdmsr
- shl rdx, 20h
- or rax, rdx
KiSystemCall64的地址就保存到了rax寄存器中。
有了KiSystemCall64的基址怎么获取SSDT的基址呢?我们用windbg看一下KiSystemCall64中的代码:
- #以下代码是KiSystemCall64的节选
- nt!KiSystemCall64:
- fffff800`03cbaec0 0f01f8 swapgs
- fffff800`03cbaec3 654889242510000000 mov qword ptr gs:[10h],rsp
- fffff800`03cbaecc 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
- fffff800`03cbaed5 6a2b push 2Bh
- fffff800`03cbaed7 65ff342510000000 push qword ptr gs:[10h]
- fffff800`03cbaedf 4153 push r11
- fffff800`03cbaee1 6a33 push 33h
- fffff800`03cbaee3 51 push rcx
- fffff800`03cbaee4 498bca mov rcx,r10
- fffff800`03cbaee7 4883ec08 sub rsp,8
- fffff800`03cbaeeb 55 push rbp
- fffff800`03cbaeec 4881ec58010000 sub rsp,158h
- fffff800`03cbaef3 488dac2480000000 lea rbp,[rsp+80h]
- ...
- nt!KiSystemServiceStart:
- fffff800`03cbafde 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp
- fffff800`03cbafe5 8bf8 mov edi,eax
- fffff800`03cbafe7 c1ef07 shr edi,7
- fffff800`03cbafea 83e720 and edi,20h
- fffff800`03cbafed 25ff0f0000 and eax,0FFFh
- nt!KiSystemServiceRepeat:
- fffff800`03cbaff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`03ef2840)]
- fffff800`03cbaff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`03ef2880)]
- fffff800`03cbb000 f7830001000080000000 test dword ptr [rbx+100h],80h
- fffff800`03cbb00a 4d0f45d3 cmovne r10,r11
- ...
看到地址fffff800`03cbaff2处的指令了么?那里有个KeServiceDescriptorTable。我们要做的,就是通过特征码匹配到这一句,然后从中计算出KeServiceDescriptorTable的地址就行了。匹配就不用说了,看怎么计算地址吧。
这条指令是RIP相关的指令(我自己起的名词^_^,解释一下:4c是rex前缀,8d是操作码,15是modrm,用来计算的关键是后面四个字节,从15(00 010 101)可以知道这四个字节代表的值其实是相对于下一条指令的地址的偏移,再看不懂就学opcode去~~^_^),所用如果用p代表此指令的地址(即fffff800`03cbaff2),x代表指令长度(即7),用C语言语法表达计算方法为:
base = *( (uint32*)( (char*)p + 3 ) ) + ( (char*)p + x )
实际计算:
*(uint32*)(fffff800`03cbaff2 + 3) --> 0x00237847
0x00237847 + (fffff800`03cbaff2 + 7) --> fffff800`03ef2840
所以KeServiceDescriptorTable的基址为fffff800`03ef2840
有了KeServiceDescriptorTable的基址,要得到KiServiceTable的基址就很简单了:
KiServiceTable = *((int**)KeServiceDescriptorTable)
终于得到KiServiceTable的基址了,事情到此好像结束了,不过我手贱的在windbg中看了一下KiServiceTable中的内容,发现里面根本不是指向系统调用函数基址的指针,而是一些看上去很无聊的数而已。继续网上查找.....
KiServiceTable的格式也和以前不一样了,现在的格式,虽然还是一个个的四字节整数,但再也不是函数指针。要想得到系统调用基址,需要用这些整数计算一下。计算方法如下(index为系统调用的调用号):
base = *( (int32*)((int32*)KiServiceTable + index) ) >> 4 + (char*)KiServiceTable
即对应的整数值右移4位,再加上KiServiceTable的基址(我怎么感觉这里文字表达得更清楚呢...)。
OK,搞定。
阅读(9567) | 评论(1) | 转发(0) |