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 掉,大功告成 ~