基于OD插件的内核调试器的设计与实现_第1页
基于OD插件的内核调试器的设计与实现_第2页
基于OD插件的内核调试器的设计与实现_第3页
基于OD插件的内核调试器的设计与实现_第4页
基于OD插件的内核调试器的设计与实现_第5页
已阅读5页,还剩31页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、目 录 TOC o 1-3 u 1绪论 PAGEREF _Toc262138811 h 2研究背景 PAGEREF _Toc262138812 h 2本文的主要研究内容 PAGEREF _Toc262138813 h 32 Windows系统基础 PAGEREF _Toc262138814 h 42.1 OD插件知识 PAGEREF _Toc262138815 h 42.1.1 OD插件简介 PAGEREF _Toc262138816 h 42.1.2 如何编写OD插件 PAGEREF _Toc262138817 h 5Windows系统理论知识 PAGEREF _Toc262138818 h

2、 52.2.1 Windows进程虚拟地址空间 PAGEREF _Toc262138819 h 5Windows系统调用 PAGEREF _Toc262138820 h 6Windows 句柄理解 PAGEREF _Toc262138821 h 7Windows 切换进程空间 PAGEREF _Toc262138822 h 8Windows调试系统原理 PAGEREF _Toc262138823 h 8Windows调试系统用户模块 PAGEREF _Toc262138824 h 8Windows调试系统内核模块 PAGEREF _Toc262138825 h 93系统需求分析 PAGEREF

3、 _Toc262138826 h 9存在的主要问题 PAGEREF _Toc262138827 h 9解决方案 PAGEREF _Toc262138828 h 10系统需求分析 PAGEREF _Toc262138829 h 10系统流程图 PAGEREF _Toc262138830 h 124系统概要设计 PAGEREF _Toc262138831 h 13应用程序模块 PAGEREF _Toc262138832 h 13内核驱动模块 PAGEREF _Toc262138833 h 145系统详细设计 PAGEREF _Toc262138834 h 15的KiFastSystemCall()

4、函数 PAGEREF _Toc262138835 h 15实现HOOK的HxKiFastSystemCall(),改变系统调用的流程 PAGEREF _Toc262138836 h 16解析微软提供的PDB文件得到未导出的内核函数地址 PAGEREF _Toc262138837 h 18向Windows系统内核中加入我们自己的系统服务表 PAGEREF _Toc262138838 h 24实现我们自己的系统调用函数 PAGEREF _Toc262138839 h 27移除EPROCESS-DebugPort端口 PAGEREF _Toc262138840 h 31HOOK Windows 内核

5、下会发送调试事件的内核函数 PAGEREF _Toc262138841 h 32HOOK Windows内核异常处理函数 PAGEREF _Toc262138842 h 35系统开发小结 PAGEREF _Toc262138843 h 36参考文献 PAGEREF _Toc262138844 h 36致 谢 PAGEREF _Toc262138845 h 37基于OD插件的内核调试器的设计与实现摘要:随着计算机互联网技术的飞速发展,网络游戏得到了很好的发展,它的保护也是日趋完善,几种常见的保护有nProtect GameGuard(NP), hackshield(HS),让OllyDbg调试器

6、(OD)不能调试,Cheat Engine(CE)不能搜索游戏内存,让人们没法开始逆向它们,本文即在这种背景下为了满足人们的工作需求而设计的。系统以VC 应用程序编译器和WIN DDK 驱动编译器作为开发工具,以OD调试器提供的静态库,Window Research Kernel(WRK)的源码为基础,HOOK KiFastSystemCall()函数,通过我们自己在Windows内核下加的系统服务表,使其当OD调试器调用我们感兴趣的系统调用时,跳到我们自己的系统调用,这样使其成功调用,突破游戏的一般保护,本文以C语言为编程语言,本系统是一个具有实际应用意义的典型系统。关键词:OD插件;NP;

7、HS;游戏保护;OD plug-in based on the kernel debugger Design and ImplementationAbstract: With the rapid development 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 deb

8、ugger (OD) can notdebugging, Cheat Engine (CE) can not search for games of memory, so that people can not start reverse them, this is in this context in order to meet the needs of people work designed.() function, through our own in the Windows kernel plus the system service table, so that the debug

9、ger 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 of this paper, C language programming language, the system is a typical system, the practical application of significance.Key words: OD Plugins;NP;HS

