过一般保护.docx

上传人:b****6 文档编号:5998178 上传时间:2023-01-02 格式:DOCX 页数:42 大小:436.69KB
下载 相关 举报
过一般保护.docx_第1页
第1页 / 共42页
过一般保护.docx_第2页
第2页 / 共42页
过一般保护.docx_第3页
第3页 / 共42页
过一般保护.docx_第4页
第4页 / 共42页
过一般保护.docx_第5页
第5页 / 共42页
点击查看更多>>
下载资源
资源描述

过一般保护.docx

《过一般保护.docx》由会员分享,可在线阅读,更多相关《过一般保护.docx(42页珍藏版)》请在冰豆网上搜索。

过一般保护.docx

过一般保护

目录

1绪论2

1.1研究背景2

1.2本文的主要研究内容3

2Windows系统基础4

2.1OD插件知识4

2.1.1OD插件简介4

2.1.2如何编写OD插件5

2.2Windows系统理论知识5

2.2.1Windows进程虚拟地址空间5

2.2.2Windows系统调用6

2.2.3Windows句柄理解7

2.2.4Windows切换进程空间8

2.3Windows调试系统原理8

2.3.1Windows调试系统用户模块8

2.3.2Windows调试系统内核模块9

3系统需求分析9

3.1存在的主要问题9

3.2解决方案10

3.3系统需求分析10

3.4系统流程图12

4系统概要设计13

4.1应用程序模块13

4.2内核驱动模块14

5系统详细设计15

5.1HOOKntdll.dll的KiFastSystemCall()函数15

5.2实现HOOK的HxKiFastSystemCall(),改变系统调用的流程16

5.3解析微软提供的PDB文件得到未导出的内核函数地址18

5.4向Windows系统内核中加入我们自己的系统服务表24

5.5实现我们自己的系统调用函数27

5.6移除EPROCESS->DebugPort端口31

5.7HOOKWindows内核下会发送调试事件的内核函数32

5.8HOOKWindows内核异常处理函数35

系统开发小结36

参考文献36

致谢37

基于OD插件的内核调试器的设计与实现

摘要:

随着计算机互联网技术的飞速发展,网络游戏得到了很好的发展,它的保护也是日趋完善,几种常见的保护有nProtectGameGuard(NP),hackshield(HS),让OllyDbg调试器(OD)不能调试,CheatEngine(CE)不能搜索游戏内存,让人们没法开始逆向它们,本文即在这种背景下为了满足人们的工作需求而设计的。

系统以VC6.0应用程序编译器和WINDDK3790.1830驱动编译器作为开发工具,以OD调试器提供的静态库,WindowResearchKernel(WRK)的源码为基础,HOOKOD进程空间ntdll.dll动态库的KiFastSystemCall()函数,通过我们自己在Windows内核下加的系统服务表,使其当OD调试器调用我们感兴趣的系统调用时,跳到我们自己的系统调用,这样使其成功调用,突破游戏的一般保护,本文以C语言为编程语言,本系统是一个具有实际应用意义的典型系统。

关键词:

OD插件;NP;HS;游戏保护;

ODplug-inbasedonthekerneldebuggerDesignandImplementation

Abstract:

WiththerapiddevelopmentofInternettechnology,computer,onlinegameshavebeenwelldeveloped,anditsprotectionisalsomaturing,severalcommonprotectnProtectGameGuard(NP),hackshield(HS),letOllyDbgdebugger(OD)cannot debugging,CheatEngine(CE)cannotsearchforgamesofmemory,sothatpeoplecannotstartreversethem,thisisinthiscontextinordertomeettheneedsofpeopleworkdesigned. ApplicationsystemtoVC6.0compilerandWINDDK3790.1830drivethecompilerasadevelopmenttooltoODdebuggerprovidesstaticlibraries,WindowResearchKernel(WRK)source-based,HOOKODprocessspaceofKiFastSystemCalldynamiclibraryntdll.dll ()function,throughourownintheWindowskernelplusthesystemservicetable,sothatthedebuggeriscalledwhentheODsystemcallwhenweareinterested,skiptoourownsystemcalls,socallitsuccess,breakingthegameingeneral protectionofthispaper,Clanguageprogramminglanguage,thesystemisatypicalsystem,thepracticalapplicationofsignificance.

