« IceSword1.22的一个小BUG上周回学校了 »

64位下好神奇啊

近期可能会有一个64位平台的驱动开发任务,找了些资料,对64位平台下的驱动开发略知一二了,好神奇。

一。在64位系统下,有一项PatchGuard技术,它是微软为了防止自己的代码被Patch,进而影响系统的稳定性引入的,这项技术会检查以下内容有没有被恶意修改过:

1- SSDT (System Service Descriptor Table) 
2- GDT (Global Descriptor Table) 
3- IDT (Interrupt Descriptor Table) 
4- System images (ntoskrnl.exe, ndis.sys, hal.dll)
5- Processor MSRs (syscall)

如果检测到以上内容被修改过就会出来一个BSOD。

二。在64位系统,内核函数开头地址的低四位一般是0,形如:xxxxxxxx`xxxxxxx0,这一特征在SSDT表中有很强大的引用,SSDT表在64位系统于32位系统有较大的差别。以下是在64位系统下的KeServiceDescriptorTable:

kd> dp KeServiceDescriptorTable
fffff800`0117bb80  fffff800`01076e00 00000000`00000000
fffff800`0117bb90  00000000`00000128 00000000`00000000

表的第二项于第四项都为0,这两项在32位系统下分别对应ServiceCounterTableBase与ParamTableBase。SSDT表还是同32位系统每4字节表示一项,由于函数的起始地址最低四位都是0,所以微软将SSDT中的低四位用来记录这个函数有多少个参数。并且由于表的每一项都为四个字节,保存的就不可能是绝对地址,而是相对KeServiceDescriptorTable表的地址。所以地址计算方法如下:

FuncAddr=([KeServiceDescriptortable+index*4]+KeServiceDescriptortable)&0xFFFFFFF0

三。在64位系统上,函数调用不再像32位系统下,先是一堆push,然后一个call。在64位系统上,参数不是通过堆栈传递的,而是寄存器与rdi。这样大大减少了被溢出的可能性,提高了安全性。

如果函数少于等于四个参数,那么直接使用寄存器,第一个参数保存在rcx,第二个参数rdx,第三个r8寄存器,第四个r9寄存器,

如果函数有大于四个参数,那么就将第五个开始的参数保护在rdi指向的内存空间,第5个参数保存在rdi+8,第6参数保存在rdi+0x10,第7个rdi+0x18.......

  • quote 1.aaa
  • 大哥,转东西也要点专业。你这个描述的,是在是没有几个人能看的明白。
    langouster 于 2010-01-03 23:12:30 回复
    你去网上找找看,哪篇文章跟这篇一样。我BLOG上的东西除了一开始有几篇网上转的,其它都是原创。即使网上有跟我一样的,也是别人转我的,我转别人的都写了转的。
    至于看不看的懂,该懂的人一看就懂。
  • 2010-1-1 22:47:04 回复该留言
  • quote 2.rap
  • 这是啥系统,和我看的WinSvr R2 x64怎么不一样
  • 2010-1-19 10:14:13 回复该留言
  • quote 3.alpha
  • 在x64下,servicetable没有输出,那么怎么得到这个值呢?
    langouster 于 2010-02-20 14:00:58 回复
    1.读nt文件
    2.在线程_KTHREAD中找到ServiceTable,要判断下是不是在nt模块里,因为有可能被Hook.
  • 2010-2-18 18:19:19 回复该留言
  • quote 4.alpha
  • 读nt文件是不是指根据特征码找?或者先静态根据symbol来找到特定版本的ntoskrnl.exe的位置然后hardcode编码?

    从KTHREAD里面找看上去是个不错的思路。不过我困惑的是你后面半句话,在x64下应该没人绕过patch guard来patch ssdt?而x86下,这个值本来就是exported,所以也不存在这个问题?
    langouster 于 2010-02-20 22:08:23 回复
    读文件就是这个意思,KTHREAD里能不能HOOK我也没有测试过,最好还是检查一下,在x86下这个值是有可能被Hook的,更要检查一下。
  • 2010-2-20 14:41:20 回复该留言
  • quote 5.alpha
  • ms的文档说不能修改ssdt,也不能修改ntoskrnl.exe的内存。我没尝试过修改ssdt,但我尝试过用类似detour的方式修改输出函数,例如ZwReadFile,会crash。

    我关注x64 ssdt的原始目的并非要hook ssdt,而是为了call某些unexport的函数。不过这个目的现在也并不明显了。因为我发现直接根据func_index在内存中找更简单(既然所有的Zw函数体的代码除了index之外完全一样,所以可以通过一个输出函数找到函数体)。而且得到的是ZwXxx,而不是ssdt中的NtXxx,连手工设置previousmode都不需要了。在x86下不能直接找内存是因为x86下detour ZwXxx的代码太多了。Zw的函数体很可能已经被patch。

    KTHREAD的数据结构看来也是个麻烦,在不同os下变化相当大,甚至不能保证下一个sp就不会改变。手工去通过symbol分析所有os的KTHREAD是个体力活,同时也不能保证下一个sp的兼容性。

    另外,x64的patch guard是个好东西,2个driver先后hook ssdt并且如果先后unload通常是灾难之源,代码不工作还是小事,常常crash,而且还不容易发现问题。这问题根本就没办法解决。
  • 2010-2-21 09:19:14 回复该留言
  • quote 6.alpha
  • 另外,你也许应该在blog上说明一下x64的ServiceTable的数据结构
    很显然应该是:
    ServiceTable
    {
    PULONG Base;
    PULONG CounterTableBase;
    SIZE_T NumberOfService;
    PBYTE ParamTableBase;
    }
    这个结构应该是x64/x86兼容了。
    x64下
    func = (PBYTE)Base+Base[index];
    func &= 0xfffffffffffffff0;
  • 2010-2-21 09:25:46 回复该留言
  • quote 7.alpha
  • 经核实,win7的KTHREAD没有servicetable字段。
    这个字段原来用于kifastentry时使用的。这个函数在x64下不存在。x64下用的方法和x86不同。
    langouster 于 2010-02-21 21:37:20 回复
    刚才看了一下Win7 x64位,确实是没有ServiceTable这一项了,而且引用SSDT的地方只有KiSystemServiceRepeat,定位这个地方也不容易,所以通过文件读原始与通过KTHREAD查找都不容易了。
    你所说的func_index法调用NT函数确实非常强大,不知道我理解的有没有问题.
    1.比如要调NtCreateProcessEx,临到它的Index,假设是100.
    2.任意找一个导出的Zwxxx函数,比如ZwQuerySystemInformation,复制下这个函数体,然后修改这个函数体中index的值为100,再处理相对地址,
    3.直接call复制下来经过修改的函数体。
    不知道我的理解是不是有错误,这个方法确实是非常强大,学习了。
  • 2010-2-21 16:12:01 回复该留言
  • quote 8.alpha
  • 不必这么麻烦。unexport的函数也在内存里,只不过pe export table里没有罢了。而且这些函数都是连续存在于内存中的。所以假设要调用index=100的func,而zwqueryinfo_index=90,那么从zwqueryinfo开始往下找即可。反之向上找,就找mov eax,100。
    每个zw的函数体都很小,而且都是固定代码,除了index不同之外完全相同。注意函数体代码是align 10h的。

    这个方法在x86下有隐患。主要是可能已经被其他driver patch过了。第一条指令可能已经改成了jmp xxxx,也可能后面的指令被改了,以至于mov eax,100都被拷贝到另一个遥远的模块了,谁知道其他人会怎么patch? 我相信变态无极限的。

    在x64下没问题,因为既然没人可以改ntoskrnl.exe的内存。

    x86下既然servicetable是输出的,所以也不需要用这个方法。直接用ssdt的NtXxx需要手工设置priviousmode,不过也不需要去用undoc的kthread->priviousmode=kernelmode。建议通过getpriviousmode这个输出函数找偏移量。
    另一个方法是直接用asm int 2eh。int 2e虽然在xp之后系统就不用了,但依然可以用。int 2eh的方法从2k到win7都是可用的。int 2e的方法不需要设置primode。
    langouster 于 2010-02-22 10:06:22 回复
    感谢alpha兄,学习了,这个方法确实非常强大,不知能否留个联系方式,方便以后进一步交流。
  • 2010-2-22 01:32:10 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Powered By Z-Blog 2.0 bate Build Theme by toboku

Copyright langouster. Some Rights Reserved.   苏ICP备06046736号