丶咖啡猫丶 发表于 2019-2-22 17:31

一个Crackeme的完整分析,算法函数的寻找+实现过程

//学习了一段时间的破解,前两天在论坛上看见了知心人发的160个cracking ,自己就尝试连了,一些,第一次发帖,由于技术有限,错误的地方请大牛指出,希望帮助那些和我一样的新手快速入门,尽力做的清晰明了。--大牛就可以飘过了。


目标:Afkayas Nag,Name/Serial(VB5)
保护:没有任何保护
方法:注册算法+算法注册机编写(C语言)
开始:
由于没有任何保护。所以直接OD载入,F9 跑起程序,
// VB用的是UNICODE编码,所以直接搜索UNICODE字符串 找到一些字符串,非常简单,很容易就能明白注册关键代码
中文搜索引擎
地址       反汇编                                    文本字符串
004085EF   JNZ SHORT AfKayAs_.004085F9               (Initial CPU selection)
0040867F   PUSH AfKayAs_.00406FC0                  You Get It//看到这里都明白是正确消息框的意思
00408684   PUSH AfKayAs_.00406FDC                  \r\n// 这里两个一个是 回车 一个是换行的意思
00408697   PUSH AfKayAs_.00406FE8                  Key Gen It Now   //正确字符串
004086E1   PUSH AfKayAs_.00407008                  You Get Wrong//如意 wrong
004086E6   PUSH AfKayAs_.00406FDC                  \r\n
004086F9   PUSH AfKayAs_.00407028                  Try Again

双击直接定位到YOU Get It
004086D0    8D45 B4         LEA EAX,DWORD PTR SS:
004086D3    52            PUSH EDX
004086D4    8D4D C4         LEA ECX,DWORD PTR SS:
004086D7    50            PUSH EAX
004086D8    51            PUSH ECX
00408677   /74 62         JE SHORT AfKayAs_.004086DB                      ; 关键跳,如果ZF等于0就跳转到下面错误消息弹弹出窗口的地方 (如果是采用暴力破解程序这里直接改成JMP就可以了,)

