??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品一久久香蕉国产线看 ,狠狠色丁香久久婷婷综合五月 ,久久996热精品xxxxhttp://www.shnenglu.com/road420/zh-cnWed, 07 May 2025 03:27:31 GMTWed, 07 May 2025 03:27:31 GMT60转蝲Q不要一辈子靠技术生存)http://www.shnenglu.com/road420/archive/2013/04/14/199436.html深邃?/dc:creator>深邃?/author>Sun, 14 Apr 2013 14:27:00 GMThttp://www.shnenglu.com/road420/archive/2013/04/14/199436.htmlhttp://www.shnenglu.com/road420/comments/199436.htmlhttp://www.shnenglu.com/road420/archive/2013/04/14/199436.html#Feedback1http://www.shnenglu.com/road420/comments/commentRss/199436.htmlhttp://www.shnenglu.com/road420/services/trackbacks/199436.html
    一?在中国你千万不要因ؓ学习技术就可以换来E_的生zd高的薪水待遇Q你千万更不要认为哪些从?市场开发,跑腿的hQ没有前途?br />
    不知道你是不是知道,׃中国有相当大的一部分软g公司Q他们的软g开发团队都的可怜,甚至只有1-3个hQ连一个项目小l都不上,而这L团队却要承担一个Y件公司所有的软g开发Q务,在Y件上U和开发的关键阶段需要团队的成员没日没夜的加班,q需要ؓ试出的BUG和不能按时提交的软g模块功能而心怀忐忑Q有的时候如果你不幸加入现场开发的团队你则需要背井离乡告别你的女友,q行闭开发,你^旉了编码之外就是吃饭和睡觉Q有q公司甚至请个保姆Z做饭Q以让你节省出更多的旉来投入到工作中,让你一直在那种累了׃息,不篏q卛_作的状态)

    更可怕的是,会让你接触的人际关系非常单一Q除了有限的技术h员之外你几乎见不到做其他行业工作和职位的人,你的朋友圈子且单一Q甚至破坏你原有的爱情(惌一下,你在外地做现场开?个月以上Q却从没跟女友见q一面的话,你的奛_是不是会对你呲牙裂嘴Q?br />
 

    也许你拿C所谓的白领的工资,但你却从此失Mn受生zȝ自由Q如果你惛_技术h员尤其是开发h员,我想你很快就会理解,你多么想在一个地斚w期待一D|_认识一些朋友,多一些生zL间的愿望?br />
    比之于我们的生活和h际关pd工作Q那些从事售前和市场开发的朋友Q却有比我们多的多的工作之外的时_甚至他们工作的时间有的时候是和生zȝ旉是可以兼儡Q他们可以通过市场开发,认识各个行业的h士,可以认识各种各样的朋友,他们比我们坦率说更有发胦和发展的ZQ只要他们跟我们一样勤奋。(有一U勤奋的普通hQ如果给他换个地方,他马上会成ؓ一个勤奋且Z的h。)

    二。在学习技术的时候千万不要认为如果做到技术最强,可以成?00%受尊重的人?br />
    有一ơ一个h在面试项目经理的时候说了这么一D话Q我只用最听话的hQ按照我的要求做只要是听话就要,如果不听话不他技术再好也不要。随后这个h得到了试用机会,如果没意外的话,他一定会是下一个项目经理的lQ者?br />
    朋友们你知道吗?不管你技术有多强Q你也不可能自由的腾出时间象别h那样研究一下LINUX源码Q甚臛_一个LINUXLC来表C的才能。你需要做的就是按照要求写代码Q写代码的含义就是都规定好,你按照规定写Q你很快׃发现你昨天写的代码,跟今天写的代码有很多cMQ等你写q一D|间的代码Q你领略:复制Q拷贝,_脓那样的技术对你来说是何等重要。(如果你没有做q?q以上的真正意义上的开发不要反xQ?br />
    如果你幸q的能够听到市场人员的谈话,或是领导们的谈话Q你会隐U觉得他们都在把技术h员当作编码的机器来看Q你的h值ƈ没有你想象的那么重要。而在你所在的团队内部Q你可能正在Z个技术问题的讨论再跟同事搞内耗,因ؓ他不服你Q你也不服他Q你们都认ؓ自己的对Q其实你们两个都对,而争论的目的是Z在关键场合证明一下自己比Ҏ技术好Q比Ҏ强。(在一个项目开发中Q没有h愿意长期听别人的QL换个位置领导别h。)

    三。你更不要认为,如果我技术够好,我就自己创业Q自己有创业的资本,因ؓ自己是搞技术的?br />
    如果你那栯为,真的是大错特错了Q你可以做个调查在非技术h中Q没有几个h知道C#与JAVA的,更谈不上来欣赏你的技术是好还是不好。一句话Q技术仅仅是一个工P善于q用q个工具为别人干zȝ人,却往往不太擅长用这个工h己创业,因ؓq是两个概念Q训l的技能也是完全不同的?br />
    创业最开始的时候,你的人际关系Q你处理人际关系的能力,你对C会潜规则的认识Q还有你明白不明白别人的心,你会不会说让人喜Ƣ的话,q有你对自己所提供的服务的{划和推销{等Q也许有一万,一百万个值得我们重视的问题,但你会发现技术却很少有可能包含在q一万或一百万之内Q如果你创业C一个快成功的阶D,你会q样告诉自己Q我q吗要亲自做技术,我聘一个h不就行了Q这时候你才真正会理解技术的作用Q和你以前做技术h员的作用?br />
    [结]

    Z上面的讨论,我奉劝那些学习技术的朋友Q千万不要拿UD考试L心态去学习技?Ҏ术的学习几近的痴qPx握所有所有的技术,以让自己成ؓ技术领域的权威和专Ӟ以在必要的时候或是心里不畅快的时候到|上对着菜鸟说自己是前辈?br />
    技术仅仅是一个工P是你在h生一个阶D는存的工具Q你可以一辈子喜欢他,但最好不要一辈子靠它生存?br />
    掌握技术的唯一目的是拿它扑ַ作(如果你不x技术当作你W二生命的话Q,是q活。所以你在学习的时候千万不要去做那些所谓的技术习题或是研I些帽泡算法,最大数法了,什么叫q活Q?br />
    是做一个东西让别h用,别h用了Q可以提高他们的工作效率Q想象吧Q你?万道技术习题有什么用Q只会让得酸腐,q是在学习的时候,多培M自己务实的态度吧,比如研究一下当地市场目前有哪些软g公司用hQ自q他们的要求到底有多远Q自己具体应该怎么做才可以辑ֈ他们的要求。等你分析完q些Q你׃发现Q找工作成功Q技术的贡献率其实ƈ没有你原来想象的那么高?br />
    不管你是学习技术ؓ了找工作q是创业Q你都要Ҏ术本w有个清醒的认识Q在中国不会出现BILL GATESQ因为,中国目前q不是十分的重技术h才,q仅仅的停留在把软g技术h才当作h才机器来用的尬境地。(如果你不理解Q一U可能是你目前仅仅从事过技术工作,你的朋友圈子里技术类的朋友占了大多数Q一U可能是你还没有工作Q但喜欢L。盖茨的传记Q?

]]>
五种E序设计Ҏhttp://www.shnenglu.com/road420/archive/2010/09/16/126764.html深邃?/dc:creator>深邃?/author>Thu, 16 Sep 2010 06:45:00 GMThttp://www.shnenglu.com/road420/archive/2010/09/16/126764.htmlhttp://www.shnenglu.com/road420/comments/126764.htmlhttp://www.shnenglu.com/road420/archive/2010/09/16/126764.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/126764.htmlhttp://www.shnenglu.com/road420/services/trackbacks/126764.html

五种E序设计Ҏ

1.        l构化程序设?/span>

Z提高E序的可L、可重用性等Q逐渐出现了将E序开发中l常用到的相同的功能Q比如数学函数运、字W串操作{,独立出来~写成函敎ͼ然后按照怺关系或应用领域汇集在相同的文仉Q这些文件构成了函数?/span>?/span>

函数库是一U对信息的封装,常用的函数装hQh们不必知道如何实现它们。只需要了解如何调用它们即可。函数库可以被多个应用程序共享,在具体编E环境中Q一般都有一个头文g怼Q在q个头文件中以标准的方式定义了库中每个函数的接口Q根据这些接口Ş式可以在E序中的M地方调用所需的函数?/span>

׃函数、库、模块等一pd概念和技术的出现Q程序设计逐渐变成如图所C的风格。程序被分解成一个个函数模块Q其中既有系l函敎ͼ也有用户定义的函数。通过对函数的调用Q程序的q行逐步被展开?/span>阅读E序Ӟ׃每一块的功能相对独立Q因此对E序l构的理解相对容易,在一定程度上~解了程序代码可L和可重用g的矛盾,但ƈ未彻底解决矛盾。随着计算机程序的规模来大Q这个问题变得更加尖锐,于是出现了另一U编E风?/span>—?/font>l构化程序设?/span>?/span>

在结构化E序设计中,ME序D늚~写都基?/span>3U结构:分支l构、@环结构和序l构?/span>E序h明显的模块化特征Q每个程序模块具有惟一的出口和入口语句。结构化E序的结构简单清晎ͼ模块化强Q描q方式脓qh们习惯的推理式思维方式。因此可L强Q在软g重用性、Y件维护等斚w都有所q步Q在大型软g开发尤其是大型U学与工E运Y件的开发中发挥了重要作用。因此到目前为止Q仍有许多应用程序的开发采用结构化E序设计技术和Ҏ。即使在目前行的面向对象Y件开发中也不能完全脱ȝ构化E序设计?/span>

 

2.        面向对象E序设计

面向对象的程序役计方法是E序设计的一U新Ҏ。所有面向对象的E序设计语言一般都含有三个斚w的语法机Ӟ卛_象和cR多态性、承性?/span>

1Q对象和c?/span>

对象的概c原理和Ҏ是面向对象的理序设计语言晕重要的特征。对象是用户定义的类型(UCؓc)的变量。一个对象是既包含数据又包合操作该数据的代码Q函敎ͼ的逻辑实体。对象中的这些数据和函数UCؓ对象的成员,x员数据和成员函数。对象中的成员分为公有的和私有的?span>公有成员是对象与外界的接口界面。外界只能通过调用讉K一个对象的公有成员来实现该对象的功能。私有成员体C个对象的l织形式和功能的实现l节。外界无法对U有成员q行操作?/span>cd象按照规范进行操作,描q客观事物的数据表达及对数据的操作处理封装在一P成功地实C面向对象的程序设计。当用户定义了一个类cd后,可以在该类型的名下定义变量Q即对象Q了?span>cLl构体类型的扩充?/span>l构体中引入成员函数q规定了其访问和l承原则后便成了cR?/span>

2Q多态?/span>

面向对象的程序设计语a支持多态?/span>Q把一个接口用于一cL动。即一个接口多U算?/span>。具体实施时该选择哪一个算法是q定的语法机制定的?/span>C++~译时和q行旉支持多态性?span>~译时的多态性体现在重蝲函数和重载运符{方面。运行时的多态性体现在l承关系及虚函数{方面?/span>

3Q承?/span>

C++E序中,׃个类Q称为基c)可以z出新c(UCؓzc)。这U派生的语法机制使得新类的出现轻松自Ӟ使得一个复杂事物可以被理成章地归lؓ由逐层z的对象描q?/span>z使得E序中定义的cd层次l构。处于子层的对参既具有其父层对象的共性.又具有自w的Ҏ。承性是一个类对象获得其基cd象特性的q程?/span>C++中严格地规定了派生类对其基类的承原则和讉K权限Q得程序中Ҏ据和函数的访_需在家族和朋友间严格区分?/span>

 

3.        事g驱动的程序设?/span>

事g驱动的程序设计实际上是面向对象程序设计的一个应用,但它目前仅适用?/span>windowspd操作pȝ?/span>windows环境中的应用E序?/span>MSQ?/span>DOS环境中的应用E序q行机制不同、设计程序的方式也不一栗?/span>windowsE序采用事g驱动机制q行Q这U事仉动程序由事g的发生与否来控制Q系l中每个对象状态副改变都是事g发生的原由或l果Q设计程序时需以一U非序方式处理事gQ与序的?span>q程驱动的传l程序设计方法E?/span>?/span>

事g也称消息Q含义比较广泛,常见的事件有鼠标事g(如民标移动、单凅R掠q窗口边?/span>)、键盘事?/span>(如按键的压下与拾?/span>){多U。应用程序运行经q一pd必要的初始化后,进入等待状态,{待有事件发生,一旦事件出玎ͼE序pȀzdƈq行相应处理?/span>

事g驱动E序设计是围l着消息的生与处理q行的.消息可来自程序中的某个对象,也可q戗?/span>wlndow s或运行着的其他应用程序生。每当事件发生时Q?/span>Windows俘获有关事gQ然后将消息分别转发到相兛_用程序中的有兛_象,需要对消息作出反应的对象应该提供消息处理函敎ͼ通过q个消息处理函数实现对象的一U功能或行ؓ。所以编写事仉动程序的大部分工作是为各个对?/span>(c?/span>)d各种消息的处理函?/span>。由于一个对象可以是消息的接收者,同时也可能是消息的发送者,所发送的消息与接收到的消息也可以是相同的消息Q而有些消息的发出旉是无法预知的(比如关于键盘的消?/span>)Q因此应用程序的执行序是无法预知的?/span>

4.        逻辑式对象程序设?/span>

逻辑式程序设计的概念来自逻辑式程序设计语aPrologq一曄在计机领域引v震动的日?/span>W五?/span>计算机的基本pȝ语言Q在q种W五?/span>计算ZQ?/span>Prolog的地位相当于当前计算Z的机器语a?/span>

Prolog主要应用在h工智能领?/span>Q在自然语言处理、数据库查询、算法描q等斚w都有应用Q?span>其适于作ؓ专家pȝ的开发工兗?/span>

Prolog是一U陈q式语言Q它不是一U严格的通用E序设计语言Q?/span>Prolog~写E序不需要描q具体的解题q程、只需l出一些必要的事实和规则,q些规则是解决问题方法的规范说明Q根据这些规则和事实Q计机利用渭词逻辑Q通过演绎推理得到求解问题的执行序列?/span>

5.        q行E序设计

一个有实际应用的ƈ行算法,最l总要在ƈ行机上实玎ͼ为此首先pƈ行算法{化ؓq行E序Q此q程是所谓的q行E序设计(Parallel Program)。它要求法设计者、系l结构师和Y件工作者广泛频J的交互。因计ƈ行程序涉及到的知识面较广Q主要包括操作系l中的有关知识和优化~译斚w的知识。操作系l内定w怸富,q行E序中最基本的计要素如d、进E、线E等基本概念、同步机制和通信操作{?/span>

目前q行E序设计的状冉|Q?/span>?/span>q行软g的发展落后于q行gQ?/span>?/span>和串行系l与应用软g相比Q现今的q行pȝ与应用Y件甚且不成熟;?/span>q行软g的缺乏是发展q行计算的主要障;?/span>不幸的是Q这U状态似乎仍在l着。究其原因是q行E序设计q比串行E序设计复杂Q?/span>?/span>q行E序设计不但包含了串行程序设计,面且q包含了更多的富有挑战性的问题Q?/span>?/span>串行E序设计仅有一个普遍被接受的冯·ZD模型,而ƈ行计模型虽有好多,但没有一个可被共同认可的像冯·Z曼那L优秀模型Q?/span>?/span>q行E序设计对环境工?/span>(如编译、查错等)的要求远比串行程序设计先q得多;?/span>串行E序设计比较适合于自然习惯,且h们在q去U篏了大量的~程知识、经验和宝贵的Y件胦富?/span>



]]>
E序的内存分?/title><link>http://www.shnenglu.com/road420/archive/2010/09/16/126758.html</link><dc:creator>深邃?/dc:creator><author>深邃?/author><pubDate>Thu, 16 Sep 2010 06:22:00 GMT</pubDate><guid>http://www.shnenglu.com/road420/archive/2010/09/16/126758.html</guid><wfw:comment>http://www.shnenglu.com/road420/comments/126758.html</wfw:comment><comments>http://www.shnenglu.com/road420/archive/2010/09/16/126758.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/road420/comments/commentRss/126758.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/road420/services/trackbacks/126758.html</trackback:ping><description><![CDATA[一个由c/C++~译的程序占用的内存分ؓ以下几个部分<br><font color=#0000ff>1、栈区(stackQ?/font> q译器自动分配释放 Q存攑և数的参数|局部变量的值等。其操作方式cM于数据结构中的栈?br><font color=#0000ff>2、堆区(heapQ?/font> ?一般由E序员分配释放, 若程序员不释放,E序l束时可能由OS回收 。注意它与数据结构中的堆是两回事Q分配方式倒是cM于链表,呵呵?br><font color=#0000ff>3、全局区(静态区Q(staticQ—,</font>全局变量和静态变量的存储是放在一块的Q初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在盔R的另一块区域?- E序l束后有pȝ释放<br><font color=#0000ff>4、文字常量区</font> —常量字W串是攑֜q里的?E序l束后由pȝ释放<br><font color=#0000ff>5、程序代码区</font>—存攑ևC的二q制代码? <p><font size=4><strong>堆和栈的理论知识</strong></font><br><font color=#0000ff>2.1甌方式</font><br><font color=#ff00ff>stack:<br><u>ql自动分配?/u></font> 例如Q声明在函数中一个局部变?int b; pȝ自动在栈中ؓb开辟空?br><font color=#ff0000>heap:<br>需要程序员自己甌</font><font color=#ff0000>Qƈ指明大小Q在c中malloc函数</font><br>如p1 = (char *)malloc(10);<br>在C++中用newq算W?br>如p2 = (char *)malloc(10);<br>但是注意p1、p2本n是在栈中的?br>2.2<font color=#0000ff>甌后系l的响应</font><br>栈:<font color=#ff00ff>只要栈的剩余I间大于所甌I间</font>Q系l将为程序提供内存,否则报异常提示栈溢出?br>堆:<font color=#ff0000>首先应该知道操作pȝ有一个记录空闲内存地址的链?/font>Q当pȝ收到E序的申hQ?br>?遍历该链表,LW一个空间大于所甌I间的堆l点Q然后将该结点从I闲l点链表中删除,q将该结点的I间分配l程序,另外Q对于大多数pȝQ会在这块内 存空间中的首地址处记录本ơ分配的大小Q这P代码中的delete语句才能正确的释放本内存I间。另外,׃扑ֈ的堆l点的大不一定正好等于申L?,pȝ会自动的多余的那部分重新放入空闲链表中?br><font color=#0000ff>2.3甌大小的限?/font><br><font color=#ff00ff>栈:在Windows?栈是向低地址扩展的数据结构,是一?q箋的内存的区域</font>。这句话的意思是栈顶的地址和栈的最大容量是pȝ预先规定好的Q在 WINDOWS下,栈的大小?MQ也有的说是1MQM是一个编译时q定的常数Q,如果甌的空间超q栈的剩余空间时Q将提示overflow。因 此,能从栈获得的I间较小?br><font color=#ff0000>堆:堆是向高地址扩展的数据结构,是不q箋的内存区域?/font>q是׃pȝ是用链表来存储的I闲内存地址的,自然是不q箋的,而链表的遍历方向是由低地址向高地址。堆的大受限于计算机系l中有效的虚拟内存。由此可见,堆获得的I间比较灉|Q也比较大?br><font color=#0000ff>2.4甌效率的比较:<br></font><font color=#ff00ff>栈由pȝ自动分配Q速度较快。但E序员是无法控制的?/font><br><font color=#ff0000>堆是由new分配的内存,一般速度比较慢,而且Ҏ产生内存片,不过用v来最方便.</font><br>另外Q在WINDOWS下,最好的方式是用VirtualAlloc分配内存Q他不是在堆Q也不是在栈是直接在q程的地址I间中保留一快内存,虽然用v来最不方ѝ但是速度Q?也最灉|<br><font color=#0000ff>2.5堆和栈中的存储内?/font><br>栈: 在函数调用时Q第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句Q的地址Q然后是函数的各个参敎ͼ在大多数的C~译器中Q参数是由右往左入栈的Q然后是函数中的局部变量。注意静态变量是不入栈的?br>当本ơ函数调用结束后Q局部变量先出栈Q然后是参数Q最后栈指针指向最开始存的地址Q也是dC的下一条指令,E序p点l运行?br>堆:一般是在堆的头部用一个字节存攑֠的大。堆中的具体内容有程序员安排?br><font color=#0000ff>2.6存取效率的比?/font><br><br>char s1[] = "aaaaaaaaaaaaaaa";<br>char *s2 = "bbbbbbbbbbbbbbbbb";<br>aaaaaaaaaaa是在q行时刻赋值的Q?br>而bbbbbbbbbbb是在~译时就定的;<br>但是Q在以后的存取中Q在栈上的数l比指针所指向的字W串(例如?快?br>比如Q?br>#include<br>void main()<br>{<br>char a = 1;<br>char c[] = "1234567890";<br>char *p ="1234567890";<br>a = c[1];<br>a = p[1];<br>return;<br>}<br>对应的汇~代?br>10: a = c[1];<br>00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]<br>0040106A 88 4D FC mov byte ptr [ebp-4],cl<br>11: a = p[1];<br>0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]<br>00401070 8A 42 01 mov al,byte ptr [edx+1]<br>00401073 88 45 FC mov byte ptr [ebp-4],al<br>W一U在d时直接就把字W串中的元素d寄存器cl中,而第二种则要先把指edx中,在根据edxd字符Q显然慢了?br><br><strong>2.7结Q?/strong><br>堆和栈的区别可以用如下的比喻来看出:<br><font color=#ff00ff>使用栈就象我们去饭馆里吃?/font>Q只点菜(发出甌Q、付钱、和吃(使用Q,吃饱了就赎ͼ不必理会切菜、洗菜等准备工作和洗、刷锅等扫尾工作Q?font color=#ff00ff>他的好处是快P但是自由度小?/font><br><font color=#ff0000>使用堆就象是自己动手做喜Ƣ吃的菜?/font>Q比较麻烦,但是?font color=#ff0000>较符合自q口味Q而且自由度大</font>?/p> <img src ="http://www.shnenglu.com/road420/aggbug/126758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/road420/" target="_blank">深邃?/a> 2010-09-16 14:22 <a href="http://www.shnenglu.com/road420/archive/2010/09/16/126758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码注入的三U方?/title><link>http://www.shnenglu.com/road420/archive/2010/09/16/126755.html</link><dc:creator>深邃?/dc:creator><author>深邃?/author><pubDate>Thu, 16 Sep 2010 06:06:00 GMT</pubDate><guid>http://www.shnenglu.com/road420/archive/2010/09/16/126755.html</guid><wfw:comment>http://www.shnenglu.com/road420/comments/126755.html</wfw:comment><comments>http://www.shnenglu.com/road420/archive/2010/09/16/126755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/road420/comments/commentRss/126755.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/road420/services/trackbacks/126755.html</trackback:ping><description><![CDATA[<ul> <li><a ><u><font color=#800080>Windows 钩子</font></u></a> <li><a href="http://www.vckbase.com/document/viewdoc/?id=1886#CreateRemoteThread_和_LoadLibrary_技?><u><font color=#800080>CreateRemoteThread ?LoadLibrary 技?/font></u></a> <br><a ><u><font color=#800080>——进E间通信</font></u></a> <li><a href="http://www.vckbase.com/document/viewdoc/?id=1886#CreateRemoteThread_和_WriteProcessMemory_技?><u><font color=#800080>CreateRemoteThread ?WriteProcessMemory 技?/font></u></a> <br>—?a ><u><font color=#800080>如何用该技术子cdq程控g</font></u></a> <br>—?a href="http://www.vckbase.com/document/viewdoc/?id=1886#何时使用_CreateRemoteThread_和_WriteProcessMemory_技?><u><font color=#800080>何时使用 CreateRemoteThread ?WriteProcessMemory 技?/font></u></a> <li><a href="http://www.vckbase.com/document/viewdoc/?id=1886#l束?><u><font color=#800080>l束?/font></u></a> <li><a ><u><font color=#800080>附录A</font></u></a> <li><a ><u><font color=#800080>附录B</font></u></a> <li><a ><u><font color=#800080>附录C</font></u></a> <li><a ><u><font color=#800080>附录D</font></u></a> <li><a ><u><font color=#800080>附录E</font></u></a> <li><a ><u><font color=#800080>附录F</font></u></a> <li><a href="http://www.vckbase.com/document/viewdoc/?id=1886#参考资?><u><font color=#800080>参考资?/font></u></a> <br></li> </ul> <p>?/p> <p>  本文讨论如何把代码注入不同的进E地址I间Q然后在该进E的上下文中执行注入的代码?我们在网上可以查C些窗?密码侦测的应用例子,|上的这些程序大多都依赖 Windows 钩子技术来实现。本文将讨论除了使用 Windows 钩子技术以外的其它技术来实现q个功能。如图一所C:</p> <p><img height=320 src="http://www.vckbase.com/document/journal/vckbase55/images/winspy1.gif" width=622 border=0></p> <p>图一 WinSpy 密码侦测E序</p> <p>Z扑ֈ解决问题的方法。首先让我们单回一下问题背景?br>  ?#8220;d”某个控g的内容——无个控件是否属于当前的应用E序——通常都是发?WM_GETTEXT 消息来实现。这个技术也同样应用到编辑控Ӟ但是如果该编辑控件属于另外一个进Eƈ讄?ES_PASSWORD 式样Q那么上面讲的方法就行不通了。用 WM_GETTEXT 来获取控件的内容只适用于进E?#8220;拥有”密码控g的情c所以我们的问题变成了如何在另外一个进E的地址I间执行Q?/p> <pre>::SendMessage( hPwdEdit, WM_GETTEXT, nMaxChars, psBuffer );</pre> <p>通常有三U可能性来解决q个问题?/p> <ol> <li>你的代码放入某?DLLQ然后通过 Windows 钩子映射该DLL到远E进E; <li>你的代码放入某?DLLQ然后通过 CreateRemoteThread ?LoadLibrary 技术映该DLL到远E进E; <li>如果不写单独?DLLQ可以直接将你的代码拯到远E进E——通过 WriteProcessMemory——ƈ?CreateRemoteThread 启动它的执行。本文将在第三部分详l描q该技术实现细节; </li> </ol> <p>W一部分Q?<a name=Windows_钩子>Windows 钩子</a></p> <p>范例E序——参见HookSpy 和HookInjEx</p> <p>  Windows 钩子主要作用是监控某些线E的消息。通常我们钩子分为本地钩子和q程钩子以及pȝU钩子,本地钩子一般监控属于本q程的线E的消息,q程钩子是线E专用的Q用于监控属于另外进E的U程消息。系l钩子监控q行在当前系l中的所有线E的消息?br>  如果钩子作用的线E属于另外的q程Q那么你的钩子过E必驻留在某个动态链接库QDLLQ中。然后系l映包含钩子过E的DLL到钩子作用的U程的地址I间。Windows映整?DLLQ而不仅仅是钩子过E。这是Z?Windows 钩子能被用于代码注入到别的q程地址I间的原因?br>  本文我不打算涉及钩子的具体细节(关于钩子的细节请参见 MSDN 库中?SetWindowHookEx APIQ,但我在此要给Z个很有用心得Q在相关文档中你是找不到q些内容的:</p> <ol> <li>在成功调?SetWindowsHookEx 后,pȝ自动映射 DLL 到钩子作用的U程地址I间Q但不必立即发生映射Q因?Windows 钩子都是消息QDLL 在消息事件发生前q没有生实际的映射。例如:<br>  如果你安装一个钩子监控某些线E(WH_CALLWNDPROCQ的非队列消息,在消息被实际发送到Q某些窗口的Q钩子作用的U程之前Q该DLL 是不会被映射到远E进E的。换句话_如果 UnhookWindowsHookEx 在某个消息被发送到钩子作用的线E之前被调用QDLL Ҏ不会被映到q程q程Q即?SetWindowsHookEx 本n调用成功Q。ؓ了强制进行映,在调?SetWindowsHookEx 之后马上发送一个事件到相关的线E?br>  在UnhookWindowsHookEx了之后,对于没有映射的DLL处理Ҏ也一栗只有在_的事件发生后QDLL才会有真正的映射? <li>当你安装钩子后,它们可能影响整个pȝ得性能Q尤其是pȝU钩子)Q但是你可以很容易解册个问题,如果你用线E专用钩子的DLL映射机制Qƈ不截h息。考虑使用如下代码Q?br> <pre>BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if( ul_reason_for_call == DLL_PROCESS_ATTACH ) { // Increase reference count via LoadLibrary char lib_name[MAX_PATH]; ::GetModuleFileName( hModule, lib_name, MAX_PATH ); ::LoadLibrary( lib_name ); // Safely remove hook ::UnhookWindowsHookEx( g_hHook ); } return TRUE; } </pre>   那么会发生什么呢Q首先我们通过Windows 钩子DLL映射到远E进E。然后,在DLL被实际映之后,我们解开钩子。通常当第一个消息到N子作用线E时QDLL此时也不会被映射。这里的处理技巧是调用LoadLibrary通过增加 DLLs的引用计数来防止映射不成功?br>  现在剩下的问题是如何卸蝲DLLQUnhookWindowsHookEx 是不会做q个事情的,因ؓ钩子已经不作用于U程了。你可以像下面这样做Q?br><br> <ul> <li>在你想要解除DLL映射前,安装另一个钩子; <li>发送一?#8220;Ҏ”消息到远E线E; <li>在钩子过E中截获q个消息Q响应该消息时调?FreeLibrary ?UnhookWindowsHookExQ?/li> </ul> <p> </p> <p>  目前只用了钩子来从处理q程q程中DLL的映和解除映射。在?#8220;作用于线E的”钩子Ҏ能没有影响?br>下面我们讨论另外一U方法,q个Ҏ?LoadLibrary 技术的不同之处是DLL的映机制不会干预目标进E。相对LoadLibrary 技术,q部分描q的Ҏ适用?WinNT和Win9x?br>  但是Q什么时候用这个技巧呢Q答案是当DLL必须在远E进E中ȝ较长旉Q即如果你子cd某个属于另外一个进E的控gӞ以及你想可能少的干涉目标进E时。我?HookSpy 中没有用它Q因为注入DLL 的时间ƈ不长——注入时间只要够得到密码即可。我提供了另外一个例子程序——HookInjEx——来C。HookInjEx DLL映射到资源管理器“explorer.exe”Qƈ从中/解除影射Q它子类?#8220;开?#8221;按钮Qƈ交换鼠标左右键单?#8220;开?#8221;按钮的功能?</p> </li> </ol> <p>HookSpy ?HookInjEx 的源代码都可以从本文?a ><u><font color=#0000ff>下蝲源代?/font></u></a>中获得? <p>W二部分Q?a name=CreateRemoteThread_和_LoadLibrary_技?CreateRemoteThread ?LoadLibrary 技?/a> <p>范例E序——LibSpy <p>通常QQ何进E都可以通过 LoadLibrary API 动态加载DLL。但是,如何强制一个外部进E调用这个函数呢Q答案是QCreateRemoteThread?br>首先Q让我们看一?LoadLibrary 和FreeLibrary API 的声明:</p> <pre>HINSTANCE LoadLibrary( LPCTSTR lpLibFileName // 库模块文件名的地址 ); BOOL FreeLibrary( HMODULE hLibModule // 要加载的库模块的句柄 );</pre> <p>现在它们与传递到 CreateRemoteThread 的线E例E——ThreadProc 的声明进行比较?/p> <pre>DWORD WINAPI ThreadProc( LPVOID lpParameter // U程数据 );</pre> <p>你可以看刎ͼ所有函数都使用相同的调用规范ƈ都接?32位参敎ͼq回值的大小都相同。也是_我们可以传递一个指针到LoadLibrary/FreeLibrary 作ؓ?CreateRemoteThread 的线E例E。但q里有两个问题,L下面对CreateRemoteThread 的描qͼ</p> <ol> <li>CreateRemoteThread ?lpStartAddress 参数必须表示q程q程中线E例E的开始地址? <li>如果传递到 ThreadFunc 的参数lpParameter——被解释为常规的 32位|FreeLibrary它解释Z?HMODULEQ,一切OK。但是,如果 lpParameter 被解释ؓ一个指针(LoadLibraryA它解释Z个串指针Q。它必须指向q程q程的某些数据?</li> </ol> <p>  W一个问题实际上是由它自p决的。LoadLibrary ?FreeLibray 两个函数都在 kernel32.dll 中。因为必M证kernel32存在q且在每?#8220;常规”q程中的加蝲地址要相同,LoadLibrary/FreeLibray 的地址在每个进E中的地址要相同,q就保证了有效的指针被传递到q程q程?br>  W二个问题也很容易解冟뀂只要通过 WriteProcessMemory ?DLL 模块名(LoadLibrary需要的DLL模块名)拯到远E进E即可?/p> <p>所以,Z使用CreateRemoteThread ?LoadLibrary 技术,需要按照下列步骤来做:</p> <ol> <li>获取q程q程QOpenProcessQ的 HANDLEQ? <li>E进E中?DLL名分配内存(VirtualAllocExQ; <li>?DLL 名,包含全\径名Q写入分配的内存QWriteProcessMemoryQ; <li>?CreateRemoteThread ?LoadLibrary. 你的DLL映射到远E进E; <li>{待直到U程l止QWaitForSingleObjectQ,也就是说直到 LoadLibrary 调用q回。另一U方法是Q一?DllMainQ用DLL_PROCESS_ATTACH调用Q返回,U程׃l止Q? <li>获取q程U程的退Z码(GetExitCodeThreadQ。注意这是一?LoadLibrary q回的|因此是所映射 DLL 的基地址QHMODULEQ?br>在第二步中释攑ֈ配的地址QVirtualFreeExQ; <li>?CreateRemoteThread ?FreeLibrary从远E进E中卸蝲 DLL。传递在W六步获取的 HMODULE 句柄?FreeLibraryQ通过 CreateRemoteThread 的lpParameter参数Q; <li>注意Q如果你注入?DLL 产生M新的U程Q一定要在卸载DLL 之前它们都l止掉; <li>{待直到U程l止QWaitForSingleObjectQ; </li> </ol> <p>  此外Q处理完成后不要忘了关闭所有句柄,包括在第四步和第八步创徏的两个线E以及在W一步获取的q程U程句柄。现在让我们看一?LibSpy 的部分代码,Z单v见,上述步骤的实现细节中的错误处理以?UNICODE 支持部分被略掉?</p> <pre>HANDLE hThread; char szLibPath[_MAX_PATH]; // “LibSpy.dll”模块的名U?(包括全\?; void* pLibRemote; // q程q程中的地址QszLibPath 被拯到此? DWORD hLibModule; // 要加载的模块的基地址QHMODULEQ? HMODULE hKernel32 = ::GetModuleHandle("Kernel32"); // 初始化szLibPath //... // 1. 在远E进E中为szLibPath 分配内存 // 2. szLibPath 写入分配的内? pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE ); ::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath, sizeof(szLibPath), NULL ); // ?LibSpy.dll" 加蝲到远E进E(使用CreateRemoteThread ?LoadLibraryQ? hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32, "LoadLibraryA" ), pLibRemote, 0, NULL ); ::WaitForSingleObject( hThread, INFINITE ); // 获取所加蝲的模块的句柄 ::GetExitCodeThread( hThread, &hLibModule ); // 清除 ::CloseHandle( hThread ); ::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE ); </pre>   假设我们实际惌注入的代码——SendMessage ——被攑֜DllMain (DLL_PROCESS_ATTACH)中,现在它已l被执行。那么现在应该从目标q程中将DLL 卸蝲Q? <pre>// 从目标进E中卸蝲"LibSpy.dll" (使用 CreateRemoteThread ?FreeLibrary) hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32, "FreeLibrary" ), (void*)hLibModule, 0, NULL ); ::WaitForSingleObject( hThread, INFINITE ); // 清除 ::CloseHandle( hThread ); </pre> <em><a name=q程间通信>q程间通信</a></em><br><br>  到目前ؓ止,我们只讨Z关于如何DLL 注入到远E进E的内容Q但是,在大多数情况下,注入?DLL 都需要与原应用程序进行某U方式的通信Q回想一下,我们的DLL是被映射到某个远E进E的地址I间里了Q不是在本地应用E序的地址I间中)。比如秘密侦程序,DLL必须要知道实际包含密码的控g句柄Q显Ӟ~译时无法将q个D行硬~码。同P一旦DLL获得了秘密,它必d它发送回原应用程序,以便能正显C出来?br>  q运的是Q有许多Ҏ处理q个问题Q文件映,WM_COPYDATAQ剪贴板以及很简单的 #pragma data_seg ׃n数据D늭Q本文我不打用这些技术,因ؓMSDNQ?#8220;q程间通信”部分Q以及其它渠道可以找到很多文档参考。不q我?LibSpy例子中还是用了 #pragma data_seg。细节请参?LibSpy 源代码? <p>W三部分Q?a name=CreateRemoteThread_和_WriteProcessMemory_技?CreateRemoteThread ?WriteProcessMemory 技?/a></p> <p>范例E序——WinSpy </p> <p>  另外一个将代码拯到另一个进E地址I间q在该进E上下文中执行的Ҏ是用远E线E和 WriteProcessMemory API。这U方法不用编写单独的DLLQ而是?WriteProcessMemory 直接代码拷贝到q程q程——然后用 CreateRemoteThread 启动它执行。先来看?CreateRemoteThread 的声明: </p> <pre>HANDLE CreateRemoteThread( HANDLE hProcess, // 传入创徏新线E的q程句柄 LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性指? DWORD dwStackSize, // 字节为单位的初始U程堆栈 LPTHREAD_START_ROUTINE lpStartAddress, // 指向U程函数的指? LPVOID lpParameter, // 新线E用的参数 DWORD dwCreationFlags, // 创徏标志 LPDWORD lpThreadId // 指向q回的线EID ); </pre> 如果你比较它?CreateThreadQMSDNQ的声明Q你会注意到如下的差别: <ul> <li>?CreateRemoteThread中,hProcess是额外的一个参敎ͼ一个进E句柄,新线E就是在q个q程中创建的Q? <li>?CreateRemoteThread中,lpStartAddress 表示的是在远E进E地址I间中的U程起始地址。线E函数必要存在于远E进E中Q所以我们不能简单地传递一个指针到本地?ThreadFunc。必d先拷贝代码到q程q程Q? <li>同样QlpParameter 指向的数据也必须要存在于q程q程Q所以也得将它拷贝到那?</li> </ul> <p>lg所qͼ我们得按照如下的步骤来做Q?/p> <ol> <li>获取一个远E进E的HANDLE (OpenProces) Q? <li>在远E进E地址I间中ؓ注入的数据分配内存(VirtualAllocExQ; <li>初始的 INDATA 数据l构的一个拷贝写入分配的内存中(WriteProcessMemoryQ; <li>在远E进E地址I间中ؓ注入的代码分配内存; <li>?ThreadFunc 的一个拷贝写入分配的内存Q? <li>?CreateRemoteThread启动q程?ThreadFunc 拯Q? <li>{待q程U程l止QWaitForSingleObjectQ; <li>获取q程来自q程q程的结果(ReadProcessMemory ?GetExitCodeThreadQ; <li>释放在第二步和第四步中分配的内存QVirtualFreeExQ; <li>关闭在第六步和第一步获取的句柄QCloseHandleQ; </li> </ol> <p>ThreadFunc 必须要遵循的原则Q?/p> <ol> <li>除了kernel32.dll 和user32.dll 中的函数之外QThreadFunc 不要调用M其它函数Q只?kernel32.dll 和user32.dll被保证在本地和目标进E中的加载地址相同Q注意,user32.dllq不是被映射到每?Win32 的进E)。如果你需要来自其它库中的函数Q将LoadLibrary ?GetProcAddress 的地址传给注入的代码,然后放手让它自己d。如果映到目标q程中的DLL有冲H,你也可以?GetModuleHandle 来代?LoadLibrary?br>  同样Q如果你惛_ ThreadFunc 中调用自q子例E,要单独把每个例程的代码拷贝到q程q程q用 INJDATA?ThreadFunc 提供代码的地址? <li>不要使用静态字W串Q而要?INJDATA 来传递所有字W串。之所以要q样Q是因ؓ~译器将静态字W串攑֜可执行程序的“数据D?#8221;中,可是引用Q指针)是保留在代码中的。那么,q程q程中ThreadFunc 的拷贝指向的内容在远E进E的地址I间中是不存在的? <li>L /GZ ~译器开养I它在调试版本中是默认讄的? <li>?ThreadFunc ?AfterThreadFunc 声明为静态类型,或者不启用增量链接? <li>ThreadFunc 中的局部变量一定不能超q一(也就?4KBQ?br>注意在调试版本中4KB的空间有大约10个字节是用于内部变量的? <li>如果你有一个开兌句块大于3个case 语句Q将它们像下面这h分开Q? <pre>switch( expression ) { case constant1: statement1; goto END; case constant2: statement2; goto END; case constant3: statement2; goto END; } switch( expression ) { case constant4: statement4; goto END; case constant5: statement5; goto END; case constant6: statement6; goto END; } END: </pre> 或者将它们修改成一?if-else if l构语句Q参见附录EQ?</li> </ol> <p>  如果你没有按照这些规则来做,目标q程很可能会崩溃。所以务必牢记。在目标q程中不要假设Q何事情都会像在本地进E中那样 Q参见附录FQ? <p>GetWindowTextRemote(A/W) <p>要想?#8220;q程”~辑框获得密码,你需要做的就是将所有功能都装在GetWindowTextRemot(A/W):中?/p> <pre>int GetWindowTextRemoteA( HANDLE hProcess, HWND hWnd, LPSTR lpString ); int GetWindowTextRemoteW( HANDLE hProcess, HWND hWnd, LPWSTR lpString ); 参数说明Q? hProcessQ编辑框控g所属的q程句柄Q? hWndQ包含密码的~辑框控件句柄; lpStringQ接收文本的~冲指针Q? q回|q回值是拯的字W数Q?/pre> <p>  下面让我们看看它的部分代码——尤其是注入数据的代码——以便明?GetWindowTextRemote 的工作原理。此处ؓ单v见,略掉?UNICODE 支持部分?/p> <pre>INJDATA typedef LRESULT (WINAPI *SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM); typedef struct { HWND hwnd; // ~辑框句? SENDMESSAGE fnSendMessage; // 指向user32.dll ?SendMessageA 的指? char psText[128]; // 接收密码的缓? } INJDATA;</pre> <p>  INJDATA 是一个被注入到远E进E的数据l构。但在注入之前,l构中指?SendMessageA 的指针是在本地应用程序中初始化的。因为对于每个用user32.dll的进E来_user32.dllL被映到相同的地址Q因此,SendMessageA 的地址也肯定是相同的。这׃证了被传递到q程q程的是一个有效的指针?br><br>ThreadFunc函数</p> <pre>static DWORD WINAPI ThreadFunc (INJDATA *pData) { pData->fnSendMessage( pData->hwnd, WM_GETTEXT, // Get password sizeof(pData->psText), (LPARAM)pData->psText ); return 0; } // 该函数在ThreadFunc之后标记内存地址 // int cbCodeSize = (PBYTE) AfterThreadFunc - (PBYTE) ThreadFunc. static void AfterThreadFunc (void) { } </pre> <p>ThradFunc 是被q程U程执行的代码?/p> <ul> <li>注释Q注意AfterThreadFunc 是如何计?ThreadFunc 大小的。通常q样做ƈ不是一个好办法Q因为链接器可以随意更改函数的顺序(也就是说ThreadFunc可能被放?AfterThreadFunc之后Q。这一点你可以在小目中很好地保证函数的顺序是预先设想好的Q比?WinSpy E序。在必要的情况下Q你q可以?/ORDER 链接器选项来解军_数链接顺序问题。或者用反汇~确?ThreadFunc 函数的大?</li> </ul> <a name=如何使用该技术子cdq程控g>如何使用该技术子cdq程控g</a> <p>范例E序——InjectEx<br><br>下面我们讨Z些更复杂的内容,如何子类化属于另一个进E的控g?br><br>首先Q你得拷贝两个函数到q程q程来完成此d</p> <ol> <li>ThreadFunc实际上是通过 SetWindowLong子类化远E进E中的控Ӟ <li>NewProc是子cd控g的新H口q程Q?</li> </ol> <p>  q里主要的问题是如何数据传到远E窗口过E?NewProcQ因?NewProc 是一个回调函敎ͼ它必遵循特定的规范和原则,我们不能单地在参C传?INJDATA指针。幸q的是我扑ֈ了有两个Ҏ来解册个问题,只不q要借助汇编语言Q所以不要忽略了汇编Q关键时候它是很有用的!<br><br>Ҏ一Q?/p> <p>如下图所C:</p> <p><img height=267 src="http://www.vckbase.com/document/journal/vckbase55/images/winspy2.gif" width=438 border=0></p> <p>  在远E进E中QINJDATA 被放在NewProc 之前Q这?NewProc 在编译时便知?INJDATA 在远E进E地址I间中的内存位置。更切地说Q它知道相对于其自n位置?INJDATA 的地址Q我们需要所有这些信息。下面是 NewProc 的代码: </p> <pre>static LRESULT CALLBACK NewProc( HWND hwnd, // H口句柄 UINT uMsg, // 消息标示W? WPARAM wParam, // W一个消息参? LPARAM lParam ) // W二个消息参? { INJDATA* pData = (INJDATA*) NewProc; // pData 指向 NewProc pData--; // 现在pData 指向INJDATA; // 回想一下INJDATA 被置于远E进ENewProc之前; //----------------------------- // 此处是子cd代码 // ........ //----------------------------- // 调用原窗口过E? // fnOldProc (由SetWindowLong q回) 被(q程QThreadFunc初始? // q被保存在(q程QINJDATA;? return pData->fnCallWindowProc( pData->fnOldProc, hwnd,uMsg,wParam,lParam ); } </pre> 但这里还有一个问题,见第一行代码: <pre>INJDATA* pData = (INJDATA*) NewProc;</pre> <p>  q种方式 pData得到的是编码|在我们的q程中是?NewProc 的内存地址Q。这不是我们十分惌的。在q程q程中,NewProc “当前”拯的内存地址与它被移到的实际位置是无关的Q换句话_我们会需要某U类型的“this 指针”?br>虽然?C/C++ 无法解决q个问题Q但借助内联汇编可以解决Q下面是?NewProc的修改:<br></p> <pre>static LRESULT CALLBACK NewProc( HWND hwnd, // H口句柄 UINT uMsg, // 消息标示W? WPARAM wParam, // W一个消息参? LPARAM lParam ) // W二个消息参? { // 计算INJDATA l构的位|? // 在远E进E中Cq个INJDATA // 被放在NewProc之前 INJDATA* pData; _asm { call dummy dummy: pop ecx // <- ECX 包含当前的EIP sub ecx, 9 // <- ECX 包含NewProc的地址 mov pData, ecx } pData--; //----------------------------- // 此处是子cd代码 // ........ //----------------------------- // 调用原来的窗口过E? return pData->fnCallWindowProc( pData->fnOldProc, hwnd,uMsg,wParam,lParam ); } </pre>   那么Q接下来该怎么办呢Q事实上Q每个进E都有一个特D的寄存器,它指向下一条要执行的指令的内存位置。即所谓的指o指针Q在32?Intel ?AMD 处理器上被表CZؓ EIP。因?EIP是一个专用寄存器Q你无法象操作一般常规存储器Q如QEAXQEBX{)那样通过~程存取它。也是说没有操作代码来d EIPQ以便直接读取或修改其内宏V但是,EIP 仍然q是可以通过间接Ҏ修改的(q且随时可以修改Q,通过JMPQCALL和RETq些指o实现。下面我们就通过例子来解释通过 CALL/RET 子例E调用机制在32?Intel ?AMD 处理器上是如何工作的?<br>  当你调用Q通过 CALLQ某个子例程Ӟ子例E的地址被加载到 EIPQ但即便是在 EIP杯修改之前,其旧的那个D自动PUSH到堆栈(被用于后面作为指令指针返回)。在子例E执行完ӞRET 指o自动堆栈顶POP?EIP?br>  现在你知道了如何通过 CALL ?RET 实现 EIP 的修改,但如何获取其当前的值呢Q下面就来解册个问题,前面讲过QCALL PUSH EIP 到堆栈,所以,Z获取其当前|调用“哑函?#8221;Q然后再POP堆栈。让我们用编译后?NewProc 来解释这个窍门? <pre>Address OpCode/Params Decoded instruction -------------------------------------------------- :00401000 55 push ebp ; entry point of ; NewProc :00401001 8BEC mov ebp, esp :00401003 51 push ecx :00401004 E800000000 call 00401009 ; *a* call dummy :00401009 59 pop ecx ; *b* :0040100A 83E909 sub ecx, 00000009 ; *c* :0040100D 894DFC mov [ebp-04], ecx ; mov pData, ECX :00401010 8B45FC mov eax, [ebp-04] :00401013 83E814 sub eax, 00000014 ; pData--; ..... ..... :0040102D 8BE5 mov esp, ebp :0040102F 5D pop ebp :00401030 C21000 ret 0010 </pre> <ul> <li>哑函数调用;是JUMPC一个指令ƈPUSH EIP到堆栈; <li>然后堆栈顶POP?ECXQECX再保存EIPQ这也是 POP EIP指o的真正地址Q? <li>注意 NewProc 的入口点?“POP ECX”之间?#8220;距离”? 个字节;因此Z计算 NewProc的地址Q要?ECX ??</li> </ul> <p>  q样一来,不管 NewProc 被移C么地方,它总能计算出其自己的地址。但是,NewProc 的入口点?“POP ECX”之间的距d能会随着你对~译/链接选项的改变而变化,由此造成 RELEASE和DEBUG版本之间也会有差别。但关键是你仍然切地知道编译时的倹{?/p> <ol> <li>首先Q编译函? <li>用反汇编定正确的距? <li>最后,用正的距离值重新编?</li> </ol> <p>此即?InjecEx 中用的解决ҎQ类g HookInjExQ交换鼠标点?#8220;开?#8221;左右键时的功能?br><br>Ҏ二: <p>对于我们的问题,在远E进E地址I间中将 INJDATA 攑֜ NewProc 前面不是唯一的解军_法。看下面 NewProc的变异版本: </p> <pre>static LRESULT CALLBACK NewProc( HWND hwnd, // H口句柄 UINT uMsg, // 消息标示W? WPARAM wParam, // W一个消息参? LPARAM lParam ) // W二个消息参? { INJDATA* pData = 0xA0B0C0D0; // 虚构? //----------------------------- // 子类化代? // ........ //----------------------------- // 调用原来的窗口过E? return pData->fnCallWindowProc( pData->fnOldProc, hwnd,uMsg,wParam,lParam ); } </pre>   此处 0xA0B0C0D0 只是q程q程地址I间中真实(l对QINJDATA地址的占位符。前面讲q,你无法在~译时知道该地址。但你可以在调用 VirtualAllocEx QؓINJDATAQ之后得?INJDATA 在远E进E中的位|。编译我们的 NewProc 后,可以得到如下l果Q? <pre> Address OpCode/Params Decoded instruction -------------------------------------------------- :00401000 55 push ebp :00401001 8BEC mov ebp, esp :00401003 C745FCD0C0B0A0 mov [ebp-04], A0B0C0D0 :0040100A ... .... :0040102D 8BE5 mov esp, ebp :0040102F 5D pop ebp :00401030 C21000 ret 0010 </pre> 因此Q其~译的代码(十六q制Q将是: <pre>558BECC745FCD0C0B0A0......8BE55DC21000. </pre> 现在你可以象下面q样l箋Q? <ol> <li>INJDATAQThreadFunc和NewProc 拯到目标进E; <li>修改 NewProc 的代码,以便 pData 中保存的?INJDATA 的真实地址?br>例如Q假?INJDATA 的地址QVirtualAllocExq回的|在目标进E中?0x008a0000。然后象下面q样修改NewProc的代码: <pre> 558BECC745FCD0C0B0A0......8BE55DC21000 <- 原来的NewProc Q注1Q? 558BECC745FC00008A00......8BE55DC21000 <- 修改后的NewProcQ用的是INJDATA的实际地址?/pre> 也就是说Q你用真正的 INJDATAQ注2Q?地址替代了虚拟?A0B0C0D0Q注2Q?<br> <li>开始执行远E的 ThreadFuncQ它负责子类化远E进E中的控件?br></li> </ol> <ul> <li>?、有人可能会问,Z么地址 A0B0C0D0 ?008a0000 在编译时序是相反的。因?Intel ?AMD 处理器?little-endian W号来表C(多字节)数据。换句话_某个数字的低位字节被存储在内存的最地址处,而高位字节被存储在最高位地址?br>假设“UNIX”q个词存储用4个字节,?big-endian pȝ中,它被存ؓ“UNIX”Q在 little-endian pȝ中,它将被存?#8220;XINU”? <li>?、某些破解(很糟Q以cM的方式修改可执行代码Q但是一旦加载到内存Q一个程序是无法修改自己的代码的Q代码驻留在可执行程序的“.text” 区域Q这个区域是写保护的Q。但仍可以修改远E的 NewProcQ因为它是先前以 PAGE_EXECUTE_READWRITE 许可方式被拷贝到某个内存块中的?</li> </ul> <p><a name=何时使用_CreateRemoteThread_和_WriteProcessMemory_技?何时使用 CreateRemoteThread ?WriteProcessMemory 技?/a><br><br>  与其它方法比较,使用 CreateRemoteThread ?WriteProcessMemory 技术进行代码注入更灉|Q这U方法不需要额外的 dllQ不q的是,该方法更复杂q且风险更大Q只要ThreadFunc出现哪怕一丁点错误Q很ҎpQƈ且最大可能地会)使远E进E崩溃(参见附录 FQ,因ؓ调试q程 ThreadFunc 是一个可怕的梦魇Q只有在注入的指令数很少Ӟ你才应该考虑使用q种技术进行注入,对于大块的代码注入,最好用 I.和II 部分讨论的方法?br><br>WinSpy 以及 InjectEx 请从q里<a ><u><font color=#0000ff>下蝲源代?/font></u></a>?/p> <a name=l束?l束?/a> <p> </p> <p>到目前ؓ止,有几个问题是我们未提及的Q现ȝ如下Q?/p> <table id=table1 width="91%" border=0> <tbody> <tr> <td align=middle width=387><strong>解决Ҏ </strong></td> <td align=middle><strong>OS</strong></td> <td align=middle width=356><strong>q程</strong></td> </tr> <tr> <td width=387>I、Hooks </td> <td>Win9x ?WinNT</td> <td width=356>仅仅?USER32.DLL Q注3Q链接的q程</td> </tr> <tr> <td width=387>II、CreateRemoteThread & LoadLibrary </td> <td>?WinNTQ注4Q?/td> <td width=356>所有进E(?Q? 包括pȝ服务Q注6Q?/td> </tr> <tr> <td width=387>III、CreateRemoteThread & WriteProcessMemory <br> </td> <td>?WinNT </td> <td width=356>所有进E? 包括pȝ服务</td> </tr> </tbody> </table> <ul> <li>?Q显Ӟ你无法hook一个没有消息队列的U程Q此外,SetWindowsHookEx不能与系l服务一起工作,即它们?USER32.DLL q行链接Q? <li>?QWin9x 中没?CreateRemoteThreadQ也没有 VirtualAllocEx Q实际上Q在Win9x 中可以仿真,但不是本文讨论的问题了)Q? <li>?Q所有进E?= 所?Win32 q程 + csrss.exe<br>本地应用 Qsmss.exe, os2ss.exe, autochk.exe {)不?Win32 APIQ所以也不会?kernel32.dll 链接。唯一一个例外是 csrss.exeQWin32 子系l本w,它是本地应用E序Q但其某些库Q~winsrv.dllQ需?Win32 DLLsQ包?kernel32.dllQ? <li>?Q如果你惌代码注入到pȝ服务中(lsass.exe, services.exe, winlogon.exe {)或csrss.exeQ在打开q程句柄QOpenProcessQ之前,你的进E优先|ؓ “SeDebugPrivilege”QAdjustTokenPrivilegesQ?</li> </ul> <p>  最后,有几件事情一定要了然于心Q你的注入代码很Ҏ摧毁目标q程Q尤其是注入代码本n出错的时候,所以要CQ权力带来责任!<br>  因ؓ本文中的许多例子是关于密码的Q你也许q读q?Zhefu Zhang 写的另外一文?#8220;<a target=_blank><u><font color=#0000ff>Super Password Spy++</font></u></a>” Q在该文中,他解释了如何获取IE 密码框中的内容,此外Q他q示范了如何保护你的密码控g免受cM的攻凅R?/p> <p><a name=附录A>附录A</a>Q?/p> <p>Z?kernel32.dll 和user32.dll L被映到相同的地址?br><br>  我的假定Q因为Microsoft 的程序员认ؓq样做有助于速度优化Qؓ什么呢Q我的解释是——通常一个可执行E序是由几个部分l成Q其中包?#8220;.reloc” 。当链接器创?EXE 或?DLL文gӞ它对文g被映到哪个内存地址做了一个假设。这是所谓的首选加?基地址。在映像文g中所有绝对地址都是Z链接器首选的加蝲地址Q如果由于某U原因,映像文g没有被加载到该地址Q那么这?#8220;.reloc”pv作用了,它包含映像文件中的所有地址的清单,q个清单中的地址反映了链接器首选加载地址和实际加载地址的差别(无论如何Q要注意~译器生的大多数指令用某U相对地址dQ因此,q没有你惌的那么多地址可供重新分配Q,另一斚wQ如果加载器能够按照链接器首选地址加蝲映像文gQ那?#8220;.reloc”p完全忽略掉了?br>  但kernel32.dll 和user32.dll 及其加蝲地址Z要以q种方式加蝲呢?因ؓ每一?Win32 E序都需要kernel32.dllQƈ且大多数Win32 E序也需?user32.dllQ那么L它们(kernel32.dll 和user32.dllQ映到首选地址可以改进所有可执行E序的加载时间。这样一来,加蝲器绝不能修改kernel32.dll and user32.dll.中的MQ绝对)地址。我们用下面的例子来说明Q?br>  某个应用程?App.exe 的映像基地址讄?KERNEL32的地址Q?base:"0x77e80000"Q或 USER32的首选基地址Q?base:"0x77e10000"Q,如果 App.exe 不是?USER32 导入方式来?USER32Q而是通过LoadLibrary 加蝲Q那么编译ƈq行App.exe 后,会报出错误信息("Illegal System DLL Relocation"——非法系lDLL地址重分配)QApp.exe 加蝲p|?br>Z么会q样呢?当创E时QWin 2000、Win XP 和Win 2003pȝ的加载器要检?kernel32.dll 和user32.dll 是否被映到首选基地址Q实际上Q它们的名字都被编码进了加载器Q,如果没有被加载到首选基地址Q将发出错误。在 WinNT4中,也会查ole32.dllQ在WinNT 3.51 和较低版本的Windows中,׃不会做这L查,所以kernel32.dll 和user32.dll可以被加载Q何地斏V只有ntdll.dllL被加载到其基地址Q加载器不进行检查,一旦ntdll.dll没有在其基地址Q进E就无法创徏?br><br>MQ对?WinNT 4 和较高的版本?/p> <ul> <li>一定要被加载到基地址的DLLs 有:kernel32.dll、user32.dll 和ntdll.dllQ? <li>每个Win32 E序都要使用?DLLs+ csrss.exeQkernel32.dll 和ntdll.dllQ? <li>每个q程都要使用的DLL只有一个,即是本地应用:ntdll.dllQ?</li> </ul> <p><a name=附录B>附录B</a>Q?/p> <p>/GZ ~译器开?/p> <p>  在生?Debug 版本Ӟ/GZ ~译器特性是默认打开的。你可以用它来捕h些错误(具体l节请参考相x档)。但Ҏ们的可执行程序意味着什么呢Q?br>  当打开 /GZ 开养I~译器会d一些额外的代码到可执行E序中每个函数所在的地方Q包括一个函数调用(被加到每个函数的最后)——检查已l被我们的函C改的 ESP堆栈指针。什么!N有一个函数调用被d?ThreadFunc 吗?那将DN。ThreadFunc 的远E拷贝将调用一个在q程q程中不存在的函敎ͼ臛_是在相同的地址I间中不存在Q?/p> <a name=附录C>附录C</a>Q? <p> </p> <p>静态函数和增量链接</p> <p>  增量链接主要作用是在生成应用E序时羃短链接时间。常规链接和增量链接的可执行E序之间的差别是——增量链接时Q每个函数调用经׃个额外的JMP指oQ该指o由链接器发出Q该规则的一个例外是函数声明为静态)。这?JMP 指o允许链接器在内存中移动函敎ͼq种Ud无需修改引用函数?CALL指o。但q些JMP指o也确实导致了一些问题:?ThreadFunc ?AfterThreadFunc 指向JMP指o而不是实际的代码。所以当计算ThreadFunc 的大时Q?</p> <pre>const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc) </pre>   你实际上计算的是指向 ThreadFunc 的JMPs 和AfterThreadFunc之间?#8220;距离” Q通常它们会紧挨着Q不用考虑距离问题Q。现在假?ThreadFunc 的地址位于004014C0 而伴随的 JMP指o位于 00401020? <pre>:00401020 jmp 004014C0 ... :004014C0 push EBP ; ThreadFunc 的实际地址 :004014C1 mov EBP, ESP ... </pre> 那么 <pre>WriteProcessMemory( .., &ThreadFunc, cbCodeSize, ..); </pre>   拷?#8220;JMP 004014C0”指oQ以及随后cbCodeSize范围内的所有指令)到远E进E——不是实际的 ThreadFunc。远E进E要执行的第一件事情将?#8220;JMP 004014C0” 。它会在其最后几条指令当中——远E进E和所有进E均如此。但 JMP指o的这?#8220;规则”也有例外。如果某个函数被声明为静态的Q它会被直接调用,即增量链接也是如此。这是Z么规?4要将 ThreadFunc ?AfterThreadFunc 声明为静态或用增量链接的缘故。(有关增量链接的其它信息参?Matt Pietrek的文?#8220;Remove Fatty Deposits from Your Applications Using Our 32-bit Liposuction Tools” Q? <p><a name=附录D>附录D</a>Q?/p> <p>Z?ThreadFunc的局部变量只?4kQ?/p> <p>  局部变量L存储在堆栈中Q如果某个函数有256个字节的局部变量,当进入该函数Ӟ堆栈指针减?56个字节(更精地_在函数开始处Q。例如,下面q个函数Q?</p> <pre>void Dummy(void) { BYTE var[256]; var[0] = 0; var[1] = 1; var[255] = 255; } </pre> ~译后的汇编如下Q? <pre>:00401000 push ebp :00401001 mov ebp, esp :00401003 sub esp, 00000100 ; change ESP as storage for ; local variables is needed :00401006 mov byte ptr [esp], 00 ; var[0] = 0; :0040100A mov byte ptr [esp+01], 01 ; var[1] = 1; :0040100F mov byte ptr [esp+FF], FF ; var[255] = 255; :00401017 mov esp, ebp ; restore stack pointer :00401019 pop ebp :0040101A ret </pre>   注意上述例子中,堆栈指针是如何被修改的?而如果某个函数需?KB以上局部变量内存空间又会怎么样呢Q其实,堆栈指针q不是被直接修改Q而是通过另一个函数调用来修改的。就是这个额外的函数调用使得我们?ThreadFunc “被破?#8221;了,因ؓ其远E拷贝会调用一个不存在的东ѝ?br>  我们看看文档中对堆栈探测?/Gs~译器选项是怎么说的Q?br>—?#8220;/GS是一个控制堆栈探的高Ҏ,堆栈探测是一pd~译器插入到每个函数调用的代码。当函数被激zLQ堆栈探需要的内存I间来存储相兛_数的局部变量?br>  如果函数需要的I间大于为局部变量分配的堆栈I间Q其堆栈探测被激zR默认的大小是一个页面(?0x86处理器上4kbQ。这个值允许在Win32 应用E序和Windows NT虚拟内存理器之间进行}慎调整以便增加运行时承诺l程序堆栈的内存?#8221;<br>我确信有Z问:文档中的“……堆栈探测C块需要的内存I间来存储相兛_数的局部变?#8230;…”那些~译器选项Q它们的描述Q在你完全弄明白之前有时真的让h气愤。例如,如果某个函数需?2KB的局部变量存储空_堆栈内存进行如下方式的分配Q更_地说?#8220;承诺” Q? <pre>sub esp, 0x1000 ; "分配" W一?4 Kb test [esp], eax ; 承诺一个新内存(如果q没有承诺) sub esp, 0x1000 ; "分配" W二? Kb test [esp], eax ; ... sub esp, 0x1000 test [esp], eax </pre>   注意4KB堆栈指针是如何被修改的,更重要的是,每一步之后堆栈底是如何被“触及”Q要l过查)。这样保证在“分配”Q承诺)另一面之前Q当前页面承诺的范围也包含堆栈底? <p>注意事项<br>  “每一个线E到辑օ自己的堆栈空_默认情况下,此空间由承诺的以及预留的内存l成Q每个线E?1 MB预留的内存,以及一|诺的内存Q系l将Ҏ需要从预留的堆栈内存中承诺一内存区?#8221; Q参?MSDN CreateThread > dwStackSize > Thread Stack SizeQ?br>  q应该清楚ؓ什么有兟뀀/GS 的文档说在堆栈探针在 Win32 应用E序和Windows NT虚拟内存理器之间进行}慎调整?br><br>现在回到我们的ThreadFunc以及 4KB 限制<br>  虽然你可以用 /Gs 防止调用堆栈探测例程Q但在文档对于这L做法l出了警告,此外Q文件描q可以用 #pragma check_stack 指o关闭或打开堆栈探测。但是这个指令好像一点作用都没有Q要么这个文档是垃圾Q要么我疏忽了其它一些信息?Q。MQCreateRemoteThread ?WriteProcessMemory 技术只能用于注入小块代码,所以你的局部变量应该尽量少耗费一些内存字节,最好不要超q?4KB限制?/p> <a name=附录E>附录E</a>Q? <p> </p> <p>Z么要开兌句拆分成三个以上?</p> <p>用下面这个例子很Ҏ解释q个问题Q假设有如下q么一个函敎ͼ </p> <pre>int Dummy( int arg1 ) { int ret =0; switch( arg1 ) { case 1: ret = 1; break; case 2: ret = 2; break; case 3: ret = 3; break; case 4: ret = 0xA0B0; break; } return ret; } </pre> ~译后变成下面这个样子: <pre>地址 操作?参数 解释后的指o -------------------------------------------------- ; arg1 -> ECX :00401000 8B4C2404 mov ecx, dword ptr [esp+04] :00401004 33C0 xor eax, eax ; EAX = 0 :00401006 49 dec ecx ; ECX -- :00401007 83F903 cmp ecx, 00000003 :0040100A 771E ja 0040102A ; JMP 到表***中的地址之一 ; 注意 ECX 包含的偏U? :0040100C FF248D2C104000 jmp dword ptr [4*ecx+0040102C] :00401013 B801000000 mov eax, 00000001 ; case 1: eax = 1; :00401018 C3 ret :00401019 B802000000 mov eax, 00000002 ; case 2: eax = 2; :0040101E C3 ret :0040101F B803000000 mov eax, 00000003 ; case 3: eax = 3; :00401024 C3 ret :00401025 B8B0A00000 mov eax, 0000A0B0 ; case 4: eax = 0xA0B0; :0040102A C3 ret :0040102B 90 nop ; 地址?** :0040102C 13104000 DWORD 00401013 ; jump to case 1 :00401030 19104000 DWORD 00401019 ; jump to case 2 :00401034 1F104000 DWORD 0040101F ; jump to case 3 :00401038 25104000 DWORD 00401025 ; jump to case 4 </pre> 注意如何实现q个开兌句?<br><br>  与其单独查每个CASE语句Q不如创Z个地址表,然后通过单地计算地址表的偏移量而蟩转到正确的CASE语句。这实际上是一U改q。假设你?0个CASE语句。如果不使用上述的技巧,你得执行50?CMP和JMP指o来达到最后一个CASE。相反,有了地址表后Q你可以通过表查询蟩转到MCASE语句Q从计算机算法角度和旉复杂度看Q我们用O(5)代替了O(2n)法。其中: <ol> <li>O表示最坏的旉复杂度; <li>我们假设需?条指令来q行表查询计偏U量Q最l蟩到相应的地址Q?</li> </ol> <p>  现在Q你也许认ؓ出现上述情况只是因ؓCASE帔R被有意选择l的Q?Q?Q?Q?Q。幸q的是,它的q个Ҏ可以应用于大多数现实例子中,只有偏移量的计算E微有些复杂。但有两个例外:</p> <ul> <li>如果CASE语句于{于三个Q? <li>如果CASE 帔R完全互不相关Q如Q?#8220;"case 1” Q?#8220;case 13” Q?#8220;case 50” Q??#8220;case 1000” Q; </li> </ul> <p>  昄Q单独判断每个的CASE帔R的话Q结果代码繁琐耗时Q但使用CMP和JMP指o则得结果代码的执行像普通的if-else 语句?br>有趣的地方:如果你不明白CASE语句使用帔R表达式的理由Q那么现在应该弄明白了吧。ؓ了创建地址表,昄在编译时应该知道相兛_址?/p> <p>现在回到问题Q?br>注意到地址 0040100C 处的JMP指o了吗Q我们来看看Intel关于十六q制操作?FF 的文档是怎么说的Q?/p> <pre>操作码 指o     描述 FF /4  JMP r/m32  Jump near, absolute indirect,            address given in r/m32</pre> <p>  原来JMP 使用了一U绝对寻址方式Q也是_它的操作敎ͼCASE语句中的 0040102CQ表CZ个绝对地址。还用我说什么吗Q远E?ThreadFunc 会盲目地认ؓ地址表中开兛_址?0040102CQJMPC个错误的地方Q造成q程q程崩溃?</p> <a name=附录F>附录F</a>Q? <p> </p> <p>Z么远E进E会崩溃呢?</p> <p>当远E进E崩溃时Q它L会因Z面这些原因:</p> <ol> <li>在ThreadFunc 中引用了一个不存在的串Q? <li>在在ThreadFunc ?中一个或多个指o使用l对dQ参见附录EQ; <li>ThreadFunc 调用某个不存在的函数Q该调用可能是编译器或链接器d的)。你在反汇编器中可以看到q样的情形: <pre>:004014C0 push EBP ; ThreadFunc 的入口点 :004014C1 mov EBP, ESP ... :004014C5 call 0041550 ; q里ɘq程q程崩溃 ... :00401502 ret </pre> 如果 CALL 是由~译器添加的指oQ因为某?#8220;忌” 开兛_/GZ是打开的)Q它被定位?ThreadFunc 的开始的某个地方或者结֤?</li> </ol> <p>  不管哪种情况Q你都要心翼C?CreateRemoteThread ?WriteProcessMemory 技术。尤其要注意你的~译?链接器选项Q一不小心它们就会在 ThreadFunc d内容?</p> <img src ="http://www.shnenglu.com/road420/aggbug/126755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/road420/" target="_blank">深邃?/a> 2010-09-16 14:06 <a href="http://www.shnenglu.com/road420/archive/2010/09/16/126755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CreateRemoteThread http://www.shnenglu.com/road420/archive/2010/09/16/126754.html深邃?/dc:creator>深邃?/author>Thu, 16 Sep 2010 06:05:00 GMThttp://www.shnenglu.com/road420/archive/2010/09/16/126754.htmlhttp://www.shnenglu.com/road420/comments/126754.htmlhttp://www.shnenglu.com/road420/archive/2010/09/16/126754.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/126754.htmlhttp://www.shnenglu.com/road420/services/trackbacks/126754.htmlCreateRemoteThread提供了一个在q程q程中执行代码的Ҏ,像代码长出膀飞到别处q行。本文将做一个入门介l,希望对广大编E爱好者有所帮助?

先解释一下远E进E,其实是要植入你的代码的q程Q相对于你的工作q程Q如果叫本地q程的话Q它叫q程q程Q可理解为宿丅R?/p>

首先介绍一下我们的主要工具CreateRemoteThreadQ这里先函数原型简单介l以下?/p>

CreateRemoteThread可将U程创徏在远E进E中?/p>

函数原型
HANDLE CreateRemoteThread(
HANDLE hProcess,                          // handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
SIZE_T dwStackSize,                       // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
LPVOID lpParameter,                       // thread argument
DWORD dwCreationFlags,                    // creation option
LPDWORD lpThreadId                        // thread identifier
);
参数说明Q?br>hProcess
[输入] q程句柄
lpThreadAttributes
[输入] U程安全描述字,指向SECURITY_ATTRIBUTESl构的指?br>dwStackSize
[输入] U程栈大,以字节表C?br>lpStartAddress
[输入] 一个LPTHREAD_START_ROUTINEcd的指针,指向在远E进E中执行的函数地址
lpParameter
[输入] 传入参数
dwCreationFlags
[输入] 创徏U程的其它标?/p>

lpThreadId
[输出] U程w䆾标志Q如果ؓNULL,则不q回

q回?br>成功q回新线E句柄,p|q回NULLQƈ且可调用GetLastError获得错误倹{?/p>

接下来我们将以两U方式用CreateRemoteThreadQ大家可以领略到CreateRemoteThread的神通,它你的代码可以q你的q程Q植入到别的q程中运行?/p>


W一U方?

W一U方式,我们使用函数的Ş式。即我们自q序中的一个函数植入到q程q程中?/p>

步骤1Q首先在你的q程中创建函数MyFuncQ我们将把它攑֜另一个进E中q行Q这里以windows

计算器ؓ目标q程?br>static DWORD WINAPI MyFunc (LPVOID pData)
{
//do something
//...
//pData输入可以是Mcd?br>//q里我们会传入一个DWORD的值做CZQƈ且简单返?br>return *(DWORD*)pData;
}
static void AfterMyFunc (void) {
}
q里有个技巧,定义了一个static void AfterMyFunc (void)Qؓ了下面确定我们的代码大小

步骤2Q定位目标进E,q里是一个计器
HWND hStart = ::FindWindow (TEXT("SciCalc"),NULL);

步骤3Q获得目标进E句柄,q里用到两个不太常用的函敎ͼ当然如果l常做线E?q程{方面的 目的话Q就很面熟了Q,但及有用
DWORD PID, TID;
TID = ::GetWindowThreadProcessId (hStart, &PID);

HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS,false,PID);

步骤4Q在目标q程中配变量地址I间Q这里我们分?0个字节,q且讑֮为可以读

写PAGE_READWRITEQ当然也可设为只ȝ其它标志Q这里就不一一说明了?br>char szBuffer[10];
*(DWORD*)szBuffer=1000;//for test
void *pDataRemote =(char*) VirtualAllocEx( hProcess, 0, sizeof(szBuffer), MEM_COMMIT,

PAGE_READWRITE );

步骤5Q写内容到目标进E中分配的变量空?br>::WriteProcessMemory( hProcess, pDataRemote, szBuffer,(sizeof(szBuffer),NULL);

步骤6Q在目标q程中分配代码地址I间
计算代码大小
DWORD cbCodeSize=((LPBYTE) AfterMyFunc - (LPBYTE) MyFunc);
分配代码地址I间
PDWORD pCodeRemote = (PDWORD) VirtualAllocEx( hProcess, 0, cbCodeSize, MEM_COMMIT,

PAGE_EXECUTE_READWRITE );

步骤7Q写内容到目标进E中分配的代码地址I间
WriteProcessMemory( hProcess, pCodeRemote, &MyFunc, cbCodeSize, NULL);

步骤8Q在目标q程中执行代?/p>

HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) pCodeRemote,
pDataRemote, 0 , NULL);
DWORD h;
if (hThread)
{
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &h );
TRACE("run and return %d\n",h);
::CloseHandle( hThread );
}

q里有几个值得说明的地?
使用WaitForSingleObject{待U程l束;
使用GetExitCodeThread获得q回|
最后关闭句柄CloseHandle?/p>

步骤9Q清理现?/p>

释放I间
::VirtualFreeEx( hProcess, pCodeRemote,
                 cbCodeSize,MEM_RELEASE );

::VirtualFreeEx( hProcess, pDataRemote,
                 cbParamSize,MEM_RELEASE );

关闭q程句柄
::CloseHandle( hProcess );

 

W二U方?

W二U方式,我们使用动态库的Ş式。即我们自׃个动态库植入到远E进E中?/p>

q里不再重复上面相同的步?只写出其中关键的地方.
关键1:
在步?中将动态库的\径作为变量传入变量空?
关键2:
在步??GetProcAddress作ؓ目标执行函数.

hThread = ::CreateRemoteThread( hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE )::GetProcAddress(
             hModule, "LoadLibraryA"),
             pDataRemote, 0, NULL );


另外在步?,清理现场中首先要先进行释放我们的动态库.也即cM步骤8执行函数FreeLibrary

hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE )::GetProcAddress(
hModule, "FreeLibrary"),
(void*)hLibModule, 0, NULL );

好了,限于幅不能够介l的很细,在用过E中如有疑问可向作者咨?Q开发环境:windows2000/vc6.0Q?
 

 


本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/fangchao918628/archive/2008/08/30/2852744.aspx



]]>
dll #pragma data_seg 实现数据的共?http://www.shnenglu.com/road420/archive/2010/09/16/126737.html深邃?/dc:creator>深邃?/author>Thu, 16 Sep 2010 02:25:00 GMThttp://www.shnenglu.com/road420/archive/2010/09/16/126737.htmlhttp://www.shnenglu.com/road420/comments/126737.htmlhttp://www.shnenglu.com/road420/archive/2010/09/16/126737.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/126737.htmlhttp://www.shnenglu.com/road420/services/trackbacks/126737.html#pragma data_seg("flag_data")
   int count=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")
q种Ҏ只能在没有def文g时用,如果通过def文gq行导出的话Q那么设|就要在def文g内设|而不?br>在代码里讄了?br>SETCTIONS
flag_data READ WRITE SHARED

 

在主文g中,?pragma data_seg建立一

个新的数据段q定义共享数据,其具体格式ؓQ?

#pragma data_seg Q?shareddata") //名称可以

//自己定义Q但必须与下面的一致?

HWND sharedwnd=NULL;//׃n数据

#pragma data_seg()



仅定义一个数据段q不能达到共享数据的目的Q还要告诉编译器该段的属性,有两U方法可以实现该目的 Q其效果是相同的Q,一U方法是?DEF文g中加入如下语句: SETCTIONS shareddata READ WRITE SHARED 另一U方法是在项目设|链接选项(Project Setting --〉Link)中加入如下语句: /SECTION:shareddata,rws

W一点:什么是׃n数据D?Z么要用共享数据段Q?它有什么用途?Q?
在Win16环境中,DLL的全局数据Ҏ个蝲入它的进E来说都是相同的Q而在Win32环境中,情况却发生了变化QDLL函数中的代码所创徏的Q何对象(包括变量Q都归调用它的线E或q程所有。当q程在蝲入DLLӞ操作pȝ自动把DLL地址映射到该q程的私有空_也就是进E的虚拟地址I间Q而且也复制该DLL的全局数据的一份拷贝到该进E空间。也是说每个进E所拥有的相同的DLL的全局数据Q它们的名称相同Q但其值却q不一定是相同的,而且是互不干涉的?

因此Q在Win32环境下要惛_多个q程中共享数据,必进行必要的讄。在讉K同一个Dll的各q程之间׃n存储器是通过存储器映文件技术实现的。也可以把这些需要共享的数据分离出来Q放|在一个独立的数据D里Qƈ把该D늚属性设|ؓ׃n。必ȝq些变量赋初|否则~译器会把没有赋初始值的变量攑֜一个叫未被初始化的数据D中?



#pragma data_seg预处理指令用于设|共享数据段。例如:

#pragma data_seg("SharedDataName") HHOOK hHook=NULL; //必须在定义的同时q行初始?!!!#pragma data_seg()

?pragma data_seg("SharedDataName")?pragma data_seg()之间的所有变量将被访问该Dll的所有进E看到和׃n。再加上一条指?pragma comment(linker,"/section:.SharedDataName,rws"),[注意Q数据节的名Uis case sensitive]那么q个数据节中的数据可以在所有DLL的实例之间共享。所有对q些数据的操作都针对同一个实例的Q而不是在每个q程的地址I间中都有一份?



当进E隐式或昑ּ调用一个动态库里的函数Ӟpȝ都要把这个动态库映射到这个进E的虚拟地址I间?以下U?地址I间")。这使得DLL成ؓq程的一部分Q以q个q程的n份执行,使用q个q程的堆栈?q项技术又叫code Injection技术,被广泛地应用在了病毒、黑客领域!呵呵^_^)



W二点:在具体用共享数据段旉要注意的一些问题!

Win32 DLLs are mapped into the address space of the calling process. By default, each process using a DLL has its own instance of all the DLLs global and static variables. (注意: 即是全局变量和静态变量也都不是共享的!) If your DLL needs to share data with other instances of it loaded by other applications, you can use either of the following approaches:

· Create named data sections using the data_seg pragma.

· Use memory mapped files. See the Win32 documentation about memory mapped files.

Here is an example of using the data_seg pragma:

#pragma data_seg (".myseg")
int i = 0;
char a[32] = "hello world";
#pragma data_seg()

data_seg can be used to create a new named section (.myseg in this example). The most typical usage is to call the data segment .shared for clarity. You then must specify the correct sharing attributes for this new named data section in your .def file or with the linker option /SECTION:.MYSEC,RWS. (q个~译参数既可以用pragma指o来指定,也可以在VC的IDE中指定!)

There are restrictions to consider before using a shared data segment:

· Any variables in a shared data segment must be statically initialized. In the above example, i is initialized to 0 and a is 32 characters initialized to hello world.

· All shared variables are placed in the compiled DLL in the specified data segment. Very large arrays can result in very large DLLs. This is true of all initialized global variables.

· Never store process-specific information in a shared data segment. Most Win32 data structures or values (such as HANDLEs) are really valid only within the context of a single process.

· Each process gets its own address space. It is very important that pointers are never stored in a variable contained in a shared data segment. A pointer might be perfectly valid in one application but not in another.

· It is possible that the DLL itself could get loaded at a different address in the virtual address spaces of each process. It is not safe to have pointers to functions in the DLL or to other shared variables.



]]>
#Pragma 指o介绍 http://www.shnenglu.com/road420/archive/2010/09/16/126736.html深邃?/dc:creator>深邃?/author>Thu, 16 Sep 2010 02:25:00 GMThttp://www.shnenglu.com/road420/archive/2010/09/16/126736.htmlhttp://www.shnenglu.com/road420/comments/126736.htmlhttp://www.shnenglu.com/road420/archive/2010/09/16/126736.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/126736.htmlhttp://www.shnenglu.com/road420/services/trackbacks/126736.html在所有的预处理指令中Q?Pragma 指o可能是最复杂的了Q它的作用是讑֮~译器的状态或者是指示~译器完成一些特定的动作?pragma指oҎ个编译器l出了一个方?在保持与C和C ++语言完全兼容的情况下,l出L或操作系l专有的特征。依据定?~译指示是机器或操作pȝ专有?且对于每个编译器都是不同的?/font>

其格式一般ؓ: #Pragma Para
其中Para 为参敎ͼ下面来看一些常用的参数?/font>

(1)message 参数?/font>

Message 参数是我最喜欢的一个参敎ͼ它能够在~译信息输出H口中输出相应的信息Q这对于源代码信息的控制是非帔R要的。其使用Ҏ为:
#Pragma message(“消息文本”)
当编译器遇到q条指o时就在编译输出窗口中消息文本打印出来?br>当我们在E序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设|这些宏Q此时我们可以用q条指o在编译的时候就q行查?br>假设我们希望判断自己有没有在源代码的什么地方定义了_X86q个宏可以用下面的方?br>#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86q个宏以后,应用E序在编译时׃在编译输出窗口里昄“_
X86 macro activated!”。我们就不会因ؓ不记得自己定义的一些特定的宏而抓x腮了?/font>

(2)另一个用得比较多的pragma参数是code_seg?/font>

格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设|程序中函数代码存放的代码段Q当我们开发驱动程序的时候就会用到它?/font>

(3)#pragma once (比较常用Q?/font>

只要在头文g的最开始加入这条指令就能够保证头文件被~译一ơ,q条指o实际上在VC6中就已经有了Q但是考虑到兼Ҏƈ没有太多的用它?/font>

(4)#pragma hdrstop

表示预编译头文g到此为止Q后面的头文件不q行预编译。BCB可以预编译头文g以加快链接的速度Q但如果所有头文g都进行预~译又可能占太多盘I间Q所以用这个选项排除一些头文g?br>有时单元之间有依赖关p,比如单元A依赖单元BQ所以单元B要先于单元A~译。你可以?pragma startup指定~译优先U,如果使用?pragma package(smart_init) QBCB׃Ҏ优先U的大小先后~译?/font>

(5)#pragma resource

#pragma resource "*.dfm"表示?.dfm文g中的资源加入工程?.dfm中包括窗体外观的定义?/font>

(6)#pragma warning

#pragma warning( disable : 4507 34; once : 4385; error : 164 )
{h于:
#pragma warning(disable:4507 34) // 不显C?507?4可告信?br>#pragma warning(once:4385) // 4385可告信息仅报告一?br>#pragma warning(error:164) // ?64可告信息作Z个错误?br>同时q个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
q里n代表一个警告等U?1---4)?br>#pragma warning( push )保存所有警告信息的现有的警告状态?br>#pragma warning( push, n)保存所有警告信息的现有的警告状态,q且把全局警告{讑֮为n?br>#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这D代码的最后,重新保存所有的警告信息(包括4705Q?706?707)?br>

Q?Qpragma comment(...)

该指令将一个注释记录放入一个对象文件或可执行文件中?br>常用的lib关键字,可以帮我们连入一个库文g?br>

Q?Qprogma data_seg

有的时候我们可能想让一个应用程序只启动一ơ,像单g模式(singleton)一P实现的方法可能有多种Q这里说说用#pragma data_seg来实现的ҎQ很是简z便利?/font>

应用E序的入口文件前面加?/font>

#pragma data_seg("flag_data")
int app_count = 0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")

然后E序启动的地方加?/font>

if(app_count>0)     // 如果计数大于0Q则退出应用程序?br>{
   //MessageBox(NULL, "已经启动一个应用程?, "Warning", MB_OK);

   //printf("no%d application", app_count);

   return FALSE;
}
app_count++;

Windows 在一个Win32E序的地址I间周围{了一道墙。通常Q一个程序的地址I间中的数据是私有的Q对别的E序而言是不可见的。但是执行STRPROG的多个执行实体表CZSTRLIB在程序的所有执行实体之间共享数据是毫无问题的。当您在一个STRPROGH口中增加或者删除一个字W串Ӟq种改变立卛_映在其它的窗口中?/font>

在全部例E之_STRLIB׃n两个变量Q一个字W数l和一个整敎ͼ记录已储存的有效字符串的个数Q。STRLIB这两个变量储存在共享的一个特D内存区D中Q?/font>

#pragma      data_seg ("shared")
        
int                 iTotal = 0 ;
        
WCHAR               szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ;
        
#pragma      data_seg ()
        

W一?pragma叙述建立数据D,q里命名为shared。您可以这D命名ؓM一个您喜欢的名字。在q里?pragma叙述之后的所有初始化了的变量都放在shared数据D中。第二个#pragma叙述标示D늚l束。对变量q行专门的初始化是很重要的,否则~译器将把它们放在普通的未初始化数据D中而不是放在shared中?/font>

q结器必ȝ道有一个「shared」共享数据段。在「Project Settings」对话框选择「Link」页面卷标。选中「STRLIB」时在「Project Options」字D(在Release和Debug讑֮中均可)Q包含下面的q结叙述Q?/font>

/SECTION:shared,RWS
        

字母RWS表示D具有读、写和共享属性。或者,您也可以直接用DLL原始码指定连l选项Q就像我们在STRLIB.C那样Q?/font>

#pragma comment(linker,"/SECTION:shared,RWS")
        

׃n的内存段允许iTotal变量和szStrings字符串数l在STRLIB的所有例E之间共享。因为MAX_STRINGS{于256Q?MAX_LENGTH{于63Q所以,׃n内存D늚长度?2,772字节QiTotal变量需?字节Q?56个指针中的每一个都需?28字节?/font>



]]>
RTTI、虚函数和虚基类的实现方式、开销分析及用指?/title><link>http://www.shnenglu.com/road420/archive/2010/08/10/122954.html</link><dc:creator>深邃?/dc:creator><author>深邃?/author><pubDate>Tue, 10 Aug 2010 09:35:00 GMT</pubDate><guid>http://www.shnenglu.com/road420/archive/2010/08/10/122954.html</guid><wfw:comment>http://www.shnenglu.com/road420/comments/122954.html</wfw:comment><comments>http://www.shnenglu.com/road420/archive/2010/08/10/122954.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/road420/comments/commentRss/122954.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/road420/services/trackbacks/122954.html</trackback:ping><description><![CDATA[<h1 align=center>RTTI、虚函数和虚基类的实现方式、开销分析及用指?/h1> <p align=center><strong>白杨</strong></p> <p align=center><strong><a >http://baiy.cn</a></strong></p> <p> </p> <p>“在正的场合使用恰当的特?#8221; 对称职的C++E序员来说是一个基本标准。想要做到这点,首先要了解语a中每个特性的实现方式及其开销。本文主要讨论相对于传统 C 而言Q对效率有媄响的几个C++新特性?/p> <p>相对于传l的 C 语言QC++ 引入的额外开销体现在以下两个方面:</p> <h3>~译时开销<br><img height=8 src="http://baiy.cn/doc/cpp/image/分割U?1.png" width=733 border=0></h3> <div align=center> <table id=table3 style="BORDER-COLLAPSE: collapse" borderColorDark=#808080 cellPadding=4 width="95%" borderColorLight=#808080 border=0> <tbody> <tr> <td>模板、类层次l构、强cd查等新特性,以及大量使用了这些新Ҏ的 STL 标准库都增加了编译器负担。但是应当看刎ͼq些新机能在不降低,甚至Q由于模板的内联能力Q提升了E序执行效率的前提下Q明昑ևMq大 C++ E序员的工作量? <p>用几U钟的CPU旉换取几h日的辛勤力_Q附带节省了日后调试和维护代码的旉Q这点开销当算倹{?/p> <p>当然Q在使用q些Ҏ的时候,也有不少优化技巧。比如:~译一?q泛依赖模板库的大型软gӞ几条昑ּ实例化指令就可能使编译速度提高几十倍;恰当地组合用部分专门化和完全专门化Q不但可以最优化E序的执行效率,q可以让同时使用多种不同参数实例化一套模板的E序体积显著减小……</p> </td> </tr> </tbody> </table> <p> </p> </div> <h3>q行时开销<br><img height=8 src="http://baiy.cn/doc/cpp/image/分割U?1.png" width=733 border=0></h3> <div align=center> <table id=table4 style="BORDER-COLLAPSE: collapse" borderColorDark=#808080 cellPadding=4 width="95%" borderColorLight=#808080 border=0> <tbody> <tr> <td>q行时开销恐怕是E序员最兛_的问题之一了。相对与传统CE序而言QC++中有可能引入额外q行时开销的新Ҏ包括: <ol> <li>虚基c? <li>虚函? <li>RTTIQdynamic_cast和typeidQ? <li>异常 <li>对象的构造和析构</li> </ol> <p>关于其中W四点:异常Q对于大多数C~译器来_在正常情况(未抛出异常)下,try块中的代码执行效率和普通代码一样高Q而且׃不再需要用传l上通过q回值或函数调用来判断错误的方式Q代码的实际执行效率q可能进一步提高。抛出和捕捉异常的效率也只是在某些情况下才会E低于函数正常返回的效率Q何况对于一个编写良好的E序Q抛出和捕捉异常的机会应该不多。关于异怋用的详细讨论Q参见:<a >C++~码规范</a>正文中的相关部分?a >C++异常机制的实现方式和开销分析</a>一节?/p> <p>而第五点Q对象的构造和析构开销也不L存在。对于不需要初始化/销毁的cdQƈ没有构造和析构的开销Q相反对于那些需要初始化/销毁的cd来说Q即使用传统的C方式实现Q也臛_需要与之相当的开销。这里要注意的一Ҏ量不要让构造和析构函数q于臃肿Q特别是在一个类层次l构中更要注意。时M持你的构造、析构函C只有最必要的初始化和销毁操作,把那些ƈ不是每个Q子Q对象都需要执行的操作留给其他Ҏ和派生类去解冟?/p> <p>其实对一个优U的编译器而言QC++的各U特性本w就是用C/汇编加以千锤癄而最优化实现的。可以说Q想用C甚至汇编比编译器更高效地实现某个C++Ҏ几乎是不可能的。要是真能做到这一点的话,大侠应该去写个~译器造福q大E序员才对~</p> <p>C++之所?被广泛认为比C“低效”Q其Ҏ原因在于Q由于程序员Ҏ些特性的实现方式及其产生的开销不够了解Q致使他们在错误的场合用了错误的特性。而这些错误基本都集中在:</p> <ul> <li>把异常当作另一U流控机Ӟ而不是仅其用于错误处理? <li>一个类?或其基类的构造、析构函数过于臃肿,包含了很多非初始?销毁范畴的代码 <li>滥用或不正确C用RTTI、虚函数和虚基类机制</li> </ul> <p>其中前两点上文已l讲q,下面讨论W三炏V?/p> <p>Z说明RTTI、虚函数和虚基类的实现方式,q里首先l出一个经典的菱Şl承实例Q及其具体实玎ͼZ便于理解Q这里故意忽略了一些无关紧要的优化Q:</p> </td> </tr> </tbody> </table> </div> <p align=center><img height=897 src="http://baiy.cn/doc/cpp/image/ȝ型承的典型内存布局.png" width=1030 border=0><br>图中虚箭头代表偏U,实箭头代表指?/p> <div align=center> <table id=table5 style="BORDER-COLLAPSE: collapse" borderColorDark=#808080 cellPadding=4 width="95%" borderColorLight=#808080 border=0> <tbody> <tr> <td>׃囑־到每U特性的q行时开销如下Q? <div align=center>  <table id=table6 style="BORDER-COLLAPSE: collapse" borderColorDark=#808080 cellPadding=4 width="95%" borderColorLight=#808080 border=1> <tbody> <tr> <td vAlign=top align=left width=143 bgColor=#c0c0c0><strong>Ҏ?/strong></td> <td vAlign=top align=left width=332 bgColor=#c0c0c0><strong>旉开销</strong></td> <td vAlign=top align=left bgColor=#c0c0c0><strong>I间开销</strong></td> </tr> <tr> <td vAlign=top align=left width=143>RTTI</td> <td vAlign=top align=left width=332>几次整Ş比较和一ơ取址操作Q可能还会有1?ơ整形加法)</td> <td vAlign=top align=left><u>每类?/u>一个type_info对象Q包括类型ID和类名称Q,典型情况下小?2字节 <p> </p> </td> </tr> <tr> <td vAlign=top align=left width=143>虚函?/td> <td vAlign=top align=left width=332>一ơ整形加法和一ơ指针间接引?/td> <td vAlign=top align=left><u>每类?/u>一个虚表,典型情况下小?28字节 <p><u>每对?/u>若干个(大部分情况下是一个)虚表指针Q典型情况下于8字节 <p> </p> </td> </tr> <tr> <td vAlign=top align=left width=143>虚基c?/td> <td vAlign=top align=left width=332>从虚l承的子cM讉K<u>虚基cȝ数据成员或其虚函?/u>Ӟ增加两ơ指针间接引用和一ơ整形加法(部分情况下可以优化ؓ一ơ指针间接引用)?/td> <td vAlign=top align=left><u>每类?/u>一个虚基类表,典型情况下小?2字节 <p><u>每对?/u>若干虚基c表指针Q典型情况下于8字节 <p>在同时用了虚函数的时候,<strong>虚基c表可以合ƈ到虚表(virtual tableQ中</strong>Q每对象的虚基类表指针(vbptrQ也可以省略Q只需vptr卛_Q。实际上Q?很多实现都是q么做的? <p> </p> </td> </tr> <tr> <td vAlign=top align=left colSpan=3> * 其中“每类?#8221;?#8220;每对?#8221;是指用到该特性的cd/对象。对于未用到q些功能的类型及其对象,则不会增加上q开销</td> </tr> </tbody> </table> </div> <p>可见Q关于老天“饿时掉馅饹{睡时掉老婆”{美好传说纯属谣a。但凡h工制品必不完,L设计上的取舍Q有光应的场合也有其不适用的地斏V?/p> <p>C++中的每个Ҏ,都是从程序员qx的生产生zM逐渐_֌而来的。在不正的场合使用它们必然会引起逻辑、行为和性能上的问题。对于上q特性,应该只在必要、合理的前提下才使用?/p> <p>"<strong>dynamic_cast</strong>" 用于在类层次l构中O游,Ҏ针或引用q行自由的向上、向下或交叉强制?<strong>typeid</strong>" 则用于获取一个对象或引用的确切类型,?"dynamic_cast" 不同Q将 "typeid" 作用于指针通常是一个错误,要得C个指针指向之对象的type_infoQ应当先其解引用(例如Q?typeid(*p);"Q?/p> <p>一般地Ԍ能用虚函数解决的问题׃要用 "dynamic_cast"Q能够用 "dynamic_cast" 解决的就不要?"typeid"。比如:</p> <div align=center> <table id=table7 style="BORDER-COLLAPSE: collapse" borderColorDark=#808080 cellPadding=4 width="95%" borderColorLight=#808080 border=0> <tbody> <tr> <td style="LINE-HEIGHT: 100%" bgColor=#ebebeb><font face=Fixedsys color=#800000><img height=46 src="http://baiy.cn/doc/cpp/image/反面教材.png" width=138 border=0><br><br></font><font face=Fixedsys color=#0000ff>void</font><font face=Fixedsys color=#800000><br>rotate(</font><font face=Fixedsys color=#ff8000>IN</font><font face=Fixedsys color=#800000> </font><font face=Fixedsys color=#0000ff>const</font><font face=Fixedsys color=#800000> </font><font face=Fixedsys color=#0000ff>CShape</font><font face=Fixedsys color=#800000>& </font><font face=Fixedsys color=#808080>iS</font><font face=Fixedsys color=#800000>)<br>{<br>    </font><font face=Fixedsys color=#0000ff>if</font><font face=Fixedsys color=#800000> (</font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#808080>iS</font><font face=Fixedsys color=#800000>) == </font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#0000ff>CCircle</font><font face=Fixedsys color=#800000>))<br>    {<br>        </font><font face=Fixedsys color=#008000>// ...</font><font face=Fixedsys color=#800000><br>    }<br>    </font><font face=Fixedsys color=#0000ff>else</font><font face=Fixedsys color=#800000> </font><font face=Fixedsys color=#0000ff>if</font><font face=Fixedsys color=#800000> (</font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#808080>iS</font><font face=Fixedsys color=#800000>) == </font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#0000ff>CTriangle</font><font face=Fixedsys color=#800000>))<br>    {<br>        </font><font face=Fixedsys color=#008000>// ...</font><font face=Fixedsys color=#800000><br>    }<br>    </font><font face=Fixedsys color=#0000ff>else</font><font face=Fixedsys color=#800000> </font><font face=Fixedsys color=#0000ff>if</font><font face=Fixedsys color=#800000> (</font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#808080>iS</font><font face=Fixedsys color=#800000>) == </font><font face=Fixedsys color=#0000ff>typeid</font><font face=Fixedsys color=#800000>(</font><font face=Fixedsys color=#0000ff>CSqucre</font><font face=Fixedsys color=#800000>))<br>    {<br>        </font><font face=Fixedsys color=#008000>// ...</font><font face=Fixedsys color=#800000><br>    }<br><br>    </font><font face=Fixedsys color=#008000>// ...</font><font face=Fixedsys color=#800000><br>}</font></td> </tr> </tbody> </table> <p align=left>以上代码?"dynamic_cast" 写会E好一点,当然最好的方式q是在CShape里定义名?"rotate" 的虚函数?/p> </div> <p>虚函数是C++众多q行时多态特性中开销最,也最常用的机制。虚函数的好处和作用q里不再多说Q应当注意在Ҏ能有苛刻要求的场合Q或者需要频J调用,Ҏ能影响较大的地方(比如每秒钟要调用成千上万ơ,而自w内容又很简单的事g处理函数Q要慎用虚函数?/p> <p>需要特别说明的一ҎQ虚函数的调用开销与通过函数指针的间接函数调用(例如Q经典CE序中常见的Q通过指向l构中的一个函数指针成员调用;以及调用DLL/SO中的函数{常见情况)是相当的。比起函数调用本w的开销Q保存现?>传递参?>传递返回?>恢复现场Q来_一ơ指针间接引用是微不道的。这׃?strong>在绝大部分可以用函数的场合中都能够负担得v虚方?/strong>的些微额外开销?/p> <p>作ؓ一U支持多l承的面向对象语aQ虚基类有时是保证类层次l构正确一致的一U必不可的手段。但在需要频J用基cL供的服务Q又Ҏ能要求较高的场合,应该量避免使用它。在基类中没有数据成员的场合Q也可以解除使用虚基cR例如,在上图中Q如果类 "BB" 中不存在数据成员Q那?"BB" 可以作Z个普通基cd别被 "B1" ?"B2" l承。这L优化在达到相同效果的前提下,解除了虚基类引v的开销。不q这U优化也会带来一些问题:?"DD" 向上强制?"BB" 时会引v歧义Q破坏了cdơ结构的逻辑关系?/p> <p>上述Ҏ的I间开销一般都是可以接受的Q当然也存在一些特例,比如Q在存储布局需要和传统Cl构兼容的场合、在考虑寚w的场合、在需要ؓ一个本来尺寸很的cd时实例化许多对象的场合等{?/p> </td> </tr> </tbody> </table> </div> <img src ="http://www.shnenglu.com/road420/aggbug/122954.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/road420/" target="_blank">深邃?/a> 2010-08-10 17:35 <a href="http://www.shnenglu.com/road420/archive/2010/08/10/122954.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Debug ? Release http://www.shnenglu.com/road420/archive/2009/11/21/101580.html深邃?/dc:creator>深邃?/author>Sat, 21 Nov 2009 14:39:00 GMThttp://www.shnenglu.com/road420/archive/2009/11/21/101580.htmlhttp://www.shnenglu.com/road420/comments/101580.htmlhttp://www.shnenglu.com/road420/archive/2009/11/21/101580.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/101580.htmlhttp://www.shnenglu.com/road420/services/trackbacks/101580.html

一、Debug  ? Release  ~译方式的本质区? 

Debug  通常UCؓ调试版本Q它包含调试信息Qƈ且不作Q何优化,便于E序员调试程序。Release  UCؓ发布版本Q它往往是进行了各种优化Q得程序在代码大小和运行速度上都是最优的Q以便用户很好地使用? 
Debug  ? Release  的真正秘密,在于一l编译选项。下面列Z分别针对二者的选项Q当焉此之外还有其他一些,?Fd  /FoQ但区别q不重要Q通常他们也不会引? Release  版错误,在此不讨论)  

Debug  版本Q? 
/MDd  /MLd  ? /MTd  使用  Debug  runtime  library(调试版本的运行时d数库)  
/Od  关闭优化开? 
/D  &quot;_DEBUG&quot;  相当? #define  _DEBUG,打开~译调试代码开?主要针对  
assert函数)  
/ZI  创徏  Edit  and  continue(~辑l箋)数据库,q样在调试过  
E中如果修改了源代码不需重新~译  
/GZ  可以帮助捕获内存错误  
/Gm  打开最化重链接开养I减少链接旉  

