说明

这部分内容是《加密与解密》第二版的,没有放进第三版。书上Flexlm这部分比较基础,放出来与大家分享。由于与电子工业出版社还有版权合同问题,因此,这部分内容仅对论坛注册会员可见,不想大面积传播,还请谅解。
其中,主要部分“FlexGen工具用法”、“利用FlexLm SDK解密”由Fisheep参与编写,在此表示感谢!


13.7 Flexlm保护
完稿时间:2003.1
 
许可证管理(License manager)是一种授权加密保护方式,属于这类的保护的有Flexlm、ElanLM、SentinelLm等。其中以Flexlm最为著名,很多大型专业软件都采用它进行加密。但是其安全性是没有保证的,不是因为它技术不好,而是因为人们对它的研究最深入和透彻。
许可证的原理通常是把要保护的软件同计算机固有的唯一的硬件特征联系起来,例如工作站的主机号(Hostid)、以太网卡得MAC地址等。
 
13.7.1 License文件格式
Flexlm加密的程序在使用时,一般都会有一个License文件,在此有必要先了解一下License文件的结构。License文件由注释行、Server行、Demon行和Feature行构成,另外有一个续行符“\”用来表示一行的继续。
 

 

 
13.7.2 设置环境变量
一般来说,License Manager需要一个环境变量才能找到License文件。有的软件不需要设置环境变量,只需要将license文件放置到它的某个目录下面就可以了,如ActiveVHDL3.3,只要将license.dat放在它的dat目录下。
通常环境变量名称是LM_LICENSE_FILE,大小写无关,Windows 9x下的设置方法是在Autoexec.bat中加入一行: 
 

 
如果有多个License文件,可以用分号隔开,如:
SET LM_LICENSE_FILE=c:\flexlm\license1.dat;d:\flexlm\license2.dat 
应用软件的License Manager自动遍历环境变量指定的所有License文件,查找它所需要的Feature。不同License与文件路径的先后次序无关。 
除了LM_LICENSE_FILE这个环境变量名称外,有些软件还使用自己特定的环境变量名称。安装软件的时候,安装程序一般会自动添加这些环境变量,如Specctra8.0使用CDS_LIC_FILE环境变量名称:
SET CDS_LIC_FILE=c:\cdsLic\license.dat 
MentorGraphicsRenoir以及ModelSimEE如果安装的时候选择Mentor Graphics License Manager,软件使用MGLS_LICENSE_FILE环境变量:
SET MGLS_LICENSE_FILE=c:\flexlm\license.dat 
如果License是由Server提供的,那么环境变量的路径应该给出Server的名称以及端口,而非License文件的路径以及名称,如:
 

 
Windows NT/2000/XP下设置环境变量需要在控制面板里面添加,格式相同。
 
13.7.3 Flexlm Server
Flexlm这个名字是Flexible License Manager的简写,Flexlm Server方式的License应该如下设置:
License.dat文件放在主机中,主机运行Server,提供License服务。
客户端的环境变量设置为:
SET LM_LICENSE_FILE=[TCP/IP端口]:[主机名称] 
其中端口和主机名称应该和主机的License文件中的Server Line一致。启动Server的方式有两种,一是DOS命令行方式,二是Windows的窗口方式。
 
1.DOS方式 
在c:\flexlm目录下可以找到一个Lmgrd.exe文件License Manager Daemon,这个 Daemon是Flexlm Server。License.dat文件需要和Daemon文件放在同一个目录下,启动License server,可在 c:\flexlm 目录下执行:
c:\flexlm\lmgrd -app -c 
关闭License server,可在 c:\flexlm目录下执行: 
c:\flexlm\lmutil lmdown 
lmutil.exe是License Manager Utility。
 
2.Windows的窗口方式 
安装某些软件的时候,会有一个安装License Server的选项,选中它安装完成后,会在控制面板中发现一个Flexlm License Manager的小工具。在Flexlm License Manager Setup的TAB里可以选择Daemon、License文件,以及输出的日志文件。Control的TAB中可以启动/关闭Server以及查看Server状态,另外几个TAB还提供一些其他辅助功能。
Windows下的这个Flexlm License Manager要比命令行方式的方便一些,至少它可以浏览,选取Daemon/License文件,不过有时候发现控制面板里面找不到。事实上它是一个控制面板的扩展应用程序,如在PowerPCB目录下可以找到Flexlm.cpl这个文件,用鼠标双击它就运行上面提到的那个管理窗口。
 
