首先需要感谢的是这个软件并没有做太多的邪恶校验(当然这是因为App Store不允许混淆加壳吧hhhhh)
打开是卡在激活界面的,因为点击会跳转到itunes到app store,所以可以在这里找字符串(itunes)从而定位到-[NOMORawStoreView jumpAction],然后往上跳转到-[NOMORawStoreView nomoItemClick]。
Objective-c
简单阅读可以发现主要的判断逻辑是对xxxx-VIP这个key的,所以考虑直接把objectForKey都监听一下看看是哪个地方引用了。 frida-trace -U -N "com.blink.academy.nomoraw" -m "-[* objectForKey:]",然后改下代码
Javascript
defineHandler({ onEnter(log, args, state) { let key = new ObjC.Object(args[2]).toString(); log(`-[xxxClass objectForKey:${key}]`); }, onLeave(log, retval, state) { log(`-[xxxClass objectForKey:${this.key}] -> ${typeof retval} ${new ObjC.Object(retval)}`); }});可以看到只有NSUserDefaults被用了,所以直接hook这个就好了。
Javascript
const objectForKey = NSUserDefaults['- objectForKey:']; const objectForKey_old = objectForKey.implementation; objectForKey.implementation = ObjC.implement(objectForKey, (self,sel,any) => { const key = new ObjC.Object(any).toString(); if(key === "NOMO-VIP") { console.warn(`----------\\nO: -[NSUserDefaults objectForKey:${key}] -> 1\\n----------`); return 1 } return objectForKey_old(self,sel,any); })嗯这样发现跑不了?继续看ida可以看到还有个地方检测了subscription_is_valid和subscription_expire_at,这两个一个是bool一个是int,那就得把这几个全都hook一遍了
于是,最终代码:
Javascript
if(ObjC.available) { console.log("ObjC.available: true"); const fakePrefs = { "xxxx-VIP": true, "subscription_is_valid": true, "subscription_expire_at": "7983066142000", }; const NSUserDefaults = ObjC.classes.NSUserDefaults; const boolForKey = NSUserDefaults['- boolForKey:']; const boolForKey_old = boolForKey.implementation; boolForKey.implementation = ObjC.implement(boolForKey, (self,sel,any) => { const key = new ObjC.Object(any).toString(); if(key in fakePrefs) { console.warn(`----------\\nO: -[NSUserDefaults boolForKey:${key}] -> ${fakePrefs[key] ? "true" : "false"}\\n----------`); return fakePrefs[key] ? 1 : 0; } let retval = boolForKey_old(self,sel,any); return retval; }) const stringForKey = NSUserDefaults['- stringForKey:']; const stringForKey_old = stringForKey.implementation; stringForKey.implementation = ObjC.implement(stringForKey, (self,sel,any) => { const key = new ObjC.Object(any).toString(); if(key in fakePrefs) { console.warn(`----------\\nO: -[NSUserDefaults stringForKey:${key}] -> ${fakePrefs[key]}\\n----------`); return ObjC.classes.NSString.stringWithString_(fakePrefs[key]); } let retval = stringForKey_old(self,sel,any); return retval; }) const integerForKey = NSUserDefaults['- integerForKey:']; const integerForKey_old = integerForKey.implementation; integerForKey.implementation = ObjC.implement(integerForKey, (self,sel,any) => { const key = new ObjC.Object(any).toString(); if(key in fakePrefs) { console.warn(`----------\\nO: -[NSUserDefaults integerForKey:${key}] -> ${fakePrefs[key]}\\n----------`); let value = parseInt(fakePrefs[key]); return value } let retval = integerForKey_old(self,sel,any); return retval; }) const objectForKey = NSUserDefaults['- objectForKey:']; const objectForKey_old = objectForKey.implementation; objectForKey.implementation = ObjC.implement(objectForKey, (self,sel,any) => { const key = new ObjC.Object(any).toString(); if(key === "NOMO-VIP") { console.warn(`----------\\nO: -[NSUserDefaults objectForKey:${key}] -> 1\\n----------`); return 1 } if(key in fakePrefs) { console.warn(`----------\\nO: -[NSUserDefaults objectForKey:${key}] -> ${fakePrefs[key]}\\n----------`); return fakePrefs[key]; } return objectForKey_old(self,sel,any); }) console.log("Hooked");}PS: 现在启动的时候必须要进入界面然后点击激活才能用。同时在激活后还会弹窗,所以这个方式显然是不对的。
如果是走dylib注入就没那么多事了,在程序启动时直接校验正确了就不会有后面什么事了(笑
Update 8.5 网上找了下还真有,但是我不贴link, 想要自己去找吧(