【文章标题】: Hide Window HotkeyV2.5注册算法分析
【文章作者】: TyroneKing
【作者邮箱】: tyroneking@126.com
【软件名称】: Hide Window Hotkey
【下载地址】: HWHSetup.rar
【KeyGen】: KeyGen.rar
【加壳方式】: ASPack 2.12 -> Alexey Solodovnikov
【保护方式】: 注册名+序列号
【编写语言】: Borland Delphi 6.0 - 7.0
【使用工具】: OD + PEiD+DeDe + MD5计算器+AspackDie+ IDA (可以不用,加强分析)
【操作平台】: WIN XP +SP3、 VC++6.0
【软件介绍】: 隐藏运行的窗口和应用程序软件。
【作者声明】: 只是为了学习,没有其他目的。失误之处敬请诸位大侠赐教!
【分析过程】
1、用PEiD查壳,为ASPack 2.12 -> Alexey Solodovnikov,用ESP定律很容易就可以脱壳了,不过这里还是用AspackDie,不对如何脱壳详说(我也是新手,脱一些简单的行,强壳,还得继续努力呀,:))。
2、用AspackDie脱壳,用PEiD查为Borland Delphi 6.0 - 7.0编写, KANAL 算法识别插件分析,软件用了MD5(地址004B549A)算法,用,直接运行脱壳后程序没啥反应,应该是自检验了。
3、用OD加载下bp GetFileSize 断点,程序中断在GetFileSize函数中,按Alt+F9返回程序领空 004030A1

引用:
00403097  |.  6A 00         PUSH 0                                   ; /pFileSizeHigh = NULL
00403099  |.  8B03          MOV EAX,DWORD PTR DS:[EBX]               ; |
0040309B  |.  50            PUSH EAX                                 ; |hFile
0040309C  |.  E8 FBE1FFFF   CALL <JMP.&kernel32.GetFileSize>         ; \GetFileSize
004030A1  |.  8BF0          MOV ESI,EAX                              ;  EAX=文件大小
……
004030C8  \.  C3            RETN
4、单步运行过004030C8后,跳出该函数,运行到004C4B8B
引用:
004C4B86  |.  E8 79DDF3FF   CALL unpacked.00402904               ;  取文件大小
004C4B8B  |.  3D 20A10700   CMP EAX,7A120                        ;  EAX为文件大小
004C4B90  |.  7E 15         JLE SHORT unpacked.004C4BA7          ;  <=7A120则跳,原文件大小6E000,现EAX=1A2000没跳
004C4B92  |.  8D85 ACFEFFFF LEA EAX,DWORD PTR SS:[EBP-154]       ;  将4C4B90跳为JMP,或将7A120改为比1A2000即可
004C4B98  |.  E8 3FE3F3FF   CALL unpacked.00402EDC
004C4B9D  |.  E8 62DDF3FF   CALL unpacked.00402904
004C4BA2  |.  E8 49F9F3FF   CALL unpacked.004044F0               ;  该函数中调用ExitProcess,退出程序
004C4BA7  |>  8D85 ACFEFFFF LEA EAX,DWORD PTR SS:[EBP-154]
5、将4C4B90改为JMP SHORT unpacked.004C4BA7,保存成Crack.exe文件,运行Crack.exe,程序正常运行(注意要先前面调试的OD,因为这个软件只允许一个进程运行)。
6、在Help->Register中打开注册窗口,输入Name和Code,提示错误,退出程序,分别用OD和DeDe加载,下面开始算法分析。
7、在DeDe中查找注册窗口的OK按钮事件
 
