本文最后更新于 2024年10月16日 下午
时间过去一周多了,我们今天就来搞个番外篇,研究一下倒数日 这个app
的逆向获取vip
的方法
那么搞完番外篇就到去除开屏广告
的教程了,那我们废话不多说,开始今天的教程
准备工作
一台电脑
jadx
一台已root 的手机
frida
查壳
这里我们可以用appmsg
这个项目点我 进行软件的查壳,如果感觉使用麻烦,可以直接使用mt管理器
,那我们把安装包拷贝到手机上看看
注:我这里下错了版本,这个是旧版本,不是最新版
可以看到是未加固状态的,这里我们要记住com.clover.daysmatter
这个包名,等下hook
要用到
反编译分析
首先是安装这个app
,打开mt管理器的activity记录
,记录app
所在的组件
打开这个app
,同时启动服务
,我们尝试在没有会员的情况下,使用会员功能
接下来轮到jadx
出场了,直接搜索一下相关的字符,查看是否能出东西
可以看到,是搜索不出的,包括vip
这个字眼也是,我们把视角回到mt管理器上的 0oo0000
,搜索一下
找到这里来了,我们直接用frida
hook一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function hook_test ( ){ Java .perform ( function ( ) { var r = Java .use ("com.clover.daysmatter.OO0000O" ) r.OooO0O0 .implementation = function (msg ) { console .log ("函数被调用" ) var t = this .OooO0O0 (msg) console .log ("函数返回了" + t) return t } } ) }setImmediate (hook_test ())
函数压根没触发
那么这条线索断了,我们尝试其他方法
这里我就想开始破解内购了,首先我们先从字符串定位一下,搜索支付成功
有关的字眼
购买成功对应着cs_pay_success
,那我们就在源代码搜索一下cs_pay_success
,发现一个监听支付事件的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public final void handleMessage (Message message) { String str; String str2; if (message.what == 1 ) { Map map = (Map) message.obj; String str3 = null ; if (map == null ) { str = null ; str2 = null ; } else { str = null ; str2 = null ; for (String str4 : map.keySet()) { if (TextUtils.equals(str4, "resultStatus" )) { str = (String) map.get(str4); } else if (TextUtils.equals(str4, "result" )) { str3 = (String) map.get(str4); } else if (TextUtils.equals(str4, "memo" )) { } else if (TextUtils.equals(str4, "outTradeNumber" )) { str2 = (String) map.get(str4); } } } C0567o0 c0567o0 = C0567o0.this ; c0567o0.getClass(); if (TextUtils.equals(str, "9000" )) { Toast.makeText(c0567o0.OooO00o, c0567o0.OooO00o.getString(com.clover.clover_cloud.R$string.cs_pay_success), 1 ).show(); CSAlipaySuccessModel cSAlipaySuccessModel = new CSAlipaySuccessModel (c0567o0.OooO0Oo, c0567o0.OooO0o0, str3, str2); Context context = c0567o0.OooO00o; String json = new Gson ().toJson(cSAlipaySuccessModel); C2072o0OO0O0.OooOO0O = json; C2072o0OO0O0.OooO00o(context).edit().putString("CS_PREFERENCE_KEY_ALI_RESULT_INFO" , json).apply(); String sku = cSAlipaySuccessModel.getSku(); C2116o0OOo000 OooO00o = C2116o0OOo000.OooO0oO.OooO00o(c0567o0.OooO00o); String cSAlipaySuccessModel2 = cSAlipaySuccessModel.toString(); C3065oOo00OO.OooO0o(sku, "sku" ); C3065oOo00OO.OooO0o(cSAlipaySuccessModel2, "purchaseInfo" ); OooO00o.OooO00o.getApplicationContext().getSharedPreferences("PREFERENCE_NAME_LOCAL_PURCHASE" , 0 ).edit().putString(sku, cSAlipaySuccessModel2).apply(); c0567o0.OooO0O0(cSAlipaySuccessModel, c0567o0.OooO0OO); return ; } Toast.makeText(c0567o0.OooO00o, c0567o0.OooO00o.getString(com.clover.clover_cloud.R$string.cs_pay_failed), 1 ).show(); } } }
因此我们可以使用android.os.Message
和java.util.HashMap
重构对象,以下是frida
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 function hook_test ( ){ Java .perform (function ( ) { var OooO00 o = Java .use ("com.clover.daysmatter.o0$OooO00o" ); var Message = Java .use ("android.os.Message" ); var k = Java .use ("java.util.HashMap" ) OooO00 o["handleMessage" ].implementation = function (msg ) { var t = Java .cast (msg, Message ) var u = Java .cast (t.obj .value , k) u.put ("resultStatus" , "9000" ); this ["handleMessage" ](msg); }; }); }
测试一下,vip到手了
但是还没完,发现这个app
还有二次验证,因此我们继续逆向
网络验证
对于这种情况,我们打开抓包工具看看,配合ssl
关闭验证模块,查看一下网络请求,得知了他请求了一个获取订单信息的api
返回内容是
1 2 3 4 5 { "products_info" : { } , "unified_receipts_info" : [ ] , "success" : 0 }
通过定位字符串,但是这里混淆太厉害了,而且验证成功后无返回结果
注:这个app
里的资源文件 反编译出来的验证成功
字符串与支付无关,不要被混乱了
换思路
由于app
在启动的时候,就会检查内购支付完成,因此我们可以hook
启动的代码,搜索一下网络验证失败
的字符串,定位到
以下是源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void OooO0O0 (CSMarkPaidEntity cSMarkPaidEntity) { final C2647oO0Oo0oo c2647oO0Oo0oo = (C2647oO0Oo0oo) this .OooO0O0; C3065oOo00OO.OooO0o(c2647oO0Oo0oo, "$onPaymentFinishedListener" ); final C2116o0OOo000 c2116o0OOo000 = (C2116o0OOo000) this .OooO0OO; C3065oOo00OO.OooO0o(c2116o0OOo000, "this$0" ); final C4044oooo000 c4044oooo000 = new C4044oooo000 (); final ?? obj = new Object (); if (cSMarkPaidEntity != null && cSMarkPaidEntity.getSuccess()) { c4044oooo000.OooO = String.valueOf(cSMarkPaidEntity.getProducts_info()); obj.OooO = true ; } o0OO0O0O.OooO0O0().execute(new Runnable () { @Override public final void run () { C2647oO0Oo0oo c2647oO0Oo0oo2 = C2647oO0Oo0oo.this ; C3065oOo00OO.OooO0o(c2647oO0Oo0oo2, "$onPaymentFinishedListener" ); C3642ooO00ooO c3642ooO00ooO = obj; C3065oOo00OO.OooO0o(c3642ooO00ooO, "$success" ); C4044oooo000 c4044oooo0002 = c4044oooo000; C3065oOo00OO.OooO0o(c4044oooo0002, "$text" ); C2116o0OOo000 c2116o0OOo0002 = c2116o0OOo000; C3065oOo00OO.OooO0o(c2116o0OOo0002, "this$0" ); boolean z = c3642ooO00ooO.OooO; Looper.getMainLooper(); Looper.myLooper(); Application application = (Application) c2647oO0Oo0oo2.OooO0O0; if (z) { C3854ooOoO00.OooO0oO(application, true ); } else { Toast.makeText(application, application.getString(R.string.cs_check_ali_pay_failed), 1 ).show(); } C0580p0.OooOOO0("pay_consumed" , "alipay_success" , BuildConfig.FLAVOR); c2116o0OOo0002.OooO0O0(); C2447o0ooOOOO.OooO0O0().execute(new Object ()); } }); }
1 2 3 4 5 6 7 8 9 10 11 function main ( ){ Java .perfrom (function ( ) { let CSMarkPaidEntity =Java .use ("com.clover.clover_cloud.models.user_entities.CSMarkPaidEntity" ); CSMarkPaidEntity ["getSuccess" ].implementation = function ( ) { console .log (`CSMarkPaidEntity.getSuccess is called` ); let result = this ["getSuccess" ](); console .log (`CSMarkPaidEntity.getSuccess result=${result} ` ); return true ; }; }) }
最后vip
验证成功,各项功能正常