为了看爱情动作片,没忍住,我把这款短视频APP给。。

栏目:成人教育  时间:2023-02-07
手机版

  点击上方“java进阶架构师”,选择右上角“置顶公众号”

  20大进阶架构专题每日送达

  

  来源:https://www.jianshu.com/p/51dcc24900ba

  前言

  在一个夜黑风高的晚上,我的基友突然给我发了一个叫“xx社区”的app,这是什么玩意?找了一张可以上墙的图

  

  我凑,是个美女直播软件,迫不及待的我,一阵点点点,发现最亮点的功能是第二个tab页,是类似抖音的短视频,找了一张正经的图,抖音都没这么正经吧。。。

  

  我去,做的效果跟抖音一毛一样,上下滑动切换视频,聊这个,那我可不困了,在滑动了差不多20个视频之后,出现了这个

  

  卧槽,这能忍,作为一个Android开发攻城狮,充值是不可能充值的。太晚了,打算明天再破解它的收费功能。于是,第二天很早就起床打开电脑,这篇文章就开始了…

  反编译

  将apk发送到电脑,然后打开jadx-gui传送门,直接选择这个apk,打开发现源码是这个样子的

  

  熟悉逆向的朋友们肯定猜到了,这个apk使用了腾讯加固,所以反编译出来只有腾讯加固的几个类,看不到目标源码。那怎么办?

  既然加固了,第一步就是要给它脱壳

  脱壳(需要xposed支持)

  FDex2

  通过Hook ClassLoader的loadClass方法,反射调用getDex方法取得Dex(com.android.dex.Dex类对象),在将里面的dex写出。

  下载安装,在xposedinstaller中勾选模块并重启,然后打开FDex2选择xx社区,然后重启xx社区,即可在对应目录找到dex文件的踪影

  分析代码

  上一步通过FDex2,成功脱壳

  

  拿到三个dex,依次用jadx-gui打开,根据包名,可以找到对应Activity的位置

  

  如何得到Activity名称,这个可以用无障碍,也可以直接过滤日志,比如,打开界面,然后日志过滤“start|activity” start u0

  D: pid=30739, uid=10274, component=ComponentInfo{com.one.tomato/com.one.tomato.ui.StartUpActivity}I: AppChangeImpl:pid: 30739 uid: 10274 pkg: com.one.tomato class: com.one.tomato.ui.StartUpActivityD: activityResumed:pid=30739, uid=10274, component=ComponentInfo{com.one.tomato/com.one.tomato.ui.StartUpActivity}D: checkIsMonitorVideoScence input :com.one.tomato,com.one.tomato.ui.StartUpActivityD: handleActivityChange, curPackage:com.one.tomato, curClass:com.one.tomato.ui.StartUpActivityD: checkIsMonitorAPKScence input :com.one.tomato,com.one.tomato.ui.StartUpActivityD: handleActivityChange, it is not a care app or scenceD: handleActivityChange, curr mAppType:-1, lastType:-1

  com.one.tomato.ui.StartUpActivity 就是主页了主页一共有5个tab,每个tab应该对应一个fragment,我们要先找到第二个tab对应的fragment

  

  看到点击事件,通过命名方式可以猜到这个是底部tab的点击事件,点一下应该会切换显示fragment

  

  果然不出我所料,第二个tab是PapaTabFragment,搜索一下

  

  打开看看

  

  虽然代码被混淆了,但是可以猜到这里是初始化的方法,有头像和收藏图标的点击事件,当然,这个不是重点,哈哈,我们还是要先分析一下什么时候触发这个弹窗,弹窗的条件是播放次数到达到一个值,根据这个条件,快速浏览一下PapaTabFragment这个类,代码不多,只有1千行多一点

  

  发现疑点,LookTimes是观看时间,VideoPlayCountUtils是播放次数。

  VideoPlayCountUtils 是一个单例,所以看b和c方法

  

  “video_play_count” 这么明显的字眼,播放次数,这个次数是从PreferencesUtil中获取的,也就是存在sp中,

  

  PreferencesUtil 中的e方法,如果登录信息不为空,就返回信息中的id,空就返回0,所以第一次打开才会是0,可以观看,之后这个LoginInfo不为空了,开始统计观看次数了。so,让它总是返回0?

  上代码

  Xposed模块开发基础就不说了,默认你已经会了,不会自己去查一下,Xposed基础不是本文的重点。//播放数总返回0,无限制观看

  private static void hookVideoCount(XC_LoadPackage.LoadPackageParam param, ClassLoader loader){ LogUtil.d(TAG,"hookVideoCount start"); hook_method("com.one.tomato.utils.PreferencesUtil", loader, "e", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"beforeHookedMethod hookVideoCount"); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"afterHookedMethod 视频次数返回 0 "); param.setResult(0); } }); }protected static void hook_method(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) { try { XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback); } catch (Exception e) { XposedBridge.log(e); xLog(e.getMessage()); } }

  然后其实会发现一个问题,class not found,找了很久,最后才反应过来,因为apk经过加固,必须要用壳的ClassLoader来加载类,因为真正的代码是腾讯加固程序启动后它去加载真正的dex文件的。

  

  这里我们可以hook TxAppEntry 的 attachBaseContext 方法

  public static void hookClassLoader(final XC_LoadPackage.LoadPackageParam loadPackageParam){ if (classLoader == null){ try { //腾讯加固,需要获取对应classloader XposedHelpers.findAndHookMethod("com.tencent.StubShell.TxAppEntry", loadPackageParam.classLoader, "attachBaseContext", Context.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); //获取到Context对象,通过这个对象来获取classloader Context context = (Context) param.args[0]; //获取classloader,之后hook加固后的就使用这个classloader TomatoModule.classLoader = context.getClassLoader(); LogUtil.d("成功hook classloader"); hookAD(loadPackageParam, classLoader); openLog(loadPackageParam, classLoader); hookVideoCount(loadPackageParam, classLoader); } }); }catch (Exception e){ LogUtil.e(e.getMessage()); } } }

  然后安装试了一下,卧槽,真的无限制观看了,我是V8了?

  

  确实成功了,效果图就不发了,大家可以动手试试。

  另外,跳过首页广告和打开日志的hook点我也很快找到了

  //自动跳过广告 private static void hookAD(XC_LoadPackage.LoadPackageParam param, ClassLoader loader) { LogUtil.d(TAG,"hook StartUpActivity start"); hook_method("com.one.tomato.ui.StartUpActivity", loader, "onCreate", Bundle.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"beforeHookedMethod onCreate"); Object object = param.thisObject; LogUtil.d(object.toString()); Class<?> aClass = object.getClass(); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { LogUtil.d(method.getName()); if (method.getName().equals("z")){ LogUtil.d("自动跳过广告页"); method.setAccessible(true); method.invoke(object); } } Toast.makeText((Context) param.thisObject, "xposed并跳过广告,功能正常", Toast.LENGTH_SHORT).show(); } }); } //打开日志开关 private static void openLog(XC_LoadPackage.LoadPackageParam param, ClassLoader loader){ LogUtil.d(TAG,"hook LogUtil start"); hook_method("com.one.tomato.utils.LogUtil", loader, "a", int.class,String.class,Object.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Object object = param.thisObject; LogUtil.d(object.toString()); Class<?> aClass = object.getClass(); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { LogUtil.d(method.getName()); if (method.getName().equals("a")){ LogUtil.d("openLog"); method.setAccessible(true); method.invoke(object,true); return; } } } }); }

  不是很难找,这里就留给大家自己去实践学习了。

  注意:不管xx社区后期是否停止服务(你懂的),本文只是技术分享,拒绝黄赌毒。

  虽然破解了短视频模块播放次数限制之后,发现播放经常弹出“参数错误 ”的toast,这TM能忍,继续干。然后看到”我的“界面的次数没有被hook

  

  说明没有hook彻底,如果应用中其它地方用了这里的 0/30 做判断,那么有可能还会被限制,请继续看:

  

  首先,打开 MineTabFragment 这个类,这个上一章已经分析过了,”我的“ 那个tab对应 MineTabFragment,然后寻寻觅觅,起初没找到免费观看次数是哪个textview,然后翻到最上面,发现导包的地方有个 import com.one.tomato.utils.VideoPlayCountUtils;,顾名思义,就是获取播放次数的类,然后就发现了上图这个调用的地方

  if (VideoPlayCountUtils.a().b() != -2) { z = false; } a(z, VideoPlayCountUtils.a().b(), VideoPlayCountUtils.a().c());

  看下 VideoPlayCountUtils.a()

  

  VideoPlayCountUtils 是一个单例,所以我们直接看 b 方法

  

  和c 方法

  

  看到 video_play_count 没,都是跟播放次数有关,盘它就对了

  private static void hookVideoCountUtil(XC_LoadPackage.LoadPackageParam param, ClassLoader loader){ hook_method("com.one.tomato.utils.VideoPlayCountUtils", loader, "b", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"afterHookedMethod VideoPlayCountUtils.b --- " + param.getResult()); } }); hook_method("com.one.tomato.utils.VideoPlayCountUtils", loader, "c", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"afterHookedMethod VideoPlayCountUtils.c --- " + param.getResult()); } }); }

  先打印两个值看看,build and install 重启xx社区,看log

  

  28/30,对应就是剩余播放次数和总的可以播放次数上面的原图是29,因为中间手抖播放了一次,所以剩下28,没毛病。。改返回值啦,很简单了,最终代码如下

  private static void hookVideoCountUtil(XC_LoadPackage.LoadPackageParam param, ClassLoader loader){ //剩余播放次数 hook_method("com.one.tomato.utils.VideoPlayCountUtils", loader, "b", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"afterHookedMethod VideoPlayCountUtils.b --- " + param.getResult()); param.setResult(1000); } }); //总的可以播放次数 hook_method("com.one.tomato.utils.VideoPlayCountUtils", loader, "c", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { LogUtil.d(TAG,"afterHookedMethod VideoPlayCountUtils.c --- " + param.getResult()); param.setResult(1000); } }); }

  重新打开xx社区

  

  可播放了总次数都是1000,怎么播都不变了

  

  PS:本文为纯技术分享,文章内容不涉黄,适合对xposed有兴趣的读者。没别的意思,技术人要对技术有追求!

  ———— e n d ————

  本期专题推荐

  手

  专

  题

  写

  【手写专题】师长说:想要进阶架构师,不仅仅只是懂得框架原理。下面,就让师长手把手带你手写Spring MVC、Spring、Mybatis、秒杀架构、RPC等框架,让你提升架构思维,真正吃透!

  ↓↓↓↓↓点击标题即可跳转↓↓↓↓↓

  跳转前别忘了先在本篇文章留言,在看

  写出我的第一个框架:迷你版Spring MVC

  透彻理解MyBatis设计思想之手写实现

  透彻理解Spring事务设计思想之手写实现

  理解数据库连接池底层原理之手写实现

  手把手带你实现JDK动态代理

  手写实现一个迷你版的Tomcat

  自己动手写一个服务网关

  手把手带你设计一个百万级的消息推送系统

  手把手带你秒杀架构实践(含完整代码)

  解密Dubbo:自己动手编写一个较为完善的RPC框架(两万字干货)

  其余的微服务、分布式、高并发、JVM调优等20大进阶架构师专题请关注公众号【Java进阶架构师】后在菜单栏查看。

  

  看到这里,说明你喜欢本文

  你的转发,是对我最大的鼓励!在看亦是支持↓

上一篇:荷兰华人餐厅老板被未成年人砍死在海牙街头!
下一篇:营口理工学院成人高考本科需要几年?报名要求高吗?

最近更新成人教育