Release  版本Q?   
/MD  /ML  ? /MT  使用发布版本的运行时d数库  
/O1  ? /O2  优化开养I使程序最或最? 
/D  &quot;NDEBUG&quot;  关闭条g~译调试代码开?即不~译assert函数)  
/GF  合ƈ重复的字W串Qƈ字W串帔R攑ֈ只读内存Q防? 
被修? 

实际上,Debug  ? Release  q没有本质的界限Q他们只是一l编译选项的集合,~译器只是按照预定的选项行动。事实上Q我们甚臛_以修改这些选项Q从而得C化过的调试版本或是带跟踪语句的发布版本? 

二、哪些情况下  Release  版会出错  

有了上面的介l,我们再来逐个对照q些选项看看  Release  版错误是怎样产生? 

1.  Runtime  LibraryQ链接哪U运行时d数库通常只对E序的性能产生影响。调试版本的  Runtime  Library  包含了调试信息,q用了一些保护机制以帮助发现错误Q因此性能不如发布版本。编译器提供? Runtime  Library  通常很稳定,不会造成  Release  版错误;倒是׃  Debug  ? Runtime  Library  加强了对错误的检,如堆内存分配Q有时会出现  Debug  有错? Release  正常的现象。应当指出的是,如果  Debug  有错Q即? Release  正常Q程序肯定是? Bug  的,只不q可能是  Release  版的某次q行没有表现出来而已? 

