2009年6月16日星期二

翻译FLEXlm9.2的破解教学五

标 题: 翻译FLEXlm9.2的破解教学五
作 者: newsearch
时 间: 2004-12-10,01:03
链 接: http://bbs.pediy.com/showthread.php?t=8221

翻译第五篇 On Software Reverse Engineering - 5
            Reverse Engineering, FLEXlm, IMSL
模糊方法

    下面,我们将研究在目标程序中种子和密钥是怎样模糊的。我们的武器是数据流分析,也就是说通过调试cmath.exe,从初始化到l_string_kry()全程追踪vendor和job结构[10]。

VENDORCODE after calling l_n36_buf():
[004AEA20] - 00000004 ....
[004AEA24] - aa3342a8 .B3.
[004AEA28] - 8d112f1f ./..
[004AEA2C] - 74df9bc4 ...t 模糊了的 VENDOR_KEY1
[004AEA30] - b19bfb18 .... 模糊了的 VENDOR_KEY2
[004AEA34] - 94011f00 .... 模糊了的 VENDOR_KEY3
[004AEA38] - 054a2ed9 ..J. 模糊了的 VENDOR_KEY4
[004AEA3C] - 00020009 ....
[004AEA40] - 39300020 .09
[004AEA44] - 0000302e .0..
 
job after calling lc_init():
[00887630] - 00000066 f...
[00887634] - 00000000 ....
[00887638] - 00000000 ....
[0088763C] - 00000000 ....
[00887640] - 00000000 ....
 
// 上面是lc_new_job()之后的结构, 然后它们被传递到lc_checkout() ® l_checkout() ® lm_start_real() ® l_good_lic_key()

VENDORCODE 在l_xorname()调用之后:
[0012EE20] - 00000004 ....
[0012EE24] - aa3342a8 .B3.
[0012EE28] - 8d112f1f ./..
[0012EE2C] - 7c2adb6a j.*| 真的 VENDOR_KEY1
[0012EE30] - b927f5a9 ..'. 真的 VENDOR_KEY2
[0012EE34] - 9cf311f8 .... 真的 VENDOR_KEY3
[0012EE38] - 0dbf7621 !v.. 真的 VENDOR_KEY4
[0012EE3C] - 00020009 ....
[0012EE40] - 39300020 .09
[0012EE44] - 0000302e .0..
[0012EE48] - 00000000 ....
 
VENDORCODE after calling l_n36_buff():
[0012EE20] - 00000004 .... 整型;
[0012EE24] - 52ed15b8 ...R 52xxxxb8, xxxx 是随机的,在每次运行时是不同的
[0012EE28] - 75cf780f .x.u 75yyyy0f, yyyy 是随机的,在每次运行时是不同的
[0012EE2C] - 7c2adb6a j.*| VENDOR_KEY1
[0012EE30] - b927f5a9 ..'. VENDOR_KEY2
[0012EE34] - 9cf311f8 .... VENDOR_KEY3
[0012EE38] - 0dbf7621 !v.. VENDOR_KEY4
[0012EE3C] - 00020009 .... FLEXlm 版本(这里是 9.2)
[0012EE40] - 39300020 .09
[0012EE44] - 0000302e .0..
[0012EE48] - 00000000 ....
 
job after calling l_n36_buff():
[00887630] - 00000066 f... 整型;
[00887634] - 0089008e .... char *mem_ptr2;
[00887638] - a06aa84e N.j. unsigned char mem_ptr2_bytes[12];
[0088763C] - 00c3a047 G...
[00887640] - 00660000 ..f.
[00887644] - 00000000 ....
[00887648] - 00000000 ....
[0088764C] - 00000000 ....
[00887650] - 00000000 ....
[00887654] - 54414d43 CMAT
[00887658] - 00000048 H...
 
// 上面是l_good_lic_key() ® l_sg()之后的结构, 然后它们被传递到 l_good_lic_key() ® l_crypt_private() ® real_crypt() ® l_string_key()
 VENDORCODE和job在3个函数中被改变:l_n36_buf(),l_xorname()和l_n36_buff() (在lc_init()中的job初始化并不是很感兴趣的,因为它仅填充O)。在l_good_lic_key()内调用后面的两个:

l_good_lic_key(LM_HANDLE* job, CONFIG* conf, VENDORCODE* key)
{
... ...
memcpy(&vc, key, sizeof(vc));
if (!(job->flags & LM_FLAG_CLEAR_VKEYS))
l_xorname(job->vendor, &vc);
l_sg(job, job->vendor, &vc); /* l_sg() 将调用 l_n36_buff() */
... ...
code = l_crypt_private(job, conf, sdate, &vc);
... ...
}

    对于4个Vendor密钥,很明显它们是初始化时在l_n36_buf()中被模糊的,在l_xorname()中进行模糊逆向,l_xorname()未做别的仅进行了一些与或操作。我们都知道与或的特征:,因此进行两次相同的与或操作将相互取消。该特性将使编码和译码的与或变得美妙。如果我们的猜测是对的话,那么l_n36_buf()将采用与l_good_lic_key()相同的方式调用l_xorname()。因为 l_n36_buf()驻留于lm_new.c, 它由lmnewgen.exe产生, 因此直接检查lmnewgen.c更好。

翻译FLEXlm9.2的破解教学四

标 题: 翻译FLEXlm9.2的破解教学四
作 者: newsearch
时 间: 2004-12-08,15:58
链 接: http://bbs.pediy.com/showthread.php?t=8148

翻译第四篇 On Software Reverse Engineering - 4
            Reverse Engineering, FLEXlm, IMSL
    因此,我们有了两个处理:在cmath.exe中进行检查和在lmcrypt.exe或makekey.exe.中产生密钥(keygen)。它们都可以产生相同的正确的许可证代码,但这两个处理不是完全相同的。我们已经在前面的章节对第一个处理进行了一定深度的分析,现在按顺序列出重要的调用链接。

1.lc_new_job() ® l_n36_buf() ® l_x77_buf()
2.lc_new_job() ® lc_init() ® l_init() ® l_sg() ® l_key() ® l_zinit()
3.lc_set_attr() ® l_set_attr() ® l_set_license_path() ® l_flush_config() ® l_init_file() ®
 l_allfeat() ® l_parse_feature_line() ® oldkey() ® l_crypt_private() ® real_crypt() ®l_string_key()

4.lc_checkout() ® l_checkout() ® lm_start_real() ® l_good_lic_key() ® l_xorname()
5.lc_checkout() ® l_checkout() ® lm_start_real() ® l_good_lic_key() ® l_sg() ® l_n36_buff()
6.lc_checkout() ® l_checkout() ® lm_start_real() ® l_good_lic_key() ® l_crypt_private() ®
real_crypt() ® l_string_key()

    一个很感兴趣的问题是:为什么l_sg()在第二个链接调用l_key()而在第五个链接调用l_n36_buff()?检查代码摘录,我们发现答案在于LM_OPTFLAG_CUSTOM_KEY5和 L_UNIQ_KEY5_FUNC。后者由l_x77_buf()(也就是 L_SET_KEY5_FUNC) 在第一个链接中设置,因此在两个调用中,l_n36_buff将非空。那么原因就是 LM_OPT_FLAG_CUSTOM_KEY5了:它在调用lc_init()后进行开关转换,这就是为什么l_key() 在第二个链接中被调用。有趣的是,在现在的FLEXLM版本中,l_key()是一个无用的子程序(它在引入l_n36_buff()之前用于早期的版本)。此外,它完全没有必要在初始化阶段调用译码加密种子的l_sg();那本应该在检查时进行的。 

lm_njob.c:
int lc_new_job(oldjob, l_new_job, vcode, newjobp)
{
... ...
(*L_NEW_JOB)(vendor_name, vcode, 0, 0, 0, &sign_level);
(*L_NEW_JOB)(0, 0, 0, 0, 0, 0);
if (!(ret = lc_init(oldjob, vendor_name, vcode, newjobp)))
{
(*newjobp)->options->flags |= LM_OPTFLAG_CUSTOM_KEY5;
... ...
}
return ret;
}
 
lm_ckout.c:
void l_sg(LM_HANDLE* job, char* vendor_id, VENDORCODE* key)
{
... ...
unsigned long x = 0x6f7330b8; /* v8.x */
if (( job->options->flags & LM_OPTFLAG_CUSTOM_KEY5) && L_UNIQ_KEY5_FUNC)
{
(*L_UNIQ_KEY5_FUNC)(job, vendor_id, key);
return;
}
l_key(vendor_id, &(key->keys[0]), keys, 4); /* Pre v6.1 style */
... ... /* 在VKEY5()中进行相同的与或操作(xor) */
}
 
