●黑石(BlackStone)

      连珠(五子棋)对弈/辅助计算软件。BlackStone本意为棋子中的黑子,在国内通译为黑石。
      黑石由俄罗斯人Victor Barykin开发,最早的版本发布于1997年。黑石被公认为目前已公开发布的连珠/五子棋对弈软件中综合棋力最强的,曾获得第二、三届世界计算机连珠程序锦标赛(Renju Computer World Championship)竞赛组冠军。黑石采用的对弈规则为三手交换,五手两打,有禁手,可调节难度(剪枝方法、计算深度和时间),4.0以后的版本可调节棋盘线数。黑石的局部计算能力强大,被普遍用于局面研究拆解和网络五子棋对弈。2000年传入中国后,对连珠/五子棋开局理论的研究起到了前所未有的巨大推进作用,并成为网络连珠/五子棋对弈中最常用的辅助计算软件。在黑石传入中国后的短短几年中,依靠黑石发展、修正、建立起的连珠/五子棋开局理论,超过了过去一百多年全世界连珠/五子棋开局理论总和的数百甚至上千倍之多;目前高水平的网络五子棋对弈、比赛,参赛选手绝大多选用黑石作为辅助计算工具。

软件明码比较,压缩壳,我们带壳调试,使用a_p大哥修改的野猪OD.

////////////
00517000 >  53              PUSH EBX                                 ; OD载入后来到这里
00517001    55              PUSH EBP
00517002    89C5            MOV EBP,EAX
00517004    33DB            XOR EBX,EBX
00517006    EB 60           JMP SHORT Wrenju.00517068                ; F8单步一次
////////////
00517068    E8 00000000     CALL Wrenju.0051706D                     ; JMP后来到这里
0051706D    58              POP EAX
0051706E    2D 6D000000     SUB EAX,6D
00517073    50              PUSH EAX
00517074    60              PUSHAD                                   ; 我们看到这里有PUSHAD
00517075    33C9            XOR ECX,ECX                              ; 所以我们向下找一下POPAD
……
……
……
00517282    61              POPAD                                    ; 在这里看到了POPAD
00517283    58              POP EAX
00517284    8BE8            MOV EBP,EAX
00517286    2E:0385 9502000>ADD EAX,DWORD PTR CS:[EBP+295]
0051728D    05 99020000     ADD EAX,299
00517292    5D              POP EBP
00517293    5B              POP EBX
00517294  ^ E9 17B0F6FF     JMP Wrenju.004822B0                      ; JMP到OEP // 我们在该行左键单击 然后F4来到这里
////////////
004822B0    55              PUSH EBP                                 ; 来到OEP
004822B1    8BEC            MOV EBP,ESP
004822B3    83C4 F4         ADD ESP,-0C
004822B6    53              PUSH EBX
////////////

F9运行,我们用OD的插件下万能断点来拦截注册部分:

77D3353D    F3:A5           REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>; 断到这里,ESI中显示我们输入的假码
77D3353F    8BC8            MOV ECX,EAX
77D33541    83E1 03         AND ECX,3
77D33544    F3:A4           REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
77D33546    E8 E3FBFFFF     CALL user32.77D3312E
77D3354B    5F              POP EDI
77D3354C    5E              POP ESI
77D3354D    8BC3            MOV EAX,EBX
77D3354F    5B              POP EBX
77D33550    5D              POP EBP
77D33551    C2 1000         RETN 10                                  ; 我们Ctrl+F9一路返回到程序领空

