2009年6月10日星期三

翻译FLEXlm9.2的破解教学二

翻译FLEXlm9.2的破解教学二

newsearch 当前离线 添加 newsearch 的声望 反映此帖
标 题: 翻译FLEXlm9.2的破解教学二
作 者: newsearch
时 间: 2004-12-06,12:44
链 接: http://bbs.pediy.com/showthread.php?t=8021

On Software Reverse Engineering - 2 选择自 zxg32 的 Blog
Reverse Engineering, FLEXlm, IMSL

翻译第二篇 FLEXlm 结构
在配备有VS+FLEXLM源代码、W32Dasm+cmath.exe以及IDA+cmath.exe(具签名)后,现在我们将能够揭示 FLEXLM的核心了。下面是我们的一些发现,在这里lm_ckout.c!lc_checkout()表示"在lm_ckout.c模块/文件中的函数 lc_checkout()",箭头符号指示函数调用。注意,由于程序分支的原因,仅部分代码被追踪和展示,但通常情况下,我们感兴趣的仅是这些分支。

0047D0C6: push 00000018 ;程序入口点
0047D22D: call 00401000 ;调用cmath.exe!main()
0047D240: call 0047F20D ;调用链接到ntdll.dll!NtTerminateProcess()
sub_401000: cmath.exe!main()
0040101D: call 004033B0 ;调用vc++\flexlm.obj!imsl_f_lin_sol_gen()
00401039: call 00401050 ;调用vc++\fwrimat.obj!imsl_f_write_matrix()
sub_004033B0: vc++\flexlm.obj!imsl_f_lin_sol_gen()
004033C8: call 00408F24 ;调用vc++\error.obj!imsl_e1psh()
0040342C: call 004034A0 ;调用vc++\flinslg.obj!l_lin_sol_gen()以进行真正的工作
sub_00408F24: vc++\error.obj!imsl_e1psh()
00408F3A: call 0040A850 ;调用链接vc++\single.obj!imsl_once() ® vc++\error.obj!l_error_init()
;® vc++\flexlm.obj!imsl_flexlm()
00408F76: call 00414AFD ;调用imsl_highland_check() ® l_check.c!lc_timer() ® l_timer_heart()
;® l_check() ® l_reconnect() ® lm_ckout.c!l_checkout() as heartbeat
sub_00413290: vc++\flexlm.obj!imsl_flexlm()
004132EF: call 004294A0 ;调用lm_njob.c!lc_new_job()
004133A4: call 00426380 ;设置LM_A_DISABLE_ENV 为1
004133DE: call 00426380 ;设置LM_A_LICENSE_FILE_PTR 为许可证文件位置
00413486: call 00426380 ;设置LM_A_CHECK_INTERVAL为 -1
004134C0: call 00426380 ;设置LM_A_RETRY_INTERVAL为 -1
004134FB: call 00426380 ;设置LM_A_RETRY_COUNT为 -1
004135A7: call 00426380 ;设置LM_A_LINGER为 0
004136A6: call 0042420C ;调用l_check.c!lc_status(), 返回LM_NEVERCHECKOUT
004138CD: call 0041A010 ;调用lm_ckout.c!lc_checkout()
00414099: call 0047FA9B ;返回当前日期和时间
004141C6: call 0042563D ;调用lm_config.c!lc_get_config()
0041434E: call 0047F8F0 ;检查许可证十分过期,如果没有返回0
sub_0041A010: lm_ckout.c!lc_checkout()
0041A093: call 0041A14B ;调用lm_ckout.c!l_checkout(),返回FFFFFFF8
sub_0041A14B: lm_ckout.c!l_checkout()
0041A2E8: call [004AA01C] ;调用lm_ckout.c!lm_start_real(),返回FFFFFFF8
sub_0041A875: lm_ckout.c!lm_start_real()
0041AA47: call 0041B4A5 ;调用lm_ckout.c!l_local_verify_conf(), 返回1=成功
0041AC01: call 0041BD89 ;调用lm_ckout.c!l_good_lic_key(), 返回0=失败
sub_0041BD89: lm_ckout.c!l_good_lic_key()
0041BE30: call 00433D15 ;调用l_getattr.c!l_xorname()
0041BE4D: call 0041DB9E ;调用lm_ckout.c!l_sg()
0041C202: call 0041EBE3 ;调用vc++\lm_ckout.obj!l_crypt_private(), 返回0
sub_0041DB9E: lm_ckout.c!l_sg()
0041DBF7: call [004AD064] ;调用lm_new.c!l_n36_buff()
0041DC16: call 00443283 ;调用l_key.c!l_key()
sub_0041EBE3: vc++\lm_ckout.obj!l_crypt_private()
0041EC07: call 0041EE44 ;调用vc++\lm_ckout.obj!real_crypt(), 返回0
sub_0041EE44: vc++\lm_ckout.obj!real_crypt()
0041F9B6: call 00420AF6 ;调用vc++\lm_ckout.obj!l_string_key(), 返回0
sub_00420AF6: vc++\lm_ckout.obj!l_string_key()
00420E94 - 00421156 ;调用宏XOR_SEEDS_INIT_ARRAY(xor_arr)
00421247: call 0047F250 ;调用strcpy(lkey, license_key)
0042145E: call 004803A0 ;调用memcpy(newinput, input, inputlen)
0042191D: call 00421D66 ;调用l_strkey.c!our_encrypt()
00421A13 - 00421B27 ;for{}循环许可证密码匹配
00421B34: call 00421C22 ;调用l_strkey.c!atox()以转换二进制串为ASCII文本
sub_00426380: lm_set_attr.c!lc_set_attr()
004263E4: call 0042641E ;调用lm_set_attr.c!l_set_attr()以设置配置结构中的属性
sub_0042641E: lm_set_attr.c!l_set_attr()
00427045: call 00427DBC ;如果设置LM_A_LICENSE_FILE_PTR,调用lm_set_attr.c!l_set_license_path()
;® lm_config.c!l_flush_config() ® l_init_file.c!l_init_file()
;® l_allfeat.c!l_allfeat() ® l_allfeat.c!l_parse_feature_line()
;® l_allfeat.c!oldkey() ® vc++\l_allfeat.obj!l_crypt_private()
sub_004294A0: lm_njob.c!lc_new_job()
004294C0: call [004A5A98] ;调用lm_new.c!l_n36_buf(), 返回 1
004294D2: call [004A5A98] ;调用具有所有的O参数的lm_new.c!l_n36_buf(),返回 0
004294E8: call 0044357F ;调用链接lm_init.c!lc_init() ® lm_init.c!l_init()
004294FD ? 00429516 ;打开LM_OPTFLAG_CUSTOM_KEY5 标志
sub_0043873B: vc++\l_allfeat.obj!l_crypt_private()
0043875F: call 00438771 ;调用vc++\l_allfeat.obj!real_crypt(), 返回 21D5B6E8572E
sub_00438771: vc++\l_allfeat.obj!real_crypt()
004392DC: call 0043A41C ;调用vc++\l_allfeat.obj!l_string_key(), 返回21D5B6E8572E
sub_0043A41C: vc++\l_allfeat.obj!l_string_key()
sub_0044359E: lm_init.c!l_init() ;接收 VENDORCODE 和 VENDORNAME 以初始化 job 结构
00443E8F ? 00444354 ;一些有效性测试,可能报告错误
004441F9: call 0041DB9E ;调用lm_ckout.c!l_sg()
sub_0044A110: lm_new.c!l_n36_buf() ;初始化VENDORCODE 结构和 VENDORNAME
0044B503: push 00450BE0 ;压入 lm_new.c!l_n36_buff() 地址
0044B508: call 00444B11 ;调用 lm_init.c!l_x77_buf() 以设置 L_UNIQ_KEY5_FUNC
sub_00450BE0: lm_new.c!l_n36_buff()
00450C34 ? 00450EB0 ;模糊化在job中的mem_ptr2_bytes[]
00450EB5 ? 00450FF8 ;模糊化在 VENDORCODE中的data[0]和 data[1]
sub_77F8DD80: ntdll.dll!NtTerminateProcess()
77F8DD8B: ret 00000008 ;程序退出