10、;Game Protect TOC o 1-3 u 1绪论1.1研究背景进入二十一世纪以来,随着社会的不断进步和互联网产业技术的飞速发展,网络游戏得到了空前的流行,研究它的人也越来越大众化。因此,因为种种原因,一些游戏和商业程序就会防止OD调试器和CE数据搜索器搜索游戏的数据防止逆向工作人员逆向这们,一些防调试方法如下:1, 比如检测系统当前系统运行的进程看有没有Od和Ce,查找窗口看有没有它们,还有就是搜索Od和Ce的特征码看有没有它们,有就退出。2, 调用IsDebugPresent()函数,检测这个游戏进程是不是被调试。3, Hook DbgUiRemoteBreakIn()函数防止运行

11、int 3断点中止游戏的程序运行,这样能防止OD调试器附加游戏进程。4, 游戏故意产生异常,然后自己去处理它,如果有调试的话,异常一般会先给调试器,由调试器处理了,所以游戏就不会进入它自己的异常处理,这样它就检测出了调试器。5, 还有就是nProtect GameGuard(NP), hackshield(HS),PerfectProtect.sys等等的保护,一般它们都是差不多的,比如系统服务表(SSDT表)HOOK,内核下Inline HOOK,一般HOOK NtOpenProcess()防止打开游戏进程得到句柄,HOOK NtReadVirtualMemory(),NtWriteVirt

12、ualMemory()防止读写游戏进程空间的内存,还有就是HOOK NtDebugActiveProcess(),NtCreateDebugObject(),NtWaitForDebugEvent()等等一系列与调试有关的系统调用防止调试游戏,还有就是HOOK 每个CPU的中断描述符表(IDT)表中的INT 1和INT 3断点,防止对游戏进程下断产生单步调试事件和异常调试事件,还有一种就是内核下建立一个内核线程一直对游戏的进程控制块(EPROCESS)结构体的(调试端口)DebugPort一直清0,防止游戏被Od调试收到调试信息。综上所述,第5种保护方法一般是现在最流行的保护,有很多的人们在研

13、究它们的保护,他们一般采用的方法是跳过这些函数的调用,比如对于SSDT HOOK,它们可以在KiFastCallEntry()函数里进行处理,对于Inline HOOK,我们可以先HOOK SSDT进入我们自己的系统调用,然后才调用真正的函数,对于调试端口端口的内核函数,改变这些偏移,使其操作EPROCESS结构中的其它没有多大用的结构成员,比如EPROCESS.time。这种方法针对一般的保护有效,但是对于特定的保护,我们可以特别的处理,比如Tessafe.sys你可以把自己加入白名单等等处理方法,还有对于HS我们可以调用一个14号功能函数通知HS保护驱动退出等等许多的方法。本文的主要研究内

14、容基于OD插件的内核调试器系统主要是在OD调试器的基础上编写OD插件,HOOK OD进程空间的的KiFastSystemCall()函数,改变OD进程调用一些关键系统函数的调用流程,使其进入我们自己在内核中的实现的系统调用,这样能够绕过一般的保护驱动的钩子。本系统是主要分为四个主要的功能来实现:1, 编写OD应用程序插件,利用OD调试器提供给我们的静态库和头文件来编写OD插件,在OD调试器上加个菜单选项用于控制OD调试器是不是启动我们的这种程序机制。比如当我们点BeginHook菜单时,我们的程序就开始工作,HOOK KiFastSystemCall()函数,Windows提供给程序员的一般的

15、打开进程OpenProcess(),读进程内存ReadVirtualmemory(),写进程内存WriteVirtualMemory()等等API函数,最终的系统调用都会调用这个函数,这时eax是系统调用ID,堆栈里有调用这个系统调用时的各个参数,这样,我们可以改变这些系统调用ID,使其调用我们自己在Windows系统内核添加的系统调用。当我们点CancelHook时,我们的程序就停止工作。2, 由于我们内核驱动要调用的很多内核函数是ntkrnelpa.exe内核文件未导出的,所以我们要自己获取内核函数在当前系统的地址,很多同学采用的是暴力搜索内存然后匹配方法处理的,这种方法效率欠缺,并且由于

16、好多硬编码,移值性很差。我是这么做的,在内核驱动下,把这些未导出的函数申明为函数变量指针,然后用户层程序(OD插件)调用DeviceIoControlFile()函数和驱动通信取得这些未导出的函数的名字,然后我们自己解析微软提供给我们的符号文件(PDB文件),得到这个版本系统的未导出的函数在这个系统版本的内核加载地址。然后再次调用DeviceIoControlFile()函数传给内核驱动。3,把微软提供的Windows Reasarch Kernel(WRK)里的dbgk目录下的dbgkobj.c,dbgkport.c,dbgkproc.c,dbgkp.h文件移值到我们的内核驱动工程里来,这几

