Educational purposes only. This article is a reverse engineering writeup intended purely for learning. The name of the software has been redacted to protect the developer’s work and avoid facilitating piracy. Do not use the techniques described here to bypass software protection on software you do not own or have not purchased.
A friend asked for help activating a copy of [Redacted software name], a small barcode creation tool that costs $128. This writeup is not about the software itself, but about the reverse engineering journey involved in understanding how its license validation works.
At launch, the software greets you with a welcome screen and a button to enter a license key:


Tools used
Before diving in, here is a quick overview of the tools used throughout this analysis:
- IDA Pro: Industry-standard disassembler and decompiler. Used here to decompile the binary and understand the high-level logic of the license validation routines.
- LLDB: The LLVM debugger. Used to set breakpoints, step through instructions at runtime, and inspect register and memory state during execution.
- lldbinit: An LLDB configuration script that enhances the debugger UI with a more informative register/code view, visible in the register dumps throughout this writeup.
The software is built with REALbasic, now called Xojo. This is relevant because Xojo produces binaries with very descriptive symbol names, which makes the decompiled output significantly easier to follow than a typical C++ or stripped binary.
Finding the entry point
Searching for the license-related string in the binary quickly reveals the relevant function. It is the event handler for the "OK" button in the license entry window.
Here is the full decompiled code:See the raw pseudo code
__int64 __fastcall EnterCodeWin_EnterCodeWin_OKBtn_Action__o_EnterCodeWin_EnterCodeWin_o_PushButton_(
__int64 a1,
__int64 a2)
{
// variables declaration
v98 = 0;
v97 = 0;
v96 = 0;
v95 = 0;
v93 = 0;
v92 = 0;
v91 = 0;
v90 = 0;
v89 = 0;
v88 = 0;
v87 = 0;
v85 = 0;
v84 = 0;
v83 = 0;
v82 = 0;
v81 = 0;
v80 = 0;
v79 = 0;
v78 = 0;
v77 = 0;
v76 = 0;
v75 = 0;
v74 = 0;
v73 = 0;
v72 = 0;
v71 = 0;
v70 = 0;
v69 = 0;
v68 = 0;
v67 = 0;
v65 = 0;
v64 = 0;
v63 = 0;
v62 = 0;
v61 = 0;
v60 = 0;
v59 = 0;
v58 = 0;
v56 = 0;
v55 = 0;
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
if ( gCurrentException )
goto LABEL_2;
v98 = 0;
v2 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a1 + 328LL))(a1);
if ( gCurrentException )
goto LABEL_2;
v98 = v2;
if ( !v2 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
s_o_TextField_i4_0 = TextField_Text_Get_s_o_TextField_i4_0(v98, 0);
if ( gCurrentException )
goto LABEL_2;
RuntimeLockUnlockStrings(s_o_TextField_i4_0, 0);
v97 = s_o_TextField_i4_0;
RuntimeUnlockString(s_o_TextField_i4_0);
RuntimeUnlockObject(v98);
if ( gCurrentException )
goto LABEL_2;
v98 = 0;
if ( (unsigned int)RuntimeStringCompare(s_o_TextField_i4_0, 0) )
{
RuntimeUnlockString(0);
v96 = 0;
v52 = j__REALbasic_Left_s_si8(s_o_TextField_i4_0, 2);
if ( gCurrentException )
goto LABEL_2;
v96 = v52;
if ( !(unsigned int)RuntimeStringCompare(v52, &unk_1007D25E8) )// "\x02PS"
goto LABEL_16;
RuntimeUnlockString(0);
v51 = j__REALbasic_Left_s_si8(s_o_TextField_i4_0, 2);
if ( gCurrentException )
goto LABEL_2;
v95 = v51;
if ( (unsigned int)RuntimeStringCompare(v51, &unk_1007D2610) )// "\x02TH"
v94 = 0;
else
LABEL_16:
v94 = 1;
RuntimeUnlockString(v52);
v96 = 0;
RuntimeUnlockString(v95);
if ( v94 )
{
v93 = 0;
v92 = 0;
v89 = 0;
v90 = 0;
v91 = 0;
v4 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v91 = v4;
if ( !v4 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
RuntimeUnlockString(0);
v92 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v93 = 0;
v5 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v93 = v5;
if ( !v5 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v6 = (*(__int64 (__fastcall **)(__int64, void *))(*(_QWORD *)v93 + 8LL))(v93, &unk_1007D2638);// "�You have entered your order number. Yo"...
v49 = v6;
if ( gCurrentException )
goto LABEL_2;
v92 = v6;
v7 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v91 + 8LL))(v91, v6);
v48 = v7;
if ( gCurrentException )
goto LABEL_2;
v90 = v7;
v89 = RuntimeAddString(v7, &unk_1007D2660);// "kIf you have not yet received it, pleas"...
j__REALbasic_MsgBox__s(v89);
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockString(v89);
v89 = 0;
RuntimeUnlockString(v48);
v90 = 0;
RuntimeUnlockObject(v91);
if ( gCurrentException )
goto LABEL_2;
v91 = 0;
RuntimeUnlockString(v49);
v92 = 0;
RuntimeUnlockObject(v93);
if ( gCurrentException )
goto LABEL_2;
v93 = 0;
}
else
{
RuntimeUnlockString(0);
v88 = 0;
v50 = j__REALbasic_Left_s_si8(s_o_TextField_i4_0, 4);
if ( gCurrentException )
goto LABEL_2;
v88 = v50;
if ( !(unsigned int)RuntimeStringCompare(v50, &unk_1007D2688) )// "\x04EZBC"
goto LABEL_41;
RuntimeUnlockString(0);
v47 = j__REALbasic_Left_s_si8(s_o_TextField_i4_0, 4);
if ( gCurrentException )
goto LABEL_2;
v87 = v47;
if ( (unsigned int)RuntimeStringCompare(v47, &unk_1007D26B0) )// "\x04EBC2"
v86 = 0;
else
LABEL_41:
v86 = 1;
RuntimeUnlockString(v50);
v88 = 0;
RuntimeUnlockString(v87);
if ( v86 )
{
v85 = 0;
v84 = 0;
v81 = 0;
v82 = 0;
v83 = 0;
v9 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v83 = v9;
if ( !v9 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
RuntimeUnlockString(0);
v84 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v85 = 0;
v10 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v85 = v10;
if ( !v10 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v11 = (*(__int64 (__fastcall **)(__int64, void *))(*(_QWORD *)v85 + 8LL))(v85, &unk_1007D26D8);// "HYou have entered a code from a previou"...
v46 = v11;
if ( gCurrentException )
goto LABEL_2;
v84 = v11;
v12 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v83 + 8LL))(v83, v11);
v45 = v12;
if ( gCurrentException )
goto LABEL_2;
v82 = v12;
v81 = RuntimeAddString(v12, &unk_1007D2700);// ":Click Order Online to order an upgrade"...
j__REALbasic_MsgBox__s(v81);
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockString(v81);
v81 = 0;
RuntimeUnlockString(v45);
v82 = 0;
RuntimeUnlockObject(v83);
if ( gCurrentException )
goto LABEL_2;
v83 = 0;
RuntimeUnlockString(v46);
v84 = 0;
RuntimeUnlockObject(v85);
if ( gCurrentException )
goto LABEL_2;
v85 = 0;
}
else
{
if ( !SNStuff_sn )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v13 = (**(__int64 (__fastcall ***)(__int64, __int64))SNStuff_sn)(SNStuff_sn, s_o_TextField_i4_0);
if ( gCurrentException )
goto LABEL_2;
if ( v13 )
{
if ( Preferences_Prefs || (RaiseNilObjectException(), !gCurrentException) )
{
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v75 = 0;
v18 = StringToVariant(&unk_1007D2778);// "\x0Eregisteredcode"
v42 = v18;
if ( !gCurrentException )
{
v75 = v18;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v74 = 0;
v41 = StringToVariant(s_o_TextField_i4_0);
if ( !gCurrentException )
{
v74 = v41;
(*(void (__fastcall **)(__int64, __int64, __int64))(*(_QWORD *)Preferences_Prefs + 64LL))(
Preferences_Prefs,
v42,
v41);
if ( !gCurrentException )
{
RuntimeUnlockObject(v42);
if ( !gCurrentException )
{
v75 = 0;
RuntimeUnlockObject(v41);
if ( !gCurrentException )
{
v74 = 0;
j__Preferences_Save_b_();
if ( !gCurrentException )
{
RuntimeUnlockString(0);
v69 = 0;
RuntimeUnlockString(0);
v70 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v71 = 0;
v19 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( !gCurrentException )
{
v71 = v19;
if ( v19 || (RaiseNilObjectException(), !gCurrentException) )
{
RuntimeUnlockString(0);
v72 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v73 = 0;
v20 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( !gCurrentException )
{
v73 = v20;
if ( v20 || (RaiseNilObjectException(), !gCurrentException) )
{
v21 = (*(__int64 (__fastcall **)(__int64, void *))(*(_QWORD *)v73 + 8LL))(
v73,
&unk_1007D27A0);// "'Your activation code has been accepted"...
v40 = v21;
if ( !gCurrentException )
{
v72 = v21;
v22 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v71 + 8LL))(
v71,
v21);
v39 = v22;
if ( !gCurrentException )
{
v70 = v22;
v69 = RuntimeAddString(v22, &unk_1007D27C8);// "\x1CThank you for your purchase!"
j__REALbasic_MsgBox__s(v69);
if ( !gCurrentException )
{
RuntimeUnlockString(v69);
v69 = 0;
RuntimeUnlockString(v39);
v70 = 0;
RuntimeUnlockObject(v71);
if ( !gCurrentException )
{
v71 = 0;
RuntimeUnlockString(v40);
v72 = 0;
RuntimeUnlockObject(v73);
if ( !gCurrentException )
{
v73 = 0;
v65 = 0;
v64 = 0;
v63 = 0;
v62 = 0;
v61 = 0;
v60 = 0;
v59 = 0;
v58 = 0;
v56 = 0;
v55 = 0;
v67 = 0;
v68 = 0;
v23 = EasyBarcodeCreatorWin_EasyBarcodeCreatorWin_o_EasyBarcodeCreatorWin_EasyBarcodeCreatorWin___0();
if ( !gCurrentException )
{
v68 = v23;
if ( v23 || (RaiseNilObjectException(), !gCurrentException) )
{
v24 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v68 + 592LL))(v68);
if ( !gCurrentException )
{
v67 = v24;
if ( v24 || (RaiseNilObjectException(), !gCurrentException) )
{
v38 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v67 + 256LL))(v67);
if ( !gCurrentException )
{
for ( i = v38 - 1; i >= 0; --i )
{
v62 = 0;
v61 = 0;
v60 = 0;
v59 = 0;
v58 = 0;
v56 = 0;
v55 = 0;
v63 = 0;
v64 = 0;
v65 = 0;
v25 = EasyBarcodeCreatorWin_EasyBarcodeCreatorWin_o_EasyBarcodeCreatorWin_EasyBarcodeCreatorWin___0();
if ( gCurrentException )
goto LABEL_2;
v65 = v25;
if ( !v25 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v26 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v65 + 592LL))(v65);
if ( gCurrentException )
goto LABEL_2;
v64 = v26;
if ( !v26 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v27 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v64 + 272LL))(
v64,
i);
if ( gCurrentException )
goto LABEL_2;
v63 = v27;
if ( !v27 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
s_o_ToolItem_i4_0 = ToolItem_Name_Get_s_o_ToolItem_i4_0(
v63,
0);
if ( gCurrentException )
goto LABEL_2;
v62 = s_o_ToolItem_i4_0;
if ( !(unsigned int)RuntimeStringCompare(
s_o_ToolItem_i4_0,
&unk_1007D27F0) )// "\x0EOrderOnlineBtn"
goto LABEL_148;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v59 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v60 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v61 = 0;
v29 = EasyBarcodeCreatorWin_EasyBarcodeCreatorWin_o_EasyBarcodeCreatorWin_EasyBarcodeCreatorWin___0();
if ( gCurrentException )
goto LABEL_2;
v61 = v29;
if ( !v29 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v30 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v61 + 592LL))(v61);
if ( gCurrentException )
goto LABEL_2;
v60 = v30;
if ( !v30 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v31 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v60 + 272LL))(
v60,
i);
if ( gCurrentException )
goto LABEL_2;
v59 = v31;
if ( !v31 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v36 = ToolItem_Name_Get_s_o_ToolItem_i4_0(v59, 0);
if ( gCurrentException )
goto LABEL_2;
v58 = v36;
if ( (unsigned int)RuntimeStringCompare(
v36,
&unk_1007D2818) )// "\x14OrderOnlineSeparator"
v57 = 0;
else
LABEL_148:
v57 = 1;
RuntimeUnlockString(s_o_ToolItem_i4_0);
v62 = 0;
RuntimeUnlockObject(v63);
if ( gCurrentException )
goto LABEL_2;
v63 = 0;
RuntimeUnlockObject(v64);
if ( gCurrentException )
goto LABEL_2;
v64 = 0;
RuntimeUnlockObject(v65);
if ( gCurrentException )
goto LABEL_2;
v65 = 0;
RuntimeUnlockString(v58);
v58 = 0;
RuntimeUnlockObject(v59);
if ( gCurrentException )
goto LABEL_2;
v59 = 0;
RuntimeUnlockObject(v60);
if ( gCurrentException )
goto LABEL_2;
v60 = 0;
RuntimeUnlockObject(v61);
if ( gCurrentException )
goto LABEL_2;
v61 = 0;
if ( v57 )
{
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v55 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v56 = 0;
v33 = EasyBarcodeCreatorWin_EasyBarcodeCreatorWin_o_EasyBarcodeCreatorWin_EasyBarcodeCreatorWin___0();
if ( gCurrentException )
goto LABEL_2;
v56 = v33;
if ( !v33 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v34 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v56 + 592LL))(v56);
if ( gCurrentException )
goto LABEL_2;
v55 = v34;
if ( !v34 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v55 + 280LL))(
v55,
i);
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockObject(v55);
if ( gCurrentException )
goto LABEL_2;
v55 = 0;
RuntimeUnlockObject(v56);
if ( gCurrentException )
goto LABEL_2;
v56 = 0;
}
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v65 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v64 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v63 = 0;
RuntimeUnlockString(0);
v62 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v61 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v60 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v59 = 0;
RuntimeUnlockString(0);
v58 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v56 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v55 = 0;
RuntimeBackgroundTask();
if ( gCurrentException )
goto LABEL_2;
}
RuntimeUnlockObject(v68);
if ( !gCurrentException )
{
v68 = 0;
RuntimeUnlockObject(v67);
if ( !gCurrentException )
{
v67 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v65 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v64 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v63 = 0;
RuntimeUnlockString(0);
v62 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v61 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v60 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v59 = 0;
RuntimeUnlockString(0);
v58 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v56 = 0;
RuntimeUnlockObject(0);
if ( !gCurrentException )
{
v55 = 0;
(*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 48LL))(a1);
if ( !gCurrentException )
goto LABEL_14;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
LABEL_2:
v54 = RuntimeTakeCurrentException();
goto LABEL_214;
}
v80 = 0;
v79 = 0;
v76 = 0;
v77 = 0;
v78 = 0;
v14 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v78 = v14;
if ( !v14 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
RuntimeUnlockString(0);
v79 = 0;
RuntimeUnlockObject(0);
if ( gCurrentException )
goto LABEL_2;
v80 = 0;
v15 = REALbasic_EndOfLine_o_EndOfLine___0();
if ( gCurrentException )
goto LABEL_2;
v80 = v15;
if ( !v15 )
{
RaiseNilObjectException();
if ( gCurrentException )
goto LABEL_2;
}
v16 = (*(__int64 (__fastcall **)(__int64, void *))(*(_QWORD *)v80 + 8LL))(v80, &unk_1007D2728);// "$The activation code appears invalid."
v44 = v16;
if ( gCurrentException )
goto LABEL_2;
v79 = v16;
v17 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v78 + 8LL))(v78, v16);
v43 = v17;
if ( gCurrentException )
goto LABEL_2;
v77 = v17;
v76 = RuntimeAddString(v17, &unk_1007D2750);// "$Please check the code and try again."
j__REALbasic_MsgBox__s(v76);
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockString(v76);
v76 = 0;
RuntimeUnlockString(v43);
v77 = 0;
RuntimeUnlockObject(v78);
if ( gCurrentException )
goto LABEL_2;
v78 = 0;
RuntimeUnlockString(v44);
v79 = 0;
RuntimeUnlockObject(v80);
if ( gCurrentException )
goto LABEL_2;
v80 = 0;
}
}
}
else
{
j__REALbasic_MsgBox__s(&unk_1007D25C0); // " Please enter an activation code."
if ( gCurrentException )
goto LABEL_2;
}
LABEL_14:
v54 = 0;
LABEL_214:
RuntimeUnlockObject(a1);
RuntimeUnlockObject(a2);
RuntimeUnlockObject(v98);
RuntimeUnlockString(0);
RuntimeUnlockString(v97);
RuntimeUnlockString(v96);
RuntimeUnlockString(0);
RuntimeUnlockObject(v93);
RuntimeUnlockString(v92);
RuntimeUnlockObject(v91);
RuntimeUnlockString(v90);
RuntimeUnlockString(v89);
RuntimeUnlockString(v88);
RuntimeUnlockString(0);
RuntimeUnlockObject(v85);
RuntimeUnlockString(v84);
RuntimeUnlockObject(v83);
RuntimeUnlockString(v82);
RuntimeUnlockString(v81);
RuntimeUnlockObject(v80);
RuntimeUnlockString(v79);
RuntimeUnlockObject(v78);
RuntimeUnlockString(v77);
RuntimeUnlockString(v76);
RuntimeUnlockObject(v75);
RuntimeUnlockObject(v74);
RuntimeUnlockObject(v73);
RuntimeUnlockString(v72);
RuntimeUnlockObject(v71);
RuntimeUnlockString(v70);
RuntimeUnlockString(v69);
RuntimeUnlockObject(v68);
RuntimeUnlockObject(v67);
RuntimeUnlockObject(v65);
RuntimeUnlockObject(v64);
RuntimeUnlockObject(v63);
RuntimeUnlockString(v62);
RuntimeUnlockObject(v61);
RuntimeUnlockObject(v60);
RuntimeUnlockObject(v59);
RuntimeUnlockString(v58);
RuntimeUnlockObject(v56);
result = RuntimeUnlockObject(v55);
if ( v54 )
{
RuntimeReraiseException(v54);
return RuntimeUnlockObject(v54);
}
return result;
}
Here is a cleaned up and annotated version for readability:
__int64 __fastcall EnterCodeWin_EnterCodeWin_OKBtn_Action(__int64 a1, __int64 a2)
{
__int64 vTextField;
__int64 vInput;
__int64 vStr;
__int64 vWin;
__int64 vToolbar;
__int64 vItem;
__int64 vItemName;
int i;
// Get input from TextField
vTextField = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a1 + 328))(a1);
vInput = TextField_Text_Get(vTextField, 0);
if ( RuntimeStringCompare(vInput, 0) ) // If input is not empty
{
// Check for Order Number prefixes (PS, TH)
vStr = j__REALbasic_Left_s_si8(vInput, 2);
if ( !RuntimeStringCompare(vStr, "\x02PS") || !RuntimeStringCompare(vStr, "\x02TH") )
{
j__REALbasic_MsgBox__s("You have entered your order number..");
return 0;
}
// Check for Old Code prefixes (EZBC, EBC2)
vStr = j__REALbasic_Left_s_si8(vInput, 4);
if ( !RuntimeStringCompare(vStr, "\x04EZBC") || !RuntimeStringCompare(vStr, "\x04EBC2") )
{
j__REALbasic_MsgBox__s("You have entered a code from a previous version..");
return 0;
}
// Validate Serial Number
if ( (**(__int64 (__fastcall ***)(__int64, __int64))SNStuff_sn)(SNStuff_sn, vInput) )
{
// Save valid code to preferences
(*(void (__fastcall **)(__int64, __int64, __int64))(*(_QWORD *)Preferences_Prefs + 64LL))(
Preferences_Prefs,
StringToVariant("registeredcode"),
StringToVariant(vInput));
j__Preferences_Save_b_();
j__REALbasic_MsgBox__s("Your activation code has been accepted..");
// Remove 'Order Online' items from toolbar
vWin = EasyBarcodeCreatorWin_EasyBarcodeCreatorWin___0();
vToolbar = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)vWin + 592LL))(vWin);
for ( i = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)vToolbar + 256LL))(vToolbar) - 1; i >= 0; --i )
{
vItem = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)vToolbar + 272LL))(vToolbar, i);
vItemName = ToolItem_Name_Get(vItem, 0);
if ( !RuntimeStringCompare(vItemName, "\x0EOrderOnlineBtn") ||
!RuntimeStringCompare(vItemName, "\x14OrderOnlineSeparator") )
{
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)vToolbar + 280LL))(vToolbar, i);
}
}
// Close window
(*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 48LL))(a1);
}
else
{
j__REALbasic_MsgBox__s("The activation code appears invalid..");
}
}
else
{
j__REALbasic_MsgBox__s(" Please enter an activation code.");
}
return 0;
}
EnterCodeWin_EnterCodeWin_OKBtn_Action is the event handler for the "OK" button in the EnterCodeWin window. Its job is to validate the user’s input and act accordingly.
Understanding the activation code validation flow
Here is a detailed breakdown of the logic flow:
Initial input collection
The function retrieves text from the input field into vInput. If the field is empty, it immediately prompts the user to enter an activation code.
Prefix validation checks
The function then inspects the first few characters to determine what type of code was entered. If the first two characters are PS or TH, the code is recognized as an order number rather than an activation code. If the first four characters are EZBC or EBC2, the code is identified as belonging to a previous version of the software.
Core validation
If the input passes the prefix checks, the function hands it off to a validation routine called SNStuff_sn. This is the real gatekeeper, the function that determines whether a serial number is actually valid.
On success, the code is stored in the app’s preferences under the key registeredcode, the "Order Online" toolbar buttons are removed by iterating over toolbar items, and the activation window is closed. On failure, an error message is shown.
The remaining “mystery”
Everything hinges on how SNStuff_sn works internally. Here is how it is called in the pseudocode:
v13 = (**(__int64 (__fastcall ***)(__int64, __int64))SNStuff_sn)(SNStuff_sn, s_o_TextField_i4_0);
Diving into SNStuff_sn
This section cleans up string locks from previous operations and checks whether the global SNStuff_sn object has been initialized before attempting to use it:
__text:000000010045C182|loc_10045C182:__text:000000010045C182|mov rdi, [rbp+var_88] ; Load pointer to a string object__text:000000010045C189|call _RuntimeUnlockString ; Unlock the string (decrement lock count)__text:000000010045C18E|mov [rbp+var_88], 0__text:000000010045C199|mov rdi, [rbp+var_98] ; Load pointer to another string object__text:000000010045C1A0|call _RuntimeUnlockString ; Unlock the string (decrement lock count)__text:000000010045C1A5|mov [rbp+var_98], 0 ; Clear the local variable__text:000000010045C1B0|cmp [rbp+var_A1], 0 ; Check a local flag__text:000000010045C1B7|jnz short loc_10045C1CF ; If flag is set, skip the SNStuff check__text:000000010045C1B9|lea rax, _SNStuff_sn ; Load address of the global SNStuff_sn object__text:000000010045C1C0|cmp qword ptr [rax], 0 ; Check if SNStuff_sn is initialized__text:000000010045C1C4|jnz loc_10045C450 ; If SNStuff_sn exists, jump to validation code__text:000000010045C1CA|jmp loc_10045C482 ; If SNStuff_sn is not initialized, jump to error handlingIf SNStuff_sn is null, execution jumps to an error handler that raises a nil object exception:
__text:000000010045C482|loc_10045C482: ; CODE XREF: _EnterCodeWin_EnterCodeWin_OKBtn_Action__o_EnterCodeWin_EnterCodeWin_o_PushButton_+89A↑j__text:000000010045C482|call _RaiseNilObjectException ; Raise an exception for nil object__text:000000010045C487|mov rax, cs:_gCurrentException_ptr__text:000000010045C48E|cmp qword ptr [rax], 0__text:000000010045C492|jnz loc_10045BBD8 ; ; If exception raised, jump to handler (RuntimeTakeCurrentException)__text:000000010045C498|jmp short loc_10045C450If it is initialized, the code proceeds to call the validation function through the vtable of SNStuff_sn. It loads the object pointer, resolves the first entry in the vtable, sets up the arguments following the ABI (rdi = this, rsi = input string), and performs the call:
__text:000000010045C450|loc_10045C450:__text:000000010045C450|lea rax, _SNStuff_sn ; Load address of SNStuff_sn__text:000000010045C457|mov rax, [rax] ; Dereference: Get the actual object pointer__text:000000010045C45A|mov rcx, [rax] ; Dereference: Get the vtable (pointer to function pointers)__text:000000010045C45D|mov rcx, [rcx] ; Dereference: Get the first function pointer from the vtable (the validation function)__text:000000010045C460|mov rsi, [rbp+var_28] ; Load 2nd argument: The input string__text:000000010045C464|mov rdi, rax ; Load 1st argument: The 'this' pointer (SNStuff_sn)__text:000000010045C467|call rcx ; Call the validation function__text:000000010045C469|mov rcx, cs:_gCurrentException_ptr__text:000000010045C470|cmp qword ptr [rcx], 0__text:000000010045C474|mov [rbp+var_2B1], al ; Store the result of validation (boolean in al) in a local variable__text:000000010045C47A|jnz loc_10045BBD8 ; If exception raised, jump to handler__text:000000010045C480|jmp short loc_10045C49A ; If no exception, continue with the resultSetting a breakpoint at 0x10045C450 in LLDB and stepping to the call rcx instruction at 0x10045C467, LLDB helpfully resolves the target for us:
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x00000001007D2058 RBX: 0x00007FE12E11BC40 RBP: 0x0000000304CF0790 RSP: 0x0000000304CF03D0 o d I t s z a P c
RDI: 0x0000600000C30680 RSI: 0x0000600000C306AA RDX: 0x0000000000000001 RCX: 0x000000010AC0DE38 RIP: 0x000000010045C450
R8: 0x0000000000000001 R9: 0x000000010AEBA9B0 R10: 0x00000000C8400000 R11: 0x0000000000000008 R12: 0x0000600003AF6080
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x0000600003D921C0
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
EnterCodeWin.EnterCodeWin.OKBtn_Action%%o<EnterCodeWin.EnterCodeWin>o<PushButton> @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x10045c450 (0x10045c450): 48 8d 05 01 5c 37 00 lea rax, [rip + 0x375c01] ; SNStuff.sn
0x10045c457 (0x10045c457): 48 8b 00 mov rax, qword ptr [rax]
0x10045c45a (0x10045c45a): 48 8b 08 mov rcx, qword ptr [rax]
0x10045c45d (0x10045c45d): 48 8b 09 mov rcx, qword ptr [rcx]
0x10045c460 (0x10045c460): 48 8b 75 d8 mov rsi, qword ptr [rbp - 0x28]
0x10045c464 (0x10045c464): 48 89 c7 mov rdi, rax
0x10045c467 (0x10045c467): ff d1 call rcx
0x10045c469 (0x10045c469): 48 8b 0d c0 1d 35 00 mov rcx, qword ptr [rip + 0x351dc0] ; (void *)0x000000010af011a0: gCurrentException
-----------------------------------------------------------------------------------------------------------------------------
Process 28636 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x000000010045c450 [REDACTED]`EnterCodeWin.EnterCodeWin.OKBtn_Action%%o<EnterCodeWin.EnterCodeWin>o<PushButton> + 2848
We can step through the instructions to see how the function pointer is obtained and called, at 0x10045c467. Here is the state of the registers when we hit the breakpoint at the call instruction:
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x000060000159D608 RBX: 0x00007FD2B81EF2C0 RBP: 0x0000000304CF33E0 RSP: 0x0000000304CF3020 o d I t s z a p c
RDI: 0x000060000159D608 RSI: 0x0000600001AE4E80 RDX: 0x0000000000000001 RCX: 0x000000010044E140 RIP: 0x000000010045C467
R8: 0x0000000000000001 R9: 0x000000010AEBA9B0 R10: 0x00000000C0C00000 R11: 0x00000000000000C3 R12: 0x000060000238C5A0
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x000060000246A160
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
EnterCodeWin.EnterCodeWin.OKBtn_Action%%o<EnterCodeWin.EnterCodeWin>o<PushButton> @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x10045c467 (0x10045c467): ff d1 call rcx ; IntelliSerial.ValidateSN%b%o<IntelliSerial>s @ 0x10044e140 @ [REDACTED].app/Contents/MacOS/[REDACTED]
0x10045c469 (0x10045c469): 48 8b 0d c0 1d 35 00 mov rcx, qword ptr [rip + 0x351dc0] ; (void *)0x000000010af011a0: gCurrentException
0x10045c470 (0x10045c470): 48 83 39 00 cmp qword ptr [rcx], 0x0
0x10045c474 (0x10045c474): 88 85 4f fd ff ff mov byte ptr [rbp - 0x2b1], al
0x10045c47a (0x10045c47a): 0f 85 58 f7 ff ff jne 0x10045bbd8 ; <+680>
0x10045c480 (0x10045c480): eb 18 jmp 0x10045c49a ; <+2922>
0x10045c482 (0x10045c482): e8 e9 1e 28 00 call 0x1006de370 ; symbol stub for: RaiseNilObjectException
0x10045c487 (0x10045c487): 48 8b 05 a2 1d 35 00 mov rax, qword ptr [rip + 0x351da2] ; (void *)0x000000010af011a0: gCurrentException
-----------------------------------------------------------------------------------------------------------------------------
Process 33347 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x000000010045c467 [REDACTED]`EnterCodeWin.EnterCodeWin.OKBtn_Action%%o<EnterCodeWin.EnterCodeWin>o<PushButton> + 2871
The actual validation function is IntelliSerial_ValidateSN_b_o_IntelliSerial_s (identified by LLDB). It returns a boolean indicating whether the serial is valid.
The validation function: IntelliSerial_ValidateSN
Here is the original decompiled code for the validation function, IntelliSerial_ValidateSN_b_o_IntelliSerial_s:See the raw pseudo code
bool __fastcall IntelliSerial_ValidateSN_b_o_IntelliSerial_s(__int64 a1, __int64 a2)
{
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
double v10; // xmm0_8
double v11; // xmm0_8
__int64 v12; // rax
double v13; // xmm0_8
__int64 v14; // rax
double v15; // xmm0_8
__int64 v17; // [rsp+8h] [rbp-308h]
__int64 v18; // [rsp+28h] [rbp-2E8h]
unsigned int v19; // [rsp+34h] [rbp-2DCh]
__int64 v20; // [rsp+48h] [rbp-2C8h]
__int64 v21; // [rsp+50h] [rbp-2C0h]
bool v22; // [rsp+6Fh] [rbp-2A1h]
__int64 v23; // [rsp+70h] [rbp-2A0h]
__int64 v24; // [rsp+A0h] [rbp-270h]
__int64 v25; // [rsp+A8h] [rbp-268h]
__int64 v26; // [rsp+C0h] [rbp-250h]
__int64 v27; // [rsp+D0h] [rbp-240h]
__int64 v28; // [rsp+E0h] [rbp-230h]
__int64 v29; // [rsp+F0h] [rbp-220h]
__int64 v30; // [rsp+100h] [rbp-210h]
__int64 v31; // [rsp+108h] [rbp-208h]
__int64 v32; // [rsp+110h] [rbp-200h]
__int64 v33; // [rsp+118h] [rbp-1F8h]
__int64 v34; // [rsp+120h] [rbp-1F0h]
__int64 v35; // [rsp+130h] [rbp-1E0h]
__int64 v36; // [rsp+160h] [rbp-1B0h]
__int64 v37; // [rsp+168h] [rbp-1A8h]
__int64 v38; // [rsp+180h] [rbp-190h]
__int64 v39; // [rsp+188h] [rbp-188h]
__int64 v40; // [rsp+228h] [rbp-E8h]
__int64 v41; // [rsp+238h] [rbp-D8h]
char v42; // [rsp+25Eh] [rbp-B2h]
__int64 v43; // [rsp+268h] [rbp-A8h]
char v44; // [rsp+276h] [rbp-9Ah]
__int64 v45; // [rsp+280h] [rbp-90h]
char v46; // [rsp+28Eh] [rbp-82h]
__int64 v47; // [rsp+298h] [rbp-78h]
char v48; // [rsp+2A6h] [rbp-6Ah]
__int64 v49; // [rsp+2C8h] [rbp-48h]
__int64 v50; // [rsp+2D0h] [rbp-40h]
__int64 v51; // [rsp+2D8h] [rbp-38h]
int i; // [rsp+2E4h] [rbp-2Ch]
int v53; // [rsp+2E4h] [rbp-2Ch]
__int64 v54; // [rsp+2F0h] [rbp-20h]
bool v55; // [rsp+2FFh] [rbp-11h]
__int64 v56; // [rsp+300h] [rbp-10h]
v56 = a2;
v55 = 0;
v54 = 0;
v51 = 0;
v50 = 0;
v49 = 0;
v47 = 0;
v45 = 0;
v43 = 0;
v41 = 0;
v40 = 0;
v39 = 0;
v38 = 0;
v37 = 0;
v36 = 0;
RuntimeLockObject(a1);
RuntimeLockString(a2);
RuntimeStackCheck();
if ( gCurrentException )
goto LABEL_2;
v49 = 0;
v50 = 0;
v2 = j__REALbasic_ReplaceAll_s_sss(a2, &unk_1007D20A0, 0);// "\x01 "
v34 = v2;
if ( gCurrentException )
goto LABEL_2;
v50 = v2;
v33 = j__REALbasic_Trim_s_s(v2);
if ( gCurrentException )
goto LABEL_2;
v49 = v33;
v32 = j__REALbasic_Uppercase_s_s(v33);
if ( gCurrentException )
goto LABEL_2;
RuntimeLockUnlockStrings(v32, a2);
v56 = v32;
RuntimeUnlockString(v32);
RuntimeUnlockString(v33);
v49 = 0;
RuntimeUnlockString(v34);
v50 = 0;
v31 = j__REALbasic_Len_i8_s(v32);
if ( gCurrentException )
goto LABEL_2;
if ( v31 != 20 )
goto LABEL_8;
v30 = j__REALbasic_CountFields_i8_ss(v32, &unk_1007D20C8);// "\x01-"
if ( gCurrentException )
goto LABEL_2;
if ( v30 == 3 )
v48 = 0;
else
LABEL_8:
v48 = 1;
if ( v48 )
goto LABEL_13;
RuntimeUnlockString(0);
v47 = 0;
v3 = j__REALbasic_NthField_s_ssi8(v32, &unk_1007D20C8, 1);// "\x01-"
if ( gCurrentException )
goto LABEL_2;
v47 = v3;
v29 = j__REALbasic_Len_i8_s(v3);
if ( gCurrentException )
goto LABEL_2;
if ( v29 != 4 )
LABEL_13:
v46 = 1;
else
v46 = 0;
if ( v46 )
goto LABEL_19;
RuntimeUnlockString(0);
v45 = 0;
v4 = j__REALbasic_NthField_s_ssi8(v32, &unk_1007D20C8, 2);// "\x01-"
if ( gCurrentException )
goto LABEL_2;
v45 = v4;
v28 = j__REALbasic_Len_i8_s(v4);
if ( gCurrentException )
goto LABEL_2;
if ( v28 != 8 )
LABEL_19:
v44 = 1;
else
v44 = 0;
if ( v44 )
goto LABEL_25;
RuntimeUnlockString(0);
v43 = 0;
v5 = j__REALbasic_NthField_s_ssi8(v32, &unk_1007D20C8, 3);// "\x01-"
if ( gCurrentException )
goto LABEL_2;
v43 = v5;
v27 = j__REALbasic_Len_i8_s(v5);
if ( gCurrentException )
goto LABEL_2;
if ( v27 != 6 )
LABEL_25:
v42 = 1;
else
v42 = 0;
RuntimeUnlockString(v47);
v47 = 0;
RuntimeUnlockString(v45);
v45 = 0;
RuntimeUnlockString(v43);
v43 = 0;
if ( v42 )
{
v55 = 0;
}
else
{
RuntimeUnlockString(0);
v6 = j__REALbasic_ReplaceAll_s_sss(v32, &unk_1007D20C8, 0);// "\x01-"
v26 = v6;
if ( gCurrentException )
goto LABEL_2;
RuntimeLockUnlockStrings(v6, v32);
v56 = v26;
RuntimeUnlockString(v26);
v7 = j__REALbasic_Len_i8_s(v26);
if ( gCurrentException )
goto LABEL_2;
if ( v7 == 18 )
{
for ( i = 5; i <= 18LL; ++i )
{
v40 = 0;
v41 = 0;
v9 = j__REALbasic_Mid_s_si8i8(v26, i, 1);
v24 = v9;
if ( gCurrentException )
goto LABEL_2;
v41 = v9;
v40 = RuntimeAddString(&unk_1007D20F0, v9);// "\x02&h"
v10 = j__REALbasic_Val_f8_s(v40);
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockString(v40);
v40 = 0;
RuntimeUnlockString(v24);
v41 = 0;
if ( (int)v10 >= 16LL )
{
v55 = 0;
goto LABEL_34;
}
RuntimeUnlockString(0);
v41 = 0;
RuntimeUnlockString(0);
v40 = 0;
RuntimeBackgroundTask();
if ( gCurrentException )
goto LABEL_2;
}
RuntimeUnlockString(0);
v41 = 0;
RuntimeUnlockString(0);
v40 = 0;
RuntimeUnlockString(0);
v8 = j__REALbasic_Mid_s_si8i8(v26, 5, 8);
v25 = v8;
if ( !gCurrentException )
{
RuntimeLockUnlockStrings(v8, 0);
v51 = v25;
RuntimeUnlockString(v25);
v11 = j__REALbasic_Val_f8_s(v25);
if ( !gCurrentException )
{
v53 = (int)v11;
RuntimeUnlockString(0);
v23 = j__REALbasic_Format_s_f8s(&unk_1007D2118);// "\b00000000"
if ( !gCurrentException )
{
v22 = (unsigned int)RuntimeStringCompare(v23, v25) != 0;
RuntimeUnlockString(v23);
if ( v22 )
{
v55 = 0;
goto LABEL_34;
}
if ( v53 < 300000LL )
{
v55 = 0;
goto LABEL_34;
}
if ( (v53 - 300000LL) % 7 )
{
v55 = 0;
goto LABEL_34;
}
RuntimeUnlockString(0);
v21 = j__REALbasic_LeftB_s_si8(v26, 4);
if ( !gCurrentException )
{
RuntimeLockUnlockStrings(v21, 0);
v54 = v21;
RuntimeUnlockString(v21);
RuntimeUnlockString(0);
v38 = 0;
RuntimeUnlockString(0);
v39 = 0;
v12 = j__REALbasic_MidB_s_si8i8(v26, 5, 8);
v20 = v12;
if ( !gCurrentException )
{
v39 = v12;
v38 = RuntimeAddString(&unk_1007D20F0, v12);// "\x02&h"
v13 = j__REALbasic_Val_f8_s(v38);
if ( !gCurrentException )
{
v19 = (int)v13;
RuntimeUnlockString(v38);
v38 = 0;
RuntimeUnlockString(v20);
v39 = 0;
RuntimeUnlockString(0);
v36 = 0;
RuntimeUnlockString(0);
v37 = 0;
v14 = j__REALbasic_MidB_s_si8i8(v26, 13, 6);
v18 = v14;
if ( !gCurrentException )
{
v37 = v14;
v36 = RuntimeAddString(&unk_1007D20F0, v14);// "\x02&h"
v15 = j__REALbasic_Val_f8_s(v36);
if ( !gCurrentException )
{
RuntimeUnlockString(v36);
v36 = 0;
RuntimeUnlockString(v18);
v37 = 0;
v17 = IntelliSerial_SNFunc_i8_o_IntelliSerial_si4_0(a1, v21, v19);
if ( !gCurrentException )
{
v55 = v17 == (int)v15;
goto LABEL_34;
}
}
}
}
}
}
}
}
}
LABEL_2:
v35 = RuntimeTakeCurrentException();
goto LABEL_64;
}
v55 = 0;
}
LABEL_34:
v35 = 0;
LABEL_64:
RuntimeUnlockObject(a1);
RuntimeUnlockString(v56);
RuntimeUnlockString(v54);
RuntimeUnlockString(v51);
RuntimeUnlockString(v50);
RuntimeUnlockString(v49);
RuntimeUnlockString(0);
RuntimeUnlockString(v47);
RuntimeUnlockString(v45);
RuntimeUnlockString(v43);
RuntimeUnlockString(0);
RuntimeUnlockString(v41);
RuntimeUnlockString(v40);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeUnlockString(v39);
RuntimeUnlockString(v38);
RuntimeUnlockString(v37);
RuntimeUnlockString(v36);
if ( v35 )
{
RuntimeReraiseException(v35);
RuntimeUnlockObject(v35);
}
return v55;
}
Here is a cleaned up and commented version of the function, with the same logic but more readable variable names and structure:
bool __fastcall IntelliSerial_ValidateSN_b_o_IntelliSerial_s(__int64 a1, __int64 a2)
{
__int64 vCleanStr;
__int64 vNoDashStr;
__int64 vPart1;
__int64 vPart2;
__int64 vPart3;
__int64 vTempStr;
int vBlock2Int;
int vBlock3Int;
int vChecksum;
int i;
double vVal;
// Clean and format input
vCleanStr = j__REALbasic_ReplaceAll(a2, " ", 0);
vCleanStr = j__REALbasic_Trim(vCleanStr);
vCleanStr = j__REALbasic_Uppercase(vCleanStr);
// Format Check (Length and Dashes)
if ( j__REALbasic_Len(vCleanStr) != 20 )
return 0;
if ( j__REALbasic_CountFields(vCleanStr, "-") != 3 )
return 0;
vPart1 = j__REALbasic_NthField(vCleanStr, "-", 1);
vPart2 = j__REALbasic_NthField(vCleanStr, "-", 2);
vPart3 = j__REALbasic_NthField(vCleanStr, "-", 3);
if ( j__REALbasic_Len(vPart1) != 4 ||
j__REALbasic_Len(vPart2) != 8 ||
j__REALbasic_Len(vPart3) != 6 )
return 0;
// Content Validation
vNoDashStr = j__REALbasic_ReplaceAll(vCleanStr, "-", 0);
if ( j__REALbasic_Len(vNoDashStr) != 18 )
return 0;
// Loop through characters 5 to 18 (0-based: 4 to 17)
// This covers Part 2 and Part 3. Checks if they are valid hex characters (0-F)
for ( i = 4; i < 18; ++i )
{
vTempStr = j__REALbasic_Mid(vNoDashStr, i + 1, 1); // Xojo is 1-based
vVal = j__REALbasic_Val( RuntimeAddString("&h", vTempStr) );
if ( vVal >= 16.0 ) // Valid hex digits return 0-15
return 0;
}
// Validate Part 2 (Numeric constraints)
// Extract Part 2 again (or reuse vPart2) to ensure we are working with the raw digits
vTempStr = j__REALbasic_Mid(vNoDashStr, 5, 8);
vBlock2Int = (int)j__REALbasic_Val(vTempStr);
// Check if the string matches the formatted integer (ensures no non-numeric chars)
if ( RuntimeStringCompare(j__REALbasic_Format(vBlock2Int, "00000000"), vTempStr) )
return 0;
if ( vBlock2Int < 300000 )
return 0;
if ( (vBlock2Int - 300000) % 7 != 0 )
return 0;
// Validate Part 3 (Checksum)
vTempStr = j__REALbasic_Mid(vNoDashStr, 13, 6); // Part 3
vBlock3Int = (int)j__REALbasic_Val( RuntimeAddString("&h", vTempStr) );
// Compute and compare checksum
vChecksum = IntelliSerial_SNFunc_i8_o_IntelliSerial_si4_0(a1, vPart1, vBlock2Int);
return vChecksum == vBlock3Int;
}
Step-by-step breakdown
Input normalization: Before any logic runs, the input is stripped of spaces, trimmed, and uppercased.
Format check: The serial must be exactly 20 characters long and follow the structure
XXXX-XXXXXXXX-XXXXXX, giving three segments separated by two dashes. Segment lengths must be 4, 8, and 6 characters respectively. Any deviation causes an immediatefalsereturn.Hex character check: After stripping the dashes to get an 18-character string, the function verifies that every character in segments 2 and 3 (positions 5-18) is a valid hexadecimal digit (
0-9,A-F). It does this by prepending&hto each character and checking that the parsed value is less than 16.Part 2 numeric constraints: The 8-character second segment is interpreted as a decimal integer (not hexadecimal). That decimal value must satisfy two conditions: it must be at least
300,000, and(value - 300,000)must be perfectly divisible by7.Checksum: If all previous checks pass, the function calls
IntelliSerial_SNFuncwith Part 1 and the decimal value of Part 2 as inputs. The result must equal the integer value of Part 3 (interpreted as hex) for the serial to be accepted.
Summary of the rules
| Segment | Length | Constraints |
|---|---|---|
| Part 1 (A) | 4 chars | Any characters |
| Part 2 (B) | 8 chars | Valid hex digits; decimal value ≥ 300,000; (decimal − 300,000) % 7 == 0 |
| Part 3 (C) | 6 chars | Valid hex digits; must equal SNFunc(Part1, Part2_decimal) |
Format: AAAA-BBBBBBBB-CCCCCC
A valid test input to start exploring with is EBC3-00300007-AAAAAA.
The checksum function: IntelliSerial_SNFunc
Now that we understand the overall shape of the key, let’s look at IntelliSerial_SNFunc_i8_o_IntelliSerial_si4_0, which computes the final checksum.
// This is a wrapper function that simply forwards the call to the actual implementation (part of Xojo's method dispatch system)
__int64 __fastcall IntelliSerial_SNFunc_i8_o_IntelliSerial_si4_0(__int64 a1, __int64 a2, unsigned int a3)
{
return IntelliSerial_SNFunc_i8_o_IntelliSerial_si4(a1, a2, a3);
}
// Main function to calculate the third part of the serial number (the checksum).
__int64 __fastcall IntelliSerial_SNFunc_i8_o_IntelliSerial_si4(__int64 a1, __int64 a2, unsigned int a3)
{
int v3;
int v4;
__int64 v6;
__int64 v7;
__int64 v9;
__int64 v10;
__int64 v11;
__int64 v12;
__int64 v13;
v13 = 0;
v12 = 0;
v11 = 0;
v10 = 0;
// Lock the object and input string to ensure thread safety and prevent garbage collection
RuntimeLockObject(a1);
RuntimeLockString(a2);
RuntimeStackCheck();
// Exception handling block
if ( gCurrentException )
goto LABEL_2;
v10 = 0;
v12 = 0;
// Convert the first part of the serial (String) to Uppercase
v7 = j__REALbasic_Uppercase_s_s(a2);
if ( gCurrentException
// Generate a "Hex Phrase" string based on the second part of the serial (Integer)
|| (v12 = v7, v11 = 0, v6 = IntelliSerial_HexPhrase_s_o_IntelliSerial_i4_0(a1, a3), gCurrentException)
// Concatenate the Uppercase Part 1 and the Hex Phrase generated from Part 2
|| (v11 = v6,
v10 = RuntimeAddString(v7, v6),
// Calculate the raw checksum integer using the concatenated string and the original Part 2 integer
v3 = IntelliSerial_SNRawFunc_i8_o_IntelliSerial_si8_0(a1, v10, a3),
gCurrentException)
// Apply a bitmask (0xFFFFFF) to the result.
// This limits the checksum to 24 bits (hex range 000000 to FFFFFF), which corresponds to 6 characters.
|| (v4 = j__SNStuff_MyBitwiseAnd_i4_i4i4(v3, 0xFFFFFF), gCurrentException) )
{
LABEL_2:
// Capture the current exception object
v9 = RuntimeTakeCurrentException();
}
else
{
// If successful, store the final masked checksum
v13 = v4;
v9 = 0;
}
// Cleanup: Unlock all resources
RuntimeUnlockObject(a1);
RuntimeUnlockString(a2);
RuntimeUnlockString(v12);
RuntimeUnlockString(v11);
RuntimeUnlockString(v10);
// If an exception was caught, re-raise it
if ( v9 )
{
RuntimeReraiseException(v9);
RuntimeUnlockObject(v9);
}
// Return the calculated checksum
return v13;
}
So first we need to take a look at the function IntelliSerial_HexPhrase_s_o_IntelliSerial_i4_0 which is calling IntelliSerial_HexPhrase_s_o_IntelliSerial_i4:
// This function converts an integer into a "Hex Phrase" string.
// It works by converting the integer to a hexadecimal string, then replacing
// each hex digit (0-F) with a corresponding word string retrieved from a lookup table.
__int64 __fastcall IntelliSerial_HexPhrase_s_o_IntelliSerial_i4(__int64 a1, unsigned int a2)
{
__int64 v2;
__int64 v3;
double v4;
__int64 v6;
__int64 v7;
__int64 v8;
int v9;
__int64 v10;
__int64 v11;
__int64 v12;
__int64 v13;
int i;
__int64 v15;
__int64 v16;
__int64 v17;
v17 = 0;
v16 = 0;
v15 = 0;
v13 = 0;
v12 = 0;
RuntimeLockObject(a1);
RuntimeStackCheck();
if ( gCurrentException
// Convert the integer a2 to its Hexadecimal string representation (e.g. 255 -> "FF")
|| (v2 = j__REALbasic_Hex_s_i4(a2), v10 = v2, gCurrentException)
// Lock the hex string and get its byte length
|| (RuntimeLockString(v2), v16 = v10, RuntimeUnlockString(v10), v9 = j__REALbasic_LenB_i8_s(v10), gCurrentException) )
{
LABEL_2:
v11 = RuntimeTakeCurrentException();
}
else
{
// Iterate through each character (byte) of the hex string
for ( i = 1; v9 >= i; ++i )
{
v12 = 0;
v13 = 0;
// Extract one character at position i
v3 = j__REALbasic_MidB_s_si8i8(v10, i, 1);
v8 = v3;
if ( gCurrentException )
goto LABEL_2;
v13 = v3;
// Prepend "&h" to the character to parse it as a hex number like before
v12 = RuntimeAddString(&unk_1007D20F0, v3); // unk_1007D20F0 is likely "&h"
// Convert the hex string character to its numeric value (0-15)
v4 = j__REALbasic_Val_f8_s(v12);
if ( gCurrentException )
goto LABEL_2;
// Retrieve the "Word" corresponding to this hex digit value
// This acts as a substitution cipher (0 -> word0, 1 -> word1, etc.)
v7 = IntelliSerial_Word_s_o_IntelliSerial_i8_0(a1, (unsigned int)(int)v4);
if ( gCurrentException )
goto LABEL_2;
// Append the retrieved word to the result string
v6 = RuntimeAddString(v15, v7);
RuntimeLockUnlockStrings(v6, v15);
v15 = v6; // Update result string
// Cleanup
RuntimeUnlockString(v6);
RuntimeUnlockString(v7);
RuntimeUnlockString(v12);
RuntimeUnlockString(v8);
RuntimeUnlockString(0);
v13 = 0;
RuntimeUnlockString(0);
v12 = 0;
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeBackgroundTask();
if ( gCurrentException )
goto LABEL_2;
}
// Finalize result
RuntimeUnlockString(0);
v13 = 0;
RuntimeUnlockString(0);
v12 = 0;
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeLockUnlockStrings(v15, 0);
v17 = v15; // Set return value to the constructed phrase
v11 = 0;
}
// Cleanup
RuntimeUnlockObject(a1);
RuntimeUnlockString(v16);
RuntimeUnlockString(v15);
RuntimeUnlockString(0);
RuntimeUnlockString(v13);
RuntimeUnlockString(v12);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
if ( v11 )
{
RuntimeReraiseException(v11);
RuntimeUnlockObject(v11);
}
return v17;
}
// This function retrieves a specific "Word" string based on an index
__int64 __fastcall IntelliSerial_Word_s_o_IntelliSerial_i8(__int64 a1)
{
__int64 v1;
__int64 v3;
__int64 v4;
__int64 v5;
__int64 v6;
v6 = 0;
v5 = 0;
RuntimeLockObject(a1);
RuntimeStackCheck();
// Check if the property at offset (a1 - 8) is valid.
if ( gCurrentException
|| !*(_QWORD *)(a1 - 8) && (RaiseNilObjectException(), gCurrentException)
|| (RuntimeUnlockString(0),
v5 = 0,
v1 = (**(__int64 (__fastcall ***)(_QWORD))(*(_QWORD *)(a1 - 8) + 48LL))(*(_QWORD *)(a1 - 8)),
v3 = v1,
gCurrentException) )
{
v4 = RuntimeTakeCurrentException();
}
else
{
v5 = v1;
RuntimeLockUnlockStrings(v1, 0);
v6 = v3; // Store the retrieved word
v4 = 0;
}
RuntimeUnlockObject(a1);
RuntimeUnlockString(v5);
if ( v4 )
{
RuntimeReraiseException(v4);
RuntimeUnlockObject(v4);
}
return v6;
}
This code is pretty boring, so I won’t go into too much detail. It returns a string composed of words representing the hexadecimal digits of the input, a simple substitution cipher. These words are retrieved from a lookup table.
The IntelliSerial_Word_s_o_IntelliSerial_i8 function retrieves the word for a given index. Setting a breakpoint on the internal array lookup call and stepping through for each possible digit (0-F), we can recover the full table by inspecting the returned string pointer:
v1 = (**(__int64 (__fastcall ***)(_QWORD))(*(_QWORD *)(a1 - 8) + 48LL))(*(_QWORD *)(a1 - 8))
So I set a breakpoint at 0x100450478 to see the call like I did before :
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x00006000037F2820 RBX: 0x00007F9A20039950 RBP: 0x0000000304CF2B30 RSP: 0x0000000304CF2AE0 o d I t s z a p c
RDI: 0x00006000037F2820 RSI: 0x0000000000000003 RDX: 0x0000000000000003 RCX: 0x000000010AD32E6D RIP: 0x0000000100450478
R8: 0x00000000000003A2 R9: 0x000000010AD61C7E R10: 0x000000010AE9CF3C R11: 0x0000000000229F46 R12: 0x00006000030B43C0
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x000060000370D3E0
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
IntelliSerial.Word%s%o<IntelliSerial>i8 @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x100450478 (0x100450478): ff d1 call rcx ; ___lldb_unnamed_symbol_239e6d @ 0x10ad32e6d @ [REDACTED].app/Contents/Frameworks/XojoFramework.framework/Versions/A/XojoFramework
0x10045047a (0x10045047a): 48 8b 0d af dd 35 00 mov rcx, qword ptr [rip + 0x35ddaf] ; (void *)0x000000010af011a0: gCurrentException
0x100450481 (0x100450481): 48 83 39 00 cmp qword ptr [rcx], 0x0
0x100450485 (0x100450485): 48 89 45 b8 mov qword ptr [rbp - 0x48], rax
0x100450489 (0x100450489): 75 a0 jne 0x10045042b ; <+59>
0x10045048b (0x10045048b): eb 14 jmp 0x1004504a1 ; <+177>
0x10045048d (0x10045048d): e8 de de 28 00 call 0x1006de370 ; symbol stub for: RaiseNilObjectException
0x100450492 (0x100450492): 48 8b 05 97 dd 35 00 mov rax, qword ptr [rip + 0x35dd97] ; (void *)0x000000010af011a0: gCurrentException
-----------------------------------------------------------------------------------------------------------------------------
Breakpoint name: word_call
Process 41814 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
frame #0: 0x0000000100450478 [REDACTED]`IntelliSerial.Word%s%o<IntelliSerial>i8 + 136
Then looking at the symbol at 0x10ad32e6d in LLDB :
(lldbinit) image lookup -a 0x10ad32e6d
Address: XojoFramework[0x0000000000239e6d] (XojoFramework.__TEXT.__text + 2327245)
Summary: XojoFramework`___lldb_unnamed_symbol_239e6d
Loading XojoFramework in IDA and looking at the symbol at 0x239e6d, it’s a sub function part of SimpleArray<stringStorage *,(RuntimeArray::Types)3>::LinearMethods:
__int64 __fastcall sub_239E6D(__int64 a1, int a2)
{
__int64 v2; // rbx
__int64 v3; // rax
__int64 StringOps; // rax
if ( a2 >= 0 && *(_DWORD *)(a1 + 76) >= a2 )
{
v3 = *(_QWORD *)(a1 + 56);
v2 = *(_QWORD *)(v3 + 8LL * a2);
if ( v2 )
{
StringOps = GetStringOps(*(_QWORD *)(v3 + 8LL * a2));
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)StringOps + 24LL))(StringOps, v2);
return v2;
}
}
else
{
RaiseOutOfBoundsException(a1);
}
return 0;
}
This seems to confirm that it loads a string.
Using the "next" command in LLDB we can direclty jump to the return of the function and see the result of the call to the method by inspecting the CPU return register (rax):
(lldbinit) next
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x0000600000683980 RBX: 0x00007F9A20039950 RBP: 0x0000000304CF2B30 RSP: 0x0000000304CF2AE0 o d I t s z a p c
RDI: 0x0000600000683980 RSI: 0x00006000006839AA RDX: 0x0000000000000002 RCX: 0x000000010AEBA9B0 RIP: 0x000000010045047A
R8: 0x00000000000003A2 R9: 0x000000010AD61C7E R10: 0x000000010AE9CF3C R11: 0x0000000000229F46 R12: 0x00006000030B43C0
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x000060000370D3E0
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
IntelliSerial.Word%s%o<IntelliSerial>i8 @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x10045047a (0x10045047a): 48 8b 0d af dd 35 00 mov rcx, qword ptr [rip + 0x35ddaf] ; (void *)0x000000010af011a0: gCurrentException
0x100450481 (0x100450481): 48 83 39 00 cmp qword ptr [rcx], 0x0
0x100450485 (0x100450485): 48 89 45 b8 mov qword ptr [rbp - 0x48], rax
0x100450489 (0x100450489): 75 a0 jne 0x10045042b ; <+59>
0x10045048b (0x10045048b): eb 14 jmp 0x1004504a1 ; <+177>
0x10045048d (0x10045048d): e8 de de 28 00 call 0x1006de370 ; symbol stub for: RaiseNilObjectException
0x100450492 (0x100450492): 48 8b 05 97 dd 35 00 mov rax, qword ptr [rip + 0x35dd97] ; (void *)0x000000010af011a0: gCurrentException
0x100450499 (0x100450499): 48 83 38 00 cmp qword ptr [rax], 0x0
-----------------------------------------------------------------------------------------------------------------------------
Process 41814 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x000000010045047a [REDACTED]`IntelliSerial.Word%s%o<IntelliSerial>i8 + 138
(lldbinit) register read rax
rax = 0x0000600000683980
(lldbinit) memory read 0x0000600000683980 --format hex --size 8 --count 2
0x600000683980: 0x0000000000000002 0x00006000006839a9
(lldbinit) memory read 0x00006000006839a9
0x6000006839a9: 0c 75 79 6a 6b 6c 39 6f 70 39 30 61 71 00 00 00 .uyjkl9op90aq...
0x6000006839b9: 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 e9 ................
The first byte (0x0c = 12) is the string length. The data that follows is the word itself. Repeating this for all 16 digits yields the complete table:
| Index | Hex Digit | Word |
|---|---|---|
| 0 | 0 | 18sjxtygtyup |
| 1 | 1 | l4678gfytbzq |
| 2 | 2 | ttvtbdrtyjtn |
| 3 | 3 | uyjkl9op90aq |
| 4 | 4 | 2wd34d34fbbt |
| 5 | 5 | yfdfwwqzgvmn |
| 6 | 6 | mpil706t8ssu |
| 7 | 7 | euyryfhhyru1 |
| 8 | 8 | 88384hshshsx |
| 9 | 9 | hfyy832747fh |
| 10 | A | dhhhplkbj2jk |
| 11 | B | sjkfi98jtn10 |
| 12 | C | sidjfhr5ytfg |
| 13 | D | s3erftg6jk89 |
| 14 | E | 0pdfhj7rfg34 |
| 15 | F | 5f2h788gfsys |
These words are also visible as plaintext strings inside the binary:

