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:
- 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
- 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)
- 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:
- If already licensed (flag at this+98 is set), returns immediately
- Otherwise, sets the license flag (this+98 = 1)
- Reads stored license key using
MainReg::ReadReg
- Contacts licensing server via
MainReg::ActivateFromLicenseServer
- Sets validity flag (this+97) based on response
- If valid:
- Updates window title
- Handles concurrent licensing if enabled
- If invalid:
- Checks if concurrent using
RegCrypt::IsConcurrent
- Generates registration URL
- Validates via network request
- Displays failure message if needed
- Checks if concurrent using
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).
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:
- Resets license flag:
*((_BYTE *)this + 100) = 0
- Reads stored license code via
GeneralSettings::GetRegistrationCode
GeneralSettings::GetRegistrationCode((__int64 *)&v73, (GeneralSettings *)v75, v4);
QString::trimmed_helper(&v74, &v73, v5);
*((QString *)this + 138) = v74;
- Reads license server IP and port
- Checks if license is concurrent
- Removes activation code if license is concurrent
- Retrieves and validates activation code
- Loads machine ID
- Performs validation using
RegCrypt::CheckActivation
- If validation fails, removes stored registration and activation codes
I attempted to patch (the second return 1LL;
):
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:
- Opens registration dialog
- Updates program’s registration state
- 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;
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:
- Retrieves registration and activation codes
- Calls
GeneralSettings::GetRegistrationCode
- Calls
GeneralSettings::GetActivationCode
- Calls
LoadMachineID
- Validates registration using
RegCrypt::CheckActivation
- 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);
}
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” buttonMainWindow::MainWindow
: Initializes main windowMainReg::RegFinished
: Handles registration completionRegCrypt::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