Proxifier Portable Edition v4.12 注册分析
Proxifier Portable Edition v4.12
目录
MFC 程序分析
可从以下方面入手
1、res
查找是否存在静态dialog资源,(没有就上xspy)
2、DoDataExchange
数据绑定
3、GetMessageMap
消息映射;此文不涉及
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
struct AFX_MSGMAP
{
const AFX_MSGMAP *(__stdcall *pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY *lpEntries;
};
CRegistrationDlg
CDialog 主要关注DoDataExchange 和OnOK
CRegistrationDlg__DoDataExchange_487450
结合ResourceHacker 可得到CRegistrationDlg 相关数据信息
void __thiscall CRegistrationDlg::DoDataExchange_487450(CRegistrationDlg *this, struct CDataExchange *a2)
{
// {
// CONTROL "", 1017, EDIT, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 23, 38, 208, 14
// CONTROL "", 1018, EDIT, ES_LEFT | ES_UPPERCASE | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 23, 77, 208, 14
// CONTROL "Current user only", 1006, BUTTON, BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP, 23, 129, 98, 10
// CONTROL "All users on this computer\n(require administrator)", 1007, BUTTON, BS_AUTORADIOBUTTON | BS_MULTILINE | WS_CHILD | WS_VISIBLE, 129, 123, 102, 22
// CONTROL "OK", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 166, 50, 14
// CONTROL "Cancel", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 199, 166, 50, 14
// CONTROL "Please enter your registration information", -1, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 7, 7, 242, 98
// CONTROL "Your name or company name:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 23, 25, 101, 8
// CONTROL "Your registration key:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 23, 63, 101, 8
// CONTROL "Set this license for", -1, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 7, 114, 242, 38
// }
DDX_Control(a2, 1017, &this->name_CEdit_B8);
DDX_Text(a2, 1017, &this->name_text138);
DDX_Control(a2, 1018, &this->key_CEdit_140);
DDX_Text(a2, 1018, &this->key_text_1C0);
DDX_Radio(a2, 1006, &this->Current_user_only_1C4);
DDX_Control(a2, 1006, &this->Current_user_only_CButton_1C8);
}
CRegistrationDlg__OnOK_4874E0
注册逻辑在Register_471DC0中
void __thiscall CRegistrationDlg::OnOK_4874E0(CRegistrationDlg *this)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
CWnd::UpdateData(this, 1);
if ( *(this->name_text138 - 3) )
{
v3 = (this->key_text_1C0 - 0x10);
if ( *(v3 + 1) )
{
if ( this->Current_user_only_1C4 )
{
CWnd::MessageBoxW(
this,
L"Portable Edition cannot be registered for all users of this computer.\n"
"Please register the current instance only.",
L"Registration",
0x30u);
}
else
{
flag = 1;
key = v2;
p_key = &key;
v10 = &key;
key = (get_text_40ADB0(v3) + 4);
v12 = 0;
v4 = (this->name_text138 - 0x10);
v10 = &v7;
name = get_text_40ADB0(v4);
v12 = 0xFFFFFFFF;
if ( Register_471DC0(v6, name + 8, key, flag) )
CDialog::OnOK(this);
}
}
else
{
CWnd::MessageBoxW(this, L"Key cannot be empty.", L"Registration", 0x30u);
}
}
else
{
CWnd::MessageBoxW(this, L"Name cannot be empty.", L"Registration", 0x30u);
}
}
Register_471DC0
do_check_43C500
key[2] == 'Y' 时 The registration key from Proxifier v2 doesn't work with Proxifier v4.
char __thiscall do_check_43C500(LicInfo *this, wchar_t *name, wchar_t *key, std_wstring *k, int a5, std_wstring *Src)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v30 = name;
v25._Mysize = 0;
v25._Myres = 7;
v25.u._Buf[0] = 0;
v7 = key;
LODWORD(src) = key;
HIDWORD(src) = key + 1;
while ( *v7++ )
;
wstring_402B60(&v25, src, (v7 - HIDWORD(src)) >> 1);
if ( !sub_402280(k, v25.u._Ptr) )
{
wstring_402B60(
Src,
L"The format of the registration key is incorrect.\n"
"\n"
"A correct key should look like the following:\n"
"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX\n"
"\n"
"If the problem persists, please contact support.",
0xAFu);
return 0;
}
Ptr = k;
if ( k->_Myres >= 8u )
Ptr = k->u._Ptr;
if ( Ptr[2] == 'Y' )
{
wstring_402B60(
Src,
L"The registration key is incorrect.\n"
"\n"
"The registration key from Proxifier v2 doesn't work with Proxifier v4. Please upgrade your license.\n"
"If you have any questions please contact support.",
0xB9u);
return 0;
}
v26 = Src;
memset(&v29, 0, 0xC);
v29.year_10 = 0;
v29.mon_14 = 0;
v29.calc_part3_1c = 0;
sub_43D770(&v25, k);
if ( !check_key_402430(&v29, v25, v26) )
{
wstring_402B60(
Src,
L"The registration key is incorrect.\n"
"\n"
"Please check that you have entered it correctly.\n"
"If the problem persists, please contact support.",
0x85u);
return 0;
}
if ( v29.Edition_id_0 == this->Edition_id )
{
if ( v29.product_version_4 != this->product_version )
{
wstring_402B60(
Src,
L"This registration key is for another version of this product.\n"
"It cannot be used with this version.\n"
"\n"
"If you have any questions please contact support.",
0x95u);
return 0;
}
if ( v29.product_version_low_8 < this->product_version_low )
{
wstring_402B60(
Src,
L"This registration key is for an older version of this product.\n"
"It cannot be used with the current version. Please update your license.\n"
"\n"
"If you have any questions please contact support.",
0xB9u);
return 0;
}
if ( v29.year_10 )
{
v35.tm_year = v29.year_10 - 0x76C;
v35.tm_mon = v29.mon_14;
memset(&v35, 0, 0xC);
memset(&v35.tm_wday, 0, 0xC);
v35.tm_mday = 1;
LODWORD(v18) = sub_60E285(&v35);
src = v18;
v19 = _time64(0);
v31 = src - v19;
if ( ((src - v19) >> 0x20) > 0 )
{
v20 = v31;
}
else if ( (((src - v19) >> 0x20) & 0x80000000) != 0i64 || (v20 = v31) == 0 )
{
sub_60B38F(v36, 0x1FF, L"This registration key expired on %B %d, %Y \n", &v35);
wstring_402B60(Src, v36, wcslen(v36));
sub_420700(Src, L"Please update your license.\n\nIf you have any questions please contact support.", 0x4Eu);
return 0;
}
this->expired_time_10 = src;
this->Days_until_expiration_4 = v20 / 0x15180;
}
else
{
this->expired_time_10 = 0i64;
}
v21 = v30;
v22 = v30;
this->registration_1 = 1;
this->is_not_expired_8 = 1;
v23 = v22 + 1;
while ( *v22++ )
;
wstring_402B60(&this->name_1C, v21, v22 - v23);
sub_402CC0(&this->dword34, k);
this->dword18 = v29.dw_c;
return 1;
}
else
{
v33._Mysize = 0;
v33._Myres = 7;
v33.u._Buf[0] = 0;
v37 = 0;
if ( v29.Edition_id_0 >= 3 )
{
v26 = 0xF;
v25._Myres = L"another product";
}
else
{
v11 = Edition_str_6A0C30[v29.Edition_id_0];
v26 = wcslen(v11);
v25._Myres = v11;
}
wstring_402B60(&v33, v25._Myres, v26);
v34._Mysize = 0;
v34._Myres = 7;
v34.u._Buf[0] = 0;
LOBYTE(v37) = 1;
Edition_id = this->Edition_id;
if ( Edition_id >= 3 )
{
v26 = 0xC;
v25._Myres = L"this product";
}
else
{
v13 = Edition_str_6A0C30[Edition_id];
v26 = wcslen(v13);
v25._Myres = v13;
}
wstring_402B60(&v34, v25._Myres, v26);
v14 = sub_431500(&v29.product_version_low_8, L"This registration key is for ", &v33);
LOBYTE(v37) = 2;
v15 = sub_43D270(&v35.tm_mday, v14, L".\nIt cannot be used with ");
LOBYTE(v37) = 3;
v16 = sub_431D20(&v27, v15, &v34);
LOBYTE(v37) = 4;
v17 = sub_43D270(v28, v16, L".\n\nIf you have any questions please contact support.");
sub_420820(v17);
sub_402EE0(v28);
sub_402EE0(&v27);
sub_402EE0(&v35.tm_mday);
sub_402EE0(&v29.product_version_low_8);
sub_402EE0(&v34);
sub_402EE0(&v33);
return 0;
}
}
check_key_402430 #校验key的关键点
char __thiscall check_key_402430(CheckRet *this, std_wstring key, std_wstring *out_result)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v31 = out_result;
check_data[6] = 0;
// XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
// 0-4 5 6-10 11 12-16 17 18-22 23 24-28
if ( key._Mysize == 29 )
{
p_key = &key;
if ( key._Myres >= 8u )
p_key = key.u._Ptr;
// 去除‘-’
wstring_remove_4027E0(&key, &t_calc_part4, p_key + 46);// [13]
Ptr = &key;
if ( key._Myres >= 8u )
Ptr = key.u._Ptr;
wstring_remove_4027E0(&key, &t_calc_part4, Ptr + 34);// [17]
v7 = &key;
if ( key._Myres >= 8u )
v7 = key.u._Ptr;
wstring_remove_4027E0(&key, &t_calc_part4, v7 + 22);// [11]
v8 = &key;
if ( key._Myres >= 8u )
v8 = key.u._Ptr;
wstring_remove_4027E0(&key, &t_calc_part4, v8 + 0xA);// [5]
// 移除‘-’后25d 个字符
v9 = &key;
if ( key._Myres >= 8u )
v9 = key.u._Ptr;
v10 = v9[14]; // [14]
v11 = &key;
if ( key._Myres >= 8u )
v11 = key.u._Ptr;
t_calc_part4 = &v29;
// plain_key[2]=>plain_key[14]
v11[2] = v10;
v29._Mysize = 0;
v29._Myres = 7;
v29.u._Buf[0] = 0;
if ( key._Mysize < 20 )
sub_402AF0();
v12 = 5;
v13 = 5;
if ( key._Mysize - 20 < 5 )
v13 = key._Mysize - 20;
v14 = &key;
if ( key._Myres >= 8u )
v14 = key.u._Ptr;
// 最后一部分
// part4 ==>[20:]
wstring_402B60(&v29, v14 + 0x14, v13);
t_calc_part4 = custom_checksum_402740(v29);
calc_part4 = t_calc_part4 ^ (t_calc_part4 << 7);
temp = &v29;
v29._Mysize = 0;
v29._Myres = 7;
v29.u._Buf[0] = 0;
if ( key._Mysize < 0xF )
sub_402AF0();
if ( key._Mysize - 0xF < 5 )
v12 = key._Mysize - 0xF;
v16 = &key;
if ( key._Myres >= 8u )
v16 = key.u._Ptr;
// 倒数第二部分
// part3 ==>[15:+5(max)]
wstring_402B60(&v29, v16 + 0xF, v12);
this->calc_part3_1c = custom_checksum_402740(v29);
v17 = 7;
temp = &v29;
Mysize = 7;
v29._Mysize = 0;
v29._Myres = 7;
v29.u._Buf[0] = 0;
v19 = &key;
if ( key._Mysize < 7 )
Mysize = key._Mysize;
if ( key._Myres >= 8u )
v19 = key.u._Ptr;
// [:7]
wstring_402B60(&v29, v19->u._Buf, Mysize);
temp = custom_checksum_402740(v29);
v30 = &v29;
v29._Mysize = 0;
v29._Myres = 7;
v29.u._Buf[0] = 0;
if ( key._Mysize < 7 )
sub_402AF0();
if ( key._Mysize - 7 < 7 )
v17 = key._Mysize - 7;
v20 = &key;
if ( key._Myres >= 8u )
v20 = key.u._Ptr;
// [7:+7(max)]
wstring_402B60(&v29, &v20->u._Buf[7], v17);
data1 = calc_part4 ^ custom_checksum_402740(v29) ^ 0x87654321;
data0 = calc_part4 ^ temp ^ 0x12345678;
check_data[0] = data0;
check_data[2] = this->calc_part3_1c;
xxcrc32_value = 0xFFFFFFFF;
check_data[1] = data1;
// xx_crc32
for ( i = 0; i < 0xC; ++i )
{
xxcrc32_value ^= *(check_data + i) << 0x18;
v24 = 8;
do
{
if ( xxcrc32_value >= 0 )
xxcrc32_value *= 2;
else
xxcrc32_value = (2 * xxcrc32_value) ^ 0x4C11DB7;
--v24;
}
while ( v24 );
}
if ( t_calc_part4 == (xxcrc32_value & 0x1FFFFFF) )
{
v25 = data0;
v26 = HIWORD(data0) & 0x1F;
// 0 'Proxifier Standard Edition'
// 1 'Proxifier Portable Edition'
// 2 'Proxifier for Mac'
this->Edition_id_0 = data0 >> 0x15; // 1
this->product_version_4 = v26; // 0
this->product_version_low_8 = v25 >> 5; // 0x190
v27 = HIWORD(data1); // 高WORD字节设置为0,则不设置过期时间
this->dw_c = v25 & 0x1F;
this->dw_18 = data1;
if ( HIWORD(data1) ) // 0 时不过期
{
this->year_10 = v27 / 12 + 2000;
v27 %= 12u;
}
else
{
this->year_10 = v27; // 0 时不过期
}
this->mon_14 = v27;
v4 = 1;
}
else
{
wstring_402B60(v31, L"Incorrect key", 0xDu);
v4 = 0;
}
}
else
{
wstring_402B60(out_result, L"Incorrect key length.", 0x15u);
v4 = 0;
}
sub_402EE0(&key);
return v4;
}
custom_checksum_402740
int __stdcall custom_checksum_402740(std_wstring a1)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
checksum = 0;
for ( i = a1._Mysize - 1; i >= 0; --i )
{
current_checksum = 0x20 * checksum;
Ptr = &a1;
if ( a1._Myres >= 8 )
Ptr = a1.u._Ptr;
temp_char = Ptr->u._Buf[i];
if ( temp_char == 'W' )
{
LOWORD(temp_char) = '0';
add = current_checksum - 0x30;
}
else
{
switch ( temp_char )
{
case 'X':
LOWORD(temp_char) = 'O';
break;
case 'Y':
LOWORD(temp_char) = '1';
add = current_checksum - 0x30;
goto LABEL_9;
case 'Z':
LOWORD(temp_char) = 'I';
break;
default:
if ( (temp_char - 0x30) <= 9u )
{
add = current_checksum - 0x30;
goto LABEL_9;
}
break;
}
add = current_checksum - 0x37;
}
LABEL_9:
checksum = temp_char + add;
}
sub_402EE0(&a1);
return checksum;
}
py
from enum import Enum
import random
from typing import Sequence
import string
str_table=string.ascii_uppercase+string.digits
class Product(Enum):
Standard=0
Portable=1
Mac=2
def xx_crc32(data:bytes):
crc_value = 0xFFFFFFFF
for i in range(len(data)):
crc_value ^= data[i] << 0x18
for j in range(8):
crc_value= 2*crc_value if crc_value<0x80000000 else (2 * crc_value) ^ 0x4C11DB7
crc_value &= 0xffffffff
return crc_value
def custom_checksum(input_str:Sequence):
if isinstance(input_str,bytes):
input_str=input_str.decode()
elif isinstance(input_str,(tuple,list)):
input_str=''.join(input_str)
input_len = len(input_str) # 字符串长度
checksum = 0
for i in range(input_len - 1, -1, -1):
current_sum = 0x20 * checksum
char = input_str[i]
temp_char = ord(char)
if char == 'W':
temp_char = ord('0') # 'W' 替换为 '0'
add_value = current_sum - 0x30
elif char == 'X':
temp_char = ord('O') # 'X' 替换为 'O'
add_value = current_sum - 0x37
elif char == 'Y':
temp_char = ord('1') # 'Y' 替换为 '1'
add_value = current_sum - 0x30
elif char == 'Z':
temp_char = ord('I') # 'Z' 替换为 'I'
add_value = current_sum - 0x37
elif '0' <= char <= '9': # 如果字符是数字
add_value = current_sum - 0x30
else:
add_value = current_sum - 0x37
checksum = temp_char + add_value
return checksum&0xffffffff
def re_custom_checksum(checksum:int,output_len:int):
checksum&=0xffffffff
output_str=['' for i in range(output_len)]
for i in range(output_len):
temp_value=checksum%0x20
checksum//=0x20
if temp_value==ord('0')-0x30:
output_str[i]='W'
elif temp_value==ord('O')-0x37:
output_str[i]='X'
elif temp_value==ord('1')-0x30:
output_str[i]='Y'
elif temp_value==ord('I')-0x37:
output_str[i]='Z'
elif 0<=temp_value<=9:
output_str[i]=str(temp_value)
else:
output_str[i]=chr(temp_value+0x37)
return ''.join(output_str)
def check_key(key:str):
if '-' in key:
key=key.replace('-','')
assert len(key)==25,"key len need 25!"
key_list=list(key)
key_list[2]=key_list[14]
t_calc_part4=custom_checksum(key_list[20:])
calc_part4=(t_calc_part4^(t_calc_part4<<7))&0xffffffff
calc_part3=custom_checksum(key_list[15:20])
calc_part0_1=custom_checksum(key_list[:7])
calc_part1_2=custom_checksum(key_list[7:14])
check_data0=calc_part0_1^calc_part4^0x12345678
# print('[-]check_data0:',check_data0)
check_data1=calc_part4^calc_part1_2^0x87654321
# print('[-]check_data1:',check_data1)
check_data2=calc_part3
# print('[-]check_data2:',check_data2)
check_data=check_data0.to_bytes(4,'little')+check_data1.to_bytes(4,'little')+check_data2.to_bytes(4,'little')
# print('[-]check_data:',check_data.hex())
xxcrc32_value=xx_crc32(check_data)
# print('[-]xxcrc32_value:',xxcrc32_value&0x1FFFFFF)
return (t_calc_part4==xxcrc32_value&0x1FFFFFF)
'''
// .rdata:006BB080 dword_6BB080 dd 1 ;Edition_id ; DATA XREF: sub_46DF40+69↑r
// .rdata:006BB084 dd 0 ;product_version
// .rdata:006BB088 dd 190h ;product_version_low
// .rdata:006BB08C dd 1Fh
'''
def gen_key(product:Product=Product.Portable,version_low:int=0x190):
key_14=random.choice(string.ascii_uppercase.replace('Y',''))
'''
check_data0
HIWORD >> 0x15为产品id
// 0 'Proxifier Standard Edition'
// 1 'Proxifier Portable Edition'
// 2 'Proxifier for Mac'
HIWORD &0x1f 为0
>>5 需大于版本0x190
'''
check_data0=(product.value<<0x15) | random.randint(version_low<<5,0xffff)
# print('[-]data0:',check_data0)
'''
check_data1
HIWORD(data1); // 高WORD字节设置为0,则不设置过期时间
'''
check_data1=random.randint(0,0xffff)
# print('[-]data1:',check_data1)
'''
check_data2 随机
check_data2=calc_part3
'''
part3_str=''.join(random.choices(str_table,k=5))
calc_part3=custom_checksum(part3_str)
'''
[15-20]
'''
part3_str=re_custom_checksum(calc_part3,5)
check_data2=calc_part3
# print('[-]data2:',check_data2)
check_data=check_data0.to_bytes(4,'little')+\
check_data1.to_bytes(4,'little')+\
check_data2.to_bytes(4,'little')
# print('[-]re_check_data:',check_data.hex())
t_calc_part4=xx_crc32(check_data)&0x1FFFFFF
# print('[-]t_calc_part4:',t_calc_part4)
'''
[20-25]
'''
part4_str=re_custom_checksum(t_calc_part4,5)
calc_part4=(t_calc_part4^(t_calc_part4<<7))&0xffffffff #可得
calc_part0_1=check_data0^calc_part4^0x12345678
'''
[0-7]
'''
part0_1_str=re_custom_checksum(calc_part0_1,7)
calc_part1_2=check_data1^calc_part4^0x87654321
'''
[7-14]
'''
part1_2_str=re_custom_checksum(calc_part1_2,7)
'''
正向计算时 [2]=[14],故逆向后part0_1_str 的[2]移动到[14]位置
'''
key=part0_1_str[:2]+key_14+part0_1_str[3:]+part1_2_str+part0_1_str[2]+part3_str+part4_str
list_key=list(key)
# print(key)
k='-'.join([''.join(list_key[i:i+5]) for i in range(0,25,5) ])
return k
def test():
k=gen_key(Product.Portable)
print(k)
x=check_key(k)
print('check:',x)
if __name__=='__main__':
# test()
k=gen_key(Product.Portable,0x190)
print(k)