For example, computing the Hex Phrase for 00300007 produces 6 word lookups (one per hex digit):
3 : uyjkl9op90aq
0 : 18sjxtygtyup
0 : 18sjxtygtyup
0 : 18sjxtygtyup
0 : 18sjxtygtyup
7 : euyryfhhyru1
The raw hash function: IntelliSerial_SNRawFunc
Here is the decompiled code for IntelliSerial_SNRawFunc_i8_o_IntelliSerial_si8 which is the function responsible for calculating the checksum, it takes the input string calculated before and performs some bitwise operations and string manipulations to calculate the final checksum:
// This function performs a complex hashing algorithm to generate the raw serial number checksum.
// It combines the input string (the "Hex Phrase") and the integer seed (Part 2 of the serial)
// through a 256-round mixing loop involving bitwise operations and arithmetic.
__int64 __fastcall IntelliSerial_SNRawFunc_i8_o_IntelliSerial_si8(__int64 a1, __int64 a2, int a3)
{
int v3;
int v4;
int v5;
__int64 v6;
int v7;
int v8;
int v10;
__int64 v11;
__int64 v12;
__int64 v14;
__int64 v15;
__int64 v16;
int i;
int v18; // Acts as the accumulator/hash state
int v19; // Intermediate calculation value
__int64 v20; // Return value
v20 = 0;
v15 = 0;
RuntimeLockObject(a1);
RuntimeLockString(a2);
RuntimeStackCheck();
// Exception handling block
if ( gCurrentException
// Initialize the hash state (v18) by XORing the seed (a3) with 0xFFFFFFFF (bitwise NOT)
|| (v3 = j__SNStuff_MyBitwiseXor_i4_i4i4(a3, -1), gCurrentException) )
{
LABEL_2:
v14 = RuntimeTakeCurrentException();
}
else
{
v18 = v3; // Set initial hash state
// Perform 256 rounds of mixing
for ( i = 0; i <= 255LL; ++i )
{
v15 = 0;
// Calculate an intermediate value 'v19' based on the current hash state 'v18'.
// This operation manipulates the high bits (30 and 31) and scales the value.
if ( v18 < 0 )
{
// Extract bits 29 and 30 (mask 0x60000000), shift them right, add to scaled value, and add a constant 4.
v5 = j__SNStuff_MyBitwiseAnd_i4_i4i4(v18, 1610612736);
if ( gCurrentException )
goto LABEL_2;
v19 = (int)((double)(8 * v18) + (double)v5 / 536870912.0 + 4.0);
}
else
{
// Same bit manipulation but without the +4 constant
v4 = j__SNStuff_MyBitwiseAnd_i4_i4i4(v18, 1610612736);
if ( gCurrentException )
goto LABEL_2;
v19 = (int)((double)(8 * v18) + (double)v4 / 536870912.0);
}
// Extract a byte from the input string (a2) to mix into the hash.
// The index wraps around using modulo length of the string.
RuntimeUnlockString(0);
v15 = 0;
v12 = j__REALbasic_LenB_i8_s(a2);
if ( gCurrentException )
goto LABEL_2;
v16 = v12 ? i % v12 : 0LL;
// Get the byte at the calculated index
v6 = j__REALbasic_MidB_s_si8i8(a2, v16 + 1, 1);
v11 = v6;
if ( gCurrentException )
goto LABEL_2;
v15 = v6;
v10 = j__REALbasic_AscB_i8_s(v6); // Get ASCII value of the byte
if ( gCurrentException )
goto LABEL_2;
RuntimeUnlockString(v11);
v15 = 0;
// Update the hash state using XORs and Multiplication.
// This mixes the intermediate value 'v19' with the character byte 'v10'.
// Mix high byte of v19 with the character byte
v7 = j__SNStuff_MyBitwiseXor_i4_i4i4((int)((double)v19 / 256.0), v10);
if ( gCurrentException )
goto LABEL_2;
// Multiply result by 65537 (0x10001) and XOR with the original intermediate value
v8 = j__SNStuff_MyBitwiseXor_i4_i4i4(65537 * v7, v19);
if ( gCurrentException )
goto LABEL_2;
// Update hash state for the next iteration
v18 = v8 + 7;
RuntimeUnlockString(0);
v15 = 0;
RuntimeBackgroundTask();
if ( gCurrentException )
goto LABEL_2;
}
// Finalize and return
RuntimeUnlockString(0);
v15 = 0;
v20 = v18;
v14 = 0;
}
RuntimeUnlockObject(a1);
RuntimeUnlockString(a2);
RuntimeUnlockString(v15);
if ( v14 )
{
RuntimeReraiseException(v14);
RuntimeUnlockObject(v14);
}
return v20;
}
// Helper function: Performs a Bitwise AND operation on two integers.
__int64 __fastcall SNStuff_MyBitwiseAnd_i4_i4i4(int a1, int a2)
{
__int64 v3;
unsigned int v4;
v4 = 0;
RuntimeStackCheck();
if ( gCurrentException )
{
v3 = RuntimeTakeCurrentException();
}
else
{
v4 = a2 & a1;
v3 = 0;
}
if ( v3 )
{
RuntimeReraiseException(v3);
RuntimeUnlockObject(v3);
}
return v4;
}
// Helper function: Performs a Bitwise XOR (Exclusive OR) operation on two integers.
__int64 __fastcall SNStuff_MyBitwiseXor_i4_i4i4(int a1, int a2)
{
__int64 v3;
unsigned int v4;
v4 = 0;
RuntimeStackCheck();
if ( gCurrentException )
{
v3 = RuntimeTakeCurrentException();
}
else
{
v4 = a2 ^ a1;
v3 = 0;
}
if ( v3 )
{
RuntimeReraiseException(v3);
RuntimeUnlockObject(v3);
}
return v4;
}
Verifying with LLDB
Setting a breakpoint just before the SNFunc call for input EBC3-00300007-AAAAAA and stepping over:
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x0000000094618084 RBX: 0x00007F9CD5812EF0 RBP: 0x0000000304CF3010 RSP: 0x0000000304CF2D00 o d I t s z a p c
RDI: 0x0000600001180C48 RSI: 0x00006000011DE8C0 RDX: 0x0000000000300007 RCX: 0x0000000094618084 RIP: 0x000000010044EF82
R8: 0x00000000948188FC R9: 0x00000000000000FF R10: 0x0000000094800000 R11: 0x0000000000000084 R12: 0x00006000027D42D0
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x000060000206FD20
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
IntelliSerial.ValidateSN%b%o<IntelliSerial>s @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x10044ef82 (0x10044ef82): e8 63 da 28 00 call 0x1006dc9ea ; symbol stub for: IntelliSerial.SNFunc%i8%o<IntelliSerial>si4
0x10044ef87 (0x10044ef87): 48 8b 35 a2 f2 35 00 mov rsi, qword ptr [rip + 0x35f2a2] ; (void *)0x000000010af011a0: gCurrentException
0x10044ef8e (0x10044ef8e): 48 83 3e 00 cmp qword ptr [rsi], 0x0
0x10044ef92 (0x10044ef92): 48 89 85 f8 fc ff ff mov qword ptr [rbp - 0x308], rax
0x10044ef99 (0x10044ef99): 0f 85 d5 f3 ff ff jne 0x10044e374 ; <+564>
0x10044ef9f (0x10044ef9f): 48 8b 85 f8 fc ff ff mov rax, qword ptr [rbp - 0x308]
0x10044efa6 (0x10044efa6): 48 89 85 38 fe ff ff mov qword ptr [rbp - 0x1c8], rax
0x10044efad (0x10044efad): 8b 8d 04 fd ff ff mov ecx, dword ptr [rbp - 0x2fc]
-----------------------------------------------------------------------------------------------------------------------------
Breakpoint name: hash_gen
Process 44723 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 10.1
frame #0: 0x000000010044ef82 [REDACTED]`IntelliSerial.ValidateSN%b%o<IntelliSerial>s + 3650
(lldbinit) next
[ TiD: 1 ]------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x000000000083EA58 RBX: 0x00007F9CD5812EF0 RBP: 0x0000000304CF3010 RSP: 0x0000000304CF2D00 o d I t s z a P c
RDI: 0x0000000000000000 RSI: 0x0000000000003280 RDX: 0x0000000000000000 RCX: 0x00000000DE40702E RIP: 0x000000010044EF87
R8: 0x00000000DE607866 R9: 0x000000000000007F R10: 0x00000000DE600000 R11: 0x000000000000002E R12: 0x00006000027D42D0
R13: 0x00007FF80B4E00C0 R14: 0x00007FF82B670C97 R15: 0x000060000206FD20
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
IntelliSerial.ValidateSN%b%o<IntelliSerial>s @ [REDACTED].app/Contents/MacOS/[REDACTED]:
-> 0x10044ef87 (0x10044ef87): 48 8b 35 a2 f2 35 00 mov rsi, qword ptr [rip + 0x35f2a2] ; (void *)0x000000010af011a0: gCurrentException
0x10044ef8e (0x10044ef8e): 48 83 3e 00 cmp qword ptr [rsi], 0x0
0x10044ef92 (0x10044ef92): 48 89 85 f8 fc ff ff mov qword ptr [rbp - 0x308], rax
0x10044ef99 (0x10044ef99): 0f 85 d5 f3 ff ff jne 0x10044e374 ; <+564>
0x10044ef9f (0x10044ef9f): 48 8b 85 f8 fc ff ff mov rax, qword ptr [rbp - 0x308]
0x10044efa6 (0x10044efa6): 48 89 85 38 fe ff ff mov qword ptr [rbp - 0x1c8], rax
0x10044efad (0x10044efad): 8b 8d 04 fd ff ff mov ecx, dword ptr [rbp - 0x2fc]
0x10044efb3 (0x10044efb3): 48 63 d1 movsxd rdx, ecx
-----------------------------------------------------------------------------------------------------------------------------
Process 44723 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x000000010044ef87 [REDACTED]`IntelliSerial.ValidateSN%b%o<IntelliSerial>s + 3655
RAX: 0x000000000083EA58
The computed checksum is 0x83EA58, which means EBC3-00300007-83EA58 is a valid serial number for this software!
Indeed:

Python keygen
Now that we understand the full algorithm, we can reimplement it in Python. I prompted Claude (or any other LLM of your choice) with the original disassembly of the function and asked it to generate a Python implementation of the same logic. I then verified this against the known-good serial from the debugger session. The following code was generated:
import ctypes
WORDS = [
'18sjxtygtyup', # 0
'l4678gfytbzq', # 1
'ttvtbdrtyjtn', # 2
'uyjkl9op90aq', # 3
'2wd34d34fbbt', # 4
'yfdfwwqzgvmn', # 5
'mpil706t8ssu', # 6
'euyryfhhyru1', # 7
'88384hshshsx', # 8
'hfyy832747fh', # 9
'dhhhplkbj2jk', # 10 (A)
'sjkfi98jtn10', # 11 (B)
'sidjfhr5ytfg', # 12 (C)
's3erftg6jk89', # 13 (D)
'0pdfhj7rfg34', # 14 (E)
'5f2h788gfsys', # 15 (F)
]
def s32(x): return ctypes.c_int32(x).value
def u32(x): return x & 0xFFFFFFFF
def Word(n): return WORDS[n & 0xF]
def HexPhrase(a3):
hex_str = format(a3, 'X')
return ''.join(Word(int(c, 16)) for c in hex_str)
def SNRawFunc(key_str, a3):
data = key_str.encode('ascii')
n = len(data)
v18 = s32(a3 ^ 0xFFFFFFFF)
for i in range(256):
if v18 < 0:
v5 = u32(v18) & 1610612736
v19 = int(8 * v18 + v5 / 536870912.0 + 4.0)
else:
v4 = u32(v18) & 1610612736
v19 = int(8 * v18 + v4 / 536870912.0)
v19 = s32(v19)
byte = data[i % n]
v7 = s32(int(v19 / 256.0) ^ byte)
v8 = s32(s32(65537 * v7) ^ v19)
v18 = s32(v8 + 7)
return v18
def SNFunc(prefix, a3):
key_str = prefix.upper() + HexPhrase(a3)
return SNRawFunc(key_str, a3) & 0xFFFFFF
def generate(prefix, n):
serial_dec = 300000 + 7 * n
serial_str = format(serial_dec, '08d')
a3 = int(serial_str, 16)
checksum = SNFunc(prefix, a3)
return f"{prefix.upper()}-{serial_str}-{format(checksum, '06X')}"
def validate(serial):
s = serial.replace(' ', '').strip().upper()
if len(s) != 20:
return False
parts = s.split('-')
if len(parts) != 3:
return False
p1, p2, p3 = parts
if len(p1) != 4 or len(p2) != 8 or len(p3) != 6:
return False
try:
int(p2, 16)
int(p3, 16)
except ValueError:
return False
try:
val_dec = int(p2)
except ValueError:
return False
if val_dec < 300000 or (val_dec - 300000) % 7 != 0:
return False
a3 = int(p2, 16)
expected = SNFunc(p1, a3)
actual = int(p3, 16)
return expected == actual
if __name__ == '__main__':
test = "EBC3-00300007-83EA58"
print(f"Validation test: {test} -> {validate(test)}")
assert validate(test), "Known-good serial failed validation!"
print("\nSample serials:")
for n in [0, 1, 2, 100, 1000, 9999]:
s = generate("EBC3", n)
ok = validate(s)
print(f" n={n:5d}: {s} valid={ok}")
Running this confirms correct output:
Validation test: EBC3-00300007-83EA58 -> True
Sample serials:
n= 0: EBC3-00300000-XXXXXX valid=True
n= 1: EBC3-00300007-XXXXXX valid=True
...
That’s it! I hope you enjoyed this write-up. Using IDA for static analysis and LLDB for dynamic inspection made it easy to recover the full algorithm without patching the binary (although patching would have saved time :’))