首页
社区
课程
招聘
[原创]将要绝种的ELANLM分析
发表于: 2011-4-22 10:20 9241

[原创]将要绝种的ELANLM分析

2011-4-22 10:20
9241

      05年注册看雪,一直没有发表过任何有意义的东西,这次分析了ELANLM的算法,发现能找到的资源很少,毕竟这个东西已经是古董了,这次补个课吧,将基本的东西总结一下,发出来供大家参考。
      Elanlm是一款老掉牙的license管理工具,在2011年的今天已经很少被商业软件使用,不过我还是碰到一个,于是进行分析。
      一阵搜索,得到的信息非常有限,但也有很大帮助。
http://www.woodmann.com/fravia/pilgrim_elanlm_spps.htm
http://www.woodmann.com/crackz/Tools/Dongles/Elanapi.zip

      我分析的应用程序提供了一个license文件产生工具,可以通过Server Code 和Key产生license文件。Server Code由这个程序自动生成,Key需要购买后根据Server Code申请,首先从这个工具入手,分析Key是如何验证的。下图是license文件产生工具的界面截图,知道的也请不要说出来这是哪个公司的产品。

      用IDA进行跟踪,定位到验证Key和产生license文件的代码在这个函数里(有多种方法可以定位到这段代码,比如在GetDlgItemTextA设断点;或者用Find crypt 插件找到DES的代码设断点)。下面是从IDA里粘出的代码,其中slm开头的函数都是通过对照Elanapi中函数的说明定位的Elanlm SDK中的函数名,Do开头的函数是我自己分析后命名的,意思不一定准确。另外为避免不必要的麻烦,尽量去掉了和该商业程序直接相关的信息,比如代码的绝对地址;明显的字符串等等。下面代码中的各类名称如sKey、p_lm_hinst等都是我在IDA中分析后标注的,并非是直接反汇编得到的。

                 sub     esp, 288h
                 push    ebx
                 push    ebp
                 mov     ebp, [esp+290h+arg_C] ; localhost
                 push    esi
                 mov     esi, [esp+294h+arg_a] ; 0x61
                 push    edi
                 mov     edi, [esp+298h+sKey] ; 
                 mov     [esp+298h+var_284], 0

lbLoop:                                 
                 push    edi             ; skey
                 mov     dword ptr dwLicInstallOkSign, 0FFFFFFFFh
                 call    DoTrimSpace
                 mov     cl, [edi]
                 add     esp, 4
                 test    cl, cl
                 mov     eax, esi
                 jz      short loc_40ZZZZ
                 mov     edx, edi
                 sub     edx, esi

lbLoopCopy:                             
                 mov     [eax], cl
                 mov     cl, [eax+edx+1]
                 inc     eax
                 test    cl, cl
                 jnz     short lbLoopCopy

                 push    esi             ; 
                 mov     byte ptr [eax], 0
                 call    DoKeyStringChk
                 add     esp, 4
                 test    eax, eax
                 jl      lbStringChkError
                 mov     eax, p_lm_hinst
                 push    esi             ; sKey
                 push    eax
                 call    DoSumKey
                 test    eax, eax
                 jz      short lbSumKeyOk
                 push    offset aNotAValidKey
                 push    1               
                 call    DoMessage
                 mov     ecx, p_lm_hinst
                 add     esp, 8
                 push    ecx             ; lm_instance
                 call    slm_getversion
                 push    esi
                 mov     bl, al
                 call    sub_40????
                 add     esp, 4
                 test    eax, eax
                 jz      lbStringChkError
                 cmp     bl, enum_slm_v1
                 jl      short lbforget_n
                 cmp     bl, enum_slm_v5
                 jle     lbStringChkError

lbforget_n:                             ; CODE XREF: DoInstallLic+A6 j
                 push    offset aDidYouForgetTh ;
                 push    1               
                 call    DoMessage
                 add     esp, 8
                 jmp     lbStringChkError