Keywords:

ODPlugins;NP;HS;GameProtect

1绪论

1.1研究背景

进入二十一世纪以来,随着社会的不断进步和互联网产业技术的飞速发展,网络游戏得到了空前的流行,研究它的人也越来越大众化。

因此,因为种种原因,一些游戏和商业程序就会防止OD调试器和CE数据搜索器搜索游戏的数据防止逆向工作人员逆向这们,一些防调试方法如下:

1,比如检测系统当前系统运行的进程看有没有Od和Ce,查找窗口看有没有它们,还有就是搜索Od和Ce的特征码看有没有它们,有就退出。

2,调用IsDebugPresent()函数,检测这个游戏进程是不是被调试。

3,HookDbgUiRemoteBreakIn()函数防止运行int3断点中止游戏的程序运行,这样能防止OD调试器附加游戏进程。

4,游戏故意产生异常,然后自己去处理它,如果有调试的话,异常一般会先给调试器,由调试器处理了,所以游戏就不会进入它自己的异常处理,这样它就检测出了调试器。

5,还有就是nProtectGameGuard(NP),hackshield(HS),PerfectProtect.sys等等的保护,一般它们都是差不多的,比如系统服务表(SSDT表)HOOK,内核下InlineHOOK,一般HOOKNtOpenProcess()防止打开游戏进程得到句柄,HOOKNtReadVirtualMemory(),NtWriteVirtualMemory()防止读写游戏进程空间的内存,还有就是HOOKNtDebugActiveProcess(),NtCreateDebugObject(),NtWaitForDebugEvent()等等一系列与调试有关的系统调用防止调试游戏,还有就是HOOK每个CPU的中断描述符表(IDT)表中的INT1和INT3断点,防止对游戏进程下断产生单步调试事件和异常调试事件,还有一种就是内核下建立一个内核线程一直对游戏的进程控制块(EPROCESS)结构体的(调试端口)DebugPort一直清0,防止游戏被Od调试收到调试信息。

综上所述,第5种保护方法一般是现在最流行的保护,有很多的人们在研究它们的保护,他们一般采用的方法是跳过这些函数的调用,比如对于SSDTHOOK,它们可以在KiFastCallEntry()函数里进行处理,对于InlineHOOK,我们可以先HOOKSSDT进入我们自己的系统调用,然后才调用真正的函数,对于调试端口DebugPort清0的检测我们可以把Windows内一切操作EPROCESS.DebugPort端口的内核函数,改变这些偏移,使其操作EPROCESS结构中的其它没有多大用的结构成员,比如EPROCESS.time。

这种方法针对一般的保护有效,但是对于特定的保护,我们可以特别的处理,比如Tessafe.sys你可以把自己加入白名单等等处理方法,还有对于HS我们可以调用一个14号功能函数通知HS保护驱动退出等等许多的方法。

1.2本文的主要研究内容

基于OD插件的内核调试器系统主要是在OD调试器的基础上编写OD插件,HOOKOD进程空间的ntdll.dll动态库的KiFastSystemCall()函数,改变OD进程调用一些关键系统函数的调用流程,使其进入我们自己在内核中的实现的系统调用,这样能够绕过一般的保护驱动的钩子。

本系统是主要分为四个主要的功能来实现:

1,编写OD应用程序插件,利用OD调试器提供给我们的OLLYDBG.LIB静态库和Plugin.h头文件来编写OD插件,在OD调试器上加个菜单选项用于控制OD调试器是不是启动我们的这种程序机制。

比如当我们点BeginHook菜单时,我们的程序就开始工作,HOOKntdll.dll动态库的KiFastSystemCall()函数,Windows提供给程序员的一般的API,比如Kernel32.dll里的打开进程OpenProcess(),读进程内存ReadVirtualmemory(),写进程内存WriteVirtualMemory()等等API函数,最终的系统调用都会调用这个函数,这时eax是系统调用ID,堆栈里有调用这个系统调用时的各个参数,这样,我们可以改变这些系统调用ID,使其调用我们自己在Windows系统内核添加的系统调用。

