MobaXterm
目錄
- MobaXterm
- 0、啟動視窗 TForm1
- 1、TForm1_FormCreate
- decrypt_9FDA48
- 1)xxBase64Decode_9FD80C
- 2)DecryptBytes_9FD9DC
- decrypt_9FDA48
- 2、許可結構
- 1) Type
- 2) version_info_3A8
- 3) user_limit
- 4) Version
- 5) unuse
- 6)NoGames
- 7)NoPlugins
- 解析函式parse_9FEB5C
- other
- sub_A03F80
- TFormAbout_FormCreate
- py
MobaXterm_Personal_24.2.exe
delphi 程式 分析首選IDR
,生成IDC指令碼後結合 IDA PRO
分析
注意:IDA 不能只看F5的反編譯結果,如果未修正函式呼叫方式、棧引數時,結果往往不正確;需要看彙編確定引數。
0、啟動視窗 TForm1
1、TForm1_FormCreate
decrypt_9FDA48
當指定 “-chklic keyfilepath" 時,會驗證檔案keyfilepath
int __usercall decrypt_9FDA48@<eax>(char **a1@<ecx>, int key@<edx>, int a3@<eax>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
a3a = 0;
v7 = &savedregs;
v6[1] = (unsigned int)&loc_9FDA90;
v6[0] = (unsigned int)NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)v6);
xxBase64Decode_9FD940(a3, (int)a1, (unsigned int)&a3a);
DecryptBytes_9FD9DC((char *)a3a, key, a1);
__writefsdword(0, v6[0]);
v7 = (int *)&loc_9FDA97;
return LStrClr();
}
1)xxBase64Decode_9FD80C
相較標準base64,位元組序解析不同
int __usercall xxBase64Decode_9FD80C@<eax>(unsigned __int8 *a1@<eax>, int *a2@<edx>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
// 相較標準base64,位元組序解析不同
v4 = LStrLen((int)a1) - 2;
if ( v4 )
{
v6 = v4 - 1;
if ( v6 )
{
result = v6 - 1;
if ( !result )
{
LStrSetLength(
a2,
3,
v5,
((unsigned __int8)index_table_BA26F4[a1[3]] << 0x12)
+ ((unsigned __int8)index_table_BA26F4[a1[2]] << 0xC)
+ ((unsigned __int8)index_table_BA26F4[a1[1]] << 6)
+ (unsigned __int8)index_table_BA26F4[*a1]);
v12 = LStrLen(*a2);
v13 = UniqueStringA(v12);
return Move(v16, v13);
}
}
else
{
LStrSetLength(
a2,
2,
v5,
((unsigned __int8)index_table_BA26F4[a1[2]] << 0xC)
+ ((unsigned __int8)index_table_BA26F4[a1[1]] << 6)
+ (unsigned __int8)index_table_BA26F4[*a1]);
v10 = LStrLen(*a2);
v11 = UniqueStringA(v10);
return Move(v15, v11);
}
}
else
{
LStrSetLength(
a2,
1,
v5,
((unsigned __int8)index_table_BA26F4[a1[1]] << 6) + (unsigned __int8)index_table_BA26F4[*a1]);
v8 = LStrLen(*a2);
v9 = UniqueStringA(v8);
return Move(v14, v9);
}
return result;
}
2)DecryptBytes_9FD9DC
位元組xor
int __usercall DecryptBytes_9FD9DC@<eax>(char *a1@<eax>, int reuse_key_or_index@<edx>, char **d_str@<ecx>)
{
__int16 key; // si
int result; // eax
char *v7; // [esp+0h] [ebp-18h]
__int16 v8; // [esp+4h] [ebp-14h]
v7 = a1;
LStrAsg((volatile __int32 *)d_str, (__int32)a1);
key = reuse_key_or_index;
result = LStrLen((int)*d_str);
if ( !(_WORD)result )
return result;
v8 = result;
LOWORD(reuse_key_or_index) = 1;
do
{
*(_BYTE *)(UniqueStringA(v7) + (unsigned __int16)reuse_key_or_index - 1) = HIBYTE(key) ^ (*d_str)[(unsigned __int16)reuse_key_or_index - 1];
result = (unsigned __int8)v7[(unsigned __int16)reuse_key_or_index - 1];
key = result & key | 0x482D;
++reuse_key_or_index;
--v8;
}
while ( v8 );
return result;
}
2、許可結構
經過解密後,許可資訊使用‘#’分隔為7部分
lic_format='{0}#{1}#{2}#{3}#{4}#{5}#{6}#'.format(
Type,
'{}|{}{}'.format(UserName, MajorVersion, MinorVersion),
Count,# number
'{}3{}6{}'.format(MajorVersion,MinorVersion,MinorVersion),
number,#unuse
NoGames,
NoPlugins,
)
1) Type
結合TFormAbout_FormCreate 分析得到
Professional Edition 1
unknow? trial version 2
Educational Edition 3
Personal Edition 4
2) version_info_3A8
'{}|{}{}'.format(UserName, MajorVersion, MinorVersion),
LStrLAsg(&version, "24.2.0.5220");
LStrAsg(&v671->ver_3_46_F84, (__int32)"3.46");
LStrAsg(&v671->ver_3_02_F88, (__int32)"2.02");
str_find_AA7528(version, 1, '.', &v638); // 24
v427 = v638;
v426 = ".";
str_find_AA7528(version, 2, '.', &v637); // 2
v424 = v637;
// LStrCatN
// eax,edx (棧不定引數,從左到右入棧)
LStrCatN((char **)&v671->version_info_3A8, 3);// 24.2
// 這裡ida 沒有識別對不定引數(因為前面有些函式沒有調整,導致這裡沒能給出不定引數)
3) user_limit
在TForm1_FormCreate 和TFormAbout_FormCreate引用
4) Version
'{}3{}6{}'.format(MajorVersion,MinorVersion,MinorVersion),
5) unuse
未使用
6)NoGames
可在sub_A03F80 中看到相關應用
7)NoPlugins
可在sub_A03F80 中看到相關應用
解析函式parse_9FEB5C
int __usercall parse_9FEB5C@<eax>(__int32 a1@<eax>, __int32 a2@<ecx>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
_InterlockedExchange(&v60, a2);
v60 = a1;
LStrAddRef(v24, v25, v26);
v23 = &savedregs;
v22 = &loc_9FF193;
ExceptionList = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)&ExceptionList);
LStrClr();
v20[2] = &savedregs;
v20[1] = &loc_9FF15E;
v20[0] = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)v20);
Trim(v60, (int)&temp);
v3 = 0x19;
LOBYTE(v3) = gvar_009FF1A4;
v19 = v3;
v18 = &v51;
StringReplace(temp, (int)"=", 0);
LStrLAsg(&temp, v51);
decrypt_9FDA48((char **)&v50, 0x787, temp);
LStrLAsg(&temp, v50);
if ( temp )
{
v17 = &savedregs;
v16 = &loc_9FF0AF;
v15 = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)&v15);
// license 使用'#' 分隔為7部分
str_find_AA7528(temp, 1, '#', &v49);
Trim(v49, (int)&type); // 第一部分:許可型別 1,3,4
str_find_AA7528(temp, 2, '#', &v48);
Trim(v48, (int)&lic_info); // UserName|MajorVersion.MinorVersion
str_find_AA7528(temp, 3, '#', &v47);
Trim(v47, (int)&count); // user_limit
str_find_AA7528(temp, 4, '#', &v46);
Trim(v46, (int)&iMinorVersion); // {MajorVersion}3{MinorVersion}6{MinorVersion}
str_find_AA7528(temp, 5, '#', &v45);
Trim(v45, (int)unknow); // unuse
str_find_AA7528(temp, 6, '#', &v44);
Trim(v44, (int)&NoGames);
str_find_AA7528(temp, 7, '#', &v43);
Trim(v43, (int)&NoPlugins);
v4 = StrToInt(type);
// license 許可型別
TForm1_BCEC40->license_type_F8C = v4;
if ( !v4 )
goto LABEL_21;
license_type_F8C = TForm1_BCEC40->license_type_F8C;
if ( license_type_F8C == 1 || license_type_F8C == 3 || license_type_F8C == 4 )
{
// 24.2
str_find_AA7528(TForm1_BCEC40->version_info_3A8, 1, '.', &v41);// 24
// push offset a3_18 ; "3"
str_find_AA7528(TForm1_BCEC40->version_info_3A8, 2, '.', &v40);// 2
// push offset a6_19 ; "6"
str_find_AA7528(TForm1_BCEC40->version_info_3A8, 2, '.', &v39);// 2
LStrCatN(&v42, 5, v39); // 版本號,x.y {MajorVersion}3{MinorVersion}6{MinorVersion}
// {x}3{y}6{y}
// 243262
LStrCmp((int)iMinorVersion, (int *)v42);
if ( v6 ) // zf
{
// -->相等時
str_find_AA7528(lic_info, 2, '|', &v32);
str_find_AA7528(TForm1_BCEC40->version_info_3A8, 1, '.', &v31);// 24
str_find_AA7528(TForm1_BCEC40->version_info_3A8, 2, '.', &v30);// 2
LStrCat(v11, v30);
LStrCmp((int)&v31, (int *)v31);
if ( v6 ) // zf
{
// 相等時
str_find_AA7528(lic_info, 1, '|', v29);
StringReplace(v29[0], (int)"\\", 0);
LStrAsg(&TForm1_BCEC40->username_F9C, v29[1]);
utf8_dec_AB6800((char *)TForm1_BCEC40->username_F9C, &v28);
LStrAsg(&TForm1_BCEC40->username_F9C, v28);
StrToInt(count);
IntToStr(v8, (int)&v27);
LStrAsg(&TForm1_BCEC40->user_limit_FA0, v27);
TForm1_BCEC40->NoGames_FA4 = 1;
TForm1_BCEC40->NoPlugins_FA5 = 1;
LStrCmp(NoGames, (int *)"1");
if ( !v6 )
TForm1_BCEC40->NoGames_FA4 = 0;
LStrCmp(NoPlugins, (int *)"1");
if ( !v6 )
TForm1_BCEC40->NoPlugins_FA5 = 0;
}
else
{
TForm1_BCEC40->license_type_F8C = 0;
}
}
else
{
TForm1_BCEC40->license_type_F8C = 0;
if ( (unsigned __int8)*iMinorVersion >= '1'
&& (unsigned __int8)*iMinorVersion <= *(_BYTE *)TForm1_BCEC40->version_info_3A8
&& (unsigned __int8)iMinorVersion[1] >= '0'
&& (unsigned __int8)iMinorVersion[1] <= '9'
&& (unsigned __int8)iMinorVersion[3] >= '0'
&& (unsigned __int8)iMinorVersion[3] <= '9' )
{
v38[1] = *iMinorVersion;
v38[0] = 1;
PStrCpy((int)v37, (unsigned __int8 *)v38);
v36 = iMinorVersion[1];
d_str = 1;
PStrNCat(v37, (unsigned __int8)&d_str, 2u);
PStrCpy((int)v34, (unsigned __int8 *)v37);
PStrNCat(v34, (unsigned __int8)"\x01.", 3u);
PStrCpy((int)v33, (unsigned __int8 *)v34);
v36 = iMinorVersion[3];
d_str = 1;
PStrNCat(v33, (unsigned __int8)&d_str, 4u);
LStrFromString(v7, (unsigned __int8 *)v33);
}
}
goto LABEL_22;
}
if ( license_type_F8C == 2 ) // unknow
{
LABEL_21:
TForm1_BCEC40->dwordF90 = StrToInt(lic_info);
TForm1_BCEC40->dwordF94 = StrToInt(count);
TForm1_BCEC40->dwordF98 = StrToInt((int)iMinorVersion);
}
LABEL_22:
__writefsdword(0, v12);
}
v9 = TForm1_BCEC40->license_type_F8C;
if ( (v9 == 1 || v9 == 3 || v9 == 4) && (!TForm1_BCEC40->username_F9C || !TForm1_BCEC40->user_limit_FA0) )
TForm1_BCEC40->license_type_F8C = 0;
__writefsdword(0, v13);
__writefsdword(0, v14);
LStrArrayClr(&loc_9FF19A);
return LStrArrayClr(v16);
}
other
sub_A03F80
NoGames_FA4
NoPlugins_FA5
TForm1_BCEC40->NoGames_FA4 = (*(int (__fastcall **)(const char *, const char *, int))(*(_DWORD *)TForm1_BCEC40->field_E08
+ 0x10))(
"NoGame",
"Customization",
1);
v62 = 1;
TForm1_BCEC40->NoPlugins_FA5 = (*(int (__fastcall **)(const char *, const char *, int))(*(_DWORD *)TForm1_BCEC40->field_E08
+ 0x10))(
"NoPingus",
"Customization",
1);
TFormAbout_FormCreate
about
LStrCmp((*TForm1_ptr_00BA4964)->user_limit_FA0, (int *)"1");
if ( v8 )
{
LStrLAsg(&v39, "(1 user)");
}
else
{
LStrCmp((*TForm1_ptr_00BA4964)->user_limit_FA0, (int *)"Unlimited");
if ( v8 || (LStrCmp((*TForm1_ptr_00BA4964)->user_limit_FA0, (int *)"0"), v8) )
LStrClr();
else
LStrCatN(&v39, 3, " users)", (*TForm1_ptr_00BA4964)->user_limit_FA0, "(");
}
license_type_F8C = (*TForm1_ptr_00BA4964)->license_type_F8C;
if ( license_type_F8C == 1 || license_type_F8C == 3 || license_type_F8C == 4 )
{
if ( license_type_F8C == 1 )
{
v21 = "Professional Edition v";
v20 = (void *)(*TForm1_ptr_00BA4964)->version_info_3A8;
v19 = v38;
IntToStr(v9, (int)&v33);
LStrCatN(v34, 5, v33, " Build ");
TControl_SetText(v11, (int *)v34[0]);
}
else if ( license_type_F8C == 3 )
{
v21 = "Educational Edition v";
v20 = (void *)(*TForm1_ptr_00BA4964)->version_info_3A8;
v19 = v38;
IntToStr(v9, (int)&v31);
LStrCatN(&v32, 5, v31, " Build ");
TControl_SetText(v12, (int *)v32);
LStrLAsg(&v39, "(for educational use only)");
}
else
{
v21 = "Personal Edition v";
v20 = (void *)(*TForm1_ptr_00BA4964)->version_info_3A8;
v19 = v38;
IntToStr(v9, (int)&v29);
LStrCatN(&v30, 5, v29, " Build ");
TControl_SetText(v13, (int *)v30);
LStrLAsg(&v39, "(for personal use only)");
}
LStrCatN(&v28, 4, v39, " ", (*TForm1_ptr_00BA4964)->username_F9C, "This version is registered to ");
TControl_SetText(v14, (int *)v28);
TControl_SetVisible(v40[0xC8], 0);
}
py
keygen by Double Sine
DoubleLabyrinth
#/usr/bin/env python3
'''
Author: Double Sine
License: GPLv3
'''
import random
from typing import Union
import os, sys, zipfile
# import base64
VariantBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
VariantBase64Dict = { i : VariantBase64Table[i] for i in range(len(VariantBase64Table)) }
VariantBase64ReverseDict = { VariantBase64Table[i] : i for i in range(len(VariantBase64Table)) }
def VariantBase64Encode(bs : bytes):
result = b''
blocks_count, left_bytes = divmod(len(bs), 3)
for i in range(blocks_count):
coding_int = int.from_bytes(bs[3 * i:3 * i + 3], 'little')
block = VariantBase64Dict[coding_int & 0x3f]
block += VariantBase64Dict[(coding_int >> 6) & 0x3f]
block += VariantBase64Dict[(coding_int >> 12) & 0x3f]
block += VariantBase64Dict[(coding_int >> 18) & 0x3f]
result += block.encode()
if left_bytes == 0:
return result
elif left_bytes == 1:
coding_int = int.from_bytes(bs[3 * blocks_count:], 'little')
block = VariantBase64Dict[coding_int & 0x3f]
block += VariantBase64Dict[(coding_int >> 6) & 0x3f]
result += block.encode()
return result
else:
coding_int = int.from_bytes(bs[3 * blocks_count:], 'little')
block = VariantBase64Dict[coding_int & 0x3f]
block += VariantBase64Dict[(coding_int >> 6) & 0x3f]
block += VariantBase64Dict[(coding_int >> 12) & 0x3f]
result += block.encode()
return result
def VariantBase64Decode(s : str):
if isinstance(s,bytes):
s=s.decode()
result = b''
blocks_count, left_bytes = divmod(len(s), 4)
for i in range(blocks_count):
block = VariantBase64ReverseDict[s[4 * i]]
block += VariantBase64ReverseDict[s[4 * i + 1]] << 6
block += VariantBase64ReverseDict[s[4 * i + 2]] << 12
block += VariantBase64ReverseDict[s[4 * i + 3]] << 18
result += block.to_bytes(3, 'little')
if left_bytes == 0:
return result
elif left_bytes == 2:
block = VariantBase64ReverseDict[s[4 * blocks_count]]
block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6
result += block.to_bytes(1, 'little')
return result
elif left_bytes == 3:
block = VariantBase64ReverseDict[s[4 * blocks_count]]
block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6
block += VariantBase64ReverseDict[s[4 * blocks_count + 2]] << 12
result += block.to_bytes(2, 'little')
return result
else:
raise ValueError('Invalid encoding.')
def EncryptBytes(key : int, bs : bytes):
result = bytearray()
for i in range(len(bs)):
result.append(bs[i] ^ ((key >> 8) & 0xff))
key = result[-1] & key | 0x482D
return bytes(result)
def DecryptBytes(key : int, bs : bytes):
result = bytearray()
for i in range(len(bs)):
result.append(bs[i] ^ ((key >> 8) & 0xff))
key = bs[i] & key | 0x482D
return bytes(result)
class LicenseType:
Professional = 1
Educational = 3
Persional = 4
def GenerateLicense(Type : LicenseType, Count :Union[int,str] , UserName : str, MajorVersion : int, MinorVersion,NoGames=0,NoPlugins=0):
#
if isinstance(Count,str) :
if Count.isdecimal():
Count=int(Count)
assert(Count >= 0)
# 'Unlimited' or number
'''
LicenseString = '%d#%s|%d%d#%d#%d3%d6%d#%d#%d#%d#' % (Type,
UserName, MajorVersion, MinorVersion,
Count,
MajorVersion, MinorVersion, MinorVersion,
0, # Unknown
NoGames, # No Games flag. 0 means "NoGames = false". But it does not work.
NoPlugins) # No Plugins flag. 0 means "NoPlugins = false". But it does not work.
'''
x=0
# x=random.randint(0,0xff)
lic_format='{0}#{1}#{2}#{3}#{4}#{5}#{6}#'.format(
Type,
'{}|{}{}'.format(UserName, MajorVersion, MinorVersion),
Count,# number or 'Unlimited'
'{}3{}6{}'.format(MajorVersion,MinorVersion,MinorVersion),
x,#unuse
NoGames,
NoPlugins,
)
LicenseString=lic_format
EncodedLicenseString = VariantBase64Encode(EncryptBytes(0x787, LicenseString.encode())).decode()
return EncodedLicenseString
def help():
print('Usage:')
print(' MobaXterm-Keygen.py <UserName> <Version>')
print()
print(' <UserName>: The Name licensed to')
print(' <Version>: The Version of MobaXterm')
print(' Example: 24.2')
print()
def main():
if len(sys.argv) != 3:
help()
exit(0)
else:
MajorVersion, MinorVersion = sys.argv[2].split('.')[0:2]
MajorVersion = int(MajorVersion)
MinorVersion = int(MinorVersion)
EncodedLicenseString=GenerateLicense(LicenseType.Professional,
1,
sys.argv[1], #username
MajorVersion,
MinorVersion)
with zipfile.ZipFile('Custom.mxtpro', 'w') as f:
f.writestr('Pro.key', data = EncodedLicenseString)
print('[*] Success!')
print('[*] File generated: %s' % os.path.join(os.getcwd(), 'Custom.mxtpro'))
print('[*] Please move or copy the newly-generated file to MobaXterm\'s installation path.')
print()
# else:
# print('[*] ERROR: Please run this script directly')
def test():
EncodedLicenseString=GenerateLicense(LicenseType.Professional,
1,
'test',
24,
2)
print('EncodedLicenseString:',EncodedLicenseString)
with open('temp.key','w') as f:
f.write(EncodedLicenseString)
print('''cmd:\n
MobaXterm_Personal_24.2.exe -chklic temp.key
## will auto generate 'Custom.mxtpro'
''')
if __name__ == '__main__':
#main()
test()