Proxifier Portable Edition v4.12 注册分析

Proxifier Portable Edition v4.12

MFC 程序分析

可从以下方面入手

1、res

查找是否存在静态dialog资源,(没有就上xspy)

image-20241020151224186

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

image-20241020151824234

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

image-20241020152333237

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)

ps

image-20241020145235261

image-20241020145339771

posted @ 2024-10-20 15:38  DirWangK  阅读(50)  评论(0编辑  收藏  举报