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; // siint result; // eaxchar *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,#unuseNoGames,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); // 24v427 = v638;v426 = ".";str_find_AA7528(version, 2, '.', &v637); // 2v424 = 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,4str_find_AA7528(temp, 2, '#', &v48);Trim(v48, (int)&lic_info); // UserName|MajorVersion.MinorVersionstr_find_AA7528(temp, 3, '#', &v47);Trim(v47, (int)&count); // user_limitstr_find_AA7528(temp, 4, '#', &v46);Trim(v46, (int)&iMinorVersion); // {MajorVersion}3{MinorVersion}6{MinorVersion}str_find_AA7528(temp, 5, '#', &v45);Trim(v45, (int)unknow); // unusestr_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.2str_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);// 2LStrCatN(&v42, 5, v39); // 版本号,x.y {MajorVersion}3{MinorVersion}6{MinorVersion}// {x}3{y}6{y}// 243262LStrCmp((int)iMinorVersion, (int *)v42);if ( v6 ) // zf{// -->相等时str_find_AA7528(lic_info, 2, '|', &v32);str_find_AA7528(TForm1_BCEC40->version_info_3A8, 1, '.', &v31);// 24str_find_AA7528(TForm1_BCEC40->version_info_3A8, 2, '.', &v30);// 2LStrCat(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();elseLStrCatN(&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 base64VariantBase64Table = '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 resultelif 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 resultelse: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 resultdef 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]] << 6block += VariantBase64ReverseDict[s[4 * i + 2]] << 12block += VariantBase64ReverseDict[s[4 * i + 3]] << 18result += block.to_bytes(3, 'little')if left_bytes == 0:return resultelif left_bytes == 2:block = VariantBase64ReverseDict[s[4 * blocks_count]]block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6result += block.to_bytes(1, 'little')return resultelif left_bytes == 3:block = VariantBase64ReverseDict[s[4 * blocks_count]]block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6block += VariantBase64ReverseDict[s[4 * blocks_count + 2]] << 12result += block.to_bytes(2, 'little')return resultelse: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 | 0x482Dreturn 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 | 0x482Dreturn bytes(result)class LicenseType:Professional = 1Educational = 3Persional = 4def 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, # UnknownNoGames, # 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,#unuseNoGames,NoPlugins,)LicenseString=lic_formatEncodedLicenseString = VariantBase64Encode(EncryptBytes(0x787, LicenseString.encode())).decode()return EncodedLicenseStringdef 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], #usernameMajorVersion, 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:\nMobaXterm_Personal_24.2.exe -chklic temp.key## will auto generate 'Custom.mxtpro'''')if __name__ == '__main__':#main()test()