F8LEFT 发表于 2014-11-24 11:16:56

Nisy's Second CrackMe 简单分析

本帖最后由 F8LEFT 于 2014-11-24 11:15 编辑

       2014PYG10周年庆典,Nisy校长发了个异常强大的cm,我是菜鸟一个,不过在机缘巧合之下还是弄出来了,发一下分析,来加深印象。这个cm的想法还是挺不错的。
       简单的东西就不多提了。我直接用一组key来进行讲解:
Name:F8LEFT
Pass   :   NS-oZQe7Eru-8EE8
      切入算法流程可以直接使用GetWindowTextW断点,直接在命令行进行bpx GetWindowTextW就可以同时在两个验证段断下了。然后单步跟踪。
      进入第一步验证,除去基础长度判断后,会来到下面一个小buff
0040E43E|.50            PUSH    EAX                                     ;Push Pass
0040E43F|.8D85 D0FBFFFF LEA   EAX,
0040E445|.50            PUSH    EAX                                     ;Push Name
0040E446|.8D45 FC       LEA   EAX,
0040E449|.8038 00       CMP   BYTE PTR DS:, 0x0                  ; = 0
0040E44C|.75 05         JNZ   SHORT NsCrackM.0040E453
0040E44E|.8B00          MOV   EAX, DWORD PTR DS:               ;Err1
0040E450|.C600 CC       MOV   BYTE PTR DS:, 0xCC               ;Err2
0040E453|>FF10          CALL    NEAR DWORD PTR DS:               ;--> Func (In SEH)
       这里入栈了 Pass 与 Name ,没啥的,关键是下面:
CMP   BYTE PTR DS:, 0x0                  ; = 0
JNZ   SHORT NsCrackM.0040E453
MOV   EAX, DWORD PTR DS:               ;Err1
MOV   BYTE PTR DS:, 0xCC               ;Err2
---------------------------->--------------------------->--------------------------->
   cmp ,0    => jnz XXXXXXXX,这里保证了里面的值是0,也即 BYTE: = 0

   MOV   EAX, DWORD :, 这里取了,于是,EAX = 0
   那么问题就来了,下一句
   MOV   BYTE PTR DS:, 0xCC , 此时EAX = 0,这就相当于 MOV DS:, 0xCC,显然是无法进行的。因此,程序到了这里,便会产生异常。
   既然有异常,那么就自然可以想到异常处理函数,直接查看OD的SEH链窗体,可以看到以下几个异常处理函数:


    只要一个函数是属于程序段的,这个便是处理函数了,可以在这个函数上面下个断点,接着F9,便会停在004127C0处,此时便可以进行下一步的跟踪了(不过,暂时用不上)。这时候可以再一次F9,因为前面下了GetWindowTextW断点,此时会停留在第二段验证处。0040DAB0
    跟踪下去就是第一段比较了:
0040DB7A   .50            PUSH    EAX                                     ;Push Name
0040DB7B   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040DB7E   .E8 BD6FFFFF   CALL    <NsCrackM.N_Strcpy>
0040DB83   .8D8D D0FDFFFF LEA   ECX, DWORD PTR SS:
0040DB89   .51            PUSH    ECX                                     ;Push Pass
0040DB8A   .8D8D C8FDFFFF LEA   ECX, DWORD PTR SS:
0040DB90   .E8 AB6FFFFF   CALL    <NsCrackM.N_Strcpy>
0040DB95   .C745 FC 01000>MOV   DWORD PTR SS:, 0x1
0040DB9C   .BA 4E000000   MOV   EDX, 0x4E                               ;N
0040DBA1   .66:8995 B0FBF>MOV   WORD PTR SS:, DX
0040DBA8   .B8 53000000   MOV   EAX, 0x53
0040DBAD   .66:8985 B2FBF>MOV   WORD PTR SS:, AX             ;S
0040DBB4   .33C9          XOR   ECX, ECX
0040DBB6   .66:898D B4FBF>MOV   WORD PTR SS:, CX             ;创建字符串 NS
0040DBBD   .8D8D C8FDFFFF LEA   ECX, DWORD PTR SS:         ;Pass
0040DBC3   .E8 F893FFFF   CALL    <NsCrackM.N_Strlen>                     ;求密码长度
0040DBC8   .83F8 03       CMP   EAX, 0x3                              ;长度>=3
0040DBCB   .7C 39         JL      SHORT NsCrackM.0040DC06
0040DBCD   .8D95 B0FBFFFF LEA   EDX, DWORD PTR SS:
0040DBD3   .52            PUSH    EDX
0040DBD4   .6A 02         PUSH    0x2                                     ;长度为2
0040DBD6   .8D85 94FBFFFF LEA   EAX, DWORD PTR SS:
0040DBDC   .50            PUSH    EAX
0040DBDD   .8D8D C8FDFFFF LEA   ECX, DWORD PTR SS:
0040DBE3   .E8 380A0000   CALL    <NsCrackM.N_StrSub>                     ;取Pass的子窜,这里是取头两个字符
0040DBE8   .838D 80FBFFFF>OR      DWORD PTR SS:, 0x1
0040DBEF   .8BC8          MOV   ECX, EAX
0040DBF1   .E8 6A090000   CALL    <NsCrackM.N_Strcmp?>                  ;与前面创建的字符串NS比较
      这里可以知道密码的前2个字符为NS。再继续跟踪。