13.7.4 FlexGen工具用法
 
1.FlexGen.ini配置
FlexGen是通用基于Flexlm的License生成器,它需要输入一些不同的信息。这些信息保存在FlexGen.Ini文件中,该文件是可以编辑和更新的,这样就可以添加信息以生成新软件的License。
FlexGen.ini包括生成License文件需要的不同信息。对于每个软件的信息,它都拥有表13-2的格式。
 

 
已经准备好INI文件,可以用它来生成License。运行FlexGen,按“Generate”试一下,如果得到一个错误信息,就表示有一些信息输错了;如信息正确,再按“Check”检测License是否合法,如果得到肯定的回答,就表示INI中输入的信息都是正确的。这样,就可自制INI中的信息来为程序生成License文件。按“Automatic License Generation”生成的INI文件中放入所有Feature的License,生成License后按“Save”按钮将其保存。
 
2.编辑过的DLL使用方法
(1)先用FileMon等工具检测软件的运行,观察软件使用的lmgr32XX.dll文件名和所在的目录。
(2)退出应用软件的运行,用FlexGen提供的LMGRS目录下的相应DLL替换原先的DLL(最好先将原先的文件备份)。
(3)运行软件,将有一个对话框弹出,记录其标题(是Flexlm的Vendor Name)和显示的数值,有80个字符(称为Magic Number是经过处理的Flexlm的Key)。
(4)运行FlexGen程序,选择“Make INI from Magic Number”选项运行,填入上面得到的两个信息,然后按“GENERATE”,再按“SAVE”按钮,如果一切都正确,将得到一个TEMPLATE.INI。填写它的所有信息,添加到当前FLEXGEN.INI文件后面,就可使用FlexGen来生成该软件的License文件了。
(5)退出应用软件的运行,用FlexGen提供的LMFEAT目录下的相应Lmgr32XX.dll替换原先的DLL,然后运行应用程序,出现一个对话框,其文本为FEATURE的名称,其标题为该FEATURE的版本号(Version)。记录下这两项内容,添加到先前生成的Template.ini文件中。在应用程序中多运行一些功能,可能发现更多的FEATURE,都将它们记录下来,然后添加到先前的Template.ini文件中。
 
3.硬盘序列号修改
上面的方法固然很不错,但是有的时候会发现个别Flexlm加密的软件并不调用lmgr32XX.dll。
Flexlm是根据硬盘序列号生成机器号的,如果有了一个机器的序列号,这时解密者修改硬盘的序列号,让它们生成一样的机器号,那么那个密码就可以在多台机器上用了。以前在DOS下的时候可以用NU8中的diskedit来编辑硬盘的序列号,这样对那些不懂磁盘结构的人来说很不方便。用FlexGen配套的DISKSER.EXE就可轻松解决这个问题,打开这个软件,会出现图13.7的界面。
 

 
输入8位数字后它会自动提示:“Serial Number Changed”,这样硬盘序列号就改变了。
FlexGen配套的DGET.EXE是查看硬盘序列号的软件。
对软件作者加密时的建议:计算本机序列号的时候尽量使用稳定的不容易变化的东西作为计算的依据,比如BIOS中的厂家的名字,但不要用BIOS的版本号因为这个东西要经常变,而主板厂家的名字却很少有人去刻意改变。硬盘序列号是一个很不错的依据,但是有了DISKSER.EXE就不怎么安全了。可把这类可以依靠的判断条件选择几个,计算出机器号,切不可只用一个来计算,不然很容易被修改!
 