lbSumKeyOk:                             
                 mov     edx, dword ptr pKeyData
                 mov     eax, p_lm_hinst
                 push    0               ; options
                 push    edx             ; arg_keydata
                 push    esi             ; key 
                 push    1               ; arg_way =1 decode
                 push    eax             ; arg_slm_instance
                 call    slm_key
                 test    eax, eax
                 jz      short loc_40XXXX
                 mov     ecx, p_lm_hinst
                 push    eax             ; code
                 push    0               ; feature
                 push    0               ; server
                 push    ecx             ; lm_instance
                 call    slm_message
                 push    eax             ; Args
                 push    offset aInvalidKeyS
                 push    1               ; int
                 call    DoMessage
                 add     esp, 0Ch
slm_key() - Generate or decode a license key
SYNTAX
int slm_key(instance, way, key, keydata, options)
SLM_INSTANCE instance; 
int way; 
char *key; 
struct slm_keydata *keydata; 
unsigned long options;
第一处调用:
push    0               ; options
mov     ecx, [esp+1570h+arg_slm_instance]
push    eax             ; arg_keydata
push    ebp             ; arg_key
push    1               ; arg_way
push    ecx             ; arg_slm_instance
call    slm_key
test    eax, eax
jnz     loc_XXXX

第二处调用:
mov     edx, dword ptr pKeyData
mov     eax, p_lm_hinst
push    0               ; options
push    edx             ; arg_keydata
push    esi             ; key 
push    1               ; arg_way =1 decode
push    eax             ; arg_slm_instance
call    slm_key
test    eax, eax
jz      short loc_40XXXX

第三处调用:
push    2               ; options
lea     ecx, [esp+29Ch+arg_key]
push    eax             ; arg_keydata
push    ecx             ; arg_key
push    0               ; arg_way
push    edx             ; arg_slm_instance
call    slm_key
test    eax, eax
jnz     loc_40xxxx
 push    1220h           ; Size
call    _malloc
add     esp, 4
mov     dword ptr pKeyData, eax
mov     eax, dword ptr pKeyData
                 mov     edx, [esp+298h+arg_bIsDefaultHostName]
                 mov     ecx, [eax+5Ch]    // slm_keydata.dwAboutServer
                 cmp     ecx, edx
                 jz      short loc_xxxx
                    …….
      loc_xxxx   mov     edx, [eax+68h]     // slm_keydata.dwAboutServer1
                add     eax, 0B9h
                push    edx
                mov     edx, [esp+29Ch+arg_10] ; server code
                push    ecx             
                mov     ecx, [eax-2Dh]  ; slm_keydata.field(0xb9-0x2d)
                push    ecx             ; arg_slm_instance_8c
                push    ebp             ; localhost
                push    eax             ; 0403
                mov     eax, [esp+2ACh+arg_serial]
                push    edx             
                push    eax              
    call    DoChkServerID
mov     eax, dword ptr pKeyData
mov     edx, [esp+298h+arg_bIsDefaultHostName]
mov     ecx, [eax+slm_keydata.field_5C]
cmp     ecx, edx
mov     eax, dword ptr pKeyData
mov     edx, [esp+298h+arg_bIsDefaultHostName]
mov     ecx, [eax+[COLOR="Yellow"]slm_keydata.dwAboutServer[/COLOR]]
cmp     ecx, edx 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 103
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
谢谢分享,呵呵。。。。
2011-4-22 11:29
0
雪    币: 208
活跃值: (321)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
非常感谢,这么详细啊
2011-4-22 12:49
0
雪    币: 47147
活跃值: (20310)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
感谢你的分享,谢谢~
2011-4-22 22:05
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
5
传说见过的玩意,谢谢分享
2011-4-23 00:11
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
楼主已经潜成仙了。。。我等还在苦力挣扎。。
2011-4-23 17:32
0
雪    币: 238
活跃值: (108)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
/*-------------------------------------------------------------*/
/*  修改号   日期         修改人     修改描述                  */
/*-------------------------------------------------------------*/
/*  初始版本 2004-09-04   HZH        初始版本                  */
/*-------------------------------------------------------------*/
#ifndef _ELANLM_H
#define _ELANLM_H

/*-------------------------------------------------------------*/
/*  include                                                    */
/*-------------------------------------------------------------*/
#include "def.h"
#include "bn.h"
#include "des.h"