在OD中004B609C 下断点。
8、OD中运行程序,(在第2点中提到004B549A(MD5算法位置)附近 004B5499 下断点,为了判断程序是否用MD5加密)打开注册窗口Name输入 TYRONEKING,Code输入 0123456789,点击OK,OD在004B609C 中断。
引用:
004B609C  /.  55            PUSH EBP                                  ;  OKButton
……
004B60BE  |.  8D55 FC       LEA EDX,DWORD PTR SS:[EBP-4]
004B60C1  |.  8B83 2C030000 MOV EAX,DWORD PTR DS:[EBX+32C]            ;  TfrmRegister.eName
004B60C7  |.  E8 5061FAFF   CALL <Crack.TControl.GetText(TControl)>   ;  这里函数名是作了标签的,具体是哪个函数参考DeDe
004B60CC  |.  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]              ;  Tips:知道函数名后进入函数,按Shift+;(即输入冒号)给该函数作标签
004B60CF  |.  E8 ECE7F4FF   CALL <Crack.@LStrLen(String)>
004B60D4  |.  83F8 08       CMP EAX,8
004B60D7  |.  7D 3E         JGE SHORT Crack.004B6117                  ;  Name的长度<8 则提示错误
004B60D9  |.  6A 10         PUSH 10
004B60DB  |.  8D55 F8       LEA EDX,DWORD PTR SS:[EBP-8]
004B60DE  |.  A1 30E74C00   MOV EAX,DWORD PTR DS:[4CE730]
004B60E3  |.  E8 4005F5FF   CALL <Crack.LoadResString(PResStringRec)>
004B60E8  |.  8B45 F8       MOV EAX,DWORD PTR SS:[EBP-8]
004B60EB  |.  E8 D0E9F4FF   CALL <Crack.@LStrToPChar(String)>
004B60F0  |.  50            PUSH EAX
004B60F1  |.  8D55 F4       LEA EDX,DWORD PTR SS:[EBP-C]
004B60F4  |.  A1 C4E64C00   MOV EAX,DWORD PTR DS:[4CE6C4]
004B60F9  |.  E8 2A05F5FF   CALL <Crack.LoadResString(PResStringRec)> ;  在资源文件中提取“提示字符串”,避免通过字符串查找破解
004B60FE  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]
004B6101  |.  E8 BAE9F4FF   CALL <Crack.@LStrToPChar(String)>
004B6106  |.  50            PUSH EAX                                  ; |Text
004B6107  |.  E8 5010F5FF   CALL <JMP.&user32.GetActiveWindow>        ; |[GetActiveWindow
004B610C  |.  50            PUSH EAX                                  ; |hOwner
004B610D  |.  E8 7A12F5FF   CALL <JMP.&user32.MessageBoxA>            ; \MessageBoxA
004B6112  |.  E9 4F010000   JMP Crack.004B6266
004B6117  |>  8D55 F0       LEA EDX,DWORD PTR SS:[EBP-10]
004B611A  |.  8B83 2C030000 MOV EAX,DWORD PTR DS:[EBX+32C]            ;  (初始 cpu 选择)
004B6120  |.  E8 F760FAFF   CALL <Crack.TControl.GetText(TControl)>
004B6125  |.  8B55 F0       MOV EDX,DWORD PTR SS:[EBP-10]             ;  取Name
004B6128  |.  B8 DC624B00   MOV EAX,Crack.004B62DC                    ;  空格 20H
004B612D  |.  E8 D2EAF4FF   CALL <Crack.@LStrPos>                     ;  Name是否包含空格
004B6132  |.  85C0          TEST EAX,EAX
004B6134  |.  7E 3E         JLE SHORT Crack.004B6174                  ;  不包含则跳
……
004B6174  |>  8D55 E4       LEA EDX,DWORD PTR SS:[EBP-1C]
004B6177  |.  8B83 2C030000 MOV EAX,DWORD PTR DS:[EBX+32C]
004B617D  |.  E8 9A60FAFF   CALL <Crack.TControl.GetText(TControl)>
004B6182  |.  8B45 E4       MOV EAX,DWORD PTR SS:[EBP-1C]
004B6185  |.  E8 36E7F4FF   CALL <Crack.@LStrLen(String)>
004B618A  |.  8BF0          MOV ESI,EAX
004B618C  |.  83EE 02       SUB ESI,2
004B618F  |.  7C 36         JL SHORT Crack.004B61C7                   ;  Name的长度小于2,则错误
004B6191  |.  46            INC ESI                                   ;  /------------------------
004B6192  |.  BF 02000000   MOV EDI,2
004B6197  |>  8D55 E0       /LEA EDX,DWORD PTR SS:[EBP-20]
004B619A  |.  8B83 2C030000 |MOV EAX,DWORD PTR DS:[EBX+32C]
004B61A0  |.  E8 7760FAFF   |CALL <Crack.TControl.GetText(TControl)>  ;  取Name
004B61A5  |.  8B45 E0       |MOV EAX,DWORD PTR SS:[EBP-20]
004B61A8  |.  8A4438 FF     |MOV AL,BYTE PTR DS:[EAX+EDI-1]           ;  AL=Name[EDI-1]
004B61AC  |.  50            |PUSH EAX
004B61AD  |.  8D55 DC       |LEA EDX,DWORD PTR SS:[EBP-24]
004B61B0  |.  8B83 2C030000 |MOV EAX,DWORD PTR DS:[EBX+32C]
004B61B6  |.  E8 6160FAFF   |CALL <Crack.TControl.GetText(TControl)>
004B61BB  |.  8B45 DC       |MOV EAX,DWORD PTR SS:[EBP-24]
004B61BE  |.  5A            |POP EDX
004B61BF  |.  3A10          |CMP DL,BYTE PTR DS:[EAX]                 ;  Name[EDI-1]与Name[0]比较
004B61C1  |.  75 04         |JNZ SHORT Crack.004B61C7
004B61C3  |.  47            |INC EDI                                  ;  相等则 EDI+1
004B61C4  |.  4E            |DEC ESI
004B61C5  |.^ 75 D0         \JNZ SHORT Crack.004B6197
004B61C7  |>  8D55 D8       LEA EDX,DWORD PTR SS:[EBP-28]
004B61CA  |.  8B83 2C030000 MOV EAX,DWORD PTR DS:[EBX+32C]
004B61D0  |.  E8 4760FAFF   CALL <Crack.TControl.GetText(TControl)>
004B61D5  |.  8B45 D8       MOV EAX,DWORD PTR SS:[EBP-28]
004B61D8  |.  E8 E3E6F4FF   CALL <Crack.@LStrLen(String)>
004B61DD  |.  3BF8          CMP EDI,EAX
004B61DF  |.  7E 3B         JLE SHORT Crack.004B621C                  ;  EDI>EAX 提示出错
004B61E1  |.  6A 10         PUSH 10                                   ;  \若用户名为单一字符,出提示错误
……
004B621C  |>  8D55 CC       LEA EDX,DWORD PTR SS:[EBP-34]
004B621F  |.  8B83 2C030000 MOV EAX,DWORD PTR DS:[EBX+32C]
004B6225  |.  E8 F25FFAFF   CALL <Crack.TControl.GetText(TControl)>   ;  取Name
004B622A  |.  8B55 CC       MOV EDX,DWORD PTR SS:[EBP-34]
004B622D  |.  8BC3          MOV EAX,EBX
004B622F  |.  E8 CC020000   CALL Crack.004B6500                       ;  计算Code
004B6234  |.  8D55 C8       LEA EDX,DWORD PTR SS:[EBP-38]
004B6237  |.  8B83 30030000 MOV EAX,DWORD PTR DS:[EBX+330]
004B623D  |.  E8 DA5FFAFF   CALL <Crack.TControl.GetText(TControl)>   ;  取假Code
004B6242  |.  8B55 C8       MOV EDX,DWORD PTR SS:[EBP-38]
004B6245  |.  8D83 38030000 LEA EAX,DWORD PTR DS:[EBX+338]            ;  保假Code
004B624B  |.  E8 04E4F4FF   CALL <Crack.@LStrAsg>
9、单步跟进 004B622F  |.  E8 CC020000   CALL Crack.004B6500                       ;  计算Code
进一步分析
引用:
004B652D  |.  837D F8 00    CMP DWORD PTR SS:[EBP-8],0
004B6531  |.  0F84 FB000000 JE Crack.004B6632                      ;  Name为空?
004B6537  |.  8D45 F4       LEA EAX,DWORD PTR SS:[EBP-C]           ;  /-----------
004B653A  |.  8B55 F8       MOV EDX,DWORD PTR SS:[EBP-8]
004B653D  |.  E8 56E1F4FF   CALL <Crack.@LStrLAsg(void;void;void;v>
004B6542  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]
004B6545  |.  E8 76E3F4FF   CALL <Crack.@LStrLen(String)>          ;  取Name的长度
004B654A  |.  8BF8          MOV EDI,EAX
004B654C  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]           ;  Name
004B654F  |.  8A00          MOV AL,BYTE PTR DS:[EAX]               ;  AL=Name[0]
004B6551  |.  8845 F3       MOV BYTE PTR SS:[EBP-D],AL
004B6554  |.  8BF7          MOV ESI,EDI                            ;  ESI=Name的长度
004B6556  |.  4E            DEC ESI
004B6557  |.  85F6          TEST ESI,ESI
004B6559  |.  7E 22         JLE SHORT Crack.004B657D
004B655B  |.  BB 01000000   MOV EBX,1
004B6560  |>  8D45 F4       /LEA EAX,DWORD PTR SS:[EBP-C]
004B6563  |.  E8 B0E5F4FF   |CALL <Crack.InternalUniqueString>     ;  UniqueString is used only in cases where an application casts a string to a PChar or PWideChar and then modifies the contents of the string.
004B6568  |.  8B55 F4       |MOV EDX,DWORD PTR SS:[EBP-C]
004B656B  |.  8A541A FF     |MOV DL,BYTE PTR DS:[EDX+EBX-1]        ;  DL=Name[EBX-1]
004B656F  |.  8B4D F4       |MOV ECX,DWORD PTR SS:[EBP-C]
004B6572  |.  0A1419        |OR DL,BYTE PTR DS:[ECX+EBX]           ;  DL=Name[EBX-1] OR Name[EBX]
004B6575  |.  885418 FF     |MOV BYTE PTR DS:[EAX+EBX-1],DL        ;  结果存在[EBP-C]的EBX-1中
004B6579  |.  43            |INC EBX
004B657A  |.  4E            |DEC ESI
004B657B  |.^ 75 E3         \JNZ SHORT Crack.004B6560              ;  处理完Len-1位了?
004B657D  |>  8D45 F4       LEA EAX,DWORD PTR SS:[EBP-C]
004B6580  |.  E8 93E5F4FF   CALL <Crack.InternalUniqueString>
004B6585  |.  8B55 F4       MOV EDX,DWORD PTR SS:[EBP-C]
004B6588  |.  8A543A FF     MOV DL,BYTE PTR DS:[EDX+EDI-1]         ;  DL=Name的最后一位
004B658C  |.  0A55 F3       OR DL,BYTE PTR SS:[EBP-D]              ;  与004B6551对应,即Name[0]
004B658F  |.  885438 FF     MOV BYTE PTR DS:[EAX+EDI-1],DL         ;  DL=Name[Len-1] OR Name[0]
004B6593  |.  8D4D EC       LEA ECX,DWORD PTR SS:[EBP-14]          ;  \--对Name的第i位与第i+1位(最后一位与第0位)进行OR运算
004B6596  |.  8B55 F4       MOV EDX,DWORD PTR SS:[EBP-C]           ;  记为NameOR,这里="][_OOOKOOW"
004B6599  |.  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004B659C  |.  E8 C3000000   CALL Crack.004B6664                    ;  第二层运算
10、单步跟进004B659C  |.  E8 C3000000   CALL Crack.004B6664                    ;  第二层运算
进一步分析
引用:
004B6694  |.  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]         ;  NameOR
004B6697  |.  E8 24E2F4FF   CALL <Crack.@LStrLen(String)>
004B669C  |.  8B55 FC       MOV EDX,DWORD PTR SS:[EBP-4]         ;  /---EAX=长度,EDX=NameOR
004B669F  |.  8A5402 FF     MOV DL,BYTE PTR DS:[EDX+EAX-1]       ;  DL=NameOR[Len-1]
004B66A3  |.  8855 FB       MOV BYTE PTR SS:[EBP-5],DL           ;  NameOR的最后一位
004B66A6  |.  8BD8          MOV EBX,EAX
004B66A8  |.  83FB 02       CMP EBX,2
004B66AB  |.  7C 19         JL SHORT Crack.004B66C6
004B66AD  |>  8D45 FC       /LEA EAX,DWORD PTR SS:[EBP-4]
004B66B0  |.  E8 63E4F4FF   |CALL <Crack.InternalUniqueString>
004B66B5  |.  8B55 FC       |MOV EDX,DWORD PTR SS:[EBP-4]        ;  NameOR="][_OOOKOOW"
004B66B8  |.  8A541A FE     |MOV DL,BYTE PTR DS:[EDX+EBX-2]      ;  DL=NameOR[EBX-2]
004B66BC  |.  885418 FF     |MOV BYTE PTR DS:[EAX+EBX-1],DL      ;  NameOR[EBX-1]=DL=NameOR[EBX-2],即后移一位
004B66C0  |.  4B            |DEC EBX
004B66C1  |.  83FB 01       |CMP EBX,1
004B66C4  |.^ 75 E7         \JNZ SHORT Crack.004B66AD            ;  移了Len-1次?
004B66C6  |>  8D45 FC       LEA EAX,DWORD PTR SS:[EBP-4]
004B66C9  |.  E8 4AE4F4FF   CALL <Crack.InternalUniqueString>
004B66CE  |.  8A55 FB       MOV DL,BYTE PTR SS:[EBP-5]           ;  NameOR的最后一位
004B66D1  |.  8810          MOV BYTE PTR DS:[EAX],DL             ;  NameOR[0]=Name[Len-1]
004B66D3  |.  8D55 E4       LEA EDX,DWORD PTR SS:[EBP-1C]        ;  \--以上算法完成对NameOR进行循环右移一位
004B66D6  |.  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]         ;  运算后NameOR="W][_OOOKOO"
004B66D9  |.  E8 1AF6FFFF   CALL <Crack.MD5?>                    ;  用F8步过,时为在前面下的 004B5499(MD5) 中断,初步判断该函数为MD5加密
004B66DE  |.  8D45 E4       LEA EAX,DWORD PTR SS:[EBP-1C]
004B66E1  |.  8D55 F4       LEA EDX,DWORD PTR SS:[EBP-C]
004B66E4  |.  E8 9BF5FFFF   CALL <Crack.HEX2Str>                 ;  将HEX转成STR
004B66E9  |.  8B55 F4       MOV EDX,DWORD PTR SS:[EBP-C]         ;  EDX=         1599C9A545CE5B7CBD605583D82E45B5
004B66EC  |.  8D45 FC       LEA EAX,DWORD PTR SS:[EBP-4]         ;  NameOR的MD5值 1599C9A545CE5B7C60B7CD3D35D8CCFA前16位一致
004B66EF  |.  E8 A4DFF4FF   CALL <Crack.@LStrLAsg(void;void;void>;  这里先不分析为什么后16位不一致,猜想作者可能对MD5算法变形了吧
004B66F4  |.  8BC7          MOV EAX,EDI
004B66F6  |.  E8 05DFF4FF   CALL <Crack.@LStrClr(void;void)>
004B66FB  |.  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004B66FE  |.  E8 BDE1F4FF   CALL <Crack.@LStrLen(String)>
004B6703  |.  8BF0          MOV ESI,EAX                          ;  NameORMD5的长度
004B6705  |.  85F6          TEST ESI,ESI
004B6707  |.  7E 3D         JLE SHORT Crack.004B6746
004B6709  |.  BB 01000000   MOV EBX,1                            ;  /-------------------
004B670E  |>  8B45 FC       /MOV EAX,DWORD PTR SS:[EBP-4]
004B6711  |.  8A4418 FF     |MOV AL,BYTE PTR DS:[EAX+EBX-1]      ;  对NameORMD5[EBX-1]位
004B6715  |.  3C 30         |CMP AL,30
004B6717  |.  72 29         |JB SHORT Crack.004B6742             ;  <'0'
004B6719  |.  3C 39         |CMP AL,39
004B671B  |.  77 25         |JA SHORT Crack.004B6742             ;  >'9'
004B671D  |.  8D45 E0       |LEA EAX,DWORD PTR SS:[EBP-20]       ;  >='0' AND <='9'
004B6720  |.  8B55 FC       |MOV EDX,DWORD PTR SS:[EBP-4]
004B6723  |.  8A541A FF     |MOV DL,BYTE PTR DS:[EDX+EBX-1]
004B6727  |.  E8 BCE0F4FF   |CALL <Crack.StrFromChar>
004B672C  |.  8B55 E0       |MOV EDX,DWORD PTR SS:[EBP-20]
004B672F  |.  8BC7          |MOV EAX,EDI
004B6731  |.  E8 92E1F4FF   |CALL <Crack.@LStrCat>
004B6736  |.  8B07          |MOV EAX,DWORD PTR DS:[EDI]
004B6738  |.  E8 83E1F4FF   |CALL <Crack.@LStrLen(String)>
004B673D  |.  83F8 0A       |CMP EAX,0A
004B6740  |.  7D 04         |JGE SHORT Crack.004B6746            ;  >=10,取前10位数字部分
004B6742  |>  43            |INC EBX
004B6743  |.  4E            |DEC ESI
004B6744  |.^ 75 C8         \JNZ SHORT Crack.004B670E
004B6746  |>  33C0          XOR EAX,EAX                          ;  \以上算法为取MD5值的前10位数字
……
004B676B  \.  C3            RETN
11、在004B676B返回后回到 004B65A1
引用:
004B659C  |.  E8 C3000000   CALL Crack.004B6664                  ;  第二层运算
004B65A1  |.  8B55 EC       MOV EDX,DWORD PTR SS:[EBP-14]        ;  运算结果="1599954557"
004B65A4  |.  8D45 F4       LEA EAX,DWORD PTR SS:[EBP-C]
004B65A7  |.  E8 ECE0F4FF   CALL <Crack.@LStrLAsg(void;void;void>
004B65AC  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]
004B65AF  |.  E8 0CE3F4FF   CALL <Crack.@LStrLen(String)>
004B65B4  |.  8BF8          MOV EDI,EAX                          ;  /-------------------
004B65B6  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]
004B65B9  |.  8A4438 FF     MOV AL,BYTE PTR DS:[EAX+EDI-1]
004B65BD  |.  8845 F3       MOV BYTE PTR SS:[EBP-D],AL
004B65C0  |.  8BDF          MOV EBX,EDI
004B65C2  |.  83FB 02       CMP EBX,2
004B65C5  |.  7C 19         JL SHORT Crack.004B65E0
004B65C7  |>  8D45 F4       /LEA EAX,DWORD PTR SS:[EBP-C]
004B65CA  |.  E8 49E5F4FF   |CALL <Crack.InternalUniqueString>
004B65CF  |.  B55 F4       |MOV EDX,DWORD PTR SS:[EBP-C]
004B65D2  |.  8A541A FE     |MOV DL,BYTE PTR DS:[EDX+EBX-2]
004B65D6  |.  885418 FF     |MOV BYTE PTR DS:[EAX+EBX-1],DL      ;  NameORMD5[i-1]=NameORMD5[i-2],右移一位
004B65DA  |.  4B            |DEC EBX
004B65DB  |.  83FB 01       |CMP EBX,1
004B65DE  |.^ 75 E7         \JNZ SHORT Crack.004B65C7            ;  处理Len-1次
004B65E0  |>  8D45 F4       LEA EAX,DWORD PTR SS:[EBP-C]
004B65E3  |.  E8 30E5F4FF   CALL <Crack.InternalUniqueString>
004B65E8  |.  8A55 F3       MOV DL,BYTE PTR SS:[EBP-D]
004B65EB  |.  8810          MOV BYTE PTR DS:[EAX],DL             ;  \---循环右移一位
004B65ED  |.  8B45 F4       MOV EAX,DWORD PTR SS:[EBP-C]         ;  KeyFind="7159995455"
004B65F0  |.  E8 CBE2F4FF   CALL <Crack.@LStrLen(String)>
004B65F5  |.  8BF0          MOV ESI,EAX
004B65F7  |.  85F6          TEST ESI,ESI
004B65F9  |.  7E 27         JLE SHORT Crack.004B6622
004B65FB  |.  BB 01000000   MOV EBX,1
004B6600  |>  8D45 F4       /LEA EAX,DWORD PTR SS:[EBP-C]
004B6603  |.  E8 10E5F4FF   |CALL <Crack.InternalUniqueString>
004B6608  |.  8B55 F4       |MOV EDX,DWORD PTR SS:[EBP-C]
004B660B  |.  0FB6541A FF   |MOVZX EDX,BYTE PTR DS:[EDX+EBX-1]   ;  取第[EBX-1]位
004B6610  |.  8B0D 00E44C00 |MOV ECX,DWORD PTR DS:[4CE400]       ;  KeyStr="5497312680"
004B6616  |.  8A5411 D0     |MOV DL,BYTE PTR DS:[ECX+EDX-30]     ;  取固定串中[EBX-1]对应数位数的字符
004B661A  |.  885418 FF     |MOV BYTE PTR DS:[EAX+EBX-1],DL      ;  Code[EBX-1]=KeyStr[KeyFind[EBX-1]-30]
004B661E  |.  43            |INC EBX
004B661F  |.  4E            |DEC ESI
004B6620  |.^ 75 DE         \JNZ SHORT Crack.004B6600
004B6622  |>  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004B6625  |.  05 34030000   ADD EAX,334                                                         ;  注册码保存地址
004B662A  |.  8B55 F4       MOV EDX,DWORD PTR SS:[EBP-C]                                        ;  最终注册码="6410001311"
004B662D  |.  E8 22E0F4FF   CALL <Crack.@LStrAsg>
004B6632  |>  33C0          XOR EAX,EAX
……
004B6654  \.  C3            RETN
12、返回到 004B6234 
引用:
004B622F  |.  E8 CC020000   CALL Crack.004B6500                        ;  计算Code
004B6234  |.  8D55 C8       LEA EDX,DWORD PTR SS:[EBP-38]
004B6237  |.  8B83 30030000 MOV EAX,DWORD PTR DS:[EBX+330]
004B623D  |.  E8 DA5FFAFF   CALL <Crack.TControl.GetText(TControl)>    ;  取假Code
004B6242  |.  8B55 C8       MOV EDX,DWORD PTR SS:[EBP-38]
004B6245  |.  8D83 38030000 LEA EAX,DWORD PTR DS:[EBX+338]             ;  保存假Code,
004B624B  |.  E8 04E4F4FF   CALL <Crack.@LStrAsg>
004B6250  |.  6A 00         PUSH 0                                     ;  lParam = 0
004B6252  |.  6A 00         PUSH 0                                     ;  wParam = 0
004B6254  |.  68 06140000   PUSH 1406                                  ;  Message = MSG(1406)
004B6259  |.  8BC3          MOV EAX,EBX
004B625B  |.  E8 A4C7FAFF   CALL <Crack.QForms.TCustomForm.GetClientHa>
004B6260  |.  50            PUSH EAX                                   ; |hWnd
004B6261  |.  E8 4E11F5FF   CALL <JMP.&user32.PostMessageA>            ; \通过提交消息来处判断注册码是否正确,在这里下 hr EBX+338 硬件访问断点,这样在消息处理访问到假码就可以中断下来。
13、在 004B6261下 hr EBX+338 硬件访问断点后,按F9运行,在 004B6373 中断
004B636D  |.  8B83 38030000 MOV EAX,DWORD PTR DS:[EBX+338]             ;  假码
004B6373  |.  E8 48E7F4FF   CALL <Crack.@LStrToPChar(String)>
004B6378  |.  50            PUSH EAX
004B6379  |.  8B83 34030000 MOV EAX,DWORD PTR DS:[EBX+334]             ;  真码
004B637F  |.  E8 3CE7F4FF   CALL <Crack.@LStrToPChar(String)>
004B6384  |.  5A            POP EDX
004B6385  |.  E8 9E31F5FF   CALL <Crack.StrComp(PChar;PChar)>          ;  关键比较
004B638A  |.  85C0          TEST EAX,EAX
004B638C  |.  75 70         JNZ SHORT Crack.004B63FE                   ;  关键跳
……
004B639E  |.  BA 44644B00   MOV EDX,Crack.004B6444                     ;  ASCII "Software\Hide Window Hotkey"
……
004B63BB  |.  BA 68644B00   MOV EDX,Crack.004B6468                     ;  ASCII "Name"
……
004B63D8  |.  BA 78644B00   MOV EDX,Crack.004B6478                     ;  ASCII "Code"
004B63DD  |.  8BC6          MOV EAX,ESI
004B63DF  |.  E8 7C47F8FF   CALL <Crack.TRegistry.WriteString>         ;  写注册表
……
004B63F2  |.  C783 4C020000>MOV DWORD PTR DS:[EBX+24C],1               ;  注册标志?
004B63FC  |.  EB 16         JMP SHORT Crack.004B6414
004B63FE  |>  6A 00         PUSH 0                                     ;  错误提示
004B6400  |.  6A 00         PUSH 0
004B6402  |.  68 07140000   PUSH 1407                                  ;  MSG=1407
004B6407  |.  8BC3          MOV EAX,EBX
004B6409  |.  E8 F6C5FAFF   CALL <Crack.QForms.TCustomForm.GetClientHa>
004B640E  |.  50            PUSH EAX                                   ; |hWnd
004B640F  |.  E8 A00FF5FF   CALL <JMP.&user32.PostMessageA>            ; \PostMessageA
14、以上对程序的注册算法以基本分析完成,但要写出注册机,还是分析作者对MD5算法做了什么手脚,导致该程序的MD5加密结果与正常的MD5加密有所不同,经分析(我是通过一个正常的MD5calculator.,反编译后与该程序的MD5算法进行比较,最终发现差别之处,这里不详细说明):
引用:
004B5B13  |.  68 91D386EB   PUSH EB86D391                            ;  MD5常量
004B5B18  |.  8BC6          MOV EAX,ESI
004B5B1A  |.  8B4D 00       MOV ECX,DWORD PTR SS:[EBP]
004B5B1D  |.  8B17          MOV EDX,DWORD PTR DS:[EDI]
004B5B1F  |.  E8 28F8FFFF   CALL <Crack.II>
004B5B24  |.  8B0424        MOV EAX,DWORD PTR SS:[ESP]               ;  中间计算一样,最后结果相加
004B5B27  |.  8B13          MOV EDX,DWORD PTR DS:[EBX]
004B5B29  |.  0110          ADD DWORD PTR DS:[EAX],EDX               ;      [0]=67452301+3E847614=A5C99915
004B5B2B  |.  8B0424        MOV EAX,DWORD PTR SS:[ESP]               ;  正常[0]=67452301+3E847614=A5C99915
004B5B2E  |.  8B16          MOV EDX,DWORD PTR DS:[ESI]
004B5B30  |.  0150 04       ADD DWORD PTR DS:[EAX+4],EDX             ;      [1]=EFCDAB89+8C8E22BC=7C5BCE45
004B5B33  |.  8B0424        MOV EAX,DWORD PTR SS:[ESP]               ;  正常[1]=EFCDAB89+8C8E22BC=7C5BCE45
004B5B36  |.  8B17          MOV EDX,DWORD PTR DS:[EDI]
004B5B38  |.  0150 0C       ADD DWORD PTR DS:[EAX+C],EDX             ;      [3]=10325476+A512DA62=B5452ED8
004B5B3B  |.  8B0424        MOV EAX,DWORD PTR SS:[ESP]               ;  正常[3]=10325476+EA9A83BF=FACCD835
004B5B3E  |.  8B55 00       MOV EDX,DWORD PTR SS:[EBP]
004B5B41  |.  0150 08       ADD DWORD PTR DS:[EAX+8],EDX             ;      [2]=98BADCFE+EA9A83BF=835560BD
004B5B44  |.  8D4424 18     LEA EAX,DWORD PTR SS:[ESP+18]            ;  正常[2]=98BADCFE+A512DA62=3DCDB760
对应为MD5算法里的MD5Update=》MD5Transform函数中 进行4轮变化后结果相加时作了修改
正常MD5算法:
引用:
  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