00469084    55              PUSH EBP
00469085    8BEC            MOV EBP,ESP
00469087    83C4 E4         ADD ESP,-1C
0046908A    53              PUSH EBX
0046908B    56              PUSH ESI
0046908C    57              PUSH EDI
0046908D    33C9            XOR ECX,ECX
0046908F    894D E4         MOV DWORD PTR SS:[EBP-1C],ECX
00469092    894D E8         MOV DWORD PTR SS:[EBP-18],ECX
00469095    8BD8            MOV EBX,EAX
00469097    33C0            XOR EAX,EAX
00469099    55              PUSH EBP
0046909A    68 F2924600     PUSH Wrenju.004692F2
0046909F    64:FF30         PUSH DWORD PTR FS:[EAX]
004690A2    64:8920         MOV DWORD PTR FS:[EAX],ESP
004690A5    8D55 E8         LEA EDX,DWORD PTR SS:[EBP-18]
004690A8    8B83 FC010000   MOV EAX,DWORD PTR DS:[EBX+1FC]
004690AE    E8 E95DFBFF     CALL Wrenju.0041EE9C                     ; 该CALL取假码
004690B3    8B45 E8         MOV EAX,DWORD PTR SS:[EBP-18]            ; 我们返回到这里
004690B6    8D55 FC         LEA EDX,DWORD PTR SS:[EBP-4]
004690B9    E8 769BF9FF     CALL Wrenju.00402C34
004690BE    8BF8            MOV EDI,EAX
004690C0    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
004690C5    8B00            MOV EAX,DWORD PTR DS:[EAX]
004690C7    8BCF            MOV ECX,EDI
004690C9    BA 08000000     MOV EDX,8
004690CE    E8 1D2CFFFF     CALL Wrenju.0045BCF0
004690D3    8D55 E8         LEA EDX,DWORD PTR SS:[EBP-18]
004690D6    8B83 00020000   MOV EAX,DWORD PTR DS:[EBX+200]
004690DC    E8 BB5DFBFF     CALL Wrenju.0041EE9C
004690E1    8B45 E8         MOV EAX,DWORD PTR SS:[EBP-18]            ; 该CALL取得Serial Number数值
004690E4    E8 5FE2F9FF     CALL Wrenju.00407348
004690E9    8BF0            MOV ESI,EAX                              ; 将Serial Number数值转化为16进制后保存到ESI
004690EB    81C6 6B030000   ADD ESI,36B                              ; Serial Number数值+36BH
004690F1    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
004690F6    8B00            MOV EAX,DWORD PTR DS:[EAX]
004690F8    8BCE            MOV ECX,ESI
004690FA    BA 09000000     MOV EDX,9
004690FF    E8 EC2BFFFF     CALL Wrenju.0045BCF0
00469104    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
00469109    8B00            MOV EAX,DWORD PTR DS:[EAX]
0046910B    8B0D 70274A00   MOV ECX,DWORD PTR DS:[4A2770]
00469111    BA 07000000     MOV EDX,7
00469116    E8 D52BFFFF     CALL Wrenju.0045BCF0
0046911B    56              PUSH ESI
0046911C    8B0D 74274A00   MOV ECX,DWORD PTR DS:[4A2774]            ; Computer Number数值放ECX
00469122    8B15 70274A00   MOV EDX,DWORD PTR DS:[4A2770]            ; Serial Number  数值放EDX
00469128    8BC7            MOV EAX,EDI
0046912A    E8 3932FFFF     CALL Wrenju.0045C368                     ; 算法CALL 我们跟进
0046912F    8B15 28434800   MOV EDX,DWORD PTR DS:[484328]            ; Wrenju.004A272D
00469135    C602 00         MOV BYTE PTR DS:[EDX],0
00469138    48              DEC EAX
00469139    0F84 8E010000   JE Wrenju.004692CD
0046913F    48              DEC EAX
00469140    74 64           JE SHORT Wrenju.004691A6
00469142    48              DEC EAX
00469143    74 0C           JE SHORT Wrenju.00469151                 ; 这里跳向注册成功  若想实现这里跳转算法CALL返回时EAX数值应为3
00469145    48              DEC EAX
00469146    0F84 9B000000   JE Wrenju.004691E7                       ; 这里跳向注册成功  若想实现这里跳转算法CALL返回时EAX数值应为4
0046914C    E9 CD000000     JMP Wrenju.0046921E
00469151    8B0D 78444800   MOV ECX,DWORD PTR DS:[484478]            ; Wrenju.004A2784
00469157    8B09            MOV ECX,DWORD PTR DS:[ECX]
00469159    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
0046915E    8B00            MOV EAX,DWORD PTR DS:[EAX]
00469160    BA 01000000     MOV EDX,1
00469165    E8 4A2DFFFF     CALL Wrenju.0045BEB4
0046916A    83F8 01         CMP EAX,1
0046916D    74 05           JE SHORT Wrenju.00469174
0046916F    83F8 16         CMP EAX,16
00469172    75 15           JNZ SHORT Wrenju.00469189
00469174    6A 00           PUSH 0
00469176    68 00934600     PUSH Wrenju.00469300                     ; ASCII "Success"
0046917B    68 08934600     PUSH Wrenju.00469308                     ; ASCII "Application authorization complete"
00469180    6A 00           PUSH 0
00469182    E8 F9CFF9FF     CALL <JMP.&user32.MessageBoxA>
00469187    EB 0B           JMP SHORT Wrenju.00469194
00469189    BA 2C934600     MOV EDX,Wrenju.0046932C                  ; ASCII "Application Violation - Error #"
0046918E    92              XCHG EAX,EDX
0046918F    E8 28080000     CALL Wrenju.004699BC
00469194    E8 8FFCFFFF     CALL Wrenju.00468E28
00469199    A1 28434800     MOV EAX,DWORD PTR DS:[484328]
0046919E    C600 01         MOV BYTE PTR DS:[EAX],1
004691A1    E9 27010000     JMP Wrenju.004692CD
004691A6    8B15 78444800   MOV EDX,DWORD PTR DS:[484478]            ; Wrenju.004A2784
004691AC    8B12            MOV EDX,DWORD PTR DS:[EDX]
004691AE    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
004691B3    8B00            MOV EAX,DWORD PTR DS:[EAX]
004691B5    E8 EE2DFFFF     CALL Wrenju.0045BFA8
004691BA    83F8 01         CMP EAX,1
004691BD    74 10           JE SHORT Wrenju.004691CF
004691BF    BA 4C934600     MOV EDX,Wrenju.0046934C                  ; ASCII "De-authorization failed - Error #"
004691C4    92              XCHG EAX,EDX
004691C5    E8 F2070000     CALL Wrenju.004699BC
004691CA    E9 FE000000     JMP Wrenju.004692CD
004691CF    6A 00           PUSH 0
004691D1    68 00934600     PUSH Wrenju.00469300                     ; ASCII "Success"
004691D6    68 70934600     PUSH Wrenju.00469370                     ; ASCII "Application de-authorization complete"
004691DB    6A 00           PUSH 0
004691DD    E8 9ECFF9FF     CALL <JMP.&user32.MessageBoxA>
004691E2    E9 E6000000     JMP Wrenju.004692CD
004691E7    8B0D 78444800   MOV ECX,DWORD PTR DS:[484478]            ; 当退出算法CALL时,EAX=4时跳到这里
004691ED    8B09            MOV ECX,DWORD PTR DS:[ECX]
004691EF    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
004691F4    8B00            MOV EAX,DWORD PTR DS:[EAX]
004691F6    BA 01000000     MOV EDX,1
004691FB    E8 B42CFFFF     CALL Wrenju.0045BEB4
00469200    E8 4F080000     CALL Wrenju.00469A54                                    ; 弹出注册成功对话框 我们跟进看看
00469205    33C0            XOR EAX,EAX
00469207    E8 3C090000     CALL Wrenju.00469B48
0046920C    E8 17FCFFFF     CALL Wrenju.00468E28
00469211    A1 28434800     MOV EAX,DWORD PTR DS:[484328]
00469216    C600 01         MOV BYTE PTR DS:[EAX],1
00469219    E9 AF000000     JMP Wrenju.004692CD
0046921E    6A 00           PUSH 0
00469220    68 98934600     PUSH Wrenju.00469398                     ; ASCII "Invalid Code"
00469225    68 A8934600     PUSH Wrenju.004693A8                     ; ASCII "Invalid Code Entered!"
0046922A    6A 00           PUSH 0
0046922C    E8 4FCFF9FF     CALL <JMP.&user32.MessageBoxA>



