毕业设计翻译的文章-系统范围内挂钩本地API控制进程的创建

系统范围内挂钩本地API控制进程的创建

翻译得好烂,这就是英语没过CET4的下场,译文中,我删除了代码,需要的话,请看原文.

原文:

Hooking the native API and controlling process creation on a system-wide basis

http://www.codeproject.com/KB/system/soviet_protector.aspx

译文:

最近我碰到的描述相当有趣的安全产品,叫避难所. 这个产品不允许执行任何程式,并没有出现在名单上的软件,即允许 运行在一个特定的程序. 这样电脑用户就可以免受各种间谍 蠕虫和木马的侵袭-即使有一块恶意的认定方式,以他/她的电脑, 它已没有机会再被执行,因此,没有机会造成任何破坏. 当然,我发现这个有趣的特点,并且经过一点思考,提出了自己的实施方案. 因此, 这篇文章描述如何实现过程,可以以编程的方式监测与控制系统的基础上,挂钩本地API .

 

这篇文章提出了"大胆"假设目标进程正在创建用户模式代码(外壳函数, CreateProcess() , 最终结果还是调用本地API) . 虽然,从理论上说,一个进程可能调用内核模式代码,这种可能性是出于实际目的,微不足道的, 所以,我们不用担心. 为什么? ? ? 想想实际情况,为启动程序,由内核模式, 必须装载了驱动程序,这反过来意味着,应该先执行用户模式代码. 因此,为了防止执行未经许可的程序, 我们可以安全地把自己局限在控制进程创建的用户模式代码一个系统范围内.

 

确定我们的策略

 

首先, 让我们决定我们要做的,建立一个监测和控制过程,在系统范围内.

 

创建进程是一个相当复杂的事,其中涉及相当多的工作(如果你不相信我, 你可以反汇编CreateProcess( ) ) . 为了启动一个进程,之后必须采取步骤:

------

1 可执行文件必须以 FILE_EXECUTE 方式打开

2 执行影象必须加载进RAM

3 进程对象(EPROCESS,KPROCESS PEB 结构体)被创建

4 分配进程的地址空间

5 主线程对象(ETHREAD,KTHREAD TEB 结构体)被创建

6 分配主线程栈

7 进程的主线程的上下文被创建

8 win32子系统记录新进程信息

---------

 

为了使这些步骤,要取得成功, 之前所有的步骤都必须成功地完成了(你不能设立一个进程过程对象无效句柄 可执行科; 你不能映射一个没有句柄的执行对象等) . 因此,如果我们决定退出任何这些步骤,随后所有的失败一样, 使创建过程会胎死腹中. 这是可以理解的,所有上述步骤,采取的手段是修改某些本地API函数. 因此,为了监测和控制进程创建, 我们所要做的是挂钩那些API函数,不能绕开的代码, 即将创建一项新的进程.

 

那个本地API要挂钩? 虽然NtCreateProcess ( )似乎是最明显的答案, 这个答案是错的,创建一个进程可能不会调用此函数. 例如, CreateProcess( )设置与进程相关的内核模式结构,而不需要NtCreateProcess ( ) . 因此,挂钩NtCreateProcess ( )对我们没有帮助.

 

为了监视进程的创建,我们要么挂钩NtCreateFile ( )NtOpenFile ( ) , 或者NtCreateSection ( ) ,创建进程是绝对要调用上述API. 如果我们决定要挂钩NtCreateFile ( )NtOpenFile ( ) , 那我们要区分,创建进程和普通的文件IO的行动. 这项任务并不容易. 举例来说,我们应该怎样做,如果一些可执行文件正以file_all_access打开呢 ? ? ? 只是一个IO的操作还是一个进程创建? ? ? 很难作出判断,在这一点我们必须清楚地看到哪些调用线程 即将做什么. 因此,挂钩NtCreateFile ( )NtOpenFile ( )不是最佳的选择.

 

挂钩NtCreateSection ( )是一个更为合理的事情,如果我们拦截NtCreateSection( )掉用 请求映射可执行文件作为图像( sec_image属性) , 结合请求页面保护,使执行 我们可以肯定,这一进程即将执行. 在这点上我们是能够作出判断的,并如果,如果我们不希望这一进程创造,使NtCreateSection( )返回status_access_denied . 因此,为了充分控制进程在目标机器上创建, 我们所要做的是在全局范围内钩钩NtCreateSection( ).

 

ntdll.dll其它调用代理那样 NtCreateSection ( )使用EAX加载系统服务索引号,EDX指向函数参数, 然后转换到内核模式执行KiDiSpatchService ( )内核模式例程(windows2000中由int 2eH实现,WindowsXP则由sysenter实现 ) . 经过验证功能参数后, KiDiSpatchService ( )的执行转移到实际执行的服务, 地势可从服务描述表(指向这个表是作为ntoskrnl.exe keservicedescriptortable的导出变量,因此它是提供给内核模式驱动) . 服务描述表的结构如下:

 

servicetable这个结构体的地址数组完成的全部功能,系统服务的实现. 因此, 我们要做的,任何在全局范围内挂钩本地API函数基础是改写KeServiceDescriptorTableservicetable地址使之转向我们的代理函数。

 