17、个.c文件是Windows系统调试系统机制的实现,只要我们集成进我们的驱动里来了,以后游戏保护对这些调试机制的一切HOOK与检测,我们可以不用去管它了,相当于OD插件它们用的是我们自己集成的调试系统机制。还有内核线程对EPROCESS结构体的DebugPort清0,我们也可以跳过它。还有就是增加游戏常HOOK的几个关键函数,比如NtOpenProcess(),NtReadVirtualMemory(),NtWriteVirtualMemory()等等,我们自己在Windows内核中实现它,下次OD调试器调用这些函数对游戏进程操作时,都会进入我们自己的这些系统调用,到时想实现什么功能就实现什么功

18、能。4, Windows系统内核下有两种系统服务表KeServiceDescriptorTable和KeServiceDescriptorTableShadow,对于图形界面(GUI)线程,它引用的是KeServiceDescriptorTableShadow这个系统服务表,非GUI线程引用的是KeServiceDescriptorTable系统服务表。对于微软提供的大多数API,内核下都有专门对应的系统调用,比如当我们调用OpenProcess() API函数时,最终会调用系统调用NtOpenProcess()。每一个系统调用ID,在Windows内核下都会对应一个系统调用函数,比如NtOp

19、enProcess的系统调用ID,在Windows Xp 3的系统下ID为122。Windows Xp3 系统大概提供了283个系统调用。因此我们自己可以在这两种系统服务表的后面加上我们自己的系统调用函数,对应相应的系统调用ID,这样当OD调试器调用我们感兴趣的系统调用时,我们可以改变系统调用ID,这样就可以进入我们自己实现的系统调用函数。2 Windows系统基础 OD插件知识 OD插件简介 OllyDbg 简称(OD) 是一款优秀的用户态调试工具。它不仅拥有强大的反汇编能力和动态分析力,还具有良好的扩展结构,允许用户自行开发插件完成特定的工作。OD插件也可以有自己的窗口逻辑和功能函数。事实

20、上,我们可以将它看成这样一个Windows 程序,它拥有自己的消息循环和窗口过程,但它的启动是由OllyDbg 发起的,具体功能的实现也通过调用OllyDbg 提供的函数来实现的。它的启动过程如下:在OllyDbg 的启动过程中,有一步是检查插件路径下是否存在DLL 文件。如果存在,逐一进行如下扫描:1, 加载该DLL 文件,找到其入口点。2, 通过回调函数,获取插件名称、版本等信息。3, 通过回调函数,对插件进行初始化,包括申请资源、恢复全局参数等。如果某个DLL 文件无法顺利执行这三步,OllyDbg 的启动将失败、报错并退出。OllyDbg 启动以后,会一直维护插件的队列,在特定时间向该

21、队列发送消息,或者直接调用插件中定义的函数,用户通过插件菜单或快捷键主动执行插件某功能。最后,当OllyDbg 被关闭时,还会调用插件中的回调函数,释放插件申请到的资源,并将需要保存的参数、配置和附加信息分别予以保存。.2 如何编写OD插件 新建立一个VC 6.0的工程,把头文件和静态库文件加进工程里面来,还有在工程里加入/J选项,使使char 默认为unsigned类型,这是OllyDbg 中的约定。然后定义两个回调函数,这是OD插件必需的两个回调函数,ODBG_Plugindata()和ODBG_Plugininit()函数,之所以说是回调函数,是因为这是由OD调试器调用它们的。比如我们在

22、ODBG_Plugindata()回调函数中传递我们插件的名字进去;在ODBG_Plugininit()中把OD窗口句柄保存起来;我们还实现了几个回调函数,ODBG_Pluginmenu(),ODBG_Pluginaction(),ODBG_Plugindestroy()函数。这些函数用于在OD插件菜单中加入菜单选项和我们的响应函数。我们可以在OD插件里面调用OD提供很多的API函数,比如有Breakpoint functions,Memory functions,Thread functions,Module functions等等许多的函数库。之后编译成功后把它们放在OD调试器目录下的Pl

23、ugin文件夹下就行了。 Windows系统理论知识 Windows进程虚拟地址空间如上图4-1-1,在Windows系统中,每一个进程都有4GB的虚拟地址空间。02GB是Windows进程的用户空间,2GB到4GB是Windows的内核空间。物理地址扩展(PAE)除外,这时,每个进程的用户空间为03GB。每个进程有4GB的虚拟地址空间,并不是指它真正有4GB的物理内存,而是指每个进程都有其页目录表和页表,通过它会把每个进程映射成4GB虚拟内存空间。进程用户空间0-2GB的映射,页表一般会把进程的这段虚拟内存空间映射到不同的物理内存中。但是有例外,就是Windows常见的DLL和可执行映象文件

