Triivi是一款英文输入法,据说是由浙大人开发的,也有人称Triivi是英文输入法中的搜狗。试用了一下,觉得输入英文单词不见得会快很多,但是对于比较生僻比较长的词有自动补全的功能,因此可以避免写错单词,还是很方便的。
Triivi有两个版本,一个是Professional,是需要付费的,还有一个版本是Educational Free,我没有仔细看这两者有什么区别,下面我们来简单分析一下Pro版本的注册算法,由于是随便写的,就不用固定的格式了。
先用PEiD侦壳,结果是VC8 -> Microsoft Corporation *,显示为未加壳,OD载入,停留在这里:
0042B9B5 > $ E8 D7600000 call 00431A91
0042B9BA .^ E9 17FEFFFF jmp 0042B7D6
先F9运行起来,然后到关于界面填写用户名ssfighter@newsmth和假序列号1234567890。
不要点注册,回到OD中,设断点bp GetDlgItemTextW。再点注册断下在这里:
77D247AD > 8BFF mov edi, edi
77D247AF 55 push ebp
按两次F9返回到这里:
004065FB . 68 00010000 push 100 ; /Arg3 = 00000100
00406600 . 8D4C24 40 lea ecx, [esp+40] ; |
00406604 . 51 push ecx ; |Arg2
00406605 . 68 2B040000 push 42B ; |Arg1 = 0000042B
0040660A . 8BCB mov ecx, ebx ; |
0040660C . E8 25970000 call 0040FD36 ; \Triivi.0040FD36
00406611 . 8D9424 3C0200>lea edx, [esp+23C]
00406618 . 52 push edx ; /注册码
00406619 . 8D8E D84B0000 lea ecx, [esi+4BD8] ; |
0040661F . E8 FCA9FFFF call 00401020 ; \关键call,F7步入
00406624 . 85C0 test eax, eax
00406626 . 75 66 jnz short 0040668E ; 关键跳,跳则死
在40661F处按F7跟进算法call,停在这里:
00401020 /$ 83EC 38 sub esp, 38
00401023 |. A1 7C994500 mov eax, [45997C]
00401028 |. 33C4 xor eax, esp
0040102A |. 894424 34 mov [esp+34], eax
0040102E |. 57 push edi
0040102F |. 8B7C24 40 mov edi, [esp+40]
00401033 |. 8BC7 mov eax, edi
00401035 |. 8D50 02 lea edx, [eax+2]
00401038 |. EB 06 jmp short 00401040
0040103A | 8D9B 00000000 lea ebx, [ebx]
00401040 |> 66:8B08 /mov cx, [eax]
00401043 |. 83C0 02 |add eax, 2 ; 由于是Unicode格式,一个字母占两个字节
00401046 |. 66:85C9 |test cx, cx
00401049 |.^ 75 F5 \jnz short 00401040 ; 这个循环是计算注册码长度
0040104B |. 2BC2 sub eax, edx
0040104D |. D1F8 sar eax, 1
0040104F |. 83F8 64 cmp eax, 64 ; 注册码长度=100?
00401052 |. 74 14 je short 00401068 ; 不等则死
跳转到这里:
00401068 |> \56 push esi
00401069 |. 33F6 xor esi, esi ; esi清零
0040106B |. 8D4F 04 lea ecx, [edi+4]
0040106E |. 8BFF mov edi, edi
00401070 |> 0FB641 FC /movzx eax, byte ptr [ecx-4] ; 注册码第一位
00401074 |. B2 1A |mov dl, 1A
00401076 |. F6EA |imul dl ; 乘以26
00401078 |. 0241 FE |add al, [ecx-2] ; 再加上注册码第二位
0040107B |. 83C6 05 |add esi, 5 ; esi=esi+5
0040107E |. 04 25 |add al, 25 ; 再加上37
00401080 |. 884434 03 |mov [esp+esi+3], al ; 除以256取模,存入堆栈
00401084 |. 0FB601 |movzx eax, byte ptr [ecx]
00401087 |. F6EA |imul dl
00401089 |. 0241 02 |add al, [ecx+2]
0040108C |. 83C1 14 |add ecx, 14
0040108F |. 04 25 |add al, 25
00401091 |. 884434 04 |mov [esp+esi+4], al
00401095 |. 0FB641 F0 |movzx eax, byte ptr [ecx-10]
00401099 |. F6EA |imul dl
0040109B |. 0241 F2 |add al, [ecx-E]
0040109E |. 04 25 |add al, 25
004010A0 |. 884434 05 |mov [esp+esi+5], al
004010A4 |. 0FB641 F4 |movzx eax, byte ptr [ecx-C]
004010A8 |. F6EA |imul dl
004010AA |. 0241 F6 |add al, [ecx-A]
004010AD |. 04 25 |add al, 25
004010AF |. 884434 06 |mov [esp+esi+6], al
004010B3 |. 0FB641 F8 |movzx eax, byte ptr [ecx-8]
004010B7 |. F6EA |imul dl
004010B9 |. 0241 FA |add al, [ecx-6]
004010BC |. 04 25 |add al, 25
004010BE |. 83FE 32 |cmp esi, 32 ; esi=50?
004010C1 |. 884434 07 |mov [esp+esi+7], al
004010C5 |.^ 72 A9 \jb short 00401070
这里把注册码按两位分成一组,进行第一位*26加上第二位再加上37,之后对256取模的操作,这样把100位注册码先合并成50位。
循环完之后再向下看:
004010C7 |. 33C0 xor eax, eax
004010C9 |. 5E pop esi
004010CA |. 8D9B 00000000 lea ebx, [ebx]
004010D0 |> 8A88 A9474400 /mov cl, [eax+4447A9]
004010D6 |. 8A90 90474400 |mov dl, [eax+444790]
004010DC |. 324C04 1D |xor cl, [esp+eax+1D]
004010E0 |. 325404 04 |xor dl, [esp+eax+4]
004010E4 |. 3AD1 |cmp dl, cl
004010E6 |.^ 0F85 68FFFFFF |jnz 00401054
004010EC |. 83C0 01 |add eax, 1
004010EF |. 83F8 19 |cmp eax, 19
004010F2 |.^ 7C DC \jl short 004010D0
这里把上面得到的50位的码再平分成前后两部分。前一部分依次和[444790]到[4447A8]的数异或,后一部分依次和[4447A9]到[4447C1]进行异或,这两部分的25位数应该依次相等,这样注册才能通过,假设50位码中第一位是a,第26位是b,则有
a xor [444790] = b xor [4447A9]
也就是
a xor b = [444790] xor [4447A9] = 常数
以后的24个数也有同样规律。到此为止,注册算法结束,可以逆推注册机的算法了,先用随机数产生a,并异或得到b,注意要保证a、b都是可以正常输入的ascii字符即可,这样就可以先得到50位中间码,然后再就可以用同样的随机数方法求出全部100位注册码了。
注册机用Delphi7编写,关键部分的算法贴在这里(其中Reg25_1和Reg25_2分别是英文版和中文版中[444790]异或[4447A9]得到的25位常数,中文版和英文版的算法完全一样,只是那些常数不一样而已):
var i,char1,char2: integer;
Reg25_1, Reg25_2, Reg50: String;
begin
Randomize;
if RadioButton1.Checked=True then
for i:=1 to 25 do
RegCode[i]:=RegCode1[i]
else
for i:=1 to 25 do
RegCode[i]:=Regcode2[i];
Reg25_1:=”;
Reg25_2:=”;
for i:=1 to 25 do
begin
char1:=32+Random(94);
char2:=RegCode[i] xor char1;
while (char2<32) or (char2>126) do
begin
char1:=32+Random(94);
char2:=RegCode[i] xor char1;
end;
Reg25_1:=Reg25_1+Chr(char1);
Reg25_2:=Reg25_2+Chr(char2);
end;
Reg50:=Reg25_1+Reg25_2;
Edit1.Text:=”;
for i:=1 to 50 do
begin
char1:=32+Random(94);
char2:=Ord(Reg50[i])-((char1*26+37) mod 256);
if char2<0 then
char2:=char2+256;
while (char2<32) or (char2>126) do
begin
char1:=32+Random(94);
char2:=Ord(Reg50[i])-((char1*26+37) mod 256);
if char2<0 then
char2:=char2+256;
end;
Edit1.Text:=Edit1.Text+Chr(char1)+chr(char2);
end;
end;
注册算法很简单,我好久没玩这些东西了,只好拿点简单的东西来练练手,高手莫笑。
注册机下载:点击此处下载