我们跟进算法CALL 0045BEB4

0045C368    55              push    ebp                              ; EAX=假码
0045C369    8BEC            mov     ebp, esp                         ; ECX=(Computer Number)
0045C36B    53              push    ebx                              ; EDX=(Code Entry)
0045C36C    56              push    esi  
0045C36D    57              push    edi
0045C36E    8345 08 34      add     dword ptr [ebp+8], 34            ; [ebp+8]存放的是数值=Serial Number +36BH
0045C372    83F9 01         cmp     ecx, 1
0045C375    7D 05           jge     short 0045C37C                   ; 我们要注意这一个跳转,如果Computer Number为字母时,ECX和EDX均为0
0045C377    B9 01000000     mov     ecx, 1                           ; 若ECX为0,则这里向ECX赋值为1
0045C37C    8BDA            mov     ebx, edx
0045C37E    81E3 00E00700   and     ebx, 7E000                       ; EDX AND 7E000
0045C384    C1EB 0D         shr     ebx, 0D                          ; 逻辑右移 D
0045C387    81C3 BC070000   add     ebx, 7BC                         ; EBX+=7BC
0045C38D    6BDB 44         imul    ebx, ebx, 44                     ; *44
0045C390    8BF2            mov     esi, edx                         ; EDX放到ESI
0045C392    81E6 00007800   and     esi, 780000                      ; ESI AND 7E000
0045C398    C1EE 13         shr     esi, 13                          ; 逻辑右移 13
0045C39B    69F6 08010000   imul    esi, esi, 108                    ; *108
0045C3A1    03DE            add     ebx, esi                         ; EBX+=ESI
0045C3A3    8BF2            mov     esi, edx                         ; EDX放到ESI
0045C3A5    81E6 001F0000   and     esi, 1F00
0045C3AB    C1EE 08         shr     esi, 8
0045C3AE    69F6 F3000000   imul    esi, esi, 0F3
0045C3B4    03DE            add     ebx, esi                         ; 再相加
0045C3B6    8BF2            mov     esi, edx                         ; 再放ESI
0045C3B8    81E6 0000807F   and     esi, 7F800000
0045C3BE    C1EE 0F         shr     esi, 0F
0045C3C1    81E2 FF000000   and     edx, 0FF                         ; EDX AND FF
0045C3C7    03F2            add     esi, edx                         ; ESI+=EDX
0045C3C9    8D1476          lea     edx, dword ptr [esi+esi*2]       ; ESI*3 送EDX
0045C3CC    03C9            add     ecx, ecx                         ; ECX*2
0045C3CE    03D1            add     edx, ecx                         ; EDX+=ECX
0045C3D0    8BCA            mov     ecx, edx                         ; EDX放ECX
0045C3D2    8BD3            mov     edx, ebx                         ; EBX =》ECX
0045C3D4    C1E2 05         shl     edx, 5                           ; 逻辑左移5
0045C3D7    2BD3            sub     edx, ebx                         ; 减法
0045C3D9    8B75 08         mov     esi, dword ptr [ebp+8]           ; [ebp+8]存放的是数值=Serial Number +36BH +34H
0045C3DC    8BFE            mov     edi, esi
0045C3DE    C1E6 03         shl     esi, 3
0045C3E1    2BF7            sub     esi, edi                         ; -
0045C3E3    03CE            add     ecx, esi                         ; +
0045C3E5    03CA            add     ecx, edx
0045C3E7    8BF3            mov     esi, ebx
0045C3E9    03F6            add     esi, esi
0045C3EB    BA 01000000     mov     edx, 1                           ; EDX做计数器
0045C3F0    8BD9            mov     ebx, ecx                         ; ECX送EBX
0045C3F2    81E3 FFFFFF7F   and     ebx, 7FFFFFFF                    ; 与运算
0045C3F8    3BC3            cmp     eax, ebx                         ; EAX的十进制即为我们输入的假码          
0045C3FA    75 04           jnz     short 0045C400
0045C3FC    8BC2            mov     eax, edx                         ; EDX将循环结果放EAX中
0045C3FE    EB 0A           jmp     short 0045C40A
0045C400    03CE            add     ecx, esi
0045C402    42              inc     edx                              ; 循环到3或4时 EBX出现的便是真码//为何要循环三次或四次呢,我们看下文
0045C403    83FA 33         cmp     edx, 33
0045C406  ^ 75 E8           jnz     short 0045C3F0
0045C408    33C0            xor     eax, eax
0045C40A    5F              pop     edi
0045C40B    5E              pop     esi
0045C40C    5B              pop     ebx
0045C40D    5D              pop     ebp
0045C40E    C2 0400         retn    4