24、,比如kernel32.dll,ntdll.dll和.exe文件运行时的情况。它们有写入时复制(Copy On Write)机制,在没有改写这些代码前,它们在物理内存中只有一份内存,所有的进程都会根据页目录和页表映射到这份物理内存。但是当有一个进程改变这些DLL的内容时,写入时复制就发生了。Windows这时会把这些内容在物理内存中拷贝一份,改变这个进程的页目录和页表,使之指向这份拷贝的物理内存,对其它的进程没有任何影响,所以这就是用户层HOOK 系统DLL时,只对本进程有效,对其它进程无效的原因。所以当我们HOOK 一个进程的ntdll.dll的KiFastSystemCall()时,它只会

25、对我们HOOK的这个进程的KiFastSystemCall()函数起作用。对别的进程没有影响。 进程内核空间24GB空间的映射,通常系统中每个进程的这段虚拟内存空间会根据页目录表和页表映射到相同的物理内存中。比如当我们改写一处内存,此时对每个进程都会有效的,对整个系统也都是有效的。 Windows系统调用下面以Windows XP3系统分析Windows用户层API是怎么调用系统调用的全过程。图4-1-2是Windows系统进行系统调用的全过程描述:比如当我们用户层进程调用常见的API比如Win32 API 比如OpenProcess(),ReadVirtualMemory(),WriteVi

26、rtualMemory(),CreateFile()等等函数,它们接着会依次调用ntdll.dll的Zw*函数,Zw*函数会传递系统调用ID,然后调用sysenter指令进入ring0,在Windows内核下首先执行的是KiFastCallEntry()函数,它会根据系统调用ID分别从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中取得相应的系统调用函数地址,然后调用之。对于GUI线程是从KeServiceDescriptorTableShadow表中取,非GUI线程是从KeServiceDescriptorTa

27、ble表中取的。所以我们可以HOOK KiFastCallEntry()这个函数,我们就可以拦截一切的系统调用。360安全卫士就是使用这种方法拦截系统调用的。当这个系统调用成功返回后,KiFastCallEntry()函数会调用sysexit指令退出本次的系统调用,然后返回到用户进程空间,依次返回到调用用户API的地方。 Windows 句柄理解句柄是Windows系统的特性。你可以简单理解为通过它你就可以访问相应的内核对象。其实Windows是这么设计的,每个进程的EPROCESS结构体中都有个ObjectTable 成员指向进程句柄表(HANDLE_TABLE),里面每一项都是HANDLE

28、_TABLE_ENTRY句柄表项。里面放着相应对象的地址,句柄只是这些表项的索引。还有一点值的注意的是句柄是进程相关的。你在本进程打开的进程句柄,在别的进程当中不能引用它的。它们的结构体定义如下:lkd dt _HANDLE_TABLEnt!_HANDLE_TABLE +0 x000 TableCode : Uint4B /指向_HANDLE_TABLE_ENTRY数组 +0 x004 QuotaProcess : Ptr32 _EPROCESS /这个句柄表属于哪个进程 +0 x008 UniqueProcessId : Ptr32 Void +0 x00c HandleTableLock

29、: 4 _EX_PUSH_LOCK +0 x01c HandleTableList : _LIST_ENTRY +0 x024 HandleContentionEvent : _EX_PUSH_LOCK +0 x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO +0 x02c ExtraInfoPages : Int4B +0 x030 FirstFree : Uint4B +0 x034 LastFree : Uint4B +0 x038 NextHandleNeedingPool : Uint4B +0 x03c HandleCount : In

30、t4B /句柄表的数量 +0 x040 Flags : Uint4B +0 x040 StrictFIFO : Pos 0, 1 Bitlkd dt _HANDLE_TABLE_ENTRYnt!_HANDLE_TABLE_ENTRY +0 x000 Object : Ptr32 Void /指向内核对象的地址 +0 x004 GrantedAccess : Uint4B比如当我们在用户进程调用OpenProcess() 打开进程时,它先会调ntdll.dll的ZwOpenProcess(),接着会传递NtOpenProcess()的系统调用ID,调用KiFastSystemCall()函数,这