像现在,我们知道我们需要知道的一切,在全局范围内监控进程的创建. 让我们开始实际工作.

 

我们的解决方案包含了内核模式驱动程序和用户模式程序. 为了开始监测过程中创造,我们的程序过滤掉ntcreatesection ( )相应的服务索引 , 再加上地址的交换缓冲区,我们的驱动. 这是由以下代码:

 

代码不需要太多的注释,唯一值得有点注意的是,我们得到服务索引的方式.ntdll.dll里面的所有服务代理的开头都是mov eax , serviceindex , 它适用于任何版本Windows NT . 这是一个5字节的指令, 其中MOV EAX opcode作为第一字节和服务索引作为剩余的4个字节. 因此,为了得到服务索引号,定位与本地API,取得后四个字节.

 

现在让我们看看,我们的驱动程序处理我们的应用程序发出的ioctl:

 

如你所见, 没有什么特别到这里,我们只映射交换缓冲到内核地址空间通过调用MmMapIoSpace ( ) , 再加上写的地址是我们的代理函数的服务表(当然, 我们这样做是经过储存地址中的实际执行的服务,在全局RealCallee变量) . 为了改写服务表,我们的映射目标地址通过MmMapIoSpace ( ) . 我们为什么要这样做? 毕竟,我们已访问一个以服务表,不就? 目前的问题是,服务表可能放在只读存储器. 因此,我们要检查是否有写权限在目标页面上。,如果我们不这样做, 我们要改变页保护之前,改写的服务表. 太多的工作,你难道不好吗? 因此,我们只是映射目标地址通过MmMapIoSpace ( ) , 所以不用担心更多的页保护,从现在开始我们理所当然的可以采取写的方式对目标页面. 现在让我们看看我们代理函数的功能:

 

Proxy( )保存寄存器和标志,把一个指向服务参数的指针入栈,并调用check( ) . 剩余的工作由check( )返回. 如果check( )返回TRUE (即要着手处理请求) ,Proxy( )恢复寄存器和标志 并将控制权转让给系统服务完成. 否则,Proxy( )EAX写入STATUS_ACCESS_DENIED , 恢复ESP并返回,从调用者的角度看来NtCreateSection ( )调用失败返回STATUS_ACCESS_DENIED错误状态.

 

check( )如何作出决定? 一旦它收到一个指向服务参数,可以检查这些参数. 首先, 它检查标志位和参数,如果区域对象不要求将其映射为一个可执行的形象, 或者如果被请求页面保护不允许执行, 我们可以肯定地说ntcreatesection ( )调用与创建进程无关. 在这种情况下检查( )返回TRUE . 否则,它会检查扩展名, sec_image的属性和页面保护,使运行的时间可能会要求载入一些映射的DLL文件. 如果扩展名不是. exe文件,检查( )返回TRUE . 否则,它给用户模式代码一个机会做出决定. 因此,它只是写到档案名称和路径交换缓冲区 由用户决定,直到得到回应.

 

在打开我们的驱动之前,我们的应用开启了一个线程完成下列功能:

 

这个代码不需要太多的说明--我们的线程每10ms便检查一次缓冲. 如果发现我们的驱动已将其请求的缓冲区, 它检查扩展名称和路径, 如果匹配,它立刻返回一个OK. 否则,它显示一个对话框,询问用户是否允程序执行. 如果运行程序, 我们把程序添加到安全程序列表. 最后,我们写用户回应缓冲,通知我们的驱动程序. 现在,我们已经完全控制了进程的创建,如果没有用户的许可,进程是不可能被创建的.

 

如你所见,我们让内核方式代码等待用户响应. 这是一件好事呢? ? ? 为了回答这个问题, 你要问自己到底是什么阻塞关键系统资源-一切视情况而定. 在我们的情况一切都发生irql passive_level ,等待用户的反应并非至关重要的. 因此,在我们的情况一切都运行良好. 然而,这样写示范仅供参考. 为了使任何实际使用它,是有道理的,改写了我们的程序,作为自启动服务. 在这种情况下,我认为我们应该让程序运行在localsystem,ntcreatesection ( )调用环境中的localsystem帐户特权 从实际执行服务不进行任何检查毕竟, localsystem帐户挤提只有那些可执行文件. 因此,这种做法是不太安全的.

 

结论

 

最后,我不得不说挂钩本地API绝对是一个最强大的编程技术. 这篇文章给你,只是一个例子,我们能够做到的挂钩本地API,你可以看到,我们设法阻止并决定本地API的执行结果. 你领会一做法的同时,充分控制硬件设备,文件IO的操作,网络流量, 等等。但是,我们目前的解决方法是行不通的内核模式下可以直接调用ntoskrnl.exe的到处函数,这些调用没有必要去通过系统调度服务. 因此,在接下来的文章里我们将挂钩ntoskrnl.exe本身.

 

这个样品已经成功地测试了几个运行在windows xp sp2 的机器. 虽然我还未测试,它应该可以在任何环境下, 我相信能运行在任何环境下。为了能够正常运行,请把protector.exe protector.sys 放在同一目录下。

 

我也非常欣赏,如果你送我一个电子邮件与你的意见和建议.

 

你可能感兴趣的:(windows,工作,image,api,Access,扩展)