当我们点CancelHook时,我们的程序就停止工作。

2,由于我们内核驱动要调用的很多内核函数是ntkrnelpa.exe内核文件未导出的,所以我们要自己获取内核函数在当前系统的地址,很多同学采用的是暴力搜索内存然后匹配方法处理的,这种方法效率欠缺,并且由于好多硬编码,移值性很差。

我是这么做的,在内核驱动下,把这些未导出的函数申明为函数变量指针,然后用户层程序(OD插件)调用DeviceIoControlFile()函数和驱动通信取得这些未导出的函数的名字,然后我们自己解析微软提供给我们的符号文件(PDB文件),得到这个版本系统的未导出的函数在这个系统版本的内核加载地址。

然后再次调用DeviceIoControlFile()函数传给内核驱动。

3,把微软提供的WindowsReasarchKernel(WRK)里的dbgk目录下的dbgkobj.c,dbgkport.c,dbgkproc.c,dbgkp.h文件移值到我们的内核驱动工程里来,这几个.c文件是Windows系统调试系统机制的实现,只要我们集成进我们的驱动里来了,以后游戏保护对这些调试机制的一切HOOK与检测,我们可以不用去管它了,相当于OD插件它们用的是我们自己集成的调试系统机制。

还有内核线程对EPROCESS结构体的DebugPort清0,我们也可以跳过它。

还有就是增加游戏常HOOK的几个关键函数,比如NtOpenProcess(),NtReadVirtualMemory(),NtWriteVirtualMemory()等等,我们自己在Windows内核中实现它,下次OD调试器调用这些函数对游戏进程操作时,都会进入我们自己的这些系统调用,到时想实现什么功能就实现什么功能。

4,Windows系统内核下有两种系统服务表KeServiceDescriptorTable和KeServiceDescriptorTableShadow,对于图形界面(GUI)线程,它引用的是KeServiceDescriptorTableShadow这个系统服务表,非GUI线程引用的是KeServiceDescriptorTable系统服务表。

对于微软提供的大多数API,内核下都有专门对应的系统调用,比如当我们调用OpenProcess()API函数时,最终会调用系统调用NtOpenProcess()。

每一个系统调用ID,在Windows内核下都会对应一个系统调用函数,比如NtOpenProcess的系统调用ID,在WindowsXp3的系统下ID为122。

WindowsXp3系统大概提供了283个系统调用。

因此我们自己可以在这两种系统服务表的后面加上我们自己的系统调用函数,对应相应的系统调用ID,这样当OD调试器调用我们感兴趣的系统调用时,我们可以改变系统调用ID,这样就可以进入我们自己实现的系统调用函数。

2Windows系统基础

2.1OD插件知识

2.1.1OD插件简介

OllyDbg简称(OD)是一款优秀的用户态调试工具。

它不仅拥有强大的反汇编能力和动态分析力,还具有良好的扩展结构,允许用户自行开发插件完成特定的工作。

OD插件也可以有自己的窗口逻辑和功能函数。

事实上,我们可以将它看成这样一个Windows程序,它拥有自己的消息循环和窗口过程,但它的启动是由OllyDbg发起的,具体功能的实现也通过调用OllyDbg提供的函数来实现的。

它的启动过程如下:

在OllyDbg的启动过程中,有一步是检查插件路径下是否存在DLL文件。

如果存在,逐一进行如下扫描:

1,加载该DLL文件,找到其入口点。

2,通过回调函数,获取插件名称、版本等信息。

3,通过回调函数,对插件进行初始化,包括申请资源、恢复全局参数等。

如果某个DLL文件无法顺利执行这三步,OllyDbg的启动将失败、报错并退出。

OllyDbg启动以后,会一直维护插件的队列,在特定时间向该队列发送消息,或者直接调用插件中定义的函数,用户通过插件菜单或快捷键主动执行插件某功能。

最后,当OllyDbg被关闭时,还会调用插件中的回调函数,释放插件申请到的资源,并将需要保存的参数、配置和附加信息分别予以保存。