31、函数里面会调用sysenter指令进入Ring0,然后由KiFastCallEntry()根据系统调用ID从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中找到NtOpenProcess()调用之。NtOpenProcess()里面会根据要打开的进程ID还是进程名字调用PsLookupProcessByProcessId()这个函数得到这个进程对象EPROCESS的地址,最后调用ObOpenObjectByPointer()返回进程句柄。ObOpenObjectByPointer()函数大致工作如下:首先会增加这

32、个EPROCESS对象的引用计数,然后构造一个_HANDLE_TABLE_ENTRY结构,填入对象的地址,然后把它加进这个用户进程的句柄表(HANDLE_TBALE)中,最后返回句柄(索引)。 Windows 切换进程空间一般而言,如果线程T属于进程P,那么当这个线程在内核中运行时的用户空间应该就是进程P的用户空间。它也没有必要访问到别的用户进程空间去,可是Windows允许一些跨进程的操作,特别是跨用户进程空间的操作。所以有时候就需要把当时的用户空间切换到别的进程空间中去。Windows提供的函数是KeStackAttachProcess()和KiAttachProcess()。它的原理其实

33、就是改变CPU的CR3寄存器使之指向要切换进程的页目录表。因为CPU访问进程用户层空间地址都是通过CR3找页目录表,然后通过Windows内存管理器把虚拟地址映射成物理地址才去访问的,所以只要我们改变CR3寄存器就行了。Windows有很多这种跨进程的操作,例如调试DbgkpPostFakeProcessCreateMessages()函数会调用KeStackAttachProcess()这个函数切换进被调试进程的用户层空间中,因为DbgkpPostFakeModuleMessages()这个函数会访问被调试进程的进程环境块(PEB),然后遍历它的用户层模块链表。最后会调用KeUnstackD

34、etachProcess()这个函数回到OD调试器的进程空间来。 Windows调试系统原理Windows调试系统用户模块 Windows系统在用户层提供了很多的调试API供用户程序调用,它们分别在kernel32.dll和ntdll.dll里面。常见的API有DebugActiveProcess(),DebugActiveProcessStop(),DebugSetProcessKillOnExit(),WaitForDebugEvent(),ContinueDebugEvent()。n调试函数有DbgUiDebugActiveProcess(),DbgUiIssueRemoteBreakI

35、n(),DbgUiContinue()等等API函数。 OD调试器一般会枚举当系统系统中的所有进程,如果我们要要调试哪个进程,它就会调用DebugActiveProcess()DbgUiConnectToDbg(),这个函数里面最终会调用ntdll.dll的ZwCreateDebugObject()建立一个调试对象用来接受调试事件信息,最终依次会进入NtCreateDebugObject()系统调用函数。接着它会调用ProcessIdToHandle(PID)打开进程,它里面会调用ZwOpenProcess()函数打开进程,如果打开进程成功,则返回进程句柄。DbgUiDebugActivePr

36、ocess()函数进行真正的附加调试。DbgUiDebugActiveProcess()函数首先会调用ZwDebugActiveProcess()函数,这个函数会把这个被调试进程的线程事件和模块事件信息插入在先前建立的调试对象中。然后会调用DbgUiIssueRemoteBreakin()函数在被调试的进程空间中建立一个远程线程用于执行int 3指令断点,目的是为了中断到调试器。之后OD调试器就会循环调用WaitForDebugEvent()和ContinueDebugEvent()等待调试事件,然后处理调试事件信息。Windows调试系统内核模块Windows系统在内核层也有很多的系统函数用

37、来支持调试机制,比如有NtCreateDebugObject(),DbgkpPostFakeProcessCreateMessages(),DbgkpSetProcessDebugObject(),NtWaitForDebugEvent(),NtDebugContinue(),DbgkInitialize(),DbgkpSendApiMessage(),DbgkCreateThread(),DbgkMapViewOfSection()NtDebugActiveProcess()等等函数。DbgkInitialize()函数里面会调用ObCreateObjectType()函数建立一个调试对象类

38、型。NtCreateDebugObject()函数里面会调用ObCreateObject()建立一个调试对象,然后再调用ObInsertObject()函数把这个对象插进OD进程的句柄表中。NtDebugActiveProcess()函数里面首先会调用DbgkpPostFakeProcessCreateMessages()函数,这个函数里面会依次调用DbgkpPostFakeThreadMessages()和DbgkpPostFakeModuleMessages()函数向调试对象发送线程和模块事件信息。最后会调用DbgkpSetProcessDebugObject()这个函数把被调试进程的EP

