GCCG 发表于 2016-12-22 21:33

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-27 12:23

本帖最后由 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 10:46

本帖最后由 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


GCCG 发表于 2019-11-15 12:46

菜鸡想学破解 发表于 2019-10-25 22:40
请问大佬那个16位MD5是怎么逆出来是固定值的

.MD5 不可逆。在分析算法之前,我找到一个外国佬的正版注册码,通过OD分析程序,我知道了一部分注册码字符是固定值,就这样。

Fris 发表于 2018-11-23 13:31

本帖最后由 Fris 于 2019-4-29 16:07 编辑

注册算法还没改,经测试试用于最新版本3.8.5

xwei9277 发表于 2016-12-27 12:58

注册成功!!!!!太厉害了!!!

GCCG 发表于 2016-12-23 19:06

cqr2287 发表于 2016-12-23 18:27
整体逻辑十分清晰。然而有些没有分析到。比如前缀Windows。而有些分析过程缺少,例如给出md5值如何倒推几位 ...

确实没怎么注释,主要是懒得写了, 我分析代码时的 “记录”比较详细,但是比较杂乱,也不想整理了。有成果了就好了,我已经总结了算法,并给出代码的关键地方,留点悬念给有心的人自己分析一遍能学到蒙多东西,说的再多,不如自己亲自练习。

zhangbaida 发表于 2016-12-22 21:40

分析的很到位呀,支持了

wuaipojie 发表于 2016-12-22 21:47

用不上 ,帮顶{:1_921:}

Sound 发表于 2016-12-22 22:45

一般也常用 PowerBasic + FireFly   :)

Tomatoman 发表于 2016-12-22 22:59

分析得很到位,谢谢

zzcn2008 发表于 2016-12-22 23:07

谢谢分享,看看源码!

palmer680 发表于 2016-12-23 07:30

学习学习

ysz 发表于 2016-12-23 08:19

感谢 楼主分享

allcam 发表于 2016-12-23 08:38

牛人正研究算法注册机研究一下!

神的子民 发表于 2016-12-23 16:18

正好需要。谢谢了哦
页: [1] 2 3 4 5 6 7 8 9
查看完整版本: LibXL 算法分析(附注册机)