2.1.2如何编写OD插件

新建立一个VC6.0的工程,把Plugin.h头文件和OLLYDBG.LIB静态库文件加进工程里面来,还有在工程里加入/J选项,使使char默认为unsigned类型,这是OllyDbg中的约定。

然后定义两个回调函数,这是OD插件必需的两个回调函数,ODBG_Plugindata()和ODBG_Plugininit()函数,之所以说是回调函数,是因为这是由OD调试器调用它们的。

比如我们在ODBG_Plugindata()回调函数中传递我们插件的名字进去;在ODBG_Plugininit()中把OD窗口句柄保存起来;我们还实现了几个回调函数,ODBG_Pluginmenu(),ODBG_Pluginaction(),ODBG_Plugindestroy()函数。

这些函数用于在OD插件菜单中加入菜单选项和我们的响应函数。

我们可以在OD插件里面调用OD提供很多的API函数,比如有Breakpointfunctions,Memoryfunctions,Threadfunctions,Modulefunctions等等许多的函数库。

之后编译成功后把它们放在OD调试器目录下的Plugin文件夹下就行了。

2.2Windows系统理论知识

2.2.1Windows进程虚拟地址空间

如上图4-1-1,在Windows系统中,每一个进程都有4GB的虚拟地址空间。

0—2GB是Windows进程的用户空间,2GB到4GB是Windows的内核空间。

物理地址扩展(PAE)除外,这时,每个进程的用户空间为0—3GB。

每个进程有4GB的虚拟地址空间,并不是指它真正有4GB的物理内存,而是指每个进程都有其页目录表和页表,通过它会把每个进程映射成4GB虚拟内存空间。

进程用户空间0-2GB的映射,页表一般会把进程的这段虚拟内存空间映射到不同的物理内存中。

但是有例外,就是Windows常见的DLL和可执行映象文件,比如kernel32.dll,ntdll.dll和.exe文件运行时的情况。

它们有写入时复制(CopyOnWrite)机制,在没有改写这些代码前,它们在物理内存中只有一份内存,所有的进程都会根据页目录和页表映射到这份物理内存。

但是当有一个进程改变这些DLL的内容时,写入时复制就发生了。

Windows这时会把这些内容在物理内存中拷贝一份,改变这个进程的页目录和页表,使之指向这份拷贝的物理内存,对其它的进程没有任何影响,所以这就是用户层HOOK系统DLL时,只对本进程有效,对其它进程无效的原因。

所以当我们HOOK一个进程的ntdll.dll的KiFastSystemCall()时,它只会对我们HOOK的这个进程的KiFastSystemCall()函数起作用。

对别的进程没有影响。

进程内核空间2—4GB空间的映射,通常系统中每个进程的这段虚拟内存空间会根据页目录表和页表映射到相同的物理内存中。

比如当我们改写一处内存,此时对每个进程都会有效的,对整个系统也都是有效的。

2.2.2Windows系统调用

下面以WindowsXP3系统分析Windows用户层API是怎么调用系统调用的全过程。

图4-1-2是Windows系统进行系统调用的全过程描述:

比如当我们用户层进程调用常见的API比如Win32API比如OpenProcess(),ReadVirtualMemory(),WriteVirtualMemory(),CreateFile()等等函数,它们接着会依次调用ntdll.dll的Zw********函数,Zw**********函数会传递系统调用ID,然后调用sysenter指令进入ring0,在Windows内核下首先执行的是KiFastCallEntry()函数,它会根据系统调用ID分别从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中取得相应的系统调用函数地址,然后调用之。

对于GUI线程是从KeServiceDescriptorTableShadow表中取,非GUI线程是从KeServiceDescriptorTable表中取的。

所以我们可以HOOKKiFastCallEntry()这个函数,我们就可以拦截一切的系统调用。

360安全卫士就是使用这种方法拦截系统调用的。

当这个系统调用成功返回后,KiFastCallEntry()函数会调用sysexit指令退出本次的系统调用,然后返回到用户进程空间,依次返回到调用用户API的地方。

2.2.3Windows句柄理解

句柄是Windows系统的特性。