2.  优化Q这是造成错误的主要原因,因ؓ关闭优化时源E序基本上是直接译的,而打开优化后编译器会作Zpd假设。这c错误主要有以下几种Q? 

(1)  帧指?Frame  Pointer)省略Q简U? FPO  Q:在函数调用过E中Q所有调用信息(q回地址、参敎ͼ以及自动变量都是攑֜栈中的。若函数的声明与实现不同Q参数、返回倹{调用方式)Q就会生错误————但  Debug  方式下,栈的讉K通过  EBP  寄存器保存的地址实现Q如果没有发生数l越界之cȝ错误Q或是越?#8220;不多”Q,函数通常能正常执行;Release  方式下,优化会省? EBP  栈基址指针Q这样通过一个全局指针讉K栈就会造成q回地址错误是程序崩溃。C++  的强cdҎ能查出大多数这L错误Q但如果用了强制cd转换Q就不行了。你可以? Release  版本中强制加? /Oy-  ~译选项来关掉指针省略Q以定是否此类错误。此c错误通常有:  

? MFC  消息响应函数书写错误。正的应ؓ  
afx_msg  LRESULT  OnMessageOwn(WPARAM  wparam,  LPARAM  lparam);  
ON_MESSAGE  宏包含强制类型{换。防止这U错误的Ҏ之一是重定义  ON_MESSAGE  宏,把下列代码加? stdafx.h  中(?include  &quot;afxwin.h&quot;之后Q?函数原Ş错误时编译会报错  
#undef  ON_MESSAGE  
#define  ON_MESSAGE(message,  memberFxn)  \  
{  message,  0,  0,  0,  AfxSig_lwl,  \  
(AFX_PMSG)(AFX_PMSGW)(static_cast&lt;  LRESULT  (AFX_MSG_CALL  \  
CWnd::*)(WPARAM,  LPARAM)  &gt;  (&memberFxn)  },  

(2)  volatile  型变量:volatile  告诉~译器该变量可能被程序之外的未知方式修改Q如pȝ、其他进E和U程Q。优化程序ؓ了ɽE序性能提高Q常把一些变量放在寄存器中(cM? register  关键字)Q而其他进E只能对该变量所在的内存q行修改Q而寄存器中的值没变。如果你的程序是多线E的Q或者你发现某个变量的g预期的不W而你信已正的讄了,则很可能遇到q样的问题。这U错误有时会表现为程序在最快优化出错而最优化正常。把你认为可疑的变量加上  volatile  试试? 

(3)  变量优化Q优化程序会Ҏ变量的用情况优化变量。例如,函数中有一个未被用的变量Q在  Debug  版中它有可能掩盖一个数l越界,而在  Release  版中Q这个变量很可能被优化调Q此时数l越界会破坏栈中有用的数据。当Ӟ实际的情况会比这复杂得多。与此有关的错误有:  
? 非法讉KQ包括数l越界、指针错误等。例? 
void  fn(void)  
{  
int  i;  
i  =  1;  
int  a[4];  
{  
int  j;  
j  =  1;  
}  
a[-1]  =  1;//当然错误不会q么明显Q例如下标是变量  
a[4]  =  1;  
}  
j  虽然在数l越界时已出了作用域Q但其空间ƈ未收回,因? i  ? j  ׃掩盖界。? Release  版由? i、j  q未其很大作用可能会被优化掉Q从而栈被破坏? 

3.  _DEBUG  ? NDEBUG  Q当定义? _DEBUG  Ӟassert()  函数会被~译Q? NDEBUG  时不被编译。除此之外,VC++中还有一pd断言宏。这包括Q? 

ANSI  C  断言  void  assert(int  expression  );  
C  Runtime  Lib  断言  _ASSERT(  booleanExpression  );  
_ASSERTE(  booleanExpression  );  
MFC  断言  ASSERT(  booleanExpression  );  
VERIFY(  booleanExpression  );  
ASSERT_VALID(  pObject  );  
ASSERT_KINDOF(  classname,  pobject  );  
ATL  断言  ATLASSERT(  booleanExpression  );  
此外QTRACE()  宏的~译也受  _DEBUG  控制? 

所有这些断a都只? Debug版中才被~译Q而在  Release  版中被忽略。唯一的例外是  VERIFY()  。事实上Q这些宏都是调用? assert()  函数Q只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了ME序代码Q而不只是布尔表达式(例如赋倹{能改变变量值的函数调用  {)Q那? Release  版都不会执行q些操作Q从而造成错误。初学者很Ҏ犯这c错误,查找的方法也很简单,因ؓq些宏都已在上面列出Q只要利? VC++  ? Find  in  Files  功能在工E所有文件中扑ֈ用这些宏的地方再一一查即可。另外,有些高手可能q会加入  #ifdef  _DEBUG  之类的条件编译,也要注意一下? 
Z值得一提的? VERIFY()  宏,q个宏允怽程序代码放在布表辑ּ里。这个宏通常用来? Windows  API  的返回倹{有些h可能个原因而滥? VERIFY()  Q事实上q是危险的,因ؓ  VERIFY()  q反了断a的思想Q不能ɽE序代码和调试代码完全分,最l可能会带来很多ȝ。因此,专家们徏议尽量少用这个宏? 

4.  /GZ  选项Q这个选项会做以下q些? 

(1)  初始化内存和变量。包括用  0xCC  初始化所有自动变量,0xCD  (  Cleared  Data  )  初始化堆中分配的内存Q即动态分配的内存Q例? new  Q,0xDD  (  Dead  Data  )  填充已被释放的堆内存Q例? delete  Q,0xFD(  deFencde  Data  )  初始化受保护的内存(debug  版在动态分配内存的前后加入保护内存以防止越界访问)Q其中括号中的词是微软徏议的助记词。这样做的好处是q些值都很大Q作为指针是不可能的Q而且  32  位系l中指针很少是奇数|在有些系l中奇数的指针会产生q行旉误)Q作为数g很少遇到Q而且q些g很容易L认,因此q很有利于在  Debug  版中发现  Release  版才会遇到的错误。要特别注意的是Q很多h认ؓ~译器会? 0  来初始化变量Q这是错误的Q而且q样很不利于查找错误Q? 
(2)  通过函数指针调用函数Ӟ会通过查栈指针验证函数调用的匹配性。(防止原Ş不匹配)  
(3)  函数q回前检查栈指针Q确认未被修攏V(防止界讉K和原形不匚wQ与W二合在一起可大致模拟帧指针省? FPO  Q? 

通常  /GZ  选项会造成  Debug  版出错? Release  版正常的现象Q因? Release  版中未初始化的变量是随机的,q有可能使指针指向一个有效地址而掩盖了非法讉K? 

除此之外Q?Gm  /GF  {选项造成错误的情冉|较少Q而且他们的效果显而易见,比较Ҏ发现? 
--------------------------------------------------------------  
Release是发行版?比Debug版本有一些优化,文g比Debug文g? 
Debug是调试版本,包括的程序信息更? 
ReleaseҎQ? 
build-&gt;batch  build-&gt;buildOK.  

-----------------------------------------------------  

一?amp;quot;Debug是调试版本,包括的程序信息更?amp;quot;  

补充Q只有DEBUG版的E序才能讄断点、单步执行、用TRACE/ASSERT{调试输句。REALEASE不包含Q何调试信息,所以体U小、运行速度快? 

二、一般发布release的方法除了hzh_shat(?  所说的之外Q还可以project-&gt;Set  Active  ConfigQ选中release版本。此后,按F5或F7~译所得的l果是release版本

]]>
DLL U程本地存储http://www.shnenglu.com/road420/archive/2009/10/26/99510.html深邃?/dc:creator>深邃?/author>Mon, 26 Oct 2009 10:33:00 GMThttp://www.shnenglu.com/road420/archive/2009/10/26/99510.htmlhttp://www.shnenglu.com/road420/comments/99510.htmlhttp://www.shnenglu.com/road420/archive/2009/10/26/99510.html#Feedback0http://www.shnenglu.com/road420/comments/commentRss/99510.htmlhttp://www.shnenglu.com/road420/services/trackbacks/99510.htmlDLL, U程本地存储
1.概览
.构造DLL  
   (1)仅导出函?/font>
      DLL可以导出全局变量和类Q但我们不徏议这么做Q徏议导出函数?/font>
   (2).lib
      每个DLL都有与之相对应的.lib文g,该文件中列出?/font>DLL中导出的函数和变量的W号?/font>
   (3)指定要导出的函数?/font>
       因ؓ不同~译器的Name mangle规则不同Q这导?/font>DLL不能跨编译器使用?/font>
       有以下两U方法可以解册个问题:
            1.?/font>.def文g中指定要导出的函数名
            2.在编译指中指定要导出的函数名Q?/font>
                #pragma comment(linker, "/export:MyFunc=_MyFunc@8")
.DLL加蝲路径
    当需要加载一?/font>DLLӞpȝ会依照下面的序d找所需DLL直到扑ֈ为止Q然后加载,否则加蝲p|?/font>
              (1)当前可执行文件\?/font>
              (2)GetWindowsDirectoryq回的Windowspȝ路径
              (3)16位系l的路径 windows"system
              (4)GetSystemDirectoryq回的Windowspȝ路径
              (5)当前q程所在\?/font>
              (6)PATH环境中所指定的\?/font>
­
.创徏\使用动态链接库
首先必须创徏一个包含需要导出的W号的头文gQ以便其他程序链接到该dll上:
// dllexample.h
#ifdef DLLEXAMPLE_EXPORTS // 在编译命令中已定义,所以实际用的是 __declspec(dllexport)
#define DLLEXAMPLE_API __declspec(dllexport)
#else
#define DLLEXAMPLE_API __declspec(dllimport)
#endif
DLLEXAMPLE_API int fnDllexample(void);
当其他应用包含该头文Ӟ意图使用该dll的导出符hQ因为没有定义DLLEXAMPLE_EXPORTSQ所以用的是__declspec(dllimport)Q这L译器~译时便知道q是从外部引入的函数。在链接Ӟ链接E序生成导入表(ImportAddressTable),该表|列了所有调用到的函敎ͼ以及一个空白的对应地址。在E序执行Ӟ加蝲器将动态的填入每个函数W号在本q程中的地址Q得程序能正确的调用到dll中的函数上?
q种通过dll提供?h?lib文gq行链接dll的用方式,UCؓ隐式链接。用vc开发程序时Q几乎所有的pȝAPI调用都用了隐式链接?
.昑ּ链接
在exe创徏时不引用.lib文g中的W号Q当然也不必包含.h头文Ӟ而是q序调用LoadLibrary(Ex)以及GetProcAddress函数来获取每个需要用的函数地址Q从而进行dll中的函数调用Q这Udll使用ҎUCؓ昑ּ链接。显式链接时不生成对应dll的IAT.
当决定不再用该dllӞ通过调用FreeLibrary来卸载。需要注意的是,同一个进E中p调用LoadLibrary的次数要和调用FreeLibrary的次数相{,因ؓpȝl护了一个用计敎ͼ当计Cؓ0Ӟ才会真正的卸载该dll.
如果想确认一个dll是否已经被映到q程I间中,量使用GetModuleHandleQ最好不要冒然用LoadLibrary(Ex).
GetProcAddress可以传递函数名或者序?通过MAKEINTRESOURCE(2)?制作"序号).
­
1.1动态加载DLL文g LoadLibraryEx
HMODULE LoadLibraryEx( //q回DLL加蝲到进E空间原首地址?/font>
PCTSTR pszDLLPathName,
HANDLE hFile,
DWORD dwFlags);
dwFlags 可以有以下几个?/font>
              (1) DONT_RESOLVE_DLL_REFERENCES
                                    永远不要使有q个|它的存在仅仅是ؓ了向后兼宏V?/font>
                  更多内容误问:http://blogs.msdn.com/oldnewthing/archive/2005/02/14/372266.aspx
              (2) LOAD_LIBRARY_AS_DATAFILE
                  把要加蝲?/font>DLL文g以数据文件的形式加蝲到进E中?/font>
                  GetModuleHandle?/font>GetProcAddressq回NULL
              (3) LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
                  与前者相同,不同的时独占打开Q禁止其它进E访问和修改?/font>DLL中的内容?/font>
              (4) LOAD_LIBRARY_AS_IMAGE_RESOURCE
                  不修?/font>DLL中的RVAQ以image的Ş式加载到q程中。常?/font>LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE一起用?/font>
              (5) LOAD_WITH_ALTERED_SEARCH_PATH
                   修改DLL的加载\?/font>
1.2 DLL的加载与卸蝲
    (1)加蝲
       不要在同一q程中,同时使用LoadLIbrary?/font>LoadLibraryEx加蝲同一DLL文g?/font>
       DLL的引用计数是以进Eؓ单位的?/font>LoadLibrary会把DLL文g加蝲到内存,然后映射到进E空间中?/font>
       多次加蝲同一DLL只会增加引用计数而不会多ơ映。当所有进E对DLL的引用计数都?/font>0Ӟpȝ会在内存中释放该DLL?/font>
    (2)卸蝲
         FreeLibrary,FreeLibraryAndExitThread对当前进E的DLL的引用计数减1
    (3) GetProcAddress
         取得函数地址。它只接?/font>ANSI字符丌Ӏ?/font>
2.DLL的入口函?/strong>
      2.1 DllMain
              BOOL WINAPI DllMain(
              HINSTANCE hInstDll, ""加蝲后在q程中的虚拟地址
              DWORD fdwReason, ""pȝ因何而调用该函数
              PVOID fImpLoad ""查看是隐工还是动态加载该DLL
    DLLs?/font>DllMainҎ来初始化他们自已?/font>DllMain中的代码应尽量简单,只做一些简单的初始化工作?/font>
    不要?/font>DllMain中调?/font>LoadLibrary,FreeLibrary?/font>Shell, ODBC, COM, RPC, ?/font> socket 函数Q从而避免不可预期的错误?/font>
  2.2 fdwReason的?/strong>
    (1)DLL_PROCESS_ATTACH
      pȝ在ؓ每个q程W一ơ加载该DLL时会Q执?/font>DLL_PROCESS_ATTACH后面的语句来初始?/font>DLL,DllMain的返回g由它军_?/font>
     pȝ会忽?/font>DLL_THREAD_ATTACH{执行后DllMain的返回倹{?/font>
     如果DllMainq回FALSE,pȝ会自动调?/font>DLL_PROCESS_DETACH的代码ƈ解除DLL文g中进E中的内存映?/font>        
    (2)DLL_PROCESS_DETACH
        如果DLL是因q程l止而卸载其在进E中的映,那么负责调用ExitProcess的线E会调用DllMain?/font>DLL_PROCESS_DETACH所对应的代码?/font>
        如果DLL是因FreeLibrary?/font>FreeLibraryAndExitThreadQ而卸载其在进E中的映? 那么FreeLibrary?/font>FreeLibraryAndExitThread会负责调?/font>DllMain?/font>DLL_PROCESS_DETACH所对应的代码?/font>
        如果DLL是因TerminateProcess而卸载其在进E中的映?/font>,pȝ不会调用DllMain?/font>DLL_PROCESS_DETACH所对应的代码?/font>
    (3) DLL_THREAD_ATTACH
        若进E是先加载的DLL,后创建的U程
        那么在进E中创徏新线E时(ȝE除?/font>)Q系l会执行该进E已载的所?/font>DLL?/font>DllMain?/font>DLL_THREAD_ATTACH对应的代码?/font>
         若进E是先创建的U程,后加载的DLL
         那么pȝ不会调用DLL?/font>DllMain中的代码?/font>
     (4) DLL_THREAD_DETACH
         q程中的U程退出时Q会先执行所有已加蝲DLL?/font>DllMain?/font>DLL_THREAD_DETACH所对应的代码。若该代码中有死循环Q线E不会退出?/font>              
2.3 同步化DllMain的调?/strong>
      同一旉只能有一个线E调?/font>DllMain中的代码Q所以下面的代码会导致死循环
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
   HANDLE hThread;
   DWORD dwThreadId;
   switch (fdwReason) {
   case DLL_PROCESS_ATTACH:
      // The DLL is being mapped into the process' address space.
      // Create a thread to do some stuff.
      hThread = CreateThread(NULL, 0, SomeFunction, NULL,
         0, &dwThreadId);// CreateThread?/font>DLL_THREAD_ATTACH中的代码Q但是由于当前线Eƈ未执行完?/font>,
      //所?/font>DLL_THREAD_ATTACH 中的代码不会被执行,?/font>CreateThread永无不会q回?/font>
      // Suspend our thread until the new thread terminates.
      WaitForSingleObject(hThread, INFINITE);
­
      // We no longer need access to the new thread.
      CloseHandle(hThread);
      break;
­
   case DLL_THREAD_ATTACH:
      // A thread is being created.
      break;
­
   case DLL_THREAD_DETACH:
      // A thread is exiting cleanly.
      break;
­
   case DLL_PROCESS_DETACH:
      // The DLL is being unmapped from the process' address space.
      break;
   }
   return(TRUE);
}
­
3.延时加蝲DLL
(1)延时加蝲DLL的限?/font>
         延迟加蝲的D L L是个隐含链接的D L LQ它实际上要{到你的代码试图引用D L L中包含的一个符h才进行加?/font>Q它与动态加载不同?/font>
4.已知的DLL (Known DLLs)
    位置Q?/font>HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Control"Session Manager"KnownDLLs
    LoadLibrary在查?/font>DLL会先去该位置查找有无相应的键gDLL要对应,若有则根据链值去%SystemRoot%"System32加蝲键值对应的DLL
    若无则根据默认规d?/font>DLL
5.Bind and Rebase Module
    它可以程序启动的速度?/font>ReBaseImage
­
DLL 注入?/font>API?/font>(DLL Injection and API Hooking)
1.概览
  每个q程都有自已独立的地址I间Q一个进E不可能创徏一个指向其它进E地址I间的指针?/font>
   然而如果我们把自已?/font>DLL注射到另一个进E的地址I间去,我们可以在那个被注入的q程里ؓ所Ʋؓ了?/font>
   Subclass同一q程中的H体Q?/font>http://msdn2.microsoft.com/en-us/library/ms649784.aspx.
2.用注册表注入DLL
     该方法适用于给GUI的程序注?/font>DLL
     所有的GUI应用E序在启动时都会加蝲User32.dllQ而在User32.dll?/font>DLL_PROCESS_ATTACH代码Ҏ注册表中的信?/font>
来注入用h定的DLL
注册表项 HKEY_LOCAL_MACHINE"Software"Microsoft"Windows NT"CurrentVersion"Windows"
中有两个|
    LoadAppInit_DllsQ键g指定要注入的DLL ?/font>:c:"inject.dll
AppInit_DllsQ若光gؓ1,则注?/font>LoadAppInit_Dlls中指定的DLLQ否则若?/font>0则不注入?/font>
    ?/font>:
(1)LoadAppInit_Dlls中的值是以空格或分号分隔的,所?/font>DLL的\径中最好不要有I格Q最后不指定路径Q直接将DLL攑ֈwindowspȝ目录中?/font>
(2) 用注册表注入DLL的方式有很大的局限?/font>,Kernel32.dll?/font>Ntdll.dll中有的函数才能调?/font>
一.注入dll
1.通过注册表项 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs 来指定你的dll的\径,那么当一个GUIE序启动时就要加载User32.dll,而User32.dll会查这个|如果有的话就LoadLibrary该Dll。这个方法不好,因ؓ大多数情冉|们只需要针Ҏ的注入Qƈ且没办法注入C使用User32.dll的进E中Q?
­
2.用SetWindowsHookEx函数Qƈ传递目标线EID、需要挂载的Dll在本q程中的映射地址(hInstance)、替换函数在本进E中的地址。这P当被挂蝲q程的这个线E要执行相应的操作时(GETMESSAGE、键盘消息之cȝ)Q就会发现已l安装了WH_XXQThe system checks to see whether the DLL containing the GetMsgProc function is mapped into Process B's address spaceQ如果还未映该DllQ则强制LoadLibrary。然后系l调用hThisInstance + (GetMsgProc - hInstance),从而实C事g的通知。这U方法的好处是可以针Ҏ个进E安装HookQ缺ҎҎ被目标进E发现、同样只适用于GUIq程。如果不再想使用挂钩了,那么需要调用UnhookWindowsHookExQ卸载Hook?
­
3.使用q程U程注入Dll(Injecting a DLL Using Remote Threads)
q个Ҏ比较好。流E是q样?
?调用VirtualAllocExQ在目标q程保留一块内存,q提交,光度是你要注入Dll的全路径长度nLen + 1Q返回地址pv;
?调用WriteProcessMemoryQ在目标q程的pv处写入Dll的全路径Q注意要d\0l束W?
?获取本进E?/strong>的LoadLibrary函数的地址Q方法是调用pfn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA")——之所以获取本q程的地址Q是因ؓkernel32.dll在每个进E的映射地址都相同,倘若不同Q那么此Ҏ则无?
?调用HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,  pfn, pv, 0, NULL)来创E线E,其实q个U程函数是LoadLibrary函数Q因此将执行映射Dll到目标进E的操作;
?调用VirtuallFreeEx(hProcessRemote, pv)释放提交的内?
q便完成了dll注入?
~点是不能用在windows98上。但是对于xp都要被微软抛弃的q代Qwindows98地媄响不大了?
­
4.披着皮的狼Q用特z伊Dll来注入Dll(Injecting a DLL with a Trojan DLL)
其实是替换某个目标q程要加载的a.dllQƈ把a.dll的所有引出函数用函数转发器在自己的dll引出?
­
5.用调试函数插入Dll
ReadProcessMemory和WriteProcessMemory是windows提供的调试函数。如果在Ҏ3中调用WriteProcessMemory写入的不是字串而是_ֿ~排好的机器指oQƈ且写在目标进E特定的地址I间Q那么这D|器指令就有机会执行——而这D|器指令恰好完成了LoadLibrary功能;
­
6.其他ҎQ略Q?
­
?挂接API(API Hooking)
其实Q这是许多注入的Dll都愿意做的事情?
所谓挂接API是在目标进E调用windows API之前Q先执行我们的仿API函数Q从而控制系lAPI的行为,辑ֈҎ的目的?
我们的仿造函数必M要替换的pȝAPI有相同的型参表以及相同的q回值类?
­
1.改写pȝAPI代码的前几个字节Q通过写入jmp指o来蟩转到我们的函数。在我们的函数里执行操作Q可以直接返回一个|也可以将pȝAPI的前几个字节复原Q调用系lAPIQƈq回pȝAPI的值——随便你x么做?
此方法的~点是对于抢占式多线E的pȝ不太用?
­
2.通过改写目标q程IAT中要调用的函数地址来达到目的。具体操作见书中CZ
­
­
­
U程本地存储(Thread-Local Storage)
例子C / C + +q行期库要用线E本地存储器Q?T L SQ。由于运行期库是在多U程应用E序出现前的许多q设计的Q因此运行期库中的大多数函数是用于单U程应用E序的。函数s t r t o k是个很好的例子?/font>
可能避免用全局变量和静态变?/font>
1.动态TLS
?1-1 用于理T L S的内部数据结?/font>
­
在创建线E时Q进E会为当前创建的U程分配一?/font>void *的数l作?/font>TLS用。它用于存储只限当前U程可见的全局变量?/font>
从而ɘq程中的每个U程都可以有自已?/font>(不能其它U程讉K?/font>)全局变量?/font>
TlsAlloc在返回时会先把槽中的值置?/font>0。每个线E至有64个槽?/font>
2.静态TLS
              __declspec(thread)关键字用于声明,U程本地的全局变量?/font>
              要求声明的变量必L全局变量或静态变量?/font>
3.Common API:
              TlsAlloc   TlsFree
              TlsSetValue   TlsGetValue
              __declspec(thread)


]]>
ũ帾ŮëƬƷþ| պŷþþwwwۺ| þþƷa޹v岻| ޹˾Ʒþþþþۺ | þۺϾɫۺ97_þþ| þþþþAVר| 99ƷþþƷһ| AVһþ| þ¾ƷĻ| ŷձþþƷ| þ޹վ| ھƷþۺ88| Ʒþþþþ| þۺϾþڹ| һһþaaۺϾƷ| þAVۺϺɫ| þۺ97ɫ| þþþþþۺձ| þþþþþۺۺϺݺ| ŷþþþþҹƷ| ھƷþþӰԺ| ձþþվ| 91Ʒ91þþþø| þþƷþ| 㽶þAһ| 99þþƷһ| þۺϺݺɫۺ| ޹պŷþ| þݺҹҹ2O2O| Ʒþþþһ | ҹƷþþþþapp| þþþþùaѹۿɫƬ| þĻƷѩ| ޳˾Ʒþ| ޾ƷþþþþðĦ | þùƷҰAV| þҹ1000ϼ| þùҹƵ| 99þ㽶߿| ۺϾþü| ھƷþþӰԺ|