【软件介绍】: LibXL is a library that can read and write Excel files. It doesn't require Microsoft Excel and .NET framework, combines an easy to use and powerful features.
LibXL 可以原格式读写 Excel ,不需要电脑安装 Office, 具体使用请参照官网的文档:http://www.libxl.com/documentation.html
好久没练手了, 这次分析下算法. 看下 libxl.dll 中的导出函数 xlBookSetKeyA(BookHandle handle, const char name, const char key);
很容易找到关键地方。编写个测试 exe 进入 xlBookSetKey 开始调试。我只简单描述下关键地方.
10032640 /$ 55 push ebp ; xlBookSetKey 函数入口
10032641 |. 8BEC mov ebp, esp
10032643 |. 6A FF push -0x1
10032645 |. 68 CA4D3810 push 10384DCA
1003264A |. 64:A1 0000000>mov eax, dword ptr fs:[0]
10032650 |. 50 push eax
下面 是对用户名,注册码长度判断
10032687 |. 85DB test ebx, ebx ; 用户名是否为空?
10032689 |. 0F84 C2040000 je 10032B51
1003268F |. 85C0 test eax, eax ; 注册码是否为空?
10032691 |. 0F84 BA040000 je 10032B51
10032697 |. 50 push eax
10032698 |. 8D4D 98 lea ecx, dword ptr [ebp-0x68]
1003269B |. E8 B02DFDFF call 10005450 ; 求注册码长度
100326A0 |. 837D AC 28 cmp dword ptr [ebp-0x54], 0x28 ; 注册码长度 是否 为 40 位?
100326A4 |. C645 FC 01 mov byte ptr [ebp-0x4], 0x1
100326A8 |. C686 44030000>mov byte ptr [esi+0x344], 0x0
100326AF |. 8D4D 98 lea ecx, dword ptr [ebp-0x68]
100326B2 |. 0F85 94040000 jnz 10032B4C
100326B8 |. 6A 08 push 0x8
100327C4 |. E8 67D0FFFF call 1002F830 ; 用户名字符串 翻转
100327C9 |. 56 push esi
100327CA |. E8 01083300 call 10362FD0
100327CF |. 83C4 14 add esp, 0x14
100327D2 |. 6A FF push -0x1
100327D4 |. 6A 00 push 0x0
100327D6 |. 8D8D 7CFFFFFF lea ecx, dword ptr [ebp-0x84]
100327DC |. 51 push ecx
100327DD |. 8B8D ECFEFFFF mov ecx, dword ptr [ebp-0x114]
100327E3 |. 81C1 A4090000 add ecx, 0x9A4
100327E9 |. E8 A2EBFCFF call 10001390
100327EE |. 83EC 1C sub esp, 0x1C
100327F1 |. 8D95 7CFFFFFF lea edx, dword ptr [ebp-0x84]
100327F7 |. 8BCC mov ecx, esp
100327F9 |. 89A5 E8FEFFFF mov dword ptr [ebp-0x118], esp
100327FF |. 52 push edx
10032800 |. E8 4BEDFCFF call 10001550
10032805 |. 8D85 28FFFFFF lea eax, dword ptr [ebp-0xD8]
1003280B |. 50 push eax
1003280C |. E8 2F750300 call 10069D40 ; 翻转后的用户名, 求 MD5 值
10032811 |. 83C4 20 add esp, 0x20
下面一段代码 取出32位注册码的第1,3,5,7,9,11,13,15,17,19,21,23,25位,并将取出的字符连接成字符串
100328E0 |> /83FE 20 /cmp esi, 0x20
100328E3 |. |73 43 |jnb short 10032928
100328E5 |. |83FE 1A |cmp esi, 0x1A
100328E8 |. |73 1B |jnb short 10032905
100328EA |. |56 |push esi
100328EB |. |8D8D F0FEFFFF |lea ecx, dword ptr [ebp-0x110]
100328F1 |. |E8 6A730300 |call 10069C60
100328F6 |. |0FB600 |movzx eax, byte ptr [eax]
100328F9 |. |50 |push eax ; /Arg1
100328FA |. |8D8D 0CFFFFFF |lea ecx, dword ptr [ebp-0xF4] ; |
10032900 |. |E8 6BD5FFFF |call 1002FE70 ; \libxl.1002FE70
10032905 |> |8D4E 01 |lea ecx, dword ptr [esi+0x1]
10032908 |. |51 |push ecx
10032909 |. |8D8D F0FEFFFF |lea ecx, dword ptr [ebp-0x110]
1003290F |. |E8 4C730300 |call 10069C60
10032914 |. |0FB610 |movzx edx, byte ptr [eax]
10032917 |. |52 |push edx ; /Arg1
10032918 |. |8D8D 44FFFFFF |lea ecx, dword ptr [ebp-0xBC] ; |
1003291E |. |E8 4DD5FFFF |call 1002FE70 ; \libxl.1002FE70
10032923 |. |83C6 02 |add esi, 0x2
10032926 |.^\EB B8 \jmp short 100328E0
对上一步取出的字符串 求 MD5,并截取 前 16 位,比较 md5 值的 前 16 位 是否是 3f8bfcaff330c39f
1003298E |. 8038 33 cmp byte ptr [eax], 0x33 ; 3
10032991 |. 0F85 41010000 jnz 10032AD8
10032997 |. 6A 01 push 0x1
10032999 |. 8D4D D0 lea ecx, dword ptr [ebp-0x30]
1003299C |. E8 BF720300 call 10069C60
100329A1 |. 8038 66 cmp byte ptr [eax], 0x66 ; f
100329A4 |. 0F85 2E010000 jnz 10032AD8
100329AA |. 6A 02 push 0x2
100329AC |. 8D4D D0 lea ecx, dword ptr [ebp-0x30]
100329AF |. E8 AC720300 call 10069C60
100329B4 |. 8038 38 cmp byte ptr [eax], 0x38 ; 8
100329B7 |. 0F85 1B010000 jnz 10032AD8
100329BD |. 6A 03 push 0x3
100329BF |. 8D4D D0 lea ecx, dword ptr [ebp-0x30]
100329C2 |. E8 99720300 call 10069C60
100329C7 |. 8038 62 cmp byte ptr [eax], 0x62 ; b
100329CA |. 0F85 08010000 jnz 10032AD8
100329D0 |. 6A 04 push 0x4
100329D2 |. 8D4D D0 lea ecx, dword ptr [ebp-0x30]
100329D5 |. E8 86720300 call 10069C60
100329DA |. 8038 66 cmp byte ptr [eax], 0x66 ; f
...............................
...............................
以下代码, 可知注册码第 27, 29, 31 位满足关系
1002EE13 |. 0FBE71 1C movsx esi, byte ptr [ecx+0x1C]
1002EE17 |. 81C6 79070000 add esi, 0x779
1002EE1D |. 83F8 1E cmp eax, 0x1E
1002EE20 |. 73 09 jnb short 1002EE2B
1002EE22 |. E8 6B3C3300 call 10362A92
1002EE27 |. 8B4424 24 mov eax, dword ptr [esp+0x24]
1002EE2B |> 8B4C24 14 mov ecx, dword ptr [esp+0x14]
1002EE2F |. 396C24 28 cmp dword ptr [esp+0x28], ebp
1002EE33 |. 73 04 jnb short 1002EE39
1002EE35 |. 8D4C24 14 lea ecx, dword ptr [esp+0x14]
1002EE39 |> 57 push edi
1002EE3A |. 0FBE79 1E movsx edi, byte ptr [ecx+0x1E]
1002EE3E |. 83EF 69 sub edi, 0x69
1002EE41 |. 83F8 1A cmp eax, 0x1A
1002EE44 |. 73 05 jnb short 1002EE4B
1002EE46 |. E8 473C3300 call 10362A92
1002EE4B |> 8B4424 18 mov eax, dword ptr [esp+0x18]
1002EE4F |. 396C24 2C cmp dword ptr [esp+0x2C], ebp
1002EE53 |. 73 04 jnb short 1002EE59
1002EE55 |. 8D4424 18 lea eax, dword ptr [esp+0x18]
1002EE59 |> 0FBE50 1A movsx edx, byte ptr [eax+0x1A]
1002EE5D |. 8D8C37 87F8FF>lea ecx, dword ptr [edi+esi-0x779]
1002EE64 |. 3BD1 cmp edx, ecx
1002EE66 |. 75 28 jnz short 1002EE90
1002EE68 |. 81FE DD070000 cmp esi, 0x7DD
1002EE6E |. 7E 0F jle short 1002EE7F
1002EE70 |. 81FE DF070000 cmp esi, 0x7DF
1002EE76 |. 7C 18 jl short 1002EE90
1002EE78 |. 75 0D jnz short 1002EE87
1002EE7A |. 83FF 03 cmp edi, 0x3
1002EE7D |. EB 06 jmp short 1002EE85
1002EE7F |> 81FE DC070000 cmp esi, 0x7DC
由上面可得知 注册码第27, 29, 31 位 满足以下关系
第29个字符串 d 0x64 + 0x779 = 0x7DD -> ESI
第31个字符串 o 0x6F-0x69 = 0x6 -> EDI
第27个字符串 j 0x6A
ESI + EDI - 0x779 = 0x6A
if ESI <= 0x7DD then
if ESI < 0x7DC then
[ebx+0x9F8]= 0x1 失败
else
[ebx+0x99C]= 0 成功
end if
else
if ESI < 0x7DF then
[ebx+0x9F8]= 0x1 失败
else
if ESI != 0x7DF then
[ebx+0x99C]= 0 成功
else
if EDI < 0x3 then
[ebx+0x9F8]= 0x1 失败
else
[ebx+0x99C]= 0 成功
end if
end if
end if
end if
算法总结:
注册码格式:windows-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(windows- 后面 32 位)
32位注册码的第1,3,5,7,9,11,13,15,17,19,21,23,25位是固定值, 分别是 22200ce06b66a
32位注册码的第2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,30, 32位是:用户名字符串,经过翻转, 求出 MD5 值, 然后 取 前 16 位
32位注册码的第27, 29, 31 位满足以下关系:
(1) ASC(29位)+ASC(31位)-0x69 = ASC(27位字母)
(2) ASC(29位) >= 0x63 并且 ASC(29位) 不能等于 0x65, 而且 当 ASC(29位) = 0x66 时,ASC(31位) >=0x6C
以下是注册机源码的关键部分(使用 PowerBASIC 语言 )
function GetRegCode(byval hWnd as dword, byref edtstr as asciiz) as STRING
local i as LONG
local oMD5 as iMD5
Dim bReg(32) as BYTE
local sName as asciiz * 260
local szMd5 as ASCIIZ * 40
local szChar as ASCIZ * 10
dim p as byte ptr
dim pRegCode as ASCIIZ ptr
if CheckIsDBCS(edtstr) = 1 THEN
MessageBox hWnd, "用户名不能包含中文。", "提示!", %MB_OK or %MB_ICONEXCLAMATION
END IF
for i = len(edtstr) to 1 step -1
sName = sName & Mid$(edtstr, i, 1)
NEXT
oMD5 = class "MD5"
szMd5 = LCASE$(oMD5.calc(sName))
ARRAY ASSIGN bReg() = &H32,&H00,&H32,&H00,&H32,&H00,&H30,&H00,&H30,&H00,&H63,&H00,&H65,&H00,&H30,&H00,&H36,&H00,&H62,&H00,&H36,&H00,&H36,&H00,&H61,&H00,&H00,&H00,&H00,&H00,&H00,&H00,&H00
p = varptr(szMd5)
for i = 1 to 31
bReg(i) = @p
p=p+1
i=i+1
NEXT
szChar = GetThreeChar()
p = varptr(szChar)
bReg(26) = @p
p = p+1
bReg(28) = @p
p = p+1
bReg(30) = @p
pRegCode = varptr(bReg(0))
function = "windows-" & @pRegCode
END FUNCTION
function GetThreeChar() as STRING
local char27 as ASCIIZ * 2
local char29 as ASCIIZ * 2
local char31 as ASCIIZ * 2
char29 = Get29()
char31 = Get31(char29)
char27 = chr$(ASC(char29) + ASC(char31) - 105)
function = char27 & char29 & char31
END FUNCTION
function Get29() as string
local char29 as ASCIIZ * 2
randomize
char29 = chr$(int(rnd*24 + 99))
if asc(char29) = 101 THEN ' e
Get29()
END IF
function = char29
END FUNCTION
function Get31(byref char as asciiz) as STRING
local char31 as ASCIIZ * 2
randomize
char31 = chr$(int(rnd*26 + 202- ASC(char)))
if ASC(char) <> 102 THEN
if ASC(char31) >= 97 and ASC(char31) < 123 THEN
function = char31
else
Get31(char)
END IF
else
if ASC(char31) >= 108 and ASC(char31) < 123 THEN
function = char31
else
Get31(char)
END IF
END IF
END FUNCTION