39、ROCESS的DebugPort设置成NtCreateDebugObject()函数建立的调试对象。当建立线程时,Windows系统会调用DbgkCreateThread()这个函数向这个调试对象发送建立线程的调试事件;当加载DLL时,Windows系统会调用DbgkMapViewOfSection()这个函数向这个调试对象发送加载模块的调试事件;当被调试进程执行用户层int 3指令时,Windows系统会调用DbgkForwardException()这个函数向这个调试对象发送异常调试事件。3系统需求分析存在的主要问题一般防调试的程序会对NtOpenProcess(),NtReadVirtu

40、alMemory(),NtWriteVirtualMemory(),NtDebugActiveProcess(),NtCreateDebugObject(),NtWaitForDebugEvent()这些函数进行HOOK的,不是SSDT HOOK这些函数,就是Inline HOOK这些函数。当我们用OD调试器还是CE搜索器,要打开它的进程并对其进行读写内存时,它都会禁止操作那进程,从而导致我们操作失败。比如程序对NtOpenProcess()函数HOOK后,防止打开进程,OD调试器就看不到那进程,对NtDebugActiveProcess()函数HOOK后,OD调试器虽然能看到进程,但是不能调

41、试成功的。他们有可能会在内核下建立系统内核线程不停的检测它们HOOK代码的地方会不会被恢复。还有可能会检测他们HOOK的代码到底有没有被调用。如果没有被调用,则认为是我们在它前面跳过去了,这样它们肯定就检测出了异常,他们还有可能会在内核下建立内核线程不时的对其保护的进程的EPROCESS的DebugPort清0,让我们的OD调试器收不到任何调试事件信息。解决方案基于以上问题,我是这么设计的,我们的应用层OD插件HOOK OD进程空间动态库的KiFastSystemCall()函数,使其跳到我们自己的HxKiFastSystemCall(),然后对OD进程调用的相应的API函数进行过滤处理,也就

42、是改变系统调用ID。然后在Windows系统内核下加入我们自己的系统调用函数和调试驱动。当OD调用比较关键的函数,就会进入我们自己的系统调用。所以对于别的驱动保护HOOK的关键函数就不起用了,对于一直清除EPROCESS的DebugPort的线程的检测,我们自己在内核下可以不操作要调试进程EPROCESS的Debugport。我们移到另一个结构体中。这样也就可以防止它一直对DebugPort调试端口清0。系统需求分析根据以上的分析,为了克服现行OD调试器存在的不足,得到新系统的功能如下:我们利用OD调试器插件库,HOOK OD进程空间的ntdll.dll的KiFastSystemCall(),

43、它是一切用户层API要进行系统调用的最后入口,然后当OD进程调用API函数时,只要这个API函数里会调用系统函数调用,它都会跳到我们自己的HxKiFastSystemCall()函数。在我们自己实现的HxKiFastSystemCall()函数中,对OD进程调用的API函数进行过滤处理,比如如果是调用OpenProcess()打开进程,ReadVirtualMemory()读进程内存,WriteVirtualMemory()写进程内存,DebugActiveProcess()附加进程,WaitForDebugEvent()等待调试事件等等关键函数,我们会在这里改变它们的系统调用ID,然后自己调

44、用sysenter指令进入Ring0,sysenter指令是intel CPU公司专门设计的快速系统调用,它会改变EIP寄存器,使其指向KiFastCallEntry()函数,因此在当前系统中,只要发生过系统调用,不管是哪个进程在要求调用系统调用,在Ring0中首先被执行的是KiFastCallEntry()函数,然后它会根据我们传来的系统调用Id从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow表中去取得相应的系统调用函数运行。由于我们内核驱动里面用到的很多内核函数很底层,MS提供的DDK中并没有导出这些函数。比如这些函数有Nt

45、ReadVirtualMemory(),NtWriteVirtualMemory(),它们都要调用的MmCopyVirtualMemory()函数,这个函数微软提供的静态库文件就没有导出,还有很多比如MiProtectVirtualMemory(),PsGetNextProcess(),ObDuplicateObject(),等等没有导出。我们不能在内核驱动中直接调用,我们可以采用暴力搜索内存找特征码然后得到当前系统这些函数的加载地址,然后加以调用。这种方法很费时而且通用性不好。我采用另一种方法,我们应用程序自己解析微软提供给我们的PDB符号文件搜索得到这些未导出函数的地址,然后传进内核驱动这

