Shark恒 发表于 2015-1-20 17:42

使用OllyDbg从零开始Cracking 第五十八章-EXECryptor v2.2.50.h脱壳

                         第五十八章-ExeCryptor v2.2.50.h好,本章还剩下ExeCryptor的最后三个UnPackMe,这三个UnPackMe是本系列教程中最难脱的。我们来尝试对它们进行脱壳,如果没有脱壳成功的话,至少也会记录下脱壳思路,供大家学习。我们双击运行UnPackMe H,可以看到它新增了如下保护:我们可以看到入口点保护开启了。也就是说,入口点可能被隐藏起来了。好,我们还是跟之前一样对代码段设置break-on-execute断点,运行起来,断到了这里。貌似有点不对劲,我们看看之前脱壳过的版本。我们可以看到入口点处的代码明显被隐藏了。这是之前脱壳版本的截图:这是现在UnPackMe H的截图:对比着看,明显有差别。我们来看看UnPackMe H此时堆栈的情况:我们再来看看之前脱过壳的版本:我们可以看到之前脱壳修复后的版本断在OEP处时,栈顶指针指向的地址是12FFC4(不同的机器这个地址可能会不同)。根据堆栈平衡的原理,对于大部分壳(PS:有少数壳可能会玩一些把戏,譬如说:ExeCryptor,它利用了TLS在入口点之前执行代码,所以此时的栈顶指针可能与OEP处时的栈顶指针不一致)来说,用OD加载断在入口点处时的栈顶指针应该和断在OEP处时的栈顶指针是保持一致的。就我们当前这个例子来说,入口点处时栈顶指针ESP指向的地址是12FFC4。大家在平时脱壳的时候也要多多留意入口点处的栈顶指针指向了哪里。成功脱壳后,OEP处的第一条指令应该是PUSH EBP。下面我们来执行PUSH EBP这条指令。我们可以看到EBP的值被压入到堆栈中了:我们可以看到EBP的值被保存到12FFC0中了,这是原程序执行的第一条指令。下面我们来看看UnPackMe H,此时的栈顶指针指向的地址明显高于12FFC4,也不等于12FFC0。我们继续观察堆栈:正常来说,到达OEP处时,栈顶指针指向12FFC4才对,这里我们姑且算它执行了PUSH EBP,那么栈顶指针也应该指向的是12FFC0才对。也就是说该UnPackMe模拟执行了PUSH EBP,并且中间掺杂了大量的垃圾指令。这里我们对黄色标注出来的区域设置硬件执行断点或者BP断点。从这里开始该UnPackMe就没有继续模拟执行指令了,跟原程序是一样的。我们来看看断在这里时的堆栈情况:断在了这里,我们来看下堆栈:我们可以看到此时12FFC0中已经存放了12FFF0(EBP寄存器的初始值),也就说该UnPackMe已经成功模拟执行了PUSH EBP指令,接着12FFBC中存放了FFFFFFFF,相当于模拟执行了PUSH -1指令。我们知道PUSH EBP与PUSH -1这两条指令之间应该有一条MOV EBP,ESP指令才对。我们耐心看的话,就会发现4271CD前面的几条指令都被成功模拟执行了。我们会发现在剥离了TLS以后,每次断在OEP处时,唯一会变化的就是EBX寄存器的值,但是无论怎么变,总是在7FFDB000~7FFDF000这个范围之内,反观其他寄存器的值都是固定不变的。好,我们重启OD,再次断到ADD EBP,-58指令处。004271CD 83C4 A8 ADD ESP,-58我们将当前未脱壳和之前已脱壳的寄存器组情况进行对比:已脱壳:未脱壳:我们可以看到在UnPackMe模拟执行指令的过程中连同堆栈以及除ECX,EDX以外的其他通用寄存器也都模拟了。也就是当已脱壳和未脱壳的版本处于同一个位置时,我们要留意EAX,EBX,ESP,EBP,ESI,EDI这几个寄存器的情况,ECX,EDX的话,我们并不关心。好,现在我们回到该UnPackMe模拟执行指令的起始地址处。我们可以看到在4271B5~4271CD之间掺杂了大量的垃圾指令-起到混淆的作用。实际上它们是要完成5条指令的功能。我们按F7单步执行RET指令,到了这里。首先是利用PUSHFD保存EFLAGS(标志寄存器)的值。接下来我们对ExeCryptor壳所在区段设置内存写入断点,看看其在执行的过程中会不会保存什么值之类的东西,这里ExeCryptor有好几个区段,大家可以逐一尝试。设置完内存写入断点以后,我们运行起来,看看会发生什么。断在了这里。这里我们可以看到将EAX的值保存到47AD0C这个内存单元中,但是此时EAX的值并不是某个寄存器的初始值,之前并没有见过这个值。虽然没弄明白这条指令实际的作用,但我们还是将什么数值被保存到哪里简单的记录一下吧,方便下面的分析。EAX = 122601B0 保存到 47AD0C中EAX = 5A731601 保存到 4815E0 中EBX = 5A731601 保存到 4815E0 中EAX = 0046C5DE 保存到 47A90C中00496CBB 01 变成了 0000496C9C 01 变成了 0200496CAB 00 变成了 01我们要时刻留意堆栈的情况:看看有没有出现12FFF0这个值,到目前为止还没有出现这个值,我们继续记录。0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFF0这里是利用POP指令将12FFF0保存到47A0CC中EDI = 7C920738 保存到47A4CC中。这里0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI这是保存EDI寄存器的值,我们要稍微留意一下,它是要模拟的寄存器之一。ESI=0046C5DE 保存到 0047A8CC中EDX=0047F3EF 保存到 0047BCF8中ECX=0047F3EF 保存到 0047B8EC中EAX=0047E97E 保存到 0047B4E4中EBX=7FFDB000 保存到 00481198中这里00498D03 899D 8C114800 MOV DWORD PTR SS: , EBXEAX = 0012FFC0 保存到 0047B0D8中这里我们可以看到是12FFC0,我们继续。EAX=14F43E15 保存到 0047ACCC中00496CAB 01 变成了 0000496C9C 02 变成了 0300496D9A 00 变成了 01跟之前一样POP利用POP指令将12FFF0保存到47A488中EDI = 7C920738 保存到 0047A888中这里我们应该欣喜才对,因为出现了12FFF0,因为要模拟执行PUSH EBP的话,EBP的值应该为12FFF0才对,但是12FFC0这个值还没有出现。ESI = FFFFFFFF 保存到 0047AC88中这里00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESIEDX=0047F3EF 保存到 0047C0B4中ECX=0047F3EF 保存到 0047BCA8中EAX=0047E97E 保存到 0047B8A0 中EBX=7FFDB000 保存到 00481554中这里跟之前某条指令是一样的,保存EBX的值00498D03 899D 8C114800 MOV DWORD PTR SS: , EBX虽然我们已经执行了大量的垃圾指令,但是还是没有遇到第一条该程序真正要执行的指令,我们只能耐心的继续往下跟。EAX = 0012FFC4 保存到 0047B494中这里00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAX0012FFC4是ESP的初始值,我们要重点关注。我们继续。EAX=1E500000 保存到 0047B088中0047B494 C4 FF 12 00这里我们可以看到12FFC4-ESP寄存器的初始值,现在要将其减去4。0047711C 83AD CCB04700 0> SUB DWORD PTR SS: , 4减去4以后0047B494 C0 FF 12 00现在变成了12FFC0。00496D9A 01 变成了 0000496C9C 03 变成了 0400496D8A 00 变成了 01又是POP指令0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFF0再次将12FFF0保存到0047A448中大家应该还记得吧-还没有执行该程序真正要执行的指令呢,嘿嘿EDI=7C920738 保存到 0047A848中再次0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI; ntdll.7C920738ESI=FFFFFFFF 保存到 0047AC48中这里又是跟之前一样保存ESI的值00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESI继续耐心往下跟踪EDX=0047F3EF 保存到 0047C074中ECX=0047F3EF 保存到 0047BC68中EAX=0047E97E 保存到 0047B860中EBX = 00478304 保存到 00481514中这里00498D03 899D 8C114800 MOV DWORD PTR SS: , EBX; UnPackMe.0047830EAX=0012FFBC 保存到 0047B454中这里我们可以看到是12FFBC-第二个值要被压入到这个地址中EAX=192082C0 保存到 0047B048中我们明显的看出这里实际上是一个循环,继续。EAX=0048F082 保存到 0048191C中EBX=0048F082 保存到 0048191C中00496D8A 01 变成了 0000496C9C 04 变成了 0500496CFB 00 变成了 01又是POP指令0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFF012FFF0保存到0047A20C中EDI=7C920738 保存到 0047A60C中这里又是跟之前一样保存EDI的值ESI = FFFFFFFF 保存到 0047AA0C中哎呀妈呀,仍然没有看到第一条真正要执行的指令。EDX=0047F3EF 保存到 0047BE38中ECX=0047F3EF 保存到 0047BA2C中EAX=0047E97E 保存到 0047B624中诶,我们看到了一点曙光EBX=7FFDB000 保存到 004812D8中再次到了这里00498D03 899D 8C114800 MOV DWORD PTR SS: , EBXEAX=0012FFC0 保存到 0047B218中大家留心点的话会发现这是最后一次循环,之前EAX的值为12FFC4,现在为12FFC0。EAX=1B700602 保存到 0047AE0C中EAX=FFFFFFFF 保存到 0047B624 7E E9 47 00中这个FFFFFFFF也是值得我们关注的,它是要被压入到堆栈中的一个值。00496CFB 01 变成了 0000472B9C 00 变成了 0100472B9C 01 变成了 0000496C9C 05 变成了 0600496CEB 00 变成了 01又是POP指令12FFF0保存到0047A1CC中然后EDI=7C920738 保存到 0047A5CC中ESI=FFFFFFFF 保存到 0047A9CC中EDX=00172CF0 保存到 0047BDF8中ECX=00000012 保存到 0047B9EC中EAX=00472B00 保存到 0047B5E4中还是没有看到真正要执行的第一条指令,我的天。EBX = 7FFDB000 保存到 00481298中EAX = 0012FFC4 保存到 0047B1D8中EAX = 18000500 保存到 0047ADCC中我们对12FFC0设置一个硬件写入断点,看看哪里会调用PUSH EBP向12FFC0进行写入。004923C8 871C24 XCHG DWORD PTR SS: , EBX我们可以看到这一行,从47A1CC中读取12FFF0的值。如果往前翻的话,会发现是通过POP指令将12FFF0保存到0047A1CC中的。12FFF0这个值是我们重点关注的,它是EBP的初始值,这里又通过XCHG DWORD PTR SS:,EBX模拟PUSH EBP执行的结果。接下来,我们会逐步发现真正要执行的指令,嘿嘿下一条真正要执行的指令是:004271B1 8BEC MOV EBP, ESP此时ESP的值为12FFC0,因此执行了MOV EBP,ESP以后,EBP的值也会变成12FFC0。这个12FFC0是通过减4得来的。0047711C 83AD CCB04700 0> SUB DWORD PTR SS: , 40047B1D8 C4 FF 12 00我们可以看到47B1D8中的值变成了12FFC0,不出意外的话,应该是要保存到EBP中的,我猜测接下来可能要执行MOV EBP,ESP。0047B1D8 C0 FF 12 00下面我们对12FFBC设置硬件写入断点,来看看哪里执行了PUSH -1将FFFFFFFF压入到12FFBC中。这里是脱壳修复后的情形。好,我们继续,看看真正要执行的第二条指令在哪里。00496CEB 01 变成了 0000496C9C 06 变成了 0700496CDB 00 变成了 01又是POP指令0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFF0将12FFF0保存到0047A18C中我们继续EDI=7C920738 保存到 0047A58C中ESI=FFFFFFFF 保存到 0047A98C中EDX=00172CF0 保存到 0047BDB8中ECX=00000012 保存到 0047B9AC中EAX=00472B00 保存到 0047B5A4中继续EBX=7FFDB000 保存到 00481258中EAX=12FFc0 保存到 0047B198中循环再次开始EAX=1590F683 保存到 0047AD8C中EAX=004820F6 保存到 00481660中EBX=004820F6 保存到 00481660中我们跟之前一样对堆栈设置硬件写入断点,到了这里00478520 68 7F354700 PUSH 47357F明显是垃圾指令,我们不理会它。00496CDB 01 变成了 00跳过位操作指令00495F2D 6A FF PUSH-1嘿嘿,这里是将FFFFFFFF保存到12FFBC中,这是真正要执行的第二条指令。好,按照这个指令执行的顺序,接下来将是450E60保存到12FFB8中,4292C8保存到12FFB4中,所以我们接着对12FFB8设置硬件写入断点。00496C9C 07 变成了 0800496CCB 00 变成了 01又是POP指令0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFC0这里我每次POP都用蓝色标注出来,方便大家观察每次循环的周期。EDI=7C920738 保存到 0047A54C中这里又是保存EDI的值。0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI; ntdll.7C920738每次循环都会保存EDI的值,现在也会保存ESI的值。ESI=FFFFFFFF 保存到 0047A94C中00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESI这里面有是大量的跳转,入栈操作,还有混淆。其实这些步骤我们完全可能编写脚本来完成,记录每次更新寄存器的指令,有助于我们理解程序的意图。这里又是将ESI的值保存到空闲区域,存放的从上图来看非常的对称。EDX=00172CF0 保存到 0047BD78中ECX=00000012 保存到 0047B96C中这里又是将寄存器的值保存到空白区域,这里保存的是12。 我晕,已经写了这么多页了。EAX=00472B00 保存到 0047B564中EBX=7FFDB000 保存到 00481218中这次是保存EBX的值。00498D03 899D 8C114800 MOV DWORD PTR SS: , EBX排列的依然很整齐 EAX=12FFBC 保存到 0047B158中这里00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAX这些指令执行过程中,ESP的值一直没有变过,我们来看看它什么时候会变化。这里我们可以看到当前的值12FFBC以及之前的值12FFC0。我们继续,又是一轮循环。EAX=1EF00200 保存到 0047AD4C中现在我们又看到了SUB指令了,减去4,接下来应该是保存ESP的值,然后PUSH,嘿嘿。0047711C 83AD CCB04700 0> SUB DWORD PTR SS: , 4这里减去4。变成了这里ESP的值更新了,那么接下来肯定是模拟PUSH指令向堆栈中压入相应的值。00496CCB 01 变成了 0000496C9C 08 变成了 0900496D3B 00 变成了 01又是POP指令0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFC012FFc0 保存到 0047A30C中我们继续0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI; UnPackMe.0049B8C7EDI=0049B8C7 保存到 0047A70C中继续ESI=FFFFFFFF 保存到 0047AB0C中EDX=00172CF0 保存到 0047BF38中ECX=00000012 变成了00000012EAX=00472B00 变成了 0047B724保存EBX的值00498D03 899D 8C114800 MOV DWORD PTR SS: , EBXEBX=7FFDB000 保存到 004813D8中这里我们可以看到ESP的值。00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAXEAX=12ffb4 保存到 0047B318中继续EAX=12A43F15 保存到 0047AF0C中貌似又一轮循环开始了00496D3B 01 变成了 0000496C9C 09 变成了 0a00496D2B 00 变成了 010048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFC0又是POP指令0047A2CC C0 FF 12 00继续变成了12FFC0跟之前一样保存EDI的值。0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI; ntdll.7C920738EDI=7C920738 保存到 0047A6CC 中ESI=FFFFFFFF 保存到 0047AACC中00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESIEDX=00172CF0 保存到 0047BEF8中ECX=0047BEF8 保存到 0047BAEC中这里有可能连EDX,ECX也模拟了。00491254 8995 ECBC4700 MOV DWORD PTR SS: , EDX0049125А 898D E0B84700 MOV DWORD PTR SS: , ECX其实这两个寄存器的值无关紧要,既然我们发现了,还是来看看吧。EAX=00472B00 保存到 0047B6E4中保存EBX的值00498D03 899D 8C114800 MOV DWORD PTR SS: , EBXEBX=7FFDB000 保存到 00481398中保存ESP的值00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAXEAX=0012FFB8 保存到 0047B2D8中又一轮循环EAX=09AFCDC0 保存到 0047AECC中EAX=00493FCD 保存到 004817A0中EBX=00493FCD 保存 004817A0中004965A4 8BB5 C0A84700 MOV ESI, DWORD PTR SS: 这里向ESI中写入内容0047AACC FF FF FF FF00496D2B 01 变成了 0000496C9C 0a 变成了 0b00496D1B 00 变成了 01再次利用POP指令更新EBP的值0048E2C2 8F85 C0A04700 POP DWORD PTR SS: ; 0012FFC0   这里将EDI,ESI恢复为正确的值。00491254 8995 ECBC4700 MOV DWORD PTR SS: , EDX0049125A 898D E0B84700 MOV DWORD PTR SS: , ECX00491260 8985 D8B44700 MOV DWORD PTR SS: , EAX 更新EDX,ECX,EAX接着更新EBX = 7FFDE00000498D03 899D 8C114800 MOV DWORD PTR SS: , EBXESP 变成了 0012FFB800498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAXESP减去40047711C 83AD CCB04700 0> SUB DWORD PTR SS: , 40047B418 B4 FF 12 00接下来将是PUSH 450E6000491254 8995 ECBC4700 MOV DWORD PTR SS: , EDX0049125A 898D E0B84700 MOV DWORD PTR SS: , ECX00491260 8985 D8B44700 MOV DWORD PTR SS: , EAX保存到了这里0048D299 870C24 XCHG DWORD PTR SS: , ECX通过XCHG指令实现了PUSH 450E60的效果,在大量的垃圾中定位到它真心困难。我们继续,关注重点的地方,其他的略过就行了。接下来的原始指令应该是PUSH 4292C8,我们继续:我们可以看到ESP的值保存在EAX中了。从上图中我们可以看到ESP的值在变化,但是变化的很小。之前是12FFB8,现在变为了12FFAC。这里ESP将加4。然后会再减去4。00471718 83AD CCB04700 0> SUB DWORD PTR SS: , 4这里保存的ESI的值不正确,然后是EDI,接着又会恢复。00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESIESI=CB4A9B05好,这里00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAXEAX实际上就是ESP的值为12FFB0。ESI的值继续变化,而且都是不正确的值,很显然是被混淆过的。00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESIESI=F2AFFFFC这里我们可以看到不仅仅使用XCHG指令模拟PUSH了,下面还夹杂着原始指令004820D9 873424 XCHG DWORD PTR SS: , ESI004820DC 64:A1 00000000 MOV EAX, DWORD PTR FS: 我们继续跟踪,会发现来到了代码段。我们继续我们可以看到已经到了OEP附近了。下面我们来尝试设置条件断点,观察各个寄存器的值是如何保存的。00470F82 89B5 C0A84700 MOV DWORD PTR SS: , ESI这里保存的是ESI的值接着的条件断点。接下来是看看哪里保存EDI的值。0047949B 89BD C0A44700 MOV DWORD PTR SS: , EDI照例接着是EBP0048E2C2 8F85 C0A04700 POP DWORD PTR SS: 接下来是ESP00498D0B 8985 CCB04700 MOV DWORD PTR SS: , EAX继续调整条件断点 0049125A这里更新ECX。这里虽然我们不关心EDX的值,还是给它设置条件断点。00491254 8995 ECBC4700 MOV DWORD PTR SS: , EDX接着EBX00498D03 899D 8C114800 MOV DWORD PTR SS:,EBX我们可以看到这里我们可以看到EBP,ECX,EDX,EBX,EDI这些值是相同的,ESP,EAX,ESI的值有些差别。好,那么我们将条件断点的条件换一换,换成ESI==FFFFFFFF试试看。到了这里此时ESI的值为FFFFFFFF。将针对ESI的断点禁用掉。我们继续跟踪到了47CE1E这里。恢复针对ESI的断点,条件依然设置为ESI == FFFFFFFF。这里已经获取到了ESI正确的值。好了,本章结束。这个系列也结束了。
