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
- emeddlgs.dll==>DoRegistDlg
- 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對應 選擇登入
透過呼叫棧可回溯到emeddlgs.dll匯出函式DoRegistDlg
匯出函式
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 存放簡體中文相關資源
註冊產品
’關於‘
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
patch公鑰
公鑰位置:
使用自己的ES384公鑰進行替換
patch完整性校驗(可選)
記憶體修補公鑰則無需patch
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
- Use the command line option
/ol "licenseFilePath"
wherelicenseFilePath
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 公鑰和完整性校驗,離線註冊之後
註冊資訊
關於頁面仍顯示未註冊!(註冊意味著要線上驗證,那必然過不了驗證0.0)