46、些内核函数的地址,我们内核驱动才可以加以调用这些未导出的函数。我们要在Windows内核下加入我们自己的系统调用表,Windows内核下有两种系统调用表,KeServiceDescriptorTable和KeServiceDescriptorTableShadow表。我们自己在非分页内存中分配一个服务表, 先拷贝KeServiceDescriptorTable和KeServiceDescriptorTableShadow两个表,然后把我们要加上的系统调用函数地址加进去,然后写进KeServiceDescriptorTable和KeServiceDescriptorTableShadow表中,下

47、次如果OD进程调用我们感兴趣的系统调用的话,KiFastCallEntry()函数就会根据服务Id从这两个表中找到我们的系统调用,然后调用之。我们自己的系统调用很多地方可以参考WRK。WRK是Windows Server 2003系统的内核。跟XP还是有点区别的。对于很重要的内核函数KeStackAttachProcess()和KiAttachProcess (),这两个是Windows系统内核切换到别的进程空间中的关键函数。由于有的游戏也有HOOK,我们要自己实现之用来跳过HOOK,功能基本已实现。有的保护驱动会在内核下建立内核线程一直对它要保护的进程的EPROCESS的DebugPort端

48、口一直清0,这样会导致OD调试器收不到任何调试事件信息。我是这么做的,因为我们OD调试器调用的API与调试有关的系统内核函数几乎都是我们自己在内核驱动中实现的函数,所以打算把操作EPROCESS的DebugPort的代码地方都改下。改成操作另一个结构体,里面专门对应要调试的进程的EPROCESS进程环境块和调试对象(DebugObject)。比如NtDebugActiveProcess-DbgkpSetProcessDebugObject()里面会对要调试进程的EPROCESS的DebugPort设置成NtCreateDebugObject()的调试对象。这里不们不把被调试进程的EPROCES

49、S的DebugPort设置成指向那调试对象,而是移到上面说的结构中去。我们此时还要HOOK 一些常见的Windows内核函数,比如建立线程时,结束线程时,加载DLL时,怎么会把调试事件信息通知到我们建立的这种对应的结构体中。我们要Hook 这些常见的函数像建立线程时的PspUserThreadStartup(),它里面会调用DbgkCreateThread()向被调试进程EPROCESS的DebugPort发消息,我们在这里要处理,还有就是PspExitThread(),NtMapViewOfSection()等等很多函数都要处理的。我们还要Hook Windows的内核异常处理机制,比如我们

50、用OD调试器经常F2下的断点,都是int 3断点,我们要这CPU 执行Int 3这个指令时向相应的EPROCESS的DebugPort发送调试事件信息,我们就要HOOK Windows的异常处理函数。系统流程图如下图 3-3为整个系统整体流程图:4系统概要设计 应用程序模块当我们把我们写的OD插件放入OD调试器目录下的Plugin文件夹中后,OD调试器启动时就会调用ODBG_Plugindata()这个回调函数,我们在这里调用HxLoadKrnl()函数负责加载我们的内核驱动,我们的插件在OD调试器插件菜单中加入了以下两个菜单选项,BeginHook和CacelHook,当用户点击BeginH

51、ook后,会调用HxKrnl_Init()这个函数,这个函数里面主要是调用DeviceIoControl()这个函数向我们的内核驱动依次发送相关命令。先是发送HXIOCTL_INIT这个命令,要求我们的内核驱动从Windows内核下得到ntkrnlpa.exe,hal.dll内核模块的加载地址,然后返回给我们的OD应用层插件;接着发送HXIOCTL_GET_FNNUMBERLIST命令,会取得内核国未导出函数的个数,然后发送HXIOCTL_GET_FNLIST命令,会取得这些内核未导出函数的名字,之后我们OD插件解析内核PDB文件得到这些未导出函数的地址,然后再次向我们的驱动发送HXIOCTL

52、_SET_DRIVER命令,通知驱动开启把这些函数地址设置到函数指针变量中。接着OD插件会发送HXIOCTL_BEGIN_ADDSERVICE命令,要求驱动在Windows系统内核下加入我们自己的系统调用表。最后OD插件会调用Hook()函数HOOK OD进程空间ntdll.dll的KiFastSystemCall()函数,之后当OD进程再次调用API时就会进入我们的函数,我们HxKiFastSystemCall()函数里面会对其系统调用ID进行判断,如果是我们感兴趣的系统调用ID,我们在这里把它改变成我们在内核添加的对应的系统调用ID,然后调用sysenter指令一样的进入Ring0,Rin

