EmEditor 24.4.1 離線註冊分析

DirWangK發表於2024-11-09

EmEditor Version 24.4.1 離線註冊分析

目錄
  • EmEditor Version 24.4.1 離線註冊分析
  • 0、註冊金鑰Regkey 格式本地驗證流程
    • emeddlgs.dll==>DoRegistDlg
      • 資源資訊
      • CRegisterDlg
      • proc_18039905C
      • 按鈕事件on_ok_180399C2C
      • check_18039ACEC
        • check_key_1803A1A60
  • 1、離線註冊
    • StoreOfflineLicenseAndValidate_140E5CD80
    • do_StoreOfflineLicenseAndValidate_14175E1F0
    • ValidateDevice_14175F1D0
      • ValidateOfflineLicense_14175EA60
      • decode_LicenseFileClaims_141856680
        • get_es384pub_key_141893580
  • 3、驗證
    • 生成OfflineLicense jwt
    • patch公鑰
    • patch完整性校驗(可選)
      • sub_141767690
    • 離線註冊
  • ps

註冊金鑰需要以網路驗證的方式進行註冊,離線無法註冊

故,不作分析,只保留本地對註冊金鑰格式的本地校驗過程分析

如其官網所述,There are two online registration flows, depending on if a registration key is used (RegisterDevice()) or a Stripe subscription is used (RegisterDeviceSubscription()). The third registration flow is for offline registration (StoreOfflineLicenseAndValidate()).

線上註冊分為:

RegisterDevice

RegisterDeviceSubscription

本文主要對StoreOfflineLicenseAndValidate 離線註冊方案分析

官網離線註冊說明:

EmEditor (Text Editor)新驗證系統說明

0、註冊金鑰Regkey 格式本地驗證流程

RegisterDevice對應選擇註冊金鑰

RegisterDeviceSubscription對應 選擇登入

image-20241109174254474

透過呼叫棧可回溯到emeddlgs.dll匯出函式DoRegistDlg

image-20241024193830860

匯出函式

image-20241109144526073

emeddlgs.dll==>DoRegistDlg

__int64 __fastcall DoRegistDlg(HWND a1, char a2)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v10[1] = 0i64;
  lpMem = 0i64;
  v12 = 0i64;
  v13 = 0;
  v14 = 0;
  v10[0] = &CRegisterDlg::`vftable';
  sub_1803E4D60(v15, 0, 0x50ui64);
  v16 = 0i64;
  v17 = 0i64;
  v18 = 0i64;
  v19 = 0i64;
  v20 = 0;
  sub_1803E4D60(v21, 0, 0x208ui64);
  sub_1803E4D60(v22, 0, 0xA0ui64);
  v23 = 0i64;
  v24 = 0;
  v25 = 0;
  sub_1803E4D60(v26, 0, 0xC8ui64);
  sub_1803E4D60(v27, 0, 0x320ui64);
  sub_1803E4D60(v28, 0, 0xC8ui64);
  v29 = 0;
  v30 = 0;
  v31 = 0;
  v33 = 0;
  v34 = 0;
  v32 = a2;
  ActiveWindow = GetActiveWindow();
  v5 = sub_18039B8F8(v10, ActiveWindow);
  v6 = v5;
  while ( v5 == 6 )
  {
    // 354,    "您的授權將在 %d 天后到期。\n請更新您的授權,並輸入新的註冊金鑰或登入。\n如果您沒有輸入新的註冊金鑰或登入,EmEditor 將降級為免費版。"
    LoadStringW(hLibModule, 353u, Buffer, 0x14A);
    x_sprintf__1802E3F78(v9, 0x28, L"%02d/%04d");
    x_sprintf__1802E3F78(v36, 0x14A, Buffer, v9);
    x_dialog_180374CC4(a1, v36, 0x30);
    v7 = GetActiveWindow();
    v5 = sub_18039B8F8(v10, v7);
    v6 = v5;
  }
  if ( lpMem )
    sub_1803AF8EC(lpMem);
  return v6;
}

資源資訊

安裝目錄下mui\2052 存放簡體中文相關資源

image-20241024202549275

註冊產品

image-20241024202657459

’關於‘

image-20241024202721220

CRegisterDlg

關注其訊息處理函式proc_18039905C

.rdata:000000018054CD68 ; class CRegisterDlg: CMyDialogImpl<CRegisterDlg,ATL::CWindow>, ATL::CDialogImpl<ATL::CWindow,ATL::CWindow>, ATL::CDialogImplBaseT<ATL::CWindow>, ATL::CWindowImplRoot<ATL::CWindow>, ATL::CWindow, ATL::CMessageMap;  [MI] (#classinformer)
.rdata:000000018054CD68                 dq offset const CRegisterDlg::`RTTI Complete Object Locator'
.rdata:000000018054CD70 const CRegisterDlg::`vftable' dq offset proc_18039905C
.rdata:000000018054CD70                                         ; DATA XREF: DoRegistDlg+36↑o
.rdata:000000018054CD78                 dq offset sub_18039B8B8
.rdata:000000018054CD80                 dq offset sub_1803191F4
.rdata:000000018054CD88                 dq offset _guard_check_icall_nop

proc_18039905C

__int64 __fastcall proc_18039905C(CRegisterDlg *a1, __int64 a2, int msg, __int64 id, __int64 a5, __int64 *a6, int a7)
{
  unsigned int v7; // edi
  CRegisterDlg *v10; // rbx
  __int64 v11; // rax
  const WCHAR *p_key_text_0_98; // r8
  __int64 qword30; // rax

  v7 = 0;
  v10 = a1;
  if ( a7 )
    return 0i64;
  switch ( msg )
  {
    case WM_INITDIALOG:
      *(a1->qword30 + 0x34i64) = 1;
      v11 = sub_180399964(a1);
LABEL_15:
      *a6 = v11;
      goto LABEL_30;
    case WM_HELP:
      *(a1->qword30 + 0x34i64) = 1;
      goto LABEL_8;
    case WM_SYSCOMMAND:
      *(a1->qword30 + 0x34i64) = 1;
      if ( id != 0xF180 )
      {
        *(a1->qword30 + 0x34i64) = 0;
        goto LABEL_29;
      }
LABEL_8:
      sub_180306994(a1->phwnd__8, 0x1B81);
LABEL_29:
      *a6 = 0i64;
LABEL_30:
      if ( *(v10->qword30 + 0x34i64) )
        return 1i64;
LABEL_31:
      LOBYTE(v7) = sub_18031905C(v10, a2, msg, id, a5, a6, 0) != 0;
      return v7;
    case WM_TIMER:
      *(a1->qword30 + 0x34i64) = 1;
      if ( id != 0x64 )
        goto LABEL_29;
      KillTimer(a1->phwnd__8, 0x64ui64);
      v10->gap2DE[0x566] = 0;
      goto LABEL_38;
    case WM_APP|WM_SYNCPAINT|WM_ACTIVATE:       // 808E
      *(a1->qword30 + 0x34i64) = 1;
      v11 = sub_18039B0B4(a1, a2, id, id);
      goto LABEL_15;
    case WM_COMMAND:
      if ( !WORD1(id) )
      {
        //    CONTROL "輸入註冊金鑰(&K)", 2106, BUTTON, BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP, 16, 20, 296, 10 
        if ( id == 2106 )
        {
          *(a1->qword30 + 0x34i64) = 1;
          if ( a1->isLogin_848 != 1 )
            goto LABEL_29;
          GetDlgItemTextW(a1->phwnd__8, 0x643, &a1->key_text_1_2DC, 0x50);
          p_key_text_0_98 = &v10->key_text_0_98;
          v10->isLogin_848 = 0;
        }
        else
        {
          //    CONTROL "登入(&S)", 2107, BUTTON, BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 16, 34, 296, 10 
          if ( id != 2107 )
          {
            if ( id != 2185 )
            {
              if ( id == 1 )
              {
                // 確定
                *(a1->qword30 + 0x34i64) = 1;
                on_ok_180399C2C(a1);
              }
              else
              {
                if ( id != 2 )
                  goto LABEL_31;
                // 取消
                *(a1->qword30 + 0x34i64) = 1;
                EndDialog(a1->phwnd__8, 2i64);
              }
              goto LABEL_29;
            }
            *(a1->qword30 + 0x34i64) = 1;
            goto LABEL_39;
          }
          *(a1->qword30 + 0x34i64) = 1;
          if ( a1->isLogin_848 )
            goto LABEL_29;
          GetDlgItemTextW(a1->phwnd__8, 0x643, &a1->key_text_0_98, 0x1E);
          p_key_text_0_98 = &v10->key_text_1_2DC;
          v10->isLogin_848 = 1;
        }
        SetDlgItemTextW(v10->phwnd__8, 0x643, p_key_text_0_98);
LABEL_38:
        a1 = v10;
LABEL_39:
        sub_18039946C(a1);
        goto LABEL_29;
      }
      if ( WORD1(id) != 0x300 )
        goto LABEL_31;
      if ( id != 0x643 )
      {
        if ( id != 0x7EB && id != 0x3F3 )       // 7eb-->email_id
                                                // 3f3 -->name_id
                                                // 
          goto LABEL_31;
        qword30 = a1->qword30;
        goto LABEL_37;
      }
      *(a1->qword30 + 0x34i64) = 1;
      sub_18039946C(a1);
      *a6 = 0i64;
      qword30 = v10->qword30;
      if ( !*(qword30 + 0x34) )
      {
LABEL_37:
        *(qword30 + 0x34) = 1;
        goto LABEL_38;
      }
      break;
    case WM_NOTIFY:
      if ( *(a5 + 0x10) != 0xFFFFFFFE
        || *(a5 + 8) != 0x7EFi64
        || (*(a1->qword30 + 0x34i64) = 1, *a6 = sub_18039AFB8(a1), !*(v10->qword30 + 0x34i64)) )
      {
        if ( *(a5 + 0x10) != 0xFFFFFFFC )
          goto LABEL_31;
        if ( *(a5 + 8) != 0x7EFi64 )
          goto LABEL_31;
        *(v10->qword30 + 0x34i64) = 1;
        *a6 = sub_18039AFB8(v10);
        if ( !*(v10->qword30 + 0x34i64) )
          goto LABEL_31;
      }
      break;
    default:
      goto LABEL_31;
  }
  return 1i64;
}

按鈕事件on_ok_180399C2C

UINT __fastcall on_ok_180399C2C(CRegisterDlg *a1)
{
  _DWORD *p_isLogin_848; // rbx
  HWND v3; // rcx
  _QWORD *v4; // rax
  UINT result; // eax
  __int64 v6[2]; // [rsp+30h] [rbp-28h] BYREF
  _Thrd_t v7; // [rsp+40h] [rbp-18h] BYREF

  p_isLogin_848 = &a1->isLogin_848;
  a1->isLogin_848 = IsDlgButtonChecked(a1->phwnd__8, 2107) == 1;
  // get email
  GetDlgItemTextW(a1->phwnd__8, 0x7EB, &a1->email_text_D4, 0x104);
  v3 = a1->phwnd__8;
  if ( *p_isLogin_848 == 1 )
  {
    GetDlgItemTextW(v3, 0x643, &a1->key_text_1_2DC, 0x50);
    a1->gap2DE[0x567] = 1;
    sub_18039946C(a1);
    v4 = sub_1803B0DB0(8i64);
    *v4 = a1;
    // net check  cb
    v6[0] = sub_1803CA080(0i64, 0i64, sub_18039BC58, v4, 0, &v6[1]);
    if ( !v6[0] )
    {
      LODWORD(v6[1]) = 0;
      sub_1803B0884(6);
    }
    if ( !LODWORD(v6[1]) || (v7 = *v6, (result = Thrd_detach(&v7)) != 0) )
      sub_1803B0884(1);
  }
  else
  {
    // 0x643 -->key id
    GetDlgItemTextW(v3, 0x643, &a1->key_text_0_98, 0x1E);
    result = GetDlgItemTextW(a1->phwnd__8, 0x3F3, &a1->field_48, 0x28);
    if ( a1->key_text_0_98 )
    {
      check_18039ACEC(a1);
      sub_180386890(L"Email");
      return sub_180386890(L"Token");
    }
  }
  return result;
}

check_18039ACEC

int __fastcall check_18039ACEC(CRegisterDlg *a1)
{
  WCHAR *p_key_text_0_98; // rdi
  unsigned int v3; // r14d
  int result; // eax
  __int64 *v5; // rdx
  __int64 v6; // rdx
  INT_PTR v7; // rdx
  const wchar_t *v8; // rdi
  int v9; // eax
  char v10; // r9
  unsigned __int16 v11; // dx
  _BYTE v12[40]; // [rsp+40h] [rbp-C8h] BYREF
  __int64 v13[4]; // [rsp+68h] [rbp-A0h] BYREF
  __int16 v14[336]; // [rsp+88h] [rbp-80h] BYREF
  WCHAR Buffer[304]; // [rsp+328h] [rbp+220h] BYREF

  p_key_text_0_98 = &a1->key_text_0_98;
  v3 = check_key_1803A1A60(&a1->key_text_0_98);
  // 0x4a  ==>1001010

  // 6 3 1


  if ( v3 <= 6 && (result = 0x4A, _bittest(&result, v3)) )
  {
    // 1,3,6 時
    if ( *p_key_text_0_98 && LOWORD(a1->field_48) && LOWORD(a1->email_text_D4) )
    {
      sub_18039ABD8(a1, v13);
      memset(&v12[8], 0, 0x20);
      v5 = v13;
      if ( v13[3] > 7ui64 )
        v5 = v13[0];
      sub_1802EAA2C(&v12[8], v5, v13[2]);
      sub_180316CB0(a1->phwnd__8, v6, p_key_text_0_98, &a1->field_48, &a1->email_text_D4, byte_1805DDB28 != 0, &v12[8]);
      v7 = 1i64;
      if ( v3 == 6 )
        v7 = 6i64;
      EndDialog(a1->phwnd__8, v7);
      return sub_1802E9990(v13);
    }
  }
  else if ( v3 == 5 )
  {
    //  227,   "本程式沒有被正確安裝。請解除安裝後重新安裝本程式。"
    dialog_180374C98(a1->phwnd__8, 227u, 0x10);
    return EndDialog(a1->phwnd__8, 2i64);
  }
  else
  {
    // 2,4,
    if ( StrCmpNW(p_key_text_0_98, L"17", 2) )
    {
      if ( StrCmpNW(p_key_text_0_98, L"18", 2) )
      {
        if ( StrCmpNW(p_key_text_0_98, L"v9", 2) )
        {
          if ( StrCmpNW(p_key_text_0_98, L"A", 1) )
          {
            if ( StrCmpNW(p_key_text_0_98, L"B", 1) )
            {
              v9 = StrCmpNW(p_key_text_0_98, L"C", 1);
              v8 = L"EmEditor Professional v13";
              if ( v9 )
                v8 = 0i64;
            }
            else
            {
              v8 = L"EmEditor Professional v12";
            }
          }
          else
          {
            v8 = L"EmEditor Professional v10/11";
          }
        }
        else
        {
          v8 = L"EmEditor Professional v9";
        }
      }
      else
      {
        v8 = L"EmEditor Professional v8";
      }
    }
    else
    {
      v8 = L"EmEditor Standard";
    }
    if ( v8 )
    {
      //  1136,  "您輸入的註冊金鑰僅用於%s。請重新輸入以字母 D 開頭的 EmEditor 專業版 v14/v15/v16/v17 的註冊金鑰。請問您是否需要開啟網路瀏覽器並檢視升級或購買資訊?"
      LoadStringW(hLibModule, 1136u, Buffer, 0x12C);
      x_sprintf__1802E3F78(v14, 330i64, Buffer, v8);
      result = x_dialog_180374CC4(a1->phwnd__8, v14, 0x34);
      if ( result == 6 )
      {
        // purchase
        sub_18030603C(v14, 330u, 0x1772);
        return sub_180380778(a1->phwnd__8, v14, 0i64, v10);
      }
    }
    else
    {
      //   1137,     "使用該註冊金鑰註冊程式時出現一個錯誤。"
      v11 = 1137;
      // 1108,   "您輸入了一個錯誤的註冊金鑰。請重新輸入以字母 D 開頭的 EmEditor 專業版 v14/v15/v16/v17 的註冊金鑰。"
      if ( v3 == 2 )
        v11 = 1108;
      return dialog_180374C98(a1->phwnd__8, v11, 0x30);
    }
  }
  return result;
}

check_key_1803A1A60

// local variable allocation has failed, the output may be wrong!
__int64 __fastcall check_key_1803A1A60(wchar_t *key)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v55[4] = 0;
  // .rdata:000000018054CFE0 dword_18054CFE0 dd 9                    ; DATA XREF: check_key_1803A1A60+26↑r
  // .rdata:000000018054CFE4                 dd 3
  // .rdata:000000018054CFE8                 dd 0
  // .rdata:000000018054CFEC                 dd 4
  *table = _mm_load_si128(&xmmword_18054CFE0);
  v2 = 1;
  table[0xC] = 0xB;
  // .rdata:000000018054D020 dword_18054D020 dd 2                    ; DATA XREF: check_key_1803A1A60+5A↑r
  // .rdata:000000018054D024                 dd 8
  // .rdata:000000018054D028                 dd 7
  // .rdata:000000018054D02C                 dd 10h
  *&table[8] = _mm_load_si128(&xmmword_18054D020);
  table[0xD] = 0xF;
  // .rdata:000000018054D040 dword_18054D040 dd 3                    ; DATA XREF: check_key_1803A1A60+74↑r
  // .rdata:000000018054D044                 dd 0Ah
  // .rdata:000000018054D048                 dd 1
  // .rdata:000000018054D04C                 dd 15h
  *v50 = _mm_load_si128(&xmmword_18054D040);
  table[0xE] = 0xE;
  // .rdata:000000018054D030 dword_18054D030 dd 8                    ; DATA XREF: check_key_1803A1A60+8B↑r
  // .rdata:000000018054D034                 dd 2
  // .rdata:000000018054D038                 dd 1Eh
  // .rdata:000000018054D03C                 dd 12h
  *v51 = _mm_load_si128(&xmmword_18054D030);
  v50[4] = 6;
  // .rdata:000000018054D050 dword_18054D050 dd 17h                  ; DATA XREF: check_key_1803A1A60+9F↑r
  // .rdata:000000018054D054                 dd 0Eh
  // .rdata:000000018054D058                 dd 5
  // .rdata:000000018054D05C                 dd 19h
  *v52 = _mm_load_si128(&xmmword_18054D050);
  v50[5] = 0x1F;
  // .rdata:000000018054CF70 dword_18054CF70 dd 3                    ; DATA XREF: check_key_1803A1A60+B3↑r
  // .rdata:000000018054CF74                 dd 1
  // .rdata:000000018054CF78                 dd 4
  // .rdata:000000018054CF7C                 dd 0
  *v54 = _mm_load_si128(&xmmword_18054CF70);
  v50[6] = 0x1C;
  // .rdata:000000018054CFD0 dword_18054CFD0 dd 1                    ; DATA XREF: check_key_1803A1A60+CA↑r
  // .rdata:000000018054CFD4                 dd 4
  // .rdata:000000018054CFD8                 dd 2
  // .rdata:000000018054CFDC                 dd 3
  *v55 = _mm_load_si128(&xmmword_18054CFD0);
  v51[4] = 0x1D;
  // .rdata:000000018054CFC0 dword_18054CFC0 dd 4                    ; DATA XREF: check_key_1803A1A60+E1↑r
  // .rdata:000000018054CFC4                 dd 0
  // .rdata:000000018054CFC8                 dd 2
  // .rdata:000000018054CFCC                 dd 3
  *v56 = _mm_load_si128(&xmmword_18054CFC0);
  v51[5] = 9;
  // .rdata:000000018054D000 dword_18054D000 dd 0Ah                  ; DATA XREF: check_key_1803A1A60+33↑r
  // .rdata:000000018054D004                 dd 5
  // .rdata:000000018054D008                 dd 1
  // .rdata:000000018054D00C                 dd 6
  *&table[4] = _mm_load_si128(&xmmword_18054D000);
  v51[6] = 0x1A;
  v52[4] = 0x14;
  v52[5] = 7;
  v52[6] = 0x16;
  v54[4] = 2;
  v56[4] = 1;
  wmemcpy(
    v49[0],
    L"2T3YKWEPXB8FQJLU5HZS4V7NDG9AC6RM36DRSQPXJCLAU5HZM74GBTKE9WVN2Y8FMAK7D5T3Y8NFQJLUCVHZ6RG942XBWEPS",
    0x60);
  if ( lstrlenW(key) != 29 )
    return 2i64;
  v3 = 0i64;
  v4 = 1;
  do
  {
    v5 = key[v3];
    if ( v4 == 6 * (v4 / 6) )
    {
      if ( v5 != '-' )
        return 2i64;
    }
    // 不含O 和I
    else if ( (v5 - 'A') > 25u && (v5 - '2') > 7u || v5 == 'O' || v5 == 'I' )
    {
      return 2i64;
    }
    ++v4;
    ++v3;
  }
  while ( v3 < 0x1D );
  if ( *key != 'D' )
    return 2i64;
  v7 = 0i64;
  memset(temp1, 0, sizeof(temp1));
  do
  {
    // {
    // 0x00000009, 0x00000003, 0x00000000, 0x00000004, 0x0000000A, 0x00000005, 0x00000001, 0x00000006,
    //  0x00000002, 0x00000008, 0x00000007, 0x00000010, 0x0000000B, 0x0000000F, 0x0000000E
    // };
    temp1[v7] = key[table[v7]];
    ++v7;
  }
  while ( v7 < 15 );
  temp1[0xF] = 0;
  // 15個字元
  md5_1803A1760(md5_hexstr, temp1);
  if ( lstrlenW(md5_hexstr) != 32 )
    return 2i64;
  *temp2 = 0i64;
  *&SystemTime = 0i64;
  v8 = 0i64;
  *(&SystemTime + 2) = 0;
  *out1_ = 0i64;
  v47 = 0;
  do
  {
    //   // .rdata:000000018054D040 dword_18054D040 dd 3                    ; DATA XREF: check_key_1803A1A60+74↑r
    //   // .rdata:000000018054D044                 dd 0Ah
    //   // .rdata:000000018054D048                 dd 1
    //   // .rdata:000000018054D04C                 dd 15h
    temp2[v8] = md5_hexstr[v50[v8]];
    ++v8;
  }
  while ( v8 < 7 );
  temp2[7] = 0;
  v9 = wcstoul_1803CDBE8(temp2, 0i64, 0x10i64);
  v10 = 0;
  for ( i = 0i64; i < 5; out1[i++] = v49[0][v15] )
  {
    v12 = 4 - v10;
    if ( v10 == 4 )
    {
      v13 = 1;
    }
    else
    {
      v13 = 32;
      if ( v12 > 1 )
      {
        v14 = v12 - 1;
        do
        {
          v13 *= 0x20;
          --v14;
        }
        while ( v14 );
      }
    }
    ++v10;
    v15 = (v9 / v13) & 31;
    v9 %= v13;
  }
  v16 = 0i64;
  out1[v10] = 0;
  do
  {
    // 3,1,4,0,2
    out1_[v16] = out1[v54[v16]];
    ++v16;
  }
  while ( v16 < 2 );
  out1_[2] = 0;
  if ( wstr_cmp_1803D1D0C(key + 12, out1_, 2i64) )
    return 2i64;
  for ( j = 0i64; j < 7; ++j )
    // dd 8                
    // dd 2
    // dd 1Eh
    // dd 12h
    // 0x1d
    // 0x9
    // 0x1a
    temp2[j] = md5_hexstr[v51[j]];
  temp2[7] = 0;
  v18 = wcstoul_1803CDBE8(temp2, 0i64, 0x10i64);
  v19 = 0;
  for ( k = 0i64; k < 5; out1[k++] = v49[1][v24] )
  {
    v21 = 4 - v19;
    if ( v19 == 4 )
    {
      v22 = 1;
    }
    else
    {
      v22 = 32;
      if ( v21 > 1 )
      {
        v23 = v21 - 1;
        do
        {
          v22 *= 32;
          --v23;
        }
        while ( v23 );
      }
    }
    ++v19;
    v24 = (v18 / v22) & 0x1F;
    v18 %= v22;
  }
  v25 = 0i64;
  out1[v19] = 0;
  do
  {
    // 1,4,2,3,0
    out1_[v25] = out1[v55[v25]];
    ++v25;
  }
  while ( v25 < 5 );
  HIWORD(v47) = 0;
  if ( wstr_cmp_1803D1D0C(key + 0x12, out1_, 5i64) )
    return 2i64;
  for ( m = 0i64; m < 7; ++m )
    // dd 17h
    // dd 0Eh
    // dd 5
    // dd 19h
    // 0x14
    // 7
    // 0x16
    temp2[m] = md5_hexstr[v52[m]];
  temp2[7] = 0;
  v27 = wcstoul_1803CDBE8(temp2, 0i64, 0x10i64);
  v28 = 0;
  for ( n = 0i64; n < 5; out1[n++] = v49[2][v33] )
  {
    v30 = 4 - v28;
    if ( v28 == 4 )
    {
      v31 = 1;
    }
    else
    {
      v31 = 32;
      if ( v30 > 1 )
      {
        v32 = v30 - 1;
        do
        {
          v31 *= 0x20;
          --v32;
        }
        while ( v32 );
      }
    }
    ++v28;
    v33 = (v27 / v31) & 0x1F;
    v27 %= v31;
  }
  v34 = 0i64;
  out1[v28] = 0;
  do
  {
    // 4,0,2,3,1
    out1_[v34] = out1[v56[v34]];
    ++v34;
  }
  while ( v34 < 5 );
  HIWORD(v47) = 0;
  if ( wstr_cmp_1803D1D0C(key + 0x18, out1_, 5i64) )
    return 2i64;
  if ( key[3] != 'Z' )
    return 3i64;
  v35 = key[1];
  v36 = key[2];
  if ( (v35 != 'E' || v36 != 'M')
    && (v35 != 'V' || v36 != 'E')
    && (v35 != 'S' || v36 != 'H')
    && (v35 != 'R' || v36 != 'N')
    && (v35 != 'P' || v36 != 'A')
    && (v35 != 'S' || v36 != 'B')
    && (v35 != 'M' || v36 != 'A')
    && (v35 != 'T' || v36 != 'H')
    && (v35 != 'V' || v36 != 'A') )
  {
    return 3i64;
  }
  v37 = key + 6;
  v38 = L"ZWT";
  if ( key[6] == 'Z' )
  {
    while ( *++v38 )
    {
      if ( *++v37 != *v38 )
        goto LABEL_82;
    }
  }
  else
  {
    v39 = L"WHY52";
    if ( *v37 != 'W' )
    {
      v40 = L"292ZX";
      v41 = '2';
      while ( *v37 == v41 )
      {
        v41 = *++v40;
        if ( !*v40 )
          return 3i64;
        ++v37;
      }
LABEL_82:
      v42 = sub_1803A1934(key);
      if ( v42 <= 0 )
        return v2;
      if ( v42 < sub_1803A186C(2024u, 10u, 20u) )
        return 6i64;
      v45 = 0i64;
      GetSystemTime(&v45);
      if ( v42 < sub_1803A186C(v45.wYear, v45.wMonth, v45.wDay) )
        return 6;
      return v2;
    }
    while ( *++v39 )
    {
      if ( *++v37 != *v39 )
        goto LABEL_82;
    }
  }
  return 3i64;
}

檢驗計算過程可參考如下python程式碼

格式xxxxx-xxxxx-xxxxx-xxxxx-xxxxx,5*5=25 加上4個’-‘,一共29個字元,2-9 A-Z 不能有'O' 和'I', 
只需關注前17個字元(前3組)
可固定位:
···
  if ( key[3] != 'Z' )
    return 3i64;
  v35 = key[1];
  v36 = key[2];
  if ( (v35 != 'E' || v36 != 'M')
    && (v35 != 'V' || v36 != 'E')
    && (v35 != 'S' || v36 != 'H')
    && (v35 != 'R' || v36 != 'N')
    && (v35 != 'P' || v36 != 'A')
    && (v35 != 'S' || v36 != 'B')
    && (v35 != 'M' || v36 != 'A')
    && (v35 != 'T' || v36 != 'H')
    && (v35 != 'V' || v36 != 'A') )
  {
    return 3i64;
  }
  v37 = key + 6;
  v38 = L"ZWT";
  if ( key[6] == 'Z' )
  {
    while ( *++v38 )
    {
      if ( *++v37 != *v38 )
        goto LABEL_82;
    }
  }
  else
  {
    v39 = L"WHY52";
    if ( *v37 != 'W' )
    {
      v40 = L"292ZX";
···
可固定的位如下:
DMAZ{}-WHY52-{}{}222
當然還有其它組合
import hashlib

tables="2T3YKWEPXB8FQJLU5HZS4V7NDG9AC6RM36DRSQPXJCLAU5HZM74GBTKE9WVN2Y8FMAK7D5T3Y8NFQJLUCVHZ6RG942XBWEPS"
table0=tables[:32]
table1=tables[32:32+32]
table2=tables[64:]

from datetime import datetime

def sub_1803A186C(year, month, day):
    try:
        # 基準日期 (1601-01-01)
        base_date = datetime(1601, 1, 1)
        # 目標日期
        target_date = datetime(year, month, day)
        # 計算兩個日期的天數差異
        days_difference = (target_date - base_date).days
        return days_difference 
    except ValueError:
        # 日期無效時返回最大值
        return 0x7FFFFFFF
    
def sub_1803A1934(part:list[str]) :
    v3=0
    for i in range(3):
        v5=ord(part[i])
        v6=0x20*i
        if (v5 - 0x32) > 7:
            if  (v5 - 0x41) > 7 :
                if  (v5 - 0x4A) > 4:
                    if  (v5 - 0x50) > 0xA:
                        return -1
                    v7 = v6 - 0x3B
                else:
                    v7 = v6 - 0x3A
            else:
            
                v7 = v6 - 0x39
        else:
        
            v7 = v6 - 0x32
        
        v3 = v5 + v7; 
    return v3
def calc_number_str(number_hexstr:str,count:int,table:list):

    if isinstance(number_hexstr,str):
        num=int(number_hexstr,16)
    elif isinstance(number_hexstr,int):
        num=number_hexstr
    else:
        raise TypeError('number_hexstr error!')
    # v18 = wcstoul_1803CDBE8(v48, 0i64, 0x10i64);
    out=[0]*count
    for i in range(count):
        r_n = 4 - i
        if  i == 4 :
            div_x = 1
        else:
            div_x = 32
            if r_n > 1 :
                for _ in range(r_n-1):
                    div_x*=32

        index = (num // div_x) & 0x1F
        num %= div_x
        out[i] = table[index]
    print("calc_number_str:",''.join(out))
    return out

'''
"DMAZM-WHY52-AX222-ZQJXN-79JXH"
'''
def check(key:str):
    temp1 = [0] * 15
    temp1[0] = key[9]
    temp1[1] = key[3]
    temp1[2] = key[0]
    temp1[3] = key[4]

    temp1[4] = key[10]
    temp1[5] = key[5]
    temp1[6] = key[1]
    temp1[7] = key[6]

    temp1[8] = key[2]
    temp1[9] = key[8]
    temp1[0xa] = key[7]
    temp1[0xb] = key[0x10]

    temp1[0xc] = key[0xb]
    temp1[0xd] = key[0xf]
    temp1[0xe] = key[0xe]
    # 計算 MD5
    md5_hexstr = hashlib.md5(''.join(temp1).encode()).hexdigest()

    if len(md5_hexstr) != 32:
        return 2
    temp2=[0]*7
    temp2[0]=md5_hexstr[3]
    temp2[1]=md5_hexstr[0xa]
    temp2[2]=md5_hexstr[1]
    temp2[3]=md5_hexstr[0x15]
    temp2[4]=md5_hexstr[6]
    temp2[5]=md5_hexstr[0x1f]
    temp2[6]=md5_hexstr[0x1c]
    n=int(''.join(temp2),16)
    print('n0:%08x'%n)
    out1=calc_number_str(n,5,table0)
    '''
    error :need SXFA4
    '''
    out1_=[0]*5
    out1_[0]=out1[3]
    out1_[1]=out1[1]
    out1_[2]=out1[4]
    out1_[3]=out1[0]
    out1_[4]=out1[2]
    s_out1_=''.join(out1_)
    print('out1:',s_out1_)
    print('key1:',key[12:12+2])
    print('check key[12:+2]==out1_[:2]:',key[12:12+2]==s_out1_[:2])


    temp2[0]=md5_hexstr[0x8]
    temp2[1]=md5_hexstr[0x2]
    temp2[2]=md5_hexstr[0x1e]
    temp2[3]=md5_hexstr[0x12]
    temp2[4]=md5_hexstr[0x1d]
    temp2[5]=md5_hexstr[0x9]
    temp2[6]=md5_hexstr[0x1a]
    n=int(''.join(temp2),16)
    print('n1:%08x'%n)
    out1=calc_number_str(n,5,table1)
    out1_[0]=out1[1]
    out1_[1]=out1[4]
    out1_[2]=out1[2]
    out1_[3]=out1[3]
    out1_[4]=out1[0]
    s_out1_=''.join(out1_)
    print('out2:',s_out1_)
    print('key2:',key[0x12:0x12+5])
    print('check key[0x12:+5]==out2_[:5]:',key[0x12:0x12+5]==s_out1_[:5])

    temp2[0]=md5_hexstr[0x17]
    temp2[1]=md5_hexstr[0xe]
    temp2[2]=md5_hexstr[5]
    temp2[3]=md5_hexstr[0x19]
    temp2[4]=md5_hexstr[0x14]
    temp2[5]=md5_hexstr[0x7]
    temp2[6]=md5_hexstr[0x16]
    n=int(''.join(temp2),16)
    print('n1:%08x'%n)
    out1=calc_number_str(n,5,table2)
    out1_[0]=out1[4]
    out1_[1]=out1[0]
    out1_[2]=out1[2]
    out1_[3]=out1[3]
    out1_[4]=out1[1]
    s_out1_=''.join(out1_)
    print('out3:',s_out1_)
    print('key3:',key[0x18:0x18+5])
    print('check key[0x18:+5]==out3_[:5]:',key[0x18:0x18+5]==s_out1_[:5])

    v42=sub_1803A1934(key[0xe:])
    print(v42)
    days=sub_1803A186C(2024,10,20)
    print('days:',days)

1、離線註冊

StoreOfflineLicenseAndValidate

透過CommandLineToArgvW定位到函式get_cmd_140EA8FB0,交叉引用定位到sub_140EA83F0內

再透過檔案讀取相關api 可定位StoreOfflineLicenseAndValidate_140E5CD80

StoreOfflineLicenseAndValidate_140E5CD80

void __fastcall StoreOfflineLicenseAndValidate_140E5CD80(__int64 a1, wchar_t *filepath, char a3)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  if ( !a3 && byte_141C3C28A )
    return;
  v10 = *(_OWORD *)filepath;
  do_StoreOfflineLicenseAndValidate_14175E1F0(&v14, (wchar_t *)&v10, a3);
  v13 = 0;
  if ( !v14.unknow_28 )
  {
    u = v14.wstr.u;
    v14.wstr.u._Buf[0] = 0;
    *(union std_wstring_union *)lpText = u;
    v12 = *(_OWORD *)&v14.wstr._Mysize;
    *(__m128i *)&v14.wstr._Mysize = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
LABEL_7:
    v13 = 1;
    sub_140337920(lpText, (__int64)L"\r\n", 2ui64);
    v6 = lpText;
    if ( *((_QWORD *)&v12 + 1) > 7ui64 )
      v6 = (LPCWSTR *)lpText[0];
    if ( Console_write_140E96360(v6) < 0 )
    {
      v7 = (const WCHAR *)lpText;
      if ( *((_QWORD *)&v12 + 1) > 7ui64 )
        v7 = lpText[0];
      MessageBoxW(0i64, v7, L"EmEditor", 0x10u);
    }
    v8 = (const WCHAR *)lpText;
    if ( *((_QWORD *)&v12 + 1) > 7ui64 )
      v8 = lpText[0];
    OutputDebugStringW(v8);
    goto LABEL_17;
  }
  if ( v14.wstr.u._Buf[0] != 1 )
  {
    v4 = *(__int128 *)((char *)&v14.wstr.u + 8);
    v5 = *(_OWORD *)&v14.wstr._Myres;
    v14.wstr.u._Buf[4] = 0;
    *(_OWORD *)lpText = v4;
    *(__m128i *)&v14.wstr._Myres = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
    v12 = v5;
    goto LABEL_7;
  }
  Console_write_140E96360(L"Offline registration successful.\r\n");
LABEL_17:
  if ( v13 )
    free_140337EE0((__int64)lpText);
  v9 = (ParseRet *)(&v14.wstr.u._Ptr + 1);
  if ( !v14.unknow_28 )
    v9 = &v14;
  free_140337EE0((__int64)v9);
}

do_StoreOfflineLicenseAndValidate_14175E1F0

呼叫rust庫介面,一言難盡

ParseRet *__fastcall do_StoreOfflineLicenseAndValidate_14175E1F0(ParseRet *a1, wchar_t *filepath, char a3)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  // 1、從登錄檔中刪除註冊金鑰、本地裝置令牌和離線許可證令牌。
  // 清理登錄檔
  clean_reg_140F04C50(0i64, (__int64)filepath);
  v22.u._Ptr = 0i64;
  xx_init_1418560C0((__int64)(&v22.u._Ptr + 1));
  v23 = 0i64;
  xx_init_1418560C0((__int64)v24);
  xx_init_1418560C0((__int64)v25);
  v26 = 0;
  xx_init_141855FC0((__int64)v27, (_QWORD *)&v22.u._Ptr + 1);
  v28 = v23;
  xx_init_141855FC0((__int64)v29, v24);
  xx_init_141855FC0((__int64)v30, v25);
  sub_14033BEE0((__int64)&v31, (__int64)&v26);
  free_1418560E0((__int64)v25);
  free_1418560E0((__int64)v24);
  free_1418560E0((__int64)(&v22.u._Ptr + 1));
  v6 = sub_14033C6D0((__int64)&v31);
  if ( (unsigned __int8)j_unknown_libname_3(v6) )
  {
    v7 = sub_14033C6D0((__int64)&v31);
    v8 = api::ffi::ResultAPI::value_141856E20(v7);
    v9 = sub_14033C6D0(v8);
    // 清除本地裝置令牌
    clean_LocalDeviceToken_140F04CA0(v9);
  }
  v21.u = *(union std_wstring_union *)filepath;
  // 2、讀取許可證檔案並將其儲存到登錄檔中。
  load_and_save_OfflineLicense_14175DC50((__int64)&v18, (__int64)&v21, a3);
  if ( v20 )
  {
    *((_QWORD *)&v21.u._Ptr + 1) = 0i64;
    v21.u._Ptr = (wchar_t *)byte_1419A9DB0;
    // 3、run ValidateDevice()
    ValidateDevice_14175F1D0((__int64)a1, 0, &v21);
    if ( !v20 )
    {
      if ( si128.m128i_i64[1] > 7ui64 )
      {
        Ptr = (char *)v18._Ptr;
        if ( (unsigned __int64)(2 * si128.m128i_i64[1] + 2) >= 0x1000 )
        {
          Ptr = (char *)*((_QWORD *)v18._Ptr + 0xFFFFFFFF);
          if ( (unsigned __int64)((char *)v18._Ptr - Ptr - 8) > 0x1F )
            invalid_parameter_noinfo_noreturn();
        }
        j_j_free(Ptr);
      }
      si128 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
      v18._Buf[0] = 0;
    }
  }
  else
  {
    v11 = v18;
    v18._Buf[0] = 0;
    LOBYTE(v23) = 0;
    v12 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
    v22.u = v11;
    *(__m128i *)&v22._Mysize = si128;
    si128 = v12;
    wstring_140337AF0(&v21, &v22, v10);
    v13 = *(_OWORD *)&v21._Mysize;
    a1->wstr.u = v21.u;
    *(_OWORD *)&a1->wstr._Mysize = v13;
    a1->unknow_28 = 0;
    if ( !(_BYTE)v23 )
    {
      if ( v22._Myres > 7 )
      {
        v14 = v22.u._Ptr;
        if ( 2 * v22._Myres + 2 >= 0x1000 )
        {
          v14 = (wchar_t *)*((_QWORD *)v22.u._Ptr + 0xFFFFFFFF);
          if ( (unsigned __int64)((char *)v22.u._Ptr - (char *)v14 - 8) > 0x1F )
            goto LABEL_25;
        }
        j_j_free(v14);
        v12 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
      }
      *(__m128i *)&v22._Mysize = v12;
      v22.u._Buf[0] = 0;
    }
    if ( !v20 )
    {
      if ( si128.m128i_i64[1] <= 7ui64 )
      {
LABEL_15:
        si128 = v12;
        v18._Buf[0] = 0;
        goto LABEL_16;
      }
      v15 = v18._Ptr;
      if ( (unsigned __int64)(2 * si128.m128i_i64[1] + 2) < 0x1000
        || (v15 = (wchar_t *)*((_QWORD *)v18._Ptr + 0xFFFFFFFF),
            (unsigned __int64)((char *)v18._Ptr - (char *)v15 - 8) <= 0x1F) )
      {
        j_j_free(v15);
        v12 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
        goto LABEL_15;
      }
LABEL_25:
      invalid_parameter_noinfo_noreturn();
    }
  }
LABEL_16:
  drop_api::ffi::ResultAPI_14033C6F0(&v31);
  return a1;
}

ValidateDevice_14175F1D0

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

  v126 = a2;
  v114 = 0i64;
  xx_init_1418560C0((__int64)v115);
  v116 = 0i64;
  xx_init_1418560C0((__int64)v117);
  xx_init_1418560C0((__int64)v118);
  v120 = 0;
  xx_init_141855FC0((__int64)v121, v115);
  v122 = v116;
  xx_init_141855FC0((__int64)v123, v117);
  xx_init_141855FC0((__int64)v124, v118);
  sub_14033BEE0((__int64)v100, (__int64)&v120);
  free_1418560E0((__int64)v118);
  free_1418560E0((__int64)v117);
  free_1418560E0((__int64)v115);
  v5 = sub_14033C6D0((__int64)v100);
  j_unknown_libname_3(v5);
  v6 = sub_14033C6D0((__int64)v100);
  if ( !v7 )
  {
    v8 = (__FrameHandler3::TryBlockMap *)sub_141856840(v6, (__int64)&cbMultiByte);
    v9 = sub_1418562A0(v8);
    v108.m128i_i64[0] = (__int64)v9;
    v10 = 0xFFFFFFFFFFFFFFFFui64;
    do
      ++v10;
    while ( *((_BYTE *)&v9->ControlPc + v10) );
    v108.m128i_i64[1] = v10;
    *(_QWORD *)&v99 = L"new_api()";
    *((_QWORD *)&v99 + 1) = 9i64;
    v11 = s2w_140F04370(&v125, &v99, &v108);
    u = v11->u;
    v13 = *(_OWORD *)&v11->_Mysize;
    v11->_Mysize = 0i64;
    v11->_Myres = 7i64;
    v11->u._Buf[0] = 0;
    *(union std_wstring_union *)a1 = u;
    *(_OWORD *)(a1 + 0x10) = v13;
    *(_BYTE *)(a1 + 0x28) = 0;
    free_140337EE0((__int64)&v125);
    free_1418560E0((__int64)&cbMultiByte);
    drop_api::ffi::ResultAPI_14033C6F0(v100);
    return a1;
  }
  v14 = api::ffi::ResultAPI::value_141856E20(v6);
  v15 = sub_14033C6D0(v14);
  // 線上註冊透過或儲存LocalDeviceToken,
  // 所以跳過下面對LocalDeviceToken的處理,
  // 關注後面的ValidateOfflineLicense
  get_LocalDeviceToken_140F04610((__int64)lpWideCharStr);
  if ( v113 )
  {
    // 對LocalDeviceToken 進行驗證
    v19 = (const WCHAR *)lpWideCharStr;
    v20 = cchWideChar[0];
    if ( v112 > 7 )
      v19 = lpWideCharStr[0];
    v21 = WideCharToMultiByte(0xFDE9u, 0, v19, cchWideChar[0], 0i64, 0, 0i64, 0i64);
    string_14033C5A0(&cbMultiByte, v21, 0);
    lpMultiByteStr = &cbMultiByte;
    if ( cbMultiByte._Myres > 0xF )
      lpMultiByteStr = (std_string *)cbMultiByte.u._Ptr;
    WideCharToMultiByte(0xFDE9u, 0, v19, v20, lpMultiByteStr->u._Buf, cbMultiByte._Mysize, 0i64, 0i64);
    parse_LocalDeviceClaims_141856660(&v127, (__int64)&cbMultiByte);
    if ( cbMultiByte._Myres > 0xF )
    {
      Ptr = cbMultiByte.u._Ptr;
      if ( cbMultiByte._Myres + 1 >= 0x1000 )
      {
        Ptr = (char *)*((_QWORD *)cbMultiByte.u._Ptr + 0xFFFFFFFF);
        if ( (unsigned __int64)(cbMultiByte.u._Ptr - Ptr - 8) > 0x1F )
          invalid_parameter_noinfo_noreturn();
      }
      j_j_free(Ptr);
    }
    v24 = sub_14033C6D0((__int64)&v127);
    sub_141856B80(v24);
    v25 = sub_14033C6D0((__int64)&v127);
    if ( !v26 )
    {
      if ( !(unsigned __int8)sub_141856BB0() )
      {
        reg_get_or_del_LocalDeviceToken_OfflineLicense__140F04C20();
        WideCharStr = 6;
        v30 = sub_14033C6D0((__int64)&v127);
        v31 = (__FrameHandler3::TryBlockMap *)sub_1418569D0(v30, (__int64)&cbMultiByte);
        v32 = sub_1418562A0(v31);
        v33 = (const CHAR *)v32;
        v34 = 0xFFFFFFFFFFFFFFFFui64;
        do
          ++v34;
        while ( *((_BYTE *)&v32->ControlPc + v34) );
        *(_OWORD *)v102 = 0i64;
        LOWORD(v102[0]) = 0;
        *(_QWORD *)v103 = 0i64;
        *(_QWORD *)&v103[2] = 7i64;
        v35 = MultiByteToWideChar(0xFDE9u, 0, (LPCCH)v32, v34, 0i64, 0);
        v36 = *(_QWORD *)v103;
        v37 = v35;
        if ( (unsigned __int64)v35 > *(_QWORD *)v103 )
        {
          v39 = v35 - *(_QWORD *)v103;
          if ( v39 > *(_QWORD *)&v103[2] - *(_QWORD *)v103 )
          {
            sub_140355020((__m128i *)v102, v39, (unsigned __int8)v127, v39, 0);
          }
          else
          {
            *(_QWORD *)v103 = v35;
            v40 = v102;
            if ( *(_QWORD *)&v103[2] > 7ui64 )
              v40 = (LPWSTR *)v102[0];
            v41 = (_WORD *)v40 + v36;
            if ( v39 )
            {
              for ( i = v35 - v36; i; --i )
                *v41++ = 0;
            }
            *((_WORD *)v40 + v39 + v36) = 0;
          }
        }
        else
        {
          v38 = v102;
          *(_QWORD *)v103 = v37;
          if ( *(_QWORD *)&v103[2] > 7ui64 )
            v38 = (LPWSTR *)v102[0];
          *((_WORD *)v38 + v37) = 0;
        }
        v43 = (WCHAR *)v102;
        if ( *(_QWORD *)&v103[2] > 7ui64 )
          v43 = v102[0];
        MultiByteToWideChar(0xFDE9u, 0, v33, v34, v43, v103[0]);
        *((_QWORD *)&v99 + 1) = 0x1Di64;
        *(_QWORD *)&v99 = L"decode local device token: {}";
        wstr_format_140F08780((__int64)&v106, (__int64)&v99, (__int64)v102);
        v44 = v106;
        v45 = v107;
        v46 = *(_QWORD *)&v103[2];
        *(_WORD *)a1 = WideCharStr;
        LOWORD(v106) = 0;
        *(_OWORD *)(a1 + 8) = v44;
        si128 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
        *(_OWORD *)(a1 + 0x18) = v45;
        *(_BYTE *)(a1 + 0x28) = 1;
        v107 = (__int128)si128;
        if ( v46 > 7 )
        {
          v48 = v102[0];
          if ( 2 * v46 + 2 >= 0x1000 )
          {
            v48 = (LPWSTR)*((_QWORD *)v102[0] + 0xFFFFFFFF);
            if ( (unsigned __int64)((char *)v102[0] - (char *)v48 - 8) > 0x1F )
              invalid_parameter_noinfo_noreturn();
          }
          j_j_free(v48);
        }
        *(_QWORD *)v103 = 0i64;
        *(_QWORD *)&v103[2] = 7i64;
        LOWORD(v102[0]) = 0;
        free_1418560E0((__int64)&cbMultiByte);
        goto LABEL_76;
      }
      v27 = 0x16;
      goto LABEL_18;
    }
    v49 = sub_141856E60(v25);
    if ( !isUsb_140F04E10() )
    {
      sub_140F04A30(v102);
      sub_141856010(&cbMultiByte, v102);
      v50 = sub_1418561D0(v49 + 0x28, (__int64)&cbMultiByte);
      free_1418560E0((__int64)&cbMultiByte);
      if ( v50 )
      {
        reg_get_or_del_LocalDeviceToken_OfflineLicense__140F04C20();
        WideCharStr = 6;
        v106 = 0i64;
        v107 = 0i64;
        sub_140338AF0(&v106, (__int64)L"machine ID mismatched", 0x15ui64);
        v51 = v106;
        v52 = v107;
        *(_WORD *)a1 = WideCharStr;
        *(_OWORD *)(a1 + 8) = v51;
        *(_OWORD *)(a1 + 0x18) = v52;
        *(_BYTE *)(a1 + 0x28) = 1;
        sub_140337F70(v102);
        goto LABEL_76;
      }
      sub_140337F70(v102);
    }
    if ( !sub_14175DB30() )
    {
      v27 = 1;
LABEL_18:
      WideCharStr = v27;
      v106 = 0i64;
      v107 = 0i64;
      sub_140338AF0(&v106, (__int64)&Data, 0i64);
      v28 = v106;
      v29 = v107;
      *(_WORD *)a1 = WideCharStr;
      *(_OWORD *)(a1 + 8) = v28;
      *(_OWORD *)(a1 + 0x18) = v29;
      *(_BYTE *)(a1 + 0x28) = 1;
LABEL_76:
      sub_140F059A0(&v127);
      v18 = v113 == 0;
      goto LABEL_77;
    }
    sub_141761610(&v126);
    v53 = sub_1418562A0((__FrameHandler3::TryBlockMap *)(v49 + 0x10));
    v54 = 0xFFFFFFFFFFFFFFFFui64;
    memset(&cbMultiByte, 0, sizeof(cbMultiByte));
    v55 = 0xFFFFFFFFFFFFFFFFui64;
    do
      ++v55;
    while ( *((_BYTE *)&v53->ControlPc + v55) );
    sub_140338C00((__m128i *)&cbMultiByte, (const __m128i *)v53, v55);
    sub_1418566A0(v15, v104, &cbMultiByte);
    if ( cbMultiByte._Myres > 0xF )
      sub_1403382A0((__int64)&cbMultiByte, cbMultiByte.u._Ptr, cbMultiByte._Myres);
    v56 = sub_14033C6D0((__int64)v104);
    sub_141856B70(v56);
    v57 = sub_14033C6D0((__int64)v104);
    if ( !v58 )
    {
      v59 = (__FrameHandler3::TryBlockMap *)sub_1418568E0(v57, (__int64)&cbMultiByte);
      v60 = sub_1418562A0(v59);
      *(_QWORD *)&v99 = v60;
      do
        ++v54;
      while ( *((_BYTE *)&v60->ControlPc + v54) );
      *((_QWORD *)&v99 + 1) = v54;
      s2w_140E6BCB0((LPWSTR)v102, (__m128i *)&v99);
      v61 = *(_OWORD *)v102;
      LOWORD(v102[0]) = 0;
      v62 = *(_OWORD *)v103;
      *(_OWORD *)a1 = v61;
      v63 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
      *(_OWORD *)(a1 + 0x10) = v62;
      *(_BYTE *)(a1 + 0x28) = 0;
      *(__m128i *)v103 = v63;
      free_1418560E0((__int64)&cbMultiByte);
      goto LABEL_75;
    }
    v67 = sub_141856E40(v57);
    v68 = (__FrameHandler3::TryBlockMap *)(v49 + 0x10);
    if ( *(_BYTE *)(v67 + 0x48) )
    {
      v69 = sub_1418562A0(v68);
      memset(&cbMultiByte, 0, sizeof(cbMultiByte));
      v70 = 0xFFFFFFFFFFFFFFFFui64;
      do
        ++v70;
      while ( *((_BYTE *)&v69->ControlPc + v70) );
      sub_140338C00((__m128i *)&cbMultiByte, (const __m128i *)v69, v70);
      sub_1418566C0(v15, &v108, &cbMultiByte);
      if ( cbMultiByte._Myres > 0xF )
        sub_1403382A0((__int64)&cbMultiByte, cbMultiByte.u._Ptr, cbMultiByte._Myres);
      v71 = sub_14033C6D0((__int64)&v108);
      sub_141856BA0(v71);
      v72 = sub_14033C6D0((__int64)&v108);
      if ( v73 )
      {
        WideCharStr = *(_WORD *)sub_141856EA0(v72);
        sub_140337A80(&v106, (__int64)L"change unregistered device to registered");
        sub_1417615D0(a1, &WideCharStr);
        free_140337EE0((__int64)&v106);
        sub_1417612D0(&v108);
      }
      else
      {
        v74 = (__FrameHandler3::TryBlockMap *)sub_141856AC0(v72, (__int64)&cbMultiByte);
        v75 = sub_1418562A0(v74);
        *(_QWORD *)&v99 = v75;
        do
          ++v54;
        while ( *((_BYTE *)&v75->ControlPc + v54) );
        *((_QWORD *)&v99 + 1) = v54;
        s2w_140E6BCB0((LPWSTR)v102, (__m128i *)&v99);
        v76 = *(_OWORD *)v102;
        LOWORD(v102[0]) = 0;
        v77 = *(_OWORD *)v103;
        v78 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
        *(_OWORD *)a1 = v76;
        *(_OWORD *)(a1 + 0x10) = v77;
        *(_BYTE *)(a1 + 0x28) = 0;
        *(__m128i *)v103 = v78;
        free_140337EE0((__int64)v102);
        free_1418560E0((__int64)&cbMultiByte);
        sub_1417612D0(&v108);
      }
      goto LABEL_75;
    }
    v79 = sub_1418562A0(v68);
    memset(&cbMultiByte, 0, sizeof(cbMultiByte));
    do
      ++v54;
    while ( *((_BYTE *)&v79->ControlPc + v54) );
    sub_140338C00((__m128i *)&cbMultiByte, (const __m128i *)v79, v54);
    sub_141856730(v15, v109, &cbMultiByte);
    sub_140337F70(&cbMultiByte);
    v80 = sub_14033C6D0((__int64)v109);
    if ( !(unsigned __int8)j_unknown_libname_3(v80) )
    {
      v81 = sub_14033C6D0((__int64)v109);
      v82 = (__FrameHandler3::TryBlockMap *)sub_141856930(v81, (__int64)v102);
      v83 = sub_1418562A0(v82);
      v99 = *(_OWORD *)sub_14033BEB0(&v108, (__int64)v83);
      v84 = sub_14175DB00(&WideCharStr, &v99);
      v85 = std::wstring::wstring(v119, v84);
      *(_OWORD *)a1 = 0i64;
      *(_QWORD *)(a1 + 0x10) = 0i64;
      *(_QWORD *)(a1 + 0x18) = 0i64;
      *(_OWORD *)a1 = *(_OWORD *)v85;
      *(_OWORD *)(a1 + 0x10) = *(_OWORD *)(v85 + 0x10);
      *(_QWORD *)(v85 + 0x10) = 0i64;
      *(_QWORD *)(v85 + 0x18) = 7i64;
      *(_WORD *)v85 = 0;
      *(_BYTE *)(a1 + 0x28) = 0;
      free_140337EE0((__int64)v119);
      free_140337EE0((__int64)&WideCharStr);
      free_1418560E0((__int64)v102);
LABEL_74:
      sub_14033C6E0(v109);
LABEL_75:
      sub_140EA01E0((LPVOID **)v104, v64, v65, v66);
      goto LABEL_76;
    }
    if ( sub_141746670(*(_QWORD *)(v49 + 8)) )
    {
      v86 = sub_1418562A0((__FrameHandler3::TryBlockMap *)(v49 + 0x10));
      sub_140337C30(&cbMultiByte, v86);
      v87 = sub_14033DC20((__int64)&WideCharStr, a3);
      sub_1418566E0(v15, &v108, v87, (__int64)&cbMultiByte);
      sub_140337F70(&WideCharStr);
      sub_140337F70(&cbMultiByte);
      v88 = sub_14033C6D0((__int64)&v108);
      sub_141856B70(v88);
      v89 = sub_14033C6D0((__int64)&v108);
      if ( !v90 )
      {
        v91 = (__FrameHandler3::TryBlockMap *)sub_141856B10(v89, &cbMultiByte);
        v92 = sub_1418562A0(v91);
        v99 = *(_OWORD *)sub_14033BEB0(v102, (__int64)v92);
        v93 = sub_14175DB00(v119, &v99);
        v94 = std::wstring::wstring(&WideCharStr, v93);
        sub_1417616C0(a1, v94);
        free_140337EE0((__int64)&WideCharStr);
        free_140337EE0((__int64)v119);
        free_1418560E0((__int64)&cbMultiByte);
        sub_140EE3A60(&v108);
        goto LABEL_74;
      }
      v95 = (__FrameHandler3::TryBlockMap *)sub_141856EB0();
      v96 = sub_1418562A0(v95);
      v99 = *(_OWORD *)sub_14033BEB0(v102, (__int64)v96);
      v97 = s2w_140E6BCB0(&WideCharStr, (__m128i *)&v99);
      v99 = *(_OWORD *)sub_140356140(v97, &cbMultiByte);
      save_LocalDeviceToken_140F045F0((BYTE **)&v99);
      free_140337EE0((__int64)&WideCharStr);
      sub_140EE3A60(&v108);
    }
    WideCharStr = 1;
    sub_140337A80(&v106, (__int64)&Data);
    sub_1417615D0(a1, &WideCharStr);
    free_140337EE0((__int64)&v106);
    goto LABEL_74;
  }
  ValidateOfflineLicense_14175EA60(a1, v16, v17);
  v18 = v113 == 0;
LABEL_77:
  if ( !v18 )
    free_140337EE0((__int64)lpWideCharStr);
  drop_api::ffi::ResultAPI_14033C6F0(v100);
  return a1;
}

ValidateOfflineLicense_14175EA60

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

  get_OfflineLicense_14175E4F0((__int64)&lpWideCharStr, a2, a3);
  if ( !v54 )
  {
    // If the offline license token does not exist, validation succeeds. One case where this may happen is if registration previously failed due to a network error but the registration key was valid. This allows EmEditor to be used in such case.
    *(_WORD *)v48 = 1;
    *(_OWORD *)&v48[8] = 0i64;
    v49 = 0i64;
    sub_140338AF0(&v48[8], (__int64)&Data, 0i64);
    v4 = *(_OWORD *)&v48[8];
    v5 = v49;
    // ?? 未驗證
    // 如果離線許可證令牌不存在,則驗證成功。
    // 可能發生這種情況的一種情況是,如果以前由於網路錯誤而註冊失敗,但註冊金鑰有效。這允許在這種情況下使用 EmEditor。
    *(_WORD *)a1 = 1;
    *(_OWORD *)(a1 + 8) = v4;
    *(_OWORD *)(a1 + 0x18) = v5;
    *(_BYTE *)(a1 + 0x28) = 1;
    goto LABEL_42;
  }
  p_lpWideCharStr = &lpWideCharStr;
  v7 = 0xFFFFFFFFFFFFFFFFui64;
  if ( lpWideCharStr._Myres > 7 )
    p_lpWideCharStr = (std_wstring *)lpWideCharStr.u._Ptr;
  v8 = 0xFFFFFFFFFFFFFFFFui64;
  do
    ++v8;
  while ( p_lpWideCharStr->u._Buf[v8] );
  v9 = WideCharToMultiByte(0xFDE9u, 0, p_lpWideCharStr->u._Buf, v8, 0i64, 0, 0i64, 0i64);
  string_14033C5A0(&cbMultiByte, v9, 0);
  lpMultiByteStr = &cbMultiByte;
  if ( cbMultiByte._Myres > 0xF )
    lpMultiByteStr = (std_string *)cbMultiByte.u._Ptr;
  WideCharToMultiByte(0xFDE9u, 0, p_lpWideCharStr->u._Buf, v8, lpMultiByteStr->u._Buf, cbMultiByte._Mysize, 0i64, 0i64);
  decode_LicenseFileClaims_141856680(&v71, (__int64)&cbMultiByte);
  v11 = sub_14033C6D0((__int64)&v71);
  if ( (unsigned __int8)sub_141856B70(v11) )
  {
    v58 = 0i64;
    xx_init_1418560C0((__int64)v59);
    v60 = 0i64;
    xx_init_1418560C0((__int64)v61);
    xx_init_1418560C0((__int64)v62);
    v66 = v58;
    xx_init_141855FC0((__int64)v67, v59);
    v68 = v60;
    xx_init_141855FC0((__int64)v69, v61);
    xx_init_141855FC0((__int64)v70, v62);
    sub_14033BEE0((__int64)&v72, (__int64)&v66);
    free_1418560E0((__int64)v62);
    free_1418560E0((__int64)v61);
    free_1418560E0((__int64)v59);
    v31 = sub_14033C6D0((__int64)&v72);
    if ( (unsigned __int8)j_unknown_libname_3(v31) )
    {
      v35 = sub_14033C6D0((__int64)&v72);
      v36 = api::ffi::ResultAPI::value_141856E20(v35);
      v37 = sub_14033C6D0(v36);
      sub_140F04A30(v55);
      sub_141856010(v63, &cbMultiByte);
      sub_141856010(v64, v55);
      sub_141856E00(v37, &v73, v63);
      v38 = sub_14033C6D0((__int64)&v73);
      if ( (unsigned __int8)sub_141856BA0(v38) )
      {
        v42 = sub_14033C6D0((__int64)&v73);
        v43 = (__int16 *)sub_141856EA0(v42);
        v51 = 0i64;
        v50 = *v43;
        v52 = 0i64;
        sub_140338AF0(&v51, (__int64)L"validate offline license", 0x18ui64);
        v44 = v51;
        v45 = v52;
        *(_WORD *)a1 = v50;
        *(_OWORD *)(a1 + 8) = v44;
        *(_OWORD *)(a1 + 0x18) = v45;
        *(_BYTE *)(a1 + 0x28) = 1;
        sub_1417612D0(&v73);
        free_1418560E0((__int64)v64);
        free_1418560E0((__int64)v63);
        if ( si128.m128i_i64[1] > 0xFui64 )
        {
          v46 = (void *)v55[0];
          if ( (unsigned __int64)(si128.m128i_i64[1] + 1) >= 0x1000 )
          {
            v46 = *(void **)(v55[0] - 8);
            if ( (unsigned __int64)(v55[0] - (_QWORD)v46 - 8) > 0x1F )
              goto LABEL_58;
          }
          j_j_free(v46);
        }
        si128 = _mm_load_si128((const __m128i *)&xmmword_141AB85E0);
        LOBYTE(v55[0]) = 0;
        drop_api::ffi::ResultAPI_14033C6F0(&v72);
        sub_1417612E0(&v71);
        if ( cbMultiByte._Myres <= 0xF )
          goto LABEL_41;
        Ptr = cbMultiByte.u._Ptr;
        if ( cbMultiByte._Myres + 1 < 0x1000 )
          goto LABEL_40;
        Ptr = (char *)*((_QWORD *)cbMultiByte.u._Ptr + 0xFFFFFFFF);
        if ( (unsigned __int64)(cbMultiByte.u._Ptr - Ptr - 8) <= 0x1F )
          goto LABEL_40;
LABEL_58:
        invalid_parameter_noinfo_noreturn();
      }
      v50 = 1;
      v51 = 0i64;
      v52 = 0i64;
      sub_140338AF0(&v51, (__int64)&Data, 0i64);
      v39 = v51;
      v40 = v52;
      *(_WORD *)a1 = v50;
      *(_OWORD *)(a1 + 8) = v39;
      *(_OWORD *)(a1 + 0x18) = v40;
      *(_BYTE *)(a1 + 0x28) = 1;
      sub_1417612D0(&v73);
      free_1418560E0((__int64)v64);
      free_1418560E0((__int64)v63);
      if ( si128.m128i_i64[1] > 0xFui64 )
      {
        v41 = (void *)v55[0];
        if ( (unsigned __int64)(si128.m128i_i64[1] + 1) >= 0x1000 )
        {
          v41 = *(void **)(v55[0] - 8);
          if ( (unsigned __int64)(v55[0] - (_QWORD)v41 - 8) > 0x1F )
            goto LABEL_58;
        }
        j_j_free(v41);
      }
      si128 = _mm_load_si128((const __m128i *)&xmmword_141AB85E0);
      LOBYTE(v55[0]) = 0;
    }
    else
    {
      v50 = 1;
      v51 = 0i64;
      v52 = 0i64;
      sub_140338AF0(&v51, (__int64)&Data, 0i64);
      v32 = v51;
      v33 = v52;
      *(_WORD *)a1 = v50;
      *(_OWORD *)(a1 + 8) = v32;
      *(_OWORD *)(a1 + 0x18) = v33;
      *(_BYTE *)(a1 + 0x28) = 1;
    }
    drop_api::ffi::ResultAPI_14033C6F0(&v72);
    sub_1417612E0(&v71);
    if ( cbMultiByte._Myres <= 0xF )
      goto LABEL_41;
    Ptr = cbMultiByte.u._Ptr;
    if ( cbMultiByte._Myres + 1 < 0x1000 )
      goto LABEL_40;
    Ptr = (char *)*((_QWORD *)cbMultiByte.u._Ptr + 0xFFFFFFFF);
    if ( (unsigned __int64)(cbMultiByte.u._Ptr - Ptr - 8) <= 0x1F )
      goto LABEL_40;
    goto LABEL_58;
  }
  v50 = 6;
  v12 = sub_14033C6D0((__int64)&v71);
  v13 = (__FrameHandler3::TryBlockMap *)sub_141856980(v12, (__int64)v65);
  // 校驗失敗
  // ex:
  // InvalidToken
  // Invalid xxxx
  v14 = sub_1418562A0(v13);
  v15 = (const CHAR *)v14;
  do
    ++v7;
  while ( *((_BYTE *)&v14->ControlPc + v7) );
  memset(v48, 0, sizeof(v48));
  *(_WORD *)v48 = 0;
  *(_QWORD *)&v49 = 7i64;
  v16 = MultiByteToWideChar(0xFDE9u, 0, (LPCCH)v14, v7, 0i64, 0);
  v17 = *(_QWORD *)&v48[0x10];
  v18 = v16;
  if ( (unsigned __int64)v16 > *(_QWORD *)&v48[0x10] )
  {
    v20 = v16 - *(_QWORD *)&v48[0x10];
    if ( v20 > (_QWORD)v49 - *(_QWORD *)&v48[0x10] )
    {
      sub_140355020((__m128i *)v48, v20, (unsigned __int8)v71, v20, 0);
    }
    else
    {
      *(_QWORD *)&v48[0x10] = v16;
      v21 = v48;
      if ( (unsigned __int64)v49 > 7 )
        v21 = *(_BYTE **)v48;
      v22 = &v21[2 * v17];
      if ( v20 )
      {
        for ( i = v16 - v17; i; --i )
          *v22++ = 0;
      }
      *(_WORD *)&v21[2 * v17 + 2 * v20] = 0;
    }
  }
  else
  {
    v19 = v48;
    *(_QWORD *)&v48[0x10] = v18;
    if ( (unsigned __int64)v49 > 7 )
      v19 = *(_BYTE **)v48;
    *(_WORD *)&v19[2 * v18] = 0;
  }
  v24 = (WCHAR *)v48;
  if ( (unsigned __int64)v49 > 7 )
    v24 = *(WCHAR **)v48;
  MultiByteToWideChar(0xFDE9u, 0, v15, v7, v24, *(int *)&v48[0x10]);
  v57[1] = 0x1Ai64;
  v57[0] = (__int64)L"decode offline license: {}";
  wstr_format_140F08780((__int64)&v51, (__int64)v57, (__int64)v48);
  v25 = v51;
  v26 = v52;
  v27 = v49;
  *(_WORD *)a1 = v50;
  LOWORD(v51) = 0;
  *(_OWORD *)(a1 + 8) = v25;
  v28 = _mm_load_si128((const __m128i *)&xmmword_141AB85A0);
  *(_OWORD *)(a1 + 0x18) = v26;
  *(_BYTE *)(a1 + 0x28) = 1;
  v52 = (__int128)v28;
  if ( v27 > 7 )
  {
    v29 = *(void **)v48;
    if ( 2 * v27 + 2 >= 0x1000 )
    {
      v29 = *(void **)(*(_QWORD *)v48 - 8i64);
      if ( (unsigned __int64)(*(_QWORD *)v48 - (_QWORD)v29 - 8i64) > 0x1F )
        goto LABEL_59;
    }
    j_j_free(v29);
  }
  *(_QWORD *)&v48[0x10] = 0i64;
  *(_QWORD *)&v49 = 7i64;
  *(_WORD *)v48 = 0;
  free_1418560E0((__int64)v65);
  sub_1417612E0(&v71);
  if ( cbMultiByte._Myres > 0xF )
  {
    Ptr = cbMultiByte.u._Ptr;
    if ( cbMultiByte._Myres + 1 < 0x1000
      || (Ptr = (char *)*((_QWORD *)cbMultiByte.u._Ptr + 0xFFFFFFFF),
          (unsigned __int64)(cbMultiByte.u._Ptr - Ptr - 8) <= 0x1F) )
    {
LABEL_40:
      j_j_free(Ptr);
      goto LABEL_41;
    }
LABEL_59:
    invalid_parameter_noinfo_noreturn();
  }
LABEL_41:
  cbMultiByte.u._Buf[0] = 0;
  cbMultiByte._Myres = 0xFi64;
  cbMultiByte._Mysize = 0i64;
LABEL_42:
  if ( v54 )
    free_140337EE0((__int64)&lpWideCharStr);
  return a1;
}

decode_LicenseFileClaims_141856680

__m128i **__fastcall decode_LicenseFileClaims_141856680(__m128i **a1, _QWORD *a2)
{
  *a1 = decode_offline_licenseapi_14009A390(a2);
  return a1;
}

decode_offline_licenseapi_14009A390

__m128i *__fastcall decode_offline_licenseapi_14009A390(_QWORD *a1)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v225 = 0xFFFFFFFFFFFFFFFEui64;
  // decode_offline_licenseapi
  v162 = aApiFfiDecodeOf;
  v163 = 0x20i64;
  if ( qword_141C2AE60 != 3 )
    get_es384pub_key_141893580();
  v3 = sub_141856330(a1);
  jumpbuf_sp = _except_get_jumpbuf_sp(a1);
  sub_1400B41F0((__int64)&v156, (__int64)v3, jumpbuf_sp);
  v5 = *(_OWORD *)&v156.m256i_u64[1];
  if ( v156.m256i_i64[0] )
  {
    *(_OWORD *)v156.m256i_i8 = *(_OWORD *)&v156.m256i_u64[1];
    v6 = sub_141894540((__int64)&v156);
    goto LABEL_271;
  }
  sub_14011D160((__int64)&v164);
  sub_140109470(&v149, v5, *((__int64 *)&v5 + 1), (__int64)&v164);
  v7 = (void *)0x8000000000000001i64;
  if ( v149.m128i_i64[0] == 0x8000000000000001ui64 )
  {
    v8 = v149.m128i_i64[1];
    goto LABEL_233;
  }
  sub_141891EC0(v146, &v149, 0x1B0ui64);
  // ??rust jsonwebtoken::decode
  jwt_decode_1400158A0(&ret_dec, v154, v155);

get_es384pub_key_141893580

runonce
get_pub_key_140017B10==>load_public_key_140107710
==>
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdFXkM+nFOhoI8bjAsThl/59LXkYK7f2F
1baTcak6GyFecRF0CXvvF2UzIozW1LZ/8bxd1XnBUvry5X6zchitsQ80ZppPrCBv
7SqnMf/A2QscH1uA5kf1iPPezfOONKla
-----END PUBLIC KEY-----

公鑰解析,python

from Crypto.PublicKey import ECC
der_public_key='''-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdFXkM+nFOhoI8bjAsThl/59LXkYK7f2F
1baTcak6GyFecRF0CXvvF2UzIozW1LZ/8bxd1XnBUvry5X6zchitsQ80ZppPrCBv
7SqnMf/A2QscH1uA5kf1iPPezfOONKla
-----END PUBLIC KEY-----'''
def import_ecc_der_pubkey(data,passphrase=None):
    try:
        # 從二進位制資料中匯入RSA金鑰
        ecc_key = ECC.import_key(data,passphrase=passphrase)
        print("ECC金鑰匯入成功!")
        # 列印公鑰的詳細資訊
        print("公鑰曲線:", ecc_key.curve)
        print("公鑰點 (x):", ecc_key.pointQ.x)
        print("公鑰點 (y):", ecc_key.pointQ.y)
        return ecc_key
    except ValueError as e:
        print(f"匯入ECC金鑰失敗: {e}")
        
        
pk=import_ecc_der_pubkey(der_public_key)
print(pk)
ECC金鑰匯入成功!
公鑰曲線: NIST P-384
公鑰點 (x): 17905674288302374126776080839605784295157890434552731362080489556256033570736476112228381427146711389417315199399551
公鑰點 (y): 37206545926132456148029572453066132337942826421428526502333949665964583459207627949305093577493904048380634710976858
EccKey(curve='NIST P-384', point_x=17905674288302374126776080839605784295157890434552731362080489556256033570736476112228381427146711389417315199399551, point_y=37206545926132456148029572453066132337942826421428526502333949665964583459207627949305093577493904048380634710976858)

3、驗證

jwt的驗證呼叫rust介面,使用jsonwebtoken庫

Keats/jsonwebtoken: JWT lib in rust

/*
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    aud: String,         // Optional. Audience
    exp: usize,          // Required (validate_exp defaults to true in validation). Expiration time (as UTC timestamp)
    iat: usize,          // Optional. Issued at (as UTC timestamp)
    iss: String,         // Optional. Issuer
    nbf: usize,          // Optional. Not Before (as UTC timestamp)
    sub: String,         // Optional. Subject (whom token refers to)
}
*/
#[derive(Debug, Serialize, Deserialize)]
struct LicenseFileClaims {
    exp: u64,
    //LicenseFile
    LicenseID: String,
    FullName: String,
    Email: String,
    StripeSubscriptionID: String
}
/*
struct LocalDeviceClaims {
    exp:u64,
    iat:u64,
    //LocalDevice
    DeviceID:String,
    MachineID:String,//m_id
    StripeSubscriptionID:String,
}
*/

生成OfflineLicense jwt

rust 不熟,讓ai生成了一段驗證程式碼

use jsonwebtoken::{decode, encode, errors::Error, Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation};
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct LicenseFileClaims {
    exp: u64,
    /*
    LicenseFile
    */
    LicenseID: String,
    FullName: String,
    Email: String,
    StripeSubscriptionID: String//Option<String>, // 使用 Option 型別來處理可選欄位
}
struct LocalDeviceClaims {
    exp:u64,
    iat:u64,
    //LocalDevice
    DeviceID:String,
    MachineID:String,//m_id
    StripeSubscriptionID:String,
}
impl LicenseFileClaims {
    fn new(exp: u64,licenseID: String, full_name: String, email: String, stripe_subscription_id: String) -> Self {
        Self {
            exp,
            LicenseID:licenseID,
            FullName: full_name,
            Email: email,
            StripeSubscriptionID: stripe_subscription_id,
        }
    }
}
fn encode_license(claims: &LicenseFileClaims, private_key_pem: &str) -> Result<String, jsonwebtoken::errors::Error> {
    // 使用 ES256 演算法
    let mut header: Header = Header::new(Algorithm::ES384);
    let encoding_key = EncodingKey::from_ec_pem(private_key_pem.as_bytes())?;
    encode(&header, claims, &encoding_key)
}

fn decode_license(token: &str, public_key_pem: &str) -> Result<TokenData<LicenseFileClaims>, jsonwebtoken::errors::Error> {
    let decoding_key = DecodingKey::from_ec_pem(public_key_pem.as_bytes())?;
    decode::<LicenseFileClaims>(token, &decoding_key, &Validation::new(Algorithm::ES384))
}
pub fn jwt_main() -> Result<(), Error> {
    let claims: LicenseFileClaims = LicenseFileClaims::new(
        10000000000,
        "licikun".to_string(),
        "ikun".to_string(),
        "ikun@ikun.com".to_string(),
        "".to_string(),
    );
/*
生成ecc 公私金鑰
openssl ecparam -genkey -name prime256v1 -noout -out ec_private_key.pem && openssl ec -in ec_private_key.pem -pubout -out ec_public_key.pem

轉pkcs#8
openssl pkcs8 -topk8 -nocrypt -in ec_private_key.pem -out ec_private_key_pkcs8.pem
*/
    // ECC 私鑰
    let private_key_pem = r#"
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAfmvhFWjbrNUXGGXcx
6m1XXlDldGtnHEVJSFI6Xa+KUc0h3gfvMPgl5s3C+4kfT3qhZANiAAQ8Kcx2uErL
iF7TY9E3v3Iv8RoCMfjQwzYFPDYbISV1zfMXVhbY9Q1muSNKqMje80iIrvRgs1mW
EqSwdoTTy1xMwjlBEiZffLDaynhKHVlfXfKmh9kw6j1+/A2qCnoDvoU=
-----END PRIVATE KEY-----
    "#;

    // ECC 公鑰
    let public_key_pem = r#"
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPCnMdrhKy4he02PRN79yL/EaAjH40MM2
BTw2GyEldc3zF1YW2PUNZrkjSqjI3vNIiK70YLNZlhKksHaE08tcTMI5QRImX3yw
2sp4Sh1ZX13ypofZMOo9fvwNqgp6A76F
-----END PUBLIC KEY-----

    "#;
    // let encoding_key = EncodingKey::from_ec_pem(private_key_pem.as_bytes())?;
    // 編碼為 JWT token
    match encode_license(&claims, private_key_pem) {
        Ok(token) => {
            println!("Encoded token: {}", token);

            // 解碼 JWT token
            let var_name = match decode_license(&token, public_key_pem) {
                Ok(decoded_data) => {
                    println!("Decoded claims: {:?}", decoded_data.claims);
                    Ok(())
                }
                Err(e) => {
                    println!("Failed to decode token: {:?}", e);
                    Ok(())
                }
            };
            var_name
        }
        Err(e) => {
            println!("Failed to encode claims: {:?}", e);
            Ok(())
        }
    }
}

將Encoded token 輸出的內用儲存到檔案中,在patch後(或者記憶體補丁)

便可以透過 /ol 離線註冊,

或者在eeCommon.ini中[Common]下新增OfflineLicense=xxxxxxxxxxx

image-20241109194204299

patch公鑰

公鑰位置:

image-20241109192612415

使用自己的ES384公鑰進行替換

patch完整性校驗(可選)

記憶體修補公鑰則無需patch

image-20241109192953876

sub_141767690

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

  if ( byte_141C3C28A )
    return 1i64;
  v2 = byte_141BE02DF;
  if ( byte_141BE02DF == (char)0xFF )
  {
    v2 = get_common_Edition_140E99B80(L"SDS", 0) == 0x78C19A68;
    byte_141BE02DF = v2;
  }
  if ( v2 == 1 )
    return 1i64;
  result = sub_140E9A6D0(a1, 0xFFFFFFFFi64);
  if ( !(_DWORD)result )
    return result;
  v10[0] = 0x20i64;
  v10[1] = a1;
  v18 = 0i64;
  v14 = 1i64;
  pWVTData = 0i64;
  v13 = 0i64;
  v15 = v10;
  v4 = 0;
  v17 = 0i64;
  LODWORD(pWVTData) = 0x58;
  v11 = 0i64;
  DWORD2(v13) = 2;
  v16 = 0i64;
  DWORD2(v17) = 0x10;
  pgActionID.Data1 = 0xAAC56B;
  *(_DWORD *)&pgActionID.Data2 = 0x11D0CD44;
  *(_DWORD *)pgActionID.Data4 = 0xC000C28C;
  *(_DWORD *)&pgActionID.Data4[4] = 0xEE95C24F;
  v5 = WinVerifyTrust(0i64, &pgActionID, &pWVTData);
  if ( !v5 )//mark
    return 1i64; 
  sub_141892560(Buffer, 0, 0x640ui64);
  // 130,    "一個程式檔案的數字簽名無法驗證或已損壞。請再次解除安裝,下載並安裝此產品。如果您的計算機未連線到網路,請連線網路,或參閱我們網站上的“常見問題解答”以安裝中間證書。"
  v6 = 130;
  // 222,    "一個程式檔案的數字簽名無法驗證。如果您的計算機未連線到網路,請連線網路,或參閱我們網站上的“常見問題解答”以安裝 Sectigo 安裝中間證書。"
  if ( v5 == 0x800B010A )
    v6 = 222;
  LoadStringW(hInstance, v6, (LPWSTR)Buffer, 0x320);
  if ( v5 != 0x800B010A )
  {
    sub_14032DEA0(v19, 0x50i64, (__int64)L" (0x%x)", v5);
    sub_14032DDE0(Buffer, 0x320i64, v19);
  }
  v21 = 0;
  ActiveWindow = (unsigned int)GetActiveWindow();
  v8 = sub_140ED9080(ActiveWindow, (int)Buffer, a1, (__int64)&v21, 2, 0xFFFEi64);
  if ( v21 )
  {
    if ( v8 == 1 )
      byte_141BE02DF = 1;
  }
  LOBYTE(v4) = v8 == 1;
  return v4;
}

離線註冊

透過 /ol "licenseFilePath" 離線註冊

Offline Registration — EmEditor Help

  1. Use the command line option /ol "licenseFilePath" where licenseFilePath is the path to the license file. The following is an example of the command.
EmEditor.exe /ol "C:\Users\Example\emeditorLicenseFile-a7PT7Au3TOelfK1w.txt"

ps

聯網不可用,必須禁用程式網路!

在patch ec384 公鑰和完整性校驗,離線註冊之後

註冊資訊

image-20241109172840892

關於頁面仍顯示未註冊!(註冊意味著要線上驗證,那必然過不了驗證0.0)

image-20241109172909516

相關文章