/*-------------------------------------------------------------*/
/*  define                                                     */
/*-------------------------------------------------------------*/
#define ELAN_MAX_LEN 196

/*-------------------------------------------------------------*/
/*  slm_checksum32                                             */
/*-------------------------------------------------------------*/
static T_u_int32 DS_checksum[] = {
    0x0000000D, 0x00000011, 0x0000018D, 0x00000013,
    0x00000017, 0x00000191, 0x0000001D, 0x0000001F,
    0x00000199, 0x00000025, 0x00000029, 0x000001A3,
    0x0000002B, 0x0000002F, 0x000001A5, 0x00000035,
    0x0000003B, 0x000001AF, 0x0000003D, 0x00000043,
    0x000001B1, 0x00000047, 0x00000049, 0x000001B7,
    0x0000004F, 0x00000053, 0x000001BB, 0x00000059,
    0x00000061, 0x000001C1, 0x00000065, 0x00000067,
    0x000001C9, 0x0000006B, 0x0000006D, 0x000001CD,
    0x00000071, 0x0000007F, 0x000001CF, 0x00000083,
    0x00000089, 0x000001D3, 0x0000008B, 0x00000095,
    0x000001DF, 0x00000097, 0x0000009D, 0x000001E7,
    0x000000A3, 0x000000A7, 0x000001EB, 0x000000AD,
    0x000000B3, 0x000001F3, 0x000000B5, 0x000000BF,
    0x000001F7, 0x000000C1, 0x000000C5, 0x000001FD,
    0x000000C7, 0x000000D3, 0x00000209, 0x000000DF,
    0x000000E3, 0x0000020B, 0x000000E5, 0x000000E9,
    0x0000021D, 0x000000EF, 0x000000F1, 0x00000223,
    0x000000FB, 0x00000101, 0x0000022D, 0x00000107,
    0x0000010D, 0x00000233, 0x0000010F, 0x00000115,
    0x00000239, 0x00000119, 0x0000011B, 0x0000023B,
    0x00000125, 0x00000133, 0x00000241, 0x00000137,
    0x00000139, 0x0000024B, 0x0000013D, 0x0000014B,
    0x00000251, 0x00000151, 0x0000015B, 0x00000257,
    0x0000015D, 0x00000161, 0x00000259, 0x00000167,
    0x0000016F, 0x0000025F, 0x00000175, 0x0000017B,
    0x00000265, 0x0000017F, 0x00000185 };

static T_u_int32 slm_checksum32(char *in, T_u_byte base)
{
    T_u_int32 rtn = 0;
    T_u_int32 cnt = 0;

    if (in[cnt] == '\0')
    {
        return (rtn);
    }

    while (in[cnt])
    {
        rtn += (in[cnt] - base + 1) * (DS_checksum[cnt % 0x6C] + (cnt / 0x6C));
        cnt++;
    }

    return (rtn);
}

/*-------------------------------------------------------------*/
/*  slm_strconvert                                             */
/*-------------------------------------------------------------*/
static T_u_byte slm_strconvert(char *in, T_u_byte len, char * out)
{
    T_u_byte cnt1, cnt2, sig;

    cnt2 = 0;

    sig  = 0;
    for (cnt1 = 0; cnt1 < len; cnt1 += 3)
    {
        out[cnt2] = in[cnt1 + sig];
        cnt2++;
    }

    sig  = 1;
    for (cnt1 = 0; cnt1 < len; cnt1 += 3)
    {
        out[cnt2] = in[cnt1 + sig];
        cnt2++;
    }

    sig  = 2;
    for (cnt1 = 0; cnt1 < len; cnt1 += 3)
    {
        out[cnt2] = in[cnt1 + sig];
        cnt2++;
    }

    return (len);
}

/*-------------------------------------------------------------*/
/*  DigitsRequired                                             */
/*-------------------------------------------------------------*/
static T_u_byte digits[] = {
    0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03,
    0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
    0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0A,
    0x0A, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D,
    0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x10, 0x10,
    0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x14,
    0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x17,
    0x17, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1A, 0x1A,
    0x1B, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E,
    0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x21,
    0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24,
    0x25, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28,
    0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2A, 0x2B,
    0x2B, 0x2C, 0x2C, 0x2D, 0x2D, 0x2D, 0x2E, 0x2E,
    0x2F, 0x2F, 0x2F, 0x30, 0x30, 0x31, 0x31, 0x31,
    0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35,
    0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x38, 0x38,
    0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B,
    0x3C, 0x3C, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F,
    0x3F, 0x40, 0x40, 0x40, 0x41, 0x41, 0x42, 0x42,
    0x43, 0x43, 0x43, 0x44, 0x44, 0x45, 0x45, 0x45,
    0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x48, 0x49,
    0x49, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4C, 0x4C,
    0x4C, 0x4D, 0x4D, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F,
    0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x53,
    0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56,
    0x56, 0x57, 0x57, 0x58, 0x00, 0x00, 0x00, 0x00,
    0x00 };

static T_u_byte DigitsRequired(T_u_byte in)
{
    T_u_byte tmp;

    in += 1;

    tmp = digits[in];

    while ((tmp == digits[in + 1]) && (in <= 0xC8))
    {
            in += 1;
    }

    return (in - 1);
}

/*-------------------------------------------------------------*/
/*  slm_bngetnumber                                            */
/*-------------------------------------------------------------*/
static T_u_byte slm_bngetnumber(char *in, T_u_byte *out)
{
    char     tmp;
    T_u_byte cnt, len, rtn;

    len = strlen(in);

    for(cnt = 0; cnt < (len / 2); cnt++)
    {
        tmp = in[cnt];
        in[cnt] = in[len - cnt - 1];
        in[len - cnt - 1] = tmp;
    }

    //

    BIGNUM *temp = BN_new();

    BN_dec2bn(&temp, in);

    rtn = BN_bn2bin(temp, out);

    BN_free(temp);

    return (rtn);
}

/*-------------------------------------------------------------*/
/*  slm_bnputnumber                                            */
/*-------------------------------------------------------------*/
static T_u_byte slm_bnputnumber(T_u_byte *in, T_u_byte len, char *out, T_u_byte digits)
{
    char     tmp;
    T_u_byte cnt, rtn;

    BIGNUM *temp = BN_new();

    BN_bin2bn(in, len, temp);

    char *buf = new char [256];

    buf = BN_bn2dec(temp);

    strcpy(out, buf);

    delete [] buf;

    //
    rtn = strlen(out);

    for(cnt = 0; cnt < (rtn / 2); cnt++)
    {
        tmp = out[cnt];
        out[cnt] = out[rtn - cnt - 1];
        out[rtn - cnt - 1] = tmp;
    }

    while (rtn < digits)
    {
        strcat(out, "0");
        rtn += 1;
    }

    return (rtn);
}

/*-------------------------------------------------------------*/
/*  slm_bntoubuf                                               */
/*-------------------------------------------------------------*/
static void slm_bntoubuf(T_u_byte *in, T_u_byte len)
{
    T_u_byte cnt, tmp;

    for(cnt = 0; cnt < (len / 2); cnt++)
    {
        tmp = in[cnt];
        in[cnt] = in[len - cnt - 1];
        in[len - cnt - 1] = tmp;
    }
}

/*-------------------------------------------------------------*/
/*  slm_bnfromubuf                                             */
/*-------------------------------------------------------------*/
static void slm_bnfromubuf(T_u_byte *in, T_u_byte len)
{
    T_u_byte cnt, tmp;

    for(cnt = 0; cnt < (len / 2); cnt++)
    {
        tmp = in[cnt];
        in[cnt] = in[len - cnt - 1];
        in[len - cnt - 1] = tmp;
    }
}

/*-------------------------------------------------------------*/
/*  SpinLast                                                   */
/*-------------------------------------------------------------*/
static void SpinLast(char *in, T_u_byte mul)
{
    char tmp[] = "000";

    T_u_byte len = strlen(in);

    strncpy(tmp , in, 3);

    T_s_int32 val = atoi(tmp);

    in[len - 1] = ((val % 0x59) * mul + in[len - 1] + 0x34) % 0xA + '0';
}