53、g0的响应函数KiFastCallEntry()会根据我们传进来的系统调用ID找到我们对应的系统调用然后调用之。 内核驱动模块 内核驱动模块有一部分是专门和我们的OD应用层插件通讯用的,当我们的OD插件调用DeviceIoControl()函数向我们驱动发送相关命令时,我们会在HxCtrl()这个函数中做相关处理,对于HXIOCTL_INIT命令,我们会调用HxGetKenelNameAndLoadAddr()这个函数遍历系统当前所有加载的内核列表中找到ntkrnlpa.exe和hal.dll,然后返回当前的加载地址,返回给我们的应用层OD插件,对于HXIOCTL_GET_FNNUMBERLI

54、ST命令,我们返回内核未导出函数的个数,对于HXIOCTL_GET_FNLIST命令,我们返回这些未导出函数的名字,对于HXIOCTL_SET_DRIVER命令,我们把OD插件得到的内核未导出的函数地址设置到函数指针变量中来,对于HXIOCTL_BEGIN_ADDSERVICE命令,我们在Windows内核下加入我们自己的系统调用表,对于HXIOCTL_FREE这个命令,我们做好一些卸载善后操作,因为OD插件通知我们的内核驱动要退出了。内核驱动还有一部分就是实现的这些系统调用函数和调试模块。5系统详细设计KiFastSystemCall()函数下面这是用Windbg看的KiFastSystem

55、Call函数,如下,它只有四个字节,一般的HOOK至少要5个字节以上。所以我们要采用另一种方式HOOK,在ntdll.dll里找到一片没有用的内存,正好KiFastSystemCall上面10个字节就没有用到。所以我们可以在这10个字节里填入我们最终要跳转到的地址,然后从KiFastSystemCall函数开头处改写代码使其跳到KiFastSystemCall-10字节处就行了,具体的HOOK代码和UnHook代码如下:0:001 u KiFastSystemCallntdll!KiFastSystemCall:7c92e4f0 8bd4 mov edx,esp7c92e4f2 0f34 sy

56、senter BOOL HOOK()PCHAR fun;UCHAR HookCode5=0;UCHAR Hook2=0;fun = (PCHAR)GetProcAddress(GetModuleHandle(ntdll.dll),KiFastSystemCall);/得到KiFastSystemCall函数映射的地址if (fun!= NULL)DWORD lOldProtect;DWORD oldprotect;fun-=10;if(VirtualProtect(PVOID)(DWORD)fun),14,PAGE_EXECUTE_READWRITE,&lOldProtect)/改写代码段内存

57、为可读写和执行的权限,方便我们能改写代码段fun0=0 xE9;*(DWORD*)(fun+1)=(DWORD)HxKiFastSystemCall - (DWORD)fun-5);fun+=10;fun0=0 xEB;fun1=0 xF4;VirtualProtect(PVOID)(DWORD)fun-10),14,lOldProtect,&oldprotect);hookFunctionAddr=fun;return TRUE;return FALSE; BOOL UNHOOK()if (hookFunctionAddr!= NULL)DWORD lOldProtect;PCHAR fun

58、 = hookFunctionAddr;if(VirtualProtect(PVOID)(DWORD)fun),4,PAGE_EXECUTE_READWRITE,&lOldProtect)fun0=0 x8b;fun1=0 xd4;VirtualProtect(PVOID)(DWORD)fun),4,lOldProtect,NULL);return TRUE;实现HOOK的HxKiFastSystemCall(),改变系统调用的流程当OD调试器调用一般的API时比如OpenProcess(),ReadVirtualMemory(),WriteVirtualMemory()函数时,最终会进入Ki

59、FastSystemCall()这个函数,从而进入我们自己实现的HxKiFastSystemCall()函数,在进入HxKiFastSystemCall函数的环境下,eax指向调用此API对应的系统调用ID,esp指向堆栈,里面放着用户层传来的参数。在这里我们把此函数定义成_declspec( naked ),这么定义是为了防止编译器额外的加上优化和自动保护堆栈代码。在此之前我们先要把esp压栈,保存上下文。然后传进系统调用ID,然后调用FilterServiceFun()函数对我们感兴趣的服务ID进行过滤改变,然后eax返回的是我们改写过的系统调用ID,之后再次调用sysenter真正的进行

60、系统调用,实现Ring3进入Ring0层,这里没用sysenter指令,因为编译器不识别它,我用的是二进制代码,具体实现代码如下:_declspec( naked ) HxKiFastSystemCall() _asm push esp; /保存堆栈 push eax; /服务Id call FilterServiceFun; pop esp; mov edx,esp _asm _emit 0 x0F /sysenter指令进入ring0 _asm _emit 0 x34extern DWORD MyServiceStartID;int WINAPI FilterServiceFun(DWOR

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论