0040DC44   .8B0D 90664200 MOV   ECX, DWORD PTR DS:            ; = 0
0040DC4A   .C701 01000000 MOV   DWORD PTR DS:, 0x1               ;SEH => 2
      喜闻乐见+丧心病狂,这里又一次触发了SEH异常,函数函数哪一个,下好断点,然后F9吧。
      断在异常处理函数后F8跟踪,注意大致的浏览一下每一个字call,此时可以定位到一个非常可疑的Call中:
00412927|.E8 A5030000   CALL    NsCrackM.00412CD1                     ;In (函数分派) ECX = 跳转目标地址
       这个Call的里面是这样的。:
00412CD1   [        DISCUZ_CODE_2321        ]nbsp; 8BEA          MOV   EBP, EDX
00412CD3   .8BF1          MOV   ESI, ECX                              ;复制跳转地址
00412CD5   .8BC1          MOV   EAX, ECX                              ;取跳转地址
00412CD7   .6A 01         PUSH    0x1
00412CD9   .E8 B74A0000   CALL    NsCrackM.00417795
00412CDE   .33C0          XOR   EAX, EAX                              ;NsCrackM.0040DC62
00412CE0   .33DB          XOR   EBX, EBX
00412CE2   .33C9          XOR   ECX, ECX
00412CE4   .33D2          XOR   EDX, EDX
00412CE6   .33FF          XOR   EDI, EDI
00412CE8   .FFE6          JMP   NEAR ESI                              ;jmp Func
   清空了一下寄存器的内容,紧接着就直接跳走了,这个显然是人为用汇编代码写上去的,非常的可疑。实际上这里是非常重要的函数分配段,后面还有一次验证会用到这里,下好断点了,然后jmp ESI,跳到相应的处理段中。来到第3段验证:0040DC62
