ida 9.0.24.807 许可校验

ida 9.0.24.807 ida64.dll 许可校验

搜索license可以找到大量信息

并且有几个license相关的导出函数

image-20250605212041916

跟踪get_license_manager

定位许可校验函数

license_manager虚表信息

.rdata:00000001003CC298 license_manager_vft dq offset vtbl_license_manager_vft_func_0
.rdata:00000001003CC298                                         ; DATA XREF: vtbl_license_manager_vft_func_0+19↑o
.rdata:00000001003CC298                                         ; new_license_manager+2E↑o
.rdata:00000001003CC2A0                 dq offset vtbl_license_manager_vft_func_1
.rdata:00000001003CC2A8                 dq offset vtbl_license_manager_vft_func_2
.rdata:00000001003CC2B0                 dq offset vtbl_license_manager_vft_func_3
.rdata:00000001003CC2B8                 dq offset vtbl_license_manager_vft_func_4
.rdata:00000001003CC2C0                 dq offset vtbl_license_manager_vft_func_5
.rdata:00000001003CC2C8                 dq offset vtbl_license_manager_vft_func_6
.rdata:00000001003CC2D0                 dq offset vtbl_license_manager_vft_func_7
.rdata:00000001003CC2D8                 dq offset vtbl_license_manager_vft_func_8
.rdata:00000001003CC2E0                 dq offset vtbl_license_manager_vft_func_9
.rdata:00000001003CC2E8                 dq offset vtbl_license_manager_vft_func_10
.rdata:00000001003CC2F0                 dq offset vtbl_license_manager_vft_func_11
.rdata:00000001003CC2F8                 dq offset vtbl_license_manager_vft_func_12

在get_license_manager 函数中发现license校验函数

  // 读取 keyfile 并进行验证
  if ( ((unsigned int (__fastcall *)(LicenseManager *, char *, int *))license_manager_obj_100451FD8->vft_0->vtbl_license_manager_vft_func_3)(
         license_manager_obj_100451FD8,
         v2,
         &v45) )
  {
    if ( v40 )
      v4 = (const char *)v39;

    debug_100022C40("Could not acquire license: %s\n", v4);
  }

vtbl_license_manager_vft_func_3_3415C0

 v52 = check_license_file_100344500(
              (char **)&obj->field_70,
              (__int64)&obj->field_88,
              (__int64)&obj->field_148,
              hexlic_fpath,
              a4,
              a5);
      if ( !v52 && (a4 & 0x10) != 0 )
      {
        qmutex_198 = 0i64;
        v74 = 0i64;
        v75 = 0i64;
        if ( !((unsigned __int8 (__fastcall *)(LicenseManager *, void **, _QWORD, _QWORD))obj->vft_0->vtbl_license_manager_vft_func_6)(
                obj,
                &qmutex_198,
                *(unsigned int *)(a3 + 8),
                0i64) )
        {
          if ( v54 )
            local_file = (const char *)v53;

          q_sprintf_100003F80(
            a5,
            "Failed to list licenses for product %s in the local file \"%s\".",
            product_1004505B0[*(int *)(a3 + 8)],
            local_file);
          v43 = (char *)qmutex_198;

check_license_file_100344500

__int64 __fastcall check_license_file_100344500(char **a1, __int64 a2, __int64 a3, const char *a4, int a5, __int64 a6)
{
  unsigned int v10; // ebx
  void *v11; // r9
  __int64 v12; // r8
  __int64 v13; // rdx
  void *lictext; // [rsp+30h] [rbp-28h] BYREF
  __int64 v16; // [rsp+38h] [rbp-20h]
  __int64 v17; // [rsp+40h] [rbp-18h]

  lictext = 0i64;
  v16 = 0i64;
  v17 = 0i64;
  if ( load_file_100344970(&lictext, a4, a6) )//读取许可
  {
    v10 = check_100342660(a1, a2, (__int64)&lictext, a5, a6);//校验
    if ( a3 )
    {
      v11 = *(void **)a3;
      v12 = *(_QWORD *)(a3 + 8);
      v13 = *(_QWORD *)(a3 + 0x10);
      *(_QWORD *)a3 = lictext;
      *(_QWORD *)(a3 + 8) = v16;
      *(_QWORD *)(a3 + 0x10) = v17;
      lictext = v11;
      v16 = v12;
      v17 = v13;
    }

    if ( v10 && *(_QWORD *)(a6 + 8) <= 1ui64 )
      q_sprintf_100003F80(a6, "The file \"%s\" doesn't appear to be a valid license file", a4);
  }
  else
  {
    v10 = 2;
  }

  qfree(lictext);
  return v10;
}

check_100342660

__int64 __fastcall check_100342660(char **a1, __int64 a2, __int64 lictext, char a4, __int64 a5)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  Block = 0i64;
  v13 = 0i64;
  v14 = 0i64;
  if ( (unsigned __int8)parse_and_check_1003438C0((__int64 *)&Block, lictext, a4, (void **)a5) )
    v7 = check_json_field_100343DC0(a1, a2, (__int64 *)&Block, 0, (void **)a5);
  else
    v7 = 2;

  v8 = Block;
  if ( !Block )
    return v7;

  v9 = v13;
  if ( v13 )
  {
    v10 = (void **)Block;
    do
    {
      jvalue_t_clear(v10 + 3);
      qfree(*v10);
      v10 += 5;
      --v9;
    }
    while ( v9 );
  }

  qfree(v8);
  return v7;
}

parse_and_check_1003438C0

__int64 __fastcall parse_and_check_1003438C0(__int64 *a1, __int64 a2, char lictext, void **a4)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v7 = 0i64;
  v18 = 0;
  v19 = 0i64;
  v8 = *(_QWORD *)(a2 + 8);
  v9 = *(const void **)a2;
  Block = 0i64;
  v10 = 0i64;
  v21 = 0i64;
  v22 = 0i64;
  if ( v8 )
  {
    if ( v8 != 0xFFFFFFFFFFFFFFFFui64 )
    {
      v7 = qvector_reserve((__int64)&Block, 0i64, v8 + 1, 1ui64);
      Block = v7;
    }

    v21 = v8 + 1;
    memmove(v7, v9, v8);
    *((_BYTE *)Block + v8) = 0;
    v10 = v21;
    v7 = Block;
  }

  v11 = &Src;
  if ( v10 )
    v11 = (const char *)v7;

  if ( (unsigned int)parse_json_string((__int64)&v18, (__int64)v11)
    || v18 != 3
    || (unsigned int)check_signature_100343A20(v19, lictext, a4) )
  {
    v16 = 0;
  }
  else
  {
    if ( a1 )
    {
      if ( v18 != 3 )
      {
        if ( under_debugger )
          __debugbreak();

        interr(0x502i64);
      }

      v12 = *a1;
      v13 = a1[1];
      v14 = a1[2];
      v15 = v19;
      *a1 = *v19;
      a1[1] = v15[1];
      a1[2] = v15[2];
      *v15 = v12;
      v15[1] = v13;
      v15[2] = v14;
    }

    v16 = 1;
  }

  qfree(Block);
  jvalue_t_clear(&v18);
  return v16;
}

signature校验==》check_signature_100343A20

__int64 __fastcall check_signature_100343A20(__int64 *lic_json, char a2, void **a3)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  if ( (a2 & 2) != 0 )
    return 0i64;

  v6 = 0i64;
  v7 = lic_json[1];
  if ( !v7 )
    goto LABEL_9;

  v8 = *lic_json;
  v9 = *lic_json;
  while ( 1 )
  {
    v10 = &Src;
    if ( *(_QWORD *)(v9 + 8) )
      v10 = *(const char **)v9;

    if ( !strcmp(v10, "signature") )
      break;

    ++v6;
    v9 += 0x28i64;
    if ( v6 >= v7 )
      goto LABEL_9;
  }

  v11 = v8 + 8 * (5 * v6 + 3);
  if ( *(_DWORD *)v11 != 2 || !v11 )
  {
LABEL_9:
    sub_100002B00(a3, "Missing \"signature\" key", 0x17ui64);
    return 9i64;
  }

  // 计算payload json_str 的sha256
  calc_json_sha256_100344600((__int64)&v39, lic_json);
  signature_hex = 0i64;
  data = 0i64;
  v13 = 0i64;
  v34 = 0i64;
  v35 = 0i64;
  if ( *(_DWORD *)v11 != 2 )
  {
    if ( under_debugger )
      __debugbreak();

    interr(0x657i64);
  }

  v14 = *(__int64 **)(v11 + 8);
  v15 = v14[1];
  v16 = v15 - 1;
  if ( !v15 )
    v16 = 0i64;

  v17 = v16 >> 1;
  if ( v17 )
  {
    data = (void *)qvector_reserve(&data, 0i64, v17, 1i64);
    memset((char *)data + v34, 0, v17 - v34);
    v13 = v17;
    v34 = v17;
    signature_hex = data;
  }
  else
  {
    v17 = 0i64;
  }

  v18 = *v14;
  v19 = &signature_hex[v17];
  if ( signature_hex != v19 )
  {
    do
    {
      v29 = 0;
      if ( (unsigned int)qsscanf(v18, "%02X", &v29) != 1 )
        break;

      if ( v29 > 0xFF )
        break;

      *signature_hex = v29;
      v18 += 2i64;
      ++signature_hex;
    }
    while ( signature_hex != v19 );

    v13 = v34;
    signature_hex = data;
  }

  v20 = v14[1];
  v21 = *v14 + v20 - 1;
  if ( !v20 )
    v21 = *v14;

  if ( v18 == v21 )
  {
    Signature_offset_20h = 0i64;
    v36 = 0i64;
    v37 = 0i64;
    v38 = 0i64;
    if ( v13 != 0x80 )
    {
      if ( under_debugger )
        __debugbreak();

      interr(0xC49i64);
    }
//rsa 解密
    v24 = Signature_decryption_100344E30(
            signature_dec,
            0xA0ui64,
            signature_hex,
            0x80ui64,
            (__int64 *)&exponent_1003CCC30,
            (char *)pub_modulus_1003CCB90);
    if ( v24 >= 0 )
    {
      v25 = 0x40i64;
      if ( (unsigned __int64)v24 <= 0x40 )
      {
LABEL_32:
        Block = 0i64;
        v31 = 0i64;
        v32 = 0i64;
        Block = (_BYTE *)qvector_reserve(&Block, 0i64, 0x20i64, 1i64);
        memset(&Block[v31], 0, 0x20 - v31);
        v31 = 0x20i64;
        v26 = Block;
        // 复制signature rsa 解密后的数据 [0x20:0x40]
        *(_OWORD *)Block = *(_OWORD *)&signature_dec[0x20];
        v26[1] = *(_OWORD *)&signature_dec[0x30];
        Signature_offset_20h = Block;
        v36 = Block;
        Block = 0i64;
        v27 = v31;
        v37 = v31;
        v31 = 0i64;
        v38 = v32;
        v32 = 0i64;
        qfree(0i64);
        if ( v40 == v27 )                       // 0x20
        {
          if ( !v40 )
          {
LABEL_42:
            v22 = 0;
            goto LABEL_36;
          }

          v28 = v39;
          // 与前面计算的sha256的值进行比较
          while ( *v28 == v28[Signature_offset_20h - (_BYTE *)v39] )
          {
            if ( ++v28 - (_BYTE *)v39 >= v40 )
              goto LABEL_42;
          }
        }

        v22 = 0xA;

LABEL_36:
        qfree(Signature_offset_20h);
        signature_hex = data;
        goto LABEL_37;
      }

      while ( !signature_dec[v25] )
      {
        if ( ++v25 >= (unsigned __int64)v24 )
          goto LABEL_32;
      }

      v24 = 0xFFFFFFFE;
    }

    q_sprintf_100003F80((__int64)a3, "Signature decryption failed with code: %d", (unsigned int)v24);
    v22 = 0xA;
    goto LABEL_36;
  }

  v22 = 9;

LABEL_37:
  qfree(signature_hex);
  qfree(v39);
  return v22;
}

许可字段校验(略)

todo

总结

简而言之,sha256+rsa

取许可中的payload,json对象序列化字符串,计算该字符串的sha256,

取许可中的signature的值,hexstr转bytes,rsa解密后取[0x20:0x40] 与上面的哈希值进行比较

e=0x13

n_bytes_le='EDFD425CF978546E8911225884436C57140525650BCF6EBFE80EDBC5FB1DE68F4C66C29CB22EB668788AFCB0ABBB718044584B810F8970CDDF227385F75D5DDDD91D4F18937A08AA83B28C49D12DC92E7505BB38809E91BD0FBD2F2E6AB1D2E33C0C55D5BDDD478EE8BF845FCEF3C82B9D2929ECB71F4D1B3DB96E3A8E7AAF930000000000000000000000000000000000000000000000000000000000000000'

n=103708259528020238281968231491212855083623649579000552826541271154713103521021202133558709859463813691153402829135224007858959801854118729056356465578692294425903276554814675228955441044359462414007297799626945860003884709392298205606490444008955270902786809983042022666144385344717700223903132058765651803629

.rdata:00000001003CCB90 pub_modulus_1003CCB90 db 0EDh, 0FDh, 42h, 5Ch, 0F9h, 78h, 54h, 6Eh, 89h, 11h
.rdata:00000001003CCB90                                         ; DATA XREF: check_signature_100343A20+1F1↑o
.rdata:00000001003CCB90                 db 22h, 58h, 84h, 43h, 6Ch, 57h, 14h, 5, 25h, 65h, 0Bh
.rdata:00000001003CCB90                 db 0CFh, 6Eh, 0BFh, 0E8h, 0Eh, 0DBh, 0C5h, 0FBh, 1Dh, 0E6h
.rdata:00000001003CCB90                 db 8Fh, 4Ch, 66h, 0C2h, 9Ch, 0B2h, 2Eh, 0B6h, 68h, 78h
.rdata:00000001003CCB90                 db 8Ah, 0FCh, 0B0h, 0ABh, 0BBh, 71h, 80h, 44h, 58h, 4Bh
.rdata:00000001003CCB90                 db 81h, 0Fh, 89h, 70h, 0CDh, 0DFh, 22h, 73h, 85h, 0F7h
.rdata:00000001003CCB90                 db 2 dup(5Dh), 0DDh, 0D9h, 1Dh, 4Fh, 18h, 93h, 7Ah, 8
.rdata:00000001003CCB90                 db 0AAh, 83h, 0B2h, 8Ch, 49h, 0D1h, 2Dh, 0C9h, 2Eh, 75h
.rdata:00000001003CCB90                 db 5, 0BBh, 38h, 80h, 9Eh, 91h, 0BDh, 0Fh, 0BDh, 2Fh, 2Eh
.rdata:00000001003CCB90                 db 6Ah, 0B1h, 0D2h, 0E3h, 3Ch, 0Ch, 55h, 0D5h, 0BDh, 0DDh
.rdata:00000001003CCB90                 db 47h, 8Eh, 0E8h, 0BFh, 84h, 5Fh, 0CEh, 0F3h, 0C8h, 2Bh
.rdata:00000001003CCB90                 db 9Dh, 2 dup(29h), 0ECh, 0B7h, 1Fh, 4Dh, 1Bh, 3Dh, 0B9h
.rdata:00000001003CCB90                 db 6Eh, 3Ah, 8Eh, 7Ah, 0AFh, 93h, 20h dup(0)

参考链接

飘云阁IDA 9.1 & IDA 8.5 算法分析 - Powered by Discuz!

IDA-Keygen/keygen2.py at main · disbuted/IDA-Keygen

posted @ 2025-06-07 23:12  DirWangK  阅读(303)  评论(0)    收藏  举报