<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[mikenoodle&#39;s home]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[mikenoodle@163.com]]></description>
<link>http://hi.baidu.com/mikenoodle</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[年终了，谈点个人入侵的经验]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/4a7f4d90c989a587a877a470.html]]></link>
        <description><![CDATA[
		
		文章作者：oldjun<br>
<br>
From oldjun(<a href="http://www.oldjun.com/" target="_blank">http://www.oldjun.com/</a>)<br>
这一年变化比较大，换了好几个工作，最终还是回到了安全的岗位，合乎自己的职业规划！<br>
平时空闲时间经常找些站练练手，基本是新闻闹出轰动的站与一些知名的大站。2008年的成果还是颇丰的，从某某国内2大知名社区论坛到某某著名交友站到某某2大读书站到某某mobile到某某政府站等大站，再到若干名不见经传的小站，都闪过的我的影踪。但我个人觉得没什么好炫耀的，于是没替换首页，没留下黑页，顶多是把<span class="t_tag" href="tag.php?name=%E6%BA%90%E4%BB%A3%E7%A0%81">源代码</span>打包带回家（硬盘吃不消了，准备买个大的移动硬）。说句老实话，随便替换一个首页，就能让我出名，比如某某禁止降价的房产局站（服务器有黄片），比如某某闹禽流感的政府站，我都提前几天拿下了；不过我不想出名，更不想进局子~<br>
下面谈谈个人入侵的经验，不分语言，只谈拿webshell，至于提权，这里不说，我也很少提权，除非确实有必要！~<br>
1.无论什么站，无论什么语言，我要渗透，第一件事就是扫目录，最好一下扫出个上传点，直接上传shell，诸位不要笑，有时候你花很久搞一个站，最后发现有个现成的上传点，而且很容易猜到，不过这种情况发生在asp居多！<br>
2.asp（aspx）+MSSQL先考虑注入，一般的注入都有DBowner权限可以直接写shell；如果写不了，或者web与数据库分离，那就猜数据，从后台下手了，后台可以上传或者改配置文件；<br>
3.asp（aspx）+ACCESS拿shell一般只有3种方法，一是前台上传或者注入进后台上传；二是注入进后台改配置文件；三是注入进后台备份数据库或者暴库后知道是asp或者asa数据库于是直接写一句话；<br>
4.php+MYSQL一般是注入进后台上传，偶尔运气好些权限够高可以注入select into outfile；然后包含，分本地与远程，远程包含在高版本php是不支持的，于是想办法本地上传图片文件或者写到log里；然后php程序某某未公开的漏洞，运气好可以直接写shell。<br>
5.jsp+MYSQL利用数据库拿权限方面基本同php，而且jsp的上传基本很少检查文件后缀，于是只要有注入点与后台，拿shell相当的容易。jsp+ORACLE的站我碰到的不多，碰到的也是猜出用户名与密码从后台下手的。<br>
6.无论什么大站，主站一般都很安全（不然早被人玩了），于是一般从二级域名下手，猜出主站的某些用户名与密码或者搞到主站的源代码，或者旁注得到同网段服务器后cain或<span class="t_tag" href="tag.php?name=arp">arp</span>。<br>
7.一般的大站很少有用现成的CMS的，于是如果你有幸找到源码，那你就发了，注入漏洞啊，上传漏洞啊，写文件漏洞啊，都掌握在你手里。多看看那些大站新出来的测试分站点，那些站还在测试中，可以很轻松拿下。<br>
8.上传有个文件名截断，这包括2个方面，一是00截断，二是长文件名截断（曾经利用这个搞下hw）；然后很多写文件的地方，都可以00，屡试不爽。上传别忘了.asp（当然.asa，.cer，.cdx都可以啦）目录的妙用。<br>
9.php站无论windows还是<span class="t_tag" href="tag.php?name=linux">linux</span>，都有magic_quotes_gpc的问题，magic_quotes_gpc为on的时候，在server变量注入的时候还是可以select into outfile，今年我搞过某未开源cms就是这个情况，一般情况下为on就别考虑写文件了，不过有这个权限别忘了读文件源码，因为load_file的参数是可以编码的。<br>
10.猜路径或者文件在入侵中非常必要，猜不到路径的时候别忘了google（baidu太烂，google很全），于是你可以考虑看站点下的robot.txt或者robots.txt，会有惊喜。<br>
11.工具的使用很重要，入侵之前用WVS扫扫会有助入侵；注入工具虽然很多，但不见得都好使，现在的软硬防火墙、防注入越来越厉害，那时候你就别偷懒，多<span class="t_tag" href="tag.php?name=%E6%89%8B%E5%B7%A5">手工</span>有助你成长。<br>
12.遇到过一流监控么，遇到其他防post的防火墙么，有时候一句话进去了都无法传大马，那时候，你先学学编码，学学变换绕过。<br>
13.想搞一般的小站，记得查看这个小站的版权，找做这个站的公司，然后从这个公司做的其他站下手，得到源码再回头搞，我曾经通过这个方法拿下某知名制药的公司站。<br>
14.旁注的思路永远不过时，遇到dbowner的注入，可以很舒服写shell到你需要的站，省得麻烦的提权了；运气不好，按部就班拿shell提权得到你所需。<br>
15.永远别忘记社会工程学，利用社工把自己当成一个什么也不会的人，从某某站长的qq，身份证，邮箱等等下手，也许有时可能会有意外；另外别忘记admin,admin；test,test；123456,123456这种简单的尝试，当然，你也可以暴力破解。<br>
16.别忽视<span class="t_tag" href="tag.php?name=XSS">XSS</span>，别忽视cookie，XSS可以偷cookie，更有若干妙用，自己学会领悟；cookie可以伪造登陆，cookie可以注入，cookie注入可以绕绝大多数的防火墙。<br>
17.平时搞站多多搜集路径啊，源码啊，工具啊，充实自己的&ldquo;武器&rdquo;库；最好把自己的入侵步骤记录下来，或者事后反思下，我一般都是记在txt里，另外要做到举一反三。<br>
18.多学习，多看源码，多看公布出来的0day，脚本是入侵的前提，而不是工具，会用工具会装B你还没入门。<br>
最后奉劝诸位有事没事改人家首页的装B者，出来混，迟早是要还的，别等进了局子再后悔。还有一点，就是我搞N多站，没挂过一个马，至于很多挂马的人，我不知道该说什么，因为大家都喜欢钱，但是还是少为之吧。<br>
今天心头一热，把一些还记得的心得写出来了，希望大家别拍砖，入侵的时候思路是很灵活的，只要不死板，总有一条使自己成功的路。如果大家有什么不解或者疑问需要<span class="t_tag" href="tag.php?name=%E8%AE%A8%E8%AE%BA">讨论</span>，欢迎来<a href="http://www.oldjun.com/" target="_blank">http://www.oldjun.com</a>讨论或者加我QQ。<br>
还有不要抱着功利心去拿站，做安全的人靠不断拿站提升自己的技术与经验，也可以把自己的产品做的更好；若是急功近利或者为了什么利益，会...哎，不知道该怎么说...世人不知有因果，因果何曾饶过谁！<br>
祝大家元旦快乐！<br>
<a href="http://forum.eviloctal.com/thread-34512-1-3.html">http://forum.eviloctal.com/thread-34512-1-3.html</a> <a href="http://hi.baidu.com/mikenoodle/blog/item/4a7f4d90c989a587a877a470.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/hacker%26%2338%3Bcracker">hacker&#38;cracker</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/4a7f4d90c989a587a877a470.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-28  20:20</pubDate>
        <category><![CDATA[hacker&#38;cracker]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/4a7f4d90c989a587a877a470.html</guid>
</item>

<item>
        <title><![CDATA[如何得到可用的无线连接]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/9e5c484a5667922908f7ef29.html]]></link>
        <description><![CDATA[
		
		<table class="FCK__ShowTableBorders" width="100%">
    <tbody>
        <tr>
            <td class="bf14">/// &lt;summary&gt; <br>
                    /// 判断wifi的连接状态，如果它的信号强度为0,则wifi为off,其他则为on,如果myAdapters为空也为off <br>
                    /// &lt;/summary&gt; <br>
                    /// &lt;returns&gt; &lt;/returns&gt; <br>
                    public static bool CheckWifiConnect() <br>
                    { <br>
                        bool result = false; <br>
                        try <br>
                        { <br>
                            AdapterCollection myAdapters = Networking.GetAdapters(); <br>
            <br>
                            foreach (OpenNETCF.Net.Adapter myAdapter in myAdapters) <br>
                            { <br>
                                if (myAdapter.IsWireless) <br>
                                { <br>
                                    if (Convert.ToInt32(myAdapter.SignalStrength.ToString()) != 0) <br>
                                    { <br>
            <br>
                                        result= true; <br>
                                    } <br>
                                    else <br>
                                    { result= false; } <br>
                                } <br>
                            } <br>
                        } <br>
                        catch <br>
                        { <br>
                            result= false; <br>
                        } <br>
                        return result; <br>
            <br>
                    }</td>
        </tr>
        <tr>
            <td align="center">
            
            
            
            <div style="display: none">-</div>
            </td>
        </tr>
    </tbody>
</table> <a href="http://hi.baidu.com/mikenoodle/blog/item/9e5c484a5667922908f7ef29.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/wm%BF%AA%B7%A2">wm开发</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/9e5c484a5667922908f7ef29.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-28  09:54</pubDate>
        <category><![CDATA[wm开发]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/9e5c484a5667922908f7ef29.html</guid>
</item>

<item>
        <title><![CDATA[远控软件gh0st源码免杀]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/83b13cc762852cd6d0006026.html]]></link>
        <description><![CDATA[
		
		远控软件gh0st源码免杀 远控软件gh0st3.6开源了，开源意味着我们可以在此基础上进行二次开发，同时也意味着杀软可以较容易的查杀该款远控木马，既然要利用，我们就做好源码基础上的木马免杀工作。 好久没有来博客了，我把免杀这部分整理了一下，先抛一砖头，有兴趣的朋友可以接着做，也可以和本人交流。 序 gh0st远控软件采用驱动级RESSDT过主动，svchost参数启动，替换系统服务的方式工作的，工作方式较为先进，美中不足的部分是没有进行驱动级或用户级隐藏，当然这部分可以添加进去。编码利用了VC的编程环境。 一、环境配置 编译环境一定要配置好：DDK+SDK+VC6，DDK用来编译sys文件的，SDK+VC6是用来编译工程的，配置部分比较简单，网上有很多资料，这里不再详述，有兴趣的朋友也可以查看DDK和SDK的相关帮助。 二、特征码定位简述 杀毒软件查杀木马的原理基本是根据特征查杀的，被查杀的部分我们称之为特征码，所以我们可以利用特征码定位工具MyCLL定位出病毒的特征码位置，定位工具原理是将被扫描木马分块，利用分段填充的方式，匹配杀软的特征值，找到杀软查杀病毒的位置。 定位出特征码，如何反向找到源码中的对应位置呢？请看下面分析， 三、二进制文件与源码定位之map文件利用 map文件是二进制和源码之间对应的一个映射文件。 我们假设根据第三步我们定位出了病毒的特征码： 病毒名称 特征码位置 内存地址 svchost.dll 000038AA_00000002 100044AA svchost.dll 00005F98_00000002 第一步设置VC编译环境生成Map文件。 在 VC 中，点击菜单&ldquo;Project -&gt; Settings&rdquo;选项页（或按下 Alt+F7），选择 C/C++ 选项卡，并在最下面的 Project Options 里面输入：/Zd ，然后要点击 Link 选项卡，选中&ldquo;Generate mapfile&rdquo;复选框，并在最下面的 Project Options 里面输入：/mapinflines，表示生成 MAP 文件时，加入行信息。设置完成。 第二步编译VC工程，设置活动工程编译即可，这个不用说明。这个步骤完成后，在release(或debug)目录，多了一个.map文件（比如svchost.map)。 第三步打开map文件（用UE或文本编辑器打开都行），形式如下： (begin) Timestamp is 488fcef2 (Wed Jul 30 10:16:18 2008) Preferred load address is 10000000 ---------------------------------------------------------------------------1----（为方便说明，wrw添加） Start Length Name Class 0001:00000000 00010a50H .text CODE 0001:00010a50 00000485H .text$x CODE 0002:00000000 000004c8H .idata$5 DATA ...... 0003:00000010 00000004H .CRT$XIZ DATA 0003:00000020 00001a50H .data DATA 0003:00001a70 00000688H .bss DATA 0004:00000000 000000a8H .rsrc$01 DATA 0004:000000b0 00000cf0H .rsrc$02 DATA ----------------------------------------------------------------------------2---（为方便说明，wrw添加） Address Publics by Value Rva+Base Lib:Object 0001:00000000 <a href="mailto:??0CAudio@@QAE@XZ">??0CAudio@@QAE@XZ</a> 10001000 f Audio.obj 0001:000000d0 <a href="mailto:??_GCAudio@@UAEPAXI@Z">??_GCAudio@@UAEPAXI@Z</a> 100010d0 f i Audio.obj 0001:000000d0 <a href="mailto:??_ECAudio@@UAEPAXI@Z">??_ECAudio@@UAEPAXI@Z</a> 100010d0 f i Audio.obj 0001:000000f0 <a href="mailto:??1CAudio@@UAE@XZ">??1CAudio@@UAE@XZ</a> 100010f0 f Audio.obj 0001:000001e0 <a href="mailto:?getRecordBuffer@CAudio@@QAEPAEPAK@Z">?getRecordBuffer@CAudio@@QAEPAEPAK@Z</a> 100011e0 f Audio.obj 0001:00000240 <a href="mailto:?playBuffer@CAudio@@QAE_NPAEK@Z">?playBuffer@CAudio@@QAE_NPAEK@Z</a> 10001240 f Audio.obj 0001:000002c0 <a href="mailto:?InitializeWaveIn@CAudio@@AAE_NXZ">?InitializeWaveIn@CAudio@@AAE_NXZ</a> 100012c0 f Audio.obj ...... 0001:00003310 <a href="mailto:?SendToken@CFileManager@@AAEHE@Z">?SendToken@CFileManager@@AAEHE@Z</a> 10004310 f FileManager.obj 0001:00003320 <a href="mailto:?UploadToRemote@CFileManager@@AAE_NPAE@Z">?UploadToRemote@CFileManager@@AAE_NPAE@Z</a> 10004320 f FileManager.obj 0001:00003440 <a href="mailto:?FixedUploadList@CFileManager@@AAE_NPBD@Z">?FixedUploadList@CFileManager@@AAE_NPBD@Z</a> 10004440 f FileManager.obj 0001:00003670 <a href="mailto:?StopTransfer@CFileManager@@AAEXXZ">?StopTransfer@CFileManager@@AAEXXZ</a> 10004670 f FileManager.obj 0001:00003730 <a href="mailto:?CreateLocalRecvFile@CFileManager@@AAEXPAE@Z">?CreateLocalRecvFile@CFileManager@@AAEXPAE@Z</a> 10004730 f FileManager.obj ...... ----------------------------------------------------------------------------3---（为方便说明，wrw添加） Line numbers for .\Release\FileManager.obj(E:\vtmp\gh0st3src\Server\svchost\common\FileManager.cpp) segment .text 17 0001:00002630 20 0001:0000267f 21 0001:00002698 24 0001:000026d0 25 0001:000026f8 26 0001:0000273c 29 0001:000027d0 33 0001:000027ee 77 0001:000027f8 36 0001:000027fb 37 0001:00002803 77 0001:0000280d ...... 532 0001:0000340f 534 0001:00003414 537 0001:00003428 540 0001:00003440 546 0001:0000345d 547 0001:00003487 548 0001:00003490 549 0001:00003492 551 0001:0000349e 552 0001:000034b8 553 0001:000034cb 554 0001:000034d4 558 0001:000034de 560 0001:000034e9 563 0001:000034ee 564 0001:00003506 ...... (end) 我们看下，定位svchost.dll 的第一个特征码内存地址为：100044AA，在第2块中，我们可以找到RVA+BASE与之很接近的是 0001:00003440 <a href="mailto:?FixedUploadList@CFileManager@@AAE_NPBD@Z">?FixedUploadList@CFileManager@@AAE_NPBD@Z</a> 10004440 f FileManager.obj 这样我们可以定位到FileManager.cpp中的FixedUploadList函数，是不是范围缩小了？ 下面我们再缩小代码行 利用这个公式：特征码行偏移 = 特征码地址（Crash Address）－ 基地址（ImageBase Address）－ 0x1000 看起来好像很难，其实很简单，我们将100044AA去掉内存基址10000000，再减1000，因为PE很多从1000开始，可以得到代码偏移地址为34AA。到第3块中找对应的代码行。 偏移地址34AA在（551 0001:0000349e 552 0001:000034b8 ）中间，也就是551行和552行中间，我们到源程序中查找第551行： wsprintf(lpszFilter, %s%s*.*, lpPathName, lpszSlash); 这样就定位出源代码了，要怎么修改就怎么修改它就可以了。 四、实战免杀 A、卡巴免杀 首次编译后，先做卡巴的免杀。卡巴杀sys文件和dll，当然也就杀包装它们的install.exe，最后卡巴还杀生成的sever，我这里说杀生成好的server不是和前面的特征码重叠的地方，而是杀配置信息。 第一步、sys免杀 sys重新编译后，增加了输入表的函数，同时系统不同，造成很多地方不同于原特征，顺利通过卡巴、金山、小红伞等杀软。 第二步、svchost.dll免杀 特征码定位MultiByteToWideChar和gh0st update两个位置。这里是通过第3步map文件得出的。 卡巴怕加花指令， 这个函数MultiByteToWideChar的调用上，可以在这个函数前面随便加几句无效语句就可以通过卡巴杀软。 字符串调用gh0st update ，这个是用于更新用的 ，如果不要在线更新，直接把这个语句所在代码块删除；嘿嘿，其实搜索工程替换这个字符串为其他的字符串就可以了^_^，这个方法同时可以过金山杀软。 第三步、server免杀 卡巴定位在最后的配置信息，采取跳转显然是不行的，采用加花的办法，在写入AAAAAA配置信息之前，随便写些东西，就可以做server免杀。 卡巴免杀完成！ B、Avast免杀 最新的avast杀软再查杀1下，杀install.exe和svchost.dll（也就是杀生成的文件和其中的资源文件），接着做它的源码免杀。 定位在特征字符串%02d/%02d/%02d和&ldquo;SYSTEM\CurrentControlSet\Services\%s&rdquo;两个地方。 解决方案： 1、svchost.dll的特征码定位在键盘记录KeyboardManager.cpp文件中的SaveInfo(char *lpBuffer)函数。特征字符串%02d/%02d/%02d,也就是我们看到键盘记录的日期，修改之，修改的方法很多，将其改为[%d/%d/%d %d:%d:%d] ，编译即可通过avast杀软。 2、install的特征码定位在&ldquo;SYSTEM\CurrentControlSet\Services\%s&rdquo;，对应文件是install.cpp里的InstallService函数,修改大小写，编译即可通过免杀。 五、添加垃圾代码的小方法 垃圾代码要移动特征码所在的位置，不要跑到堆栈中了，这样的代码没有用。可以采取添加for循环，做计数，简单统计，采用局部变量，不改变后面的逻辑为宜。 添加输出表的方法： 有杀输出表的，可以在生成的svchost.dll上添加空函数 ，但是每次编译都要修改1次资源 ，其实我们在源码上添加如下语句： extern C __declspec(dllexport) bool JustTempFun();//声明 …… extern C __declspec(dllexport) bool JustTempFun() //实现 { return false; } 编译后，输出表就被改变了，有的杀软就可做到代码免杀。 六、gh0st自动生成6to4ex.dll的修改 看到好多站友提问自动生成6to4ex.dll的问题，有热心站友也提出了自己的见解 ，我感觉有些人提出的解决方案不完全正确，有可能造成刚入手人误解，我根据自己的理解说明1下。 gh0st服务端是通svchost -netsvcs启动的，所以程序要利用netsvcs 服务，服务端也就是根据netsvcs生成的，故不能说服务端生成是随机的，相对于大多数系统来讲，基本是固定的，下面看分析。 查看install.cpp里面的InstallService()方法，首先遍历HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Windows NT\CurrentVersion\Svchost中的服务项，查找到一个服务后，程序采取替换服务的方法，将原服务删除，然后生成对应服务项+ ex.dll的文件替换原服务，6to4服务一般排在第一位，6to4服务是一种自动构造隧道的方式，作用在于只需要一个全球惟一的IPv4地址便可使得整个站点获得IPv6 的连接，这个服务对一般人来讲，基本闲置，所以我们的程序就把6to4服务给替换掉，同时在windows\system32\目录下生成 6to4ex.dll，以后启动就是6to4ex了，如果把这个服务跳过去，就依次向下生成Ias、Iprip等服务啦，如果netsvcs项没有可以替换的服务，则程序将自己添加1个服务，名称就是由 AddsvchostService()方法产生的netsvcs_0x%d。 这样说不知道关心服务名称的明白了不？ 这个不能说是技术问题，但是小技巧问题可以从这里产生，我不知道其他人的360是怎么过的，但是我觉得可以提示1下的是，如果是360默认系统安全的服务，它肯定不会报不安全，替换闲置的系统安全的服务则通过360的效果要好的多<br>
本文来自: IXPUB技术社区(<a href="http://www.ixpub.net/">www.ixpub.net</a>) 详细出处参考：<a href="http://www.ixpub.net/viewthread.php?tid=891005&amp;extra=&amp;page=1">http://www.ixpub.net/viewthread.php?tid=891005&amp;extra=&amp;page=1</a> <a href="http://hi.baidu.com/mikenoodle/blog/item/83b13cc762852cd6d0006026.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/hacker%26%2338%3Bcracker">hacker&#38;cracker</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/83b13cc762852cd6d0006026.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-28  09:35</pubDate>
        <category><![CDATA[hacker&#38;cracker]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/83b13cc762852cd6d0006026.html</guid>
</item>

<item>
        <title><![CDATA[经典串口通信类CSerialPort 串口类的一个bug]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/b0686a22373af1ae4623e89e.html]]></link>
        <description><![CDATA[
		
		<p>CSerialPort 是一个老外写的，非常经典的C++类，方便简洁，不想微软自己的MSComm控件那样繁琐，更不需要接收字符和发送字符时做类型转换。<br>
<br>
但是今天发现了其中的一个严重bug，会造成死循环。<br>
<br>
先贴代码：<br>
BOOL CSerialPort::InitPort(CWnd* pPortOwner,&nbsp;&nbsp;&nbsp;  // the owner (CWnd) of the port (receives message)<br>
UINT  portnr,&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  // portnumber (1..4)<br>
UINT  baud,&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  // baudrate<br>
char  parity,&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  // parity <br>
UINT  databits,&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  // databits <br>
UINT  stopbits,&nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  // stopbits <br>
DWORD dwCommEvents,&nbsp;&nbsp;&nbsp;  // EV_RXCHAR, EV_CTS etc<br>
UINT  writebuffersize)&nbsp;&nbsp;&nbsp;  // size to the writebuffer<br>
{<br>
assert(portnr &gt; 0 &amp;&amp; portnr &lt; 5);<br>
assert(pPortOwner != NULL);<br>
<br>
// if the thread is alive: Kill<br>
if (m_bThreadAlive)<br>
{<br>
<font color="#ff0000">do<br>
{<br>
SetEvent(m_hShutdownEvent);<br>
} while (m_bThreadAlive);<br>
TRACE(&quot;Thread ended\n&quot;);</font><br>
}<br>
=========<br>
问题就出在红色区域所示代码，该代码第一次调用时，由于m_bThreadAlive == false,仅执行一次do while循环便跳出。但如果是第二次调用（我今天就是在第二次调用时发现的，因为我的串口配置参数做了更改，需要再一次初始化窗口，结果第二次调用后，程序死掉了。。。）。<br>
就会死循环！！！</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/44993599fa45a2bcc8eaf45c.html">http://hi.baidu.com/isafesoft/blog/item/44993599fa45a2bcc8eaf45c.html</a></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/b0686a22373af1ae4623e89e.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/b0686a22373af1ae4623e89e.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:34</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/b0686a22373af1ae4623e89e.html</guid>
</item>

<item>
        <title><![CDATA[自己贴图绘制对话框主界面时注意的问题]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/6e68fafa603c22d4b48f3193.html]]></link>
        <description><![CDATA[
		
		１，自己贴图绘制主对话框，需要去掉标题栏（Title Bar），那么如何在窗口最小化时点击左键，依然保持窗口最小化，还原的功能循环呢？<br>
<br>
这个问题我问了三年之久，包括在许多论坛，vckbase, codeproject，csdn等知名编程论坛都提问过多次，但是都没有得到一个正确的答案。<br>
<br>
最后我发现，只要在OnInitialDialog里面添加：<br>
<br>
<font color="#0000ff" size="4">ModifyStyle(WS_CAPTION, WS_MINIMIZEBOX, SWP_DRAWFRAME  );//设置图标<br>
SetWindowText(&quot;www.isafesoft.com , isafe computer watch, 如来神探电脑监控王 &quot;);//设置对话框的标题</font><br>
<br>
这两句，就能保持点击左键时的最小化和还原功能！<br>
真是功夫不负有心人啊…………<br>
<br>
2，如何保持点击右键时弹出系统菜单功能（系统菜单就那个包含&ldquo;最大化，最小化，关闭，关于&rdquo;）<br>
<br>
这个答案我更是苦苦寻求了好久，今天贴出来跟大家一起分享，省的大家跟我一样走弯路，这种感觉很郁闷的确，容易引起心脏病。<br>
<br>
打开.rc,然后找到相应的Dialog,在里面添加：<br>
<font color="#9900ff" size="3">STYLE WS_POPUP  | WS_SYSMENU</font><br>
<br>
好了，自己绘制对话框窗口的细节还有很多，包括解决绘图闪烁，拖动窗口等问题，<br>
以后再继续跟大家一起分享了…… <a href="http://hi.baidu.com/mikenoodle/blog/item/6e68fafa603c22d4b48f3193.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/6e68fafa603c22d4b48f3193.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:25</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/6e68fafa603c22d4b48f3193.html</guid>
</item>

<item>
        <title><![CDATA[如何动态御载键盘过滤驱动]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/3a9412ceaebe0433b600c893.html]]></link>
        <description><![CDATA[
		
		<p> </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  最近写个键盘过滤驱动，遇到的问题是动态御载后再有按键操作就会蓝屏，看了些资料终于明白了原因，写出来供大家参考，免得后来的朋友再重复这个郁闷的过程。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  要做到动态御载键盘过滤驱动，明白其工作运行的原理是很重要的。首先必须要知道键盘过滤驱动是工作在异步模式下的，这一点很重要。为了得到一个按键操作，首先需要发送一个IRP_MJ_READ到驱动的设备栈，驱动收到这个irp会做什么样的处理呢？它会一直保持这个irp为pending未确定状态，因为其实现在一直没有按键操作，直到一个键被真正的按下，驱动此时就会立刻完成这个irp，并将刚按下的键的相关数据做为该irp的返回值。在该irp带着对应的数据返回后，操作系统将这些值传递给对应的事件系统来处理，然后做什么呢？？系统紧接着又会立刻发送一个IRP_MJ_READ请求，等待下次的按键操作，重复以上的步骤。也就是说，任何时候设备栈底都会有一个键盘的IRP_MJ_READ请求处于pending未确定状态。这意味着只有在该irp完成返回，并却新的irp请求还未发送到的时候才会有一个很短暂的时间。由此我们想到，我们按照一般的方式动态御载键盘过滤驱动的时候，基本都是有IRP_MJ_READ请求处于pending未确定状态，而我们却御载了驱动，以后按键的时候需要处理这个irp却找不到对应的驱动当然会蓝屏。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  以上分析了动态御载键盘过滤驱动蓝屏的原因，分析到了中间存在一个短暂的时间栈底是没有irp的，那么让我们想办法来解决它。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  网上一份e文资料显示，只有使用IoAttachDevice挂接\Device\KeyboardClass0(or others)才可以动态御载，而加载到UpperFilters的却不能。以下是该段的原文：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  This problem occurs in most of the keyloggers based on the sysinternals Ctrl2cap model, which has been widely adopted and adapted. It applies to the earlier versions of the filter, which manually attach to \Device\KeyboardClass0 (or others) using IoAttachDevice. (If you install your filter by adding it to the UpperFilters value of HKCR\CCS\Control\Class\{4D36E96B-E325-11CE-BFC1-08002BE10318} key as more recent versions of Ctrl2cap do, you just can't unload: your filter is wedged into the stack for as long as the system is running and can't get out. But if you wedge by IoAttachDevice, unloading is still possible in theory.)。</p>
<p><br>
让我们再深入分析下蓝屏的原因，栈底有irp为什么我们的驱动御载就会有问题呢？这是由于IRM_MJ_READ是异步的，对于异步的请求，基本上我们会关心这个异步请求的结果，如何得到完成后的数据呢？大家一定想到了，设置完成例程。对，就是这样，由于我们给IRP_MJ_READ设置了完成例程，该irp完成后会调用我们的完成例程，使我们有处理返回数据的机会。在这样的情况下，我们动态御载了键盘过滤驱动，也就是说完成例程已经被我们御载掉了，而以后的再次按键在完成这个irp后会调用这个根本已经不存在了的东东，结果蓝屏就可想而知了。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;  那是否是不设置完成例程就不会有问题了呢？答案是肯定的。可是没有完成例程我们就没有办法处理到返回的数据，也就在很大程度上失去了键盘过滤驱动的作用了。如何做到既能设置完成例程来处理数据又可以实现动态的御载呢？这里我们这样想，当有IRP_MJ_READ到来的时候，我不为这个irp设置完成例程，也不将该irp向下传递，而是创建一个我自己的irp，并参考前面的IRP_MJ_READ做对应的设置，然后为我自己的这个irp设置完成例程后将我的irp向下传递，并设置原来的IRP_MJ_READ为pending状态。当有按键操作时，我的irp返回触发为它设置的完成例程，在这里取得返回的数据填充前面的IRP_MJ_READ后将该IRP_MJ_READ完成返回。相当于我们使用了一个代理，而这一切都是透明的。到这里我们实现了完成例程，也就是有了处理数据的机会。下面该说到重点了，那就是这样处理后又如何实现动态的御载呢？</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;  假设现在我们收到御载的请求，让我们看看当前所有的irp处于何种状态：<br>
(1)一个我们保存的原本的IRP_MJ_READ处于pending,注意它并没向下传递,也未设置完成例程。<br>
(2)一个我们自己构造的irp处于栈底，并注意我们为自己的这个irp设置了完成例程。<br>
基本就这2个irp，由于我们自己的irp有完成例程，所以直接御载会出现和上面一样的情况，导致蓝屏。如何处理呢？这里注意到是我们自己构造的irp，所以我们可以将其取消，这样并不会有太大的影响。可是取消后栈底本来该有的IRP_MJ_READ就没有了，注意到(1),我们不是还有原来的IRP_MJ_READ吗？对，就是将原来的这个IRP_MJ_READ向下传递，这里千万注意，我们自己的驱动马上要御载，所以我们传递原来的IRP_MJ_READ的时候不要给它设置完成例程。向下传递后御载我们的驱动。哈哈，成功！！！</p>
<p>&nbsp;&nbsp;&nbsp;  当然,这里还有更简单的办法,使用计数器也可以实现,还简单的多:)</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/a742770b8db9e0a52fddd42d.html">http://hi.baidu.com/isafesoft/blog/item/a742770b8db9e0a52fddd42d.html</a></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/3a9412ceaebe0433b600c893.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/3a9412ceaebe0433b600c893.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:24</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/3a9412ceaebe0433b600c893.html</guid>
</item>

<item>
        <title><![CDATA[编写驱动拦截NT的API实现隐藏文件目录]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/4f4f5b60efc28fd58db10d90.html]]></link>
        <description><![CDATA[
		
		<div class="cnt">目前NT下有很多种隐藏文件和目录的方法，其中最简单的一种是给文件和文件夹加上系统属性和隐藏属性，操作系统就会不在显示了，而且查找也找不到了，但是这种方法一点都不彻底，没有可用性！下面我们来介绍用NT驱动程序来拦截NTAPI来实现彻底隐藏文件和目录的目的。NT下有一个文件NTDLL.DLL，大部分NTAPI都是在这个库中封装的。其中实现查找文件和目录的API接口是ZwQueryDirectoryFile，所以我们只要拦截这个API的话，文件和目录就可以完全隐藏了！下面来一步不实现（准备工作：到NTDDK中找一个WDM驱动程序模型，也就是最简单的驱动程序了）：<br>
<br>
1.定义FILE_INFORMATION_CLASS的第3号结构：_FILE_BOTH_DIR_INFORMATION，这个结构是ZwQueryDirectoryFile必须参数。<br>
<br>
typedef struct _FILE_BOTH_DIR_INFORMATION {<br>
ULONG NextEntryOffset;<br>
ULONG FileIndex;<br>
LARGE_INTEGER CreationTime;<br>
LARGE_INTEGER LastAccessTime;<br>
LARGE_INTEGER LastWriteTime;<br>
LARGE_INTEGER ChangeTime;<br>
LARGE_INTEGER EndOfFile;<br>
LARGE_INTEGER AllocationSize;<br>
ULONG FileAttributes;<br>
ULONG FileNameLength;<br>
ULONG EaSize;<br>
CCHAR ShortNameLength;<br>
WCHAR ShortName[12];<br>
WCHAR FileName[1];<br>
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;<br>
<br>
<br>
<br>
2.先申明ZwQueryDirectoryFile，然后定义ZwQueryDirectoryFile的原型：<br>
<br>
extern NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile(<br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
//定义ZwQueryDirectoryFile的原型<br>
<br>
typedef NTSTATUS (*REALZWQUERYDIRECTORYFILE)(IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
//定义一个原函数指针<br>
REALZWQUERYSYSTEMINFORMATION RealZwQuerySystemInformation;<br>
<br>
3.定义替换API函数的原型：<br>
<br>
NTSTATUS HookZwQueryDirectoryFile( <br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
4.在DriverEntry（驱动入口）函数中加入如下申明：<br>
<br>
//保存真正的ZwQueryDirectoryFile函数地址<br>
<br>
RealZwQueryDirectoryFile=(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile));<br>
<br>
//把自定义的替换函数指针指向真正的ZwQueryDirectoryFile函数<br>
<br>
(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile))=HookZwQueryDirectoryFile;<br>
<br>
5.在DriverUnload（驱动卸载函数）函数中加入恢复代码：<br>
<br>
//恢复原来的函数指针<br>
<br>
(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile))=RealZwQueryDirectoryFile;<br>
<br>
6.现在准备工作做好了，函数指针都已经设置转向了，剩下的是实现这个我们自定义的替换函数HookZwQueryDirectoryFile，代码如下：<br>
<br>
NTSTATUS HookZwQueryDirectoryFile(<br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery)<br>
{<br>
NTSTATUS rc;<br>
ULONG CR0VALUE;<br>
<br>
ANSI_STRING ansiFileName,ansiDirName,HideDirFile;<br>
UNICODE_STRING uniFileName;<br>
<br>
//初始化要过虑的文件名这里是debug.exe<br>
RtlInitAnsiString(&amp;HideDirFile,&quot;DBGVIEW.EXE&quot;); <br>
<br>
// 执行真正的ZwQueryDirectoryFile函数<br>
rc = ((REALZWQUERYDIRECTORYFILE)(RealZwQueryDirectoryFile))(<br>
hFile, <br>
hEvent,<br>
IoApcRoutine,<br>
IoApcContext,<br>
pIoStatusBlock,<br>
FileInformationBuffer,<br>
FileInformationBufferLength,<br>
FileInfoClass,<br>
bReturnOnlyOneEntry,<br>
PathMask,<br>
bRestartQuery);<br>
/*如果执行成功（而且FILE_INFORMATION_CLASS的值为FileBothDirectoryInformation，我们就进行处理，过滤*/<br>
if(NT_SUCCESS(rc)&amp;&amp; (FileInfoClass == FileBothDirectoryInformation))<br>
{<br>
PFILE_BOTH_DIR_INFORMATION pFileInfo;<br>
PFILE_BOTH_DIR_INFORMATION pLastFileInfo;<br>
BOOL bLastOne;<br>
//把执行结果赋给pFileInfo <br>
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer; <br>
pLastFileInfo = NULL;<br>
//循环检查<br>
do<br>
{<br>
bLastOne = !( pFileInfo-&gt;NextEntryOffset );<br>
RtlInitUnicodeString(&amp;uniFileName,pFileInfo-&gt;FileName);<br>
RtlUnicodeStringToAnsiString(&amp;ansiFileName,&amp;uniFileName,TRUE);<br>
RtlUnicodeStringToAnsiString(&amp;ansiDirName,&amp;uniFileName,TRUE);<br>
RtlUpperString(&amp;ansiFileName,&amp;ansiDirName);<br>
//打印结果，用debugview可以查看打印结果<br>
DbgPrint(&quot;ansiFileName :%s\n&quot;,ansiFileName.Buffer);<br>
DbgPrint(&quot;HideDirFile :%s\n&quot;,HideDirFile.Buffer);<br>
<br>
// 开始进行比较，如果找到了就隐藏这个文件或者目录<br>
if( RtlCompareMemory(ansiFileName.Buffer,HideDirFile.Buffer,HideDirFile.Length ) == HideDirFile.Length)<br>
{<br>
DbgPrint(&quot;This is HideDirFile!\n&quot;);<br>
if(bLastOne) <br>
{<br>
if(pFileInfo == (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer )<br>
{<br>
rc = 0x80000006; //隐藏文件或者目录；<br>
}<br>
else<br>
{<br>
pLastFileInfo-&gt;NextEntryOffset = 0;<br>
}<br>
break;<br>
} <br>
else //指针往后移动<br>
{<br>
int iPos = ((ULONG)pFileInfo) - (ULONG)FileInformationBuffer;<br>
int iLeft = (DWORD)FileInformationBufferLength - iPos - pFileInfo-&gt;NextEntryOffset;<br>
RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo + pFileInfo-&gt;NextEntryOffset ), (DWORD)iLeft );<br>
continue;<br>
}<br>
}<br>
pLastFileInfo = pFileInfo;<br>
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((char *)pFileInfo + pFileInfo-&gt;NextEntryOffset);<br>
<br>
}while(!bLastOne);<br>
RtlFreeAnsiString(&amp;ansiDirName); <br>
RtlFreeAnsiString(&amp;ansiFileName);<br>
}<br>
return(rc);<br>
}<br>
<br>
本代码在开发机器（WINXP+SP1+XPDDK）上测试通过！</div>
<br>
<div class="opt"><a title="查看该分类中所有文章" href="http://hi.baidu.com/cealy/blog/category/%C7%FD%B6%AF">类别：驱动</a> | <a title="将此文章添加到百度搜藏" href="http://cang.baidu.com/do/add" target="_blank">添加到搜藏</a> | 浏览(<span>54</span>) | <a href="http://hi.baidu.com/cealy/blog/item/c02a700e190e0cc97acbe16d.html#send">评论</a> (0)
<div style="line-height: 5px; height: 5px"> </div>
<div>上一篇：<a title="学习写一个Hello World驱动" href="http://hi.baidu.com/cealy/blog/item/512bdf542eb2495bd009066c.html">学习写一个Hello World驱动</a>&nbsp;&nbsp;&nbsp;&nbsp;  下一篇：<a title="应用层与驱动层同步事件处理方法" href="http://hi.baidu.com/cealy/blog/item/4738eb24555676024c088db8.html">应用层与驱动层同步事件处理方法</a>
<div class="cnt">
<p>目前NT下有很多种隐藏文件和目录的方法，其中最简单的一种是给文件和文件夹加上系统属性和隐藏属性，操作系统就会不在显示了，而且查找也找不到了，但是这种方法一点都不彻底，没有可用性！下面我们来介绍用NT驱动程序来拦截NTAPI来实现彻底隐藏文件和目录的目的。NT下有一个文件NTDLL.DLL，大部分NTAPI都是在这个库中封装的。其中实现查找文件和目录的API接口是ZwQueryDirectoryFile，所以我们只要拦截这个API的话，文件和目录就可以完全隐藏了！下面来一步不实现（准备工作：到NTDDK中找一个WDM驱动程序模型，也就是最简单的驱动程序了）：<br>
<br>
1.定义FILE_INFORMATION_CLASS的第3号结构：_FILE_BOTH_DIR_INFORMATION，这个结构是ZwQueryDirectoryFile必须参数。<br>
<br>
typedef struct _FILE_BOTH_DIR_INFORMATION {<br>
ULONG NextEntryOffset;<br>
ULONG FileIndex;<br>
LARGE_INTEGER CreationTime;<br>
LARGE_INTEGER LastAccessTime;<br>
LARGE_INTEGER LastWriteTime;<br>
LARGE_INTEGER ChangeTime;<br>
LARGE_INTEGER EndOfFile;<br>
LARGE_INTEGER AllocationSize;<br>
ULONG FileAttributes;<br>
ULONG FileNameLength;<br>
ULONG EaSize;<br>
CCHAR ShortNameLength;<br>
WCHAR ShortName[12];<br>
WCHAR FileName[1];<br>
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;<br>
<br>
<br>
<br>
2.先申明ZwQueryDirectoryFile，然后定义ZwQueryDirectoryFile的原型：<br>
<br>
extern NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile(<br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
//定义ZwQueryDirectoryFile的原型<br>
<br>
typedef NTSTATUS (*REALZWQUERYDIRECTORYFILE)(IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
//定义一个原函数指针<br>
REALZWQUERYSYSTEMINFORMATION RealZwQuerySystemInformation;<br>
<br>
3.定义替换API函数的原型：<br>
<br>
NTSTATUS HookZwQueryDirectoryFile( <br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery);<br>
<br>
4.在DriverEntry（驱动入口）函数中加入如下申明：<br>
<br>
//保存真正的ZwQueryDirectoryFile函数地址<br>
<br>
RealZwQueryDirectoryFile=(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile));<br>
<br>
//把自定义的替换函数指针指向真正的ZwQueryDirectoryFile函数<br>
<br>
(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile))=HookZwQueryDirectoryFile;<br>
<br>
5.在DriverUnload（驱动卸载函数）函数中加入恢复代码：<br>
<br>
//恢复原来的函数指针<br>
<br>
(REALZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile))=RealZwQueryDirectoryFile;<br>
<br>
6.现在准备工作做好了，函数指针都已经设置转向了，剩下的是实现这个我们自定义的替换函数HookZwQueryDirectoryFile，代码如下：<br>
<br>
NTSTATUS HookZwQueryDirectoryFile(<br>
IN HANDLE hFile,<br>
IN HANDLE hEvent OPTIONAL,<br>
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br>
IN PVOID IoApcContext OPTIONAL,<br>
OUT PIO_STATUS_BLOCK pIoStatusBlock,<br>
OUT PVOID FileInformationBuffer,<br>
IN ULONG FileInformationBufferLength,<br>
IN FILE_INFORMATION_CLASS FileInfoClass,<br>
IN BOOLEAN bReturnOnlyOneEntry,<br>
IN PUNICODE_STRING PathMask OPTIONAL,<br>
IN BOOLEAN bRestartQuery)<br>
{<br>
NTSTATUS rc;<br>
ULONG CR0VALUE;<br>
<br>
ANSI_STRING ansiFileName,ansiDirName,HideDirFile;<br>
UNICODE_STRING uniFileName;<br>
<br>
//初始化要过虑的文件名这里是debug.exe<br>
RtlInitAnsiString(&amp;HideDirFile,&quot;DBGVIEW.EXE&quot;); <br>
<br>
// 执行真正的ZwQueryDirectoryFile函数<br>
rc = ((REALZWQUERYDIRECTORYFILE)(RealZwQueryDirectoryFile))(<br>
hFile, <br>
hEvent,<br>
IoApcRoutine,<br>
IoApcContext,<br>
pIoStatusBlock,<br>
FileInformationBuffer,<br>
FileInformationBufferLength,<br>
FileInfoClass,<br>
bReturnOnlyOneEntry,<br>
PathMask,<br>
bRestartQuery);<br>
/*如果执行成功（而且FILE_INFORMATION_CLASS的值为FileBothDirectoryInformation，我们就进行处理，过滤*/<br>
if(NT_SUCCESS(rc)&amp;&amp; (FileInfoClass == FileBothDirectoryInformation))<br>
{<br>
PFILE_BOTH_DIR_INFORMATION pFileInfo;<br>
PFILE_BOTH_DIR_INFORMATION pLastFileInfo;<br>
BOOL bLastOne;<br>
//把执行结果赋给pFileInfo <br>
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer; <br>
pLastFileInfo = NULL;<br>
//循环检查<br>
do<br>
{<br>
bLastOne = !( pFileInfo-&gt;NextEntryOffset );<br>
RtlInitUnicodeString(&amp;uniFileName,pFileInfo-&gt;FileName);<br>
RtlUnicodeStringToAnsiString(&amp;ansiFileName,&amp;uniFileName,TRUE);<br>
RtlUnicodeStringToAnsiString(&amp;ansiDirName,&amp;uniFileName,TRUE);<br>
RtlUpperString(&amp;ansiFileName,&amp;ansiDirName);<br>
//打印结果，用debugview可以查看打印结果<br>
DbgPrint(&quot;ansiFileName :%s\n&quot;,ansiFileName.Buffer);<br>
DbgPrint(&quot;HideDirFile :%s\n&quot;,HideDirFile.Buffer);<br>
<br>
// 开始进行比较，如果找到了就隐藏这个文件或者目录<br>
if( RtlCompareMemory(ansiFileName.Buffer,HideDirFile.Buffer,HideDirFile.Length ) == HideDirFile.Length)<br>
{<br>
DbgPrint(&quot;This is HideDirFile!\n&quot;);<br>
if(bLastOne) <br>
{<br>
if(pFileInfo == (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer )<br>
{<br>
rc = 0x80000006; //隐藏文件或者目录；<br>
}<br>
else<br>
{<br>
pLastFileInfo-&gt;NextEntryOffset = 0;<br>
}<br>
break;<br>
} <br>
else //指针往后移动<br>
{<br>
int iPos = ((ULONG)pFileInfo) - (ULONG)FileInformationBuffer;<br>
int iLeft = (DWORD)FileInformationBufferLength - iPos - pFileInfo-&gt;NextEntryOffset;<br>
RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo + pFileInfo-&gt;NextEntryOffset ), (DWORD)iLeft );<br>
continue;<br>
}<br>
}<br>
pLastFileInfo = pFileInfo;<br>
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((char *)pFileInfo + pFileInfo-&gt;NextEntryOffset);<br>
<br>
}while(!bLastOne);<br>
RtlFreeAnsiString(&amp;ansiDirName); <br>
RtlFreeAnsiString(&amp;ansiFileName);<br>
}<br>
return(rc);<br>
}<br>
<br>
本代码在开发机器（WINXP+SP1+XPDDK）上测试通过！</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/c1c3dcc64eb2d7c138db4945.html">http://hi.baidu.com/isafesoft/blog/item/c1c3dcc64eb2d7c138db4945.html</a></p>
</div>
</div>
</div> <a href="http://hi.baidu.com/mikenoodle/blog/item/4f4f5b60efc28fd58db10d90.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/4f4f5b60efc28fd58db10d90.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:16</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/4f4f5b60efc28fd58db10d90.html</guid>
</item>

<item>
        <title><![CDATA[Windows平台内核级文件访问]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/c7d659df50d91a1949540396.html]]></link>
        <description><![CDATA[
		
		<p>Windows平台内核级文件访问<br>
<br>
<br>
1.背景<br>
在windows平台下，应用程序通常使用API函数来进行文件访问，创建，打开，读写文件。从kernel32的 CreateFile/ReadFile/WriteFile函数，到本地系统服务，再到FileSystem及其FilterDriver，经历了很多 层次。在每个层次上，都存在着安全防护软件，病毒或者后门作监视或者过滤的机会。作为安全产品开发者，我们需要比别人走得更远，因此我们需要一个底层的 &ldquo;windows平台内核级文件访问&rdquo;的方法来确保我们能够看到正确的干净的文件系统。<br>
<br>
2.用途<br>
直接的内核级别文件访问，在信息安全领域内有广泛的用途。用于入侵者的方面，可以让他绕过杀毒软件，IDS等安全保护系统的监视。用于检测者的方 面，可以看到一个干净的系统，以此来查杀隐藏的后门或者rootkit。用于监控者的方面，则可以了解最新的绕过监控的技术，可以根据来设计更新的监控方 案。<br>
<br>
3.直接访问FSD的内核级别文件访问<br>
FSD(FileSystemDriver)层是文件API函数经过本地系统服务层(native API)最后到达的驱动层次。如果我们可以模 仿操作系统，在我们自己的驱动程序里直接向FSD发送IRP，就可以绕过那些native API 和win32 API了，也就可以绕过设置在这些层次 上面的API钩子等监控措施。<br>
<br>
3.1文件的Create和Open<br>
文件的Create和Open可以通过发送IRP_MJ_CREATE给FSD，或者调用IoCreateFile函数来完成。Create和 Open的区别实际上在于IoCreateFile/IRP_MJ_CREATE的一个参数Disposition的取值。使用IoCreateFile 函数的样例代码：<br>
<br>
HANDLE openfile(WCHAR* name,ACCESS_MASK access,ULONG share) <br>
{<br>
//return 0 for error.<br>
HANDLE hfile;<br>
IO_STATUS_BLOCK iosb;<br>
int stat;<br>
OBJECT_ATTRIBUTES oba;<br>
UNICODE_STRING nameus;<br>
///<br>
if(KeGetCurrentIrql()&gt;PASSIVE_LEVEL){return 0;}<br>
RtlInitUnicodeString(&amp;nameus,name);<br>
InitializeObjectAttributes(&amp;oba,&amp;nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);<br>
stat=IoCreateFile(&amp;hfile,access,&amp;oba,&amp;iosb,0,FILE_ATTRIBUTE_NORMAL,share,FILE_OPEN,0,0,0,0,0,0);<br>
if(!NT_SUCCESS(stat)){return 0;}<br>
return hfile;<br>
}<br>
<br>
HANDLE createnewfile(WCHAR* name,ACCESS_MASK access,ULONG share) <br>
{<br>
//return 0 for error.<br>
HANDLE hfile;<br>
IO_STATUS_BLOCK iosb;<br>
int stat;<br>
OBJECT_ATTRIBUTES oba;<br>
UNICODE_STRING nameus;<br>
///<br>
if(KeGetCurrentIrql()&gt;PASSIVE_LEVEL){return 0;}<br>
RtlInitUnicodeString(&amp;nameus,name);<br>
InitializeObjectAttributes(&amp;oba,&amp;nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);<br>
stat=IoCreateFile(&amp;hfile,access,&amp;oba,&amp;iosb,0,//AllocationSize this set to 0 that when file opened it was zeroed.<br>
FILE_ATTRIBUTE_NORMAL,share,FILE_OVERWRITE_IF,0,0,0,0,0,0);<br>
if(!NT_SUCCESS(stat)){return 0;}<br>
return hfile;<br>
}<br>
<br>
通过发送IRP_MJ_CREATE给FSD的方法与此类似，可以参考IFSDDK document的IRP_MJ_CREATE说明。不同于 上面方法的是需要自己创建一个FILE_OBJECT，好于上面方法的是这种方法不需要一个HANDLE，HANDLE是线程依赖 的,FileObject则是线程无关。<br>
<br>
3.2文件的Read和Write<br>
我们通过给FSD发送IRP_MJ_READ来读取文件，给FSD发送IRP_MJ_WRITE来改写文件。<br>
如果我们是通过一个HANDLE来执行(如使用IoCreateFile打开的文件)，就要先用 ObReferenceObjectByHandle函数来获得这个Handle对应的FileObject。我们只能给FileObject发送 IRP。<br>
<br>
stat=ObReferenceObjectByHandle(handle,GENERIC_READ,*IoFileObjectType,KernelMode,(PVOID*)&amp;fileob,0);<br>
<br>
之后我们使用IoAllocateIrp分配一个IRP。根据FileObject-&gt;DeviceObject-&gt;Flags的 值，我们判断目标文件系统使用什么样的IO方式。<br>
<br>
if(fileob-&gt;DeviceObject-&gt;Flags &amp; DO_BUFFERED_IO)<br>
{<br>
irp-&gt;AssociatedIrp.SystemBuffer=buffer;//buffered io<br>
}<br>
else if(fileob-&gt;DeviceObject-&gt;Flags &amp; DO_DIRECT_IO)<br>
{<br>
mdl=IoAllocateMdl(buffer,count,0,0,0);<br>
MmBuildMdlForNonPagedPool(mdl);<br>
irp-&gt;MdlAddress=mdl;//direct io<br>
}<br>
else<br>
{<br>
irp-&gt;UserBuffer=buffer;//neither i/o, use kernel buffer<br>
}<br>
<br>
对每种不同的IO方式使用不同的地址传递方式。随后我们填充IRP内的各个参数域，就可以发送IRP了。以Read为例：<br>
<br>
irpsp-&gt;FileObject=fileob;<br>
irpsp-&gt;MajorFunction=IRP_MJ_READ;<br>
irpsp-&gt;MinorFunction=IRP_MN_NORMAL;//0<br>
irpsp-&gt;Parameters.Read.ByteOffset=offsetused;<br>
irpsp-&gt;Parameters.Read.Key=0;<br>
irpsp-&gt;Parameters.Read.Length=count;<br>
<br>
接着要考虑如果IRP不能及时完成，会异步的返回的情况，我们安装一个CompletionRoutine，在 CompletionRoutine里面设置一个<a name="baidusnap1"></a><strong style="color: black; background-color: rgb(160,255,255)">事件</strong>为已激活，通知我们的主线程读取或者写入操作已经完成。<br>
<br>
<a name="baidusnap0"></a><strong style="color: black; background-color: rgb(255,255,102)">IoSetCompletionRoutine</strong>(irp,IoCompletion,&amp;event,1,1,1);<br>
<br>
NTSTATUS<br>
IoCompletion(<br>
IN PDEVICE_OBJECT&nbsp;&nbsp; DeviceObject,<br>
IN PIRP&nbsp;&nbsp; Irp,<br>
IN PVOID&nbsp;&nbsp; Context<br>
)<br>
{<br>
KeSetEvent((PRKEVENT)Context, IO_DISK_INCREMENT, 0);<br>
return STATUS_MORE_PROCESSING_REQUIRED;<br>
}<br>
<br>
现在可以发送IRP了。如果不采取特殊的措施的话，IRP发送目标是FileObject对应的DeviceObject。发送后，等待IRP的 完成并且释放资源，返回。<br>
<br>
stat=IoCallDriver(fileob-&gt;DeviceObject,irp);<br>
if(stat==STATUS_PENDING){<br>
KeWaitForSingleObject(&amp;event, Executive,KernelMode,0,0);<br>
stat=irp-&gt;IoStatus.Status;<br>
}<br>
if(!NT_SUCCESS(stat))<br>
{<br>
IoFreeIrp(irp);<br>
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO<br>
return -1;<br>
}<br>
stat=irp-&gt;IoStatus.Information;//bytes read<br>
IoFreeIrp(irp);<br>
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO<br>
return stat;<br>
<br>
3.3文件的Delete<br>
Delete实际上是通过向FSD发送IRP_MJ_SET_INFORMATION的IRP，并把 IrpSp-&gt;Parameters.SetFile.FileInformationClass设置为 FileDispositionInformation，用一个FILE_DISPOSITION_INFORMATION结构填充buffer来执行 的。<br>
<br>
fdi.DeleteFile=TRUE;<br>
<br>
irpsp-&gt;MajorFunction=IRP_MJ_SET_INFORMATION;<br>
irpsp-&gt;Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION);<br>
irpsp-&gt;Parameters.SetFile.FileInformationClass = FileDispositionInformation; <br>
irpsp-&gt;Parameters.SetFile.DeleteHandle = (HANDLE)handle; <br>
<br>
3.4文件的Rename<br>
类似于Delete，Rename是向FSD发送IRP_MJ_SET_INFORMATION的IRP，把 IrpSp-&gt;Parameters.SetFile.FileInformationClass设置为 FileRenameInformation，填充buffer为FILE_RENAME_INFORMATION结构。<br>
<br>
fri.ReplaceIfExists=TRUE;<br>
fri.RootDirectory=0;//Set fri.FileName to full path name.<br>
fri.FileNameLength=wcslen(filename)*2;<br>
wcscpy(fri.FileName,filename);//If the RootDirectory member is NULL, and the file is being moved to a different directory, this member specifies the full pathname to be assigned to the file. <br>
<br>
irpsp-&gt;MajorFunction=IRP_MJ_SET_INFORMATION;<br>
irpsp-&gt;Parameters.SetFile.Length = sizeof(FILE_FILE_RENAME_INFORMATION);<br>
irpsp-&gt;Parameters.SetFile.FileInformationClass = FileRenameInformation; <br>
<br>
<br>
综上，于是我们可以在驱动里面通过发送IRP来直接访问文件系统了，绕过了native API 和win32 API层次。<br>
<br>
<br>
4.绕过文件系统过滤驱动和钩子<br>
<br>
有了第三部分的内容，我们目前可以直接给FSD发送请求操作文件。但是这还不够，因为有很多的杀毒软件或者监视工具使用 FSD Filter Driver或者FSD Hook的办法来监控文件操作。在今天这篇文章里我讲一些原理性的东西，提供绕过 FSD Filter Driver / FSD Hook的思路。<br>
<br>
4.1对付文件系统过滤驱动<br>
<br>
文件系统过滤驱动Attach在正常的文件系统之上，监视和过滤我们的文件访问。文件系统驱动栈就是由这一连串的Attach起来的过滤驱动组 成。我们可以用IoGetRelatedDeviceObject这个函数来获得一个FileObject对应的最底层的那个功能驱动对象(FDO)。但 是这样虽然绕过了那些过滤驱动，却同时也绕过了正常的FSD如Ntfs/Fastfat，因为正常的FSD也是作为一个过滤驱动存在的。磁盘文件对象的对 应的最底层的FDO是Ftdisk.sys，它已经因为过于底层而不能处理我们投递的IRP请求。<br>
其实正常的FSD信息存储在一个Vpb结构中，我们可以使用IoGetBaseFileSystemDeviceObject这个未公开的内核函 数来得到它。它就是我们发送IRP的目标了。<br>
<br>
4.2对付替换DispatchRoutine的FSD Hook<br>
<br>
这是一种常用的FSD Hook方式。我们需要得到原本的DispatchRoutine，向原本的DispatchRoutine发送我们的 IRP。这里提供一个思路：我们可以读取原本FSD驱动的.INIT段或者.TEXT段，查找其DriverEntry函数，在它的 DriverEntry函数中肯定设置了自己的DriverObject的各个DispatchRoutine。在这个函数中我们就能找到我们想要的 DispatchRoutine的地址。只需要使用特征码搜索的方法就可以搜索到这个值。<br>
<br>
4.3对付Inline Hook DispatchRoutine函数本身的FSD Hook<br>
<br>
这种Hook方法比较狠毒，但不是非常常见于安全产品中，一般应用在木马和rootkit上，比如我自己写的rootkit。它没有更改 DriverObject里面的DispatchRoutine的函数指针，而是向函数开头写入汇编指令的JMP来跳转函数。对付它的基本思路就是读取存 在磁盘上的FSD的文件，加载到内存一份干净的备份，察看我们要调用的DispatchRoutine开头的几个字节和这个干净备份是否一致。如果不一 致，尤其是存在JMP,RET,INT3一类的汇编指令的时候，很可能就是存在了Inline Hook。（但要充分考虑重定位的情况。）如果存在 Inline Hook，我们就把干净的函数开头拷贝过来覆盖掉被感染的函数头。然后在发送IRP，就不会被Inline Hook监视或篡改了。</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/fd22a1ee5bff2b37269791db.html">http://hi.baidu.com/isafesoft/blog/item/fd22a1ee5bff2b37269791db.html</a></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/c7d659df50d91a1949540396.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/c7d659df50d91a1949540396.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:11</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/c7d659df50d91a1949540396.html</guid>
</item>

<item>
        <title><![CDATA[Win32应用程序中进程间通信方法分析与比较]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/5cbd7e31333d6213ebc4af96.html]]></link>
        <description><![CDATA[
		
		<p><strong><font face="宋体" size="4">1 进程与进程通信</font></strong></p>
<p><font face="宋体" size="3">　　进程是装入内存并准备执行的程序，每个进程都有私有的虚拟地址空间，由代码、数据以及它可 利用的系统资源(如文件、管道等)组成。多进程/多线程是Windows操作系统的一个基本特征。Microsoft Win32应用编程接口 (Application Programming Interface, API)提供了大量支持应用程序间数据共享和交换的机制，这些机制行使的活动 称为进程间通信(InterProcess Communication, IPC)，进程通信就是指不同进程间进行数据共享和数据交换。<br>
正因为使用Win32 API进行进程通信方式有多种，如何选择恰当的通信方式就成为应用开发中的一个重要问题，下面本文将对Win32中进程通信的 几种方法加以分析和比较。</font></p>
<p><strong><font face="宋体" size="4">2 进程通信方法</font></strong></p>
<p><font face="宋体" size="3"><strong>2.1 文件映射</strong><br>
文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此，进程不必使用文件I/O操作，只 需简单的指针操作就可读取和修改文件的内容。<br>
Win32 API允许多个进程访问同一文件映射对象，各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针，不同进程就可以读或修改文件 的内容，实现了对文件中数据的共享。<br>
应用程序有三种方法来使多个进程共享一个文件映射对象。<br>
(1)继承：第一个进程建立文件映射对象，它的子进程继承该对象的句柄。<br>
(2)命名文件映射：第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另 外，第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。<br>
(3)句柄复制：第一个进程建立文件映射对象，然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取 得对该文件映射对象的访问权限。<br>
文件映射是在多个进程间共享数据的非常有效方法，有较好的安全性。但文件映射只能用于本地机器的进程之间，不能用于网络中，而开发者还必须控制进程间 的同步。<br>
<strong>2.2 共享内存</strong><br>
Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代 替文件句柄(HANDLE)，就表示了对应的文件映射对象是从操作系统页面文件访问内存，其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是 用文件映射实现的，所以它也有较好的安全性，也只能运行于同一计算机上的进程之间。<br>
<strong>2.3 匿名管道</strong><br>
管道(Pipe)是一种具有两个端点的通信通道：有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向－一端是只读的，另一端点是只写的； 也可以是双向的一管道的两端点既可读也可写。<br>
匿名管道(Anonymous Pipe)是在父进程和子进程之间，或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管 道，然后由要通信的子进程继承通道的读端点句柄或写端点句柄，然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可 以使用管道直接通信，不需要通过父进程。<br>
匿名管道是单机上实现子进程标准I/O重定向的有效方法，它不能在网上使用，也不能用于两个不相关的进程之间。<br>
<strong>2.4 命名管道</strong><br>
命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和 不同计算机之间使用，服务器建立命名管道时给它指定一个名字，任何进程都可以通过该名字打开管道的另一端，根据给定的权限和服务器进程通信。<br>
命名管道提供了相对简单的编程接口，使通过网络传输数据并不比同一计算机上两进程之间通信更困难，不过如果要同时和多个进程通信它就力不从心了。<br>
<strong>2.5 邮件槽</strong><br>
邮件槽(Mailslots)提供进程间单向通信能力，任何进程都能建立邮件槽成为邮件槽服务器。其它进程，称为邮件槽客户，可以通过邮件槽的名字给 邮件槽服务器进程发送消息。进来的消息一直放在邮件槽中，直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户，因此可建立多个邮 件槽实现进程间的双向通信。<br>
通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过 400字节，非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。<br>
邮件槽与命名管道相似，不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的，一旦网络发生错误则无法保证消息正确地接收， 而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力，所以邮件槽不失为应用程序发 送和接收消息的另一种选择。<br>
<strong>2.6 剪贴板</strong><br>
剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息，为Windows应用程序之间进行数据共享提供了一 个中介，Windows已建立的剪切(复制)－粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时， 应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据，从给定格式中选择适合自己的格式。<br>
剪贴板是一个非常松散的交换媒介，可以支持任何数据格式，每一格式由一无符号整数标识，对标准(预定义)剪贴板格式，该值是Win32 API定义的 常量；对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一 致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用，不能在网络上使用。<br>
<strong>2.7 动态数据交换</strong><br>
动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输，也可以当出现新 数据时，通过发送更新值在应用程序间动态交换数据。<br>
DDE和剪贴板一样既支持标准数据格式(如文本、位图等)，又可以支持自己定义的数据格式。但它们的数据传输机制却不同，一个明显区别是剪贴板操作几 乎总是用作对用户指定操作的一次性应答－如从菜单中选择Paste命令。尽管DDE也可以由用户启动，但它继续发挥作用一般不必用户进一步干预。DDE有 三种数据交换方式：<br>
(1) 冷链：数据交换是一次性数据传输，与剪贴板相同。<br>
(2) 温链：当数据交换时服务器通知客户，然后客户必须请求新的数据。<br>
(3) 热链：当数据交换时服务器自动给客户发送数据。<br>
DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC，它们有更紧密耦 合的通信要求。大多数基于Windows的应用程序都支持DDE。<br>
<strong>2.8 对象连接与嵌入</strong><br>
应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档)，OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的 服务。例如，OLE支持的字处理器可以嵌套电子表格，当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时，该表格已在 原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展，而如果使用DDE，用户要显式地启动电子表格编辑器。<br>
同DDE技术相同，大多数基于Windows的应用程序都支持OLE技术。<br>
<strong>2.9 动态连接库</strong><br>
Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享，这就又给进程间通信开辟了一条新的途径，当然访问时要注意同步问题。<br>
虽然可以通过DLL进行进程间数据共享，但从数据安全的角度考虑，我们并不提倡这种方法，使用带有访问权限控制的共享内存的方法更好一些。<br>
<strong>2.10 远程过程调用</strong><br>
<strong>Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数，这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既 可以在单机不同进程间使用也可以在网络中使用。<br>
由于Win32 API提供的RPC服从OSF- DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过 Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程 序。</strong><br>
<strong>2.11 NetBios函数</strong><br>
Win32 API提供NetBios函数用于处理低级网络控制，这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊 低级网络功能要求的应用程序，其它应用程序最好不要使用NetBios函数来进行进程间通信。<br>
<strong>2.12 Sockets</strong><br>
Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的 网络编程接口。除了Berkeley Socket原有的库函数以外，还扩展了一组针对Windows的函数，使程序员可以充分利用Windows的消息 机制进行编程。<br>
现在通过Sockets实现进程通信的网络应用越来越多，这主要的原因是Sockets的跨平台性要比其它IPC机制好得多，另外 WinSock 2.0不仅支持TCP/IP协议，而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作，这使得在单机的 进程间进行简单数据传递不太方便，这时使用下面将介绍的WM_COPYDATA消息将更合适些。<br>
<strong>2.13 WM_COPYDATA消息</strong><br>
WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时，发送方只需使用调用SendMessage函数，参数是 目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息，这样收发双方就实现 了数据共享。<br>
WM_COPYDATA是一种非常简单的方法，它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高，并且它只能用于Windows平台的单 机环境下。</font></p>
<p><strong><font face="宋体" size="4">3 结束语</font></strong></p>
<p><font face="宋体" size="3">　　Win32 API为应用程序实现进程间通信提供了如此多种选择方案，那么开发者如何进行 选择呢？通常在决定使用哪种IPC方法之前应考虑以下一些问题：<br>
(1)应用程序是在网络环境下还是在单机环境下工作。<br>
(2)应用程序是否需要和运行其它操作系统的程序通信。<br>
(3)应用程序是否需要动态选择与其进行通信的应用程序。<br>
(4)应用程序是否需要提供多种数据格式与许多不同的应用程序通信，例如使用其它应用程序的剪切和粘贴功能。<br>
(5)性能是应用程序的关键吗？所有的IPC机制都有一定的通信开销，但不同的机制间相差很大。<br>
综上所述，选择哪种IPC机制进行通信主要是根据应用的类型和运行环境来选择，在实用性的前提下，兼顾通用性、易扩充性和高效性。在实际应用中，一个 应用往往要用到多种IPC机制来完成通信任务</font></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/5cbd7e31333d6213ebc4af96.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/5cbd7e31333d6213ebc4af96.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:09</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/5cbd7e31333d6213ebc4af96.html</guid>
</item>

<item>
        <title><![CDATA[开机自启动 进程默认目录 SetcurrentDirectory]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/e932560f549f06206059f394.html]]></link>
        <description><![CDATA[
		
		<p>软件即将发布，可以一个莫名其妙的问题将我差点雷到！</p>
<p>本软件是需要随开机启动的，软件启动后会开启几个新的进程。如果是鼠标双击启动，好好的，但是一旦安装后，随开机启动，就会弹出&ldquo;系统不能找到指定文件的错误&rdquo;。</p>
<p>原来是 ：系统当前目录错误搞的鬼！</p>
<p>如果是随开机启动，进程的当前目录为：C:\Documents and Settings\Administrator ，即系统的用户所在目录。</p>
<p>这时候我们 就需要使用SetCurrentDirectory 将系统当前目录设置为我们想要的，这样，在开启其他进程时，就能够找到以相对目录设置的可执行文件的路径了</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/94b4871a0fbcabbe4bedbce4.html">http://hi.baidu.com/isafesoft/blog/item/94b4871a0fbcabbe4bedbce4.html</a></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/e932560f549f06206059f394.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/e932560f549f06206059f394.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:05</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/e932560f549f06206059f394.html</guid>
</item>

<item>
        <title><![CDATA[vc 实现监控打印机 打印过的文档]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/16a3e011598aa7cca7ef3f8b.html]]></link>
        <description><![CDATA[
		
		<p>要实现监控打印过的文档，获得打印过的文档的目录名称，文件名称，以及文件内容，经过我几天的研究，最终确定了如下方案：<br>
1.枚举系统上的所有打印机<br>
EnumPrinters 枚举系统中安装的打印机 <br>
2.<br>
打开该打印机并获得其句柄、<br>
OpenPrinter(<strong>printerName</strong>, &amp;hPrinter, NULL) ,<br>
<strong>PrinterName为打印机的名称。</strong><br>
3，<br>
GetPrinter(hPrinter, 2, (LPBYTE)(&amp;pPrinterStatus), 0, &amp;iLenPrinterBNeeded);<br>
pPrinterStatus = (PRINTER_INFO_2*)malloc(iLenPrinterBNeeded);<br>
<br>
<br>
while(1) <br>
{<br>
if ( GetPrinter(hPrinter, 2, (LPBYTE)pPrinterStatus, iLenPrinterBNeeded, &amp;iPrinterInfoReceived)  )&nbsp;&nbsp;  //Get Printer information<br>
{<br>
if ( pPrinterStatus-&gt;cJobs &gt; 0 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //check if there are jobs in the print queue<br>
{<br>
<br>
EnumJobs( hPrinter, 0, 1, 2, (LPBYTE)pEnumJobInfo2, 0, &amp;iLenEnumJobBNeeded, &amp;iJobNums);<br>
pEnumJobInfo2 = (JOB_INFO_2*)malloc(iLenEnumJobBNeeded);<br>
<br>
if ( EnumJobs( hPrinter, 0, 1, 2, (LPBYTE)pEnumJobInfo2, iLenEnumJobBNeeded, &amp;iEnumJobInfoReceived, &amp;iJobNums) )&nbsp;&nbsp;&nbsp;  <br>
{<br>
currentJobID = pEnumJobInfo2-&gt;JobId;<br>
<br>
if( lastJobID != currentJobID)<br>
{<br>
LogSystem();<br>
}<br>
<br>
lastJobID = pEnumJobInfo2-&gt;JobId;<br>
}<br>
else<br>
{<br>
GetLocalTime(&amp;sysTime);<br>
<br>
ofstream startupInfo;<br>
startupInfo.open(&quot;C:\\PMW32_Logs\\SystemStartupInfo.txt&quot;,ios::app);<br>
startupInfo&lt;&lt;sysTime.wYear&lt;&lt;&quot;-&quot;&lt;&lt;sysTime.wMonth&lt;&lt;&quot;-&quot;&lt;&lt;sysTime.wDay&lt;&lt;&quot; &quot;&lt;&lt;sysTime.wHour&lt;&lt;&quot;:&quot;&lt;&lt;sysTime.wMinute&lt;&lt;endl;<br>
startupInfo.close();<br>
<br>
free(pEnumJobInfo2);<br>
pEnumJobInfo2 = NULL;<br>
}<br>
<br>
}<br>
<br>
}<br>
else<br>
{<br>
GetLocalTime(&amp;sysTime);<br>
<br>
ofstream startupInfo;<br>
startupInfo.open(&quot;C:\\PMW32_Logs\\SystemStartupInfo.txt&quot;,ios::app);<br>
startupInfo&lt;&lt;sysTime.wYear&lt;&lt;&quot;-&quot;&lt;&lt;sysTime.wMonth&lt;&lt;&quot;-&quot;&lt;&lt;sysTime.wDay&lt;&lt;&quot; &quot;&lt;&lt;sysTime.wHour&lt;&lt;&quot;:&quot;&lt;&lt;sysTime.wMinute&lt;&lt;endl;<br>
startupInfo&lt;&lt;&quot;  &quot;&lt;&lt;&quot;ERROR! Retrieve current printer status failed ERROR CODE: &quot;&lt;&lt;GetLastError()&lt;&lt;endl&lt;&lt;endl;<br>
startupInfo.close();<br>
<br>
ClosePrinter(hPrinter);<br>
free(pPrinterStatus);<br>
<br>
Sleep(1000);<br>
pPrinterStatus = NULL;<br>
<br>
return 1;<br>
}<br>
<br>
//DebugPrinterInfo( pPrinterStatus );&nbsp;&nbsp;  //replace this function with file streamwriter function when release<br>
<br>
Sleep(500);&nbsp;&nbsp;  //printer status checking interval<br>
<br>
}<br>
<br>
free(pPrinterStatus);<br>
free(pEnumJobInfo2);<br>
pPrinterStatus = NULL;<br>
pEnumJobInfo2 = NULL;<br>
ClosePrinter(hPrinter);<br>
<br>
4，<strong>StartDoc </strong>开始一个打印作业，里面包含一个打印结构，结构里面包含打印的文档路径<br>
<strong>StartDoc里面包含打印的文件名称。</strong><br>
5，WritePrinter 将发送目录中的数据写入打印机<br>
在这里获取打印的内容。<br>
搞定。</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/eb39a5c08ef7ae110ff477c7.html">http://hi.baidu.com/isafesoft/blog/item/eb39a5c08ef7ae110ff477c7.html</a></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/16a3e011598aa7cca7ef3f8b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/16a3e011598aa7cca7ef3f8b.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:03</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/16a3e011598aa7cca7ef3f8b.html</guid>
</item>

<item>
        <title><![CDATA[如何设置进程线程只运行在指定的cpu上，及为什么这么做]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/01ad502c76ba78e68a13998b.html]]></link>
        <description><![CDATA[
		
		７．１０ 亲缘性<br>
按照默认设置，当系统将线程分配给处理器时，Windows 2000使用软亲缘性来进行操作。这意味着如果所有其他因素相同的话，它将设法在它上次运行的那个处理器上运行线程。让线程留在单个处理器上，有助于重复 使用仍然在处理器的内存高速缓存中的数据。<br>
有一种新的计算机结构，称为NUMA（非统一内存访问），在该结构中，计算机包含若干块插件板，每个插 件板上有4个CPU和它自己的内存区。下面这个插图显示了一台配有3块插件板的计算机，总共有12个CPU，这样，任何一个线程都可以在12个CPU中的 任何一个CPU上运行。<br>
当CPU访问的内存是它自己的插件板上的内存时，NUMA系统运行的性能最好。如果CPU需要访问位于另一个插件板上的内 存时，就会产生巨大的性能降低。在这样的环境中，就需要来自一个进程中的线程在CPU 0至3上运行，让另一个进程中的线程在CPU 4至7上运行，依次类推。为了适应这种计算机结构的需要，Windows 2000允许你设置进程和线程的亲缘性。换句话说，你可以控制哪个CPU能够运行某些线程。这称为硬亲缘性。<br>
请注意，子进程可以继承进程的亲缘 性。<br>
为此<strong>windows提供了设置亲缘性的函数。SetProcessAffinityMask</strong>。 当然，还有一个函数能够返回进程的亲缘性位屏蔽，它就是GetProcessAffinityMask。<br>
有时你可能想要将进程中的一个线程限制到 一组CPU上去运行。可以通过调用SetThreadAffinityMask，你就能为各个线程设置亲缘性屏蔽。<br>
最后作以下几点说明：<br>
（1）Windows９８ 无论计算机中实际拥有多少个CPU，Windows 98只使用一个CPU。<br>
（2）在大多数环境中，改变线程的亲缘性就会影响调度程序有效地在 各个CPU之间移植线程的能力，而这种能力可以最有效地使用CPU时间。 <br>
（3）有时强制将一个线程分配给特定的CPU的做法是不妥当的。<br>
（4） 当Windows 2000在x86计算机上引导时，你可以限制系统能够使用的CPU的数量。只要在Boot.ini文件的[operating systems]栏改动如下示：<br>
<span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">get system info</span><span style="color: rgb(0,128,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"></span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp; SYSTEM_INFO SystemInfo;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp; GetSystemInfo(</span><span style="color: rgb(0,0,0)">&amp;</span><span style="color: rgb(0,0,0)">SystemInfo);<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp; printf(</span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)">dwNumberOfProcessors=%u, dwActiveProcessorMask=%u, wProcessorLevel=%u, </span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)">wProcessorArchitecture=%u, dwPageSize=%u </span><span style="color: rgb(0,0,0)">&quot;</span><span style="color: rgb(0,0,0)">,<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SystemInfo.dwNumberOfProcessors, SystemInfo.dwActiveProcessorMask, SystemInfo.wProcessorLevel, <br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SystemInfo.wProcessorArchitecture,SystemInfo.dwPageSize<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">if</span><span style="color: rgb(0,0,0)">(SystemInfo.dwNumberOfProcessors </span><span style="color: rgb(0,0,0)">&lt;=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">1</span><span style="color: rgb(0,0,0)">) </span><span style="color: rgb(0,0,255)">return</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwMask </span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">0x0000</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwtmp </span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">0x0001</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">int</span><span style="color: rgb(0,0,0)"> nProcessorNum </span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">0</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">for</span><span style="color: rgb(0,0,0)">(</span><span style="color: rgb(0,0,255)">int</span><span style="color: rgb(0,0,0)"> i </span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">0</span><span style="color: rgb(0,0,0)">; i </span><span style="color: rgb(0,0,0)">&lt;</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">32</span><span style="color: rgb(0,0,0)">; i</span><span style="color: rgb(0,0,0)">++</span><span style="color: rgb(0,0,0)">)<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img style="display: none" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span><span style="color: rgb(0,0,0)">{<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">if</span><span style="color: rgb(0,0,0)">(SystemInfo.dwActiveProcessorMask </span><span style="color: rgb(0,0,0)">&amp;</span><span style="color: rgb(0,0,0)"> dwtmp)<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img style="display: none" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span><span style="color: rgb(0,0,0)">{<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nProcessorNum</span><span style="color: rgb(0,0,0)">++</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">if</span><span style="color: rgb(0,0,0)">(nProcessorNum </span><span style="color: rgb(0,0,0)">&lt;=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">2</span><span style="color: rgb(0,0,0)">)<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img style="display: none" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span><span style="color: rgb(0,0,0)">{<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">如果系统中有多个处理器，则选择第二个处理器</span><span style="color: rgb(0,128,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwMask </span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)"> dwtmp;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img style="display: none" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span><span style="color: rgb(0,0,0)">{<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">break</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwtmp </span><span style="color: rgb(0,0,0)">*=</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,0)">2</span><span style="color: rgb(0,0,0)">;<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">end of for<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">进程与指定cpu绑定</span><span style="color: rgb(0,128,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"></span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp; SetProcessAffinityMask(GetCurrentProcess(), dwMask);<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">线程与指定cpu绑定<br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">SetThreadAffinityMask(GetCurrentThread(),dwMask);</span><span style="color: rgb(0,128,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top"></span><span style="color: rgb(0,0,0)"><br>
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">return</span><span style="color: rgb(0,0,0)"> ;<br>
==========<br>
一个典型的应用：<br>
将界面的线程限制在一个cpu，将其他实时性要求较高的线程限制在另一个cpu，这样，当界面需要占用大量cpu时间时，就不会拖累其他实时性要求较高的线程的执行。<br>
<br>
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  前几天在客户现场调试设备时，客户的程序开了一个线程来兜各个状态，并显示各个动作所花费的时间，用鼠标拖拽界面进行移动时，CPU资源耗费很高，设备运 行的速度也随之下降。为了解决这个问题，我们想办法将线程绑定到另一颗CPU上，实现方法如下。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  首先用API函数创建一个线程，</p>
<pre class="syntax"><strong>HANDLE CreateThread(<br>  LPSECURITY_ATTRIBUTES</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">lpThreadAttributes</a></em><strong>, </strong>// SD<br><strong>  SIZE_T</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">dwStackSize</a></em><strong>,                       </strong>// initial stack size<br><strong>  LPTHREAD_START_ROUTINE</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">lpStartAddress</a></em><strong>,    </strong>// thread function<br><strong>  LPVOID</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">lpParameter</a></em><strong>,                       </strong>// thread argument<br><strong>  DWORD</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">dwCreationFlags</a></em><strong>,                    </strong>// creation option<br><strong>  LPDWORD</strong><em> <a class="synParam" href="http://hi.baidu.com/fc/editor/">lpThreadId</a></em>                        // thread identifier<br><strong>);</strong></pre>
<pre class="syntax"> </pre>
<pre class="syntax">通过调用SetThreadAffinityMask，就能为各个线程设置亲缘性屏蔽：   <br>  DWORD_PTR   SetThreadAffinityMask(HANDLE   hThread,   <br>        DWORD_PTR   dwThreadAffinityMask);   <br>  该函数中的h   T   h   r   e   a   d参数用于指明要限制哪个线程，   dwThreadAffinityMask用于指明该线程能够在哪个CPU上运行。dwThreadAffinityMask必须是进程的亲缘性屏蔽的相应子集。返回值是线程的前一个亲缘性屏蔽。因此，若要将3个线程限制到CPU1、2和3上去运行，可以这样操作：   <br>  //Thread   0   can   only   run   on   CPU   0.   <br>  SetThreadAffinityMask(hThread0,   0x00000001);   <br>    <br>  //Threads   1,   2,   3   run   on   CPUs   1.   <br>  SetThreadAffinityMask(hThread1,   0x0000000E);   <br>     经试验，此方法解决了客户拖拽界面导致整台设备变慢的问题。</pre> <a href="http://hi.baidu.com/mikenoodle/blog/item/01ad502c76ba78e68a13998b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/01ad502c76ba78e68a13998b.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:02</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/01ad502c76ba78e68a13998b.html</guid>
</item>

<item>
        <title><![CDATA[API hook实现r3下简单的进程保护]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/6455d307149f37c77b89478a.html]]></link>
        <description><![CDATA[
		
		<p><span>API hook实现r3下简单的进程保护</span></p>
<span>
<p>其实在r3下似乎做进程保护没什么强度可言，不过总是可以用来玩玩的：P<br>
现在似乎没有多少人还会去hook kernel32中的函数了，因为大多数（或许有一些是例外的）函数都得从ntdll.dll中过然后sysenter。&mdash;&mdash;至少，我们需要照顾的几个函数是需要过ntdll的 ：）</p>
<p>杀进程常见的就是OpenProcess＋TerminateProcess，也可以一个一个线程杀（OpenThread＋ TerminateThread），所以要挂的函数就是这么4个了，当然。。OpenProcess &amp; OpenThread还有许多方法可以代替，比如直接枚举句柄然后复制过来，这样就有了个源句柄相同的访问权限，并且，这个权限有可能是包含 PROCESS(THREAD)_TERMINATE的，所以我们还需要在DuplicateHandle上下钩，剩下的一些后文再说。</p>
<p>做hook的方法多如牛毛，在此不多说了。主要需要说的就是关于判断函数参数的问题。</p>
<p>注：在hook时，应hook NtOpenProcess(对应OpenProcess) NtOpenThread(对应OpenThread) NtTerminateProcess(对应TerminateProcess) NtTerminateThread(T对应erminateThread) NtDuplicateObject(对应DuplicateHandle)，为方便描述，下文中仍然使用kernel32中的函数名。</p>
<p>OpenProcess，OpenThread就不多说了，ProcessId 和ThreadId都是系统范围内唯一的，所以不需要做什么深究。主要是TerminateProcess，TerminateThread， DuplicateHandle传入的句柄都是进程范围内唯一，这就需要去判断Handle所指向的目标。</p>
<p>在ntdll中有两个API可以帮助我们完成对TerminateProcess，TerminateThread的Handle所指向的 Process(Thread)Id的判断&mdash;&mdash;Zw(Nt)QueryInformationProcess &amp; Zw(Nt)QueryInformationThread。</p>
<p>一些资料：</p>
<p>NTSYSCALLAPI<br>
NTSTATUS<br>
NTAPI<br>
NtQueryInformationThread (<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in HANDLE ThreadHandle,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in THREADINFOCLASS ThreadInformationClass,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __out_bcount(ThreadInformationLength) PVOID ThreadInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in ULONG ThreadInformationLength,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __out_opt PULONG ReturnLength<br>
&nbsp;&nbsp;&nbsp;&nbsp;  );</p>
<p>NTSYSCALLAPI<br>
NTSTATUS<br>
NTAPI<br>
NtQueryInformationProcess (<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in HANDLE ProcessHandle,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in PROCESSINFOCLASS ProcessInformationClass,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __out_bcount(ProcessInformationLength) PVOID ProcessInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __in ULONG ProcessInformationLength,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  __out_opt PULONG ReturnLength<br>
&nbsp;&nbsp;&nbsp;&nbsp;  );</p>
<p>typedef enum _PROCESSINFOCLASS {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessBasicInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessQuotaLimits,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessIoCounters,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessVmCounters,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessTimes,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessBasePriority,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessRaisePriority,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessDebugPort,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessExceptionPort,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessAccessToken,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessLdtInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessLdtSize,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessDefaultHardErrorMode,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessIoPortHandlers,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // Note: this is kernel mode only<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessPooledUsageAndLimits,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessWorkingSetWatch,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessUserModeIOPL,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessEnableAlignmentFaultFixup,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessPriorityClass,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessWx86Information,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessHandleCount,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessAffinityMask,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessPriorityBoost,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessDeviceMap,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessSessionInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessForegroundInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessWow64Information,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessImageFileName,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessLUIDDeviceMapsEnabled,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessBreakOnTermination,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessDebugObjectHandle,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessDebugFlags,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessHandleTracing,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessIoPriority,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessExecuteFlags,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessResourceManagement,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessCookie,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ProcessImageInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  MaxProcessInfoClass&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // MaxProcessInfoClass should always be the last enum<br>
} PROCESSINFOCLASS;</p>
<p>typedef enum _THREADINFOCLASS {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadBasicInformation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadTimes,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadPriority,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadBasePriority,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadAffinityMask,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadImpersonationToken,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadDescriptorTableEntry,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadEnableAlignmentFaultFixup,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadEventPair_Reusable,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadQuerySetWin32StartAddress,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadZeroTlsCell,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadPerformanceCount,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadAmILastThread,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadIdealProcessor,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadPriorityBoost,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadSetTlsArrayAddress,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadIsIoPending,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadHideFromDebugger,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadBreakOnTermination,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadSwitchLegacyState,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ThreadIsTerminated,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  MaxThreadInfoClass<br>
} THREADINFOCLASS;</p>
<p>typedef struct _PROCESS_BASIC_INFORMATION {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  NTSTATUS ExitStatus;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  PPEB PebBaseAddress;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ULONG_PTR AffinityMask;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  KPRIORITY BasePriority;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ULONG_PTR UniqueProcessId;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ULONG_PTR InheritedFromUniqueProcessId;<br>
} PROCESS_BASIC_INFORMATION;<br>
typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;</p>
<p>typedef struct _CLIENT_ID {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  HANDLE UniqueProcess;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  HANDLE UniqueThread;<br>
} CLIENT_ID;<br>
typedef CLIENT_ID *PCLIENT_ID;</p>
<p>typedef struct _THREAD_BASIC_INFORMATION {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  NTSTATUS ExitStatus;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  PTEB TebBaseAddress;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  CLIENT_ID ClientId;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  ULONG_PTR AffinityMask;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  KPRIORITY Priority;<br>
&nbsp;&nbsp;&nbsp;&nbsp;  LONG BasePriority;<br>
} THREAD_BASIC_INFORMATION;<br>
typedef THREAD_BASIC_INFORMATION *PTHREAD_BASIC_INFORMATION;</p>
<p>在THREAD_BASIC_INFORMATION中，ClientId.UniqueThread既是hThread所代表的 ThreadId，至于ClientId.UniqueProcess是hThread所属的Process的ProcessId还是为NULL，我没有 去测试。PROCESS_BASIC_INFORMATION中的UniqueProcessId既是hProcess所代表的ProcessId。我们 可以通过这两个函数判断传入的Handle是否为我们要保护的进程（的线程），如果是，那么直接return STATUS_ACCESS_DENIED。</p>
<p>DuplicateHandle传入的参数则要难判断一些，因为看起来似乎没有现成的函数来帮助我们完成我们需要做的工作（至少我没有找到相关函数），但是，仍然有两种方法：</p>
<p>1、DuplicateHandle调用之前ZwQuerySystemInformation的SystemHandleInformation 号调用枚举系统中的句柄，找到返回的数组结构中的.Handle==SourceHandle的那一项，然后检查.UniqueProcessId即可。<br>
2、DuplicateHandle调用之后直接尝试作为hThread和hProcess查询得到的Handle，如果能 ZwQueryInformationProcess(Thread)能正常进行，那么就和ZwTerminateProcess(Thread)的处理 方法相同，否则放行。</p>
<p>不过进程保护远远不是这么简单的事情，还有很多的函数可以用来终止（或破坏）进程的运行，比如ZwSuspendProcess(Thread) (暂停进程（线程）) ZwSetContextThread(设置线程上下文) ZwSetInformationThread(Process)(设置线程（进程）信息) ZwWriteVirtualMemory(读写进程内存) DbgUiDebugActiveProcess(调试进程) 等。自己看看ntdll的导出表吧，偶把什么都说了的话，就没有你实践的机会了 ：P</p>
<p>到这儿，除了代码，剩下的你所需要都应当得到了……</p>
<p>对了。。做hook别忘记连读取文件的那些函数一并挂住，让他恢复hook都恢复不了。。嘿嘿。。</p>
<p><a href="http://hi.baidu.com/isafesoft/blog/item/605ed6c81a2c744cf31fe7c5.html">http://hi.baidu.com/isafesoft/blog/item/605ed6c81a2c744cf31fe7c5.html</a></p>
</span> <a href="http://hi.baidu.com/mikenoodle/blog/item/6455d307149f37c77b89478a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/6455d307149f37c77b89478a.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-27  00:01</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/6455d307149f37c77b89478a.html</guid>
</item>

<item>
        <title><![CDATA[CreateEvent 和OpenEvent时事件全局名称问题 Global]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/523aaa34f4e163bdd0a2d389.html]]></link>
        <description><![CDATA[
		
		<table class="FCK__ShowTableBorders" style="table-layout: fixed; width: 100%">
    <tbody>
        <tr>
            <td>
            <div class="cnt" >今天看别人的代码，注意到在操作事件对象时如下：<br>
            sprintf(cTemp,&quot;<strong>Global\\EVENT_%s</strong>&quot;,lpFileName);<br>
            m_hMapEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,cTemp);<br>
            <br>
            注意到了没有，事件对象名称前面加了个Global，开始以为这个Global是任意加的，可以随便改成其他字符，后来经过查阅msdn和google，才发现这个Global大有来头！<br>
            <br>
            Global\\xxxEvent 可以保证：<span class="Apple-style-span" style="font-weight: normal; font-size: medium; word-spacing: 0px; text-transform: none; color: rgb(0,0,0); text-indent: 0px; line-height: normal; font-style: normal; white-space: normal; letter-spacing: normal; border-collapse: separate; font-variant: normal; orphans: 2; widows: 2"><span class="Apple-style-span" style="font-size: 14px; line-height: 21px; text-align: left">在创建命名时间对象时指定名字是全局的。<br>
            这样做的好处如下：<br>
            这样创建的内核对象无论出于服务，还是内核中，应用层都可以打开并使用这个内核对象。<br>
            </span></span>CreateEvent( NULL, FALSE, FALSE, &quot;Global\\CSAPP&quot; );  这是一个内核对象。<br>
            ==========<br>
            <span class="Apple-style-span" style="font-weight: normal; font-size: medium; word-spacing: 0px; text-transform: none; color: rgb(0,0,0); text-indent: 0px; line-height: normal; font-style: normal; white-space: normal; letter-spacing: normal; border-collapse: separate; font-variant: normal; orphans: 2; widows: 2"><span class="Apple-style-span" style="font-size: 14px; line-height: 21px; text-align: left">
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">关于在通过 事件对象 在服务程序和普通桌面应用程序相互之间通信的问题，分类情况进行讨论：<br>
            1、普通桌面应用程序中创建事件，服务程序中打开事件<br>
            XP的情况<br>
            普通桌面应用程序中创建：</p>
            <div class="highlighter">
            <ol class="highlighter-cpp" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 5px 0px 5px 35px; padding-top: 0px; list-style-type: none">
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span>m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT(</span><span class="string">&quot;{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;</span><span>));</span></li>
            </ol>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">服务程序中打开并置其为有信号：</p>
            <div class="highlighter">
            <ol class="highlighter-cpp" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 5px 0px 5px 35px; padding-top: 0px; list-style-type: none">
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span class="datatypes">HANDLE</span><span> hEvent = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT(</span><span class="string">&quot;{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;</span><span>));</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span class="datatypes">DWORD</span><span> dwErr = ::GetLastError();</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span>::SetEvent(m_hEvent);</span></li>
            </ol>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px"> vista下情况<br>
            vista下有点问题是，如果像上面那样写的话，服务程序在打开该事件对象时报错&ldquo;系统找不到指定的文件。&rdquo;，原因是XP下服务程序和应用程序创建的内核对象的命名空间默认是全局的，而vista下则不是，服务创建的内核对象默认在session0下，而用户创建的内核对象默认在各自的session下（session1，session2……），解决此问题的方法很简单，就是在创建命名时间对象时指定名字是全局的，也就是将CreateEvent和OpenEvent的最后一个参数设置为TEXT(&quot;Global\\{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;)。</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">2、服务程序中创建事件，普通桌面应用程序中打开事件<br>
            下面就不分系统说明，只说说根本的问题。</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">服务程序中创建：</p>
            <div class="highlighter">
            <ol class="highlighter-cpp" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 5px 0px 5px 35px; padding-top: 0px; list-style-type: none">
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span>m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT(</span><span class="string">&quot;{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;</span><span>));</span></li>
            </ol>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">普通桌面应用程序中打开：</p>
            <div class="highlighter">
            <ol class="highlighter-cpp" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 5px 0px 5px 35px; padding-top: 0px; list-style-type: none">
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span class="datatypes">HANDLE</span><span> hEvent = ::OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT(</span><span class="string">&quot;{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;</span><span>));</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span>::SetEvent(hEvent);</span></li>
            </ol>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">上面的代码不能正常工作，在普通桌面应用程序中打开事件对象时，报错&ldquo;拒绝访问。&rdquo;，并且获得的事件句柄是NULL，原因是这样的，在服务程序中创建的内核对象，默认情况下桌面程序无法打开这个对象，每个内核对象都是有访问控制的，而服务中创建的内核对象权限比较高，当LPSECURITY_ATTRIBUTES这个参数传NULL的时候，将使用默认访问控制。普通桌面应用程序自然没有权限访问了，解决方法如下，在服务程序创建事件对象时，指定确定的安全描述符。</p>
            <div class="highlighter">
            <ol class="highlighter-cpp" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 5px 0px 5px 35px; padding-top: 0px; list-style-type: none">
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> </span><span class="comment">// set SECURITY_DESCRIPTOR</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> SECURITY_DESCRIPTOR secutityDese;</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> ::InitializeSecurityDescriptor(&amp;secutityDese, SECURITY_DESCRIPTOR_REVISION);</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> ::SetSecurityDescriptorDacl(&amp;secutityDese,TRUE,NULL,FALSE);</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"> </li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> SECURITY_ATTRIBUTES securityAttr;</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"> </li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> </span><span class="comment">// set SECURITY_ATTRIBUTES</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> securityAttr.nLength = </span><span class="keyword">sizeof</span><span> SECURITY_ATTRIBUTES;</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> securityAttr.bInheritHandle = FALSE;</span></li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> securityAttr.lpSecurityDescriptor = &amp;secutityDese;</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"> </li>
                <li style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"><span> m_hEvent = ::CreateEvent(&amp;securityAttr, FALSE, FALSE, TEXT(</span><span class="string">&quot;{67BDE5D7-C2FC-49f5-9096-C255AB791B75}&quot;</span><span>));</span></li>
                <li class="alt" style="padding-right: 0px; padding-left: 0px; padding-bottom: 2px; margin: 0px; padding-top: 2px; list-style-type: decimal"> </li>
            </ol>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">这样普通桌面应用程序再去打开该事件对象就没有问题了。（注：vista下事件对象的名字仍然要指定全局空间）</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px"> </p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">另外再谈谈Windows编程中的session，最近遇到一些很郁闷的问题。一直在折腾Vista下的服务程序启动进程的问题，有了点小小的体会，记下来，希望能帮到跟我遇到一样问题的朋友。Windows xp、Vista中，服务程序都是运行在session0中，而后面的第1、2、...、N个用户则分别运行在session1、session2、...、sessionN中。不同的session有不同的namespace，但是由于目前主流的用户windows平台WinXP支持快速用户切换，所以我们感觉不到这些差异。</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">在XP中，用Sevice启动的进程跟我们用当前用户启动的进程在编程上似乎没什么区别，用起来都一样。  可是到了vista下，情况就不一样了。vista新的安全机制对不同的session之间的限制做了加强。一些命名内核对象，如Event的使用，为了进行进程通信，在进程1(处在session1中)中，我创建了一个命名的事件对象，然后在进程2（由我的服务启动，所以运行在session0中）中检测该Event，发现始终检查不到，而且错误信息是&ldquo;系统找不到指定的文件。&rdquo;另外专门写了个小程序去检测（直接运行，也是运行在session1中），却能检测到。</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">后来仔细读了MSDN中关于&ldquo; Kernel Object Name Spaces&rdquo;的资料，才明白：一些命名的内核对象，比如： events, semaphores, mutexes, waitable timers, file-mapping objects, job objects，都只是在自己的namespace里唯一存在，不同的session因为namespace不同，所以会导致上面的现象。详细的信息可以参考MSDN中的CreateEvent资料中对参数lpName的说明。</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">大家可以参考MSDN中的&ldquo; Kernel Object Name Spaces&rdquo;（没事贴点MSDN中的东东，MSDN才是王道啊）：</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px"> </p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">Kernel Object Name Spaces</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">A Terminal Services server has multiple namespaces for the following named kernel objects: events, semaphores, mutexes, waitable timers, file-mapping objects, and job objects. There is a global namespace used primarily by services such as client/server applications. In addition, each client session has a separate namespace for these objects. Note that this differs from the standard Windows environment in which all processes and services share a single namespace for these objects.</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the &quot;Global\&quot; prefix to the object name. For example, the following code calls<span class="Apple-converted-space"> </span><mshelp:link tabindex="0" errorurl="../common/badjump.htm" keywords="_win32_createevent"><strong>CreateEvent</strong></mshelp:link><span class="Apple-converted-space"> </span>and creates an event object named CSAPP in the global namespace:</p>
            <div class="conditionalSection" development_language="C++">
            <div class="conditionalSection" development_language="C++"><span class="devlangLabel">[C++]</span>
            <div class="codeSnippet">
            <pre>CreateEvent( NULL, FALSE, FALSE, &quot;Global\\CSAPP&quot; );</pre>
            </div>
            </div>
            </div>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">Service applications in a Terminal Services environment use the global namespace by default. Processes started under session zero (typically the console session) also use the global namespace by default. The global namespace enables processes on multiple client sessions to communicate with a service application or with the console session. For example, a client/server application might use a mutex object for synchronization. The server component running as a service can create the mutex object in the global namespace. Then the client component running as a process running under a client session can use the &quot;Global\&quot; prefix to open the mutex object.</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">Another use of the global namespace is for applications that use named objects to detect that there is already an instance of the application running in the system across all sessions. This named object must be created or opened in the global namespace instead of the per-session namespace. Note that the more common case of running the application once per session is supported by default since the named object is created in a per session namespace.</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">Client processes can also use the &quot;Local\&quot; prefix to explicitly create an object in their session namespace.</p>
            <p style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 1em 0px 0.5em; padding-top: 0px">The &quot;Local&quot;, &quot;Global&quot; and &quot;Session&quot; prefixes are reserved for system use and should not be used as names for kernel objects. These keywords are case sensitive. On Windows 2000 without Terminal Services, these keywords are ignored. On Windows NT 4.0 without Terminal Services, and earlier versions of Windows NT, the functions for creating or opening these objects fail if you specify a name containing the backslash character (\).</p>
            <blockquote style="border-right: rgb(152,164,127) 1px dotted; padding-right: 10px; border-top: rgb(152,164,127) 1px dotted; padding-left: 10px; font-weight: normal; font-size: 12px; padding-bottom: 10px; margin: 10px 0px; border-left: rgb(152,164,127) 1px dotted; line-height: normal; padding-top: 10px; border-bottom: rgb(152,164,127) 1px dotted; font-style: normal; background-color: rgb(245,245,245); font-variant: normal; font-size-adjust: none; font-stretch: normal"><strong>Windows XP:&nbsp;&nbsp;</strong>Fast user switching is implemented using Terminal Services sessions. The first user to log on uses session zero, the next user to log on uses session one, and so on. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users. For more information, see<span class="Apple-converted-space"> </span><mshelp:link tabindex="0" errorurl="../common/badjump.htm" keywords="_win32_fast_user_switching">Fast User Switching</mshelp:link>.</blockquote></span></span></div>
            </td>
        </tr>
    </tbody>
</table>
<a href="http://hi.baidu.com/isafesoft/blog/item/5576b7ffff4b1c1c09244d83.html">http://hi.baidu.com/isafesoft/blog/item/5576b7ffff4b1c1c09244d83.html</a> <a href="http://hi.baidu.com/mikenoodle/blog/item/523aaa34f4e163bdd0a2d389.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/Win32">Win32</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/523aaa34f4e163bdd0a2d389.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-26  23:58</pubDate>
        <category><![CDATA[Win32]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/523aaa34f4e163bdd0a2d389.html</guid>
</item>

<item>
        <title><![CDATA[《明朝那些事儿》中的经典语录]]></title>
        <link><![CDATA[http://hi.baidu.com/mikenoodle/blog/item/4067ecc48ab093a38326ac1b.html]]></link>
        <description><![CDATA[
		
		<font color="#00ccff">朋友？交情？呸！时务！<br>
</font>
<p><font color="#00ccff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1.风萧萧兮易水寒，欠了债兮你要还。<br>
<br>
　　2.必须亮出自己的獠牙,才能有效地控制住所有的人,即使是皇帝也不例外.<br>
<br>
　　3.观点斗争是假的、方向斗争也是假的，只有权力斗争才是真的。<br>
<br>
　　4.其次,我们知道,但凡高水平的打群架斗殴,都有固定的行动计划,逃跑路线,事前统一分发兵器(如菜刀,木棍等),事后找人出来背黑锅,一应俱全才开始行动.<br>
<br>
　　5.这就是传说中的&quot;官话&quot;，俗称废话。<br>
<br>
　　6.哭是为了发泄情绪，流泪是最为重要的，而闹事要的就是声势，低声哭没啥用，一定要做到雷声大雨点小，以最小的精力换取最大的效果。<br>
<br>
　　7.纵使憨直，诚然不屈，这就是明代官员的气节。<br>
<br>
　　8.没有竞争的完全市场只存在于理论想象之中&mdash;&mdash;引自微观经济学(高等教育出版社出版)<br>
<br>
　　9.要知道，越接近心脏的部位越能得到血液，同理，天天见皇帝也着实是个美差，甭管表现如何，混个脸熟才是正理。<br>
<br>
　　10.请大家务必相信，长得帅除了好找老婆外，还容易升官，这条理论应该是靠得住的，夏先生就是一个最典型的例子。<br>
<br>
　　11.世界在发展,时代在进步,事实证明,一对一的政治单挑已经落伍了，为适应潮流的发展,政治组织应运而生,大规模的集体斗殴即将拉开序幕。<br>
<br>
　　12.人无耻到这个地步,是不容易的。<br>
<br>
　　13.不知为什么，这位皇帝继位十年，却一直没有儿子，原因不详，这种事向来都是绝对隐私，一般也是大娘大婶街头谈论的热门话题，换到今天也得偷偷摸摸地上医院，更何况在那万恶的旧社会。<br>
<br>
　　14.所谓以天下为己任，通俗解释就是天下都是老子的，天下事就是本人的私事。<br>
<br>
　　15.活着是我的人，死了是我的死人，化成了灰还要拿去肥田!<br>
<br>
　　16.其实统治王朝就是经营企业，只不过治国这一摊生意更大而已，做一般生意要交税、还要应付工商检查、安全检查、消防检查，逢年过节还得上贡，流年不利还会亏本破产。<br>
<br>
　　相对而言，建立王朝这笔生意就好做得多了，除了启动资金过高(要敢拚命)，经营周期不定(没准明天就牺牲)外，只要一朝成功，就立马鸟枪换炮。从此不但不用交钱，还可以收别人的钱，想收多少自己说了算，除了你管别人，没人敢管你。<br>
<br>
　　因为开政府比开公司的利润更大，前景更广，所以自古以来，无数人都跃跃欲试，但成功者寥寥无几(就那么几个朝代)。<br>
<br>
　　17.所谓神秘，就是搞不清，摸不透，整日捧着道经，四处搜集奇怪的材料，在烟雾缭绕的丹炉前添柴火，然后看着那炼出的鬼都没胆吃的玩意手舞足蹈，谁也不知道这帮人一天到晚到底在干嘛。总之一个字：玄。<br>
<br>
　　18.这实在是让人悲痛的事情，一般到这种时候，都会有固定剧本：跳出来一大帮亲戚朋友，说些什么不要悲伤、要正常发挥水平、告慰先人之类的话，然后主人公擦干眼泪，抬头望天，握拳作苦大仇深状，毅然踏上前进的道路。<br>
<br>
　　19.在清除敌人首脑之前，必须先扫除一切外围和帮手，这就是中华民族的传统智慧，所谓掺沙子、挖墙脚是也。<br>
<br>
　　20.一个二十岁的青年人是不会懂得这些的，他们太天真，太幼稚，他们或许能够在考试中得到一百分，却不可能真正了解其中的含义。所以他们虽然手握真理，却无法使用，满怀热情地踏入社会，却被撞得头破血流。<br>
<br>
　　21.什么是知行合一?答：就是知与行的合一。评：废话。<br>
<br>
　　22.利益，只有充足的利益，才有驱动人们的魔力，这就是这个世界的真实面目，极其的残酷，却异常的真实。面对这个残酷的现实，徐阶终于明白了知行合一的真意，无论有多么伟大正直的理想，要实现它，还必须懂得两个字&mdash;&mdash;变通。只有变通，只有切合实际的行动，才能适应这个变化万千的世界。[816]<br>
<br>
　　23.因为天真的理想主义者纵使执着、纵使顽强，却依然是软弱的。他们并不明白，在这世上，很多事情你可以不理解，却必须接受。只有真正了解这个世界的丑陋与污浊，被现实打击，被痛苦折磨，遍体鳞伤、无所遁形，却从未放弃对光明的追寻，依然微笑着，坚定前行的人，才是真正的勇者。不经历黑暗的人，是无法懂得光明的。<br>
<br>
　　24.所谓同志，是指志同道合的人。<br>
<br>
　　25.其实在这个世界上，只要你敢忽悠，什么奇迹都是可能发生的。正所谓：只有想不到，没有忽不了。<br>
<br>
　　26.这个&ldquo;世代&rdquo;到底有多久?<br>
<br>
　　一般来说，怎么也得有个一百年吧?<br>
<br>
　　一百年?那是起步价，六百年起!还不打折!<br>
<br>
　　27.所谓事可以做绝，话不能说绝，是也。<br>
<br>
　　28.虽然这套把戏在历史上屡见不鲜，却屡试不爽，而要使出这一招，也并非凡人可行，要知道，突然之间悲从心头起，鼻涕眼泪说下就下，毫不含糊，对脸部肌肉和中枢神经的技巧控制已到出神入化之地步，百年之后，犹让人叹为观止。<br>
<br>
　　29.历史证明，落水狗如果不打，就会变成恶狼。<br>
<br>
　　30.可是在权力的擂台上，不折不扣的好人注定是要完蛋的。<br>
<br>
　　31.可是政治高手就如同江湖大侠，想要金盆洗手一走了之，那是很难的，须知做大侠虽然风光，干掉大侠却更为风光。而政治高手们在打架时，从来不会玩三板斧，他们都是耍套路的，从毫不起眼的起手式，环环相扣，直到最后那致命的一击。<br>
<br>
　　32.&ldquo;即使日后身处绝境，亦需坚守，万勿轻言放弃!&rdquo;是的，这句话我一直牢记在心，要隐忍，要忍受痛苦和折磨，要坚强地活下去，只有活下去，才有胜利的希望。<br>
<br>
　　33.这是国家形象问题，换句话说，就算给得起钱，也丢不起人。<br>
<br>
　　34.外交，是指处理国与国之间关系的方法，但它还有另外一个通俗的解释&mdash;&mdash;用最礼貌的方式，说出最肮脏的话。<br>
<br>
　　35.牺牲尊严是不够的，要想在这场残酷的游戏里笑到最后，还必须背离原则，因为眼前的敌手，是一个不讲原则的人。而要战胜一个无原则的对手，唯一的方法就是放弃所有的原则。<br>
<br>
　　36.这就是传说中骂人的最高境界&mdash;&mdash;先夸后骂，夸骂合一。<br>
<br>
　　37.但隐忍和沉寂不是目的，而是手段，它终将爆发在最后那一刻。<br>
<br>
　　38.居庙堂之上，处江湖之远，皆忧其民者，方可为官。<br>
<br>
　　39.在很多情况下，弹劾是一种政治手段，是为了达到某种目的，大家同朝为官，混个功名也不容易，弹劾贪污，下次就少贪点，弹劾礼仪，那就注意点形象，就算是弹劾长相不佳，最多不过是去整容，你来我往，相敬如宾。而死劾，并非是简单的文书，它是一种态度，一种决心，弹劾的罪状是足以置对方死地的罪名，弹劾的对象是足以决定自己生死的人，弹劾的结果是九死一生.知无不言，言无不尽，以生命为赌注，冒死上劾，是为死劾。死劾,不是你死,就是我亡!<br>
<br>
　　40.明知不能成功，明知必死无疑，依然慷慨而行。一般说来这种行为有着很多称呼，比如愚蠢、不自量力、飞蛾扑火等等，而在西方人的眼中，这更是一种不可思议的违反逻辑的行为。而在中国古老的哲学中，这种行为有着一个恰如其当的名称：明知不可为而为之。我深信，这正是我们这个伟大民族的魂魄。<br>
<br>
　　41.十六世纪是信息的时代，想在保住脑袋，混碗饭吃，就得时刻掌握朝廷的最新动态。<br>
<br>
　　42.历经磨难，矢志不移，叫做信念。不畏强权，虽死无惧，叫做勇气。<br>
<br>
　　43.对付流氓，要用流氓的方法。<br>
<br>
　　44.倭人为寇，是为倭寇。但恶劣的品行并不能否定他们的战斗力，且不说这帮人的武艺和战术水平，单说人家冒着掉进海里喂鱼的危险，跑上千里路来抢劫，就能充分说明他们的犯罪决心和毅力。<br>
<br>
　　45.四十个人就敢到南京搞自助游，要有四千个人，没准就敢去北京集资建房了(打不过地产商)。<br>
<br>
　　46.事实证明，中华武术确实是博大精深，拿刀的武士干不过拿棍的和尚，管你什么&ldquo;阴流&rdquo;、&ldquo;剑道&rdquo;，几棍子扫过去全部滚蛋。<br>
<br>
　　47.要说这本书，那可真算得上是万金油，上至外星生物，天外来客，下到世界文明，人类前途，都可以从这本书里推出来，反正随你去读。<br>
<br>
　　48.一说起明代的科举考试制度，总是千人踩、万人踹，什么葬送人才，禁锢思想等等，比黑社会还黑，比十大酷刑还狠，但历史已经证明，在那年头，这是一个最为科学的制度。在科举的考场上，没有绝对的公正，却有相对的公平，无论你是世家子弟，还是贫苦百姓，要想奔出美好前途，只有一个选择&mdash;&mdash;拿起手中的笔，把那张考卷答完。然后封上你的姓名，等待着命运的来临。事实证明，好好学习，天天向上，才是中第的最佳途径，想玩花样，走后门，几乎肯定是死路一条</font></p>
<p><font color="#00ccff"><a href="http://zhaojiaming030.blog.163.com/blog/static/133267803200910245416454/">http://zhaojiaming030.blog.163.com/blog/static/133267803200910245416454/</a></font></p> <a href="http://hi.baidu.com/mikenoodle/blog/item/4067ecc48ab093a38326ac1b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/mikenoodle/blog/category/%C0%CF%D7%E6%D7%DA%B5%C4%B6%AB%CE%F7">老祖宗的东西</a>&nbsp;<a href="http://hi.baidu.com/mikenoodle/blog/item/4067ecc48ab093a38326ac1b.html#comment">查看评论</a>]]></description>
        <pubDate>2009-11-26  21:08</pubDate>
        <category><![CDATA[老祖宗的东西]]></category>
        <author><![CDATA[mikenoodle]]></author>
		<guid>http://hi.baidu.com/mikenoodle/blog/item/4067ecc48ab093a38326ac1b.html</guid>
</item>


</channel>
</rss>