0040DC62   .8B65 E8       MOV   ESP, DWORD PTR SS:            ;3
0040DC65   .8D95 D0FDFFFF LEA   EDX, DWORD PTR SS:
0040DC6B   .52            PUSH    EDX
0040DC6C   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:
0040DC72   .E8 C96CFFFF   CALL    <NsCrackM.N_Strcpy>                     ;复制Pass
0040DC77   .8D8D A8FBFFFF LEA   ECX, DWORD PTR SS:
0040DC7D   .E8 CE92FFFF   CALL    NsCrackM.00406F50
0040DC82   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:         ;Pass
0040DC88   .E8 3393FFFF   CALL    <NsCrackM.N_Strlen>                     ;PassLen
0040DC8D   .83F8 03       CMP   EAX, 0x3
0040DC90   .0F8E 2D010000 JLE   NsCrackM.0040DDC3
0040DC96   .6A 2D         PUSH    0x2D                                    ;0x2D = "-"
0040DC98   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:         ;Pass
0040DC9E   .E8 6D0A0000   CALL    <NsCrackM.N_StrIndex>                   ;取-在Pass中的第一个位置
0040DCA3   .8985 A0FBFFFF MOV   DWORD PTR SS:, EAX
0040DCA9   .8B85 A0FBFFFF MOV   EAX, DWORD PTR SS:
0040DCAF   .83C0 01       ADD   EAX, 0x1
0040DCB2   .50            PUSH    EAX
0040DCB3   .6A 2D         PUSH    0x2D
0040DCB5   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:
0040DCBB   .E8 C00A0000   CALL    <NsCrackM.IndexOfStr>                   ;取第二个-在Pass的位置
0040DCC0   .8985 A4FBFFFF MOV   DWORD PTR SS:, EAX
0040DCC6   .8B8D A4FBFFFF MOV   ECX, DWORD PTR SS:         ;Len
0040DCCC   .2B8D A0FBFFFF SUB   ECX, DWORD PTR SS:         ;-2
0040DCD2   .83E9 01       SUB   ECX, 0x1                              ;-1(-3)
0040DCD5   .51            PUSH    ECX                                     ;第二个-位置
0040DCD6   .8B95 A0FBFFFF MOV   EDX, DWORD PTR SS:
0040DCDC   .83C2 01       ADD   EDX, 0x1
0040DCDF   .52            PUSH    EDX                                     ;第一个-位置
0040DCE0   .8D85 90FBFFFF LEA   EAX, DWORD PTR SS:
0040DCE6   .50            PUSH    EAX
0040DCE7   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:
0040DCED   .E8 8E080000   CALL    <NsCrackM.N_SubStr>                     ;Pass取两个-中间的字符串
0040DCF2   .8B08          MOV   ECX, DWORD PTR DS:
0040DCF4   .51            PUSH    ECX
0040DCF5   .8B95 A4FBFFFF MOV   EDX, DWORD PTR SS:
0040DCFB   .52            PUSH    EDX
0040DCFC   .8B85 A0FBFFFF MOV   EAX, DWORD PTR SS:
0040DD02   .50            PUSH    EAX
0040DD03   .68 9C0C4200   PUSH    NsCrackM.00420C9C                     ;%d %d %s
0040DD08   .8D8D A8FBFFFF LEA   ECX, DWORD PTR SS:
0040DD0E   .51            PUSH    ECX
0040DD0F   .E8 DC0A0000   CALL    NsCrackM.0040E7F0                     ;(格式化字符串)第一个-位置 第二个-位置 两个-中间的字符串
0040DD14   .83C4 14       ADD   ESP, 0x14
0040DD17   .8D8D 90FBFFFF LEA   ECX, DWORD PTR SS:
0040DD1D   .E8 7E70FFFF   CALL    <NsCrackM.ToString>
0040DD22   .8B95 A4FBFFFF MOV   EDX, DWORD PTR SS:
0040DD28   .3B95 A0FBFFFF CMP   EDX, DWORD PTR SS:
0040DD2E      0F8E 8F000000 JLE   NsCrackM.0040DDC3
0040DD34   .8B85 A4FBFFFF MOV   EAX, DWORD PTR SS:         ;同上
0040DD3A   .2B85 A0FBFFFF SUB   EAX, DWORD PTR SS:
0040DD40   .83E8 01       SUB   EAX, 0x1
0040DD43   .50            PUSH    EAX
0040DD44   .8B8D A0FBFFFF MOV   ECX, DWORD PTR SS:
0040DD4A   .83C1 01       ADD   ECX, 0x1
0040DD4D   .51            PUSH    ECX
0040DD4E   .8D95 8CFBFFFF LEA   EDX, DWORD PTR SS:
0040DD54   .52            PUSH    EDX
0040DD55   .8D8D ACFBFFFF LEA   ECX, DWORD PTR SS:
0040DD5B   .E8 20080000   CALL    <NsCrackM.N_SubStr>                     ;继续取两个-中间的字符串
0040DD60   .50            PUSH    EAX
0040DD61   .8D8D A8FBFFFF LEA   ECX, DWORD PTR SS:
0040DD67   .E8 44B4FFFF   CALL    NsCrackM.004091B0
0040DD6C   .8D8D 8CFBFFFF LEA   ECX, DWORD PTR SS:         ;取中间的字符串
0040DD72   .E8 2970FFFF   CALL    <NsCrackM.ToString>
0040DD77   .8D85 A8FBFFFF LEA   EAX, DWORD PTR SS:
0040DD7D   .50            PUSH    EAX                                     ;str2
0040DD7E   .8D8D 88FBFFFF LEA   ECX, DWORD PTR SS:
0040DD84   .51            PUSH    ECX                                     ;str2
0040DD85   .E8 A689FFFF   CALL    NsCrackM.00406730                     ;进行某种变换
0040DD8A   .83C4 08       ADD   ESP, 0x8
0040DD8D   .50            PUSH    EAX
0040DD8E   .8D8D A8FBFFFF LEA   ECX, DWORD PTR SS:
0040DD94   .E8 17B4FFFF   CALL    NsCrackM.004091B0
0040DD99   .8D8D 88FBFFFF LEA   ECX, DWORD PTR SS:
0040DD9F   .E8 FC6FFFFF   CALL    <NsCrackM.ToString>
0040DDA4   .8D95 B8FBFFFF LEA   EDX, DWORD PTR SS:
0040DDAA   .52            PUSH    EDX                                     ;Push Name
0040DDAB   .8D8D A8FBFFFF LEA   ECX, DWORD PTR SS:
0040DDB1   .E8 AA070000   CALL    <NsCrackM.N_Strcmp?>                  ;与用户名比较
       代码虽然比较长,但实际上就做了几件事。首先,用-分割Pass为3段,然后取中间的那一段,进行某种变换,并把变换结果与用户名比较。所以,我们得知Pass的大致结构如下:
NS-Code(Name)-Str3
       这里先不管加密方式,继续跟踪下去:
       下面几步F8后,达到一个call
0040DDE7   .E8 05000000   CALL    NsCrackM.0040DDF1                     ;OneCheck F7进去
      F7跟踪进去:
0040DDF1/[        DISCUZ_CODE_2324        ]nbsp; 6A 02         PUSH    0x2
0040DDF3|.8D8D C8FDFFFF LEA   ECX,
0040DDF9|.E8 E2060000   CALL    NsCrackM.0040E4E0                     ;取Pass
0040DDFE|.0FB7C0      MOVZX   EAX, AX
0040DE01|.83F8 2D       CMP   EAX, 0x2D                               ;Pass = 0x2D('-')
0040DE04      75 77         JNZ   SHORT NsCrackM.0040DE7D
0040DE06|.83BD CCFDFFFF>CMP   , 0x1                        ;bool
0040DE0D|.75 6E         JNZ   SHORT NsCrackM.0040DE7D
0040DE0F|.8D4D E4       LEA   ECX,
0040DE12|.51            PUSH    ECX
0040DE13|.8D8D 9CFBFFFF LEA   ECX,
0040DE19|.E8 5291FFFF   CALL    NsCrackM.00406F70                     ;Name
0040DE1E|.68 B00C4200   PUSH    NsCrackM.00420CB0                     ;-
0040DE23|.8D8D 9CFBFFFF LEA   ECX,
0040DE29|.E8 02070000   CALL    NsCrackM.0040E530
0040DE2E|.8D95 C8FDFFFF LEA   EDX,
0040DE34|.52            PUSH    EDX
0040DE35|.8D8D 9CFBFFFF LEA   ECX,                         ;Pass
0040DE3B|.E8 C0060000   CALL    NsCrackM.0040E500
0040DE40|.8D8D 9CFBFFFF LEA   ECX,
0040DE46|.E8 E573FFFF   CALL    <NsCrackM.*p>                           ;用 - 链接Name与Pass
0040DE4B|.50            PUSH    EAX
0040DE4C|.68 04010000   PUSH    0x104
0040DE51|.68 A0664200   PUSH    NsCrackM.004266A0
0040DE56|.E8 35060000   CALL    NsCrackM.0040E490
0040DE5B|.6A 00         PUSH    0x0                                     ; /lParam = 0x0
0040DE5D|.6A 00         PUSH    0x0                                     ; |wParam = 0x0
0040DE5F|.A1 AC684200   MOV   EAX, DWORD PTR DS:            ; |
0040DE64|.50            PUSH    EAX                                     ; |Message => MSG(0xC1A3)
0040DE65|.8B0D B0684200 MOV   ECX, DWORD PTR DS:            ; |
0040DE6B|.51            PUSH    ECX                                     ; |hWnd => 0xB02A6
0040DE6C|.FF15 B0014200 CALL    NEAR DWORD PTR DS:[<&USER32.PostMessage>; \PostMessageW
0040DE72|.8D8D 9CFBFFFF LEA   ECX,
0040DE78|.E8 236FFFFF   CALL    <NsCrackM.ToString>
0040DE7D\>C3            RETN
      这里发送了一个我们不知道的消息,显然这个消息是Nisy校长自己定义的。跟踪之下,会发现是发给主窗体的,于是,需要在主窗体相应的Msg处理的地方下一个断点,才能继续跟踪。
      在0040DE5F处,右键,查找参考,地址常量,找到一处CMP   EDX, DWORD PTR DS:, (00407441) 可以快速的到底处理的地方,在 CMP...JNZ...的下面下个int3断点,F9运行,便会断下来。到底第4处验证(真多。。。。)
   这里用F7跟踪,进入一个CALL后,便会看到希望了。

    紧接着马上对这个字符串进行某种变换得到: 恭喜,注册成功!@>---    (Nice!!!!)
   然后继续初始化另外一个字符串: EOo&xA^W
   变换后得到:恭喜!
   Good Job!!于是,这里就成功了吗?不是的,必须得再留心一下,前面的Pass还有第三段没有进行验证呢!肯定还有下一个验证点!慢慢跟踪下去,会发现一个非常典型的跳转:
004079DD|.E8 CE660000   CALL    NsCrackM.0040E0B0                     ;4
004079E2|.85C0          TEST    EAX, EAX
004079E4      74 27         JE      SHORT NsCrackM.00407A0D
      这个CALL便是最后一段验证,下面的JE Nop掉就直接成功了,当然,我们继续跟进这个call,看一下它做了什么。
      这个call便是处理Pass第三段的验证段。0040E0B0   $55            PUSH    EBP
0040E0B1   .8BEC          MOV   EBP, ESP
0040E0B3   .6A FE         PUSH    -0x2
0040E0B5   .68 48354200   PUSH    NsCrackM.00423548
0040E0BA   .68 C0274100   PUSH    NsCrackM.004127C0
0040E0BF   .64:A1 0000000>MOV   EAX, DWORD PTR FS:
0040E0C5   .50            PUSH    EAX
0040E0C6   .83C4 C0       ADD   ESP, -0x40
0040E0C9   .53            PUSH    EBX
0040E0CA   .56            PUSH    ESI
0040E0CB   .57            PUSH    EDI
0040E0CC   .A1 74534200   MOV   EAX, DWORD PTR DS:
0040E0D1   .3145 F8       XOR   DWORD PTR SS:, EAX
0040E0D4   .33C5          XOR   EAX, EBP
0040E0D6   .50            PUSH    EAX
0040E0D7   .8D45 F0       LEA   EAX, DWORD PTR SS:
0040E0DA   .64:A3 0000000>MOV   DWORD PTR FS:, EAX
0040E0E0   .8965 E8       MOV   DWORD PTR SS:, ESP
0040E0E3   .C745 DC 00000>MOV   DWORD PTR SS:, 0x0
0040E0EA   .C745 D4 00000>MOV   DWORD PTR SS:, 0x0
0040E0F1   .C745 E0 00000>MOV   DWORD PTR SS:, 0x0   ;取前面链接的 Name-Pass
0040E0F8   .68 A0664200   PUSH    NsCrackM.004266A0                ;F8LEFT-NS-oZQe7Eru-8EE8
0040E0FD   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040E100   .E8 3B68FFFF   CALL    <NsCrackM.N_Strcpy>
0040E105   .8D4D D8       LEA   ECX, DWORD PTR SS:
0040E108   .E8 438EFFFF   CALL    NsCrackM.00406F50
0040E10D   .C745 FC 00000>MOV   DWORD PTR SS:, 0x0
0040E114   .C745 FC 01000>MOV   DWORD PTR SS:, 0x1
0040E11B   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040E11E   .E8 9D8EFFFF   CALL    <NsCrackM.N_Strlen>            ;buffLen
0040E123   .85C0          TEST    EAX, EAX                         ;判断长度是否为0
0040E125   .75 05         JNZ   SHORT NsCrackM.0040E12C
0040E127   .E9 AB000000   JMP   NsCrackM.0040E1D7
0040E12C   >68 08020000   PUSH    0x208
0040E131   .6A 00         PUSH    0x0
0040E133   .68 A0664200   PUSH    NsCrackM.004266A0
0040E138   .E8 23400000   CALL    <NsCrackM.memset?>               ;清除空间
0040E13D   .83C4 0C       ADD   ESP, 0xC
0040E140   .6A 2D         PUSH    0x2D                           ;'-'
0040E142   .8D4D E4       LEA   ECX, DWORD PTR SS:   ;Name-Pass链接
0040E145   .E8 E6050000   CALL    <NsCrackM.StrLastIndex>          ;取最后一个-的位置
0040E14A   .8945 D0       MOV   DWORD PTR SS:, EAX
0040E14D   .8B45 D0       MOV   EAX, DWORD PTR SS:
0040E150   .50            PUSH    EAX
0040E151   .8D4D CC       LEA   ECX, DWORD PTR SS:
0040E154   .51            PUSH    ECX
0040E155   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040E158   .E8 C3040000   CALL    <NsCrackM.N_StrSub>            ;取最后一个-前面的所有数据
0040E15D   .51            PUSH    ECX                              ;F8LEFT-NS-oZQe7Eru
0040E15E   .8BCC          MOV   ECX, ESP
0040E160   .8D55 CC       LEA   EDX, DWORD PTR SS:
0040E163   .52            PUSH    EDX
0040E164   .E8 078EFFFF   CALL    NsCrackM.00406F70                ;利用上面取出的字符串
0040E169   .E8 8287FFFF   CALL    NsCrackM.004068F0                ;计算出加密key (DWORD)
0040E16E   .83C4 04       ADD   ESP, 0x4
0040E171   .8945 E0       MOV   DWORD PTR SS:, EAX   ;保存DWORD
0040E174   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040E177   .E8 448EFFFF   CALL    <NsCrackM.N_Strlen>
0040E17C   .2B45 D0       SUB   EAX, DWORD PTR SS:
0040E17F   .83E8 01       SUB   EAX, 0x1
0040E182   .50            PUSH    EAX
0040E183   .8D45 C0       LEA   EAX, DWORD PTR SS:
0040E186   .50            PUSH    EAX
0040E187   .8D4D E4       LEA   ECX, DWORD PTR SS:
0040E18A   .E8 01050000   CALL    NsCrackM.0040E690                ;取Pass的最后的一个buff
0040E18F   .50            PUSH    EAX
0040E190   .8D4D D8       LEA   ECX, DWORD PTR SS:
0040E193   .E8 18B0FFFF   CALL    NsCrackM.004091B0
0040E198   .8D4D C0       LEA   ECX, DWORD PTR SS:
0040E19B   .E8 006CFFFF   CALL    <NsCrackM.ToString>
0040E1A0   .8D4D D8       LEA   ECX, DWORD PTR SS:
0040E1A3   .E8 188EFFFF   CALL    <NsCrackM.N_Strlen>            ;取长度,与4比较
0040E1A8   .83F8 04       CMP   EAX, 0x4
0040E1AB   .74 02         JE      SHORT NsCrackM.0040E1AF
0040E1AD   .EB 28         JMP   SHORT NsCrackM.0040E1D7
0040E1AF   >8D4D D8       LEA   ECX, DWORD PTR SS:
0040E1B2   .E8 098EFFFF   CALL    <NsCrackM.N_Strlen>
0040E1B7   .83F8 04       CMP   EAX, 0x4
0040E1BA   .75 13         JNZ   SHORT NsCrackM.0040E1CF
0040E1BC   .C745 D4 01000>MOV   DWORD PTR SS:, 0x1   ;bool Continue = 1
0040E1C3   .8B0D 90664200 MOV   ECX, DWORD PTR DS:   ;ECX = 0
0040E1C9   .C701 01000000 MOV   DWORD PTR DS:, 0x1          ;Err3 (To SEH)
      处理了一下用户名与密码,计算出一个加密key,判断Pass的最后一段长度是否为4,是的话就触发新的异常。异常处理函数还是哪一个,就连分配的地点也是一样的。这下知道了我前面下分配函数的那个断点的作用了吧。慢慢跟踪下去,来到最后一段验证: 0040E1E9
      0040E1E9   .8B65 E8       MOV   ESP, DWORD PTR SS:
0040E1EC   .837D D4 00    CMP   DWORD PTR SS:, 0x0   ;if bool = 0?
0040E1F0   .74 74         JE      SHORT NsCrackM.0040E266
0040E1F2   .51            PUSH    ECX
0040E1F3   .8BCC          MOV   ECX, ESP
0040E1F5   .8D55 D8       LEA   EDX, DWORD PTR SS:
0040E1F8   .52            PUSH    EDX
0040E1F9   .E8 728DFFFF   CALL    NsCrackM.00406F70                ;取Pass最后一段
0040E1FE   .E8 1D88FFFF   CALL    <NsCrackM.atoi>                  ;Str 转换 为 DWORD = Temp
0040E203   .83C4 04       ADD   ESP, 0x4
0040E206   .8945 C8       MOV   DWORD PTR SS:, EAX
0040E209   .8B45 E0       MOV   EAX, DWORD PTR SS:   ;取出刚才算出的key
0040E20C   .25 0000FFFF   AND   EAX, 0xFFFF0000
0040E211      C1E8 10       SHR   EAX, 0x10                        ;a = (key & 0xFFFF0000) >> 10
0040E214   .8B4D E0       MOV   ECX, DWORD PTR SS:
0040E217   .81E1 FFFF0000 AND   ECX, 0xFFFF                      ;b = key & 0x0000FFFF
0040E21D   .33C1          XOR   EAX, ECX                         ;c = a ^ b, 也就是key高地位异或
0040E21F   .8945 E0       MOV   DWORD PTR SS:, EAX
0040E222   .8B55 C8       MOV   EDX, DWORD PTR SS:
0040E225   .0355 E0       ADD   EDX, DWORD PTR SS:   ;d = Temp + c
0040E228   .33C0          XOR   EAX, EAX
0040E22A   .81FA 00000100 CMP   EDX, 0x10000                     ;cmp d, 0x10000
0040E230   .0F94C0      SETE    AL                               ;Set Flag
       这下终于弄完大致的流程了,终结一下,程序现在有两段加密的算法没有弄出来,记为Encode1, Encode2.那么加密的数据为
Pass1 = NS
Name = Encode1(Pass2)
Pass3 = Encode2(Name-Pass1-Pass2)
       最后就剩下Encode1与Encode2要判断了。先来看Encode2,只是一个简单的查表变换,很简单的。
00406890/$55            PUSH    EBP
00406891|.8BEC          MOV   EBP, ESP
00406893|.83EC 08       SUB   ESP, 0x8
00406896|.8B45 08       MOV   EAX,
00406899|.83F0 FF       XOR   EAX, 0xFFFFFFFF
0040689C|.8945 FC       MOV   , EAX                   ;sum = -1
0040689F|.C745 F8 00000>MOV   , 0x0
004068A6|.EB 09         JMP   SHORT NsCrackM.004068B1
004068A8|>8B4D F8       /MOV   ECX,
004068AB|.83C1 01       |ADD   ECX, 0x1
004068AE|.894D F8       |MOV   , ECX
004068B1|>8B55 F8      MOV   EDX,
004068B4|.3B55 10       |CMP   EDX,                   ;while(i < iPasslen)
004068B7|.73 24         |JNB   SHORT NsCrackM.004068DD
004068B9|.8B45 0C       |MOV   EAX,
004068BC|.0345 F8       |ADD   EAX,
004068BF|.0FBE08      |MOVSX   ECX, BYTE PTR DS:          ;UCHAR a = Str
004068C2|.334D FC       |XOR   ECX,                   ;a ^= sum
004068C5|.81E1 FF000000 |AND   ECX, 0xFF
004068CB|.8B55 FC       |MOV   EDX,
004068CE|.C1EA 08       |SHR   EDX, 0x8                        ;sum >>= 8
004068D1|.33148D C80342>|XOR   EDX, DWORD PTR DS: (查表变换)
004068D8|.8955 FC       |MOV   , EDX
004068DB|.^ EB CB         \JMP   SHORT NsCrackM.004068A8
004068DD|>8B45 FC       MOV   EAX,
004068E0|.83F0 FF       XOR   EAX, 0xFFFFFFFF                  ;sum ^= -1
004068E3|.8BE5          MOV   ESP, EBP
004068E5|.5D            POP   EBP
004068E6\.C3            RETN
      接着来看Encode1,直接说结论,这是一个变形Base64解密的代码,变换表变了,数据组合方式也有一点改变。
      假设原始数据为 A,B,C 分组后的数据为 a,b,c,d。 其中A,B,C都是8bit的数据,a,b,c,d 都是6bit的数据。那么他们的组合为
   d   |       c   |      b      |       a
111111111111   111111111111
   C         |          B       |      A   
      与标准的组合算法恰好相反。注意一下就可以了。另外,解密的DecodeTable为:
base64:         |Start   Y为结束标记,解密表为 0012E4B5 ~ 0012E535 长0x80 * 2
0012E4B3   59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Y...............
0012E4C3   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00................
0012E4D3   00 00 00 00 02 37 00 3A 00 00 00 00 0F 00 00 00....7.:.......
0012E4E3   3E 30 11 10 28 17 24 00 05 25 0A 00 00 36 3D 00>0($.%...6=.
0012E4F3   00 1A 00 00 1F 07 19 1E 09 1C 2F 0C 31 29 38 00.32.../.1)8.
0012E503   00 2A 03 27 0B 00 33 12 32 00 00 21 00 2B 1B 16.*' .32..!.+
0012E513   08 00 22 2D 01 34 13 18 35 00 26 14 1D 00 2C 39."-45.&.,9
0012E523   06 00 2E 04 0D 20 15 23 3C 0E 3F 3B 00 00 00 00... #<?;....
0012E533   00 00            变换一下:
unsigned char CodeTable = {0};
        int i;
        for(i = 0; i < 0x80; i++) {
                CodeTable] = i;
        }      得到原始的加密表为:
char table[] = {               
                0x36, 0x63, 0x23, 0x51, 0x72, 0x37, 0x6F, 0x44, 0x5F, 0x47, 0x39, 0x53, 0x4A, 0x73, 0x78, 0x2B,
                0x32, 0x31, 0x56, 0x65, 0x6A, 0x75, 0x5E, 0x34, 0x66, 0x45, 0x40, 0x5D, 0x48, 0x6B, 0x46, 0x43,
                0x74, 0x5A, 0x61, 0x76, 0x35, 0x38, 0x69, 0x52, 0x33, 0x4C, 0x50, 0x5C, 0x6D, 0x62, 0x71, 0x49,
                0x30, 0x4B, 0x57, 0x55, 0x64, 0x67, 0x3C, 0x24, 0x4D, 0x6E, 0x26, 0x7A, 0x77, 0x3D, 0x2F, 0x79
        };       到此,这个算法也就得到了,CM中相应的分析就不发了,因为我本身也没有做多少,知道是Base64后就直接弄出结果来了。。。
       keygen源码见附件,相信也是不难的。要爆破的话就把上面相应的点给修改一下足以。这里在说一下程序的自校验(暗桩)。
       程序在点击注册的时候会验证代码是否被修改(KMP),如果改了的话就直接退出程序。首先异常的地方在上面说过PostMessage处,哪里变成这样了。
