﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-孤独的夜-文章分类-Driver</title><link>http://www.cnitblog.com/wangk/category/1593.html</link><description>&lt;table align=center&gt;
&lt;span id="dict_daily"&gt;
&lt;a href="http://dict.cn/" target="_blank"&gt;英语学习&lt;/a&gt;
&lt;/span&gt; 
&lt;script language="JavaScript" src="http://dict.cn/daily.php" defer="defer"&gt;
&lt;/script&gt;
&lt;/table&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 28 Sep 2011 13:00:47 GMT</lastBuildDate><pubDate>Wed, 28 Sep 2011 13:00:47 GMT</pubDate><ttl>60</ttl><item><title>借用 kd/windbg 引擎访问核心态空间 [引用]</title><link>http://www.cnitblog.com/wangk/articles/16403.html</link><dc:creator>孤独的夜</dc:creator><author>孤独的夜</author><pubDate>Wed, 06 Sep 2006 02:55:00 GMT</pubDate><guid>http://www.cnitblog.com/wangk/articles/16403.html</guid><wfw:comment>http://www.cnitblog.com/wangk/comments/16403.html</wfw:comment><comments>http://www.cnitblog.com/wangk/articles/16403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/wangk/comments/commentRss/16403.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/wangk/services/trackbacks/16403.html</trackback:ping><description><![CDATA[long long ago, 传说中的 Win32 世界原本是非常美好和貌似平等的。虽然各个进程被隔离在虚拟地址空间里，也不允许随便访问内核空间。但只要你有足够强烈的愿望，以及相应的一点点技巧，还是可以通过 \Device\PhysicalMemory 自由的读写物理内存来绕过这些通向自由的限制。虽然受到管理员权限和虚拟地址到物理地址转换的限制，但好歹大家还有希望，能通过种种不懈的努力去绕过它。<br />可是突然有一天魔王放出了 Win2003 SP1，打着捍卫世界和平的旗号，偷偷的收回了用户态物理内存访问的自由，致使大量无辜的程序濒临灭绝，或者走上了自带驱动的不归路。好在还有 kd 和 windbg 的拿把石中剑，能拔出它的人就能拯救世界和平 -_-b<br /><br /><a href="http://technet2.microsoft.com/WindowsServer/en/Library/e0f862a3-cf16-4a48-bea5-f2004d12ce351033.mspx?mfr=true" target="_blank">What does \Device\PhysicalMemory Object do?</a><br /><br />哈哈，yy 结束，开始说正题 <a href="http://www.blogcn.com/images/biggrin.gif" target="_blank"><img src="http://www.blogcn.com/images/biggrin.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a><br /><br />在禁止用户态通过 \Device\PhysicalMemory 访问物理内存之后，Win2003 SP1 和至少是后续服务器平台版本上，唯有编写驱动一条途径来访问核心态空间。但一旦涉及到驱动的开发，稳定性和兼容性的问题就无法逃避，这是我等草民所无力承担的。就如歌中唱道：“没有枪没有炮，敌人给我们造”，这个时候如果能直接利用 MS 现有机制，以彼之矛攻彼之盾的话，是一种较为完美的解决方法。这儿的军火库，就是 MS 提供的 kd/windbg 调试环境公用的 dbgeng.dll 调试引擎。<br /><br /><a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx" target="_blank">Debugging Tools for Windows</a><br /><br />从 WinXP 开始，MS 在操作系统一级提供了 live kernel debug 的支持。在 windbg 的文件菜单中，点击 kernel debug 并选择 local 页，就能进入对内核自身进行调试的状态，进而完成对核心态空间的访问。<a href="http://www.sysinternals.com/" target="_blank">sysinternals</a> 提供的 <a href="http://www.sysinternals.com/Utilities/LiveKd.html" target="_blank">LiveKd</a> 也能在 win2000 上提供类似的功能。虽然这种调试本身受到一定限制，但对于访问核心态空间这类需求是绰绰有余了。<br /><br />作为通用调试引擎的 dbgeng.dll，本身提供了伪 COM 的控制和管理接口，使用者可以通过其 DebugCreate/DebugConnect(Wide) 函数，创建 IDebugClient 接口的指针，并进而通过其 QueryInterface 方法获得其它接口的思想。其中最常用的接口有以下几类：<br /><br />IDebugClient: 提供对调试引擎自身以及运行时环境的管理<br />IDebugControl: 提供对调试工作的控制，如断点、异常处理等<br />IDebugSystemObjects: 提供对调试环境的运行时系统对象的信息获取，如进程、线程等<br />IDebugSymbols: 提供对调试符号的管理支持<br />IDebugRegisters: 提供对调试目标的寄存器、堆栈等的管理<br />IDebugDataSpaces: 提供对调试目标的数据空间的访问，如虚拟地址、物理地址访问等<br />IDebugAdvanced: 提供对调试线程上下文、源文件信息等的支持<br /><br />一个典型的使用实例代码如下：<br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><table class="ubb_code" cellspacing="0" cellpadding="6" width="95%" align="right" border="0"><tbody><tr><td><b>以下内容为程序代码:</b><br /><br />void check(HRESULT hr, const char *msg, int err = -1)<br />{<br />  if (FAILED(hr))<br />  {<br />    std::cerr &lt;&lt; msg &lt;&lt; std::endl &lt;&lt; "errno = 0x" &lt;&lt; std::hex &lt;&lt; std::setw(8) &lt;&lt; std::setfill('0') &lt;&lt; hr &lt;&lt; std::endl;<br /><br />    exit(err);<br />  }<br />}<br /><br />int _tmain(int, _TCHAR *[])<br />{<br />// 1.通过 DebugCreate 创建 IDebugClient 接口的实例<br /><br />  CComPtr&lt;IDebugClient&gt; spDbgClient;<br /><br />  check(:[img]/images/biggrin.gif[/img]ebugCreate(__uuidof(IDebugClient), (PVOID *) &amp;spDbgClient), "Create debug client of engine failed."[img]/images/wink.gif[/img];  <br />  <br />  // 2.获得最新的 IDebugClient5 接口实例<br /><br />  CComPtr&lt;IDebugClient5&gt; spDbgClient5;<br /><br />  check(spDbgClient.QueryInterface(&amp;spDbgClient5), "Query interface of IDebugClient5 failed."[img]/images/wink.gif[/img];<br /><br />// 3.判断系统是否提供内核调试支持，需要在启动时通过 /debug 配置<br /><br />  bool enabled = S_OK == spDbgClient5-&gt;IsKernelDebuggerEnabled();<br />  <br />  std::cout &lt;&lt; "Kernel debugger is " &lt;&lt; (enabled ? "enabled" : "disabled"[img]/images/wink.gif[/img] &lt;&lt; std::endl;<br /><br />// 4.启动本地内核调试模式<br /><br />  installPatch();<br /><br />  check(spDbgClient5-&gt;AttachKernelWide(DEBUG_ATTACH_LOCAL_KERNEL, NULL), "Attach debugger to kernel failed."[img]/images/wink.gif[/img];<br /><br />  uninstallPatch();<br />  <br />  // 5.等待引擎初始化后的调试事件<br /><br />  CComPtr&lt;IDebugControl4&gt; spDbgControl4;<br /><br />  check(spDbgClient.QueryInterface(&amp;spDbgControl4), "Query interface of IDebugControl4 failed."[img]/images/wink.gif[/img];<br /><br />  check(spDbgControl4-&gt;WaitForEvent(0, INFINITE), "Wait for kernel debug event failed."[img]/images/wink.gif[/img];<br />  <br />  // 6.do something<br />  // ...<br /><br />return 0;<br />}<br /></td></tr></tbody></table></td></tr></tbody></table><br />就代码逻辑上非常简单，但如果要让这个代码顺利跑起来，还需要做一些准备工作。<br /><br />首先要从 kd.exe/windbg.exe 里面把实际的内核驱动扒出来，呵呵。这个工作可以通过 PE Explorer 或者 Borland 的 Resource Workshop 之类工具，打开 exe 文件并提取类型为 30583 名称为 17476 的资源，并另存为 kldbgdrv.sys 文件。因为这个资源的类型和名字是硬编码到 dbgeng.dll 里面，因此需要通过资源定义文件 xxx.rc 的方式，将之编译到我们的目标 exe 文件中，例如在 RC 文件中增加一行：<br /><blockquote><table class="ubb_quote" cellspacing="0" cellpadding="6" width="100%" border="0"><tbody><tr><td><b>以下为引用：</b><blockquote><br />/////////////////////////////////////////////////////////////////////////////<br />//<br />// RCDATA<br />//<br /><br />30583 17476             "kldbgdrv.sys"<br /></blockquote></td></tr></tbody></table></blockquote><br /><br /><br />此外 dbgeng.dll 为了防止被其它程序误用，在 IDebugClient5-&gt;AttachKernelWide 初始化本地内核调试模式时，对当前可执行文件的名称进行了检测，只允许名称为 kd.exe/windbg.exe 的程序加载驱动。因此我们得通过 Hook API 的方式，在 installPatch/uninstallPatch 里面绕过此检测，代码如下：<br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><table class="ubb_code" cellspacing="0" cellpadding="6" width="95%" align="right" border="0"><tbody><tr><td><b>以下内容为程序代码:</b><br /><br />DWORD (WINAPI *g_fnGetModuleFileNameW)(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) = ::GetModuleFileNameW;<br /><br />DWORD WINAPI MyGetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize)<br />{<br />  DWORD dwSize = g_fnGetModuleFileNameW(hModule, lpFilename, nSize);<br /><br />  if (!hModule)<br />  {<br />    wcscpy(wcsrchr(lpFilename, L'\\'), L"\\kd.exe"[img]/images/wink.gif[/img];<br />    dwSize = wcslen(lpFilename);<br />  }<br /><br />  return dwSize;<br />}<br /><br />void installPatch(void)<br />{<br />  DetourTransactionBegin();<br />  DetourUpdateThread(GetCurrentThread());<br />  DetourAttach(&amp;(PVOID&amp;[img]/images/wink.gif[/img]g_fnGetModuleFileNameW, MyGetModuleFileNameW);<br />  DetourTransactionCommit();<br />}<br /><br />void uninstallPatch(void)<br />{<br />  DetourTransactionBegin();<br />  DetourUpdateThread(GetCurrentThread());<br />  DetourDetach(&amp;(PVOID&amp;[img]/images/wink.gif[/img]g_fnGetModuleFileNameW, MyGetModuleFileNameW);<br />  DetourTransactionCommit();<br />}<br /></td></tr></tbody></table></td></tr></tbody></table><br />这儿的 Hook API 操作直接使用的 <a href="http://research.microsoft.com/sn/detours/" target="_blank">Detours Express 2.1</a>，其提供的线程级操作事务支持功能非常实用。<br />至此我们就可以通过 IDebugDataSpaces 接口完成对核心态空间的直接读写访问了，例如：<br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><table class="ubb_code" cellspacing="0" cellpadding="6" width="95%" align="right" border="0"><tbody><tr><td><b>以下内容为程序代码:</b><br /><br />CComPtr&lt;IDebugDataSpaces4&gt; spDataSpace4;<br /><br />check(spDbgClient.QueryInterface(&amp;spDataSpace4), "Query interface of IDebugDataSpaces4 failed."[img]/images/wink.gif[/img];<br /><br />char buf[4096];<br /><br />check(spDataSpace4-&gt;ReadVirtualUncached(0x80800000, buf, sizeof(buf), &amp;dwSize), "Read virtual memory failed."[img]/images/wink.gif[/img];<br /></td></tr></tbody></table></td></tr></tbody></table><br /><br />这种模式要求发布时自带最新版本 dbgeng.dll/dbghelp.dll，并以资源方式内含 kldbgdrv.sys 驱动。估计就授权协议来说是无法用于商业产品了，呵呵，不过用来做开放的小工具还是可以容忍的，稍加改动也可应用于 x86 环境。<img src ="http://www.cnitblog.com/wangk/aggbug/16403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/wangk/" target="_blank">孤独的夜</a> 2006-09-06 10:55 <a href="http://www.cnitblog.com/wangk/articles/16403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用钩子技术控制进程创建</title><link>http://www.cnitblog.com/wangk/articles/5330.html</link><dc:creator>孤独的夜</dc:creator><author>孤独的夜</author><pubDate>Tue, 13 Dec 2005 01:27:00 GMT</pubDate><guid>http://www.cnitblog.com/wangk/articles/5330.html</guid><wfw:comment>http://www.cnitblog.com/wangk/comments/5330.html</wfw:comment><comments>http://www.cnitblog.com/wangk/articles/5330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/wangk/comments/commentRss/5330.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/wangk/services/trackbacks/5330.html</trackback:ping><description><![CDATA[<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文将描述如何通过钩住本机API的方式来实现监控一个进程的创建并在系统级上对之进行控制。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文大胆假设，目标进程是以一种用户模式(外壳函数，CreateProcess()，用一系列的本机API调用的手工的进程创建，等等)创建的。尽管从理论上，一个进程能够以内核方式启动；不过从实际来看，如此的可能性是可以忽略不计的，因此我们不必为此担心。为什么？请逻辑地思考一下-为了以内核方式启动一个进程，用户必须装载一个驱动程序，该驱动程序反过来首先要暗示某种用户模式代码的执行。因此，为了防止未被授权程序的执行，我们可以安全地在系统级上以用户模式限制我们自己控制的进程的创建。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先让我们明确，之所以这样做的目的是为了在系统级上监视和控制进程创建。</P>
<P>　　进程创建是一件相当复杂的事情-它包含相当多的工作(如果你不相信我，可以反汇编CreateProcess()，这样你就会亲眼看到这点)。为了启动一个进程，可以使用下列步骤：</P>
<P>　　1.可执行文件必须被以FILE_EXECUTE存取方式打开。</P>
<P>　　2.可执行映像必须被装载进RAM。</P>
<P>　　3.必须建立进程执行对象(EPROCESS，KPROCESS和PEB结构)。</P>
<P>　　4.必须为新建进程分配地址空间。</P>
<P>　　5.必须建立进程的主线程的线程执行对象(ETHREAD，KTHREAD和TEBstructures)。</P>
<P>　　6.必须为主线程分配堆栈。</P>
<P>　　7.必须建立进程的主线程的执行上下文。</P>
<P>　　8.必须通知Win32子系统有关该新进程的创建情况。</P>
<P>　　为确保这些步骤中的任何一步的成功，所有其前面的步骤必须是成功执行的(你不能够在没有一个可执行区句柄的情况下建立一个可执行进程对象；没有文件句柄的情况下你无法映射一个可执行区，等等)。因此，如果我们决定退出任何这些步骤，所有后面的步骤也会失败，以至于整个进程创建会失败。上面所有的步骤都可以通过调用某些本机API函数的方式来实现，这是可以理解的。因此，为了监视和控制进程创建，我们所有要做的就是钩住这些API函数-它们无法旁路掉要创建一新进程所要执行的代码。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们应该钩住哪些本机API函数呢?尽管NtCreateProcess()似乎是问题的最显然的答案，但是，这个答案是错误的-有可能不需要调用这个函数也可以创建一个新的进程。例如，CreateProcess()可以创建与进程相关的内核模式结构而不是调用NtCreateProcess()。因此，这样以来钩住NtCreateProcess()对我们毫无帮助。</P>
<P>　　为了监视进程的创建，我们必须或者钩住NtCreateFile()和NtOpenFile()，或者钩住NtCreateSection()-不经调用这些API是绝对无法运行任何可执行文件的。如果我们决定监视对NtCreateFile()和NtOpenFile()的调用，那么我们必须区别开进程创建和常规的文件IO操作。这项任务并不总是那么容易。例如，如果一些可执行文件正在被以FILE_ALL_ACCESS存取方式打开，我们该怎么办？这仅是一个IO操作还是一个进程创建的一部分？在这点上，是很难判断的-我们需要了解调用线程下一步要干什么。因此，钩住NtCreateFile()和NtOpenFile()不是最好的可能性选择。</P>
<P>　　钩住NtCreateSection()是更为合理的-如果我们想拦截对NtCreateSection()的调用，发出的请求是作为一个映像(SEC_IMAGE属性)映射可执行文件(SEC_IMAGE属性)，同时请求允许执行的页面保护；那么，我们可以确信该进程将要被启动。在这一点上，我们是能够作出决定的，并且在我们不想要创建该进程的情况下，让NtCreateSection()返回STATUS_ACCESS_DENIED。因此，为了完全控制目标机器上的进程创建，所有我们要做的是在系统级上钩住NtCreateSection()。</P>
<P>　　象来自于ntdll.dll中的任何其它代理一样，NtCreateSection()用服务索引加载EAX，使EDX指向函数参数，并且把执行权传递到KiDispatchService()内核模式例程（这是通过Windows NT/2000中的INT 0x2E指令或者Windows XP下的SYSENTER指令实现的）。在校验完函数参数之后，KiDispatchService()把执行权传递到服务的实际实现部分-它的地址可用于服务描述表（指向这个表的指针由ntoskrnl.exe作为KeServiceDescriptorTable变量所输出，所以它对于内核模式驱动程序是可用的）中。服务描述表通过下列结构所描述：</P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #000000">struct&nbsp;SYS_SERVICE_TABLE&nbsp;{&nbsp;<BR></SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">**</SPAN><SPAN style="COLOR: #000000">ServiceTable;&nbsp;<BR><FONT color=#0000ff>unsigned</FONT>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">long</SPAN><SPAN style="COLOR: #000000">&nbsp;CounterTable;&nbsp;<BR><FONT color=#0000ff>unsigned</FONT>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">long</SPAN><SPAN style="COLOR: #000000">&nbsp;ServiceLimit;&nbsp;<BR></SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">**</SPAN><SPAN style="COLOR: #000000">ArgumentsTable;&nbsp;<BR>};&nbsp;</SPAN></DIV><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个结构中的ServiceTable字段指向一个数组-它拥有所有实现系统服务的函数的地址。因此，为了在系统级上钩住任何本机API函数，所有我们必须做的是把我们的代理函数的地址写入被KeServiceDescriptorTable的ServiceTable字段所指向的数组的第i个入口(i是服务索引)。<BR>至此，看起来我们已了解了在系统级上监视和控制进程创建的一切。现在让我们开始实际的工作。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们的解决方案由一个内核模式驱动程序和一个用户模式应用程序组成。为了开始监视进程创建，我们的应用程序要把服务索引（相应于NtCreateSection()）以及交换缓冲区的地址传递到我们的驱动程序。这是由下列代码所完成的：<BR><BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">打开设备</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">device</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">CreateFile(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000"><FONT color=#ff0000>\\\\.\\PROTECTOR</FONT></SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,GENERIC_READ</SPAN><SPAN style="COLOR: #000000">|</SPAN><SPAN style="COLOR: #000000">GENERIC_WRITE,&nbsp;<BR></SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,OPEN_EXISTING,&nbsp;FILE_ATTRIBUTE_SYSTEM,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">得到NtCreateSection的索引并把它连同输出缓冲区的地址传递给设备</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000"><FONT color=#0000ff>DWORD</FONT>&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">&nbsp;addr</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(<FONT color=#0000ff>DWORD</FONT>&nbsp;</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)<BR>(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">(DWORD)GetProcAddress(GetModuleHandle(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000"><FONT color=#ff0000>ntdll.dll</FONT></SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">),</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000"><FONT color=#ff0000>NtCreateSection</FONT></SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">));<BR>ZeroMemory(outputbuff,</SPAN><SPAN style="COLOR: #000000">256</SPAN><SPAN style="COLOR: #000000">);<BR>controlbuff[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">addr[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">];<BR>controlbuff[</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(DWORD)</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">];<BR>DeviceIoControl(device,</SPAN><SPAN style="COLOR: #000000">1000</SPAN><SPAN style="COLOR: #000000">,controlbuff,</SPAN><SPAN style="COLOR: #000000">256</SPAN><SPAN style="COLOR: #000000">,controlbuff,</SPAN><SPAN style="COLOR: #000000">256</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">dw,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此代码是显然的-唯一需要注意的是我们得到服务索引的方式。所有来自于ntdll.dll的代理都从一行代码MOV EAX,ServiceIndex开始-它可以适用于任何版本和风味的Windows NT。这是一条5字节长的指令，以MOV EAX操作码作第一字节，服务索引作为留下的4字节。因此，为了得到相应于一些特别的本机API函数的服务索引，所有你要做的是从该地址读取4个字节，-位于从这个代理开始1字节距离的地方。</P>
<P>　　现在让我们看一下我们的驱动程序做什么，当它收到来自我们的应用程序的IOCTL时：<BR></P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #000000"><BR><FONT color=#0000ff>NTSTATUS</FONT>&nbsp;DrvDispatch(IN&nbsp;PDEVICE_OBJECT&nbsp;device,IN&nbsp;PIRP&nbsp;Irp)<BR>{<BR>　<FONT color=#0000ff>UCHAR</FONT></SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">buff</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;&nbsp;<FONT color=#0000ff>ULONG</FONT>&nbsp;a,base;<BR>　<FONT color=#0000ff>PIO_STACK_LOCATION</FONT>&nbsp;loc</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">IoGetCurrentIrpStackLocation(Irp);<BR>　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(loc</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">Parameters.DeviceIoControl.IoControlCode</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #000000">1000</SPAN><SPAN style="COLOR: #000000">)<BR>　{<BR>　　buff</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(UCHAR</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)Irp</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">AssociatedIrp.SystemBuffer;<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">钩住服务调度表</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">Index,buff,</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">Index</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">(ULONG)KeServiceDescriptorTable</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">ServiceTable;<BR>　　base</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(ULONG)MmMapIoSpace(MmGetPhysicalAddress((</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)a),</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);<BR>　　a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(<FONT color=#0000ff>ULONG</FONT>)</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">Proxy;<BR>　　<FONT color=#0000ff>_asm</FONT><BR>　　{<BR><FONT color=#ff1493>　　　mov&nbsp;eax,base<BR>　　　mov&nbsp;ebx,dword&nbsp;ptr[eax]<BR>　　　mov&nbsp;RealCallee,ebx<BR>　　　mov&nbsp;ebx,a<BR>　　　mov&nbsp;dword&nbsp;ptr[eax],ebx<BR></FONT>　　}<BR>　　MmUnmapIoSpace(base,</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">buff[</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　output</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">char</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)MmMapIoSpace(MmGetPhysicalAddress((</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)a),</SPAN><SPAN style="COLOR: #000000">256</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);<BR>　}<BR>　Irp</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">IoStatus.Status</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR>　IoCompleteRequest(Irp,IO_NO_INCREMENT);<BR>　</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR>}</SPAN></DIV>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正如你所见，这里没有什么特别的-我们只是通过MmMapIoSpace()来把交换缓冲区映射到内核中，另外把我们的代理函数的地址写到服务表(当然，我们这是在把实际的服务执行的地址保存到全局变量RealCallee以后这样做的)。为了改写服务表的适当入口，我们通过MmMapIoSpace()来映射目标地址。为什么我们要这样做？不管怎么说，我们已经可以存取服务表了，不是吗？问题是，服务表可能驻留在一段只读内存中。因此，我们必须检查一下是否我们有对目标空间写的权限，而如果我们没有这个权限，那么在改写服务表之前，我们必须改变页面保护。你不认为这样以来工作太多了吗？因此，我们仅用MmMapIoSpace()来映射我们的目标地址，这样以来，我们就不必担心任何的页面保护问题了-从现在开始，我们假定已有到目标页面写的权限了。现在让我们看一下我们的代理函数:<BR></P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">这个函数用来确定是否我们应该允许NtCreateSection()调用成功</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000"><FONT color=#0000ff>ULONG</FONT>&nbsp;__stdcall&nbsp;check(<FONT color=#0000ff>PULONG</FONT>&nbsp;arg)<BR>{<BR>　<FONT color=#0000ff>HANDLE</FONT>&nbsp;hand</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<FONT color=#0000ff>PFILE_OBJECT</FONT>&nbsp;file</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR>　<FONT color=#0000ff>POBJECT_HANDLE_INFORMATION</FONT>&nbsp;info;<FONT color=#0000ff>ULONG&nbsp;</FONT>a;</SPAN><SPAN style="COLOR: #0000ff">char</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">buff;<BR>　<FONT color=#0000ff>ANSI_STRING</FONT>&nbsp;str;&nbsp;<FONT color=#0000ff>LARGE_INTEGER</FONT>&nbsp;li;li.QuadPart</SPAN><SPAN style="COLOR: #000000">=-</SPAN><SPAN style="COLOR: #000000">10000</SPAN><SPAN style="COLOR: #000000">;<BR>　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">检查标志。如果所要求的存取方式不是PAGE_EXECUTE,</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">这并不要紧</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">((arg[</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">]</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">0xf0</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;<BR>　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">((arg[</SPAN><SPAN style="COLOR: #000000">5</SPAN><SPAN style="COLOR: #000000">]</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">0x01000000</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;<BR>　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">经由文件句柄得到文件名</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　hand</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(<FONT color=#0000ff>HANDLE</FONT>)arg[</SPAN><SPAN style="COLOR: #000000">6</SPAN><SPAN style="COLOR: #000000">];<BR>　ObReferenceObjectByHandle(hand,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,KernelMode,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">file,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">info);<BR>　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">file)</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;<BR>　　RtlUnicodeStringToAnsiString(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">str,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">file</SPAN><SPAN style="COLOR: #000000">-&gt;</SPAN><SPAN style="COLOR: #000000">FileName,</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">);<BR>　　a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">str.Length;buff</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">str.Buffer;<BR>　　</SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">)<BR>　　{<BR>　　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(buff[a]</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #000000">'.'){a</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">;</SPAN><SPAN style="COLOR: #0000ff">break</SPAN><SPAN style="COLOR: #000000">;}<BR>　　　a</SPAN><SPAN style="COLOR: #000000">--</SPAN><SPAN style="COLOR: #000000">;<BR>　　}<BR>　　ObDereferenceObject(file);<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">如果它是不可执行的,这也不要紧</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">返回1</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(_stricmp(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">buff[a],</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">exe</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">)){RtlFreeAnsiString(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">str);</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;}<BR>　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">现在，我们要询问用户的选择。</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">把文件名写入缓冲区，并等待直到用户显示响应</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">(第一个DWORD为1意味着我们可以继续)</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">同步存取该缓冲区</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　KeWaitForSingleObject(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">event,Executive,KernelMode,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);<BR>　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">把缓冲区的前两个DWORD置为0，</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">把字符串复制到该缓冲区中，并循环下去，直到用户把每一个</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">DWORD置为1.</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">第二个DWORD的值指明用户的响应</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　strcpy(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">output[</SPAN><SPAN style="COLOR: #000000">8</SPAN><SPAN style="COLOR: #000000">],buff);<BR>　　RtlFreeAnsiString(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">str);<BR>　　a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;<BR>　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">output[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　</SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">)<BR>　　{<BR>　　　KeDelayExecutionThread(KernelMode,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">li);<BR>　　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">output[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">a)</SPAN><SPAN style="COLOR: #0000ff">break</SPAN><SPAN style="COLOR: #000000">;<BR>　　}<BR>　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">output[</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　KeSetEvent(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">event,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">);<BR>　　</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;a;<BR>　}<BR>　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">仅保存执行上下文并调用check()&nbsp;</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　_declspec(naked)&nbsp;Proxy()<BR>　{<BR>　　<FONT color=#0000ff>_asm</FONT>{<BR>　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">保存执行上下文并调用check()</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">-后面的依赖于check()所返回的值</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;如果返回值是1，继续实际的调用。</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">否则，返回STATUS_ACCESS_DENIED</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><FONT color=#ff1493><SPAN style="COLOR: #000000">　　　<FONT color=#ff1493>pushfd<BR>　　　pushad<BR>　　　mov&nbsp;ebx,esp<BR>　　　add&nbsp;ebx,</FONT></SPAN></FONT><SPAN style="COLOR: #000000"><FONT color=#ff1493>40</FONT></SPAN><SPAN style="COLOR: #000000"><BR><FONT color=#ff1493>　　　push&nbsp;ebx<BR>　　　call&nbsp;check<BR>　　　cmp&nbsp;eax,</FONT></SPAN><SPAN style="COLOR: #000000"><FONT color=#ff1493>1</FONT></SPAN><SPAN style="COLOR: #000000"><BR><FONT color=#ff1493>　　　jne&nbsp;block</FONT><BR>　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">继续实际的调用</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000"><FONT color=#ff1493>　　　popad<BR>　　　popfd<BR>　　　jmp&nbsp;RealCallee</FONT><BR>　　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">返回STATUS_ACCESS_DENIED</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><FONT color=#ff1493><SPAN style="COLOR: #000000">　　　<FONT color=#ff1493>block:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popad<BR>　　　mov&nbsp;ebx,&nbsp;dword&nbsp;ptr[esp+8</FONT></SPAN></FONT><SPAN style="COLOR: #000000"><FONT color=#ff1493>]<BR></FONT><FONT color=#ff1493>　　　mov&nbsp;dword&nbsp;ptr[ebx<FONT color=#ff1493>],</FONT></SPAN></FONT><SPAN style="COLOR: #000000"><FONT color=#ff1493>0</FONT></SPAN><SPAN style="COLOR: #000000"><BR><FONT color=#ff1493>　　　mov&nbsp;eax,0xC0000022L<BR>　　　popfd<BR>　　　ret&nbsp;</FONT></SPAN><SPAN style="COLOR: #000000"><FONT color=#ff1493>32</FONT></SPAN><SPAN style="COLOR: #000000"><BR>　　}<BR>　}</SPAN></DIV>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Proxy()保存寄存器和标志，把一个指向服务参数的指针压入栈中并调用check()。其它的依赖于check()所返回的值。如果check()返回TRUE(也就是，我们想要继续请求)，那么，Proxy()将恢复寄存器和标志，并且把控制权交给服务实现部分。否则，Proxy()将把STATUS_ACCESS_DENIED写入EAX，恢复ESP并返回-从调用者的观点来看，这就象对NtCreateSection()的调用失败一样-以错误状态STATUS_ACCESS_DENIED返回。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; check()函数是怎样做出决定的？一旦它收到一个指向服务参数的指针参数，它就可以检查这些参数。首先，它检查标志和属性-如果有一部分没有被要求作为一个可执行映像映射，或如果要求的页面保护不允许执行，那么我们可以确定NtCreateSection()调用与进程创建毫无关系。在这种情况下，check()直接返回TRUE。否则，它将检查该潜在文件的扩展-毕竟，SEC_IMAGE属性和允许执行的页面保护可能被要求来映射某个DLL文件。如果该潜在文件不是一个.exe文件，那么，check()将返回TRUE。否则，它给用户模式代码一个作出决定的机会。因此，它仅把文件名和路径写到交换缓冲区，并且对它循环查询，直到它得到响应为止。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在打开我们的驱动程序前，我们的应用程序创建一个运行下面函数的线程：<BR></P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;thread()<BR>{<BR>　<FONT color=#0000ff>DWORD</FONT>&nbsp;a,x;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">char</SPAN><SPAN style="COLOR: #000000">&nbsp;msgbuff[</SPAN><SPAN style="COLOR: #000000">512</SPAN><SPAN style="COLOR: #000000">];<BR>　</SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">)<BR>　{<BR>　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">如果什么也没有，Sleep()&nbsp;10毫秒并再检查</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">a){Sleep(</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #0000ff">continue</SPAN><SPAN style="COLOR: #000000">;}<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">看起来象我们的权限被询问。&nbsp;</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">如果被怀疑的文件已经存在于空白列表中，</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;则给出一个积极的响应。</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #0000ff">char</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">name</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">char</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">8</SPAN><SPAN style="COLOR: #000000">];<BR>　　</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(x</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;x</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">stringcount;x</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">)<BR>　　{<BR>　　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">stricmp(name,strings[x])){a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;goto&nbsp;skip;}<BR>　　}<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">要求用户允许运行该程序</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　strcpy(msgbuff,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Do&nbsp;you&nbsp;want&nbsp;to&nbsp;run&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>　　strcat(msgbuff,</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">8</SPAN><SPAN style="COLOR: #000000">]);<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">如果用户的答复是积极的，那么把这个程序添加到空白列表中&nbsp;</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(IDYES</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #000000">MessageBox(</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">,&nbsp;msgbuff,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">WARNING</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,MB_YESNO</SPAN><SPAN style="COLOR: #000000">|</SPAN><SPAN style="COLOR: #000000">MB_ICONQUESTION</SPAN><SPAN style="COLOR: #000000">|</SPAN><SPAN style="COLOR: #000000">0x00200000L))<BR>　　　{a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;&nbsp;strings[stringcount]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">_strdup(name);stringcount</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">;}<BR>　　</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;把响应写入缓冲区中，而由驱动程序之后取回它</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　skip:memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">告诉驱动程序继续</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">　　a</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR>　　memmove(</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">outputbuff[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">],</SPAN><SPAN style="COLOR: #000000">&amp;</SPAN><SPAN style="COLOR: #000000">a,</SPAN><SPAN style="COLOR: #000000">4</SPAN><SPAN style="COLOR: #000000">);<BR>　}<BR>}</SPAN></DIV>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这段代码是显然的-我们的线程每10毫秒查询交换缓冲区。如果它发现我们的驱动程序已经把它的请求寄到了该缓冲区中，它就检查被允许在本机上运行的程序列表中的文件的文件名和路径。如果发现匹配，它直接给出一个OK响应。否则，它显示一个消息窗口，询问用户是否允许有问题的程序执行。如果响应是积极的，我们就把有问题的程序添加到允许在本机上运行的软件列表中。最后，我们把用户响应写入缓冲区，也就是说，把它传递到我们的驱动程序。因此，该用户就能完全控制它的PC上的进程的创建-只要我们的程序运行，在没有用户所给予权限的情况下，绝对没有办法来启动该PC上的任何进程。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正如你所见，我们让内核方式代码等待用户反应。这是否是一种聪明的举措呢？为了回答这个问题，你必须问你自己你是否正在堵住任何关键的系统资源-一切都依赖于具体的情况。在我们的情况下，一切发生在IRQLPASSIVE_LEVEL级上，并没有包含对IRPs的处理，并且必须等待用户响应的线程并不十分重要。因此，在我们的情况下，一切工作正常。然而，本例仅为演示之目的而编写。为了实际地使用它，以一个自动启动的服务的方式来重写我们的应用程序是很重要的。在这种情况下，我建议我们解除LocalSystem帐户，并且，在NtCreateSection()被用LocalSystem帐户特权在一个线程的上下文中调用的情况下，可以继续实际的服务实现而不施行任何检查-不管怎么说，LocalSystem帐户仅运行那些在注册表中指定的可执行程序。因此，这样的一种解除不会是与我们的安全相妥协的。<BR></P><img src ="http://www.cnitblog.com/wangk/aggbug/5330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/wangk/" target="_blank">孤独的夜</a> 2005-12-13 09:27 <a href="http://www.cnitblog.com/wangk/articles/5330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>