//个人觉得暴力破解不利于以后学习,所以建议和我一样的小鸟学习的时候多分析,不用总是采用暴力破解,不然学不到多少东西。
00408679   |8B35 14B14000   MOV ESI,DWORD PTR DS:[<&MSVBVM50.__vbaSt>; MSVBVM50.__vbaStrCat   // 调用一个字符串处理函数,(字符串连接用的。因为他正确消息框显示了2个字符串)
0040867F   |68 C06F4000   PUSH AfKayAs_.00406FC0                   ; You Get It   压入字符串到堆栈
00408684   |68 DC6F4000   PUSH AfKayAs_.00406FDC                   ; \r\n 压入格式控制到堆栈
00408689   |FFD6            CALL ESI                                 //      调用(strCat)函数   
0040868B   |8BD0            MOV EDX,EAX                           // eax的值赋值给EDXeax=edx= You Get It
0040868D   |8D4D E8         LEA ECX,DWORD PTR SS:          //这里将这个堆栈段的地址赋值给ECX 可以在寄存器的ECX查看一下
00408690   |FF15 94B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMo>; MSVBVM50.__vbaStrMove                        //字符串分解函数
00408696   |50            PUSH EAX                               // EAX= you get it
00408697   |68 E86F4000   PUSH AfKayAs_.00406FE8                   ; Key Gen It Now
0040869C   |FFD6            CALL ESI                              //这个函数就是将这2个字符串在MSG消息框中以2行形式显示
0040869E   |8945 CC         MOV DWORD PTR SS:,EAX
004086A1   |8D45 94         LEA EAX,DWORD PTR SS:
004086A4   |8D4D A4         LEA ECX,DWORD PTR SS:
004086A7   |50            PUSH EAX
004086A8   |8D55 B4         LEA EDX,DWORD PTR SS:
004086AB   |51            PUSH ECX
004086AC   |52            PUSH EDX
004086AD   |8D45 C4         LEA EAX,DWORD PTR SS:
004086B0   |6A 00         PUSH 0x0
004086B2   |50            PUSH EAX
004086B3   |C745 C4 0800000>MOV DWORD PTR SS:,0x8
004086BA   |FF15 24B14000   CALL DWORD PTR DS:[<&MSVBVM50.#595>]   ; MSVBVM50.rtcMsgBox         //根据前面的那些参数可以看出是弹出正确的MSG消息框
004086C0   |8D4D E8         LEA ECX,DWORD PTR SS:
004086C3   |FF15 A8B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeS>; MSVBVM50.__vbaFreeStr       //释放分配的资源
004086C9   |8D4D 94         LEA ECX,DWORD PTR SS:
004086CC   |8D55 A4         LEA EDX,DWORD PTR SS:
004086CF   |51            PUSH ECX
004086D0   |8D45 B4         LEA EAX,DWORD PTR SS:
004086D3   |52            PUSH EDX
004086D4   |8D4D C4         LEA ECX,DWORD PTR SS:
004086D7   |50            PUSH EAX
004086D8   |51            PUSH ECX
004086D9   |EB 60         JMP SHORT AfKayAs_.0040873B
004086DB   \8B35 14B14000   MOV ESI,DWORD PTR DS:[<&MSVBVM50.__vbaSt>; MSVBVM50.__vbaStrCat
004086E1    68 08704000   PUSH AfKayAs_.00407008                                       ; You Get Wrong
004086E6    68 DC6F4000   PUSH AfKayAs_.00406FDC                                        ; \r\n
004086EB    FFD6            CALL ESI                                                   //和上面分析一样
004086ED    8BD0            MOV EDX,EAX
004086EF    8D4D E8         LEA ECX,DWORD PTR SS:
通过上面的调试分析 可以看出这里的代码可以分2个 分别是弹出2个消息框用C语言可以

If(x==0)
{
压入错误消息框MSG参数
弹出错误消息框。
}
Else
{
压入正确消息框MSG参数
正确消息框
}

那么怎么定位到程序调用消息窗口的函数地方呢。 1. 可以直接向上面找,通常是可以找到的。 2.可以通过设置消息断点。但是要麻烦许多,一般都会跳到系统库里面。
这里是直接向上找,就可以找到 调用函数关键代码
004085D1    50            PUSH EAX                                                ; 压入用户密码
004085D2    FF15 74B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>]                ; 可能是计算正确密码
004085D8    8B4D E4         MOV ECX,DWORD PTR SS:                           ; 正确密码 ()SS:0018f41c   跟紧这个地址
004085DB    DD9D 1CFFFFFF   FSTP QWORD PTR SS:
004085E1    51            PUSH ECX
004085E2    FF15 74B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>]                ; MSVBVM50.__vbaR8Str
004085E8    833D 00904000 0>CMP DWORD PTR DS:,0x0                           ; 关键位置    00409000不能为0
004085EF    75 08         JNZ SHORT AfKayAs_.004085F9
004085F1    DCBD 1CFFFFFF   FDIVR QWORD PTR SS:
004085F7    EB 11         JMP SHORT AfKayAs_.0040860A
004085F9    FFB5 20FFFFFF   PUSH DWORD PTR SS:
004085FF    FFB5 1CFFFFFF   PUSH DWORD PTR SS:
00408605    E8 888AFFFF   CALL <JMP.&MSVBVM50._adj_fdivr_m64>
0040860A    DFE0            FSTSW AX
0040860C    A8 0D         TEST AL,0xD                                                ; AX的值与 13进行与操作 结果必须不为0
0040860E    0F85 AB010000   JNZ AfKayAs_.004087BF                                       //这里ZF不等于0程序会错误
00408614    FF15 34B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaFpR8>]               ; MSVBVM50.__vbaFpR8
0040861A    DC1D 28104000   FCOMP QWORD PTR DS:
00408620    DFE0            FSTSW AX                                                ; 把状态寄存器的值存入AX
00408622    F6C4 40         TEST AH,0x40                                                ; AH 必须等于40 下面才能指向正确的窗口
00408625    74 07         JE SHORT AfKayAs_.0040862E                                  ; 关键代码(这里如果跳转成功表示用户输入密码是错误的)
00408627    BE 01000000   MOV ESI,0x1
0040862C    EB 02         JMP SHORT AfKayAs_.00408630
0040862E    33F6            XOR ESI,ESI
00408630    8D55 E4         LEA EDX,DWORD PTR SS:                        // SS: 放置正确密码的栈段地址传递给EDX
00408633    8D45 E8         LEA EAX,DWORD PTR SS:                        //用户输入密码地址传递给EAX

