| 关键词: nbsp ebp eax 一个 push ecx dword CALL ptr 就是 |
目标程序下载地址:http://opencrackmes.crackmes.de/opencrackmes/Collections/keygenning4newbies/Crackmes/k4n.zip 到一些黑客站点上转转,发现那些最受欢迎、点击次数最多的文章都是一些“教菜鸟使用木马”“入侵初级教程”之类文章,而“Outlook Express HTML邮件超长字节href值缓冲溢出缺陷”“PHP fopen()函数易遭到CRLF Injection攻击”这种文章却乏人问津,尽管这或许才是真正高手所关心的。 现在看雪论坛上后一种文章逐渐增多,当然是因为大家的水平都提高了,但也给刚入门的菜鸟带来了麻烦,来了之后什么也看不懂,在高手看来不是问题的问题常常困扰新手们很长时间:为什么这里是关键跳转,那边是重要CALL?为什么有的CALL要跟进,有的CALL只需带过?我按照高手所写的一步步做下去成功了,可为什么要这样做呢?我想更有不少人好不容易破了一个明码比较的软件,想贴出来又怕为高手所不屑而做罢。 因此我找了一个非常简单的CrackMe(如果你是高手,只静态调试应该就可以搞定了:)),从头到尾仔仔细细地写出了分析过程,在里面加入了些我认为应该注意的地方,希望能对刚入门的朋友有些帮助。我不是什么高手,最多算是破解得熟练一点儿了而已,也许这样才更能了解新手们的难处吧。 这里我用W32Dasm作为静态反汇编的工具,各位可能有用C32Asm或者其他的,基本功能其实应该差不多。 先胡乱输入用户名和注册码,点"Check the Serial",看看错误信息是什么(如果出现“注册码正确”的话就别在这儿看了,赶紧买**去:D)好出来了,“This serial is *NOT* Valid!! Try again... : UNREGISTERED”。 好了运行W32Dasm把它反汇编,在串式参考(string reference)中找吧,注意当字串较长时有时不能完全显示,这时只要找前面一段就行了,比如这次就是,找到"This serial is *NOT* Valid!! Try ",就是它了,双击来到引用该字串的地方。(PS:有时错误信息在代码中会不止一次出现,这时只要多次双击就能找到其他的地方。) 下面我要引用代码了(这是在W32DASM里的形式,如果你用OD或SI动态跟踪时形式会略有不同)。不要被这么多代码搞晕了,建议先跳过代码看我的后面的说明,再从代码中对照。 代码:-------------------------------------------------------------------------------- * Possible Reference to Dialog: DialogID_0001, CONTROL_ID:0066, "" | :00401085 6A66 push 00000066 ;输入用户名的文本框ID :00401087 53 push ebx ;对话框句柄 * Reference To: USER32.GetDlgItem, Ord:0000h | :00401088 E8159C0000 Call 0040ACA2 ;得到文本框句柄 :0040108D 6A64 push 00000064 ;得到字符串的最大长度 :0040108F 8D9548FFFFFF lea edx, dword ptr [ebp+FFFFFF48] :00401095 52 push edx ;EDX是存取字符串的地址 :00401096 50 push eax ;EAX是上面得到的文本框句柄 * Reference To: USER32.GetWindowTextA, Ord:0000h | :00401097 E8129C0000 Call 0040ACAE ;得到用户名,在[ebp+FFFFFF48] * Possible Reference to Dialog: DialogID_0001, CONTROL_ID:0068, "" | :0040109C 6A68 push 00000068 ;同样的操作,输入注册码的文本框ID :0040109E 53 push ebx * Reference To: USER32.GetDlgItem, Ord:0000h | :0040109F E8FE9B0000 Call 0040ACA2 :004010A4 6A64 push 00000064 :004010A6 8D8DE4FEFFFF lea ecx, dword ptr [ebp+FFFFFEE4] :004010AC 51 push ecx :004010AD 50 push eax * Reference To: USER32.GetWindowTextA, Ord:0000h | :004010AE E8FB9B0000 Call 0040ACAE ;得到注册码,在[ebp+FFFFFEE4] * Possible Reference to Dialog: DialogID_0001, CONTROL_ID:0067, "" | :004010B3 6A67 push 00000067 ;这个是最下面的提示的文本框的ID :004010B5 53 push ebx 7 * Reference To: USER32.GetDlgItem, Ord:0000h | :004010B6 E8E79B0000 Call 0040ACA2 ;得到句柄 :004010BB 8BF0 mov esi, eax ;放在ESI备用 :004010BD 8D8548FFFFFF lea eax, dword ptr [ebp+FFFFFF48] :004010C3 50 push eax ;指向用户名 :004010C4 E867050000 call 00401630 ;得到用户名长度 :004010C9 59 pop ecx :004010CA 8945D8 mov dword ptr [ebp-28], eax ;长度放在[ebp-28] :004010CD 8D95E4FEFFFF lea edx, dword ptr [ebp+FFFFFEE4] :004010D3 52 push edx ;指向注册码 :004010D4 E857050000 call 00401630 ;得到注册码长度 :004010D9 59 pop ecx :004010DA 68EAB04000 push 0040B0EA :004010DF E84C050000 call 00401630 :004010E4 59 pop ecx :004010E5 680EB14000 push 0040B10E :004010EA E841050000 call 00401630 :004010EF 59 pop ecx :004010F0 837DD803 cmp dword ptr [ebp-28], 00000003 :004010F4 7E7B jle 00401171 ;用户名长度不能小于等于3 :004010F6 90 nop :004010F7 90 nop :004010F8 90 nop :004010F9 90 nop :004010FA 33C9 xor ecx, ecx :004010FC 33D2 xor edx, edx :004010FE 33DB xor ebx, ebx :00401100 33C0 xor eax, eax :00401102 837DD832 cmp dword ptr [ebp-28], 00000032 :00401106 7D69 jge 00401171 ;用户名长度不能大于等于32h :00401108 90 nop :00401109 90 nop :0040110A 90 nop :0040110B 90 nop * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040111C(C) | :0040110C 0FBE840D48FFFFFF movsx eax, byte ptr [ebp+ecx-000000B8];依次取用户名的字符 :00401114 41 inc ecx ;ECX为循环变量 :00401115 33C1 xor eax, ecx ;取的字符与循环变量XOR :00401117 03D8 add ebx, eax ;把结果累加到EBX :00401119 3B4DD8 cmp ecx, dword ptr [ebp-28] ;循环变量与用户名长度相比 :0040111C 75EE jne 0040110C ;如果未取完就跳回继续 :0040111E 6BC006 imul eax, 00000006 ;最后一轮计算的结果在EAX, 乘6 :00401121 C1E307 shl ebx, 07 ;前面累加结果左移7位 :00401124 03C3 add eax, ebx ;相加 :00401126 8945C8 mov dword ptr [ebp-38], eax :00401129 FF75C8 push [ebp-38] ;把上面结果压栈 * Possible StringData Ref from Data Obj ->"%lX" | :0040112C 6838B44000 push 0040B438 ;一个转换的标识 :00401131 8D8D80FEFFFF lea ecx, dword ptr [ebp+FFFFFE80] :00401137 51 push ecx ;存放转换结果的地址 :00401138 E8873D0000 call 00404EC4 ;数字转为十六进制字串 :0040113D 83C40C add esp, 0000000C :00401140 8D8580FEFFFF lea eax, dword ptr [ebp+FFFFFE80] :00401146 50 push eax ;上面转换的字串 :00401147 8D95E4FEFFFF lea edx, dword ptr [ebp+FFFFFEE4] :0040114D 52 push edx ;假注册码 * Reference To: KERNEL32.lstrcmpA, Ord:0000h | :0040114E E8339C0000 Call 0040AD86 ;比较 :00401153 85C0 test eax, eax :00401155 750D jne 00401164 ;这里就是关键的跳转 * Possible StringData Ref from Data Obj ->"Congratulations! IF this number " ->"comes *FROM YOUR* keygen, Write " ->"a tutorial dude ;)." | :00401157 683CB44000 push 0040B43C ;指向表示成功的字符串 :0040115C 56 push esi ;ESI还记得么?那个提示文本框的句柄 * Reference To: USER32.SetWindowTextA, Ord:0000h | :0040115D E8289B0000 Call 0040AC8A ;显示出来 :00401162 EB18 jmp 0040117C * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401155(C) | * Possible StringData Ref from Data Obj ->"This serial is *NOT* Valid!! Try " ->"again... : UNREGISTERED" | :00401164 6890B44000 push 0040B490 ;开始时停在这句,向上找跳转 :00401169 56 push esi ;ESI提示文本框的句柄 * Reference To: USER32.SetWindowTextA, Ord:0000h | :0040116A E81B9B0000 Call 0040AC8A :0040116F EB0B jmp 0040117C * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004010F4(C), :00401106(C) | * Possible StringData Ref from Data Obj ->"Name must contain more than 4 " ->"chars and less than 50 chars !!" | :00401171 68C9B44000 push 0040B4C9 ;用户名不符合要求跳到这里 :00401176 56 push esi ;ESI提示文本框的句柄 * Reference To: USER32.SetWindowTextA, Ord:0000h | :00401177 E80E9B0000 Call 0040AC8A * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00401162(U), :0040116F(U) | :0040117C 5F pop edi :0040117D 5E pop esi :0040117E 5B pop ebx :0040117F 8BE5 mov esp, ebp :00401181 5D pop ebp ;整理一下返回了 :00401182 C3 ret -------------------------------------------------------------------------------- 双击后光标停在401164这一句。很明显,如果我们来到这句时我们就死翘翘了,而如果我们的注册码正确的话当然不会来到这一句(废话太多了:p)那么这一句上面就肯定会有一个条件跳转的指令。(这是找爆破点时的基本思想)向上找找看,找到了: :00401155 750D jne 00401164 正好跳到401164错误信息那一句。呵呵,如果你想爆破的话,只要把750D改成740D(je,把条件反过来,注册码错误就显示正确信息:D)或者改成EB0D(jmp,无条件跳转,不管三七二十一就正确)。 OK,我们不能满足于此啊,咱们看看它的算法是怎样的,也像那些神秘兮兮的高手似的写一个注册机出来。:D 我先给各位补一点课,就是对函数的调用。除了一些DELPHI程序之外,对函数参数的传递大都用堆栈来完成,简单地说就是把函数的各个参数先PUSH进去,然后再CALL这个函数。在函数内部呢,一般[ebp+8]是第一个参数,[ebp+C]是第二个参数,每次多加4依此类推。而函数内部的局部变量常用[ebp-4][ebp-8]...等等。(原因讲起来有点复杂,先记住就行了)函数的返回值在EAX里。 一般来说,软件的判断注册部分都是一个函数,在函数开头最经典的两句就是 push ebp mov ebp,esp 这和堆栈处理有关,我们菜鸟先不用太明白,知道这通常是一个函数的开始就行了。向上找找有没有丫,找到了没有,在最上面哪(我上面没有列出来)。如果你想完整地判断它的算法的话,一般从这里开始就行了。在这个程序中前面都是一些初始化之类的东东,所以我把前面一部分省略了。(这也是破解时的原则,不要在无关紧要的地方费功夫,在高级语言中,代码有很大部分是机器自动生成的,电脑一行行写代码不知道累,人脑一行行读代码怎么受得了?你的脑袋是几GHz的CPU?常见有些没有破解经验的汇编高手,完全懂得每行代码的意思,就是找不到关键的地方,原来他跟了半天都是在API里转,白做无用功了。) 好了,现在可以动态调试了。我们在开头这里下个断点,一步步向下看,出现了一个CALL GetDlgItem,我们来看一看函数说明(手头一份这个是必需的) HWND GetDlgItem( HWND hDlg, // handle of dialog box int nIDDlgItem // identifier of control ); 呵呵,简单的说这个函数就是让程序确定一个对话框上的控件,第一个参数是对话框的句柄,第二个参数是对话框上某个控件的ID,函数会返回该控件的句柄,这样在下面就可以用这个句柄来操作了。看程序: 代码:-------------------------------------------------------------------------------- * Possible Reference to Dialog: DialogID_0001, CONTROL_ID:0066, "" | :00401085 6A66 push 00000066 ;控件ID :00401087 53 push ebx ;对话框句柄 * Reference To: USER32.GetDlgItem, Ord:0000h | :00401088 E8159C0000 Call 0040ACA2 -------------------------------------------------------------------------------- 看见第一行没有,DialogID_0001,CONTROL_ID:0066,压进去了一个66作为第二个参数:控件ID(注意API调用是从右至左,也就是最后面一个参数先PUSH),用 资源查看工具 看看ID为66的是甚么呀,呵呵就是那个输入用户名的文本框嘛。好了,现在我们有文本框的句柄了,存在EAX里。接着向下看,又一个API,是GetWindowText。看看说明: int GetWindowText( HWND hWnd, // handle of window or control with text LPTSTR lpString, // address of buffer for text int nMaxCount // maximum number of characters to copy ); 从名字也能猜出来了,这个函数就是得到一个窗口类控件的文本。第一个参数是该控件的句柄,第二个是存放得到的文本的缓冲区的地址,第三个参数设定取文本的最大长度。看程序: 代码:-------------------------------------------------------------------------------- :0040108D 6A64 push 00000064 ;最大长度 :0040108F 8D9548FFFFFF lea edx, dword ptr [ebp+FFFFFF48];把[ebp+FFFFFF48]先放在EDX里 :00401095 52 push edx ;缓冲区地址[ebp+FFFFFF48] :00401096 50 push eax ;EAX?是上面那个API的返回值呀,控件句柄 * Reference To: USER32.GetWindowTextA, Ord:0000h | :00401097 E8129C0000 Call 0040ACAE -------------------------------------------------------------------------------- 好了,现在 D ebp+FFFFFF48 看看,是不是输入的用户名?(说明,在OLLY或SICE里这个是[ebp-B8]的形式,其实是一样的) 下面有类似的操作,只是控件ID成了68,可想而知就是得到注册码了,注册码放在[ebp+FFFFFEE4]。 呵呵,继续,到了这么几句: 代码:-------------------------------------------------------------------------------- :004010BD 8D8548FFFFFF lea eax, dword ptr [ebp+FFFFFF48] :004010C3 50 push eax :004010C4 E867050000 call 00401630 -------------------------------------------------------------------------------- 这是干甚么牙?要不要进4010C4这个CALL 401630看看?且慢!我前面说过了,不要在无关紧要的地方费力,(啪!一个鸡蛋扔上来:谁知道这里是不是重要啊?)别急嘛,一般来说,先粗略地跟一遍,试着猜猜CALL的作用。如果发现返回值很可疑,再跟进细看也不迟。(这就需要一定的“直觉”了,英文叫“Sence”,台湾老大叫“触机”或者“先死:D”,别担心,破解得多了自然会有这种感觉,这种感觉很难说出来,有时候一见那种阵势就知道关键地方到了) 又扯远了,我们回来看代码。一个CALL,前面有PUSH EAX,很明显是CALL的参数了。(见我前面的说明)看看EAX:lea eax,dword ptr [ebp+FFFFFF48]。[ebp+FFFFFF48]还有没有印象?对了,就是前面API调用中放用户名的地址呀。过了这里后D eax,呵呵不就是咱们的用户名吗,我输入的是“RoBa”,带过这个CALL后看看EAX是什么,嗯~~~~是4。想到什么了?没有?没关系咱们继续看下面: 代码:-------------------------------------------------------------------------------- :004010C9 59 pop ecx :004010CA 8945D8 mov dword ptr [ebp-28], eax ;记住,上面的返回结果在[ebp-28] :004010CD 8D95E4FEFFFF lea edx, dword ptr [ebp+FFFFFEE4] :004010D3 52 push edx :004010D4 E857050000 call 00401630 -------------------------------------------------------------------------------- 又是一个CALL 401630,和上面一样的。看看它的参数EDX,[ebp+FFFFFEE4]呵就是假注册码呀,我输的是'87654321',返回值EAX=8,发现什么没有?对,这个CALL就是求一个字符串的长度嘛,要是跟进去又给费不少时间。向下: 代码:-------------------------------------------------------------------------------- :004010F0 837DD803 cmp dword ptr [ebp-28], 00000003 :004010F4 7E7B jle 00401171 :00401102 837DD832 cmp dword ptr [ebp-28], 00000032 :00401106 7D69 jge 00401171 -------------------------------------------------------------------------------- 还记得[ebp-28]是什么吗?对,就是用户名的长度,这几句的意思就很明显了,用户名的长度必须大于3小于32h,不然就跳到401171去了,你可以跟过去看看是什么,呵呵是用户名不符合要求之类的提示。 好了我们来到关键地方喽: 代码:-------------------------------------------------------------------------------- * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040111C(C) | :0040110C 0FBE840D48FFFFFF movsx eax, byte ptr [ebp+ecx-000000B8] :00401114 41 inc ecx :00401115 33C1 xor eax, ecx :00401117 03D8 add ebx, eax :00401119 3B4DD8 cmp ecx, dword ptr [ebp-28] :0040111C 75EE jne 0040110C -------------------------------------------------------------------------------- 呵呵,是不是又晕了?[ebp+ecx-B8]这是啥呀?别着急,车到山前必有路。先 d ebp+ecx-B8看看,哟,不还是咱们的用户名吗?:D 怎么回事?好好想想,ecx现在是0,[ebp-B8]和[ebp+FFFFFF48](记得不?放用户名的地址)不是一回事嘛!(不明白的去补习一下负数的表示方法,我也不知道为什么W32DASM有时候非把-B8写成FFFFFF48) 再仔细看看,movsx eax,byte ptr [ebp+ecx-000000B8],注意是byte ptr,即以字节的方式读取(就是说每次读出一个字符),而且又加上了个ecx。如果你破解多了的话,应该立刻就明白:关键的地方到了。 这是一个很典型的循环结构,看出来没?ecx就是循环变量了,每执行一次会从用户名中取一个字符,然后ecx加1,这样[ebp+ecx-B8]就指向用户名的下一个字符了。对取出来的字符与循环变量进行XOR运算,把结果累加到EBX。然后循环变量与[ebp-28]也就是用户名长度比较,如果不等于的话也就是还没取完,就返回上去继续取用户名的下一个字符。这样直到取完为止。 代码:-------------------------------------------------------------------------------- :0040111E 6BC006 imul eax, 00000006 ;EAX其实是上面最后一轮计算的结果,乘6 :00401121 C1E307 shl ebx, 07 ;EBX是几轮计算累加起来的结果,左移7位 :00401124 03C3 add eax, ebx ;加起来 :00401126 8945C8 mov dword ptr [ebp-38], eax :00401129 FF75C8 push [ebp-38] ;上面的结果,作为一个参数 * Possible StringData Ref from Data Obj ->"%lX" | :0040112C 6838B44000 push 0040B438 ;"%lX"有点眼熟哟 :00401131 8D8D80FEFFFF lea ecx, dword ptr [ebp+FFFFFE80] :00401137 51 push ecx ;这是什么呢? :00401138 E8873D0000 call 00404EC4 :0040113D 83C40C add esp, 0000000C -------------------------------------------------------------------------------- 前面几句是继续上面的计算,把EAX*6和EBX左移7位的值加进来,然后结果复制到[ebp-38]这个局部变量里作为下面CALL的一个参数,接着[ebp+FFFFFE80]作第二个参数,然后又一个CALL,还是那样,先别着急跟进去,前后看看有没有可疑之处: 第一个参数[ebp-38],没什么好说的,上面计算的结果。第二个参数指向"%lX",这个字串写过C语言的都有印象吧,就是在printf里把一个数字按照大写的16进制方式显示出来所用的标识符,比方说把一个数字255转换成字串“FF”。(在WinAPI里叫wsprintf)第三个参数是什么呢?前面这种形式见多了吧,猜猜! 我们验证一下前面猜想是否正确,我用"RoBa"上面算出来的结果是46430,也就是16进制的B55E,我们跟过这个CALL看看结果如何?什么,结果在哪?聪明的你还没想到吗,上面那第三个参数[ebp+FFFFFE80]就是结果的地址呀。怎么样不出所料吧。 (其实从40113D这一句add esp,C也能看出来,因为在函数外平衡堆栈的只有这个一个参数数目不定的函数。引申一下,看不懂没关系啦:D ) 接着看啦: 代码:-------------------------------------------------------------------------------- :00401140 8D8580FEFFFF lea eax, dword ptr [ebp+FFFFFE80];眼熟吗,刚才的转换结果呀 :00401146 50 push eax ;EAX指向上面转换得到的字符串 :00401147 8D95E4FEFFFF lea edx, dword ptr [ebp+FFFFFEE4];这个很早了,向前面找找是啥 :0040114D 52 push edx ;EDX指向我们输入的假注册码 * Reference To: KERNEL32.lstrcmpA, Ord:0000h | :0040114E E8339C0000 Call 0040AD86 :00401153 85C0 test eax, eax :00401155 750D jne 00401164 ;关键跳转哟 -------------------------------------------------------------------------------- 哈哈,lstrcmp,什么意思不用我说了吧,当然是STRingCoMPare字符串比较啦。把计算的结果与前面输入的假码比较,相等就OK了。好了,现在把上面的完整的代码过一遍,怎么样,写个注册机不难吧? 代码:-------------------------------------------------------------------------------- #include <string.h> #include <stdio.h> #include <stdlib.h> void main() { int EAX=0,EBX=0,len; char name[50]={0}; char password[50]={0}; printf("Please input your name:"); scanf("%s",name); len=strlen(name); for (int i=0;i<len;i++) { EAX=name[i]^(i+1); EBX+=EAX; } EAX*=6; EBX<<=7; EAX+=EBX; printf("Your password is: %lX\n",EAX); printf("KeyGen by RoBa Enjoy Cracking,Newbies!\n"); } -------------------------------------------------------------------------------- 简单的C程序哟,你也可以用你熟悉的语言写一个。 后记: 这是一个简单的CRACKME,本来三言两语就能解决的,被我??嗦嗦说了这么一大堆,就是希望能把问题真正地说明白了,希望通过这篇文章让你发现,破解软件乃至写注册机并不是多么困难的事,只要坚持下去,谁都有成为高手的那一天,也希望高手们能够抽时间发一些适合较低水平的文章或者至少对发这种文章的新手多多鼓励。Enjoy Cracking,Newbies! -------------------------------------------------------------------------------- routty2004-12-26, 16:38 NO2: 作者:RoBa 教菜鸟写注册机――中级篇 上次那篇破文(不是破解文章,是破烂文章)丢了,我补在这贴的后面。这次说是中级,其实只难了一点点而已,cmp You,高手 jz offset NextPage。 好了,再来一篇。还是那个系列的,下载地址: http://opencrackmes.crackmes.de/opencrackmes/Collections/keygenning4newbies/Crackmes/k4n2.zip 运行一下,呵呵,外观一模一样。反汇编,前面的部分几乎完全一样,GetDlgItem,GetWindowText,我都不写了,直接看下面,注意[ebp-2C]是用户名的长度。(可以先跳过去看后面的说明。) 代码:-------------------------------------------------------------------------------- :004010ED 837DD403 cmp dword ptr [ebp-2C], 00000003 :004010F1 0F8E38010000 jle 0040122F ;用户名必须大于3位 :004010F7 33D2 xor edx, edx :004010F9 33DB xor ebx, ebx :004010FB 8B55D4 mov edx, dword ptr [ebp-2C] :004010FE 0155C4 add dword ptr [ebp-3C], edx :00401101 0155C4 add dword ptr [ebp-3C], edx ;算出[EBP-3C] :00401104 8BC2 mov eax, edx :00401106 83C005 add eax, 00000005 :00401109 8945B8 mov dword ptr [ebp-48], eax ;算出[EBP-48] :0040110C 33C0 xor eax, eax :0040110E 8BCF mov ecx, edi :00401110 83C104 add ecx, 00000004 :00401113 894DB4 mov dword ptr [ebp-4C], ecx ;算出[EBP-4C] :00401116 33C9 xor ecx, ecx :00401118 0155BC add dword ptr [ebp-44], edx :0040111B 017DBC add dword ptr [ebp-44], edi ;算出[EBP-44] :0040111E 6BFF03 imul edi, 00000003 :00401121 897DC0 mov dword ptr [ebp-40], edi ;算出[EBP-40] :00401124 33FF xor edi, edi :00401126 0FBE8C0544FFFFFF movsx ecx, byte ptr [ebp+eax-000000BC] :0040112E 83F961 cmp ecx, 00000061 :00401131 7C07 jl 0040113A :00401133 90 nop :00401134 90 nop :00401135 90 nop :00401136 90 nop :00401137 83E920 sub ecx, 00000020 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401131(C) | :0040113A 8BF1 mov esi, ecx :0040113C 03DE add ebx, esi :0040113E 0FAFD9 imul ebx, ecx :00401141 4A dec edx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401178(C) | :00401142 0FBE8C2F44FFFFFF movsx ecx, byte ptr [edi+ebp-000000BC] :0040114A 0FBEB42F45FFFFFF movsx esi, byte ptr [edi+ebp-000000BB] :00401152 83F961 cmp ecx, 00000061 :00401155 7D12 jge 00401169 :00401157 90 nop :00401158 90 nop :00401159 90 nop :0040115A 90 nop * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040116C(U) | :0040115B 83FE61 cmp esi, 00000061 :0040115E 7D0E jge 0040116E :00401160 90 nop :00401161 90 nop :00401162 90 nop :00401163 90 nop :00401164 EB0B jmp 00401171 :00401166 90 nop :00401167 90 nop :00401168 90 nop * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401155(C) | :00401169 83E920 sub ecx, 00000020 :0040116C EBED jmp 0040115B * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040115E(C) | :0040116E 83EE20 sub esi, 00000020 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401164(U) | :00401171 47 inc edi :00401172 03DE add ebx, esi :00401174 0FAFD9 imul ebx, ecx :00401177 4A dec edx :00401178 75C8 jne 00401142 :0040117A 895DC8 mov dword ptr [ebp-38], ebx ;算出[EBP-38] :0040117D 33C9 xor ecx, ecx :0040117F 33D2 xor edx, edx :00401181 33DB xor ebx, ebx :00401183 33C0 xor eax, eax :00401185 837DD432 cmp dword ptr [ebp-2C], 00000032 :00401189 0F8DA0000000 jnl 0040122F * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040119F(C) | :0040118F 0FBE840D44FFFFFF movsx eax, byte ptr [ebp+ecx-000000BC] :00401197 03C1 add eax, ecx :00401199 03D8 add ebx, eax :0040119B 41 inc ecx :0040119C 3B4DD4 cmp ecx, dword ptr [ebp-2C] :0040119F 75EE jne 0040118F :004011A1 D1C0 rol eax, 1 :004011A3 3540E20100 xor eax, 0001E240 :004011A8 8945B0 mov dword ptr [ebp-50], eax ;算出[EBP-50] :004011AB 33C9 xor ecx, ecx :004011AD 33D2 xor edx, edx :004011AF 33DB xor ebx, ebx :004011B1 33C0 xor eax, eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004011C6(C) | :004011B3 0FBE840D44FFFFFF movsx eax, byte ptr [ebp+ecx-000000BC] :004011BB 6BD006 imul edx, eax, 00000006 :004011BE 33C2 xor eax, edx :004011C0 03D8 add ebx, eax :004011C2 41 inc ecx :004011C3 3B4DD4 cmp ecx, dword ptr [ebp-2C] :004011C6 75EB jne 004011B3 :004011C8 035DB0 add ebx, dword ptr [ebp-50] :004011CB 895DAC mov dword ptr [ebp-54], ebx ;算出[EBP-54] :004011CE FF75C0 push [ebp-40] :004011D1 FF75C4 push [ebp-3C] :004011D4 FF75BC push [ebp-44] :004011D7 FF75C8 push [ebp-38] :004011DA FF75B4 push [ebp-4C] :004011DD FF75B8 push [ebp-48] :004011E0 FF75AC push [ebp-54] :004011E3 FF75B0 push [ebp-50] * Possible StringData Ref from Data Obj ->"%lX%lu-%lu%lX-%lu%lu-%lX%lX" | :004011E6 6838B44000 push 0040B438 :004011EB 8D857CFEFFFF lea eax, dword ptr [ebp+FFFFFE7C] :004011F1 50 push eax :004011F2 E88D3D0000 call 00404F84 ;wsprinf() :004011F7 83C428 add esp, 00000028 :004011FA 8D957CFEFFFF lea edx, dword ptr [ebp+FFFFFE7C] :00401200 52 push edx :00401201 8D8DE0FEFFFF lea ecx, dword ptr [ebp+FFFFFEE0] :00401207 51 push ecx * Reference To: KERNEL32.lstrcmpA, Ord:0000h | :00401208 E8399C0000 Call 0040AE46 ;比较 :0040120D 85C0 test eax, eax :0040120F 750F jne 00401220 ;关键跳转 -------------------------------------------------------------------------------- 用我上篇文章介绍的方法找串式参考,然后向上找关键跳转。具体过程不说了,看看你是不是找的到。对了,就是40120F这个地方了。向上看看,有一个lstrcmp,上次已经说了,这个就是字符串比较。可以看到它的前面有两个PUSH作为比较的字符串,在这里下断点,看看两个字串是什么?D ecx,是我们输入的假注册码,D edx,是一个长长的字符串,当然就是真正的注册码啦。嗯,再向上看这个注册码是咋来的: 代码:-------------------------------------------------------------------------------- :004011CE FF75C0 push [ebp-40] :004011D1 FF75C4 push [ebp-3C] :004011D4 FF75BC push [ebp-44] :004011D7 FF75C8 push [ebp-38] :004011DA FF75B4 push [ebp-4C] :004011DD FF75B8 push [ebp-48] :004011E0 FF75AC push [ebp-54] :004011E3 FF75B0 push [ebp-50] * Possible StringData Ref from Data Obj ->"%lX%lu-%lu%lX-%lu%lu-%lX%lX" | :004011E6 6838B44000 push 0040B438 :004011EB 8D857CFEFFFF lea eax, dword ptr [ebp+FFFFFE7C] :004011F1 50 push eax ;结果存在[ebp+FFFFFE7C] :004011F2 E88D3D0000 call 00404F84 ;这个CALL其实是wsprinf :004011F7 83C428 add esp, 00000028 -------------------------------------------------------------------------------- 呵呵,一个"%lX%lu-%lu%lX-%lu%lu-%lX%lX"。还记得上次的例子吗,那回是一个"%lX",这回复杂了一些哟。别担心,还是很简单的。上次说了,"%lX"是十六进制的大写形式,那么"%lu"呢,就是普通的十进制形式啦。再看前面PUSH进了一堆参数,这些[ebp-xx]的形式都是函数里面的局部变量,在这里把它们以不同的形式表示出来再组合好,就是真正的注册码了。下一步的目标,当然就是看这8个变量是如何计算出的啦。记住这几个变量都是什么.从头看: 代码:-------------------------------------------------------------------------------- :004010FB 8B55D4 mov edx, dword ptr [ebp-2C] ;edx=[ebp-2C]是用户名的长度n :004010FE 0155C4 add dword ptr [ebp-3C], edx ;[ebp-3C]可是一个重要变量, :00401101 0155C4 add dword ptr [ebp-3C], edx ;[ebp-3C]=2n,看出来没有 :00401104 8BC2 mov eax, edx :00401106 83C005 add eax, 00000005 :00401109 8945B8 mov dword ptr [ebp-48], eax ;[ebp-48]=n+5,也是一个重要变量 :0040110C 33C0 xor eax, eax :0040110E 8BCF mov ecx, edi ;edi是一个常数64F4F0 :00401110 83C104 add ecx, 00000004 :00401113 894DB4 mov dword ptr [ebp-4C], ecx ;[ebp-4C]=64f4f4 :00401116 33C9 xor ecx, ecx :00401118 0155BC add dword ptr [ebp-44], edx :0040111B 017DBC add dword ptr [ebp-44], edi ;[ebp-44]=64f4f0+n :0040111E 6BFF03 imul edi, 00000003 :00401121 897DC0 mov dword ptr [ebp-40], edi ;[ebp-40]=64f4f0*3 -------------------------------------------------------------------------------- 这几个计算都比较简单的,有5个变量已经被搞定了。其中那个EDI我实在没看明白和我们的输入有什么关系,我改动用户名和注册码它也不会变化,因此我认为这是一个常量,如果不对请高手指正。 代码:-------------------------------------------------------------------------------- :00401126 0FBE8C0544FFFFFF movsx ecx, byte ptr [ebp+eax-BC];[EBP-BC]是用户名,EAX作为指针 :0040112E 83F961 cmp ecx, 00000061 :00401131 7C07 jl 0040113A ;如果小于61即'a'就跳转 :00401137 83E920 sub ecx, 00000020 ;如果大于就减20,对于字母来说是小写转大写 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401131(C) | :0040113A 8BF1 mov esi, ecx ;ECX为用户名第一个字符 :0040113C 03DE add ebx, esi ;EBX=ECX :0040113E 0FAFD9 imul ebx, ecx ;实际上EBX=ECX*ECX :00401141 4A dec edx ;EDX为循环变量减1 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401178(C) | :00401142 0FBE8C2F44FFFFFF movsx ecx, byte ptr [edi+ebp-000000BC];前一个字符 :0040114A 0FBEB42F45FFFFFF movsx esi, byte ptr [edi+ebp-000000BB];后一个字符 ;EDI也是控制取字符的指针,这里相当于每次取出两个字符,前一个放在ECX,后一个放在ESI :00401152 83F961 cmp ecx, 00000061 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040116C(U) | :0040115B 83FE61 cmp esi, 00000061 :0040115E 7D0E jge 0040116E ;这里对字符进行同样的转换 :00401164 EB0B jmp 00401171 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401155(C) | :00401169 83E920 sub ecx, 00000020 :0040116C EBED jmp 0040115B * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040115E(C) | :0040116E 83EE20 sub esi, 00000020 :00401171 47 inc edi ;EDI这个指针+1 :00401172 03DE add ebx, esi ;EBX是累加的结果,再加上后一个字符 :00401174 0FAFD9 imul ebx, ecx ;再乘上前一个字符 :00401177 4A dec edx :00401178 75C8 jne 00401142 ;是否取完? :0040117A 895DC8 mov dword ptr [ebp-38], ebx ;累加结果存在[ebp-38] -------------------------------------------------------------------------------- 这段麻烦一些,看不明白的话看我的注册机代码就清楚了.来看下面一段: 代码:-------------------------------------------------------------------------------- :0040118F 0FBE840D44FFFFFF movsx eax, byte ptr [ebp+ecx-000000BC];循环取字符 :00401197 03C1 add eax, ecx ;EAX=每位字符+ECX :00401199 03D8 add ebx, eax ;累加到EBX :0040119B 41 inc ecx ;循环变量递增 :0040119C 3B4DD4 cmp ecx, dword ptr [ebp-2C] :0040119F 75EE jne 0040118F ;如果未取完则继续 :004011A1 D1C0 rol eax, 1 ;EAX左移1位 :004011A3 3540E20100 xor eax, 0001E240 ;EAX XOR 1E240 :004011A8 8945B0 mov dword ptr [ebp-50], eax -------------------------------------------------------------------------------- 相信你已经对这个形式很熟悉了吧,[EBP-BC]这是用户名,然后用一个ECX循环递增来取每个字符,再看看:add eax,ecx / add ebx,eax 好像是把每位字符的值再和字符的位置(ECX)累加起来的,呵呵,作者开了个小玩笑。看看下面的操作,都是对EAX进行的,可是累加的结果是放在EBX中呀,其实EAX是用户名的最后一个字符加上用户的长度。要说下的是ROL,这个本来是“滚动”,但因为EAX肯定很小,最高位为0,所以在注册机中我简单的用左移SHL代替了。计算结果放在[ebp-50]。 代码:-------------------------------------------------------------------------------- :004011B3 0FBE840D44FFFFFF movsx eax, byte ptr [ebp+ecx-000000BC] :004011BB 6BD006 imul edx, eax, 00000006 :004011BE 33C2 xor eax, edx :004011C0 03D8 add ebx, eax :004011C2 41 inc ecx :004011C3 3B4DD4 cmp ecx, dword ptr [ebp-2C] :004011C6 75EB jne 004011B3 :004011C8 035DB0 add ebx, dword ptr [ebp-50] :004011CB 895DAC mov dword ptr [ebp-54], ebx -------------------------------------------------------------------------------- 这一段不写说明了,当作测验,应该看懂了吧,算出结果放在[ebp-54]。 至此八个变量都出来了,注册机也容易了吧。 代码:-------------------------------------------------------------------------------- #include <string.h> #include <stdio.h> #include <stdlib.h> void main() { int len,i; int EBP_40,EBP_3C,EBP_44,EBP_38,EBP_4C,EBP_48,EBP_54,EBP_50; int EDI=0x64F4F0; char name[50]={0}; printf("Please input your name:"); scanf("%s",name); len=strlen(name); EBP_3C=len*2; EBP_48=len+5; EBP_4C=EDI+4; EBP_44=EDI+len; EBP_40=EDI*3; EBP_50=((len-1+name[len-1])<<1); EBP_50^=0x1E240; EBP_54=0; for (i=0;i<len;i++) EBP_54+=((name[i]*6)^name[i]); EBP_54+=EBP_50; for (i=0;i<len;i++) if (name[i]>='a') name[i]-=0x20; EBP_38=name[0]*name[0]; for (i=1;i<len;i++) EBP_38=(EBP_38+name[i])*name[i-1]; printf("Your password is: %lX%lu-%lu%lX-%lu%lu-%lX%lX\n", EBP_50,EBP_54,EBP_48,EBP_4C,EBP_38,EBP_44,EBP_3C,EBP_40); printf("KeyGen by RoBa Enjoy Cracking!\n"); } -------------------------------------------------------------------------------- 一个可用的注册码: Name: RoBa Serial: 1E288125744-964F4F4-29089574586616308-812EDED0 _____________________________@ -------------------------------------------------------------------------------- routty2004-12-26, 16:40 NO3: 作者:RoBa 教菜鸟写注册机――高级篇(注意我这里说的高级只是对偶辈菜鸟来说是难一些) HEYA,我又来灌喽!还是那个系列的第3位CRACKME。下载: http://opencrackmes.crackmes.de/opencrackmes/Collections/keygenning4newbies/Crackmes/k4n3.zip 用W32DASM来反,(可以先看后面说明) 代码:-------------------------------------------------------------------------------- :004011BF 6A45 push 00000045 :004011C1 50 push eax :004011C2 A4 movsb * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :004011C3 8B3DA8404000 mov edi, dword ptr [004040A8] ;注意这里把地址放在EDI * Possible Reference to Dialog: DialogID_0065, CONTROL_ID:03E8, "" | :004011C9 68E8030000 push 000003E8 :004011CE 51 push ecx :004011CF FFD7 call edi ;实际是CALL GetDlgItemTextA,得到用户名 :004011D1 8BF0 mov esi, eax :004011D3 85F6 test esi, esi :004011D5 0F844B010000 je 00401326 :004011DB 83FE40 cmp esi, 00000040 :004011DE 0F8742010000 ja 00401326 :004011E4 8B4508 mov eax, dword ptr [ebp+08] :004011E7 8D5594 lea edx, dword ptr [ebp-6C] :004011EA 6A13 push 00000013 :004011EC 52 push edx * Possible Reference to Dialog: DialogID_0065, CONTROL_ID:03E9, "" | :004011ED 68E9030000 push 000003E9 :004011F2 50 push eax :004011F3 FFD7 call edi ;再次调用GetDlgItemText,得到注册码 :004011F5 6BC003 imul eax, 00000003 ;EAX是注册码的长度 :004011F8 C1E002 shl eax, 02 ;左移二位 :004011FB 05CD000000 add eax, 000000CD ;加上0CD :00401200 8945FC mov dword ptr [ebp-04], eax :00401203 817DFCA5010000 cmp dword ptr [ebp-04], 000001A5;看看计算结果是不是1A5 :0040120A 0F85BC000000 jne 004012CC ;不是就死,可以逆算出(1A5-0CD)>>2=12 :00401210 33C0 xor eax, eax ;即注册码不能小于12h位 :00401212 8A4594 mov al, byte ptr [ebp-6C] :00401215 84C0 test al, al :00401217 7413 je 0040122C :00401219 8D4D94 lea ecx, dword ptr [ebp-6C] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040122A(C) | :0040121C 3C30 cmp al, 30 :0040121E 0F82C6000000 jb 004012EA ;注册码每位不能小于30h,即'0' :00401224 8A4101 mov al, byte ptr [ecx+01] :00401227 41 inc ecx :00401228 84C0 test al, al :0040122A 75F0 jne 0040121C * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401217(C) | :0040122C E8CFFDFFFF call 00401000 ;这是什么呀?好像很重要哟,进去看看! :00401231 8D852CFFFFFF lea eax, dword ptr [ebp+FFFFFF2C] :00401237 50 push eax :00401238 E843FEFFFF call 00401080 ;转换过程一,跟进 :0040123D 8945FC mov dword ptr [ebp-04], eax :00401240 E8BBFDFFFF call 00401000 ;还进去不?呀....别打我! :00401245 8D8D2CFFFFFF lea ecx, dword ptr [ebp+FFFFFF2C] :0040124B 56 push esi :0040124C 51 push ecx :0040124D E8BEFDFFFF call 00401010 :00401252 83C40C add esp, 0000000C :00401255 33C9 xor ecx, ecx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401284(C) | :00401257 8B45FC mov eax, dword ptr [ebp-04] \ :0040125A 33D2 xor edx, edx | :0040125C BE1A000000 mov esi, 0000001A | :00401261 F7F6 div esi | :00401263 8A941510FFFFFF mov dl, byte ptr [ebp+edx-000000F0] :0040126A 88540DC8 mov byte ptr [ebp+ecx-38], dl| :0040126E 8B45FC mov eax, dword ptr [ebp-04] | :00401271 C1E003 shl eax, 03 |---转换过程二 :00401274 BA45230100 mov edx, 00012345 | :00401279 F7E8 imul eax | :0040127B 03C2 add eax, edx | :0040127D 8945FC mov dword ptr [ebp-04], eax | :00401280 41 inc ecx | :00401281 83F912 cmp ecx, 00000012 | :00401284 72D1 jb 00401257 / :00401286 E875FDFFFF call 00401000 :0040128B 33C0 xor eax, eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004012A2(C) | :0040128D 8A4C0594 mov cl, byte ptr [ebp+eax-6C] \ :00401291 8A5405C8 mov dl, byte ptr [ebp+eax-38] | :00401295 80E930 sub cl, 30 | :00401298 32D1 xor dl, cl |---转换过程三 :0040129A 885405C8 mov byte ptr [ebp+eax-38], dl | :0040129E 40 inc eax | :0040129F 83F812 cmp eax, 00000012 | :004012A2 72E9 jb 0040128D / :004012A4 E857FDFFFF call 00401000 :004012A9 8D55C8 lea edx, dword ptr [ebp-38] :004012AC 52 push edx :004012AD E85EFEFFFF call 00401110 ;转换过程四 :004012B2 E849FDFFFF call 00401000 :004012B7 8D45C8 lea eax, dword ptr [ebp-38] * Possible StringData Ref from Data Obj ->"KEYGENNING4NEWBIES" | :004012BA 6814514000 push 00405114 ;固定字串"KEYGENNING4NEWBIES" :004012BF 50 push eax ;上面转换而来的字串 :004012C0 E86BFEFFFF call 00401130 ;进行比较 :004012C5 83C40C add esp, 0000000C :004012C8 85C0 test eax, eax :004012CA 753C jne 00401308 ;关键跳转 -------------------------------------------------------------------------------- 找串式参考,然后向上很容易找到关键跳转。可以发现是对EAX进行判断,在一般情况下这EAX就是上面CALL的返回值了。再看CALL上面有两个PUSH,猜猜是什么?应该是对字符串比较吧。在上面就可以看出405114是一个固定的字串"KEYGENNIG4NEWBIES",动态跟一跟看EAX是啥,果然,也是一个同样长度的字符串。这时几乎可以肯定这个EAX就是我们的用户名和注册码进行某种运算产生的结果。那么我们就跟一跟看: 从前面的方法找到这个函数的开头,开头还是一些初始化的东东,向下看,可以找到一个API:GetDlgItemText这个API从名字猜猜看,就是我们介绍过的GetDlgItem和GetWindowText和“合体” 具体说明请查资料,我不写了。值得一提的是程序把这个CALL的地址先放在EDI里,以后每次调用时只用CALL EDI就行了,这是多次调用同一API时翻译器的优化方法,注意! 调用这个API后返回值是得到的文本的长度,然后在后面对注册码的长度进行一些计算(注释在上面),我们可以反算出注册码的长度为12h位(多于12h位也可以,因为只取前12h位)。 然后就是一个CALL 401000,我直觉感到这是一个重要的CALL,结果跟进去一看:!@#$%,只是把几个寄存器清零了,这告诉我们:直觉会有出错的时候,代码才是检验真理的唯一标准。 下面一个CALL 401080,在它前面压入了一个参数正是我们的用户名,我直觉感到这个CALL有问题。跟进看看,这回对了啦: 代码:-------------------------------------------------------------------------------- * Referenced by a CALL at Address: |:00401238 | :00401080 55 push ebp :00401081 8BEC mov ebp, esp :00401083 51 push ecx :00401084 53 push ebx :00401085 56 push esi :00401086 57 push edi * Possible StringData Ref from Data Obj ->"eheh" | :00401087 6880504000 push 00405080 :0040108C 6A00 push 00000000 :0040108E E8ADFFFFFF call 00401040 ;这个CALL有问题 :00401093 83C408 add esp, 00000008 :00401096 8BD8 mov ebx, eax :00401098 E863FFFFFF call 00401000 * Possible StringData Ref from Data Obj ->" is a whore." | :0040109D BF70504000 mov edi, 00405070 :004010A2 83C9FF or ecx, FFFFFFFF :004010A5 33C0 xor eax, eax :004010A7 F2 repnz :004010A8 AE scasb :004010A9 F7D1 not ecx :004010AB 2BF9 sub edi, ecx :004010AD 8BF7 mov esi, edi :004010AF 8B7D08 mov edi, dword ptr [ebp+08] :004010B2 8BD1 mov edx, ecx :004010B4 83C9FF or ecx, FFFFFFFF :004010B7 F2 repnz :004010B8 AE scasb :004010B9 8BCA mov ecx, edx :004010BB 4F dec edi :004010BC C1E902 shr ecx, 02 :004010BF F3 repz :004010C0 A5 movsd :004010C1 8BCA mov ecx, edx :004010C3 83E103 and ecx, 00000003 :004010C6 F3 repz :004010C7 A4 movsb :004010C8 33FF xor edi, edi :004010CA 33F6 xor esi, esi ;上面这一段是不是有点晕,没关系,只看结果 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004010F6(C) | :004010CC 8B4508 mov eax, dword ptr [ebp+08] :004010CF 50 push eax ;在这里D eax看看是什么,RoBa is a whore. :004010D0 56 push esi ;ESI每次加4,从第ESI个字符开始取值 :004010D1 E86AFFFFFF call 00401040 ;还是上面的CALL :004010D6 8B8E30504000 mov ecx, dword ptr [esi+00405030];这也是一个表,从里面取值 :004010DC 83C408 add esp, 00000008 :004010DF 33CF xor ecx, edi :004010E1 03C1 add eax, ecx :004010E3 8945FC mov dword ptr [ebp-04], eax :004010E6 C145FC07 rol dword ptr [ebp-04], 07 ;ROL是一个“滚动”移位 :004010EA 8B45FC mov eax, dword ptr [ebp-04] :004010ED 83C604 add esi, 00000004 :004010F0 33D8 xor ebx, eax ;进行一些运算,EBX最初是"eheh"=68656865 :004010F2 47 inc edi :004010F3 83FE40 cmp esi, 00000040 ;ESI每次加4,所以这是计算10H=16次 :004010F6 7CD4 jl 004010CC ;这里是循环计算 :004010F8 5F pop edi :004010F9 8BC3 mov eax, ebx :004010FB 5E pop esi :004010FC 5B pop ebx :004010FD 8BE5 mov esp, ebp :004010FF 5D pop ebp :00401100 C3 ret -------------------------------------------------------------------------------- 呵呵,别害怕,这是整个程序最复杂的一处运算,过了这儿就简单了 一上来压入了一个"eheh",一个0,然后一个CALL 401040,我跟进。。。。别着急,先带过看返回值是什么?嗯,EAX=68656865。作了这么多练习了,你的“先死”练出来了么?这个EAX怎么这么别扭,就是"eheh"倒过来了呀。'e'=65h,'h'=68h。当然如果没有看出来,跟进一下更保险了。 再向下,一个字串" is a whore."(whore什么意思牙,我英语太菜)接下来就是倒来倒去。记住repnz scasb/ repnz movsd这样的计算经常是字符串的计算。我们不算三七二十一先走过这一段看看结果如何。走到4010CF时下D eax。呵呵,是"RoBa is a whore."(到底是什么意思?不是骂我的吧)。可见上面那一段就是把用户名接上一个" is a whore." 所以,看不懂的计算不妨先跳过去,往往结果却非常明了。 接下来就是一个循环。又用了CALL 401040。这次是从字串"RoBa is a whore."里取值,而第二个参数ESI每次加4,可以看出它是控制从第几个字符开始取。从循环的最后可以看出要算10h次。 每次从字串中取出四个字符放在EAX中后,再从[405030]这个表里从一个值,进行一些计算,把结果与EBX进行XOR,(EBX最初是上面得到的68656865)。这个CALL返回的值就是10H次计算后的EBX。(这一段较乱,也不太好说明,亲自跟一下就会明白,有疑问请看我的注册机是如何实现的。或者直接找我啦) 好了,我们出来喽,往下走,把返回放在[EBP-4],清空一些寄存器,又来到一个循环。(过程二) 代码:-------------------------------------------------------------------------------- :00401257 8B45FC mov eax, dword ptr [ebp-04] ;最初这里是上面过程一的结果 :0040125A 33D2 xor edx, edx :0040125C BE1A000000 mov esi, 0000001A :00401261 F7F6 div esi ;除以1Ah=26 :00401263 8A941510FFFFFF mov dl, byte ptr [ebp+edx-000000F0];根据余数从表中取值 :0040126A 88540DC8 mov byte ptr [ebp+ecx-38], dl ;把取得的值组成一个字串 :0040126E 8B45FC mov eax, dword ptr [ebp-04] :00401271 C1E003 shl eax, 03 :00401274 BA45230100 mov edx, 00012345 :00401279 F7E8 imul eax :0040127B 03C2 add eax, edx ;进行一些计算,准备下次再取 :0040127D 8945FC mov dword ptr [ebp-04], eax :00401280 41 inc ecx ;ECX是循环变量 :00401281 83F912 cmp ecx, 00000012 ;共计算12h=18次 :00401284 72D1 jb 00401257 -------------------------------------------------------------------------------- 先讲下DIV除法运算。它把商放在EAX,余数放在EDX。在这里我们可以看到除以1Ah即26。[ebp-F0]看看是啥,哈哈是一个字符表"ABCDEFGHIJKLMNOPQRSTUVWXYZ"。根据除得的余数从这个表中取出一个字符,(恰好除以26)。然后把取出的字符放在[ebp+ecx-38]这个地方,因为ECX是递增的,这样就形成了一个新串。对后面的计算也有一点要说明。imul eax这句,是两个32位的数相乘,这样结果会是64位,结果的高32位放在EDX,低32放入EAX,一定要注意! 下面我们来到过程三: 代码:-------------------------------------------------------------------------------- & |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|