0040DE5B|.6A 00         PUSH    0x0                              ; /lParam = 0x0
0040DE5D|.6A 00         PUSH    0x0                              ; |wParam = 0x0
0040DE5F|.A1 AC684200   MOV   EAX, DWORD PTR DS:   ; |
0040DE64|.50            PUSH    EAX                              ; |Message => WM_CLOSE
0040DE65|.8B0D B0684200 MOV   ECX, DWORD PTR DS:   ; |
0040DE6B|.51            PUSH    ECX                              ; |hWnd => 0x130204
0040DE6C|.FF15 B0014200 CALL    NEAR DWORD PTR DS:[<&USER32.Post>; \PostMessageW
       这次变成了发送WM_CLOSE消息了。。。继续的查找地址常量参考,来到赋值处
00401853|.E8 E8F9FFFF   CALL    Crack.00401240                   ;自校验
00401858|.85C0          TEST    EAX, EAX
0040185A      75 0A         JNZ   SHORT Crack.00401866
0040185C|.C705 AC684200>MOV   DWORD PTR DS:, 0x10    ;WM_CLOSE
   该怎么搞就怎么搞,call里面是使用KMP算法来验证程序段某些部分是否被修改掉了。
   keygen附上,大家来指点一下吧。


wgz001 发表于 2014-11-24 11:18:31

本帖最后由 wgz001 于 2014-11-24 11:33 编辑

沙发

明显自己功力还不够,看了文章还是不懂 {:soso_e149:}

pentium450 发表于 2014-11-24 11:20:31

大赞!太厉害了!!

nop 发表于 2014-11-24 11:21:54

膜拜中,纯粹是来膜拜的

VC8 发表于 2014-11-24 11:27:59

膜拜把校长的CM玩的如此透彻。。。

sdnyzjzx 发表于 2014-11-24 11:33:16

膜拜 + 羡慕

冷月孤心 发表于 2014-11-24 11:58:34

写的太精彩了,谢谢分享

sdzzb 发表于 2014-11-24 12:06:17

分析清晰   表述详细 。。LZ异常的叼

xingbing 发表于 2014-11-24 12:07:32

分析的很不错,厉害。功底很深呀。

tree_fly 发表于 2014-11-24 12:20:26

分析很好,各种膜拜
页: [1] 2 3
查看完整版本: Nisy's Second CrackMe 简单分析