这里EAX=?EBX中即为真值是通过返回算法CALL后,是通过EDC EAX 下放这句JE==>注册成功来推断出来的。通过观察明显可看到当EAX=3时注册成功,当EAX=其他值时,我们也来看一下。当EAX=4时,退出算法CALL后来到这里:

004691E7    8B0D 78444800   MOV ECX,DWORD PTR DS:[484478]            ; 当退出算法CALL时,EAX=4时跳到这里
004691ED    8B09            MOV ECX,DWORD PTR DS:[ECX]
004691EF    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
004691F4    8B00            MOV EAX,DWORD PTR DS:[EAX]
004691F6    BA 01000000     MOV EDX,1
004691FB    E8 B42CFFFF     CALL Wrenju.0045BEB4                     
00469200    E8 4F080000     CALL Wrenju.00469A54                                     ; 弹出注册成功对话框 我们跟进看看

跟进CALL 00469A54:

00469A54    83C4 E8         ADD ESP,-18
00469A57    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
00469A5C    8B00            MOV EAX,DWORD PTR DS:[EAX]
00469A5E    B9 F09A4600     MOV ECX,Wrenju.00469AF0
00469A63    BA 09000000     MOV EDX,9
00469A68    E8 E722FFFF     CALL Wrenju.0045BD54
00469A6D    8D4424 08       LEA EAX,DWORD PTR SS:[ESP+8]
00469A71    50              PUSH EAX
00469A72    E8 11C0F9FF     CALL <JMP.&kernel32.GetLocalTime>
00469A77    8D4424 08       LEA EAX,DWORD PTR SS:[ESP+8]
00469A7B    E8 24ECF9FF     CALL Wrenju.004086A4
00469A80    D805 F49A4600   FADD DWORD PTR DS:[469AF4]
00469A86    DD1C24          FSTP QWORD PTR SS:[ESP]
00469A89    9B              WAIT
00469A8A    FF7424 04       PUSH DWORD PTR SS:[ESP+4]
00469A8E    FF7424 04       PUSH DWORD PTR SS:[ESP+4]
00469A92    8D4424 10       LEA EAX,DWORD PTR SS:[ESP+10]
00469A96    E8 D1EBF9FF     CALL Wrenju.0040866C
00469A9B    66:8B4424 0A    MOV AX,WORD PTR SS:[ESP+A]
00469AA0    50              PUSH EAX
00469AA1    66:8B4424 0C    MOV AX,WORD PTR SS:[ESP+C]
00469AA6    50              PUSH EAX
00469AA7    A1 E8424800     MOV EAX,DWORD PTR DS:[4842E8]
00469AAC    8B00            MOV EAX,DWORD PTR DS:[EAX]
00469AAE    66:8B4C24 16    MOV CX,WORD PTR SS:[ESP+16]
00469AB3    BA 02000000     MOV EDX,2
00469AB8    E8 5723FFFF     CALL Wrenju.0045BE14
00469ABD    83F8 01         CMP EAX,1
00469AC0    75 15           JNZ SHORT Wrenju.00469AD7
00469AC2    6A 00           PUSH 0
00469AC4    68 F89A4600     PUSH Wrenju.00469AF8                     ; ASCII "Success!"
00469AC9    68 049B4600     PUSH Wrenju.00469B04                     ; ASCII "Application authorization complete"
00469ACE    6A 00           PUSH 0
00469AD0    E8 ABC6F9FF     CALL <JMP.&user32.MessageBoxA>
00469AD5    EB 0B           JMP SHORT Wrenju.00469AE2

综上所述,退出算法CALL时EAX数值为3或4时,EBX保存的为真码。但我用KeyMake做了内存KG,取截取不到注册码,不知为何,KeyMake设置如下:


这款软件还有一种注册情况,就是Code Entry和Computer Number这里出现的并非数字,而是字母,此时只要将算法部分的变量A和B重新赋值即可。


我们看一下应该赋值为多少,我们看算法CALL中这个关键语句:

0045C372    83F9 01         cmp     ecx, 1
0045C375    7D 05           jge     short 0045C37C                   ; 我们要注意这一个跳转,如果Computer Number为字母时,ECX和EDX均为0
0045C377    B9 01000000     mov     ecx, 1                           ; 若ECX为0,则这里向ECX赋值为1

由此可见,将A赋值为0,B赋值为1即可。

算法总结:

情况1:
当Code Entry和Computer Number为数字时:

Code Entry      设为 A
Computer Number 设为 B
Serial Number   设为 C

D=[shr  d(A and 7E000)+7BC]*44H +[shr 13(A and 780000)]*108H +[shr  d(A)and 1F00]*0F3

F=(((shr f(A and 7F800000))+( A and FF))*3)+B*2 +(shl 3 (C+36BH+34H))-(C+36BH+34H)+(shl 5 D)-D