本系列文章汉化版转载看雪论坛
感谢原作者:RicardoNarvaja(西班牙人) 原作者个人主页:http://www.ricardonarvaja.info/
感谢热心翻译的朋友:1~3章译者:BGCoder4~58章译者:安于此生
全集配套程序下载地址:
链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv



逍遥枷锁 发表于 2015-1-20 18:06

真的很快,直接图文教程,效果很好,谢谢,附加程序都打包了,真心给力,恒大辛苦了,谢谢。

Shark恒 发表于 2015-1-20 18:07

逍遥枷锁 发表于 2015-1-20 18:06
真的很快,直接图文教程,效果很好,谢谢,附加程序都打包了,真心给力,恒大辛苦了,谢谢。

都是Word格式,为了大家方便看,一个一个传到论坛。。还那么多图片,真是累死了。弄了一天。。。

逍遥枷锁 发表于 2015-1-20 18:09

Shark恒 发表于 2015-1-20 18:07
都是Word格式,为了大家方便看,一个一个传到论坛。。还那么多图片,真是累死了。弄了一天。。。

真是付出太多啊,忍心观看学习,心中会铭记恒大老师的奉献的,谢谢了,老师有劳你了,工作量实在是大,心痛我矣{:5_116:}

Mrsin 发表于 2015-1-20 18:34

{:6_221:}我被吓到了!

水寒 发表于 2015-1-20 19:16

Desire 发表于 2015-1-20 19:25

{:5_117:}鱼叔叔辛苦了

ooooo尒禁 发表于 2015-1-20 19:35

啥也不管。先顶了再说!

kitessa 发表于 2015-1-20 22:48

反应真快。今天刚更完,你就搬来了。辛苦~

彡墨鱼灬丶 发表于 2015-1-20 23:45

{:5_123:}谢谢鲨鱼叔这么辛苦编辑收集教程
页: [1] 2 3
查看完整版本: 第五十八章-EXECryptor v2.2.50.h脱壳