lm_init.c:
void (*L_UNIQ_KEY5_FUNC)() = 0;
void L_SET_KEY5_FUNC( void (*f)())
{
if (!L_UNIQ_KEY5_FUNC) L_UNIQ_KEY5_FUNC = f;
}

   在检查处理的同时,我们也在密钥产生(keygen)处理中调用链接。我们将使用lmcrypt.exe用于分析,因为它比makekey.exe来得更直接 (它们都执行相同的工作)。
1.lmcrypt.c!main() ® lc_init() ® l_init() ® l_sg() ® l_key() ® l_zinit()
2.lmcrypt.c!main() ® dofilecrypt() ® dofpcrypt() ® lm_crstr.c!lc_cryptstr() ® parsefeaturelist() 
 ® l_parse_feature_line() ® oldkey() ® l_crypt_private() ® real_crypt() ® l_string_key()
3.lmcrypt.c!main() ® dofilecrypt() ® dofpcrypt() ® lm_crstr.c!lc_cryptstr() ® cryptfeaturelist() 
 ® docryptfeat() ® lc_crypt() = l_crypt_private() ® real_crypt() ® l_string_key()

    注意:对于Vendor和job的初始化,cmath.exe将调用lc_new_job(),(该函数)轮流调用lc_init();但是 lmcrypt.exe将直接调用lc_init(),因为vendor密钥、种子和名称都已经包含于lmcrypt.exe中了(一起通过宏放到 vendor结构中),因此仅需要初始化工作。在这两个处理中,有对l_string_key()的两个调用;在这两种情况下,第一个返回不重要的 oldkey()号为21D5B6E8572E,仅第二个调用其实质。这两个处理在调用l_string_key()上仅有细微的差别。基本上,对于 checksum(总和检查)比较,checkout(检查)需要提供用户许可证密钥,但是keygen则不需要。但是,对于计算实际的杂乱信息上则是相同的。

int idx = (*job->vendor) % XOR_SEEDS_ARRAY_SIZ; /* idx = V % 20 = 86 % 20 = 6 */
... ...
memset(y, 0, L_STRKEY_BLOCKSIZE); /* L_STRKEY_BLOCKSIZE = 8, in lmachdep.h */
length = (inputlen) / L_STRKEY_BLOCKSIZE;
XOR_SEEDS_INIT_ARRAY(xor_arr) /*在l_strkey.h中定义的替换表 */
... ... /* memcpy() 从输入到newinput和其他充填(值)*/
p = newinput;
for (i = 0; i < length; i++)
{
XOR(p, y, y);/*在l_strkey.h中定义的与或(XOR)和L_MOVELONG */
if (i == 0)
{
if (!user_crypt_filter && !user_crypt_filter_gen
&& (job->flags & LM_FLAG_MAKE_OLD_KEY))
{
q = y; /*在l_privat.h中定义的SEEDS_XOR = mem_ptr2_bytes */
L_MOVELONG(code->data[0]
^((long)(job->SEEDS_XOR[xor_arr[idx][0]])<<0)
^((long)(job->SEEDS_XOR[xor_arr[idx][1]])<<8) 
^((long)(job->SEEDS_XOR[xor_arr[idx][2]])<<16)
^((long)(job->SEEDS_XOR[xor_arr[idx][3]])<<24), q)
L_MOVELONG(code->data[1]
^((long)(job->SEEDS_XOR[xor_arr[idx][0]])<<0)
^((long)(job->SEEDS_XOR[xor_arr[idx][1]])<<8) 
^((long)(job->SEEDS_XOR[xor_arr[idx][2]])<<16)
^((long)(job->SEEDS_XOR[xor_arr[idx][3]])<<24), q)
}
... ...
}
if (!(job->flags & LM_FLAG_MAKE_OLD_KEY) && !demo)
our_encrypt2(y);
else
our_encrypt(y); /* our_encrypt()并不涉及代码或工作*/
p += L_STRKEY_BLOCKSIZE;
}
if (len == L_SECLEN_SHORT) /* 在l_privat.h中,L_SECLEN_SHORT = 0x66D8B337 */
{
... ...
y[6] = y[7] = 0;
}
 
   因为它们共享了相同的代码,因此为了计算相同的杂乱信息,传入的参数必须相同。实际的追踪结果为:

cmath.exe:
job = 00887630, input = 0012e170, inputlen = 0x16, code = 0012ee20, len = 66d8b337, license_key = 6d5c...
[00887630] - 00000066 f... 整型;
[00887634] - 0089008e .... char *mem_ptr2;
[00887638] - a06aa84e N.j. unsigned char mem_ptr2_bytes[12]; (12是视进制)
[0088763C] - 00c3a047 G...
[00887640] - 00660000 ..f.
[00887644] - 00000000 ....
[00887648] - 00000000 ....
[0088764C] - 00000000 ....
[00887650] - 00000000 ....
[00887654] - 54414d43 CMAT
[00887658] - 00000048 H...
 
[0012E170] - ab370fd2 ..7. 输入串用于杂乱信息,取决于FEATURE行信息
[0012E174] - 414d4300 .CMA
[0012E178] - 88054854 TH..
[0012E17C] - 6a000113 ...j
[0012E180] - c5876e61 an..
[0012E184] - 000073d0 .s.. 总长度= 0x16 = 22,在73结束
[0012E188] - 00000000 ....
 
[0012EE20] - 00000004 .... 整型;
[0012EE24] - 52ed15b8 ...R 52xxxxb8, xxxx 是随机的,在每次运行是不同的
[0012EE28] - 75cf780f .x.u 75yyyy0f, yyyy 是随机的,在每次运行是不同的
[0012EE2C] - 7c2adb6a j.*| VENDOR_KEY1
[0012EE30] - b927f5a9 ..'. VENDOR_KEY2
[0012EE34] - 9cf311f8 .... VENDOR_KEY3
[0012EE38] - 0dbf7621 !v.. VENDOR_KEY4
[0012EE3C] - 00020009 .... FLEXlm 版本(这里是 9.2)
[0012EE40] - 39300020 .09
[0012EE44] - 0000302e .0..
[0012EE48] - 00000000 ....
 
lmcrypt.exe:
job = 008C49E8, input = 0012D8D4, inputlen = 0x16, code = 004D7B48, len = 66D8B337
0x008C49E8 66 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f...............
0x008C49F8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x008C4A08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x008C4A18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
 
0x0012D8D4 d2 0f 37 ab 00 43 4d 41 54 48 05 88 13 01 00 6a &Ograve;.7&laquo;.CMATH.....j
0x0012D8E4 61 6e 87 c5 d0 73 00 00 00 00 00 00 00 00 00 00 an.&Aring;&ETH;s..........
0x0012D8F4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0012D904 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
 
0x004D7B48 04 00 00 00 b8 39 c1 52 0f 54 e3 75 6a db 2a 7c ....&cedil;9&Aacute;R.T&atilde;uj&Ucirc;*|
0x004D7B58 a9 f5 27 b9 f8 11 f3 9c 21 76 bf 0d 09 00 02 00 &copy;&otilde;'&sup1;&oslash;.ó.!v&iquest;.....
0x004D7B68 20 00 30 38 2e 30 00 00 c3 80 f4 83 2c c0 1c 77 .08.0..&Atilde;.&ocirc;.,&Agrave;.w
0x004D7B78 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................

    注意:我们在这里处理诸如6D5C01FD71C9的非-CRO的短密钥,它由12个ASCII字符组成表示6个十六进制位,这也就是为什么我们在上面的代码中看到"y[6] = y[7] = 0;"。除了VENDORCODE和job结构之外,所有其它参数是相等的[9],且在这两个结构中并非所有的组成部分都是重要的。很容易勾勒出在杂乱化(hashing?)输入串中所参与的东西为:code->data[], code->keys[] 和job->mem_ptr2_bytes[]。
   我们立即拷贝在检查(checkout)处理中揭示的四个vendor密钥到lm_code.h中(并证实在内存信息转储中),但是我们仍然缺少加密种子和神秘的VENDOR_LEY5。现在,更进一步察看n密钥产生(keyge)处理,它在检查(checkout)的 code->data[]和job->mem_ptr2_bytes[](它的job结构是空的)中出现不同。那是为什么呢?