13.7.5 利用FlexLm SDK解密
Flexlm的License文件可以用函数f如下表示: 
f( 主机号,软件名,版本,期限,许可证个数,加密参数)= 加密串 
这个函数还和Flexlm的版本有关系,实际上只要得到相应版本的 Flexlm SDK,就可以执行此函数。Flexlm SDK是公开的(以前如此),唯一保密的是f函数中的“加密参数”由Flexlm公司通过保密的途径提供给其客户,这些参数由五个Vendor Key(客户码)和两个Encryption Seed(加密种子)组成,只要知道了这七个参数,就可以用相应版本的SDK构造出完整的License文件。该方法是一种最彻底的、最完美的办法,但也是flexlm 重点防范的对象,他们采用了很多技术隐藏上述信息。
根据SDK的描述,如下两个函数最重要:
 lc_init( )使用基本的Vendor Key等信息进行Flexlm的初始化,通过它可以得到基本信息;
 lc_checkout( )检查是否可以使用指定Feature的功能,通过它可以得到程序的所有Feature名称。
为了讲述方便,在此以FlexLm加密的XprismPro 1.0程序为例。
 
1.SDK开发包
首先安装Flexlm开发包,这里所要关心的主要文件是: 
 lm_code.h在这要设置所要解密程序的加密信息,有Vendor名称、Vender Key和Seed等,这些内容是重点;
 lm_client.h包含Flexlm用来保护软件的函数原型和出错码的定义;
 GenLic32.exe该程序用来检查在lm_code.h中设置的Key等信息生成Flexlm的License文件。
 
2.使用lc_init( )得到信息
使用lc_init( )来找Vendor Name、Vendor Key 1 - Vendor Key 4。载入目标EXE文件,然后在lc_init调用处设置一个断点,运行程序,直到它停止。
在lm_client.h中,可以看到lc_init的原型如下: 
 
代码:
lm_extern API_ENTRY lc_init lm_args(
                   (LM_HANDLE_PTR job,  
                   LM_CHAR_PTR vendor_id,  
                   VENDORCODE_PTR vendor_key, 
                   LM_HANDLE_PTR_PTR job_id)
          );
 
在最前面的job将是NULL,job_id是一个指向将被填充的job结构的指针,这里所关心的是Vendor_id和vendor_key,其中Vendor_id是一个文本字符串。
再看看lm_client.h中的结构:
 
 
代码:
typedef struct vendorcode5 { 
                            short type;             /* 结构类型*/ 
                            unsigned long data[2];  /* 加密的Seed信息 */ 
                            unsigned long keys[4];  /* Vendor Keys 1 到 4 */
                            short flexlm_version; 
                            short flexlm_revision; 
                            char flexlm_patch[2]; 
                            char behavior_ver[LM_MAX_BEH_VER + 1]; 
                          } VENDORCODE5,  FAR *VENDORCODE_PTR; 
#define LM_MAX_BEH_VER  "06.0"
 
在上面的结构中:
data[0] = Seed1 XOR VendorKey5
data[1] = Seed2 XOR VendorKey5
keys[0..3] = vendor keys 1 到 4 
behavior_ver[] = 包含Flexlm版本的字符串(在这儿是"06.0") 
在调用lc_init前,上面的的内容将被压到栈中。
程序中断后将看到如下代码:
 

 
这里获得了vendor ID字符串(这里是“VENDOR”),用W32Dasm反汇编主程序,再看00528DC8处的内容,可以看到code结构的内容:
 
代码:
[00528DC8] - 00000004  ....
[00528DCC] - ab5e32e5  .?^    ; seed1 XOR key5
[00528DD0] - 7bc6313d  =1.{   ; seed2 XOR key5 
[00528DD4] - fc62965d  ].l.   ; key 1 
[00528DD8] - 853df75c  \7=.   ; key 2
[00528DDC] - 2f324f23  #O:.   ; key 3 
[00528DE0] - 1133e43b  ;.3.   ; key 4 
[00528DE4] - 00000006  ....   ; version and revision
[00528DE8] - 36300069  i.06   ; patch和behaviour_ver 
[00528DEC] - 0000302e  .0..
 
这里面获得了4个Keys和2个加密的Seed值。
 
3.找Feature名称
需要Feature名称,得到一个可以使用的License文件。清除在lc_init( )处设的断点后,然后用lc_checkout设置断点。
在lm_client.h中可以看到如下的函数原形:
 
代码:
lm_extern API_ENTRY lc_checkout lm_args((LM_HANDLE_PTR job,  
                                const LM_CHAR_PTR feature, 
                                const LM_CHAR_PTR  version,  
                                int nlic,  
                                int flag,  
                                const VENDORCODE_PTR key,  
                                int dup));
 