该程序MD5算法:
引用:
  state[0] += a;
  state[1] += b;
  state[2] += d;
  state[3] += c;
以上是对该程序的所有注册算法进行分析。
-------------------------------------------------------------------------
【算法总结】
1、  用户名长度不能小于8,不能包含空格,不能为单一字符。
2、  对用户名的第i位与第i+1位(最后一位与第0位)进行OR运算。
3、  对2的结果循环右移一字节
4、  对3的结果进行变形MD5运算(该结果与正常MD5值的前16字节一致,后16字节不同)
5、  取4的结果前10位数字部分
6、  对5的结果循环右移一字节
7、  根据6的结果对应的数字取固定字符串“5497312680”对应位,即为注册码。
这里附上用VC++6.0编写的注册机的原码,注册机是经《加密与解密》第三版 6.1.1 MD5算法的MD5calculator修改而成的。
下面是部分原码:
代码:
MD5_CTX context;
  long dtLength;
  int i;
  int len;
  int j;
  TCHAR *szKeyStr="5497312680";
  TCHAR sTemp=' ';

  TCHAR szName[MAXINPUTLEN]={0};
  TCHAR szHash[MAXINPUTLEN]={0};
  TCHAR szBuffer[MAXINPUTLEN]={0};

  dtLength=GetDlgItemText(hWnd, IDC_TXT0, szName, sizeof(szName)/sizeof(TCHAR)+1);  
  len=strlen(szName);
  if (len<=8)//长度判断
  {
    SetDlgItemText(hWnd, IDC_TXT1, "Name必须大于8位!");
    return FALSE;
  }
  for(i=0;i < len; i++)//是否包含 空格
    if(szName[i]==' ')
    {
    SetDlgItemText(hWnd, IDC_TXT1, "Name不能包含空格!");
    return FALSE;
    }      
  sTemp=szName[0];
  i=1;
  while(i<len)
  {
    if(sTemp==szName[i])
      i++;
    else
      break;
  }
  if(i>=len)
  {
    SetDlgItemText(hWnd, IDC_TXT1, "Name不能为单一字符!");
    return FALSE;
  }

  for(i=0;i < len -1; i++)
    szName[i]=szName[i] | szName[i+1];
  szName[len-1]=szName[len-1] | sTemp;//各字节进行 OR 运算
  
  sTemp=szName[len-1];
  for(i=len-1; i > 0 ;i--)
    szName[i]=szName[i-1];
  szName[0]=sTemp;//循环右移一字节

  MD5Init(&context);
  MD5Update(&context, szName, dtLength);//该函数里的MD5Transform函数变形了
  MD5Final(szHash, &context);
  for(i=0; i < 16; i++)   // 将szHash[]中的16进制转换成字符形式显示
  {
    wsprintf(&szBuffer[i*2], "%02X", *(byte*)(szHash+i));
  }//变形MD5
  
  i=0;
  len=0;
  while(len<10 && i<32)
  {
    if(szBuffer[i]>='0' && szBuffer[i]<='9')
    {
      szName[len]=szBuffer[i];
      len+=1;
    }
    i++;
  }//取前10位数字
  szName[len]='\0';
  if(len<1)
  {
    SetDlgItemText(hWnd, IDC_TXT1,"注册码为空?!");       
    return TRUE;
  }

  sTemp=szName[len-1];
  for(i=len-1; i > 0 ;i--)
    szName[i]=szName[i-1];
  szName[0]=sTemp;//循环右移一字节

  for(i=0; i < len ;i++)
  {
    j=szName[i]-'0';
    szName[i]=szKeyStr[j];//根据szName[i]值对取应szKeyStr值
  }

  SetDlgItemText(hWnd, IDC_TXT1,szName);       
  return TRUE;  
【总结】
1、软件虽然经过多次运算,但注册码还是在内存中出现,不分析算法也可以轻易写出内存注册机。
2、不知作者是有意还是无意(为什么只变形后16位,而不32位一起变形呢?)将MD5变形了,这点会加大算法分析者的难度。
3、将注册码计算、注册码判断、错误提示分开,多少会加大破解的难度(至少对于新手,本人一开始为了查找消息处理函数就花了大量时间)。
4、没有很好地加解密提示字符串(或不提示),将提示串放在资源文件也能简单防止通过字符串查找破解。
5、注册码与机器无关,只要有一对正确用户名和注册码就可以任意机器注册。
第一次写破文,不足之处还望大侠多多指教!谢谢!
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2010年04月09日