【梅哥的Ring0湿润插入教程】
第一课Windows内核/驱动编程概述及应用、 商业驱动保护软件原理简单分析
【湿润前言】
随着驱动保护技术的逐步成熟,诸如 络游戏公司等越来越多的商业软件公司开始使用Ring0级保护技术保护自己的产品,以起到反用户级调试、反RootKit、反各类钩子、反各类远程注入等作用。目前,使用驱动保护技术的代表产品主要有上海盛大 络发展有限公司开发的GPK(Game Protect Kit)、深圳市腾讯计算机系统有限公司开发的TenSafe及韩国neople棒子公司开发的nProtect等。显然,进入到Ring0级进行某些操作是应对这些驱动保护技术的较好办法之一,而运行于核心态即Ring0级的Windows设备驱动程序则可以说是进入到Ring0的唯一方法。
微软在其WDK,即所谓Windows Driver Kit的官方文档《Getting Started with the Windows Driver Development Environment》一文中开篇指出:“Even for experienced developers, getting started with Windowsdevice drivers can be difficult. You have a new programming model to learn and a new set of tools for building and debugging”。
正如上文所述,Windows内核编程与Win32编程区别较大,其调试方法也与Win32下的调试方法有极大不同;同时,Ring0级的反汇编与Ring3反汇编环境也有不小的差异;而大量计算机专业从业人员在没有点拨的情况下也难以意识到内核编程化御姐为萝莉、化少年为怪蜀黍的威力(以下省略一万字)。正是出于以上及其他的各种原因,梅哥将在学习Windows内核编程的同时写下本系列的Ring0湿润插入大法,旨在分享Windows内核编程经验、并说明Windows内核编程在绕过/破解商业驱动保护软件中的重要地位和神奇作用!
好了,下面就抛开用户级的桎梏,跟梅哥一起开始深入Ring0的湿润之旅吧~
============================我是湿润的昏割线=============================
【商业驱动保护软件的原理简述】
盛大公司的GPK驱动保护产品目前在其内部及外部的多家公司游戏产品中使用,例如由 易运营的 络游戏《梦幻西游》、由深圳市迅雷 络技术有限公司运营的 络游戏《仙剑神曲》等众多游戏都采用了GPK驱动保护产品。下面梅哥将以由深圳市迅雷 络技术有限公司运营的 络游戏《仙剑神曲》为例分析商业驱动保护软件的原理。
盛大公司的GPK将驱动伪装成1394总线集线器设备驱动在内核中注册,利用相关工具可在内核驱动模块中找到1394hub.sys,如下图所示:
Hook该函数即在某种程度上阻止几种常见三方非法软件,例如:
1)需要使用ExAllocateVirtualMemory在目标进程内部开辟空间的远程线程注入方法等;
2)Cheat Engineer等通过向目标进程空间读取/写入数据的软件;
再如Shadow SSDT中的NtUserPrintWindow函数被Hook,可以实现阻止应用层进程在游戏窗体内进行绘图操作等;除此以外还有防止输入法注入的NtUserSetAppImeLevel等:
用户空间不能访问高端2G的内核空间,同时,当涉及进程调度等时,内核只切换低端2G的虚拟内存,正因为如此才有了 络协议栈中所谓的零拷贝技术。例如,以UDP常用API函数SendTo为例,其发送缓冲区往往使用malloc等运行时函数在用户层2G内的堆空间分配(运行时函数、堆栈区别不知道的请百度),当SendTo最终陷入系统内核时,由于用户态2G空间与内核态2G空间相互独立,所以就需要将用户层的发送缓冲区拷贝至内核空间,这种拷贝在一定程度上导致了性能的降低,因而有人也就提出了所谓的零拷贝技术。
【Win32子系统与内核】
前戏:在深入内核之前,我们首先需要了解NT内核的基本结构。
首先纠正Linux环境、实时操作系统或Anti-Windows背景同学的常见误区(作为一名曾经坚定的Anti-Windows者,梅哥真心只能表示谁人年少不2B啊,哎),即可以在相当程度上认为Ntoskrnl.exe为Windows NT架构操作系统的内核。当然,由于Windows为非典型的微内核结构,故Ntoskrnl.exe实际上包括了操作系统内核及执行体组件两部分组成。在Ntoskrnl.exe以外,用户层功能由诸如Win32、OS/2和POSIX等的子系统DLL的形式提供,这些子系统中的APIs最终通过调用Ntoskrnl.exe提供的系统服务实现。
目前,可以认为Win32子系统为最主要的子系统,其包含的API即应用开发人员常提到的Win32 API,更加具体地讲Win32 API又分为:
1)GDI函数,对应GDI32.DLL,在物理设备上执行绘图操作;
2)USER函数,对应USER32.DLL,管理窗口、菜单、对话框及各类控件等;
3)KERNEL函数,对应KERNEL32.DLL,管理进程、线程、文件、同步等非GUI资源;
在当前的NT内核架构中,上述三个DLL文件均只包含函数导出定义,其实现全部在内核文件Ntdll.dll中,而Ntdll.dll文件中实现的函数即为所谓Native API。Native API一般以Nt开头,例如Win32 API函数OpenProcess在Ntdll.dll中的实现为NtOpenProcess,如图所示(途中是以WriteFile为例的):
我们也可以通过内核函数的反汇编代码看出,如微软为文档化的内核函数ZwQuerySystemInformation函数反汇编如下:
Ntoskrnl.exe中导出了一个KeServiceDescriptorTable变量,其实际是一个指向结构体struct ServiceDescriptorTable的指针,是访问SSDT的关键。在WinDbg中可以直接使用dd KeServiceDescriptorTable命令查看:
kd> dd KeServiceDescriptorTable
8055b220 804e36b8 00000000 0000011c 805110c8
8055b230 00000000 00000000 00000000 00000000
8055b240 00000000 00000000 00000000 00000000
8055b250 00000000 00000000 00000000 00000000
8055b260 00002710 bf80c361 00000000 00000000
8055b270 f8a49a80 82378cc0 822090f0 80700f40
8055b280 00000000 00000000 32d80ecc 0005f5b0
8055b290 587f2d8c 01cca04b 00000000 00000000
而KeServiceDescriptorTable定义为(对于该结构 上有大量错误代码):
typedef struct ServiceDescriptorTable_s
{
PULONG ServiceTableBase; // System Service Dispatch Table的基地址
PULONG ServiceCounterTable(0);
unsigned int NumberOfServices; //由ServiceTableBase描述的服务的数目
PULONG ParamTableBase; //SSPT
}ServiceDescriptorTable,*pServiceDescriptorTable;
extern pServiceDescriptorTable KeServiceDescriptorTable;
其中,ServiceTableBase指向SSDT(System Service Dispatch Table)系统服务分发表的基址,其中每个地址4字节长;ServiceCounterTable用于checked builds,包含着SSDT中每个服务被调用次数的计数器,这个计数器由INT 0x2E或SYSENTER处理程序KiSystemService更新,一般为零;NumberOfServices描述了SSDT中包换的系统服务的个数;ParamTableBase指向 SSPT(System Service Parameter Table)。
通过梅哥以上的介绍,我们已经知道SSDT对应Ntoskrnel.exe为Kernel.dll中包含的API服务;除此以外,还有所谓的Shadow SSDT,对应KeServiceDescriptorTableShadow指针,它与GDI相关API有关,具体对应Win32k.sys。本系列教程以后会对Shadow SSDT做详细说明。
我们可以直接在WinDbg中查看KeServiceDescriptorTable(符 表中已包含):
kd> dd KeServiceDescriptorTable
8055b220 804e36b8 00000000 0000011c 805110c8
8055b230 00000000 00000000 00000000 00000000
8055b240 00000000 00000000 00000000 00000000
8055b250 00000000 00000000 00000000 00000000
8055b260 00002710 bf80c361 00000000 00000000
8055b270 f8a49a80 823e42f8 8219b0f0 80700f40
8055b280 00000000 00000000 314d818d 00000000
8055b290 3d76644d 01cca1f4 00000000 00000000
显然,第一个0x804e36b8即为SSDT的基址。在老版本的WinDbg中,可以利用poi执行显示某一地址处的内容,例如显示地址0x804e36b8处的内容可使用dd poi[0x804e36b8],而新版的WinDbg已经可以直接支持dd [0x804e36b8]:
kd> dd [804e36b8]
804e36b8 8058a1f1 8057a2d1 8058d5e8 8058b52c
804e36c8 80591aa6 806393f2 8063b583 8063b5cc
804e36d8 8057b8c4 8064a391 80638bad 805910c4
804e36e8 80630cf4 8057bdad 80592876 80627c4d
804e36f8 805de479 80569fca 805da817 805a353d
804e3708 804e3cc4 8062d4ae 805cabb6 804edfbc
804e3718 8056a676 805688cd 80591532 8064fc88
804e3728 8058ca4e 8058af39 8064fef5 8058d63a
上述的内容依次就是用工具观察SSDT时得到的当前地址,也就是说SSDT中存放是相应索引对应函数的所在地址,例如第一个0x8058a1f1中存放的就是索引0对应函数NtAcceptConnectPort的地址,如图所示:

另外,由于每个函数地址为4字节,故索引为Index的函数对应的地址为[[KeServiceDescriptorTable]+Index*4]。
============================我是湿润的昏割线=============================
在下节课中,将深入讲解SSDT的相关知识,具体涉及SSDT的读取、写不可写页面(SSDT的写入)等。请大家继续期待梅哥的Ring0湿润插入教程吧~
相关资源:PPT中FLASH插入软件.rar-Flash文档类资源-CSDN文库
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!