正如我们所看到的,我们先前提及的子程序0041A010实际上是lm_ckout.c!lc_checkout(),它是一个真正的核心函数。如果许可证检查成功,它将返回0;否则,它将返回错误代码(在lmclient.h中FFFFFFF8被定义为LM_BADCODE )。由于是核心,l_checkout()可能将被调用几次,但我们不关心(它)。记住,我们的目标是找到checksum对比代码的位置和恢复真正的签名。
一个快速的搜索告诉我们: STRNCMP(在l_privat.h中定义的宏, 如果串匹配则设置结果=0)仅在 lm_ckout.c!l_good_lic_key()和l_crypt.c!l_crypt_private()中出现。注意l_crypt.c 和 l_strkey.c 并不直接被编译为目标文件,而是被包含进模块lm_ckout.obj 和 l_allfeat.obj中。除了这两个之外,lm_crypt.obj也包含了l_crypt.c并揭示其函数到外部作为了API,其下的许多(函数)具有不同的别名,比如 lc_crypt()。但是,在cmath.exe中并未用该(代码)拷贝:lc_crypt()的地址是00469960,但我们在该处设置断点却什么也没有发生。

lm_ckout.c
... ...
#define l_crypt_private l_ckout_crypt
#define l_string_key l_ckout_string_key
... ...
/* Include l_crypt.c, so that these functions won't be global. */
#define LM_CKOUT
#include "l_crypt.c"
l_allfeat.c
... ...
#define LM_CRYPT_HASH
#include "l_crypt.c"
l_crypt.c
#include "l_strkey.c"
l_strkey.c
#include "l_strkey.h"
lm_crypt.c
#define l_crypt_private lc_crypt
... ...
#define LM_CRYPT
... ...
#include "l_crypt.c"

由于包含了超过一次,因为编译指令的原因,l_crypt/l_strkey函数的多个拷贝将不再相同。下面的 l_crypt_private()代码说明了这点(在这里,STRNCMP不是真正的问题)。取决于LM_CKOUT是否被定义,0041EBE3 (长版本)和0043873B (短版本)在两个模块中将有所不同。IDA FLAIR认知后者而非前者-实践中,我们必须在 lm_ckout.obj中手工识别这些函数。

ret = real_crypt(job, conf, sdate, code);
#ifdef LM_CKOUT
if (!(job->user_crypt_filter) && !(job->lc_this_keylist) && valid_code(conf->code))
{
if (job->flags & LM_FLAG_MAKE_OLD_KEY)
{
STRNCMP(conf->code, ret, MAX_CRYPT_LEN, not_eq);
}
else
{
STRNCMP(conf->lc_sign, ret, MAX_CRYPT_LEN, not_eq);
}
if (not_eq && !(job->options->flags & LM_OPTFLAG_STRINGS_CASE_SENSITIVE))
{
job->options->flags |= LM_OPTFLAG_STRINGS_CASE_SENSITIVE;
ret = real_crypt(job, conf, sdate, code);
job->options->flags &= ~LM_OPTFLAG_STRINGS_CASE_SENSITIVE;
}
}

没有评论: