sublime_text_build_4169 分析

sublime_text 记录

sublime_text 4169 版本4.1.6.9

1、定位注册对话框

搜索"Enter License",

image-20240728150521384

函数交叉引用,定位到license_window

license_window_1400A25D2

_QWORD *__fastcall license_window_1400A25D2(_QWORD *a1, __int64 a2, int a3, __int64 a4)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v42 = 0xFFFFFFFFFFFFFFFEui64;
  sub_140005412();
  *a1 = &license_window::`vftable';
  a1[1] = &license_window::`vftable';
  a1[0x4D] = a4;
  v40 = a1;
  v35 = a1 + 0x52;
  sub_140032268(a1 + 0x52, a2);
  sub_1401642B0(v40, "dialog window");
  v8 = operator new(0x220ui64);
  v34 = 0i64;
  button_control = v8;
  sub_1401861A0(v8, &v34);
  v9 = button_control;
  v40[0x4E] = button_control;
  v29 = &unk_14075F4E1;
  v30 = "";
  (*(void (__fastcall **)(void *))(*(_QWORD *)v9 + 0x1E8i64))(v9);
  button_control = operator new(0x260ui64);
  v10 = &v25;
  string_140001FD0(&v25, "https://www.sublimehq.com/store/text");
  if ( v27 >= 0x10 )
    v10 = (__int128 *)v25;
  v36[0] = (__int64)v10;
  v36[1] = (__int64)v10 + (_QWORD)v26;
  v11 = button_control;
  sub_1401861A0(button_control, v36);
  *v11 = &link_label_control::`vftable';
  v11[1] = &link_label_control::`vftable';
  v11[0x4B] = 0i64;
  str_clear_140004410(&v25);
  *(_QWORD *)&v25 = ___7___Func_impl_no_alloc_V_lambda_2___0___0license_window__QEAA_AEBV__function___A6AXXZ_std__PEAVtext_control_environment__PEAUlicense_info___Z_X__V_std__6B_;
  *((_QWORD *)&v25 + 1) = v11;
  v28 = &v25;
  sub_1400328D4(&v25, v11 + 0x44);
  sub_140005A2A((__int64)&v25, v12);
  sub_1401642B0(v11, "label_control link_label");
  v38 = operator new(0x2B8ui64);
  sub_1400A2D10(v38, 1i64);
  v13 = v38;
  sub_14018881A(v38, v40[0x4E], 0i64);
  sub_14018881A(v13, v11, 1i64);
  button_control = operator new(0x368ui64);
  button_control_14015314C((__int64)button_control);
  v14 = button_control;
  v40[0x51] = button_control;
  *(_QWORD *)v31.u._Buf = "Use License";
  *(_QWORD *)&v31.u._Buf[8] = "";
  sub_140153C02((__int64)v14, (__int128 *)v31.u._Buf);
  v15 = v40[0x51];
  *(_QWORD *)&v25 = ___7___Func_impl_no_alloc_V_lambda_3___0___0license_window__QEAA_AEBV__function___A6AXXZ_std__PEAVtext_control_environment__PEAUlicense_info___Z_X__V_std__6B_;
  *((_QWORD *)&v25 + 1) = v40;
  v28 = &v25;
  sub_140153214(v15, &v25);
  sub_140005A2A((__int64)&v25, v16);
  button_control = operator new(0x3B8ui64);
  LOBYTE(v17) = 1;
  sub_14047E4F4((_DWORD)button_control, a3, v17, 0, 1);
  v18 = button_control;
  v40[0x4F] = button_control;
  v18[0x71] = "license:input";
  v39 = operator new(0x318ui64);
  sub_1400FDEDA(v39, v18, v18 + 0x22);
  v40[0x50] = v39;
  button_control = operator new(0x1B0ui64);
  sub_14017BDF0(button_control, 3i64, 1i64);
  v19 = button_control;
  (*(void (__fastcall **)(void *))(*(_QWORD *)button_control + 0x1F0i64))(button_control);
  (*(void (__fastcall **)(_QWORD *))(*v19 + 0x1E8i64))(v19);
  *(_DWORD *)(v19[0x26] + 4i64) = 0x3F800000;
  *(_DWORD *)v19[0x29] = 0x3F800000;
  sub_14017BF3E((_DWORD)v19, (_DWORD)v13, 0, 0, 0);
  sub_14017BF3E((_DWORD)v19, v40[0x50], 1, 0, 0);
  sub_14017BF3E((_DWORD)v19, v40[0x51], 2, 0, 0);
  sub_140162720(v40, v19);
  *(_QWORD *)&v25 = 0i64;
  *((_QWORD *)&v25 + 1) = 0x200i64;
  v26 = &v27;
  v20 = v36;
  sub_1400A134D(v36);
  if ( v37 >= 0x10 )
    v20 = (__int64 *)v36[0];
  v21 = sub_140122FEA(v20, &v25, 0x40000000i64);
  str_clear_140004410(v36);
  if ( v21 )
  {
    v22 = *(_QWORD *)(*(_QWORD *)(v40[0x4F] + 0x128i64) + 0x848i64);
    v32 = v26;
    v33 = (__int64)v26 + v25;
    v23 = v36;
    sub_140385275(v36);
    if ( v37 >= 4 )
      v23 = (__int64 *)v36[0];
    v31._Mysize = (size_t)v23;
    v31._Myres = (size_t)v23 + 4 * v36[2];
    sub_1403F3CEE(v22, 0, (unsigned int)&v31._Mysize, 0, 1);
    sub_140019D58(v36);
  }
  sub_1401309D8(&v25);
  return v40;
}

定位按钮事件lambda

image-20240728151319595

2、注册函数on_ok_clicked_license_window_1400A3F60

void __fastcall on_ok_clicked_license_window_1400A3F60(__int64 a1)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v23 = 0xFFFFFFFFFFFFFFFEui64;
  v1 = *(_QWORD *)(a1 + 8);
  gettext_14047A122(*(_QWORD *)(v1 + 0x278), (__int64)&v13[56]);
  memset(xor_str, 0, sizeof(xor_str));
  v18 = 0xFi64;
  v20 = 0i64;
  v19 = 0i64;
  v21 = 0xFi64;
  sub_1400A154A(*(_OWORD **)(v1 + 0x268), xor_str);
  sub_1400A0FC0(xor_str);
  if ( *(_QWORD *)&v13[72] )
  {
    // need ret 0
    v2 = check_lic_1400A19BC(
           &v14,
           (_QWORD *)(*(_QWORD *)(v1 + 0x268) + 0x10i64),
           *(_DWORD **)(v1 + 0x268),
           (unsigned int *)&v22,
           (int *)(*(_QWORD *)(v1 + 0x268) + 0xCi64),
           (_BYTE *)(*(_QWORD *)(v1 + 0x268) + 4i64));
    v3 = *(_QWORD *)(v1 + 0x268);
    *(_BYTE *)(v3 + 5) = v2 == 0;
    if ( v2 )
    {
      switch ( v2 )
      {
        case 1:
          // 这似乎是 Sublime Merge 许可证密钥,而不是 Sublime Text 密钥
          callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2EAF, 0i64);
          break;
        case 2:
          // 该许可证密钥似乎无效。
          callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2EBD, 0i64);
          break;
        case 3:
          // 许可证密钥已失效
          callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2ECB, 0i64);
          break;
        case 4:
          // 该许可证密钥因被共享而失效
          callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2ED9, 0i64);
          break;
        case 5:
          // 由于您的订阅已结束或已取消,许可证密钥已失效。
          callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2EE7, 0i64);
          break;
        default:
          break;
      }
    }
    else
    {
      vec_pop_14000233C((_QWORD *)(v3 + 0x30), &v14);
      string_140003006(&v15, &v14);
      v16 = v22;
      *(_QWORD *)xor_str = ___7___Func_impl_no_alloc_V_lambda_4___0__on_ok_clicked_license_window__QEAAXXZ_X__V_std__6B_;
      *(std_string *)&xor_str[8] = v15;
      v15._Mysize = 0i64;
      v15._Myres = 0xFi64;
      v15.u._Buf[0] = 0;
      LODWORD(v18) = v22;
      *((_QWORD *)&v19 + 1) = xor_str;
      sub_14020C932(xor_str, 0x1D4C0i64);
      sub_140005A2A((__int64)xor_str, v5);
      str_clear_140004410(&v15);
      // MachineGuid md5
      gen_lic_xor_data_1400A0FEE((__int64)xor_str);
      lic_data = &v14;
      // xor lic
      license_file_xor_1400A1307(*(std_string **)xor_str, &v14);
      sub_14000439A((std_vector_str *)xor_str);
      Ptr = v14.u._Ptr;
      lic_data_sz = v14._Mysize;
      Myres = v14._Myres;
      v10 = xor_str;
      save_lic_1400A134D((__int64)xor_str);
      if ( Myres >= 0x10 )
        lic_data = (std_string *)Ptr;
      if ( *(_QWORD *)&xor_str[0x18] >= 0x10ui64 )
        v10 = *(char **)xor_str;
      // 保存lic文件到
      // C:\Users\xxx\AppData\Roaming\Sublime Text\Local\License.sublime_license
      v11 = write_lic_1401230FC(v10, lic_data, lic_data_sz, 1);
      str_clear_140004410((std_string *)xor_str);
      if ( !v11 )
        callback_work_14013685C((__int64)&unk_1408E8998, (__int64)sub_1400A2D84, 0i64);
      v12 = thread_1401329DC(net_license_notification_1400A1583, (LPVOID)v22);
      CloseHandle(v12);
      if ( *(_BYTE *)(*(_QWORD *)(v1 + 0x268) + 4i64) )
        callback_work_14013685C((__int64)&unk_1408E8998, (__int64)purchasing_cb_1400A2E2E, 0i64);
      else
        callback_work_14013685C((__int64)&unk_1408E8998, (__int64)purchasing_cb_1400A2EA1, 0i64);
    }
  }
  else
  {
    v4 = xor_str;
    save_lic_1400A134D((__int64)xor_str);
    if ( *(_QWORD *)&xor_str[0x18] >= 0x10ui64 )
      v4 = *(_BYTE **)xor_str;
    sub_140123489(v4);
    str_clear_140004410((std_string *)xor_str);
  }
  if ( *(_QWORD *)(v1 + 0x2C8) )
    sub_140005A7E(v1 + 0x290);
  (*(void (__fastcall **)(_QWORD))(**(_QWORD **)(v1 + 0x28) + 0x18i64))(*(_QWORD *)(v1 + 0x28));
  str_clear_140004410(&v14);
}

check_lic_1400A19BC(to patch)

patch点 修改此函数返回0

该函数需返回0

image-20240728152916158

parse_lic_1405B0E48

解析lic格式,并进行签名校验

// 需返回1
__int64 __fastcall parse_lic_1405B0E48(
        std_string *input_lic,
        std_string *emb,
        std_string *out_username,
        int *lic_type,
        std_string *prefix,
        std_string *random,
        std_string *unknow)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v16 = -2ui64;
  *(_OWORD *)&lic_verify_data[96] = 0i64;
  *(_QWORD *)&lic_verify_data[112] = 0i64;
  *(_QWORD *)&lic_verify_data[120] = 0xFi64;
  if ( (unsigned __int8)split_1405B0F77(
                          input_lic,
                          out_username,
                          lic_type,
                          prefix,
                          random,
                          unknow,
                          (std_string *)&lic_verify_data[96]) )
  {
    // ret==>
    // Mifeng User
    // Single User License
    // EA7E-1184812
    User_License_1405B13B8(&v14, out_username, *lic_type, prefix, random, unknow);
    // ibtomcrypt中rsa_verify_hash
    // pkcs1_15 签名验证,sha1
    LOBYTE(v10) = verify_rsa_signature_1405B1B69(&v14, &data, emb);
    v11 = v10;
    str_clear_140004410(&v14);
  }
  else
  {
    v11 = 0;
  }
  str_clear_140004410(&data);
  return v11;
}

verify_rsa_signature_1405B1B69

bool __fastcall verify_rsa_signature_1405B1B69(std_string *userinfo, std_string *data, std_string *embed)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  // LibTomMath
  memmove(&qword_1408F3A20, &off_1406DE250, 0x1A0ui64);
  sub_1404E87B8(&off_1406DE0A0);
  stat = sub_1404E8754("sha1");
  sha1hash_sz = 0x80;
  Mysize = userinfo->_Mysize;
  if ( userinfo->_Myres >= 0x10 )
    userinfo = (std_string *)userinfo->u._Ptr;
  if ( (unsigned int)calc_sha1_1404E504C(stat, (__int64)userinfo, Mysize, (__int64)sha1hash, &sha1hash_sz) )
    return 0;
  v8 = SLODWORD(embed->_Mysize) / 2;
  if ( embed->_Myres >= 0x10 )
    embed = (std_string *)embed->u._Ptr;
  hexstr2bytes_1405B1B24((__int64)embed, (__int64)v16, v8);// hexstr??to bytes
  if ( (unsigned int)rsa_import_1404ED7E8((__int64)v16, v8, v13) )
    return 0;
  sigdata_sz = LODWORD(data->_Mysize) >> 1;
  if ( data->_Myres >= 0x10 )
    data = (std_string *)data->u._Ptr;
  hexstr2bytes_1405B1B24((__int64)data, (__int64)sigdata, sigdata_sz);
  pub_key_ = 0;
  // sig: 签名的二进制数据。
  // siglen: 签名的长度。
  // hash: 要验证的哈希值。
  // hashlen: 哈希值的长度。
  // padding: 填充方案(如LTC_PKCS_1_V1_5)。
  // hash_idx: 哈希算法的索引(如find_hash("sha256"))。
  // stat: 返回的验证状态(0表示验证失败,1表示验证成功)。
  // key: 公钥结构体。
  if ( (unsigned int)rsa_verify_hash_1404EE0D8(
                       (__int64)sigdata,
                       sigdata_sz,
                       (__int64)sha1hash,
                       sha1hash_sz,
                       1,
                       stat,
                       0,
                       &pub_key_,
                       (__int64)v13) )
    return 0;
  sub_1404ED778(v13);
  return pub_key_ == 1;
}

3、网络校验

patch 线程函数直接ret

1、net_check_license_1400A30E3

2、net_license_notification_1400A1583(可选,在check_lic_1400A19BC patch后可以忽略)

image-20240728160159474

net_check_license_1400A30E3(to patch)

image-20240728160244863

BOOL __fastcall clicked_license_net_check_1400A3752(_QWORD *a1, int a2)
{
  void *v4; // rax
  _DWORD *Block; // [rsp+20h] [rbp-10h]

  Block = operator new(0x28ui64);
  string_140003006(Block, a1);
  Block[8] = a2;
  v4 = (void *)thread_1401329DC(net_check_license_1400A30E3, Block);
  return CloseHandle(v4);
}

net_check_license_1400A30E3

image-20240728160359922

4、other

由于该n 未被分解factordb.com

因此需进行patch

image-20240728161227248

image-20240728161248297

License.sublime_license解析&签名验证脚本


import binascii
import hashlib
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA1


import winreg

EMBEDDED_XOR_DATA = bytes([0x87, 0x36, 0x2A, 0x87, 0xBA, 0xB1, 0xBE, 0x9D, 0x31, 0xFF,
                           0x31, 0x40, 0xBA, 0xB6, 0xB6, 0xB6, 0xB2, 0xB7, 0xB4, 0x36,
                           0x3C, 0xB7, 0x87, 0x36, 0x30, 0xB5, 0x36, 0x36, 0xB7, 0x6F,
                           0xCC, 0x15, 0xF2, 0xD5, 0x40, 0x72, 0x66, 0xFD, 0xBB, 0x4C,
                           0xA5, 0x0E, 0xC3, 0xBB, 0xAE, 0xEB, 0xDC, 0x6B, 0xC9, 0xDA,
                           0xD9, 0x7E, 0x9C, 0x1B, 0xB9, 0x05, 0x2A, 0xEE, 0x56, 0x6E,
                           0x19, 0xD0, 0x3E, 0xBB, 0x9C, 0x3F, 0x74, 0x1C, 0x6B, 0x18,
                           0x49, 0xCA, 0xFD, 0x84, 0x6B, 0x76, 0x08, 0x09, 0xE4, 0xAD,
                           0x92, 0xAB, 0x58, 0xBB, 0x25, 0x88, 0xB1, 0x09, 0xCE, 0x05,
                           0x85, 0x32, 0xEE, 0x1B, 0x49, 0x5E, 0x31, 0x62, 0x56, 0xE9,
                           0xFA, 0xA0, 0xD1, 0x5D, 0xE1, 0x73, 0x56, 0xB1, 0xE0, 0x4D,
                           0xC3, 0x6C, 0xBE, 0xC0, 0x74, 0x4C, 0xC2, 0x35, 0x00, 0x3B,
                           0x63, 0xCC, 0x05, 0x70, 0x4E, 0x05, 0xE5, 0x03, 0x1E, 0xF1,
                           0x8A, 0xA2, 0x41, 0x19, 0xD9, 0x5E, 0x94, 0xCA, 0xE3, 0x72,
                           0xFF, 0xAC, 0x44, 0x57, 0x07, 0x2E, 0x97, 0xAE, 0xBC, 0x78,
                           0x04, 0xA9, 0xEC, 0x52, 0xBE, 0x74, 0x8C, 0xB5, 0xB6, 0xA6])


def get_machine_guid():
    try:
        registry_key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Cryptography", 0, winreg.KEY_READ)
        value, regtype = winreg.QueryValueEx(registry_key, "MachineGuid")
        winreg.CloseKey(registry_key)
        return value
    except FileNotFoundError:
        return None


def int_to_bytes(n: int, order='little') -> bytes:
    # 获取整数的位长度
    bit_length = n.bit_length()
    # 计算所需的最小字节数
    byte_length = (bit_length + 7) // 8
    # 转换为字节序列,使用大端字节序
    byte_array = n.to_bytes(byte_length, byteorder=order)
    return byte_array


def import_rsa_der_pubkey(binary_data):
    try:
        # 从二进制数据中导入RSA密钥
        rsa_key = RSA.import_key(binary_data)
        print("RSA密钥导入成功!")
        print('e:', rsa_key.e)
        print('n:', rsa_key.n)
        return rsa_key
    except ValueError as e:
        print(f"导入RSA密钥失败: {e}")


def verify_rsa_signature(public_key, signature, message):
    try:
        # 导入公钥
        # public_key = RSA.import_key(public_key_pem)

        # 计算消息的哈希值(使用SHA-1)
        h = SHA1.new(message)

        # 验证签名
        pkcs1_15.new(public_key).verify(h, signature)
        print("签名验证成功!")
        return True
    except (ValueError, TypeError) as e:
        print(e)
        print("签名验证失败!")
        return False


def get_embedded_pubkey() -> str:
    ret = b''
    for i in range(0xa0):
        x = (EMBEDDED_XOR_DATA[i] ^ 0xb7) & 0xff
        ret += x.to_bytes(1, 'little')
    ret = ret.hex().upper()
    return ret


EMBEDDED_PUBKEY_STR = get_embedded_pubkey()


'''
#失效的lic,只为检查签名验证
—– BEGIN LICENSE —–
Mifeng User
Single User License
EA7E-1184812
C0DAA9CD 6BE825B5 FF935692 1750523A
EDF59D3F A3BD6C96 F8D33866 3F1CCCEA
1C25BE4D 25B1C4CC 5110C20E 5246CC42
D232C83B C99CCC42 0E32890C B6CBF018
B1D4C178 2F9DDB16 ABAA74E5 95304BEF
9D0CCFA9 8AF8F8E2 1E0A955E 4771A576
50737C65 325B6C32 817DCB83 A7394DFA
27B7E747 736A1198 B3865734 0B434AA5
—— END LICENSE ——

'''


def test():

    print('embedded_pubkey:', EMBEDDED_PUBKEY_STR)
    x = hashlib.sha256(EMBEDDED_PUBKEY_STR.encode()).digest()
    print('sha256 embedded key:', x.hex())
    '''
    sha256[1] ^ 0x34 | sha256[0xD] ^ 0xD7 | sha256[0x1E] ^ 0x56
    '''
    if (x[1] ^ 0x34 | x[0xD] ^ 0xD7 | x[0x1E] ^ 0x56) != 0:
        print('embedded_pubkey check sha256 error!')
    else:
        print('check embedded_pubkey sha256 success!')
    '''
Mifeng User
Single User License
EA7E-1184812
    '''
    userinfo = binascii.a2b_hex(
        '4D6966656E6720557365720A53696E676C652055736572204C6963656E73650A454137452D31313834383132')
    msg_sha1 = hashlib.sha1(userinfo).digest()
    print('[#]msg_sha1:', msg_sha1.hex())

    rsa_key = import_rsa_der_pubkey(binascii.a2b_hex(EMBEDDED_PUBKEY_STR))
    user_data = binascii.a2b_hex(
        'C0DAA9CD6BE825B5FF9356921750523AEDF59D3FA3BD6C96F8D338663F1CCCEA1C25BE4D25B1C4CC5110C20E5246CC42D232C83BC99CCC420E32890CB6CBF018B1D4C1782F9DDB16ABAA74E595304BEF9D0CCFA98AF8F8E21E0A955E4771A57650737C65325B6C32817DCB83A7394DFA27B7E747736A1198B38657340B434AA5')

    print(
        '\n###############################[manual verify]###############################')
    i_data = bytes_to_long(user_data)
    data = pow(i_data, rsa_key.e, rsa_key.n)
    # print('rsa dec:',int_to_bytes(data).hex())

    # 解析解密后的数据
    # 对于PKCS#1 v1.5 签名,结构是:
    # 00 01 ff ... ff 00 [ASN.1编码的哈希算法ID和哈希值]
    # 查找ASN.1编码的起始位置(0x00之后)
    data = long_to_bytes(data)
    start_idx = data.index(b'\x00', 2) + 1
    asn1_encoded = data[start_idx:]

    dec_msg = asn1_encoded[-20:]
    print('[#]dec_msg:', dec_msg.hex())
    print('msg_sha1==dec_msg', msg_sha1 == dec_msg)
    print(
        '###############################[manual verify end]###############################\n\n')

    print('pkcs1_15 verify:')
    verify_rsa_signature(rsa_key, user_data, userinfo)


def calc_lic_xor_table() -> bytes:
    # MachineGuid=b'692a3bbc-1b8b-44c2-b95f-8a18dc8b5664'
    MachineGuid = get_machine_guid().encode()
    hs = hashlib.md5(MachineGuid).digest()
    return hs


XOR_TABLE = calc_lic_xor_table()


def lic_file_parse(path: str):
    with open(path, 'rb') as f:
        data = bytearray(f.read())
    for i in range(len(data)):
        data[i] ^= XOR_TABLE[i & 0xf]
    print("License.sublime_license dec:")
    print(data.decode(encoding='utf-8'))


if __name__ == "__main__":
    # path=r'C:\Users\xxxx\AppData\Roaming\Sublime Text\Local\License.sublime_license'
    # lic_file_parse(path)
    test()
    pass

posted @ 2024-07-28 16:58  DirWangK  阅读(6)  评论(0编辑  收藏  举报