The Code =(循环三次){(F and 7FFFFFFF)+2*D }
或者
The Code =(循环四次){(F and 7FFFFFFF)+2*D}


情况2:
当Code Entry和Computer Number为字母时:

Code Entry      设为 A=0
Computer Number 设为 B=1
Serial Number   设为 C

算法同上。


软件将注册码保存到Bl&wh.dat这个文件中,算法部分研究完毕,再来搞一下暴破吧。

我们这里说的暴破呢,有这样几种情况是可以实现完美暴破的:
1.修改软件的功能部分和注册部分的标志位达到暴破。(多用与算法CALL唯一的情况)
2.修改关键跳转,使软件跳转到注册后实现的功能部分的地址。(强制暴破,多用于软件标志处出现“未注册”字样)
3.将真码保存到系统。(多用于真假码比较的软件)
4.将关键值保存到系统。(软件用检测是否保存关键值来判断软件是否已注册,关键值多保存在注册表后某文件中)

我们这款软件是真假码比较,所以采用第三中方式暴破,我们先看一下真假码比较部分:

0045C3EB    BA 01000000     mov     edx, 1                           ; EDX做计数器
0045C3F0    8BD9            mov     ebx, ecx                         ; ECX送EBX
0045C3F2    81E3 FFFFFF7F   and     ebx, 7FFFFFFF                    ; 与运算
0045C3F8    3BC3            cmp     eax, ebx                         ; EAX的十进制即为我们输入的假码          
0045C3FA    75 04           jnz     short 0045C400
0045C3FC    8BC2            mov     eax, edx                         
0045C3FE    EB 0A           jmp     short 0045C40A
0045C400    03CE            add     ecx, esi                         ; 我们在这里JMP出来写暴破数据 JMP 0048238A 
0045C402    42              inc     edx                              
0045C403    83FA 33         cmp     edx, 33
0045C406  ^ 75 E8           jnz     short 0045C3F0
0045C408    33C0            xor     eax, eax
0045C40A    5F              pop     edi
0045C40B    5E              pop     esi
0045C40C    5B              pop     ebx
0045C40D    5D              pop     ebp
0045C40E    C2 0400         retn    4


0048238A      83FA 03       CMP EDX,3
0048238D      74 08         JZ SHORT dump.00482397
0048238F      03CE          ADD ECX,ESI
00482391      42            INC EDX
00482392    ^ E9 59A0FDFF   JMP dump.0045C3F0
00482397      8BC3          MOV EAX,EBX
00482399    ^ E9 5AA0FDFF   JMP dump.0045C3F8

这段Patch写入后,软件注册成功后是无法正常运行的,至于是软件是哪里出的问题,我偷个懒留给读者吧,这里给出的就当是一种思路吧。