/*-------------------------------------------------------------*/
/*  slm_descfbenc                                              */
/*-------------------------------------------------------------*/
static T_s_int slm_descrypt(T_u_byte         *input,
                            T_u_byte         *output,
                            T_u_byte          len,
                            DES_cblock       *key,
                            DES_cblock       *pad,
                            T_s_int           enc)
{
    T_s_byte      cnt;

    DES_key_schedule schedule;
    DES_set_key(key, &schedule);

    DES_cblock ivec;
    while (len-- > 0)
    {
        DES_ecb_encrypt(pad, &ivec, &schedule, enc);

        *output = *input ^ ivec[7];

        for (cnt = 6; cnt >= 0; cnt--)
        {
            *(*pad + cnt + 1) = *(*pad + cnt);
        }
        **pad = *output;

        ++input;
        ++output;
    }

    return (0);
}

/*-------------------------------------------------------------*/
/*  slm_descrypt_decimal                                       */
/*-------------------------------------------------------------*/
static void slm_descrypt_decimal(char       *Elan_realinfo,
                                 char       *Elan_cryptinfo,
                                 T_u_byte   *salt,
                                 T_u_int    saltlen,
                                 T_u_int32  slm_port_salt,
                                 T_s_int    enc)
{
    T_u_int   len;
    T_u_int   cnt;
    T_u_byte  digits;
    T_u_int32 chksum;

    DES_cblock deskey = {0};
    DES_cblock vector = {0};

    char Elan_checksum[] = "0000";

    char Elan_temp[ELAN_MAX_LEN] = "";
    char Elan_convert[ELAN_MAX_LEN] = "";

    T_u_byte plain[ELAN_MAX_LEN] = {0};
    T_u_byte cipher[ELAN_MAX_LEN] = {0};

    //由salt(256 bytes) slm_port_salt计算deskey vector
    for (cnt = 0; cnt < saltlen; cnt++)
    {
        deskey[(cnt + 2) % 8] ^= salt[cnt];
    }

    for (cnt = 0; cnt < 4; cnt++)
    {
        deskey[cnt] ^= (T_u_byte)((slm_port_salt >> (cnt * 8)) & 0xFF);
    }

    for (cnt = 0; cnt < 8; cnt++)
    {
        deskey[cnt] = (deskey[cnt] * 3) % 0xFB;
    }

    for (cnt = 0; cnt < 8; cnt++)
    {
        vector[cnt] = (deskey[cnt % 8] & 0xFE) ^ (deskey[cnt % 3] >> 1);
    }

    //将明码信息字符串进行位置变换
    len = slm_strconvert(Elan_realinfo, strlen(Elan_realinfo), Elan_convert);

    //计算需要的字节
    digits = DigitsRequired(len + 4);

    //计算slm_checksum32
    chksum =  (slm_checksum32(Elan_realinfo, 0x30) % 0xD03) * 3 - 4;
    chksum =  chksum - len + digits;
    sprintf(Elan_checksum, "%04lu", chksum);

    //连接生成新的字符串
    lstrcat(Elan_temp, Elan_checksum);
    lstrcat(Elan_temp, Elan_convert);

    //slm_bngetnumber
    cnt = slm_bngetnumber(Elan_temp, plain);

    //slm_bntoubuf
    slm_bntoubuf(plain, cnt);

    //slm_descrypt
    slm_descrypt(plain, cipher, cnt, &deskey, &vector, enc);

    //slm_bnfromubuf
    slm_bnfromubuf(cipher, cnt);

    //slm_bnputnumber
    memset(Elan_cryptinfo, 0, ELAN_MAX_LEN);
    slm_bnputnumber(cipher, cnt, Elan_cryptinfo, digits + 1);

    //SpinLast
    SpinLast(Elan_cryptinfo, 1);

    return;
}

/*-------------------------------------------------------------*/
#endif /* _ELANLM_H */

忘记是啥年代分析的东西了, 不记得是ElanLM什么版本的!
2011-4-23 20:37
0
雪    币: 268
活跃值: (155)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
楼上的代码应该是我标注的‘DoSumKey’中逻辑的逆,我分析的是V5.05版本。
2011-4-24 22:21
0
雪    币: 220
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
呵呵 还是得支持一下的
2011-4-27 01:40
0
游客
登录 | 注册 方可回帖
返回
//