LibXL 算法分析(附注册机)
本帖最后由 GCCG 于 2017-1-15 14:25 编辑【文章标题】: LibXL 算法分析(附注册机)
【文章作者】: GCCG
【软件名称】: LibXL
【下载地址】: http://www.libxl.com
【加壳方式】: 无加壳
【编写语言】: Visual C++
【使用工具】: ollydbg
【操作平台】: windows
【软件介绍】: 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:
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
1003269B|.E8 B02DFDFF call 10005450 ;求注册码长度
100326A0|.837D AC 28 cmp dword ptr , 0x28 ;注册码长度 是否 为 40 位?
100326A4|.C645 FC 01 mov byte ptr , 0x1
100326A8|.C686 44030000>mov byte ptr , 0x0
100326AF|.8D4D 98 lea ecx, dword ptr
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
100327DC|.51 push ecx
100327DD|.8B8D ECFEFFFF mov ecx, dword ptr
100327E3|.81C1 A4090000 add ecx, 0x9A4
100327E9|.E8 A2EBFCFF call 10001390
100327EE|.83EC 1C sub esp, 0x1C
100327F1|.8D95 7CFFFFFF lea edx, dword ptr
100327F7|.8BCC mov ecx, esp
100327F9|.89A5 E8FEFFFF mov dword ptr , esp
100327FF|.52 push edx
10032800|.E8 4BEDFCFF call 10001550
10032805|.8D85 28FFFFFF lea eax, dword ptr
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
100328F1|. |E8 6A730300 |call 10069C60
100328F6|. |0FB600 |movzx eax, byte ptr
100328F9|. |50 |push eax ; /Arg1
100328FA|. |8D8D 0CFFFFFF |lea ecx, dword ptr ; |
10032900|. |E8 6BD5FFFF |call 1002FE70 ; \libxl.1002FE70
10032905|> |8D4E 01 |lea ecx, dword ptr
10032908|. |51 |push ecx
10032909|. |8D8D F0FEFFFF |lea ecx, dword ptr
1003290F|. |E8 4C730300 |call 10069C60
10032914|. |0FB610 |movzx edx, byte ptr
10032917|. |52 |push edx ; /Arg1
10032918|. |8D8D 44FFFFFF |lea ecx, dword ptr ; |
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 , 0x33 ;3
10032991|.0F85 41010000 jnz 10032AD8
10032997|.6A 01 push 0x1
10032999|.8D4D D0 lea ecx, dword ptr
1003299C|.E8 BF720300 call 10069C60
100329A1|.8038 66 cmp byte ptr , 0x66 ;f
100329A4|.0F85 2E010000 jnz 10032AD8
100329AA|.6A 02 push 0x2
100329AC|.8D4D D0 lea ecx, dword ptr
100329AF|.E8 AC720300 call 10069C60
100329B4|.8038 38 cmp byte ptr , 0x38 ;8
100329B7|.0F85 1B010000 jnz 10032AD8
100329BD|.6A 03 push 0x3
100329BF|.8D4D D0 lea ecx, dword ptr
100329C2|.E8 99720300 call 10069C60
100329C7|.8038 62 cmp byte ptr , 0x62 ;b
100329CA|.0F85 08010000 jnz 10032AD8
100329D0|.6A 04 push 0x4
100329D2|.8D4D D0 lea ecx, dword ptr
100329D5|.E8 86720300 call 10069C60
100329DA|.8038 66 cmp byte ptr , 0x66 ;f
...............................
...............................
以下代码, 可知注册码第 27, 29, 31 位满足关系
1002EE13 |. 0FBE71 1C movsx esi, byte ptr
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
1002EE2B |> 8B4C24 14 mov ecx, dword ptr
1002EE2F |. 396C24 28 cmp dword ptr , ebp
1002EE33 |. 73 04 jnb short 1002EE39
1002EE35 |. 8D4C24 14 lea ecx, dword ptr
1002EE39 |> 57 push edi
1002EE3A |. 0FBE79 1E movsx edi, byte ptr
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
1002EE4F |. 396C24 2C cmp dword ptr , ebp
1002EE53 |. 73 04 jnb short 1002EE59
1002EE55 |. 8D4424 18 lea eax, dword ptr
1002EE59 |> 0FBE50 1A movsx edx, byte ptr
1002EE5D |. 8D8C37 87F8FF>lea ecx, dword ptr
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个字符串 d0x64 + 0x779 = 0x7DD -> ESI
第31个字符串 o0x6F-0x69= 0x6 -> EDI
第27个字符串 j0x6A
ESI + EDI - 0x779 = 0x6A
ifESI <= 0x7DD then
if ESI < 0x7DC then
= 0x1 失败
else
= 0成功
end if
else
if ESI < 0x7DF then
= 0x1失败
else
if ESI != 0x7DF then
= 0成功
else
if EDI < 0x3 then
= 0x1失败
else
= 0 成功
end if
end if
end if
end if
算法总结:
1. 注册码格式:windows-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(windows- 后面 32 位)
2. 32位注册码的第1,3,5,7,9,11,13,15,17,19,21,23,25位是固定值, 分别是22200ce06b66a
3. 32位注册码的第2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,30, 32位是:用户名字符串,经过翻转, 求出 MD5 值, 然后 取 前 16 位
4. 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 语言 {:1_907:})
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
--------------------------------------------------------------------------------
【版权声明】: 本文原创于吾爱破解论坛, 转载请注明作者并保持文章的完整, 谢谢!
附一张注册机截图:(帖子附件包含注册机,以及注册机的 PowerBASIC 源码 ){:1_912:}
(提示:杀软可能会误报, 不信者可以自己编译。)
注册机+源码:
用法:
见30楼:http://www.52pojie.cn/forum.php?mod=redirect&goto=findpost&ptid=566985&pid=14784663
本帖最后由 GCCG 于 2016-12-28 12:20 编辑
longavailable 发表于 2016-12-26 20:31
非常感谢大神的分享
有个非常弱的问题,生成的key怎么用?
是在调用程序(比如官方example)中加入:
重要提示:官方例子中的 example.xls 本身已经是第一行被锁定,你要测试就把那个EXCEL恢复正常, 然后再注册后的EXE读写它, 就不会出现试用信息了。如果注册码不对,即使你把之前的EXCEL恢复正常, 也会出现试用信息。另外一个要注意的是:用户名区分大小写。
先用下图两个函数创建一个实例句柄
xls 格式的 Excel 用 xlCreateBook()
xlsx 格式的 Excel 用 xlCreateXMLBook()
例如下面一段C代码(官方例子,我加了“注册的函数 xlBookSetKey”):
#include <stdio.h>
#include <conio.h>
#include "libxl.h"
int main()
{
BookHandle book = xlCreateBook();
if(book)
{
xlBookSetKey(book,L"GCCG",L"windows-282123090cc0e6036db16b60a1o3p0h9"); //注册
if(xlBookLoad(book, L"example.xls"))
{
SheetHandle sheet = xlBookGetSheet(book, 0);
if(sheet)
{
double d = xlSheetReadNum(sheet, 3, 1, 0);
xlSheetWriteNum(sheet, 3, 1, d * 2, 0);
xlSheetWriteStr(sheet, 4, 1, L"new string", 0);
}
if(xlBookSave(book, L"example.xls")) printf("\nFile example.xls has been modified.\n");
}
xlBookRelease(book);
}
printf("\nPress any key to exit...");
_getch();
return 0;
}
然后用 book 这个实例句柄变量,读写Excel 即可。 调用LibXL 库函数之前,一定要先注册,创建了实例句柄之后,就要调用注册函数, 然后再用其他函数。
@longavailable
本帖最后由 GCCG 于 2016-12-28 12:09 编辑
longavailable 发表于 2016-12-27 22:01
http://pan.baidu.com/s/1kVe7c8Z
注册名 借用了大神的名号,注册码也是您对应的那个
我找到原因了,是 fortran 的 模块声明文件不对, 导致转了两次 unicode, 出错了。
解决方案,需要以下步骤:
1.删掉 libxl.f90 文件中的 xlBookSetKey 函数
subroutine xlBookSetKey(handle,name,key)
type(BookHandle) handle
character(*) name,key
integer i,ii,iii
integer(2),allocatable,dimension(:) :: buff1,buff2
i=len(name)
ii=len(key)
allocate(buff1(i+1),buff2(ii+1))
iii = MBConvertMBToUnicode(name,buff1)
iii = MBConvertMBToUnicode(key,buff2)
buff1(i+1)=0
buff2(ii+1)=0
call xlBookSetKeyPrivate(handle,buff1,buff2)
deallocate(buff1,buff2)
end subroutine
2. 修改 xlBookSetKeyPrivate 函数声明 为以下形式。
subroutine xlBookSetKeyPrivate(handle,name,key)
!DEC$ATTRIBUTES C, DLLIMPORT, ALIAS:'_xlBookSetKeyA' :: xlBookSetKeyPrivate
!DEC$ ATTRIBUTES REFERENCE, ALLOW_NULL :: name
!DEC$ ATTRIBUTES REFERENCE, ALLOW_NULL :: key
import
type(BookHandle) handle
character(*) name, key
end subroutine
3. 直接调用xlBookSetKeyPrivate
call xlBookSetKeyPrivate(book,"GCCG","windows-282123090cc0e6036db16b60a1o3p0h9")
附一个修改好的 libxl.f90 文件, 替换官方文档例子中的libxl.f90 文件即可
libxl.f90 文件:http://pan.baidu.com/s/1dEEuTNV密码:lyan
菜鸡想学破解 发表于 2019-10-25 22:40
请问大佬那个16位MD5是怎么逆出来是固定值的
.MD5 不可逆。在分析算法之前,我找到一个外国佬的正版注册码,通过OD分析程序,我知道了一部分注册码字符是固定值,就这样。 本帖最后由 Fris 于 2019-4-29 16:07 编辑
注册算法还没改,经测试试用于最新版本3.8.5
注册成功!!!!!太厉害了!!! cqr2287 发表于 2016-12-23 18:27
整体逻辑十分清晰。然而有些没有分析到。比如前缀Windows。而有些分析过程缺少,例如给出md5值如何倒推几位 ...
确实没怎么注释,主要是懒得写了, 我分析代码时的 “记录”比较详细,但是比较杂乱,也不想整理了。有成果了就好了,我已经总结了算法,并给出代码的关键地方,留点悬念给有心的人自己分析一遍能学到蒙多东西,说的再多,不如自己亲自练习。 分析的很到位呀,支持了 用不上 ,帮顶{:1_921:} 一般也常用 PowerBasic + FireFly :) 分析得很到位,谢谢 谢谢分享,看看源码! 学习学习 感谢 楼主分享 牛人正研究算法注册机研究一下! 正好需要。谢谢了哦