转轮里的秘密,跟踪一非经典算法
Phatnotes是一款功能强大的信息管理软件,同时拥有Pocket PC端、PalmOS端以及PC端版本。内置强大的文本搜索、文档安全保护和email收发功能(同时允许你快速查看email地址和联络信息)。特点如下:1.可以按照主题,类别,创建/修改时间对信息进行归类 2.数据兼容性良好,可以随意导出/导入文本信息,或在不同程序间进行复制、粘贴操作 3.程序、数据占用存储空间小 4.支持对数据进行密码保护 5.简洁直观的用户操作界面 6.可以在记事编辑器中选择Send mail按钮来直接进行邮件发送 7.支持用户自定义浏览方式/参数
8.强大的文档过滤、搜索功能 9.支持多记事数据库同步 10.支持Microsoft Outlook,导入或到处Outlook记事和邮件信息 11.内置记事类别管理功能,允许用户按记事的修改日期、类别、颜色或优先级进行分类 12.支持记事间的超链接跳转 13.支持文件附件(一个记事文件最多支持32个附件链接) 14.提供目录树的数据浏览模式 15.支持拼写检查 16.支持文本格式设定 17.支持PhatPad直接调用 18.可以与Palm OS用户(装有PhatNotes)的数据进行同步。此为Phatnotes的专业版,在同步更新功能方面做了改进。支持在Pocket PC与
PC(或PalmOS)间对多个数据库中的记事,记事间的链接,文件附件和数据库层次等信息进行同步。
软件主页:
http://www.phatware.com/
最近总是心情浮躁,看到软件爆破了之。很是不爽。定下决心,潜心看看这个软件的算法,以解郁闷。
其实这个软件有2种注册方式,对应不同的版本,其中一种注册码是:4000610710001 而另外一种注册码格式是xxxxx-xxxxx-xxxxx-xxxxx-xxxxx 这就是我为什么要跟踪他的原因了。(是在跟踪中无意看到算法要临时调用dll才发现的)
在OD中bp LoadLibraryA 来到核心:
0046DF70 /$ 53 PUSH EBX
0046DF71 |. 56 PUSH ESI
0046DF72 |. 68 D8105500 PUSH 005510D8 ; /FileName = "PltInstall.dll"
0046DF77 |. FF15 60525000 CALL [<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA
0046DF7D |. 8BF0 MOV ESI, EAX
0046DF7F |. 32DB XOR BL, BL
0046DF81 |. 85F6 TEST ESI, ESI
0046DF83 |. 74 5A JE SHORT 0046DFDF
0046DF85 |. 68 C4105500 PUSH 005510C4 ; /ProcNameOrOrdinal = "PW_IsCodeCorrect"
0046DF8A |. 56 PUSH ESI ; |hModule
0046DF8B |. FF15 64525000 CALL [<&KERNEL32.GetProcAddress>] ; \GetProcAddress
0046DF91 |. 85C0 TEST EAX, EAX
0046DF93 |. 74 43 JE SHORT 0046DFD8
0046DF95 |. 8A4C24 14 MOV CL, [ESP+14]
0046DF99 |. 84C9 TEST CL, CL
0046DF9B |. 74 2B JE SHORT 0046DFC8
0046DF9D |. 8B4C24 10 MOV ECX, [ESP+10]
0046DFA1 |. 85C9 TEST ECX, ECX
0046DFA3 |. 74 23 JE SHORT 0046DFC8
0046DFA5 |. 8039 00 CMP BYTE PTR [ECX], 0
0046DFA8 |. 74 1E JE SHORT 0046DFC8
0046DFAA |. 51 PUSH ECX
0046DFAB |. 8B4C24 10 MOV ECX, [ESP+10]
0046DFAF |. 68 01000010 PUSH 10000001
0046DFB4 |. 51 PUSH ECX
0046DFB5 |. FFD0 CALL EAX ; PltInsta.PW_IsCodeCorrect 调用dll的这个函数
0046DFB7 |. 85C0 TEST EAX, EAX
0046DFB9 |. 56 PUSH ESI ; /hLibModule
0046DFBA |. 0F95C3 SETNE BL ; |
0046DFBD |. FF15 68525000 CALL [<&KERNEL32.FreeLibrary>] ; \FreeLibrary
0046DFC3 |. 8AC3 MOV AL, BL
0046DFC5 |. 5E POP ESI
0046DFC6 |. 5B POP EBX
0046DFC7 |. C3 RETN
0046DFC8 |> 8B5424 0C MOV EDX, [ESP+C]
0046DFCC |. 6A 00 PUSH 0
0046DFCE |. 6A 01 PUSH 1
0046DFD0 |. 52 PUSH EDX
0046DFD1 |. FFD0 CALL EAX
0046DFD3 |. 85C0 TEST EAX, EAX
0046DFD5 |. 0F95C3 SETNE BL
0046DFD8 |> 56 PUSH ESI ; /hLibModule
0046DFD9 |. FF15 68525000 CALL [<&KERNEL32.FreeLibrary>] ; \FreeLibrary
0046DFDF |> 8AC3 MOV AL, BL
0046DFE1 |. 5E POP ESI
0046DFE2 |. 5B POP EBX
0046DFE3 \. C3 RETN
01302900 >SUB ESP, 114 ; 进入算法核心
01302906 >PUSH EBX
01302907 >PUSH EBP
01302908 >MOV EBP, [ESP+120]
0130290F >LEA EAX, [ESP+1D]
01302913 >PUSH ESI
01302914 >MOV [ESP+10], ECX
01302918 >PUSH EDI
01302919 >MOV [ESP+10], EAX
0130291D >MOV EDI, EBP
0130291F >OR ECX, FFFFFFFF
01302922 >XOR EAX, EAX
01302924 >XOR EDX, EDX
01302926 >XOR ESI, ESI
01302928 >LEA EBX, [ESP+20]
0130292C >REPNE SCAS BYTE PTR ES:[EDI]
0130292E >NOT ECX
01302930 >DEC ECX
01302931 >JE SHORT 01302997
01302933 >MOV AL, [EDX+EBP]
01302936 >CMP AL, 2D ; 是 - 就去掉
01302938 >JE SHORT 01302986
0130293A >INC ESI
0130293B >CMP ESI, 5 ; 每组五位
0130293E >JB SHORT 01302947
01302940 >MOV [EBX], AL ; 取每组的第五位
01302942 >INC EBX
01302943 >XOR ESI, ESI
01302945 >JMP SHORT 01302986
01302947 >CMP AL, [13098A8] ; 如果是数字必须在2-9之间 内存=2
0130294D >JL SHORT 01302957
0130294F >CMP AL, [13098AB] ; 内存=9
01302955 >JLE SHORT 0130296B
01302957 >CMP AL, [13098A4] ; 如果是字符在A-Z之间 DS:[013098A6]=4F ('O')
0130295D >JL 013029E6
01302963 >CMP AL, [13098A5] ; DS:[013098A5]=5A ('Z')
01302969 >JG SHORT 013029E6
0130296B >CMP AL, [13098A7] ; 注册码中不能有I DS:[013098A7]=49 ('I')
01302971 >JE SHORT 013029E6
01302973 >CMP AL, [13098A6] ; 注册码中不能有O DS:[013098A6]=4F ('O')
01302979 >JE SHORT 013029E6
0130297B >MOV ECX, [ESP+10]
0130297F >MOV [ECX], AL
01302981 >INC ECX
01302982 >MOV [ESP+10], ECX
01302986 >MOV EDI, EBP
01302988 >OR ECX, FFFFFFFF
0130298B >XOR EAX, EAX
0130298D >INC EDX
0130298E >REPNE SCAS BYTE PTR ES:[EDI]
01302990 >NOT ECX
01302992 >DEC ECX
01302993 >CMP EDX, ECX
01302995 ^>JB SHORT 01302933
01302997 >MOV ECX, [ESP+10]
0130299B >LEA EDI, [ESP+20]
0130299F >XOR EAX, EAX
013029A1 >MOV BYTE PTR [ECX], 0
013029A4 >OR ECX, FFFFFFFF
013029A7 >REPNE SCAS BYTE PTR ES:[EDI]
013029A9 >NOT ECX
013029AB >DEC ECX
013029AC >CMP ECX, 19 ; 去掉-后的注册码长度
013029AF >JNZ SHORT 013029E6
013029B1 >PUSH 105
013029B6 >CALL 013036DD
013029BB >MOV ESI, EAX
013029BD >ADD ESP, 4
013029C0 >TEST ESI, ESI
013029C2 >MOV [ESP+10], ESI
013029C6 >JE SHORT 013029E6
013029C8 >PUSH 105
013029CD >CALL 013036DD
013029D2 >ADD ESP, 4
013029D5 >MOV [ESP+18], EAX
013029D9 >TEST EAX, EAX
013029DB >JNZ SHORT 013029F5
013029DD >PUSH ESI
013029DE >CALL 013036D2
013029E3 >ADD ESP, 4
013029E6 >POP EDI
013029E7 >POP ESI
013029E8 >POP EBP
013029E9 >XOR EAX, EAX
013029EB >POP EBX
013029EC >ADD ESP, 114
013029F2 >RETN 4
013029F5 >XOR EDI, EDI ; 到这里
013029F7 >XOR EBX, EBX
013029F9 >XOR ESI, ESI
013029FB >MOV DL, [ESP+EBX+20] ; 经过上面的重排,取每组的第五位计算
013029FF >MOV ECX, [ESP+14]
01302A03 >PUSH EDX
01302A04 >CALL 01302B30 ; 转轮指针
01302A09 >AND EAX, 1F
01302A0C >MOV ECX, ESI
01302A0E >SHL EAX, CL
01302A10 >ADD ESI, 5
01302A13 >OR EDI, EAX
01302A15 >INC EBX
01302A16 >CMP ESI, 19
01302A19 ^>JB SHORT 013029FB
01302A1B >MOV ESI, [ESP+14] ; 经过上面分析,每组的第五位越靠近A越好
01302A1F >MOV EAX, 1
01302A24 >LEA EDX, [EDI-3E8]
01302A2A >LEA ECX, [ESI+75]
01302A2D >CMP [ECX], EDX ; 判断上面计算的值的范围
01302A2F >JA SHORT 01302A3A
01302A31 >INC EAX
01302A32 >ADD ECX, 25
01302A35 >CMP EAX, 33
01302A38 ^>JB SHORT 01302A2D
01302A3A >DEC EAX
01302A3B >ADD ESI, EAX ; 这个范围将用于下面取表的指针
01302A3D >LEA ECX, [EAX+EAX*8]
01302A40 >MOV EDX, [ESI+ECX*4+50]
01302A44 >LEA EBX, [ESI+ECX*4]
01302A47 >SUB EDI, EDX ; 计算的值与表中的值相减
01302A49 >SUB EDI, 3E8
01302A4F >MOV EBP, EDI
01302A51 >CMP EBP, 1 ; 上面函数计算的值必须符合第一个条件。
01302A54 ^>JB SHORT 013029E6
01302A56 >CMP EBP, 7C830 ; 第一个条件是值在0x1--0x7C830之间
01302A5C ^>JA SHORT 013029E6
01302A5E >MOV EDX, [ESP+10]
01302A62 >MOV ECX, 41 ; 转轮长度
01302A67 >XOR EAX, EAX
01302A69 >MOV EDI, EDX
01302A6B >REP STOS DWORD PTR ES:[EDI] ; 初始化转轮的内存地址
01302A6D >MOV EDI, [ESP+18]
01302A71 >MOV ECX, 41
01302A76 >REP STOS DWORD PTR ES:[EDI]
01302A78 >MOV EDI, EDX
01302A7A >XOR ESI, ESI
01302A7C >MOV DL, [EBX+ESI+54]
01302A80 >MOV ECX, [ESP+14]
01302A84 >PUSH EDI
01302A85 >PUSH EDX
01302A86 >CALL 01302C50 ; 开始制造转轮
01302A8B >ADD EDI, 8
01302A8E >INC ESI
01302A8F >CMP ESI, 20
01302A92 ^>JB SHORT 01302A7C
01302A94 >TEST EBP, EBP ; 如果是0就直接到注册码验证部分
01302A96 >JBE SHORT 01302ABC
01302A98 >MOV EBX, EBP ; 如果值不等于0就开始转动转轮
01302A9A >MOV ESI, [ESP+18]
01302A9E >MOV EDI, [ESP+10]
01302AA2 >MOV ECX, [ESP+14]
01302AA6 >PUSH 102
01302AAB >PUSH ESI
01302AAC >PUSH EDI
01302AAD >CALL 01302CC0
01302AB2 >MOV ECX, 41
01302AB7 >DEC EBX ; 转轮转动的次数就是上面计算的值
01302AB8 >REP MOVS DWORD PTR ES:[EDI], DWORD PTR [ESI] ; 写入新的值
01302ABA ^>JNZ SHORT 01302A9A
01302ABC >MOV EBP, [ESP+10] ; 第二个条件计算
01302AC0 >MOV DWORD PTR [ESP+1C], 1
01302AC8 >MOV ESI, EBP ; 转轮起始地址
01302ACA >XOR EDI, EDI
01302ACC >XOR BL, BL
01302ACE >XOR EAX, EAX
01302AD0 >CMP BYTE PTR [ESI+EAX], 1
01302AD4 >SETE CL
01302AD7 >OR CL, BL
01302AD9 >SHL CL, 1
01302ADB >INC EAX
01302ADC >MOV BL, CL
01302ADE >CMP EAX, 5 ; 表中每5位一组
01302AE1 ^>JB SHORT 01302AD0
01302AE3 >MOV DL, [ESP+EDI+25] ; 每组剩下的四位组成新的串,共20位
01302AE7 >MOV ECX, [ESP+14] ; 0012E7E9 53 33 53 53 34 43 51 55 45 51 51 45 4D 43 51 35 S3SS4CQUEQQEMCQ5
01302AEB >PUSH EDX
01302AEC >CALL 01302B30 ; 用注册试验码变换,变换函数使用转轮指针函数
01302AF1 >AND EBX, 1F
01302AF4 >CMP EAX, EBX ; 注册码变相比较
01302AF6 >JNZ SHORT 01302B07
01302AF8 >ADD ESI, 5
01302AFB >INC EDI
01302AFC >CMP EDI, 14
01302AFF ^>JB SHORT 01302ACC
01302B01 >MOV ESI, [ESP+1C] ; 成功标志
01302B05 >JMP SHORT 01302B09
01302B07 >XOR ESI, ESI ; 失败标志
01302B09 >PUSH EBP
01302B0A >CALL 013036D2
01302B0F >MOV EAX, [ESP+1C]
01302B13 >PUSH EAX
01302B14 >CALL 013036D2
01302B19 >ADD ESP, 8
01302B1C >MOV EAX, ESI
01302B1E >POP EDI
01302B1F >POP ESI
01302B20 >POP EBP
01302B21 >POP EBX
01302B22 >ADD ESP, 114
01302B28 >RETN 4
01302B30 8A4C24 04 MOV CL, [ESP+4] ; 重点是这几个函数的值将作为指针
01302B34 8A15 A4983001 MOV DL, [13098A4] ; A到Z之间值处理
01302B3A 33C0 XOR EAX, EAX
01302B3C 3ACA CMP CL, DL
01302B3E 7C 13 JL SHORT 01302B53
01302B40 3A0D A5983001 CMP CL, [13098A5]
01302B46 7F 0B JG SHORT 01302B53
01302B48 0FBED2 MOVSX EDX, DL
01302B4B 0FBEC1 MOVSX EAX, CL
01302B4E 2BC2 SUB EAX, EDX ; 值-A
01302B50 C2 0400 RETN 4
01302B53 3A0D A8983001 CMP CL, [13098A8] ; 数字2的处理
01302B59 75 0F JNZ SHORT 01302B6A
01302B5B 0FBE05 A6983001 MOVSX EAX, BYTE PTR [13098A6] ; DS:[010598A6]=4F ('O')
01302B62 0FBECA MOVSX ECX, DL
01302B65 2BC1 SUB EAX, ECX ; DS:[010598A6]=4F ('O')-41('A')=E
01302B67 C2 0400 RETN 4
01302B6A 3A0D A9983001 CMP CL, [13098A9] ; 数字3的处理
01302B70 75 0F JNZ SHORT 01302B81
01302B72 0FBE05 A7983001 MOVSX EAX, BYTE PTR [13098A7] ; DS:[010598A7]=49 ('I')
01302B79 0FBED2 MOVSX EDX, DL
01302B7C 2BC2 SUB EAX, EDX ; DS:[010598A7]=49 ('I')-41('A')=8
01302B7E C2 0400 RETN 4
01302B81 8A15 AA983001 MOV DL, [13098AA] ; 数字4到9的处理 DS:[013098AA]=34 ('4')
01302B87 3ACA CMP CL, DL
01302B89 7C 13 JL SHORT 01302B9E
01302B8B 3A0D AB983001 CMP CL, [13098AB] ; DS:[013098AB]=39 ('9')
01302B91 7F 0B JG SHORT 01302B9E
01302B93 0FBED2 MOVSX EDX, DL
01302B96 0FBEC1 MOVSX EAX, CL
01302B99 2BC2 SUB EAX, EDX ; 4~9-4=0~5
01302B9B 83C0 1A ADD EAX, 1A
01302B9E C2 0400 RETN 4
转轮的一部分:
01322E90 00 01 00 00 01 01 01 01 01 00 00 01 00 00 01 00 ........
01322EA0 01 00 00 01 01 01 01 01 01 00 00 00 00 01 01 01 ......
01322EB0 00 00 00 00 01 00 01 00 01 00 00 01 00 00 01 00 ...........
01322EC0 00 00 01 01 00 00 00 00 00 00 01 00 01 00 01 01 ..........
01322ED0 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 00 ..........
01322EE0 00 00 01 00 01 00 00 01 00 01 00 00 01 01 00 00 ..........
好了,知道了算法流程,怎么得到可用的注册码呢?
第一要满足第一个条件:每组的第五位计算的值保证在0x1--0x7C830之间,这个比较好办,通过分析发现越靠近A的字符指针返回的值越小。
越到后面组的第五位值会越大。根据这个原则可以取很多值。
第二个要通过第一个条件做指针通过转轮后变换的值来逆出剩余的注册码:
1.修改01302AF6 >JNZ SHORT 01302B07 的代码为nop
2.在代码01302AF4 >CMP EAX, EBX处下 条件记录 断点,设置记录的参数为EBX 且始终记录这个参数。
3.在01302B01 >MOV ESI, [ESP+1C] 下个中断,运行程序到中断的地方,可以得到一张表,如下:
01302AF4 COND: 00000012
01302AF4 COND: 0000001C
01302AF4 COND: 00000012
01302AF4 COND: 00000012
01302AF4 COND: 0000001E
01302AF4 COND: 00000002
01302AF4 COND: 00000010
01302AF4 COND: 00000014
01302AF4 COND: 00000004
01302AF4 COND: 00000010
01302AF4 COND: 00000010
01302AF4 COND: 00000004
01302AF4 COND: 0000000C
01302AF4 COND: 00000002
01302AF4 COND: 00000010
01302AF4 COND: 0000001C
01302AF4 COND: 0000000A
01302AF4 COND: 0000000A
01302AF4 COND: 0000000C
01302AF4 COND: 00000010
01302B01 断点位于 PltInsta.01302B01
注意这个表中第七位重复了,去掉。
4.通过上面的表还原注册码,根据这个函数知道:
i>
01302B30 8A4C24 04 MOV CL, [ESP+4] ; 重点是这几个函数的值将作为指针
01302B34 8A15 A4983001 MOV DL, [13098A4] ; A到Z之间值处理
01302B3A 33C0 XOR EAX, EAX
01302B3C 3ACA CMP CL, DL
01302B3E 7C 13 JL SHORT 01302B53
01302B40 3A0D A5983001 CMP CL, [13098A5]
01302B46 7F 0B JG SHORT 01302B53
01302B48 0FBED2 MOVSX EDX, DL
01302B4B 0FBEC1 MOVSX EAX, CL
01302B4E 2BC2 SUB EAX, EDX ; 值-A
01302B50 C2 0400 RETN 4
这个函数的返回值为0到19
01302B53 3A0D A8983001 CMP CL, [13098A8] ; 数字2的处理
01302B59 75 0F JNZ SHORT 01302B6A
01302B5B 0FBE05 A6983001 MOVSX EAX, BYTE PTR [13098A6] ; DS:[010598A6]=4F ('O')
01302B62 0FBECA MOVSX ECX, DL
01302B65 2BC1 SUB EAX, ECX ; DS:[010598A6]=4F ('O')-41('A')=E
01302B67 C2 0400 RETN 4
这个函数的返回值是E,如果上面表中值是E那么注册码就是2
01302B6A 3A0D A9983001 CMP CL, [13098A9] ; 数字3的处理
01302B70 75 0F JNZ SHORT 01302B81
01302B72 0FBE05 A7983001 MOVSX EAX, BYTE PTR [13098A7] ; DS:[010598A7]=49 ('I')
01302B79 0FBED2 MOVSX EDX, DL
01302B7C 2BC2 SUB EAX, EDX ; DS:[010598A7]=49 ('I')-41('A')=8
01302B7E C2 0400 RETN 4
这个函数的返回值是8,如果上面表中的值是8那么注册码就是3
01302B81 8A15 AA983001 MOV DL, [13098AA] ; 数字4到9的处理 DS:[013098AA]=34 ('4')
01302B87 3ACA CMP CL, DL
01302B89 7C 13 JL SHORT 01302B9E
01302B8B 3A0D AB983001 CMP CL, [13098AB] ; DS:[013098AB]=39 ('9')
01302B91 7F 0B JG SHORT 01302B9E
01302B93 0FBED2 MOVSX EDX, DL
01302B96 0FBEC1 MOVSX EAX, CL
01302B99 2BC2 SUB EAX, EDX ; 4~9-4=0~5
01302B9B 83C0 1A ADD EAX, 1A
01302B9E C2 0400 RETN 4
这个函数的返回值最小是1A,如果上面表中的值大于等于1A的注册码就是4-9是多少就根据表中大于1A的值-1A所得的值加上4
一组可以使用的注册码:S6SSK-8CQUN-EQQEK-MCQ6N-KKMQA
好了,愿自己心情好起来。
fxyang
2006.6.9
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)