一直在用 BetterZip,之前是找了一个网上的 License 最近突然不能用了,感觉可能是 BetterZip 升级到 3.x 之后之前的 License 过期 ✊。过期的 BetterZip 有几个不方便的地方:
1. 不可以单个文件拖入/拖出压缩文件夹
2. 不可以保存修改过的压缩文件夹
3. 每次打开会弹框,提示需要更新
之前一直在用 Hopper 静态分析 MachO 感觉不爽的地方很多 😥 也不知道是不是用的有问题,最后还是操起 IDA,MachO 拖进去,静态分析的时候有一个需要注意的问题,因为 IDA 还原出的函数很多很多,需要确定一下哪些函数是 Crack Registration 需要注意的。
直接点开过期的 BetterZip 有一个这样的框,一堆提示,直接到 IDA 里面找上面截取的部分字符串,我这里选的是 "trial period"。这个东西吧这玩意平常看到真的是很烦,不过倒是挺好用的来做入口的 ~
搜到了,然后通过 DATA XREF 找到定义字符串的位置 cfstr_YourTrialPeriod,然后通过 cfstr_YourTrialPeriod 再找到函数段的 DATA XREF ~ 比较重要的是选中高亮的部分。
从 DATA XREF :-[RegistrationController testCode]:loc_100039429 可以看到调用位置,跳到 testCode,另外这个时候注意一下 testCode 的 class 为 RegistrationController 这个类名很有诱惑力,如果现在跟进的 testCode 没有收获的话接下来看看这个 class。
下面是 testCode 还原成 C 为代码之后的样子,可以从我打的注释看出来 testCode 大多是一些根据当前注册状态修改提示界面和信息的代码,没有实质性的 Registration 逻辑代码,所以我也没有往下看了,有兴趣的话可以结合注释往下看看。
// RegistrationController - (char)testCode
char __cdecl -[RegistrationController testCode](struct RegistrationController *self, SEL a2)
{
struct RegistrationController *self_1; // r15@1
void (*objc_msgSend)(void *, const char *, ...); // r12@1
void *v4; // rax@1
__int64 tfCode; // rax@1
__int64 tfCode_1; // rbx@1
__int64 NoticeChkReg_1; // rax@1
struct NSButton *btnContinue; // r14@1
void *v9; // rax@1
void *mainBundle; // rax@1
void *mainBundle_1; // r13@1
void *v12; // rax@1
__int64 laterNotice; // rax@1
__int64 laterNotice_1; // rbx@1
__int64 laterNotice_2; // rdi@1
void (__fastcall *objc_release_1)(void *); // rbx@1
struct NSButton *btnBuy; // r14@2
void *v18; // rax@2
void *v19; // rax@2
void *v20; // r13@2
void *v21; // rax@2
__int64 RegistrationNotice; // rax@2
__int64 RegistrationNotice_1; // rbx@2
void (__fastcall *objc_release_2)(void *); // r14@2
struct NSTextField *tfHead; // r13@2
void *v26; // rax@2
void (*objc_msgSend_1)(void *, const char *, ...); // rbx@2
void *mainBundle_2; // rax@2
void *mainBundle_3; // r12@2
void (*v30)(void *, const char *, ...); // r15@3
__int64 v31; // rax@3
void *v32; // rax@3
void *v33; // rbx@3
void (*objc_msgSend_3)(void *, const char *, ...); // r13@3
void (*objc_msgSend_4)(void *, const char *, ...); // rbx@4
__int64 NoticeChkReg_len; // rax@4
__int64 NoticeChkReg_len_1; // ST20_8@4
struct NSButton *btnBuy_1; // r12@4
__int64 v39; // rax@4
__int64 mainBundle_6; // rax@4
__int64 mainBundle_7; // r13@4
__int64 v42; // rax@4
__int64 buyNowNotice; // rax@4
__int64 buyNowNotice_1; // r14@4
void (*objc_msgSend_5)(void *, const char *, ...); // r12@4
__int64 buyNowNotice_2; // rdi@4
void (__fastcall *objc_release_5)(_QWORD); // r14@4
char v48; // r14@5
__int64 v49; // r12@5
void (*objc_msgSend_2)(void *, const char *, ...); // r15@6
__int64 v51; // rax@7
__int64 upgradeNotice; // rax@7
__int64 upgradeNotice_1; // rbx@7
struct NSTextField *tfHead_1; // rdi@7
__int64 v55; // rdi@7
void (__fastcall *objc_release_3)(void *); // rbx@7
struct NSTextField *tfHead_2; // r14@8
__int64 v58; // rax@8
__int64 v59; // rax@8
__int64 registeredNotice; // rbx@8
void (__fastcall *objc_release_4)(void *); // r15@8
struct NSButton *btnContinue_1; // ST20_8@8
__int64 v63; // rax@8
void *mainBundle_4; // rax@8
void *mainBundle_5; // r12@8
__int64 v66; // rax@8
void *continueNotice_1; // rax@8
void *continueNotice; // rbx@8
struct RegistrationController *self_3; // r14@10
int installedDays_1; // ebx@10
int trialDaysLeft; // er15@10
__int64 v72; // rax@11
__int64 v73; // rax@11
__int64 v74; // ST20_8@11
__int64 v75; // rax@11
__int64 Notice_1; // rax@11
__int64 Notice; // rbx@11
__int64 v78; // rax@11
__int64 Notice_2; // rax@11
__int64 Notice_3; // r12@11
__int64 Notice_4; // rdi@11
void (__fastcall *objc_release_6)(_QWORD); // rbx@11
__int64 v83; // rax@13
__int64 mainBundle_8; // rax@13
__int64 mainBundle_9; // rbx@13
__int64 v86; // rax@13
__int64 v87; // rax@13
void *v88; // rax@14
void *v89; // rax@14
void *v90; // r13@14
void *v91; // rax@15
__int64 v92; // rax@15
__int64 v93; // rbx@15
void *v94; // rax@16
__int64 v95; // rax@16
__int64 v96; // rax@18
__int64 tfCode_2; // rax@18
__int64 tfCode_3; // rbx@18
__int64 v99; // rax@18
__int64 v100; // r12@18
struct NSTextField *v101; // r14@18
__int64 v102; // rax@19
__int64 v103; // rax@19
__int64 v104; // r15@19
__int64 v105; // rax@19
__int64 v106; // rax@19
__int64 v107; // rbx@19
__int64 v108; // rdi@19
void (__fastcall *v109)(_QWORD); // rbx@19
__int64 v110; // rdi@19
__int64 v111; // rax@20
__int64 v112; // rax@20
__int64 v113; // ST08_8@20
__int64 v114; // rax@20
__int64 v115; // rax@20
__int64 v116; // r12@20
__int64 v117; // rax@20
__int64 v118; // rax@20
__int64 v119; // rbx@20
__int64 v120; // rdi@20
__int64 v121; // rax@22
__int64 v122; // rax@22
__int64 v123; // rbx@22
__int64 v124; // rdi@22
void (__fastcall *v125)(_QWORD); // rbx@22
struct RegistrationController *self_2; // [sp+10h] [bp-50h]@2
struct RegistrationController *self_2a; // [sp+10h] [bp-50h]@18
__int64 NoticeChkReg_2; // [sp+18h] [bp-48h]@1
int registerFlag; // [sp+20h] [bp-40h]@1
__int64 registerFlaga; // [sp+20h] [bp-40h]@18
__int64 NoticeChkReg; // [sp+28h] [bp-38h]@1
int v133; // [sp+34h] [bp-2Ch]@1
self_1 = self;
v133 = 0;
objc_msgSend = *objc_msgSend_ptr;
v4 = objc_msgSend_ptr(self->tfCode, selRef_stringValue);
LODWORD(tfCode) = objc_retainAutoreleasedReturnValue(v4);
tfCode_1 = tfCode;
NoticeChkReg = 0LL;
registerFlag = checkRegistration(tfCode, &NoticeChkReg, &v133);// 检查用户 tfCode 是否符合序列号标准,把返回的信息保存到 NoticeChkReg 里, 是否注册保存到 registerFlag
LODWORD(NoticeChkReg_1) = objc_retain_ptr(NoticeChkReg, &NoticeChkReg);
NoticeChkReg_2 = NoticeChkReg_1;
objc_release(tfCode_1);
objc_msgSend(self->btnBuy, selRef_setHidden_, 0LL);
objc_msgSend(self->btnLost, selRef_setHidden_, 0LL);
objc_msgSend(self->btnBuy, selRef_setKeyEquivalent_, &cfstr_Enter);// btnBuy 和回车键绑定
objc_msgSend(self->btnBuy, selRef_setTag_, 1LL);
objc_msgSend(self->btnContinue, selRef_setKeyEquivalent_, &cfstr_ESC);// btnContinue 和 ESC 绑定
btnContinue = self->btnContinue;
v9 = (objc_msgSend)(classRef_NSBundle, selRef_mainBundle);
LODWORD(mainBundle) = objc_retainAutoreleasedReturnValue(v9);
mainBundle_1 = mainBundle;
v12 = (objc_msgSend)(
mainBundle,
selRef_localizedStringForKey_value_table_,
&cfstr_Later,
&cfstr_nil,
&cfstr_Registration); // Registration.string 里 Later 对应的字符串
LODWORD(laterNotice) = objc_retainAutoreleasedReturnValue(v12);
laterNotice_1 = laterNotice;
objc_msgSend(btnContinue, selRef_setTitle_, laterNotice);
laterNotice_2 = laterNotice_1;
objc_release_1 = objc_release;
objc_release(laterNotice_2);
objc_release_1(mainBundle_1);
if ( registerFlag )
{
btnBuy = self_1->btnBuy;
v18 = (objc_msgSend)(classRef_NSBundle, selRef_mainBundle);
LODWORD(v19) = objc_retainAutoreleasedReturnValue(v18);
v20 = v19;
v21 = (objc_msgSend)(
v19,
selRef_localizedStringForKey_value_table_,// localizedStringForKey:value:table
&cfstr_UpgradeNow, // key="Upgrade Now"
&cfstr_nil, // value=""
&cfstr_Registration); // table="Registration"
LODWORD(RegistrationNotice) = objc_retainAutoreleasedReturnValue(v21);
RegistrationNotice_1 = RegistrationNotice;
objc_msgSend(btnBuy, selRef_setTitle_, RegistrationNotice);
objc_release_2 = objc_release;
objc_release(RegistrationNotice_1);
objc_release_2(v20);
objc_msgSend(self_1->btnBuy, selRef_setTag_, 2LL);
tfHead = self_1->tfHead;
self_2 = self_1;
v26 = (objc_msgSend)(classRef_NSBundle, selRef_mainBundle);
objc_msgSend_1 = objc_msgSend;
LODWORD(mainBundle_2) = objc_retainAutoreleasedReturnValue(v26);
mainBundle_3 = mainBundle_2;
if ( registerFlag == 9999998 )
{ // License for 2
v30 = objc_msgSend_1;
LODWORD(v31) = (objc_msgSend_1)(
mainBundle_2,
selRef_localizedStringForKey_value_table_,
&cfstr_ThisIsALicenseFor2,
&cfstr_nil,
&cfstr_Registration);
LODWORD(v32) = objc_retainAutoreleasedReturnValue(v31);
v33 = v32;
(v30)(tfHead, selRef_setStringValue_, v32);
objc_release_2(v33);
objc_release_2(mainBundle_3);
objc_msgSend_3 = v30;
self_1 = self_2;
}
else
{
objc_msgSend_2 = objc_msgSend_1;
if ( registerFlag == 9999999 )
{ // License for 2 from App Store
LODWORD(v51) = (objc_msgSend_1)(
mainBundle_2,
selRef_localizedStringForKey_value_table_,
&cfstr_ThisIsALicenseFor2_0,
&cfstr_nil,
&cfstr_Registration);
LODWORD(upgradeNotice) = objc_retainAutoreleasedReturnValue(v51);
upgradeNotice_1 = upgradeNotice;
tfHead_1 = tfHead;
objc_msgSend_3 = objc_msgSend_2;
(objc_msgSend_2)(tfHead_1, selRef_setStringValue_, upgradeNotice);
v55 = upgradeNotice_1;
objc_release_3 = objc_release;
objc_release(v55);
objc_release_3(mainBundle_3);
self_1 = self_2;
}
else
{
tfHead_2 = tfHead;
objc_msgSend_3 = objc_msgSend_1;
LODWORD(v58) = (objc_msgSend_1)(
mainBundle_2,
selRef_localizedStringForKey_value_table_,
&cfstr_YourCopyOfBett, // "\nYour copy of BetterZip is registered. Thank you!"
&cfstr_nil,
&cfstr_Registration);
LODWORD(v59) = objc_retainAutoreleasedReturnValue(v58);
registeredNotice = v59;
(objc_msgSend_2)(tfHead_2, selRef_setStringValue_, v59);
objc_release_4 = objc_release;
objc_release(registeredNotice);
objc_release_4(mainBundle_3);
btnContinue_1 = self_2->btnContinue;
LODWORD(v63) = (objc_msgSend_3)(classRef_NSBundle, selRef_mainBundle);
LODWORD(mainBundle_4) = objc_retainAutoreleasedReturnValue(v63);
mainBundle_5 = mainBundle_4;
LODWORD(v66) = (objc_msgSend_3)(
mainBundle_4,
selRef_localizedStringForKey_value_table_,
&cfstr_Continue,
&cfstr_nil,
&cfstr_Registration);
LODWORD(continueNotice_1) = objc_retainAutoreleasedReturnValue(v66);
continueNotice = continueNotice_1;
(objc_msgSend_3)(btnContinue_1, selRef_setTitle_, continueNotice_1);
objc_release_4(continueNotice);
objc_release_4(mainBundle_5);
self_1 = self_2;
(objc_msgSend_3)(self_2->btnContinue, selRef_setTag_, 0LL);
(objc_msgSend_3)(self_2->btnContinue, selRef_setKeyEquivalent_, &cfstr_Enter);
(objc_msgSend_3)(self_2->btnBuy, selRef_setKeyEquivalent_, &cfstr_nil);
(objc_msgSend_3)(self_2->btnBuy, selRef_setHidden_, 1LL);
(objc_msgSend_3)(self_2->btnLost, selRef_setHidden_, 1LL);
}
}
v49 = NoticeChkReg_2;
objc_msgSend_ptr(self_1->tfText, selRef_setStringValue_, NoticeChkReg_2);
v48 = 1;
}
else
{ // Licensed
objc_msgSend_4 = objc_msgSend;
LODWORD(NoticeChkReg_len) = (objc_msgSend)(NoticeChkReg_2, selRef_length);
NoticeChkReg_len_1 = NoticeChkReg_len;
btnBuy_1 = self_1->btnBuy;
LODWORD(v39) = (objc_msgSend_4)(classRef_NSBundle, selRef_mainBundle);
LODWORD(mainBundle_6) = objc_retainAutoreleasedReturnValue(v39);
mainBundle_7 = mainBundle_6;
LODWORD(v42) = (objc_msgSend_4)(
mainBundle_6,
selRef_localizedStringForKey_value_table_,
&cfstr_BuyNow,
&cfstr_nil,
&cfstr_Registration);
LODWORD(buyNowNotice) = objc_retainAutoreleasedReturnValue(v42);
buyNowNotice_1 = buyNowNotice;
(objc_msgSend_4)(btnBuy_1, selRef_setTitle_, buyNowNotice);
objc_msgSend_5 = objc_msgSend_4;
buyNowNotice_2 = buyNowNotice_1;
objc_release_5 = objc_release;
objc_release(buyNowNotice_2);
objc_release_5(mainBundle_7);
if ( NoticeChkReg_len_1 )
{ // Registered
objc_msgSend_ptr(self_1->tfHead, selRef_setStringValue_, NoticeChkReg_2);
v48 = 0;
objc_msgSend_3 = objc_msgSend_4;
v49 = NoticeChkReg_2;
}
else
{ // Trial
self_3 = self_1;
installedDays_1 = installedDays;
trialDaysLeft = 30 - installedDays;
if ( 30 - installedDays < 2 )
{
if ( trialDaysLeft == 1 )
{
objc_msgSend_3 = objc_msgSend_5;
LODWORD(v83) = (objc_msgSend_5)(classRef_NSBundle, selRef_mainBundle);
LODWORD(mainBundle_8) = objc_retainAutoreleasedReturnValue(v83);
mainBundle_9 = mainBundle_8;
LODWORD(v86) = (objc_msgSend_5)(
mainBundle_8,
selRef_localizedStringForKey_value_table_,
&cfstr_BetterzipIsN_2,// "BetterZip is not yet registered. Your trial period will end tomorrow
&cfstr_nil,
&cfstr_Registration);
LODWORD(v87) = objc_retainAutoreleasedReturnValue(v86);
Notice_3 = v87;
objc_release(mainBundle_9);
trialDaysLeft = 1;
}
else
{
v88 = objc_msgSend_ptr(classRef_NSBundle, selRef_mainBundle);
LODWORD(v89) = objc_retainAutoreleasedReturnValue(v88);
v90 = v89;
if ( installedDays_1 == 30 )
{
v91 = objc_msgSend_ptr(
v89,
selRef_localizedStringForKey_value_table_,
&cfstr_BetterzipIsN_0, // BetterZip is not yet registered. Your trial period ends today
&cfstr_nil,
&cfstr_Registration);
LODWORD(v92) = objc_retainAutoreleasedReturnValue(v91);
v93 = v92;
objc_release(v90);
}
else
{
v94 = objc_msgSend_ptr(
v89,
selRef_localizedStringForKey_value_table_,
&cfstr_YourTrialPerio, // "Your trial period is over.
&cfstr_nil,
&cfstr_Registration);
LODWORD(v95) = objc_retainAutoreleasedReturnValue(v94);
v93 = v95;
objc_release(v90);
trialDaysLeft = 0;
}
objc_msgSend_3 = objc_msgSend_5;
Notice_3 = v93;
}
}
else
{
LODWORD(v72) = (objc_msgSend_5)(classRef_NSBundle, selRef_mainBundle);
LODWORD(v73) = objc_retainAutoreleasedReturnValue(v72);
v74 = v73;
LODWORD(v75) = (objc_msgSend_5)(
v73,
selRef_localizedStringForKey_value_table_,
&cfstr_BetterzipIsNot, // "BetterZip is not yet registered. Your trial period will end in %d days.
&cfstr_nil,
&cfstr_Registration);
LODWORD(Notice_1) = objc_retainAutoreleasedReturnValue(v75);
Notice = Notice_1;
objc_msgSend_3 = objc_msgSend_5;
LODWORD(v78) = (objc_msgSend_5)(classRef_NSString, selRef_stringWithFormat_, Notice_1, trialDaysLeft);
LODWORD(Notice_2) = objc_retainAutoreleasedReturnValue(v78);
Notice_3 = Notice_2;
Notice_4 = Notice;
objc_release_6 = objc_release;
objc_release(Notice_4);
objc_release_6(v74);
}
registerFlaga = Notice_3;
self_2a = self_3;
(objc_msgSend_3)(self_3->tfHead, selRef_setStringValue_, Notice_3);
LODWORD(v96) = (objc_msgSend_3)(self_3->tfCode, selRef_stringValue);
LODWORD(tfCode_2) = objc_retainAutoreleasedReturnValue(v96);
tfCode_3 = tfCode_2;
LODWORD(v99) = (objc_msgSend_3)(tfCode_2, selRef_length);
v100 = v99;
objc_release(tfCode_3);
v101 = self_3->tfText;
if ( v100 )
{ // self.tfCode is not nil
LODWORD(v102) = (objc_msgSend_3)(classRef_NSBundle, selRef_mainBundle);
LODWORD(v103) = objc_retainAutoreleasedReturnValue(v102);
v104 = v103;
LODWORD(v105) = (objc_msgSend_3)(
v103,
selRef_localizedStringForKey_value_table_,
&cfstr_TheCodeIsInval,// "The code is invalid
&cfstr_nil,
&cfstr_Registration);
LODWORD(v106) = objc_retainAutoreleasedReturnValue(v105);
v107 = v106;
(objc_msgSend_3)(v101, selRef_setStringValue_, v106);
v108 = v107;
v109 = objc_release;
objc_release(v108);
v110 = v104;
}
else
{
LODWORD(v111) = (objc_msgSend_3)(classRef_NSBundle, selRef_mainBundle);
LODWORD(v112) = objc_retainAutoreleasedReturnValue(v111);
v113 = v112;
LODWORD(v114) = (objc_msgSend_3)(
v112,
selRef_localizedStringForKey_value_table_,
&cfstr_TrialUserDDays,// Trial user\n%d days left"
&cfstr_nil,
&cfstr_Registration);
LODWORD(v115) = objc_retainAutoreleasedReturnValue(v114);
v116 = v115;
LODWORD(v117) = (objc_msgSend_3)(classRef_NSString, selRef_stringWithFormat_, v115, trialDaysLeft);
LODWORD(v118) = objc_retainAutoreleasedReturnValue(v117);
v119 = v118;
(objc_msgSend_3)(v101, selRef_setStringValue_, v118);
v120 = v119;
v109 = objc_release;
objc_release(v120);
v109(v116);
v110 = v113;
}
v109(v110);
v49 = NoticeChkReg_2;
objc_release(registerFlaga);
v48 = 0;
self_1 = self_2a;
}
}
LODWORD(v121) = (objc_msgSend_3)(classRef_NSNotificationCenter, selRef_defaultCenter);
LODWORD(v122) = objc_retainAutoreleasedReturnValue(v121);
v123 = v122;
(objc_msgSend_3)(v122, selRef_postNotificationName_object_, MIBRegDidChangeNotification, self_1);
v124 = v123;
v125 = objc_release;
objc_release(v124);
v125(v49);
return v48;
}
虽然说粗看一遍不关注上面显示提示信息的代码了,不过发现了另外一个比较关键的函数 checkRegistration 点进去看看,可以看到是验证 tfCode 的函数,如下。
__int64 __fastcall checkRegistration(__int64 tfCode, _QWORD *Notice_2, _DWORD *a3)
{
_DWORD *v3; // r12@1
void *tfCode_1; // rax@1
void *tfCode_2; // r15@1
int result_mid; // er13@1
BIO *mem_buf_1; // r14@1
void *(*objc_msgSend)(void *, const char *, ...); // r14@2
char *codeLength; // rax@2
void *v10; // rax@2
void *lastCode; // rax@2
void *lastCode_1; // rbx@2
void *v13; // rax@3
void *tfCodeEndsWithEuqlSign_1; // rax@3
void *tfCodeEndsWithEuqlSign; // rbx@3
BIO *mem_buf; // rax@4
void *v17; // rax@6
char *v18; // rax@6
int (__fastcall *objc_msgSend_1)(void *, char *, signed __int64, signed __int64); // r13@8
void *v20; // rax@8
void *decrypt_UTF8; // rax@8
void *decrypt_UTF8_1; // rbx@8
unsigned __int8 prefixFlag2; // al@8
int (__fastcall *objc_msgSend_2)(void *, char *, signed __int64, signed __int64); // rbx@8
__int64 v25; // rax@9
__int64 decrypt_UTF8_index16; // rax@9
__int64 decrypt_UTF8_index16_1; // r13@9
int decrypt_UTF8_index16_intValue; // ebx@9
int *v29; // rax@9
unsigned int v30; // ecx@9
void *(*objc_msgSend_3)(void *, const char *, ...); // r13@13
void *v32; // rax@13
void *decrypt_UTF8_index24_1; // rax@13
void *decrypt_UTF8_index24; // rbx@13
void *DateFormatter; // rax@13
void *v36; // rax@13
void *v37; // r12@13
void *v38; // rax@13
void *decrypt_UTF8_index24_dateFormat; // rax@13
void *v40; // rax@13
void *dateFormat_2014_05_1; // rax@13
void *decrypt_UTF8_index41; // rax@13
__int64 v46; // rax@13
void *v45; // rax@13
void *decrypt_UTF8_index35_1; // rax@13
void *decrypt_UTF8_index35; // rbx@13
void *v44; // rax@13
void *mainBundle; // rax@13
void *v49; // rax@14
__int64 v50; // rax@14
__int64 v51; // rbx@14
__int64 v52; // r9@14
void *v53; // rax@14
void *v54; // rax@16
__int64 v55; // rax@16
__int64 Notice_1; // rax@17
__int64 Notice; // rax@17
void (__fastcall *objc_release_1)(void *); // r12@17
void (__fastcall *objc_release_2)(void *); // rbx@20
__int64 result; // rax@26
void *NSDate_init; // [sp+8h] [bp-1A8h]@13
void *v62; // [sp+10h] [bp-1A0h]@14
void *dateFormat_2014_05_2; // [sp+20h] [bp-190h]@13
void *decrypt_UTF8_index24_dateFormat_1; // [sp+28h] [bp-188h]@13
__int64 decrypt_UTF8_index41_2; // [sp+30h] [bp-180h]@13
void *decrypt_UTF8_index24_2; // [sp+38h] [bp-178h]@13
unsigned __int8 prefixFlag1; // [sp+43h] [bp-16Dh]@8
int decrypt_UTF8_index16_intValue_1; // [sp+44h] [bp-16Ch]@13
_DWORD *decrypt_UTF8_index35_intValue; // [sp+48h] [bp-168h]@7
void *decrypt_UTF8_2; // [sp+50h] [bp-160h]@8
int len[2]; // [sp+60h] [bp-150h]@4
RSA *x; // [sp+68h] [bp-148h]@1
unsigned __int8 *from; // [sp+70h] [bp-140h]@1
void *base64Buf; // [sp+78h] [bp-138h]@1
unsigned __int8 decrypt[16]; // [sp+80h] [bp-130h]@7
__int128 v76; // [sp+90h] [bp-120h]@7
__int128 v77; // [sp+A0h] [bp-110h]@7
__int128 v78; // [sp+B0h] [bp-100h]@7
__int128 v79; // [sp+C0h] [bp-F0h]@7
__int128 v80; // [sp+D0h] [bp-E0h]@7
__int128 v81; // [sp+E0h] [bp-D0h]@7
__int128 v82; // [sp+F0h] [bp-C0h]@7
__int128 v83; // [sp+100h] [bp-B0h]@7
__int128 v84; // [sp+110h] [bp-A0h]@7
__int128 v85; // [sp+120h] [bp-90h]@7
__int128 v86; // [sp+130h] [bp-80h]@7
__int128 v87; // [sp+140h] [bp-70h]@7
__int128 v88; // [sp+150h] [bp-60h]@7
__int128 v89; // [sp+160h] [bp-50h]@7
__int128 v90; // [sp+170h] [bp-40h]@7
__int64 stack_chk_guard; // [sp+180h] [bp-30h]@1
v3 = a3;
stack_chk_guard = *__stack_chk_guard_ptr;
LODWORD(tfCode_1) = objc_retain_ptr(tfCode, Notice_2);
tfCode_2 = tfCode_1;
base64Buf = 0LL;
from = 0LL;
x = 0LL;
*v3 = 0;
objc_retainAutorelease(&cfstr_nil);
*Notice_2 = &cfstr_nil;
result_mid = 0;
mem_buf_1 = 0LL;
if ( objc_msgSend_ptr(tfCode_2, selRef_length) >= 0x80 )
{
objc_msgSend = *objc_msgSend_ptr;
codeLength = objc_msgSend_ptr(tfCode_2, selRef_length);
v10 = objc_msgSend(tfCode_2, selRef_substringFromIndex_, codeLength - 1);// tfCode.substring(length - 1)
LODWORD(lastCode) = objc_retainAutoreleasedReturnValue(v10);
lastCode_1 = lastCode;
LOBYTE(objc_msgSend) = objc_msgSend(lastCode, selRef_isEqualTo_, &cfstr_equalSign);
objc_release(lastCode_1);
if ( !objc_msgSend ) // lastCode != "="
{
v13 = objc_msgSend_ptr(tfCode_2, selRef_stringByAppendingString_, &cfstr_equalSign);
LODWORD(tfCodeEndsWithEuqlSign_1) = objc_retainAutoreleasedReturnValue(v13);
tfCodeEndsWithEuqlSign = tfCodeEndsWithEuqlSign_1;
objc_release(tfCode_2);
tfCode_2 = tfCodeEndsWithEuqlSign;
}
decodeBase64(b64_key, &base64Buf, len);
mem_buf = BIO_new_mem_buf(base64Buf, len[0]);
mem_buf_1 = mem_buf;
result_mid = 0;
if ( mem_buf )
{ // RSA Decrypt
result_mid = 0;
if ( PEM_read_bio_RSA_PUBKEY(mem_buf, &x, 0LL, 0LL) )
{
LODWORD(v17) = objc_retainAutorelease(tfCode_2);
tfCode_2 = v17;
v18 = objc_msgSend_ptr(v17, selRef_UTF8String);
decodeBase64(v18, &from, len);
if ( *len == 128LL )
{
decrypt_UTF8_index35_intValue = v3;
v90 = 0LL;
v89 = 0LL;
v88 = 0LL;
v87 = 0LL;
v86 = 0LL;
v85 = 0LL;
v84 = 0LL;
v83 = 0LL;
v82 = 0LL;
v81 = 0LL;
v80 = 0LL;
v79 = 0LL;
v78 = 0LL;
v77 = 0LL;
v76 = 0LL;
*decrypt = 0LL;
if ( RSA_public_decrypt(128, from, decrypt, x, 1) != -1 )
{
objc_msgSend_1 = *objc_msgSend_ptr;
v20 = objc_msgSend_ptr(classRef_NSString, selRef_stringWithUTF8String_, decrypt);
LODWORD(decrypt_UTF8) = objc_retainAutoreleasedReturnValue(v20);
decrypt_UTF8_1 = decrypt_UTF8;
decrypt_UTF8_2 = decrypt_UTF8;
prefixFlag1 = (objc_msgSend_1)(decrypt_UTF8, selRef_hasPrefix_, &cfstr___mas_betterzp);// __MAS_BetterZp2_
prefixFlag2 = (objc_msgSend_1)(decrypt_UTF8_1, selRef_hasPrefix_, &cfstr___mib_betterzi);// __MIB_BetterZip_
objc_msgSend_2 = objc_msgSend_1;
result_mid = 0;
if ( prefixFlag1 | prefixFlag2 )
{
LODWORD(v25) = objc_msgSend_2(decrypt_UTF8_2, selRef_substringWithRange_, 16LL, 7LL);// range(16, 16+7)
LODWORD(decrypt_UTF8_index16) = objc_retainAutoreleasedReturnValue(v25);
decrypt_UTF8_index16_1 = decrypt_UTF8_index16;
decrypt_UTF8_index16_intValue = (objc_msgSend_2)(decrypt_UTF8_index16, selRef_intValue);
objc_release(decrypt_UTF8_index16_1);
v29 = &unk_1000AEE20;
v30 = 0;
do
{
if ( *v29 > decrypt_UTF8_index16_intValue )
break;
result_mid = 0;
if ( *v29 == decrypt_UTF8_index16_intValue )
goto LABEL_21;
++v29;
++v30;
}
while ( v30 <= 0x2F );
decrypt_UTF8_index16_intValue_1 = decrypt_UTF8_index16_intValue;
objc_msgSend_3 = *objc_msgSend_ptr;
v32 = objc_msgSend_ptr(decrypt_UTF8_2, selRef_substringWithRange_, 24LL, 10LL);
LODWORD(decrypt_UTF8_index24_1) = objc_retainAutoreleasedReturnValue(v32);
decrypt_UTF8_index24 = decrypt_UTF8_index24_1;
decrypt_UTF8_index24_2 = decrypt_UTF8_index24_1;
DateFormatter = objc_msgSend_3(classRef_NSDateFormatter, selRef_alloc);
v36 = objc_msgSend_3(DateFormatter, selRef_init);
v37 = v36;
NSDate_init = v36;
objc_msgSend_3(v36, selRef_setDateFormat_, &cfstr_YyyyMmDd);
v38 = objc_msgSend_3(v37, selRef_dateFromString_, decrypt_UTF8_index24);
LODWORD(decrypt_UTF8_index24_dateFormat) = objc_retainAutoreleasedReturnValue(v38);
decrypt_UTF8_index24_dateFormat_1 = decrypt_UTF8_index24_dateFormat;
v40 = objc_msgSend_3(v37, selRef_dateFromString_, &cfstr_20140501);// "2014-05-01"
LODWORD(dateFormat_2014_05_1) = objc_retainAutoreleasedReturnValue(v40);
dateFormat_2014_05_2 = dateFormat_2014_05_1;
decrypt_UTF8_index41 = objc_msgSend_3(decrypt_UTF8_2, selRef_substringFromIndex_, 41LL);
LODWORD(v46) = objc_retainAutoreleasedReturnValue(decrypt_UTF8_index41);
decrypt_UTF8_index41_2 = v46;
v45 = objc_msgSend_3(decrypt_UTF8_2, selRef_substringWithRange_, 35LL, 4LL);
LODWORD(decrypt_UTF8_index35_1) = objc_retainAutoreleasedReturnValue(v45);
decrypt_UTF8_index35 = decrypt_UTF8_index35_1;
*decrypt_UTF8_index35_intValue = objc_msgSend_3(decrypt_UTF8_index35_1, selRef_intValue);
objc_release(decrypt_UTF8_index35);
LODWORD(decrypt_UTF8_index35) = *decrypt_UTF8_index35_intValue;
v44 = objc_msgSend_3(classRef_NSBundle, selRef_mainBundle);
LODWORD(mainBundle) = objc_retainAutoreleasedReturnValue(v44);
if ( decrypt_UTF8_index35 < 2 )
{
v62 = mainBundle;
v54 = objc_msgSend_3(
mainBundle,
selRef_localizedStringForKey_value_table_,
&cfstr_Registration_2, // "Registration: # %d on %@\nLicensee: %@"
&cfstr_nil,
&cfstr_Registration);
LODWORD(v55) = objc_retainAutoreleasedReturnValue(v54);
v51 = v55;
v53 = objc_msgSend_3(
classRef_NSString,
selRef_stringWithFormat_,
v55,
decrypt_UTF8_index16_intValue_1,
decrypt_UTF8_index24_2);
}
else
{
v62 = mainBundle;
v49 = objc_msgSend_3(
mainBundle,
selRef_localizedStringForKey_value_table_,
&cfstr_RegistrationDO, // "Registration: # %d on %@ for %d licenses\nLicensee: %@"
&cfstr_nil,
&cfstr_Registration);
LODWORD(v50) = objc_retainAutoreleasedReturnValue(v49);
v51 = v50;
v52 = *decrypt_UTF8_index35_intValue;
v53 = objc_msgSend_3(
classRef_NSString,
selRef_stringWithFormat_,
v50,
decrypt_UTF8_index16_intValue_1,
decrypt_UTF8_index24_2,
decrypt_UTF8_index41_2,
NSDate_init);
}
LODWORD(Notice_1) = objc_retainAutoreleasedReturnValue(v53);
LODWORD(Notice) = objc_autorelease(Notice_1);
*Notice_2 = Notice;
objc_release_1 = objc_release;
objc_release(v51);
objc_release_1(v62);
if ( objc_msgSend_ptr(decrypt_UTF8_index24_dateFormat_1, selRef_compare_, dateFormat_2014_05_2) == -1
|| prefixFlag1 )
{
decrypt_UTF8_index16_intValue_1 = (prefixFlag1 != 0) | 0x98967E;
}
objc_release_2 = objc_release;
objc_release(decrypt_UTF8_index41_2);
objc_release_2(dateFormat_2014_05_2);
objc_release_2(decrypt_UTF8_index24_dateFormat_1);
objc_release_2(NSDate_init);
objc_release_2(decrypt_UTF8_index24_2);
result_mid = decrypt_UTF8_index16_intValue_1;
}
LABEL_21:
objc_release(decrypt_UTF8_2);
}
}
}
}
else
{
mem_buf_1 = 0LL;
}
}
if ( x )
RSA_free(x);
if ( mem_buf_1 )
BIO_free(mem_buf_1);
free(from);
free(base64Buf);
objc_release(tfCode_2);
result = *__stack_chk_guard_ptr;
if ( *__stack_chk_guard_ptr == stack_chk_guard )
result = result_mid;
return result;
}
也打了一些注释,可以看到先对 tfCode 的格式做了判断,对应补全后做了 base64 + RSA 的解码/解密,还有一些后续对特定字符位置比较的操作,如果要写一个注册机的话从这里入手是个很好的路子。
上面提到了好几次 tfCode,这个在 IDA 反编译出来的代码中显示的比较不直观,需要在 Structures(SHIFT + F9)里找到对应类查看,这里推荐使用 class-dump 可以比较方便得导出 oc 运行时的声明文件,也就包括类的定义文件。看上去很舒服,比 IDA 直接查看更加直观。
➜ test ❯❯❯ class-dump -C RegistrationController BetterZip
....
@interface RegistrationController : NSWindowController
{
NSTabView *tabView;
NSTextField *tfEmail;
NSButton *btnConvert;
NSTextField *tfHead;
NSTextField *tfText;
NSTextField *tfCode;
NSTextField *tfLabel;
NSButton *btnBuy;
NSButton *btnLost;
NSButton *btnContinue;
RendezvousController *rendezvous;
int usersFound;
NSString *ownName;
NSTimer *timer;
int timerCountDown;
BOOL isModalSheet;
NSString *hashString;
}
+ (BOOL)isRegistered;
+ (int)trialDaysLeft;
+ (BOOL)trialIsOver;
- (void).cxx_destruct;
- (BOOL)testCode;
- (id)regCode;
- (void)lostKey:(id)arg1;
- (void)laterContinue:(id)arg1;
- (void)buy:(id)arg1;
- (int)registrationStatus;
- (id)createNameArrayFromDiscoveredServices:(id)arg1;
- (void)discoveredServicesDidChange;
- (void)controlTextDidChange:(id)arg1;
- (void)registerWithCode:(id)arg1;
- (void)convert:(id)arg1;
- (void)convertMASReceiptToCode;
- (void)windowDidLoad;
- (id)initWithParams:(id)arg1;
// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;
@end
如果要根据 checkRegistration 写注册机显然比较麻烦,看看 checkRegistration 的调用位置,DATA XREF 找到 registrationStatus ,是一个操作 self.is_registered 的函数,根据不同的情况保存不同的值到 is_registered 看到这个已经离终点很近了。
因为 checkRegistration 是把当前注册状态写到 is_registered 里的函数,那么肯定就有另外一个读函数,根据 is_registered 的 DATA XREF 找到读函数 isRegistered。
// RegistrationController + (char)isRegistered
char __cdecl +[RegistrationController isRegistered](struct RegistrationController_meta *self, SEL a2)
{
return is_registered ^ 1;
}
可以看到这个函数其实是在开始很显眼的 RegistrationController 里,逆向中,类名往往可以透漏很多信息。接下来这里的操作只需要把 isRegistered 修改成永远返回 1 的函数就行了,如果要用 IDA 修改的话只能找到对应 x86 汇编指令的 16 进制机器码然后在 HEX-View 里修改比较麻烦。
这里推荐使用 Hopper 来修改,直接 alt + A 就可以修改汇编指令了,很简单 ~ 图如下
改好了之后把 MachO 拖入 BetterZip.app 里面打开,👏👏👏,已经解决之前提出的 1.2 点问题了,现在只剩下问题 3 —— 反复弹框。
按理来说此时 BetterZip 已经认为是注册了的状态,不应该再弹框的,可现在还在弹框,那么有一种很可能的情况,先谈一个窗然后再根据注册状态修改/关闭弹窗。巧了,再回到之前看过的 testCode 当时有这么一句总结:“testCode 大多是一些根据当前注册状态修改提示界面和信息的代码”,刚好证实了我的这个猜想。
然后再看看 -[RegistrationController initWithParams:] 确实有调用相关代码 [[self super] initWithWindowNibName:@"Register"] , 直接把相关代码 ret 掉,大功告成 ~