你可以简单理解为通过它你就可以访问相应的内核对象。

其实Windows是这么设计的,每个进程的EPROCESS结构体中都有个ObjectTable成员指向进程句柄表(HANDLE_TABLE),里面每一项都是HANDLE_TABLE_ENTRY句柄表项。

里面放着相应对象的地址,句柄只是这些表项的索引。

还有一点值的注意的是句柄是进程相关的。

你在本进程打开的进程句柄,在别的进程当中不能引用它的。

它们的结构体定义如下:

lkd>dt_HANDLE_TABLE

nt!

_HANDLE_TABLE

+0x000TableCode:

Uint4B//指向_HANDLE_TABLE_ENTRY数组

+0x004QuotaProcess:

Ptr32_EPROCESS//这个句柄表属于哪个进程

+0x008UniqueProcessId:

Ptr32Void

+0x00cHandleTableLock:

[4]_EX_PUSH_LOCK

+0x01cHandleTableList:

_LIST_ENTRY

+0x024HandleContentionEvent:

_EX_PUSH_LOCK

+0x028DebugInfo:

Ptr32_HANDLE_TRACE_DEBUG_INFO

+0x02cExtraInfoPages:

Int4B

+0x030FirstFree:

Uint4B

+0x034LastFree:

Uint4B

+0x038NextHandleNeedingPool:

Uint4B

+0x03cHandleCount:

Int4B//句柄表的数量

+0x040Flags:

Uint4B

+0x040StrictFIFO:

Pos0,1Bit

lkd>dt_HANDLE_TABLE_ENTRY

nt!

_HANDLE_TABLE_ENTRY

+0x000Object:

Ptr32Void//指向内核对象的地址

+0x004GrantedAccess:

Uint4B

比如当我们在用户进程调用kernel32.dll里的OpenProcess()打开进程时,它先会调ntdll.dll的ZwOpenProcess(),接着会传递NtOpenProcess()的系统调用ID,调用KiFastSystemCall()函数,这函数里面会调用sysenter指令进入Ring0,然后由KiFastCallEntry()根据系统调用ID从KeServiceDescriptorTable或者KeServiceDescriptorTableShadow这两种系统调用表中找到NtOpenProcess()调用之。

NtOpenProcess()里面会根据要打开的进程ID还是进程名字调用PsLookupProcessByProcessId()这个函数得到这个进程对象EPROCESS的地址,最后调用ObOpenObjectByPointer()返回进程句柄。

ObOpenObjectByPointer()函数大致工作如下:

首先会增加这个EPROCESS对象的引用计数,然后构造一个_HANDLE_TABLE_ENTRY结构,填入对象的地址,然后把它加进这个用户进程的句柄表(HANDLE_TBALE)中,最后返回句柄(索引)。

2.2.4Windows切换进程空间

一般而言,如果线程T属于进程P,那么当这个线程在内核中运行时的用户空间应该就是进程P的用户空间。

它也没有必要访问到别的用户进程空间去,可是Windows允许一些跨进程的操作,特别是跨用户进程空间的操作。

所以有时候就需要把当时的用户空间切换到别的进程空间中去。

Windows提供的函数是KeStackAttachProcess()和KiAttachProcess()。

它的原理其实就是改变CPU的CR3寄存器使之指向要切换进程的页目录表。

因为CPU访问进程用户层空间地址都是通过CR3找页目录表,然后通过Windows内存管理器把虚拟地址映射成物理地址才去访问的,所以只要我们改变CR3寄存器就行了。

Windows有很多这种跨进程的操作,例如调试DbgkpPostFakeProcessCreateMessages()函数会调用KeStackAttachProcess()这个函数切换进被调试进程的用户层空间中,因为DbgkpPostFakeModuleMessages()这个函数会访问被调试进程的进程环境块(PEB),然后遍历它的用户层模块链表。

最后会调用KeUnstackDetachProcess()这个函数回到OD调试器的进程空间来。

2.3Windows调试系统原理

2.3.1Windows调试系统用户模块

Windows系统在用户层提供了很多的调试API供用户程序调用,它们分别在kernel32.dll和ntdll.d

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1