1、过一般保护目 录1绪论 21.1研究背景 21.2本文的主要研究内容 32 Windows系统基础 42.1 OD插件知识 42.1.1 OD插件简介 42.1.2 如何编写OD插件 52.2 Windows系统理论知识 52.2.1 Windows进程虚拟地址空间 52.2.2 Windows系统调用 62.2.3 Windows 句柄理解 72.2.4 Windows 切换进程空间 82.3 Windows调试系统原理 82.3.1 Windows调试系统用户模块 82.3.2 Windows调试系统内核模块 93系统需求分析 93.1 存在的主要问题 93.2解决方案 103.3系统需求
2、分析 103.4系统流程图 124系统概要设计 134.1 应用程序模块 134.2 内核驱动模块 145系统详细设计 155.1 HOOK ntdll.dll的KiFastSystemCall()函数 155.2 实现HOOK的HxKiFastSystemCall(),改变系统调用的流程 165.3 解析微软提供的PDB文件得到未导出的内核函数地址 185.4 向Windows系统内核中加入我们自己的系统服务表 245.5 实现我们自己的系统调用函数 275.6 移除EPROCESS-DebugPort端口 315.7 HOOK Windows 内核下会发送调试事件的内核函数 325.8 H
3、OOK Windows内核异常处理函数 35系统开发小结 36参考文献 36致 谢 37基于OD插件的内核调试器的设计与实现摘 要:随着计算机互联网技术的飞速发展,网络游戏得到了很好的发展,它的保护也是日趋完善,几种常见的保护有nProtect GameGuard(NP), hackshield(HS),让OllyDbg调试器(OD)不能调试,Cheat Engine(CE)不能搜索游戏内存,让人们没法开始逆向它们,本文即在这种背景下为了满足人们的工作需求而设计的。系统以VC 6.0 应用程序编译器和WIN DDK 3790.1830 驱动编译器作为开发工具,以OD调试器提供的静态库,Wind
4、ow Research Kernel(WRK)的源码为基础,HOOK OD进程空间ntdll.dll动态库的KiFastSystemCall()函数,通过我们自己在Windows内核下加的系统服务表,使其当OD调试器调用我们感兴趣的系统调用时,跳到我们自己的系统调用,这样使其成功调用,突破游戏的一般保护,本文以C语言为编程语言,本系统是一个具有实际应用意义的典型系统。关键词:OD插件;NP;HS;游戏保护;OD plug-in based on the kernel debugger Design and ImplementationAbstract: With the rapid devel
5、opment of Internet technology, computer, online games have been well developed, and its protection is also maturing, several common protect nProtect GameGuard (NP), hackshield (HS), let OllyDbg debugger (OD) can notdebugging, Cheat Engine (CE) can not search for games of memory, so that people can n
6、ot start reverse them, this is in this context in order to meet the needs of people work designed.Application system to VC 6.0 compiler and WIN DDK 3790.1830 drive the compiler as a development tool to OD debugger provides static libraries, Window Research Kernel (WRK) source-based, HOOK OD process
7、space of KiFastSystemCall dynamic library ntdll.dll() function, through our own in the Windows kernel plus the system service table, so that the debugger is called when the OD system call when we are interested, skip to our own system calls, so call it success, breaking the game in generalprotection
8、 of this paper, C language programming language, the system is a typical system, the practical application of significance.Key words: OD Plugins;NP;HS;Game Protect1绪论1.1研究背景进入二十一世纪以来,随着社会的不断进步和互联网产业技术的飞速发展,网络游戏得到了空前的流行,研究它的人也越来越大众化。因此,因为种种原因,一些游戏和商业程序就会防止OD调试器和CE数据搜索器搜索游戏的数据防止逆向工作人员逆向这们,一些防调试方法如下:1,
9、 比如检测系统当前系统运行的进程看有没有Od和Ce,查找窗口看有没有它们,还有就是搜索Od和Ce的特征码看有没有它们,有就退出。2, 调用IsDebugPresent()函数,检测这个游戏进程是不是被调试。3, Hook DbgUiRemoteBreakIn()函数防止运行int 3断点中止游戏的程序运行,这样能防止OD调试器附加游戏进程。4, 游戏故意产生异常,然后自己去处理它,如果有调试的话,异常一般会先给调试器,由调试器处理了,所以游戏就不会进入它自己的异常处理,这样它就检测出了调试器。5, 还有就是nProtect GameGuard(NP), hackshield(HS),Perfe
10、ctProtect.sys等等的保护,一般它们都是差不多的,比如系统服务表(SSDT表)HOOK,内核下Inline HOOK,一般HOOK NtOpenProcess()防止打开游戏进程得到句柄,HOOK NtReadVirtualMemory(),NtWriteVirtualMemory()防止读写游戏进程空间的内存,还有就是HOOK NtDebugActiveProcess(),NtCreateDebugObject(),NtWaitForDebugEvent()等等一系列与调试有关的系统调用防止调试游戏,还有就是HOOK 每个CPU的中断描述符表(IDT)表中的INT 1和INT 3断
11、点,防止对游戏进程下断产生单步调试事件和异常调试事件,还有一种就是内核下建立一个内核线程一直对游戏的进程控制块(EPROCESS)结构体的(调试端口)DebugPort一直清0,防止游戏被Od调试收到调试信息。综上所述,第5种保护方法一般是现在最流行的保护,有很多的人们在研究它们的保护,他们一般采用的方法是跳过这些函数的调用,比如对于SSDT HOOK,它们可以在KiFastCallEntry()函数里进行处理,对于Inline HOOK,我们可以先HOOK SSDT进入我们自己的系统调用,然后才调用真正的函数,对于调试端口DebugPort清0的检测我们可以把Windows内一切操作EPRO
12、CESS.DebugPort端口的内核函数,改变这些偏移,使其操作EPROCESS结构中的其它没有多大用的结构成员,比如EPROCESS.time。这种方法针对一般的保护有效,但是对于特定的保护,我们可以特别的处理,比如Tessafe.sys你可以把自己加入白名单等等处理方法,还有对于HS我们可以调用一个14号功能函数通知HS保护驱动退出等等许多的方法。1.2本文的主要研究内容基于OD插件的内核调试器系统主要是在OD调试器的基础上编写OD插件,HOOK OD进程空间的ntdll.dll动态库的KiFastSystemCall()函数,改变OD进程调用一些关键系统函数的调用流程,使其进入我们自己
13、在内核中的实现的系统调用,这样能够绕过一般的保护驱动的钩子。本系统是主要分为四个主要的功能来实现:1, 编写OD应用程序插件,利用OD调试器提供给我们的OLLYDBG.LIB静态库和Plugin.h头文件来编写OD插件,在OD调试器上加个菜单选项用于控制OD调试器是不是启动我们的这种程序机制。比如当我们点BeginHook菜单时,我们的程序就开始工作,HOOK ntdll.dll动态库的KiFastSystemCall()函数,Windows提供给程序员的一般的API,比如Kernel32.dll里的打开进程OpenProcess(),读进程内存ReadVirtualmemory(),写进程内
14、存WriteVirtualMemory()等等API函数,最终的系统调用都会调用这个函数,这时eax是系统调用ID,堆栈里有调用这个系统调用时的各个参数,这样,我们可以改变这些系统调用ID,使其调用我们自己在Windows系统内核添加的系统调用。当我们点CancelHook时,我们的程序就停止工作。2, 由于我们内核驱动要调用的很多内核函数是ntkrnelpa.exe内核文件未导出的,所以我们要自己获取内核函数在当前系统的地址,很多同学采用的是暴力搜索内存然后匹配方法处理的,这种方法效率欠缺,并且由于好多硬编码,移值性很差。我是这么做的,在内核驱动下,把这些未导出的函数申明为函数变量指针,然后
15、用户层程序(OD插件)调用DeviceIoControlFile()函数和驱动通信取得这些未导出的函数的名字,然后我们自己解析微软提供给我们的符号文件(PDB文件),得到这个版本系统的未导出的函数在这个系统版本的内核加载地址。然后再次调用DeviceIoControlFile()函数传给内核驱动。3,把微软提供的Windows Reasarch Kernel(WRK)里的dbgk目录下的dbgkobj.c,dbgkport.c,dbgkproc.c,dbgkp.h文件移值到我们的内核驱动工程里来,这几个.c文件是Windows系统调试系统机制的实现,只要我们集成进我们的驱动里来了,以后游戏保护
16、对这些调试机制的一切HOOK与检测,我们可以不用去管它了,相当于OD插件它们用的是我们自己集成的调试系统机制。还有内核线程对EPROCESS结构体的DebugPort清0,我们也可以跳过它。还有就是增加游戏常HOOK的几个关键函数,比如NtOpenProcess(),NtReadVirtualMemory(),NtWriteVirtualMemory()等等,我们自己在Windows内核中实现它,下次OD调试器调用这些函数对游戏进程操作时,都会进入我们自己的这些系统调用,到时想实现什么功能就实现什么功能。4, Windows系统内核下有两种系统服务表KeServiceDescriptorTab
17、le和KeServiceDescriptorTableShadow,对于图形界面(GUI)线程,它引用的是KeServiceDescriptorTableShadow这个系统服务表,非GUI线程引用的是KeServiceDescriptorTable系统服务表。对于微软提供的大多数API,内核下都有专门对应的系统调用,比如当我们调用OpenProcess() API函数时,最终会调用系统调用NtOpenProcess()。每一个系统调用ID,在Windows内核下都会对应一个系统调用函数,比如NtOpenProcess的系统调用ID,在Windows Xp 3的系统下ID为122。Window
18、s Xp3 系统大概提供了283个系统调用。因此我们自己可以在这两种系统服务表的后面加上我们自己的系统调用函数,对应相应的系统调用ID,这样当OD调试器调用我们感兴趣的系统调用时,我们可以改变系统调用ID,这样就可以进入我们自己实现的系统调用函数。2 Windows系统基础2.1 OD插件知识2.1.1 OD插件简介 OllyDbg 简称(OD) 是一款优秀的用户态调试工具。它不仅拥有强大的反汇编能力和动态分析力,还具有良好的扩展结构,允许用户自行开发插件完成特定的工作。OD插件也可以有自己的窗口逻辑和功能函数。事实上,我们可以将它看成这样一个Windows 程序,它拥有自己的消息循环和窗口过
19、程,但它的启动是由OllyDbg 发起的,具体功能的实现也通过调用OllyDbg 提供的函数来实现的。它的启动过程如下:在OllyDbg 的启动过程中,有一步是检查插件路径下是否存在DLL 文件。如果存在,逐一进行如下扫描:1, 加载该DLL 文件,找到其入口点。2, 通过回调函数,获取插件名称、版本等信息。3, 通过回调函数,对插件进行初始化,包括申请资源、恢复全局参数等。如果某个DLL 文件无法顺利执行这三步,OllyDbg 的启动将失败、报错并退出。OllyDbg 启动以后,会一直维护插件的队列,在特定时间向该队列发送消息,或者直接调用插件中定义的函数,用户通过插件菜单或快捷键主动执行插
20、件某功能。最后,当OllyDbg 被关闭时,还会调用插件中的回调函数,释放插件申请到的资源,并将需要保存的参数、配置和附加信息分别予以保存。2.1.2 如何编写OD插件 新建立一个VC 6.0的工程,把Plugin.h头文件和OLLYDBG.LIB静态库文件加进工程里面来,还有在工程里加入/J选项,使使char 默认为unsigned类型,这是OllyDbg 中的约定。然后定义两个回调函数,这是OD插件必需的两个回调函数,ODBG_Plugindata()和ODBG_Plugininit()函数,之所以说是回调函数,是因为这是由OD调试器调用它们的。比如我们在ODBG_Plugindata()
21、回调函数中传递我们插件的名字进去;在ODBG_Plugininit()中把OD窗口句柄保存起来;我们还实现了几个回调函数,ODBG_Pluginmenu(),ODBG_Pluginaction(),ODBG_Plugindestroy()函数。这些函数用于在OD插件菜单中加入菜单选项和我们的响应函数。我们可以在OD插件里面调用OD提供很多的API函数,比如有Breakpoint functions,Memory functions,Thread functions,Module functions等等许多的函数库。之后编译成功后把它们放在OD调试器目录下的Plugin文件夹下就行了。 2.2
22、Windows系统理论知识2.2.1 Windows进程虚拟地址空间如上图4-1-1,在Windows系统中,每一个进程都有4GB的虚拟地址空间。02GB是Windows进程的用户空间,2GB到4GB是Windows的内核空间。物理地址扩展(PAE)除外,这时,每个进程的用户空间为03GB。每个进程有4GB的虚拟地址空间,并不是指它真正有4GB的物理内存,而是指每个进程都有其页目录表和页表,通过它会把每个进程映射成4GB虚拟内存空间。进程用户空间0-2GB的映射,页表一般会把进程的这段虚拟内存空间映射到不同的物理内存中。但是有例外,就是Windows常见的DLL和可执行映象文件,比如kerne
23、l32.dll,ntdll.dll和.exe文件运行时的情况。它们有写入时复制(Copy On Write)机制,在没有改写这些代码前,它们在物理内存中只有一份内存,所有的进程都会根据页目录和页表映射到这份物理内存。但是当有一个进程改变这些DLL的内容时,写入时复制就发生了。Windows这时会把这些内容在物理内存中拷贝一份,改变这个进程的页目录和页表,使之指向这份拷贝的物理内存,对其它的进程没有任何影响,所以这就是用户层HOOK 系统DLL时,只对本进程有效,对其它进程无效的原因。所以当我们HOOK 一个进程的ntdll.dll的KiFastSystemCall()时,它只会对我们HOOK的
24、这个进程的KiFastSystemCall()函数起作用。对别的进程没有影响。 进程内核空间24GB空间的映射,通常系统中每个进程的这段虚拟内存空间会根据页目录表和页表映射到相同的物理内存中。比如当我们改写一处内存,此时对每个进程都会有效的,对整个系统也都是有效的。 2.2.2 Windows系统调用下面以Windows XP3系统分析Windows用户层API是怎么调用系统调用的全过程。图4-1-2是Windows系统进行系统调用的全过程描述:比如当我们用户层进程调用常见的API比如Win32 API 比如OpenProcess(),ReadVirtualMemory(),WriteVirt
25、ualMemory(),CreateFile()等等函数,它们接着会依次调用ntdll.dll的Zw*函数,Zw*函数会传递系统调用ID,然后调用sysenter指令进入ring0,在Windows内核下首先执行的是KiFastCallEntry()函数,它会根据系统调用ID分别从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中取得相应的系统调用函数地址,然后调用之。对于GUI线程是从KeServiceDescriptorTableShadow表中取,非GUI线程是从KeServiceDescriptorTabl
26、e表中取的。所以我们可以HOOK KiFastCallEntry()这个函数,我们就可以拦截一切的系统调用。360安全卫士就是使用这种方法拦截系统调用的。当这个系统调用成功返回后,KiFastCallEntry()函数会调用sysexit指令退出本次的系统调用,然后返回到用户进程空间,依次返回到调用用户API的地方。2.2.3 Windows 句柄理解句柄是Windows系统的特性。你可以简单理解为通过它你就可以访问相应的内核对象。其实Windows是这么设计的,每个进程的EPROCESS结构体中都有个ObjectTable 成员指向进程句柄表(HANDLE_TABLE),里面每一项都是HAN
27、DLE_TABLE_ENTRY句柄表项。里面放着相应对象的地址,句柄只是这些表项的索引。还有一点值的注意的是句柄是进程相关的。你在本进程打开的进程句柄,在别的进程当中不能引用它的。它们的结构体定义如下:lkd dt _HANDLE_TABLEnt!_HANDLE_TABLE +0x000 TableCode : Uint4B /指向_HANDLE_TABLE_ENTRY数组 +0x004 QuotaProcess : Ptr32 _EPROCESS /这个句柄表属于哪个进程 +0x008 UniqueProcessId : Ptr32 Void +0x00c HandleTableLock :
28、 4 _EX_PUSH_LOCK +0x01c HandleTableList : _LIST_ENTRY +0x024 HandleContentionEvent : _EX_PUSH_LOCK +0x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO +0x02c ExtraInfoPages : Int4B +0x030 FirstFree : Uint4B +0x034 LastFree : Uint4B +0x038 NextHandleNeedingPool : Uint4B +0x03c HandleCount : Int4B /句柄表的
29、数量 +0x040 Flags : Uint4B +0x040 StrictFIFO : Pos 0, 1 Bitlkd dt _HANDLE_TABLE_ENTRYnt!_HANDLE_TABLE_ENTRY +0x000 Object : Ptr32 Void /指向内核对象的地址 +0x004 GrantedAccess : Uint4B比如当我们在用户进程调用kernel32.dll里的OpenProcess() 打开进程时,它先会调ntdll.dll的ZwOpenProcess(),接着会传递NtOpenProcess()的系统调用ID,调用KiFastSystemCall()函数,
30、这函数里面会调用sysenter指令进入Ring0,然后由KiFastCallEntry()根据系统调用ID从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中找到NtOpenProcess()调用之。NtOpenProcess()里面会根据要打开的进程ID还是进程名字调用PsLookupProcessByProcessId()这个函数得到这个进程对象EPROCESS的地址,最后调用ObOpenObjectByPointer()返回进程句柄。ObOpenObjectByPointer()函数大致工作如下:首先会增加
31、这个EPROCESS对象的引用计数,然后构造一个_HANDLE_TABLE_ENTRY结构,填入对象的地址,然后把它加进这个用户进程的句柄表(HANDLE_TBALE)中,最后返回句柄(索引)。2.2.4 Windows 切换进程空间一般而言,如果线程T属于进程P,那么当这个线程在内核中运行时的用户空间应该就是进程P的用户空间。它也没有必要访问到别的用户进程空间去,可是Windows允许一些跨进程的操作,特别是跨用户进程空间的操作。所以有时候就需要把当时的用户空间切换到别的进程空间中去。Windows提供的函数是KeStackAttachProcess()和KiAttachProcess()。
32、它的原理其实就是改变CPU的CR3寄存器使之指向要切换进程的页目录表。因为CPU访问进程用户层空间地址都是通过CR3找页目录表,然后通过Windows内存管理器把虚拟地址映射成物理地址才去访问的,所以只要我们改变CR3寄存器就行了。Windows有很多这种跨进程的操作,例如调试DbgkpPostFakeProcessCreateMessages()函数会调用KeStackAttachProcess()这个函数切换进被调试进程的用户层空间中,因为DbgkpPostFakeModuleMessages()这个函数会访问被调试进程的进程环境块(PEB),然后遍历它的用户层模块链表。最后会调用KeUnstackDetachProcess()这个函数回到OD调试器的进程空间来。2.3 Windows调试系统原理2.3.1 Windows调试系统用户模块 Windows系统在用户层提供了很多的调试API供用户程序调用,它们分别在kernel32.dll和ntdll.d
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1