Editplus4注册码分析
本帖最后由 fly1n 于 2017-1-14 05:14 编辑更新一下,这两天偶尔又用到了Editplus,结果用了几下发现又提示无效了,发现自己有暗桩没有过掉,所以又研究了一下
这次用的1月5号才发布的最新的build 420
有些图没保存,所以前面的步骤就简单文字描述下
注册码又被无效之后,给 MessageBoxW 下断,断下后看到前面有个KillTimer函数,所以这个是个Timer事件
记下 TimerID = 6D,搜索 All Intermodular Calls,把 SetTimer 都打开看看,标记下ID为6D
发现有两处 SetTimer 的ID为6D,这两处就分别是 License 3 和 License 4 的校验,失败则 SetTimer
然后到 SetTimer 的代码处看一下,发现和我们上次分析的一样,这里还是在用CheckV4校验,不过Editplus在暗桩处就改了 License,所以会导致我们过不了校验
由于不知道暗桩的触发时机,所以动态调试就非常麻烦了
此时我们注意到我们已经发现的关闭前的一处暗桩,是下面这行代码更改了注册码
004D6029 .C701 01000000 mov dword ptr ds:,0x1
我们搜索 C701 01000000 这个特征码,并且注意一下这个暗桩的形式为
mov ax, xxx
cmp ax, xxx
je xxx
mov ecx, xxx
C701 01000000 mov dword ptr ds:,0x1
在找到的所有 mov dword ptr ds:,0x1 处过滤一下,就能找到所有的暗桩了
原来Editplus对注册码的前11位(不包括连接符 '-' )都有校验,囧,原来上回我才做了一半的工作啊
暗桩可以直接借助 IDA 分析
这里给出新的注册机源码片段和一组新码
int sum = 1;
for (int i = 0; i < wcslen(usr); ++i)
{
sum += usr;
}
int res = (sum * 3 + 39) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
res = ((sum + 41) / 6 * 7) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
res = ((3 * sum + 19) / 9) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
res = (sum + 2) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
res = ((3 * sum + 43) / 8) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
res = ((sum + 10) / 3 * 8) % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
新码:
fly1n
D3958-85DD8-12345-12345-12345
===================================================================================
Editplus的注册码网上还是挺多的,但是其实都是3的(在注册表HKEY_LOCAL_MACHINE/SOFTWARE/ES-Computing/Editplus/Install里可以发现存的都是License,如果是4的key存的会是License 4),虽然3的key其实4也能用,但是由于本人有强迫症,key不用新版的心里不舒服- -,所以还是自己逆向了一下Editplus4的注册机制,采用的版本是build 395
首先PEID查下壳,发现是VC8的无壳程序
首先我们搜索VC8的按钮事件,Ctrl+G到00401000,Ctrl+B搜索33C05E5B5DC21C00,来到下面这里
00514E5A|> \33C0 xor eax,eax ;editplus.00595374; Default case of switch 00514D89
00514E5C|>5E pop esi ;editplus.00514DA0
00514E5D|>5B pop ebx ;editplus.00514DA0
00514E5E|.5D pop ebp ;editplus.00514DA0
00514E5F\.C2 1C00 retn 0x1C
往上拉,找到 ja, jmp这里
00514D90|. /0F87 C4000000 ja editplus.00514E5A
00514D96|. |FF2485 634E51>jmp dword ptr ds:
00514D9D|> |FF55 14 call ;editplus.0053C26E; Case 39 ('9') of switch 00514D89
00514DA0|. |E9 B1000000 jmp editplus.00514E56
在jmp这行下断,输入用户名和假码,点Register,单步走两步就到了下面这里,跟一下发现004D6CFF这行是错误提示,所以004D6CC3和004D6CD7这两个是关键call,由于网上已经有了很多3的真码,我用真码跟了一下,比较了一下和假码的流程的不同,很容易就知道004D6CC3这个函数是用V4的注册机制去测试注册码,我给他加了个标签_CheckV4,004D6CD7这个自然就是_CheckV3了,我们的任务就是第一个函数_CheckV4
004D6CB0 .83EC 08 sub esp,0x8
004D6CB3 .56 push esi ;editplus.0059B654
004D6CB4 .8D4424 04 lea eax,dword ptr ss:
004D6CB8 .50 push eax ;editplus.00595374
004D6CB9 .8BF1 mov esi,ecx
004D6CBB .C74424 08 000>mov dword ptr ss:,0x0
004D6CC3 .E8 58FDFFFF call <editplus._CheckV4>
004D6CC8 .85C0 test eax,eax ;editplus.00595374
004D6CCA .75 3D jnz short editplus.004D6D09
004D6CCC .8D4C24 08 lea ecx,dword ptr ss:
004D6CD0 .51 push ecx
004D6CD1 .8BCE mov ecx,esi ;editplus.0059B654
004D6CD3 .894424 0C mov dword ptr ss:,eax ;editplus.00595374
004D6CD7 .E8 84FEFFFF call <editplus._CheckV3>
004D6CDC .8B4C24 08 mov ecx,dword ptr ss:
004D6CE0 .8B5424 04 mov edx,dword ptr ss: ;editplus.0059B654
004D6CE4 .85C9 test ecx,ecx
004D6CE6 .76 0A jbe short editplus.004D6CF2
004D6CE8 .81FA 7F4E0000 cmp edx,0x4E7F
004D6CEE .74 02 je short editplus.004D6CF2
004D6CF0 .8BD1 mov edx,ecx
004D6CF2 >85C0 test eax,eax ;editplus.00595374
004D6CF4 .75 13 jnz short editplus.004D6D09
004D6CF6 .85D2 test edx,edx
004D6CF8 .74 0A je short editplus.004D6D04
004D6CFA .6A FF push -0x1
004D6CFC .6A 10 push 0x10
004D6CFE .52 push edx
004D6CFF .E8 B33D0400 call <editplus.ShowError>
004D6D04 >5E pop esi ;editplus.00514DA0
004D6D05 .83C4 08 add esp,0x8
004D6D08 .C3 retn
跟进_CheckV4,004D6AF1这里有个跳,eax为0就直接跳出去了,所以上面的那个call是关键call,检查注册码的
004D6A20 >/$81EC 5C040000 sub esp,0x45C
004D6A26|.A1 7C9D5C00 mov eax,dword ptr ds:
004D6A2B|.33C4 xor eax,esp
004D6A2D|.898424 580400>mov dword ptr ss:,eax
004D6A34|.8B8424 600400>mov eax,dword ptr ss:
004D6A3B|.53 push ebx
004D6A3C|.55 push ebp
004D6A3D|.8BE9 mov ebp,ecx
004D6A3F|.8D4C24 08 lea ecx,dword ptr ss:
004D6A43|.51 push ecx
004D6A44|.68 F4010000 push 0x1F4
004D6A49|.8D9424 800000>lea edx,dword ptr ss:
004D6A50|.894424 14 mov dword ptr ss:,eax
004D6A54|.52 push edx
004D6A55|.8D85 C8000000 lea eax,dword ptr ss:
004D6A5B|.50 push eax
004D6A5C|.E8 4FFDFFFF call <editplus.GetDlgItemText> ;get username
004D6A61|.83C4 10 add esp,0x10
004D6A64|.837C24 08 00cmp dword ptr ss:,0x0
004D6A69|.8BD8 mov ebx,eax
004D6A6B|.895C24 10 mov dword ptr ss:,ebx
004D6A6F|.75 07 jnz short editplus.004D6A78 ;username not null
004D6A71|.33C0 xor eax,eax
004D6A73|.E9 CF000000 jmp editplus.004D6B47
004D6A78|>57 push edi
004D6A79|.8D4C24 0C lea ecx,dword ptr ss:
004D6A7D|.51 push ecx
004D6A7E|.6A 32 push 0x32
004D6A80|.8D5424 20 lea edx,dword ptr ss:
004D6A84|.52 push edx
004D6A85|.8D45 74 lea eax,
004D6A88|.50 push eax
004D6A89|.E8 22FDFFFF call <editplus.GetDlgItemText> ;get password
004D6A8E|.8BF8 mov edi,eax
004D6A90|.8B4424 1C mov eax,dword ptr ss:
004D6A94|.83C4 10 add esp,0x10
004D6A97|.85C0 test eax,eax
004D6A99|.74 7E je short editplus.004D6B19 ;password not null
004D6A9B|.56 push esi
004D6A9C|.33F6 xor esi,esi
004D6A9E|.85C0 test eax,eax
004D6AA0|.7E 44 jle short editplus.004D6AE6
004D6AA2|>0FB71C77 /movzx ebx,word ptr ds: ;******** non sense ********
004D6AA6|.B9 00010000 |mov ecx,0x100
004D6AAB|.66:3BD9 |cmp bx,cx
004D6AAE|.73 0D |jnb short editplus.004D6ABD
004D6AB0|.0FB6D3 |movzx edx,bl
004D6AB3|.0FB70455 30F7>|movzx eax,word ptr ds:
004D6ABB|.EB 1A |jmp short editplus.004D6AD7
004D6ABD|>0FB7C3 |movzx eax,bx
004D6AC0|.50 |push eax ; /StringOrChar = 0000
004D6AC1|.FF15 5CD75700 |call dword ptr ds:[<&USER32.CharUpperW>] ; \CharUpperW
004D6AC7|.0FB7C0 |movzx eax,ax
004D6ACA|.66:85C0 |test ax,ax
004D6ACD|.75 05 |jnz short editplus.004D6AD4
004D6ACF|.0FB7C3 |movzx eax,bx
004D6AD2|.EB 03 |jmp short editplus.004D6AD7
004D6AD4|>0FB7C0 |movzx eax,ax
004D6AD7|>66:890477 |mov word ptr ds:,ax
004D6ADB|.46 |inc esi
004D6ADC|.3B7424 10 |cmp esi,dword ptr ss:
004D6AE0|.^ 7C C0 \jl short editplus.004D6AA2 ;***************************
004D6AE2|.8B5C24 18 mov ebx,dword ptr ss:
004D6AE6|>57 push edi
004D6AE7|.53 push ebx
004D6AE8|.E8 93F6FFFF call <editplus.CheckV4>
004D6AED|.83C4 08 add esp,0x8
004D6AF0|.5E pop esi ;00CB9A10
004D6AF1|.85C0 test eax,eax
004D6AF3|.75 0C jnz short editplus.004D6B01
004D6AF5|.8B4C24 10 mov ecx,dword ptr ss:
004D6AF9|.C701 815F0000 mov dword ptr ds:,0x5F81
004D6AFF|.EB 45 jmp short editplus.004D6B46
004D6B01|>57 push edi
004D6B02|.53 push ebx
004D6B03|.E8 48F0FFFF call <editplus.WriteReg>
004D6B08|.83C4 08 add esp,0x8
004D6B0B|.85C0 test eax,eax
004D6B0D|.75 0E jnz short editplus.004D6B1D
004D6B0F|.8B5424 10 mov edx,dword ptr ss:
004D6B13|.C702 7F4E0000 mov dword ptr ds:,0x4E7F
004D6B19|>33C0 xor eax,eax
004D6B1B|.EB 29 jmp short editplus.004D6B46
004D6B1D|>8B85 20010000 mov eax,dword ptr ss:
004D6B23|.53 push ebx
004D6B24|.68 F4010000 push 0x1F4
004D6B29|.50 push eax
004D6B2A|.E8 1D7B0700 call <editplus.wcscpy_s> ; store username
004D6B2F|.8B8D 24010000 mov ecx,dword ptr ss:
004D6B35|.57 push edi
004D6B36|.6A 32 push 0x32
004D6B38|.51 push ecx
004D6B39|.E8 0E7B0700 call <editplus.wcscpy_s> ; store password
004D6B3E|.83C4 18 add esp,0x18
004D6B41|.B8 01000000 mov eax,0x1
004D6B46|>5F pop edi ;00CB9A10
004D6B47|>8B8C24 600400>mov ecx,dword ptr ss:
004D6B4E|.5D pop ebp ;00CB9A10
004D6B4F|.5B pop ebx ;00CB9A10
004D6B50|.33CC xor ecx,esp
004D6B52|.E8 7F740700 call editplus.0054DFD6
004D6B57|.81C4 5C040000 add esp,0x45C
004D6B5D\.C2 0400 retn 0x4
跟进CheckV4,关键函数我都加了标签,其实不知道004D61D7是建CRC的表,不知道后面004D61F5的是CRC都没关系,就把第一个函数当生成了一个替换表然后第二个函数当查表就行了
GetAsciiOfUnicode这个函数是生成一段Unicode字符串里每个字符的每个十六进制位对应的ascii码,比如用户名fly1n,对应的Uniode是0066h,006Ch,0079h,0031h,0061h,这个函数生成的Ascii字符串就是"0066006C007900310061",在内存里就是30h,30h,36h,36h, ......
其实本质上就是个sprintf,C代码也放在下面了,调用过GetAsciiOfUnicode之后,Editplus又对生成的字符串计算了CRC16(关于查表法计算CRC16,CRC32网上有很多资料,就不赘述了),计算出一个16位的CRC16校验码后,用swprintf转成unicode字符串(4位数)
然后下面有两个比较,就是把CRC的结果字符串的前两位和注册码的3,4位比较,所以我们知道了注册码的3,4位是怎么得来的
下面的部分又对注册码第三位以后的部分重复了以上步骤,与注册码的第1,2位比较
至此我们就知道了注册码的前4位怎么来,对用户名fly1n,一个符合以上条件的码是08955-12345-12345-12345-12345
输入后我们得到提示,重启验证的,而Editplus用到了很多unicode,所以我们可以猜测它用的函数应该是W版本(实际上上面004D6B03那里的写注册表的函数跟进去就会发现确实如此)
004D6180 >/$ 81EC D00B0000 sub esp,0xBD0
004D6186 |. A1 7C9D5C00 mov eax,dword ptr ds:
004D618B |. 33C4 xor eax,esp
004D618D |. 898424 CC0B00>mov dword ptr ss:,eax
004D6194 |. 53 push ebx
004D6195 |. 55 push ebp
004D6196 |. 8BAC24 DC0B00>mov ebp,dword ptr ss:
004D619D |. 56 push esi
004D619E |. 8BB424 E40B00>mov esi,dword ptr ss: ; editplus.00575E08
004D61A5 |. 8BC5 mov eax,ebp
004D61A7 |. 57 push edi
004D61A8 |. 8D50 02 lea edx,dword ptr ds:
004D61AB |. EB 03 jmp short editplus.004D61B0
004D61AD | 8D49 00 lea ecx,dword ptr ds:
004D61B0 |> 66:8B08 /mov cx,word ptr ds:
004D61B3 |. 83C0 02 |add eax,0x2
004D61B6 |. 66:85C9 |test cx,cx
004D61B9 |.^ 75 F5 \jnz short editplus.004D61B0
004D61BB |. 2BC2 sub eax,edx
004D61BD |. D1F8 sar eax,1
004D61BF |. 8BD8 mov ebx,eax ; ebx = username.length
004D61C1 |. 8BC6 mov eax,esi
004D61C3 |. 8D50 02 lea edx,dword ptr ds:
004D61C6 |> 66:8B08 /mov cx,word ptr ds:
004D61C9 |. 83C0 02 |add eax,0x2
004D61CC |. 66:85C9 |test cx,cx
004D61CF |.^ 75 F5 \jnz short editplus.004D61C6
004D61D1 |. 2BC2 sub eax,edx
004D61D3 |. D1F8 sar eax,1
004D61D5 |. 8BF8 mov edi,eax ; edi = password.length
004D61D7 |. E8 94F2FFFF call <editplus.BuildCRCTable>
004D61DC |. 68 B80B0000 push 0xBB8
004D61E1 |. 8D4424 28 lea eax,dword ptr ss:
004D61E5 |. 50 push eax
004D61E6 |. 53 push ebx
004D61E7 |. 55 push ebp
004D61E8 |. E8 23F3FFFF call <editplus.GetAsciiOfUnicode>
004D61ED |. 50 push eax
004D61EE |. 8D4C24 38 lea ecx,dword ptr ss:
004D61F2 |. 51 push ecx
004D61F3 |. 6A 00 push 0x0
004D61F5 |. E8 D6F2FFFF call <editplus.CRC16>
004D61FA |. 0FB7D0 movzx edx,ax
004D61FD |. 52 push edx
004D61FE |. 8D4424 30 lea eax,dword ptr ss:
004D6202 |. 68 80FC5800 push editplus.0058FC80 ; UNICODE "%04X"
004D6207 |. 50 push eax
004D6208 |. E8 D33AF3FF call <editplus.swprintf>
004D620D |. 66:8B4E 04 mov cx,word ptr ds:
004D6211 |. 8D46 04 lea eax,dword ptr ds:
004D6214 |. 83C4 28 add esp,0x28
004D6217 |. 66:3B4C24 10 cmp cx,word ptr ss:
004D621C |. 74 04 je short editplus.004D6222
004D621E |> 33C0 xor eax,eax
004D6220 |. EB 5A jmp short editplus.004D627C
004D6222 |> 66:8B56 06 mov dx,word ptr ds:
004D6226 |. 66:3B5424 12 cmp dx,word ptr ss:
004D622B |.^ 75 F1 jnz short editplus.004D621E
004D622D |. 68 B80B0000 push 0xBB8
004D6232 |. 8D4C24 28 lea ecx,dword ptr ss:
004D6236 |. 51 push ecx
004D6237 |. 83C7 FE add edi,-0x2
004D623A |. 57 push edi
004D623B |. 50 push eax
004D623C |. E8 CFF2FFFF call <editplus.GetAsciiOfUnicode>
004D6241 |. 50 push eax
004D6242 |. 8D5424 38 lea edx,dword ptr ss:
004D6246 |. 52 push edx
004D6247 |. 6A 00 push 0x0
004D6249 |. E8 82F2FFFF call <editplus.CRC16>
004D624E |. 0FB7C0 movzx eax,ax
004D6251 |. 50 push eax
004D6252 |. 8D4C24 30 lea ecx,dword ptr ss:
004D6256 |. 68 80FC5800 push editplus.0058FC80 ; UNICODE "%04X"
004D625B |. 51 push ecx
004D625C |. E8 7F3AF3FF call <editplus.swprintf>
004D6261 |. 66:8B16 mov dx,word ptr ds:
004D6264 |. 83C4 28 add esp,0x28
004D6267 |. 33C0 xor eax,eax
004D6269 |. 66:3B5424 10 cmp dx,word ptr ss:
004D626E |. 75 0C jnz short editplus.004D627C
004D6270 |. 66:8B4E 02 mov cx,word ptr ds:
004D6274 |. 66:3B4C24 12 cmp cx,word ptr ss:
004D6279 |. 0F94C0 sete al
004D627C |> 8B8C24 DC0B00>mov ecx,dword ptr ss:
004D6283 |. 5F pop edi ; 0012F130
004D6284 |. 5E pop esi ; 0012F130
004D6285 |. 5D pop ebp ; 0012F130
004D6286 |. 5B pop ebx ; 0012F130
004D6287 |. 33CC xor ecx,esp
004D6289 |. E8 487D0700 call editplus.0054DFD6
004D628E |. 81C4 D00B0000 add esp,0xBD0
004D6294 \. C3 retn
void GetAsciiOfUnicode(wchar_t *words, char *ascii){
for (size_t i = 0, j = 0; i < wcslen(words); ++i, j += 4)
{
sprintf(ascii + j, "%04x", words);
}
}
对RegQueryValueW下断,重启程序,跟一下,发现我们的注册码还原出来和原来不一样了,第一位变成了1,同时发现写入还原后的注册码是在0050DAC4这一行,跟一下可以发现这里程序并不是先还原出我们原来的key再更改,而是直接就还原出了一个错误的key,所以我怀疑是这个所谓的重启验证其实是个误导,程序在关闭前还有个验证
0050DA99|>8A1E |/mov bl,byte ptr ds:
0050DA9B|. |8B7C24 28 ||mov edi,dword ptr ss:
0050DA9F|. |8819 ||mov byte ptr ds:,bl
0050DAA1|. |41 ||inc ecx
0050DAA2|. |46 ||inc esi
0050DAA3|. |4F ||dec edi
0050DAA4|. |897C24 28 ||mov dword ptr ss:,edi
0050DAA8|.^\75 EF |\jnz short editplus.0050DA99
0050DAAA|.8B7C24 3C |mov edi,dword ptr ss:
0050DAAE|.894C24 14 |mov dword ptr ss:,ecx
0050DAB2|.EB 20 |jmp short editplus.0050DAD4
0050DAB4|>33C9 |xor ecx,ecx
0050DAB6|.8A4E 01 |mov cl,byte ptr ds:
0050DAB9|.D3EA |shr edx,cl
0050DABB|.2BC1 |sub eax,ecx
0050DABD|.8A4E 04 |mov cl,byte ptr ds:
0050DAC0|.8B7424 14 |mov esi,dword ptr ss:
0050DAC4|.880E |mov byte ptr ds:,cl ;!!!!
0050DAC6|.8B4C24 18 |mov ecx,dword ptr ss:
0050DACA|.46 |inc esi
程序重启验证的部分如下,可以发现其实在算出我们的用户名和注册码以后,用的还是我们的CheckV4函数,所以这也验证了我们了以上猜想,我们只要找到程序关闭前的另一处校验,pass掉保证程序不更改我们的注册码,重启后就能注册成功
004603D0 $83EC 10 sub esp,0x10
004603D3 .55 push ebp
004603D4 .8D4424 0C lea eax,dword ptr ss:
004603D8 .50 push eax
004603D9 .68 74B65800 push editplus.0058B674 ;UNICODE "License 4"
004603DE .894C24 0C mov dword ptr ss:,ecx
004603E2 .C74424 18 000>mov dword ptr ss:,0x0
004603EA .E8 11FDFFFF call <editplus.ReadKey> ;read key
004603EF .8BE8 mov ebp,eax
004603F1 .85ED test ebp,ebp
004603F3 .75 05 jnz short editplus.004603FA
004603F5 .5D pop ebp ;00CB9A10
004603F6 .83C4 10 add esp,0x10
004603F9 .C3 retn
004603FA >53 push ebx
004603FB .0FB65D 00 movzx ebx,byte ptr ss:
004603FF .33C9 xor ecx,ecx
00460401 .8D43 01 lea eax,dword ptr ds:
00460404 .BA 02000000 mov edx,0x2
00460409 .F7E2 mul edx
0046040B .0F90C1 seto cl
0046040E .56 push esi
0046040F .57 push edi
00460410 .F7D9 neg ecx
00460412 .0BC8 or ecx,eax
00460414 .51 push ecx
00460415 .E8 2BDB0A00 call <editplus.new>
0046041A .8BF0 mov esi,eax
0046041C .8D3C1B lea edi,dword ptr ds:
0046041F .57 push edi
00460420 .8D45 01 lea eax,dword ptr ss:
00460423 .50 push eax
00460424 .8D4F 02 lea ecx,dword ptr ds:
00460427 .51 push ecx
00460428 .56 push esi
00460429 .897424 28 mov dword ptr ss:,esi
0046042D .897C24 2C mov dword ptr ss:,edi
00460431 .E8 AFDB0E00 call <editplus.memcpy_s> ;username
00460436 .83C4 14 add esp,0x14
00460439 .33D2 xor edx,edx
0046043B .66:891437 mov word ptr ds:,dx
0046043F .90 nop
00460440 >0FB706 movzx eax,word ptr ds:
00460443 .B9 20000000 mov ecx,0x20 ;skip space
00460448 .66:3BC8 cmp cx,ax
0046044B .74 0A je short editplus.00460457
0046044D .BA 09000000 mov edx,0x9 ;skip tab
00460452 .66:3BD0 cmp dx,ax
00460455 .75 05 jnz short editplus.0046045C
00460457 >83C6 02 add esi,0x2
0046045A .^ EB E4 jmp short editplus.00460440
0046045C >8BC3 mov eax,ebx
0046045E .85DB test ebx,ebx
00460460 .7E 2D jle short editplus.0046048F
00460462 .8D4C37 FE lea ecx,dword ptr ds:
00460466 .EB 08 jmp short editplus.00460470
00460468 .8DA424 000000>lea esp,dword ptr ss:
0046046F .90 nop
00460470 >0FB711 movzx edx,word ptr ds:
00460473 .BB 20000000 mov ebx,0x20
00460478 .66:3BDA cmp bx,dx
0046047B .74 0A je short editplus.00460487
0046047D .BB 09000000 mov ebx,0x9
00460482 .66:3BDA cmp bx,dx
00460485 .75 08 jnz short editplus.0046048F
00460487 >48 dec eax
00460488 .83E9 02 sub ecx,0x2
0046048B .85C0 test eax,eax
0046048D .^ 7F E1 jg short editplus.00460470
0046048F >8B5C24 10 mov ebx,dword ptr ss: ;editplus.00465210
00460493 .56 push esi
00460494 .33C9 xor ecx,ecx
00460496 .81C3 68180000 add ebx,0x1868
0046049C .68 F4010000 push 0x1F4
004604A1 .53 push ebx
004604A2 .66:890C46 mov word ptr ds:,cx
004604A6 .E8 A1E10E00 call <editplus.wcscpy_s> ;copy username
004604AB .8B5424 20 mov edx,dword ptr ss:
004604AF .52 push edx
004604B0 .E8 9BDA0A00 call editplus.0050DF50
004604B5 .0FB67C2F 01 movzx edi,byte ptr ds:
004604BA .33C9 xor ecx,ecx
004604BC .8D47 01 lea eax,dword ptr ds:
004604BF .BA 02000000 mov edx,0x2
004604C4 .F7E2 mul edx
004604C6 .0F90C1 seto cl
004604C9 .F7D9 neg ecx
004604CB .0BC8 or ecx,eax
004604CD .51 push ecx
004604CE .E8 72DA0A00 call <editplus.new>
004604D3 .8BF0 mov esi,eax
004604D5 .8B4424 2C mov eax,dword ptr ss:
004604D9 .03FF add edi,edi
004604DB .57 push edi
004604DC .8D4C28 02 lea ecx,dword ptr ds:
004604E0 .51 push ecx
004604E1 .8D57 02 lea edx,dword ptr ds:
004604E4 .52 push edx
004604E5 .56 push esi
004604E6 .E8 FADA0E00 call <editplus.memcpy_s>
004604EB .33C0 xor eax,eax
004604ED .66:890437 mov word ptr ds:,ax
004604F1 .8B7C24 34 mov edi,dword ptr ss:
004604F5 .56 push esi
004604F6 .81C7 501C0000 add edi,0x1C50
004604FC .6A 32 push 0x32
004604FE .57 push edi
004604FF .E8 48E10E00 call <editplus.wcscpy_s>
00460504 .56 push esi
00460505 .E8 46DA0A00 call editplus.0050DF50
0046050A .55 push ebp
0046050B .E8 40DA0A00 call editplus.0050DF50
00460510 .8BC3 mov eax,ebx
00460512 .83C4 38 add esp,0x38
00460515 .8D50 02 lea edx,dword ptr ds:
00460518 .EB 06 jmp short editplus.00460520
0046051A 8D9B 00000000 lea ebx,dword ptr ds:
00460520 >66:8B08 mov cx,word ptr ds:
00460523 .83C0 02 add eax,0x2
00460526 .66:85C9 test cx,cx
00460529 .^ 75 F5 jnz short editplus.00460520
0046052B .2BC2 sub eax,edx
0046052D .D1F8 sar eax,1
0046052F .74 53 je short editplus.00460584
00460531 .8BC7 mov eax,edi
00460533 .8D50 02 lea edx,dword ptr ds:
00460536 >66:8B08 mov cx,word ptr ds:
00460539 .83C0 02 add eax,0x2
0046053C .66:85C9 test cx,cx
0046053F .^ 75 F5 jnz short editplus.00460536
00460541 .2BC2 sub eax,edx
00460543 .D1F8 sar eax,1
00460545 .74 3D je short editplus.00460584
00460547 .57 push edi
00460548 .53 push ebx
00460549 .BE 01000000 mov esi,0x1
0046054E .E8 2D5C0700 call <editplus.CheckV4>
有了这样的怀疑以后,这次我们在过了004D6B03 call <editplus.WriteReg>这行后,留心一下注册表,关闭程序后,注册码果然变了,那我们就找一下是哪里改变了这个注册码,先看看CheckV4,WriteReg那里,过了验证以后004D6B2A 和004D6B39 这里有两个wcscpy_s将堆栈中的数据拷贝了出来,于是我猜测这里存的两个字符串就是用作之后读取和更改的。对其中存注册码的那段地址下内存写入断点,关闭程序。果然!关闭程序的时候断点触发了,我们停在了下面这里,如果注册码第一位是0,就改为1,否则改为0,而注册码改了一位以后,同样的校验函数,我们是无论如何都不可能校验成功的,所以我们不能跳到更改注册码那里,所以上面00468A4D的那个跳转应该实现,00468A3E这行ds:应该为0,而实际我们这里现在是1,那我们就对这里下内存写入断点,看看这里的这个1是什么时候被写入的
00468A10 >/$6A FF push -0x1
00468A12|.68 C20E5700 push editplus.00570EC2
00468A17|.64:A1 0000000>mov eax,dword ptr fs:
00468A1D|.50 push eax
00468A1E|.51 push ecx
00468A1F|.56 push esi
00468A20|.A1 7C9D5C00 mov eax,dword ptr ds:
00468A25|.33C4 xor eax,esp
00468A27|.50 push eax
00468A28|.8D4424 0C lea eax,dword ptr ss:
00468A2C|.64:A3 0000000>mov dword ptr fs:,eax
00468A32|.8BF1 mov esi,ecx
00468A34|.897424 08 mov dword ptr ss:,esi
00468A38|.C706 D4C15800 mov dword ptr ds:,editplus.0058C1D4 ;ASCII "朗E"
00468A3E|.83BE EC1E0000>cmp dword ptr ds:,0x0
00468A45|.C74424 14 0B0>mov dword ptr ss:,0xB
00468A4D|.74 52 je short editplus.00468AA1
00468A4F|.80BE A81E0000>cmp byte ptr ds:,0x30
00468A56|.8D86 A81E0000 lea eax,dword ptr ds:
00468A5C|.0F94C1 sete cl
00468A5F|.50 push eax
00468A60|.8D96 B41C0000 lea edx,dword ptr ds:
00468A66|.80C1 30 add cl,0x30
00468A69|.52 push edx
00468A6A|.8808 mov byte ptr ds:,cl
00468A6C|.E8 2FCE0600 call editplus.004D58A0
00468A71|.8D86 501C0000 lea eax,dword ptr ds: ;Get regcode
00468A77|.B9 30000000 mov ecx,0x30
00468A7C|.83C4 08 add esp,0x8
00468A7F|.66:3B08 cmp cx,word ptr ds:
00468A82|.75 0A jnz short editplus.00468A8E ;regcode != 0
00468A84|.BA 31000000 mov edx,0x31
00468A89|.66:8910 mov word ptr ds:,dx ;!!!Change Key!!!
00468A8C|.EB 03 jmp short editplus.00468A91
00468A8E|>66:8908 mov word ptr ds:,cx
00468A91|>50 push eax
00468A92|.8D96 68180000 lea edx,dword ptr ds:
00468A98|.52 push edx
00468A99|.E8 B2D00600 call <editplus.WriteReg>
00468A9E|.83C4 08 add esp,0x8
00468AA1|>8B86 441F0000 mov eax,dword ptr ds:
00468AA7|.C64424 14 0Amov byte ptr ss:,0xA
00468AAC|.85C0 test eax,eax
00468AAE|.74 08 je short editplus.00468AB8
00468AB0|.8B08 mov ecx,dword ptr ds:
00468AB2|.8B51 08 mov edx,dword ptr ds:
00468AB5|.50 push eax
00468AB6|.FFD2 call edx
00468AB8|>8B86 64180000 mov eax,dword ptr ds: ;editplus.005F3F40
00468ABE|.83E8 10 sub eax,0x10
00468AC1|.C64424 14 09mov byte ptr ss:,0x9
00468AC6|.8D48 0C lea ecx,dword ptr ds:
00468AC9|.83CA FF or edx,-0x1
00468ACC|.F0:0FC111 lock xadd dword ptr ds:,edx
00468AD0|.4A dec edx
00468AD1|.85D2 test edx,edx
00468AD3|.7F 0A jg short editplus.00468ADF
00468AD5|.8B08 mov ecx,dword ptr ds:
00468AD7|.8B11 mov edx,dword ptr ds:
00468AD9|.50 push eax
00468ADA|.8B42 04 mov eax,dword ptr ds:
00468ADD|.FFD0 call eax
00468ADF|>8D8E 94170000 lea ecx,dword ptr ds:
00468AE5|.C64424 14 08mov byte ptr ss:,0x8
00468AEA|.E8 7163FAFF call editplus.0040EE60
00468AEF|.8B86 6C170000 mov eax,dword ptr ds:
00468AF5|.C786 68170000>mov dword ptr ds:,editplus.0>
00468AFF|.85C0 test eax,eax
00468B01|.74 09 je short editplus.00468B0C
00468B03|.50 push eax
00468B04|.E8 47540A00 call editplus.0050DF50
00468B09|.83C4 04 add esp,0x4
00468B0C|>8D8E DC150000 lea ecx,dword ptr ds:
00468B12|.C64424 14 06mov byte ptr ss:,0x6
00468B17|.E8 64FEFFFF call editplus.00468980
00468B1C|.8D8E 2C0C0000 lea ecx,dword ptr ds:
00468B22|.C64424 14 05mov byte ptr ss:,0x5
00468B27|.E8 F4FB0700 call editplus.004E8720
00468B2C|.8D8E FC050000 lea ecx,dword ptr ds:
00468B32|.C64424 14 04mov byte ptr ss:,0x4
00468B37|.E8 74630500 call editplus.004BEEB0
00468B3C|.8D8E 80040000 lea ecx,dword ptr ds:
00468B42|.C64424 14 03mov byte ptr ss:,0x3
00468B47|.E8 84FDFFFF call editplus.004688D0
00468B4C|.8D8E 04030000 lea ecx,dword ptr ds:
00468B52|.C64424 14 02mov byte ptr ss:,0x2
00468B57|.E8 74FDFFFF call editplus.004688D0
00468B5C|.8D8E 88010000 lea ecx,dword ptr ds:
00468B62|.C64424 14 01mov byte ptr ss:,0x1
00468B67|.E8 64FDFFFF call editplus.004688D0
00468B6C|.8D8E EC000000 lea ecx,dword ptr ds:
00468B72|.C64424 14 00mov byte ptr ss:,0x0
00468B77|.E8 5D0F0C00 call editplus.00529AD9
00468B7C|.8BCE mov ecx,esi
00468B7E|.C74424 14 FFF>mov dword ptr ss:,-0x1
00468B86|.E8 CF5A0B00 call editplus.0051E65A
00468B8B|.8B4C24 0C mov ecx,dword ptr ss:
00468B8F|.64:890D 00000>mov dword ptr fs:,ecx
00468B96|.59 pop ecx
00468B97|.5E pop esi
00468B98|.83C4 10 add esp,0x10
00468B9B\.C3 retn
然后我们就到了下面这里,这段代码计算了用户名unicode的和然后+1存在ecx里,然后ecx=9*ecx+10,后面是个最简单的除法,除以3,之后计算了一下结果的绝对值,用swprintf将16进制的结果转为unicode字符串,然后将第一个位与我们的注册码的第5位做了一下比较,需要相等,至此我们就逆向出了Editplus4的整个注册码验证过程
004D5F10 $6A FF push -0x1
004D5F12 .68 834F5700 push editplus.00574F83
004D5F17 .64:A1 0000000>mov eax,dword ptr fs:
004D5F1D .50 push eax
004D5F1E .83EC 20 sub esp,0x20
004D5F21 .A1 7C9D5C00 mov eax,dword ptr ds:
004D5F26 .33C4 xor eax,esp
004D5F28 .894424 1C mov dword ptr ss:,eax
004D5F2C .53 push ebx
004D5F2D .55 push ebp
004D5F2E .56 push esi
004D5F2F .57 push edi
004D5F30 .A1 7C9D5C00 mov eax,dword ptr ds:
004D5F35 .33C4 xor eax,esp
004D5F37 .50 push eax
004D5F38 .8D4424 34 lea eax,dword ptr ss:
004D5F3C .64:A3 0000000>mov dword ptr fs:,eax
004D5F42 .8BE9 mov ebp,ecx
004D5F44 .896C24 18 mov dword ptr ss:,ebp
004D5F48 .C745 00 74535>mov dword ptr ss:,editplus.00595374
004D5F4F .8B95 20010000 mov edx,dword ptr ss:
004D5F55 .8BC2 mov eax,edx
004D5F57 .C74424 3C 010>mov dword ptr ss:,0x1
004D5F5F .8D70 02 lea esi,dword ptr ds:
004D5F62 >66:8B08 mov cx,word ptr ds:
004D5F65 .83C0 02 add eax,0x2
004D5F68 .66:85C9 test cx,cx
004D5F6B .^ 75 F5 jnz short editplus.004D5F62
004D5F6D .2BC6 sub eax,esi
004D5F6F .D1F8 sar eax,1
004D5F71 .8BD8 mov ebx,eax
004D5F73 .895C24 14 mov dword ptr ss:,ebx
004D5F77 .85DB test ebx,ebx
004D5F79 .0F8E B5000000 jle editplus.004D6034 ;usrlen != 0
004D5F7F .33F6 xor esi,esi
004D5F81 .33FF xor edi,edi
004D5F83 .33C0 xor eax,eax
004D5F85 .83FB 02 cmp ebx,0x2
004D5F88 .8D4E 01 lea ecx,dword ptr ds: ;ecx = 1
004D5F8B .7C 1F jl short editplus.004D5FAC
004D5F8D .4B dec ebx
004D5F8E .8BFF mov edi,edi
004D5F90 >0FB72C42 movzx ebp,word ptr ds:
004D5F94 .03F5 add esi,ebp ;esi += usr
004D5F96 .0FB76C42 02 movzx ebp,word ptr ds:
004D5F9B .83C0 02 add eax,0x2
004D5F9E .03FD add edi,ebp ;edi += usr
004D5FA0 .3BC3 cmp eax,ebx
004D5FA2 .^ 7C EC jl short editplus.004D5F90
004D5FA4 .8B5C24 14 mov ebx,dword ptr ss:
004D5FA8 .8B6C24 18 mov ebp,dword ptr ss:
004D5FAC >3BC3 cmp eax,ebx
004D5FAE .7D 05 jge short editplus.004D5FB5
004D5FB0 .0FB70C42 movzx ecx,word ptr ds: ;unicode of username last char
004D5FB4 .41 inc ecx
004D5FB5 >03F7 add esi,edi ;esi = username 2x位 unicode sum, 2x是小于用户名长度的最大偶数
004D5FB7 .03CE add ecx,esi ;ecx = username unicode sum + 1
004D5FB9 .8D4CC9 0A lea ecx,dword ptr ds: ;ecx=9*ecx+10
004D5FBD .B8 56555555 mov eax,0x55555556
004D5FC2 .F7E9 imul ecx
004D5FC4 .8BC2 mov eax,edx
004D5FC6 .C1E8 1F shr eax,0x1F
004D5FC9 .8D4C02 24 lea ecx,dword ptr ds: ; ecx/3+0x24
004D5FCD .81E1 0F000080 and ecx,0x8000000F ;带符号的最低位
004D5FD3 .79 05 jns short editplus.004D5FDA
004D5FD5 .49 dec ecx
004D5FD6 .83C9 F0 or ecx,-0x10
004D5FD9 .41 inc ecx
004D5FDA >51 push ecx ;take ABS
004D5FDB .8D5424 20 lea edx,dword ptr ss:
004D5FDF .68 1C685800 push editplus.0058681C ;UNICODE "%1X"
004D5FE4 .52 push edx
004D5FE5 .E8 F63CF3FF call <editplus.swprintf>
004D5FEA .8B95 24010000 mov edx,dword ptr ss:
004D5FF0 .8BC2 mov eax,edx
004D5FF2 .83C4 0C add esp,0xC
004D5FF5 .8D70 02 lea esi,dword ptr ds:
004D5FF8 .EB 06 jmp short editplus.004D6000
004D5FFA 8D9B 00000000 lea ebx,dword ptr ds:
004D6000 >66:8B08 mov cx,word ptr ds:
004D6003 .83C0 02 add eax,0x2
004D6006 .66:85C9 test cx,cx
004D6009 .^ 75 F5 jnz short editplus.004D6000 ;pwdlen != 0
004D600B .2BC6 sub eax,esi
004D600D .D1F8 sar eax,1
004D600F .83F8 05 cmp eax,0x5
004D6012 .72 0F jb short editplus.004D6023
004D6014 .66:8B42 08 mov ax,word ptr ds: ;ax = pwd
004D6018 .66:3B4424 1Ccmp ax,word ptr ss: ;pwd = 计算结果第一位
004D601D .0F84 DC000000 je editplus.004D60FF
004D6023 >8B8D 34010000 mov ecx,dword ptr ss:
004D6029 .C701 01000000 mov dword ptr ds:,0x1 ;Checkpoint2!
算法的总结看注册机就好了,还挺简单易懂的,这里其实(tmp*9+10)/3直接用tmp*3+3作为结果应该也没事
UINT16 table;
void BuildCRCTable(UINT16 *table)
{
int i, j, ax;
for (i = 0; i < 0x100; ++i)
{
ax = 0xc0c1;
for (j = 1; j < 0x100; j <<= 1)
{
if (i & j)
table ^= ax;
ax <<= 1;
ax ^= 0x4003;
}
}
}
void GetAsciiOfUnicode(wchar_t *words, char *ascii)
{
for (size_t i = 0, j = 0; i < wcslen(words); ++i, j += 4)
{
sprintf(ascii + j, "%04x", words);
}
}
UINT16 CRC16(char *ascii)
{
UINT16 res = 0;
byte tmp;
for (size_t i = 0; i < strlen(ascii); ++i)
{
tmp = res & 0xFF;
res >>= 8;
tmp ^= ascii;
res ^= table;
}
return res;
}
void CEditplusKeygenDlg::OnBnClickedOk()
{
wchar_t usr, unicode, regcode = L"12345-12345-12345-12345-12345";
GetDlgItemText(IDC_EDIT1, usr, 19);
int tmp = 1;
for (int i = 0; i < wcslen(usr); ++i)
{
tmp += usr;
}
tmp = (tmp * 9 + 10) / 3 + 0x24;
int res = tmp % 16;
swprintf(unicode, 9, L"%x", res);
regcode = unicode;
char ascii;
GetAsciiOfUnicode(usr, ascii);
swprintf(unicode, 5, L"%04x", CRC16(strupr(ascii)));
regcode = unicode;
regcode = unicode;
wcsupr(regcode);
GetAsciiOfUnicode(regcode + 2, ascii);
swprintf(unicode, 5, L"%04x", CRC16(strupr(ascii)));
regcode = unicode;
regcode = unicode;
SetDlgItemText(IDC_EDIT2, CString(regcode));
}
最后放上一组key
Username: fly1n
Regcode: 79958-12345-12345-12345-12345
在注册表里可以看到这个是4的key
有说的不对或者不清楚的地方欢迎大家指正! 按楼主的代码编译不成功,修改后能编译,但不能得到注册码。能否指点一下。
#include <stdio.h>
#include "editplusC1.h"
UINT16 table;
void BuildCRCTable(UINT16 *table)
{
int i, j, ax;
for (i = 0; i < 0x100; ++i)
{
ax = 0xc0c1;
for (j = 1; j < 0x100; j <<= 1)
{
if (i & j)
table ^= ax;
ax <<= 1;
ax ^= 0x4003;
}
}
}
void GetAsciiOfUnicode(wchar_t *words, char *ascii)
{
for (size_t i = 0, j = 0; i < wcslen(words); ++i, j += 4)
{
sprintf(ascii + j, "%04x", words);
}
}
UINT16 CRC16(char *ascii)
{
UINT16 res = 0;
byte tmp;
for (size_t i = 0; i < strlen(ascii); ++i)
{
tmp = res & 0xFF;
res >>= 8;
//tmp ^= ascii;
tmp ^= ascii;
res ^= table;
}
return res;
}
int main()
{
wchar_t usr, unicode, regcode = L"12345-12345-12345-12345-12345";
// GetDlgItemText(IDC_EDIT1, usr, 19);
scanf("%s",usr);
int tmp = 1;
for (int i = 0; i < wcslen(usr); ++i)
{
tmp += usr;
}
tmp = (tmp * 9 + 10) / 3 + 0x24;
int res = tmp % 16;
swprintf(unicode, L"%x", res);
regcode = unicode;
char ascii;
GetAsciiOfUnicode(usr, ascii);
swprintf(unicode, L"%04x", CRC16(strupr(ascii)));
regcode = unicode;
regcode = unicode;
wcsupr(regcode);
GetAsciiOfUnicode(regcode + 2, ascii);
swprintf(unicode, L"%04x", CRC16(strupr(ascii)));
regcode = unicode;
regcode = unicode;
//SetDlgItemText(IDC_EDIT2, CString(regcode));
printf("%s",regcode);
}
本帖最后由 fly1n 于 2017-2-12 02:38 编辑
whyida 发表于 2017-2-11 23:09
按楼主的代码编译不成功,修改后能编译,但不能得到注册码。能否指点一下。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
uint16_t table;
void BuildCRCTable(uint16_t *table)
{
int i, j, ax;
for (i = 0; i < 0x100; ++i)
{
ax = 0xc0c1;
for (j = 1; j < 0x100; j <<= 1)
{
if (i & j)
table ^= ax;
ax <<= 1;
ax ^= 0x4003;
}
}
}
void GetAsciiOfUnicode(wchar_t *words, char *ascii)
{
for (size_t i = 0, j = 0; i < wcslen(words); ++i, j += 4)
{
sprintf_s(ascii + j, 5, "%04x", words);
}
}
uint16_t CRC16(char *ascii)
{
uint16_t res = 0;
uint8_t tmp;
for (size_t i = 0; i < strlen(ascii); ++i)
{
tmp = res & 0xFF;
res >>= 8;
//tmp ^= ascii;
tmp ^= ascii;
res ^= table;
}
return res;
}
int main()
{
BuildCRCTable(table);
wchar_t usr, unicode, regcode = L"12345-12345-12345-12345-12345";
// GetDlgItemText(IDC_EDIT1, usr, 19);
wscanf_s(L"%s", usr, (unsigned)_countof(usr));
int sum = 1;
for (size_t i = 0; i < wcslen(usr); ++i)
{
sum += usr;
}
int res = ((sum * 9 + 10) / 3 + 0x24) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
char ascii;
GetAsciiOfUnicode(usr, ascii);
_strupr_s(ascii, (unsigned)_countof(ascii));
swprintf_s(unicode, (unsigned)_countof(unicode), L"%04x", CRC16(ascii));
regcode = unicode;
regcode = unicode;
_wcsupr_s(regcode, (unsigned)_countof(regcode));
GetAsciiOfUnicode(regcode + 2, ascii);
_strupr_s(ascii, (unsigned)_countof(ascii));
swprintf_s(unicode, (unsigned)_countof(unicode), L"%04x", CRC16(ascii));
regcode = unicode;
regcode = unicode;
/********************新加部分*******************************/
res = ((sum + 41) / 6 * 7) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
res = ((3 * sum + 19) / 9) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
res = (sum + 2) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
res = ((3 * sum + 43) / 8) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
res = ((sum + 10) / 3 * 8) % 16;
swprintf_s(unicode, (unsigned)_countof(unicode), L"%x", res);
regcode = unicode;
_wcsupr_s(regcode, (unsigned)_countof(regcode));
/**************************************************************/
wprintf(L"%s", regcode);}
自己跟着错误提示调试一下就出来了。。。另外注册机更新了,我发现这软件的暗桩巨多。。。在各个窗口类的析构函数里都有,更新以后的注册机要加几行代码,见帖子最前面的更新 新人贴 还是算法分析,加精鼓励! 不错不错。 这个新人贴好。。一来就发精华帖。 厉害!真人不露相。
太棒了,这个必须支持啦 这个软件是很好的代替文本文档的软件. 赞,Editplus没有网络验证key是否是黑名单之类的吗? 不错,学习了,感谢楼主热心分享 膜拜大神,一直是很喜欢的TXT编辑软件。