在程序中看起来:
 
代码:
push  00004000 
push  0528DC8                 ; code结构
push  00000000 
lea   eax, dword ptr [esp+10] 
push  00000001 
push  eax                     ; 版本
push  ecx                     ; feature名称 
push  edx 
Call  LMGR326A.lc_checkout 
 
这里获得一个Feature名称,反汇编后就可在该Feature名称的周围,看到一些程序可能用到的其他Feature名称。
 
4.制作license.dat文件
编辑lm_code.h文件,在其中填入所找到的加了密的seeds值和vendor keys 1到4,先设置key 5为0。
运行genlic32.exe。如果跳出一个错误框,则表示lm_code.h中信息有错;输入在前面找到的feature名称,点击“permanent”和“run anywhere”,这样就不需要一个server daemon在运行。点击“Make License”,填好文件名license.dat,然后点击“Make License”,查看License文件,应该看起来像这样:
 

 
5.寻找Vendor Key 5
FlexLm提供基于Vendor名称的5个Key,因此它必须以某种方式进行校验。如果在lm_code.h里的Key1到Key4或Vendor名称有错,那么genlic32.exe就不会正常工作。
再次装载目标文件,设置lc_checkout断点,载入daemon DLL,开始单步执行,单步跟踪到里面。程序检查licence.dat文件:
l_validversion( ), l_compare_version( ) 检查license文件中的版本;
l_date( ), l_extract_date( ) 在加密的Key中展开日期(无时间限制的日期被转化为1-jan-0);
l_start_ok( ) 运行日期是正确的;
l_host( ) 运行的机器是否正确;
然后进入一些有很多XOR运算的函数中,这里是用XOR运算加密Seed的地方,可以看到Key1、Key 2、Key 3、Key 4以及Vendor名称,然后碰到如下代码:
 
代码:
call 10021160                    ; 将key 5放到ebx 
add  esp, 0000000C 
mov  eax, dword ptr [edi+04]     ; 从license.dat取seed1 
xor  eax, ebx                    ; seed1 XOR key5   
lea  ecx, dword ptr [ebp-58]    
push ecx 
mov  dword ptr [ebp-54], eax     ; 保存seed1 
mov  eax, dword ptr [edi+08]     ; 从license.dat取Seed2
mov  edi, dword ptr [ebp+0C] 
xor  eax, ebx                    ; seed2 XOR key5 
mov  dword ptr [ebp-50], eax     ; 保存seed2 
lea  eax, dword ptr [edi+54] 
push eax 
push esi 
call LMGR326A.l_extract_date
 
就这样,得到key5和经过XOR的seeds1和Seed2。
 
 
6.制作最终的license.dat
在lm_code.h中替换掉seeds和key5的值,运行genlic32.exe制作完全的licence文件。
上面这一篇教程是针对使用Flexlm的DLL加密的程序。其函数名称可以从DLL的输出表中得到,给予解密一个很大的提示。但是对于那些没有使用DLL的加密程序,该如何发现这些关键函数的位置呢?
这时候可以使用IDA和SoftICE来进行解密。首先用IDA的辅助工具Flair(该工具仅IDA注册用户有)建立Flexlm SDK的Sig文件,然后使用IDA对目标程序进行反汇编,再将建立好的Flexlm的Sig作用于已经反汇编的目标文件,这样就能直观地看到Flex关键函数所在的地址。
在SoftICE中载入该目标程序,然后在IDA中得出的关键函数地址处设置断点,运行就可以在所期望的地方停下来,使用D命令得到需要的信息。
因为已经存在Flexlm的辅助解密工具,可以根据部分信息Key1~Key4、Vendor名称得到另外的信息Key5和Seeds。这样,解密工作就可简化为在_lc_init处设置断点,获得部分加密信息,使用Flexlm辅助工具得到另外的信息Key5等,然后在_lc_checkout处设置断点获得加密程序的Feature名称,最后使用所有得到的信息,生成没有限制的license文件。
 
看雪软件安全
http://www.pediy.com
本文结稿时间:2003.1