#include "lmprikey.h"
#include "lmclient.h"
#include "lm_code.h"
#include "lmseeds.h"
... ...
/* 设置site_code.data = {ENCRYPTION_SEED1 ^ VENDOR_KEY5, 
ENCRYPTION_SEED2 ^ VENDOR_KEY5}
site_code.keys = {VENDOR_KEY1, VENDOR_KEY2, VENDOR_KEY3, VENDOR_KEY4} */
LM_CODE(site_code, ENCRYPTION_SEED1, ENCRYPTION_SEED2, VENDOR_KEY1,
VENDOR_KEY2, VENDOR_KEY3, VENDOR_KEY4, VENDOR_KEY5);
... ...
int main(int argc, char **argv)
{
... ...
/*设置site_code.data = {ENCRYPTION_SEED1, ENCRYPTION_SEED2} */
LM_CODE_GEN_INIT(&site_code);
if (lc_init((LM_HANDLE *)0, VENDOR_NAME, &site_code, &lm_job))
{
lc_perror(lm_job, "lc_init failed");
exit(-1);
}
... ...
/*调用链接dofilecrypt() -> dofpcrypt() -> lc_cryptstr() */
estat |= dofilecrypt(infilename, outfilename, &site_code);
return 0;
}
 
    这是lmcrypt.c的简明源代码及其自身说明。仔细注意到在lmclient.h中定义的两个宏LM_CODE和 LM_CODE_GEN_INIT。前者初始化site_code.data为与VENDOR_KEY5进行与或的加密种子(与omcode.c一致),但后者很快逆向它为原始的加密种子。我是否曾告诉你FLEXLM有很糟的代码类型?:)
    不管怎样,原始的加密种子和零job->mem_ptr2_bytes值被用于密钥产生(keygen),它不同于检查(checkout)。很自然的事情是从检查处理拷贝code->data到lmseeds.h作为加密种子1和2,并重新编译 lmcrypt.exe。但它并不工作,因为在表格52xxxxB8和75yyyy0F中,code->data是随机的,而xxxx和yyyy在每次运行中都将改变。相同的是,对于job->mem_ptr2_bytes是真实的。我们推断,加密种子必定被模糊并存储在vendor软件中的两个地方:code->data[]和job->mem_ptr2_bytes[](它们应该是关系很近的一对),因为它提供给最终用户而原始种子需要保护。相反,lmcrypt.exe仅对vendor可获得,因此加密种子可以以一般形式出现。

Flexlm保护

《加密与解密》第二版 
 
13.6 Flexlm保护
 
http://www.pediy.com/book/book.gif
 
说明

这部分内容是《加密与解密》第二版的,没有放进第三版。书上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行构成,另外有一个续行符"\"用来表示一行的继续。
 
名称:  Flexlm01.gif
查看次数: 2320
文件大小:  18.4 KB
 
名称:  Flexlm02.gif
查看次数: 2322
文件大小:  18.3 KB
 
13.7.2 设置环境变量
一般来说,License Manager需要一个环境变量才能找到License文件。有的软件不需要设置环境变量,只需要将license文件放置到它的某个目录下面就可以了,如ActiveVHDL3.3,只要将license.dat放在它的dat目录下。
通常环境变量名称是LM_LICENSE_FILE,大小写无关,Windows 9x下的设置方法是在Autoexec.bat中加入一行: 
 
名称:  Flexlm03.gif
查看次数: 2315
文件大小:  2.7 KB
 
如果有多个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文件的路径以及名称,如:
 
名称:  Flexlm04.gif
查看次数: 2315
文件大小:  3.0 KB
 
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的格式。
 
名称:  Flexlm05.gif
查看次数: 2321
文件大小:  29.2 KB
 
已经准备好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的界面。
 
名称:  Flexlm06.gif
查看次数: 2300
文件大小:  5.0 KB
 
输入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前,上面的的内容将被压到栈中。
程序中断后将看到如下代码:
 
名称:  Flexlm07.gif
查看次数: 2307
文件大小:  2.6 KB
 
这里获得了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文件,应该看起来像这样:
 
名称:  Flexlm08.gif
查看次数: 2312
文件大小:  2.2 KB
 
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