国外某软件3.47
XXX是一个16位的程序,ne结构,以前破解过3.41版本,在98下面用trw2000破解的,现在的Ollydbg不支持NE结构的程序。奈何?
好在他升级后,加密方式也有所升级,目录下面有个wsLicmgr.exe负责授权校验,是PE结构,于是打算做注册机。
这是个vc的程序,用mfcspy可以看到按钮的事件,
00407C3E . 83C4 04 add esp,4
00407C41 . 50 push eax ; |Arg1
00407C42 . E8 83E40000 call WSLICMGR.004160CA 校验第一个key,里面可以写出注册机来。 ; /WSLICMGR.004160CA
00407C78 . 50 push eax ; key2: 20c
00407C79 . E8 1AE30000 call WSLICMGR .00415F98 ; this call verify key2
这个call很关键,里面首先对key2进行检验是否合法,然后不停的test,结果影响eax,最终eax是返回值。
00407C7E . 6A 00 push 0
00407C80 . 51 push ecx
00407C81 . 8D96 740A0000 lea edx,dword ptr ds:[esi+A74]; 42042021
00407C87 . 8BF8 mov edi,eax ; return value
00407C89 . 8BCC mov ecx,esp
00407C8B . 896424 28 mov dword ptr ss:[esp+28],esp
00407C 8F . 52 push edx ; userid:
00407C90 . 897C24 20 mov dword ptr ss:[esp+20],edi ; Return value
00407C94 . E8 85990100 call <jmp.&MFC42.#535>
00407C99 . 57 push edi ; 前面一个函数的返回值 eax
00407C 9A . 8BCE mov ecx,esi
00407C 9C . E8 8F180000 call WSLICMGR.00409530; this call verify key2 and setupcode 这个call里面对上面的call的返回值和setup code进行比较,应该是从这里推出key2的构成形式。
00407CA1 . 84C0 test al,al ; return value can't be zero
2个核心的地方,基本上在字符串比较里面,
00409B90 |> /8B5424 40 mov edx,dword ptr ss:[esp+40]
00409B94 |. 68 E 4A04200 push WSLICMGR .0042A0E4 ; ASCII "AE1"
00409B99 |. 52 push edx
00409B 9A |. FFD6 call esi
00409B 9C |. 83C4 08 add esp,8
00409B 9F |. 85C0 test eax,eax
00409FC2 |> /8B4424 40 mov eax,dword ptr ss:[esp+40] ; Case C of switch 0040960F
12e954=结果?
004095F0 /$ 6A FF push -1
004095F2 |. 68 B0344200 push WSLICMGR.004234B0 ; SE handler installation
004095F7 |. 64:A1 00000000 mov eax,dword ptr fs:[0]
004095FD |. 50 push eax
004095FE |. 64:8925 00000000 mov dword ptr fs:[0],esp
00409605 |. 83EC 24 sub esp,24
00409608 |. 53 push ebx
00409609 |. 56 push esi
0040960A |. 57 push edi
0040960B |. 8B4424 48 mov eax,dword ptr ss:[esp+48] ; 12E 92c
12E 92C这个地址里存放着跳转的地址,应该是从key2转换得来的,现在是0,对于WDL应该是C
后来的f(key2)经过运算应该等于0
有两个小call
00414642 /$ B8 01000000 mov eax,1
00414647 |. 8B 4C24 08 mov ecx,dword ptr ss:[esp+8]
0041464B |. FEC9 dec cl
0041464D |. D3E0 shl eax,cl
0041464F |. 234424 04 and eax,dword ptr ss:[esp+4]
00414653 |. 83F8 01 cmp eax,1
00414656 |. 1BC0 sbb eax,eax
00414658 |. 40 inc eax
00414659 /. C2 0800 retn 8
上面这个call好像没有用处,仅仅是判断返回值eax是否等于1。没有对key进行变换,继续看下面一个call
00414610 /$ B8 01000000 mov eax,1
00414615 |. 8B 4C24 08 mov ecx,dword ptr ss:[esp+8]
00414619 |. FEC9 dec cl
0041461B |. D3E0 shl eax,cl
0041461D |. 8B 4C24 04 mov ecx,dword ptr ss:[esp+4]
00414621 |. F7D0 not eax
00414623 |. 2301 and eax,dword ptr ds:[ecx]
00414625 |. 8901 mov dword ptr ds:[ecx],eax
00414627 /. C2 0800 retn 8
E 95c
UserKey1的码也不是固定的,根据lic种类不同有不同,影响返回值eax,然后根据eax进行跳转。
Eax=1的时候WB
Case eax of
1:WB
2:
3:default case
4:prx
5:prx
6:jmp 8196 contract service upgrade to 1.0
7:jmp 82c2 没反应?
8:WL
9:default
A:83e9 PP
B:default
C: 84c1
Else
408b 2f push 0ch
校验的路径基本清楚了。
Var
Var
Idx : integer;
Idx := VerifyKey1(0x5426,code1,code2,key1);
Case idx of
1: ;
2:;
Default: ;
必须要跳到default
ebx:= VerifyKey1(0x892D,Code1,Code2,key1);
VerifyKey2(0xB5,key2);
0040867B . E8 18D90000 call <WSLICMGR.VerifyKey2> ; check key2
00408680 . 8BE8 mov ebp,eax ß----VerifyKey2的返回值
00408682 . 8BC3 mov eax,ebx ß-- VerifyKey1的返回值
00408684 . 48 dec eax ;Switch (cases 1..2)
00408685 . 0F84 EE010000 je WSLICMGR.00408879 ß-不能跳
0040868B . 48 dec eax
0040868C . 0F85 08040000 jnz WSLICMGR .00408A 9Aß---必须要跳
在上面,VerifyKey1必须大于2,
Ret := VerifyKey1(0x 4c50,Code1,Code2,Key1); ret=1
VerifyKey2(0x6B,key2)
00408ABC . E8 09D60000 call <WSLICMGR.VerifyKey1> ; /WSLICMGR.004160CA
00408AC1 . 8BE8 mov ebp,eax
00408AC3 . 8B4424 1C mov eax,dword ptr ss:[esp+ 1C] ; key1
00408AC7 . 6A 6B push 6B
00408AC9 . 50 push eax
00408ACA . FFD7 call edi
00408ACC . 83C4 04 add esp,4
00408ACF . 50 push eax
00408AD0 . E 8 C3D40000 call <WSLICMGR.VerifyKey2>
00408AD5 . 8BF8 mov edi,eax
00408AD7 . 8BC5 mov eax,ebp ß--verifyKey1的返回值,=1
00408AD9 . 48 dec eax ; Switch (cases 1..3)
00408ADA . 74 53 je short WSLICMGR.00408B 2F必须要跳转 里面出现push 0c,正确的地方。
00408ADC . 83E8 02 sub eax,2
00408ADF . 74 18 je short WSLICMGR.00408AF9
00408B 2F > / 6A 0C push 0C ; Case 1 of switch 00408AD9
00408B31 . 51 push ecx
00408B32 . 8D96 740A0000 lea edx,dword ptr ds:[esi+A74]
00408B38 . 8BCC mov ecx,esp
00408B 3A . 896424 2C mov dword ptr ss:[esp+ 2C],esp
00408B3E . 52 push edx
00408B 3F . E8 DA 8A0100 call <WSLICMGR.CString::CString(CString>; jmp to MFC42.#535
00408B44 . 57 push edi
00408B45 . 8BCE mov ecx,esi
00408B47 . E8 E4090000 call <WSLICMGR.VerifySetupCode>
这个call校验SetupCode,”WDL”,成功返回1,也就是这里最终跳到成功的地方。
00408B 4C . 84C0 test al,al
00408B4E .^ 0F84 46FDFFFF je WSLICMGR .0040889Aß-----成功了!!!!!
跟进00408B47处的call
这个函数应该有三个参数,第二个参数应该是setupCode:’WDL’,最后一个参数表示跳转的位置,也就是根据它判断去什么地方跳转,这个参数是很核心的,我也大部分是根据这个参数倒退得到的解法,因为只有这个参数=C才有可能跳转到比较WDL的地方,否则是别的类型,比如WB1,WB2等。现在有点奇怪的是第二个参数居然得不到。难道是key2还有问题?
00409530 <>/$ 6A FF push -1
00409532 |. 68 48344200 push WSLICMGR.00423448 ; SE handler installation
00409537 |. 64:A1 00000000 mov eax,dword ptr fs:[0]
0040953D |. 50 push eax
0040953E |. 64:8925 00000000 mov dword ptr fs:[0],esp
00409545 |. 51 push ecx
00409546 |. 53 push ebx
00409547 |. 55 push ebp
00409548 |. 56 push esi ; "haB"
00409549 |. 57 push edi ; f(key2)
0040954A |. 8BF1 mov esi,ecx
0040954C |. 8B4424 28 mov eax,dword ptr ss:[esp+28] ; "WDL"
00409550 |. C74424 1C 00000000 mov dword ptr ss:[esp+ 1C],0
00409558 |. 50 push eax ; /s
00409559 |. B3 01 mov bl,1 ; |here, assign bl=1
0040955B |. FF15 4C544200 call dword ptr ds:[<&MSVCRT._strdup>]; /_strdup
00409561 |. 8B3D 00544200 mov edi,dword ptr ds:[<&MSVCRT.strtok>] ; MSVCRT.strtok
00409567 |. 8BE8 mov ebp,eax
00409569 |. 68 54A94200 push WSLICMGR .0042A954 ; /s2 = "-"
0040956E |. 55 push ebp ; |s1
0040956F |. 896C24 1C mov dword ptr ss:[esp+ 1C],ebp ; |
00409573 |. FFD7 call edi ; /strtok
00409575 |. 83C4 0C add esp, 0C
00409578 |. 85C0 test eax,eax
0040957A |. 74 38 je short WSLICMGR.004095B4 《----jmp
0040957C |. 8B 6C24 2C mov ebp,dword ptr ss:[esp+ 2C] ; case code
00409580 |> 8D 4C24 24 /lea ecx,dword ptr ss:[esp+24] ; f(key2)
00409584 |. 55 |push ebp
00409585 |. 51 |push ecx
00409586 |. 51 |push ecx
00409587 |. 8BCC |mov ecx,esp
00409589 |. 896424 38 |mov dword ptr ss:[esp+38],esp
0040958D |. 50 |push eax
0040958E |. E8 2D7E0100 |call <WSLICMGR.CString::CString(char c>; jmp to MFC42.#537
00409593 |. 8BCE |mov ecx,esi ; |
00409595 |. E8 56000000 |call <WSLICMGR.GetSetupCodeType> ; /check userid in this call
0040959A |. 84C0 |test al,al
0040959C |. 75 02 |jnz short WSLICMGR .004095A0
0040959E |. 32DB |xor bl,bl ; fail
004095A0 |> 68 54A94200 |push WSLICMGR .0042A954 ; setup code
004095A5 |. 6A 00 |push 0
004095A7 |. FFD7 |call edi
004095A9 |. 83C4 08 |add esp,8
004095AC |. 85C0 |test eax,eax
004095AE |.^ 75 D0 /jnz short WSLICMGR.00409580
004095B0 |. 8B 6C24 10 mov ebp,dword ptr ss:[esp+10]; "WDL"
004095B4 |> 8B4424 24 mov eax,dword ptr ss:[esp+24] ; f(key2)12E954,very important address
004095B8 |. 85C0 test eax,eax 这个地方有点奇怪,如果前面的GetSetupType没有正确执行的话,那么这个地方eax是f(key2)一定是非零。
004095BA |. 74 02 je short WSLICMGR.004095BE
004095BC |. 32DB xor bl,bl ; fail
004095BE |> 55 push ebp ; /block
004095BF |. FF15 08544200 call dword ptr ds:[<&MSVCRT.free>] ; /free
004095C5 |. 83C4 04 add esp,4
004095C8 |. 8D 4C24 28 lea ecx,dword ptr ss:[esp+28]
004095CC |. C74424 1C FFFFFFFF mov dword ptr ss:[esp+ 1C],-1
004095D4 |. E8 CF7D0100 call <WSLICMGR.CString::~CString(void)> ; jmp to MFC42.#800
004095D9 |. 8B 4C24 14 mov ecx,dword ptr ss:[esp+14]
004095DD |. 5F pop edi
004095DE |. 5E pop esi
004095DF |. 8AC3 mov al,bl
004095E1 |. 5D pop ebp
004095E2 |. 64:890D 00000000 mov dword ptr fs:[0],ecx
004095E9 |. 5B pop ebx
004095EA |. 83C4 10 add esp,10
004095ED /. C2 0C00 retn 0C
Key1=5270131 (根据第三次的verifyKey1必须=1得到)
Key2=0x52343023 or 0x20000 = 52363023 ->1379282979
(key2 and C284249=42042021) and (key2 and 10311282=111280) and (key2 and 10311282=10301002)
后面2个条件好像没法同时成立,所以取第一个和第三个,因为第三个校验有用。
整理:
根据VerifyKey1写出key1的算法
根据Verifykey2写出key2的算法。
修改VerifySetupCode里面见上