Online Activation Process

I tested the registration process using code “test”. The application appears to use the device’s UUID as part of the activation:

Device ID: 6C074FFE-0939-5A8A-BC0F-4994FCA543E0-5986

The application makes the following HTTP request:

curl 'http://reg.code-industry.net/mpe-5/pr/index-5.php?1=1&2=test&3=6C074FFE-0939-5A8A-BC0F-4994FCA543E0-5986&4=6d61634f532031352e33202d2031352e33' \
-H 'Host: reg.code-industry.net' \
-H 'Connection: Keep-Alive' \
-H 'Accept-Language: en-US,*' \
-H 'User-Agent: Mozilla/5.0'

Which returns:

not found

I confirmed the device UUID is retrieved using:

ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { print $3; }'

"6C074FFE-0939-5A8A-BC0F-4994FCA543E0"

The “5986” suffix indicates the software version (5.9.86).

According to Apple documentation, the hardware UUID persists across OS upgrades, reinstalls, and multi-boot installations, making it a reliable identifier for license enforcement.

Registration URL Generation

All online activation is handled by the RegCrypt::GetRegistrationURL function, which:

  1. Checks subscription status:
  • If active: uses URL http://reg.code-industry.net/mpe-5/pr/sub-5.php
  • Otherwise: uses URL http://reg.code-industry.net/mpe-5/pr/index-5.php
  1. Modifies the URL based on flags:
  • If a4’s first bit is set: appends “?1=1&2=”
  • Otherwise: appends “?1=2&2=”
  • Then appends the user identifier, “&3=”, and additional parameters (a2 and a3)
  1. Handles subscriptions differently:
  • For active subscriptions: adds more parameters including timestamp and encoded values
  • Otherwise: simply appends identifiers directly

This function is referenced by:

  • RegDialog::pb_deactive
  • RegDialog::pb_reg
  • MainWindow::ValidateLicense
  • MainReg::Deactivate

Offline Activation

The function sub_1002E4108 only initializes UI elements of the registration dialog (setting text for labels, buttons, and group boxes). It doesn’t handle button clicks or user interactions. This function is called by sub_1002E13F4.

License Validation Analysis

MainWindow::ValidateLicense

void __fastcall MainWindow::ValidateLicense(MainWindow *this, const QString *a2)
{
  // variables declarations removed for clarity

  if ( *(_DWORD *)(*((_QWORD *)this + 138) + 4LL) && !*((_BYTE *)this + 98) )
  {
    *((_BYTE *)this + 98) = 1;  // Set license flag
    
    if ( *(_DWORD *)(*((_QWORD *)this + 139) + 4LL) )
    {
      // License server validation path
      MainReg::ReadReg((MainReg *)&MainReg::GetInstance(void)::instance, &v32);
      
      // [Memory cleanup code removed]
      
      MainReg::ActivateFromLicenseServer(
        (MainReg *)&MainReg::GetInstance(void)::instance,
        (const QString *)this + 138,
        (const QString *)this + 143,
        (const QString *)this + 139,
        (const QString *)this + 140);
        
      MainReg::ReadReg((MainReg *)&MainReg::GetInstance(void)::instance, v28);
      *((_BYTE *)this + 97) = *((_DWORD *)v28[0].d + 1) != 0;  // Set validity flag
      
      // [Memory cleanup code removed]
      
      QDocTab::SetRegProgram(*((QDocTab **)this + 18), *((_BYTE *)this + 97));
      
      if ( *((_BYTE *)this + 97) )  // If license is valid
      {
        // Update window title and handle concurrent licensing
        // [Window title update code removed]
      }
      else if ( byte_101D16CC1 )
      {
        // Show license server connection failure
        QMetaObject::tr(&v29, (QMetaObject *)&MainWindow::staticMetaObject, 
                        "Failed to connect to License server", 0LL, -1);
        // [Message box code retained]
      }
    }
    else
    {
      // Local validation path
      QString::toLatin1_helper(v28, (QString *)this + 138, a2);
      IsConcurrent = RegCrypt::IsConcurrent((RegCrypt *)v28, v8);
      
      // [Memory cleanup code removed]
      
      if ( (IsConcurrent & 1) != 0 )
        return;
      
      // Generate registration URL and validate via network
      // [Network request setup code retained]
      v21 = QNetworkAccessManager::get(v19, (const QNetworkRequest *)&v29);
      QObject::connect(&v23, v21, "2finished()", this, "1Check_for_Validation_Finished()", 0LL);
      
      // [Cleanup code removed]
    }
  }
}