//就上面而言得到正确注册码很简单,甚至不用OD分析都可以达到, 直接用CE搜索下用户输入密码,在加个一级偏移可能就能找到正确注册码。但是遇到变态的程序,你在调试的时候是很难直接看见一个完整字符串密码或者注册码的。
看到正确密码的 004085D8    8B4D E4         MOV ECX,DWORD PTR SS:   这里大家可能都以为 上面那个估计就是计算正确密码的函数了,
其实不然, 注意这个SS:0018f41c地址这里保存了运算结果,所以一些函数必定要操作这个地址,那么简单了,我们随便输入个密码在这个计算函数前面一点下个段,然后dd SS:0018f41c   这个地址发现里面有一串数字,带入前面的账号,弹出正确消息框,那说明了什么,说明在这个函数前面就已经有了正确密码,那说明这个也不是验证码计算函数。那么我们该怎么着计算函数呢,。。其实简单, 只要跟着SS:0018f41c 这个地址就可以了。我们往上面找,隔一段地址就下断看看有没有用户或者密码,或者什么关键信息。
-----这个时候耐心很重要了,我自己找的时候也跟错了很多函数。一进去乱七八糟的,分析了半天发现不是。解决办法,------打开QQ 音乐来点杀马特音乐

提前说明下注册码的计算过程: 用户名->值A->值A+2->值B->值B+15>正确注册码

好了开了 我们继续向上面跟。
004084DF    FF15 74B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>]                ; MSVBVM50.__vbaR8Str
004084E5    DC25 20104000   FSUB QWORD PTR DS:                              ; 把浮点数寄存器中的值减去-15既+15得到正确注册码,这里是关键注意浮点数寄存器
004084EB    83EC 08         SUB ESP,0x8
004084EE    DFE0            FSTSW AX
004084F0    A8 0D         TEST AL,0xD
004084F2    0F85 C7020000   JNZ AfKayAs_.004087BF
004084F8    DD1C24          FSTP QWORD PTR SS:
004084FB    FF15 48B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrR8>]                ; MSVBVM50.__vbaStrR8
00408501    8BD0            MOV EDX,EAX                                             ; 正确的注册码
00408503    8D4D E4         LEA ECX,DWORD PTR SS:
00408506    FF15 94B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>]            ; MSVBVM50.__vbaStrMove

那么继续想上面跟,找004084E5    DC25 20104000   FSUB QWORD PTR DS: 中浮点数寄存器值相关的代码,
004083E3    FF15 18B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheckObj>]      ; MSVBVM50.__vbaHresultCheckObj
004083E9    8B8D 58FFFFFF   MOV ECX,DWORD PTR SS:
004083EF    8B55 E8         MOV EDX,DWORD PTR SS:
004083F2    52            PUSH EDX
004083F3    8B19            MOV EBX,DWORD PTR DS:                              ; //注意右边的浮点数寄存器
004083F5    FF15 74B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>]                ; MSVBVM50.__vbaR8Str
004083FB    DC0D 10104000   FMUL QWORD PTR DS:                              ; 这里计算出关键值用53384*3=ST0=1600152 值B
00408401    83EC 08         SUB ESP,0x8
00408404    DC25 18104000   FSUB QWORD PTR DS:                              ; 1600152减去-2   1600152与注册码计算相关数值注意这里 值B-2
0040840A    DFE0            FSTSW AX
0040840C    A8 0D         TEST AL,0xD
0040840E    0F85 AB030000   JNZ AfKayAs_.004087BF
00408414    DD1C24          FSTP QWORD PTR SS:
00408417    FF15 48B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrR8>]               ; MSVBVM50.__vbaStrR8
0040841D    8BD0            MOV EDX,EAX                                             ; EAX =1600150 传递给EDX
0040841F    8D4D E4         LEA ECX,DWORD PTR SS:                              ; 这里将 533384地址传递给ECX    这个段寄存器中的值也是与注册码相关的值。
00408422    FF15 94B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>]               ; MSVBVM50.__vbaStrMove
00408428    899D 2CFFFFFF   MOV DWORD PTR SS:,EBX
0040842E    8B9D 58FFFFFF   MOV EBX,DWORD PTR SS:
00408434    50            PUSH EAX                                                ; 与正确注册码有关
00408435    8B85 2CFFFFFF   MOV EAX,DWORD PTR SS:
0040843B    53            PUSH EBX
0040843C    FF90 A4000000   CALL DWORD PTR DS:

继续想上面找值A+2的地方
00408322    A8 0D         TEST AL,0xD
00408324    0F85 95040000   JNZ AfKayAs_.004087BF
0040832A    DD1C24          FSTP QWORD PTR SS:
0040832D    FF15 48B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrR8>]                ; 值A+2
00408333    8BD0            MOV EDX,EAX                                             ; 533384
00408335    8D4D E4         LEA ECX,DWORD PTR SS:
00408338    FF15 94B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>]            ; MSVBVM50.__vbaStrMove

这里先不分析那些CALL的执行过程,只需要推测注册码怎么来的就可以了。
好了继续想上面找值A

004081E3    FF15 18B14000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheckObj>]      ; MSVBVM50.__vbaHresultCheckObj
004081E9    8B95 50FFFFFF   MOV EDX,DWORD PTR SS:
004081EF    8B45 E4         MOV EAX,DWORD PTR SS:                           ; 压入用户名
004081F2    50            PUSH EAX                                                ; 将用户名压入堆栈
004081F3    8B1A            MOV EBX,DWORD PTR DS:
004081F5    FF15 F8B04000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaLenBstr>]            ; 计算用户名长度
004081FB    8BF8            MOV EDI,EAX                                             ; 将用户名长度赋值给EDI
004081FD    8B4D E8         MOV ECX,DWORD PTR SS:                           ; 将用户名赋值给ECX
00408200    69FF 385B0100   IMUL EDI,EDI,0x15B38                                    ; 15B38 10进制=88888不知道意义何在这里
00408206    51            PUSH ECX
00408207    0F80 B7050000   JO AfKayAs_.004087C4                                    ; JO表示 OF溢出标志位=1 就跳转
0040820D    FF15 0CB14000   CALL DWORD PTR DS:[<&MSVBVM50.#516>]                      ; MSVBVM50.rtcAnsiValueBstr
00408213    0FBFD0          MOVSX EDX,AX                                              ; 代符号扩展的传送指令
00408216    03FA            ADD EDI,EDX                                  这里这个值很奇怪 把他转换成10进制看看 发现这里就是值A 根本计算过程
00408218    0F80 A6050000   JO AfKayAs_.004087C4
0040821E    57            PUSH EDI
0040821F    FF15 F4B04000   CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrI4>]                ; 计算可能是出值A的函数

好了到这里基本可以推测出注册的形式了,只需要知道值A怎么来的就可以了
00408216    03FA            ADD EDI,EDX   通过这里可以知道值A= ADD EDI,EDX   那么只需要知道EDI 和EDX就可以了00408213    0FBFD0          MOVSX EDX,AX 这里可以看出EDX =AX   0040820D    FF15 0CB14000   CALL DWORD PTR DS:[<&MSVBVM50.#516>]   这里看出 AX=用户账号第一个字符 的ansi 的编码值
00408200    69FF 385B0100   IMUL EDI,EDI,0x15B38这里可以看出EDI的值 = EDI*0x15b38 得出的   那继续向上面跟    EDI=EAX   EAX=用户名长度值
好了进过分析总结出了注册码的计算方法,这个代码跨度还是挺大的!!。。不像C语言程序注册码计算都在一个函数里面多方便。

注册码=(用户名长度值*0x15B38+用户名字符串第一个字符串的ansi+2)*3-2+15   
C语言算法注册机
# include <stdio.h>
# include <string.h>
int main()
{
    char user;
    char s;
    int len;
    int number;
    int ansi;
    printf("请输入用户名:");
    scanf("%s",&user);
    s=user;
    ansi=(int)s;
    len=strlen(user);
    //printf("%d",ansi);
    number=(len*0x15B38+ansi+2)*3-2+15;
    printf("注册码为:%d",number);
    return 0;
}
好了分析结束了,很多地方注意不到,希望大家能体谅。。。
附加里面我吧WORD文档和程序都添加了

戰龍在野 发表于 2019-2-22 19:13

謝謝提供原創好好學習一下

qwe124040 发表于 2019-2-22 21:38

学习学习

Hmily 发表于 2019-2-22 21:43

@丶咖啡猫丶 图片盗链了,另外可以把代码放到代码框中处理一下。

丶咖啡猫丶 发表于 2019-2-23 14:33

Hmily 发表于 2019-2-22 21:43
@丶咖啡猫丶 图片盗链了,另外可以把代码放到代码框中处理一下。

好的版主大大,已经整改的,没有在吾爱发过贴,规矩不是很清楚

六月飞雪 发表于 2019-2-27 18:45

谢谢楼主详细的教程
页: [1]
查看完整版本: 一个Crackeme的完整分析,算法函数的寻找+实现过程