This function checks if the application is licensed:

  1. If already licensed (flag at this+98 is set), returns immediately
  2. Otherwise, sets the license flag (this+98 = 1)
  3. Reads stored license key using MainReg::ReadReg
  4. Contacts licensing server via MainReg::ActivateFromLicenseServer
  5. Sets validity flag (this+97) based on response
  6. If valid:
    • Updates window title
    • Handles concurrent licensing if enabled
  7. If invalid:
    • Checks if concurrent using RegCrypt::IsConcurrent
    • Generates registration URL
    • Validates via network request
    • Displays failure message if needed

The key conditional check is:

if ( *(_DWORD *)(*((_QWORD *)this + 138) + 4LL) && !*((_BYTE *)this + 98) )

Where !*((_BYTE *)this + 98) evaluates if the license flag is 0 (false).

assembly.png

Looking at the ARM64 assembly:

SUB   SP, SP, #0xB0        ; Allocate stack space
STP   X22, X21, [SP,#0xA0+var_20] ; Save registers
STP   X20, X19, [SP,#0xA0+var_10] ; Save registers
STP   X29, X30, [SP,#0xA0+var_s0] ; Save frame pointer & return address
ADD   X29, SP, #0xA0       ; Set frame pointer

LDR   X8, [X0,#0x450]      ; Load pointer from [this + 0x450]
LDR   W8, [X8,#4]          ; Load DWORD from offset 4
CBZ   W8, loc_1002E97C4    ; If 0, skip validation

MOV   X19, X0              ; Store this pointer in X19
LDRB  W8, [X0,#0x62]       ; Load byte at [this + 0x62] (license flag)
CBZ   W8, loc_1002E97D8    ; If zero, continue validation

loc_1002E97D8:
ADD   X20, X19, #0x450     ; X20 = (this + 0x450)
MOV   W8, #1               ; W8 = 1
STRB  W8, [X19,#0x62]      ; Store 1 at (this + 0x62) (set license flag)

I attempted to patch this by changing:

88 00 00 34 F3 03 00 AA 08 88 41 39 C8 00 00 34
to 
00 00 00 14 F3 03 00 AA 08 88 41 39 C8 00 00 34

However, this caused the app to fail with:

00:38:30 23.02.25  Registration:  Error: REG06
00:38:30 23.02.25  Main:  Main window initilization finished

MainWindow::VerifyLicense

Simply calls MainWindow::ValidateLicense.

MainWindow::ReadReg

__int64 __fastcall MainWindow::ReadReg(MainWindow *this)
{
  // Reset license flag
  *((_BYTE *)this + 100) = 0;
  
  // Clear any existing registration data
  // [Memory cleanup code removed]
  
  // Read license information from settings
  GeneralSettings::GeneralSettings((GeneralSettings *)v67);
  MainVersion = GeneralSettings::GetMainVersion((GeneralSettings *)v67);
  
  // Get registration code
  GeneralSettings::GetRegistrationCode((GeneralSettings *)v67, v4, v5);
  QString::trimmed_helper(&v66, &v65, v6);
  *((QString *)this + 138) = v66;
  
  // [Memory cleanup code removed]
  
  // Get license server IP
  GeneralSettings::GetLicenseServerIp((__int64 *)&v66, (GeneralSettings *)v67, v10);
  *((QString *)this + 139) = v66;
  
  // Get license server port
  LicenseServerPort = (QString *)GeneralSettings::GetLicenseServerPort((GeneralSettings *)v67);
  QString::number(&v66, LicenseServerPort, 10, v15);
  *((QString *)this + 140) = v66;
  
  // Check if license is concurrent
  if ( !*((_DWORD *)*v13 + 1) )
    goto LABEL_47;
    
  QString::toLatin1_helper(&v66, (QString *)this + 138, v16);
  IsConcurrent = RegCrypt::IsConcurrent((RegCrypt *)&v66, v18);
  
  // [Memory cleanup code removed]
  
  if ( IsConcurrent )
  {
    // Remove activation code if license is concurrent
    QPDFSettings::Remove((QPDFSettings *)v67, v22);
    // [Memory cleanup code removed]
  }
  else
  {
LABEL_47:
    // Get activation code for non-concurrent license
    GeneralSettings::GetActivationCode((__int64 *)&v65, (GeneralSettings *)v67);
    QString::trimmed_helper(&v66, &v65, v24);
    *((QString *)this + 141) = v66;
  }
  
  // Load machine ID
  LoadMachineID(&v66);
  *((QString *)this + 143) = v66;
  
  // [Machine ID handling code removed]
  
  // Version check logic
  if ( MainVersion <= 5729 && MainVersion )
  {
    // Handle old version
    // [Old version code removed]
    return 0LL;
  }
  
  // Validation check
  if ( *(_DWORD *)(*((_QWORD *)this + 139) + 4LL) || *(int *)(*((_QWORD *)this + 141) + 4LL) > 50 )
  {
    if ( *((int *)*v13 + 1) < 18 || *(int *)(*((_QWORD *)this + 141) + 4LL) < 40 )
      return 0LL;
      
    // Perform license validation
    RegCrypt::CheckActivation(&v58, v41, v42, v40, &v65, 0LL);
    
    // [Memory cleanup code removed]
    
    return 1LL;  // This is the key return value for license validation success
  }
  
  // License validation failed path
  QPDFSettings::Remove((QPDFSettings *)v67, v34);
  
  // Log error
  Logger::LogMessage(v47, v50, v51);
  
  // [Memory cleanup code removed]
  
  return 0LL;  // Return failure
}

This function:

  1. Resets license flag: *((_BYTE *)this + 100) = 0
  2. Reads stored license code via GeneralSettings::GetRegistrationCode
GeneralSettings::GetRegistrationCode((__int64 *)&v73, (GeneralSettings *)v75, v4);
QString::trimmed_helper(&v74, &v73, v5);
*((QString *)this + 138) = v74;
  1. Reads license server IP and port
  2. Checks if license is concurrent
  3. Removes activation code if license is concurrent
  4. Retrieves and validates activation code
  5. Loads machine ID
  6. Performs validation using RegCrypt::CheckActivation
  7. If validation fails, removes stored registration and activation codes

I attempted to patch (the second return 1LL;): assembly3.png

TBZ  W20, #0, loc_100078328
  
74 00 00 36 20 00 80 52 6E 00 00 14 E8 00 80 52
to
1F 20 03 D5   ; NOP (No Operation)

MainWindow::on_actionRegister_triggered

This function handles the “Register” button click:

  1. Opens registration dialog
  2. Updates program’s registration state
  3. Triggers license validation
void __fastcall MainWindow::on_actionRegister_triggered(MainWindow *this)
{
  // Initial setup and flags
  if ( (*(_BYTE *)(*((_QWORD *)this + 5) + 10LL) & 1) != 0 )
    *((_BYTE *)this + 101) = 1;
    
  // Create and show registration dialog
  v2 = (RegDialog *)operator new(0xE8uLL);
  RegDialog::RegDialog(v2, this);
  RegDialog::SetRegProgram(v2, *((_BYTE *)this + 97), (const QString *)this + 143);
  (*(void (__fastcall **)(RegDialog *))(*(_QWORD *)v2 + 424LL))(v2);
  
  // Get registration status from dialog
  v14 = 0;
  RegProgram = RegDialog::getRegProgram(v2, &v14);
  *((_BYTE *)this + 97) = RegProgram;  // Set validity flag
  *((_BYTE *)this + 98) = RegProgram;  // Set license flag
  
  // Update window title
  QString::operator=((char *)this + 1088, &qword_101D78C40);
  
  // Handle unregistered state
  if ( *((_BYTE *)this + 97) || *((_BYTE *)this + 391) )
    goto LABEL_17;
    
  // [Unregistered title update code removed]
  
LABEL_17:
  // Clean up dialog
  (*(void (__fastcall **)(RegDialog *))(*(_QWORD *)v2 + 32LL))(v2);
  
  // Update window title and document tabs
  MainWindow::SetWindowTitle(this, v8);
  QDocTab::SetRegProgram(*((QDocTab **)this + 18), *((_BYTE *)this + 97));
  
  // Validate license if newly registered
  if ( *((_BYTE *)this + 97) )
  {
    if ( v14 )
    {
      *((_BYTE *)this + 98) = 0;  // Reset license flag
      MainWindow::ReadReg(this);  // Read registration info
      MainWindow::ValidateLicense(this, v10);  // Validate license
    }
  }
}

Key code section to patch:

RegProgram = RegDialog::getRegProgram(v2, &v14);
*((_BYTE *)this + 97) = RegProgram;
*((_BYTE *)this + 98) = RegProgram;

assembly2.png

I modified:

1B 86 09 94   ; BL RegDialog::getRegProgram
60 86 01 39   ; STRB W0, [X19,#0x61]
60 8A 01 39   ; STRB W0, [X19,#0x62]
75 02 11 91   ; ADD X21, X19, #0x440
to
20 00 80 52   ; MOV W0, #1
60 86 01 39   ; STRB W0, [X19,#0x61] (Store 1 in this + 0x61)
60 8A 01 39   ; STRB W0, [X19,#0x62] (Store 1 in this + 0x62)
75 02 11 91   ; ADD X21, X19, #0x440 (Keep existing instruction)

MainReg::ReadReg

atomic_uint *__usercall MainReg::ReadReg@<X0>(MainReg *this@<X0>, _QWORD *a2@<X8>)
{
  // [Variables and initial setup removed for clarity]

  // Get registration and activation information
  GeneralSettings::GeneralSettings((GeneralSettings *)v33);
  GeneralSettings::GetRegistrationCode((GeneralSettings *)v33, v5, v6);
  // [Memory management code removed]
  GeneralSettings::GetActivationCode((__int64 *)&v34, (GeneralSettings *)v33);
  // [Memory management code removed]
  
  // Load machine ID for validation
  LoadMachineID(&v31[4]);
  *(_DWORD *)v31 = -1;
  
  // Check if registration/activation codes are valid length
  if ( d[1] >= 18 && v10[1] >= 19 )
  {
    // Prepare parameters for validation
    v30.d = d;  // Registration code
    v29 = *(atomic_uint **)&v31[4];  // Machine ID
    v28 = v10;  // Activation code
    
    // Validate the registration
    RegCrypt::CheckActivation(&v30, v14, v15, v13, (const QString *)v31, 0LL);
    
    // [Memory cleanup code removed]
    
    // Store result and return
    *a2 = *(_QWORD *)&v31[4];
    *(_QWORD *)&v31[4] = &QArrayData::shared_null;
  }
  else
  {
    // Log error if codes don't meet length requirements
    v19 = (Logger *)*((_QWORD *)this + 9);
    v27 = (atomic_uint *)QString::fromAscii_helper((QString *)"Registration", (const char *)0xC, v12);
    QString::number(&v34, (QString *)*(unsigned int *)v31, 10, v20);
    QString::fromUtf8_helper(&v26, (QString *)"Error: REG0", (const char *)0xB, v21);
    QString::append(&v26, &v34);
    Logger::LogMessage(v19, v22, v23);
    
    // [Memory cleanup code removed]
    
    *a2 = &QArrayData::shared_null;
  }
  
  // [Final memory cleanup removed]
  
  return result;
}

This function:

  1. Retrieves registration and activation codes
  • Calls GeneralSettings::GetRegistrationCode
  • Calls GeneralSettings::GetActivationCode
  • Calls LoadMachineID
  1. Validates registration using RegCrypt::CheckActivation
  2. Handles validation results

Potentially patchable section:

RegCrypt::CheckActivation(&v30, v14, v15, v13, (const QString *)v31, 0LL);
    v16 = v28;
    if ( *v28 != -1 )
    {
      if ( *v28 )
      {
        if ( atomic_fetch_add(v28, 0xFFFFFFFF) != 1 )
          goto LABEL_29;
        v16 = v28;
      }
      QArrayData::deallocate(v16, 2LL, 8LL);
    }

assembly4.png

BL   __ZN8RegCrypt15CheckActivationE7QStringS0_S0_RKS0_Rib ; RegCrypt::CheckActivation
TBZ  W0, #0, loc_100392FF0

20 05 00 36 E0 0F 40 F9 08 00 40 B9 1F 05 00 31

Could patch with:

E0 0F 40 F9   ; MOV W0, #1

or

1F 20 03 D5   ; NOP (No Operation)

Additional Functions to analyse later

  • TrialMessage::Setup: Sets up trial message UI with watermark and “Register” button
  • MainWindow::MainWindow: Initializes main window
  • MainReg::RegFinished: Handles registration completion
  • RegCrypt::CheckActivation: Core validation function

Additional Patch

I didn’t like the Incorrect registration code. even if the random code was working so I changed :

00496E636F727265637420726567697374726174696F6E20636F64652E00
to
00637261636B65642062792061717A73203A290000000000000000000000

Working for now

Activation check bypassed :)