吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7406|回复: 15
收起左侧

[Android 原创] 记录一次xx应用的伪破解(二)(利用代码注入和远程调试smali解决下载bug)

  [复制链接]
nbwzlyd 发表于 2019-6-14 18:31
本帖最后由 nbwzlyd 于 2019-6-17 14:11 编辑

上一篇链接
[记录一次xx应用的伪破解(一)(去广告 增加vip功能)](https://blog.csdn.net/wzlyd1/article/details/90695187)
上一篇我们破解了vip的投屏功能和 签名验证,这一篇我么将 破解VIP的下载功能和代{过}{滤}理验证功能。
@[TOC]
废话不多说,现在开始
## 破解VIP的下载功能


和投屏功能一样,一个下载还要VIP,不能忍,点击如下图
TIM截图20190530153200.png
简直不能忍,与破解搜索功能一模一样的逻辑,搜索下载功能文案,搜索到命名 dialog_download_for_vip
再次搜索,得到id 0x7f0f00aa,搜索id,查到引用的地方,打开java源码,搜索到调用的地方
TIM截图20190530153539.png
然后搜索FH()的引用,
TIM截图20190530153740.png
可以看到,如果用户等级不符合的情况将会弹框,然后return,方法如出一辙,返回AK,查看smali代码,然后将其进行屏蔽,打包,
TIM截图20190530154048.png


运行,发现,崩了~
TIM截图20190530154544.png

这就很出人意料了,甭在哪里了呢,这就需要看log了,
方法如下
点击AK的日志
TIM截图20190530154640.png

TIM截图20190530154811.png
TIM截图20190530154829.png

选择我们应用的进程,点击开始,然后运行应用让他崩掉,就可以看到崩溃日志了,原因如下
TIM截图20190530155019.png
具体是啥原因呢,咱也不敢问,但是一定是我们的屏蔽代码出了问题,导致代码运行出错,所以,我们返回ak,查看smali文件,因为我们要的效果是不弹框,然后能下载,所以我们只需要屏蔽掉这段,然后将跳转逻辑进行更改,如下图
TIM截图20190530160001.png
运行,完美
TIM截图20190530160418.png
但是你以为这样就完成了么,其实并没有,点击我的----->我的缓存,发现里面赫然写着
TIM截图20190530160547.png
已缓存完成,这绝对不可能,肯定是有bug了,难道有服务端校验?我们排查一下,首先想到的是抓包看看在点击缓存的时候有没有再次请求服务器,所以我们要进行抓包,配置好代{过}{滤}理,开搞
WTF
出现了再次弹框
TIM截图20190530160908.png
看来app为了防止装包又再次进行了检测,判断开了代{过}{滤}理连数据都不下发了,兵来将挡,水来土掩,我们接下来破解代{过}{滤}理检测
## 代{过}{滤}理检测的破解
话说你写了这个判断逻辑不让我抓包,那我就破解掉


破解方案如出一辙:搜索文案,查找代码,进行逻辑修改屏蔽


[Java] 纯文本查看 复制代码
.method private ax(Landroid/content/Context;)Z
    .locals 5


    .line 41
    sget v0, Landroid/os/Build$VERSION;->SDK_INT:I


    const/4 v1, 0x0


    const/4 v2, 0x1


    const/16 v3, 0xe


    if-lt v0, v3, :cond_0


    const/4 v0, 0x1


    goto :goto_0


    :cond_0
    const/4 v0, 0x0


    :goto_0
    if-eqz v0, :cond_2


    const-string p1, "http.proxyHost"


    .line 49
    invoke-static {p1}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;


    move-result-object p1


    const-string v0, "http.proxyPort"


    .line 51
    invoke-static {v0}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;


    move-result-object v0


    if-eqz v0, :cond_1


    goto :goto_1


    :cond_1
    const-string v0, "-1"


    .line 53
    :goto_1
    invoke-static {v0}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I


    move-result v0


    goto :goto_2


    .line 56
    :cond_2
    invoke-static {p1}, Landroid/net/Proxy;->getHost(Landroid/content/Context;)Ljava/lang/String;


    move-result-object v0


    .line 57
    invoke-static {p1}, Landroid/net/Proxy;->getPort(Landroid/content/Context;)I


    move-result p1


    move-object v4, v0


    move v0, p1


    move-object p1, v4


    .line 59
    :goto_2
    invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z


    move-result p1


    if-nez p1, :cond_3


    const/4 p1, -0x1


    if-eq v0, p1, :cond_3


    const/4 v1, 0x1


    :cond_3
    return v1
.end method


这么长一大段代码怎么看呢,其实很简单,method private ax(Landroid/content/Context;)Z 表示一个叫ax的方法,返回值是布尔类型,所以我们只需要看return值。
可以看到return的值的V1,那么V1的值是多少呢,代码const/4 v1, 0x1 是1 ,也就是true,这段代码的意思就是在开启代{过}{滤}理的情况下返回true,所以,我们给他返回false即可,将v1改为0x0,永远返回false即可。打包验证,完美获得数据
TIM截图20190530162223.png
这时候我们再进行抓包,就没问题了。
~~果然,在点击下载按钮后,会请求网络,然后真VIP用户会不断下发.ts结尾的视频流网址 如图~~
TIM截图20190530165651.png
~~但是非VIP用户在请求完数据后,这些数据将不再下发,所以这个功能破解失败。(只是暂时的,剩下的我们需要动态调试smali才行,前期准备工作参考[《教我兄弟学Android逆向04 动态调试smali代码 》](https://www.52pojie.cn/thread-658865-1-1.html)
其实这个说好弄也好弄,说不好弄也不好弄,就是查看VIP的请求数据~~
TIM截图20190530170217.png
~~GET方式请求,其中有个wsSecet参数,很可疑,我们复制出来在AK中进行搜索:
查到如下代码~~
TIM截图20190530170317.png
~~所以我们就可以在这里作为突破点进行测试,把值写死。这是一个解决方案,但是查看这个接口返回,并没有值,所以我怀疑是其他地方的原因,至于是哪里,就需要动态调试smali参数才知道了,这个还没调试出来,目前搁置~~
## 解决应用的VIP下载功能存在的逻辑bug
在不断的调试过程中,发现vip用户和非vip用户下发的数据是一致的,之前不一致可能是因为网络的原因,囧。
是怎么发现的呢?在调试过程中发现,突然有文件下载成功了,如图
TIM截图20190531111401.png
我一度怀疑是假数据,于是断网看了一下,发现观看一点问题没有,为了保险起见,我又进行了抓包,发现不断有数据的请求
如图

于是我就断定,原来数据在后台是一直在下载的,只不过我们没有看见!那么,问题会出在哪里呢?有两种猜测:
1.列表组件被Gone或者Invisible了。
2.某一段代码有对权限判断,但是判断的逻辑又不完整,存在bug。
可是这么多代码我们怎么去找问题?这个时候就需要借助一下DDMS工具,这个工具在SDK目录下的tools文件夹下,双击monitor.bat,启动DDMS
TIM截图20190531192044.png
点击上方的手机图案,进行界面分析
TIM截图20190531192319.png
通过这个界面得到这个布局的id后,我们和之前一样,将id复制进AK,进行搜索,然后就可以得到这个布局的变量引用
TIM截图20190531192623.png
然后再输入该变量名进行搜索 ,可以看到代码中引用的地方了,通过jd-gui进行查看源码可以看到,在Fe方法中,会将


该布局进行显示,同时会将数据布局进行隐藏
TIM截图20190531193011.png
所以关键就在Fe()方法的调用处了,这个方法是public的方法,调用的地方大概率是在其他地方调用的,这个调用方式不像我们的AS那么容易,一点就可以自动定位到调用的地方,我们依然需要全局搜索该方法。可以看到,我们总共有3个地方调用了该方法
TIM截图20190531193425.png
第一个就不用关心了,点击第二个进去看看,发现这个只是个抽象方法, 点击第三个文件,发现这个就是我们要找的调用的地方,那么,我们怎么判断出来的呢?
TIM截图20190531193739.png
这个Fe()方法并没有显眼的DownLoadActivity字眼,但是有个DownloadContract$b的字眼,前面的只是他的包名,这个是smali语法。我们这时候切换回DownLoadActivity源码进行查看
TIM截图20190531193935.png
看到了吗,DownLoadActivity是继承自DownloadContract.b的,我们就可以大胆的判定这就是我们要找的引用的地方了。
通过jd-gui进行查看
TIM截图20190531194137.png
[Java] 纯文本查看 复制代码
void getMU38Items(String paramString, List<M3U8DownloadBean> paramList1, List<M3U8DownloadBean> paramList2)
  {
    ArrayList localArrayList = new ArrayList();
    StringBuilder localStringBuilder = new StringBuilder();
    localStringBuilder.append("getMU38Items downloaded size");
    localStringBuilder.append(paramList1.size());
    com.pumpkinteam.pumpkinplayer.utils.k.d("DownloadPresenter", localStringBuilder.toString());
    localStringBuilder = new StringBuilder();
    localStringBuilder.append("getMU38Items downloading size");
    localStringBuilder.append(paramList2.size());
    com.pumpkinteam.pumpkinplayer.utils.k.d("DownloadPresenter", localStringBuilder.toString());
    switch (paramString.hashCode())
    {
    default: 
      break;
    case 52: 
      if (paramString.equals("4")) {
        i = 3;
      }
      break;
    case 51: 
      if (paramString.equals("3")) {
        i = 1;
      }
      break;
    case 50: 
      if (paramString.equals("2")) {
        i = 2;
      }
      break;
    case 49: 
      if (paramString.equals("1")) {
        i = 0;
      }
      break;
    }
    int i = -1;
    switch (i)
    {
    default: 
      break;
    case 2: 
    case 3: 
      if (!paramList2.isEmpty())
      {
        localArrayList.add("正在缓存");
        localArrayList.addAll(paramList2);
      }
      if (!paramList1.isEmpty())
      {
        localArrayList.add("已缓存");
        localArrayList.addAll(paramList1);
      }
      break;
    case 0: 
    case 1: 
      if (!paramList1.isEmpty())
      {
        localArrayList.add("已缓存");
        localArrayList.addAll(paramList1);
      }
      else
      {
        ((DownloadContract.b)this.mView).Fe();
      }
      break;
    }
    ((DownloadContract.b)this.mView).H(localArrayList);
  }
```

总共就这两个地方调用了该方法。剩下的就是进行逻辑分析了,`if ((localList1.isEmpty()) && (localList2.isEmpty()))`
如果这个判断成立,就会显示暂无缓存数据,然后return掉。所以,我们可能在这个地方出现了问题。接下来,我们将利用代码注入的功能,来辅助我们进行判断。(我自己有进行smali的debug,效果不尽如意,费时费力,不如打log,所以本篇以log形式来讲述)
## 利用代码注入分析问题
怎么注入代码呢?我们仍然需要再smali文件中进行操作,这个就要求你对smali语法不仅会读,还要会写。如果你真的不会写,也没关系,打开AK,如图,点击Android---->插入代码管理器--->选择log---->复制代码,
TIM截图20190531195328.png
粘贴到smali代码中合适的位置。这个就对你的smali语法要求比较高了,否则你根本不知道插入到哪里。插入结果如下图
TIM截图20190531195549.png
意思就是在下载数据为空的时候,我们打印个log,显示“kongle”,运行app,那么。怎么查看打印的log呢?
方法如下图:
TIM截图20190531200123.png
TIM截图20190531200147.png
打开日志---->选择PID---->选中要调试的app进行----->确定---->点击开始按钮 即可,如果自己写的代码没问题,会显示打印log
TIM截图20190531200610.png
可以看到,我们在没做任何操作的情况下,确实打印了log,走进了`if ((localList1.isEmpty()) && (localList2.isEmpty()))`分支,这个逻辑没有问题,但是如果我们下载一个视频后再看日志,发现这个日志并没有再次打印,这就说明其中有个数组肯定是不为空的,那么哪个数组不为空呢?同样,打印log来看:
![在这里插入图片描述]()
(具体log代码不再贴出来,自己分析代码)
所以通过这个我们可以得到两个信息:
**1.我么点击下载后,数组确实不为空了,说明数据源上没有大问题
2.paramList1 是已下载的数据,paramList2是正在下载的数据。**
那么问题应该不是出现在这里了,别忘了,我们还有一个地方调用了Fe()方法,很可能,就是这里出的问题,
TIM截图20190531201640.png
可以看到,在case0 和case1 时候,如果已经下载的数据为空,就直接显示没有缓存,隐藏RV了。那么问题就出现在case 这个分支上,0,和1 是哪里来的呢? 这时候我们就得查找这个方法引用的地方,通过搜索发现在`void getM3U8Tasks(String paramString)`调用了这个方法,那么`void getM3U8Tasks(String paramString)`又是在哪里调用的呢?
在DownLoadActivity里面
TIM截图20190531202405.png
这时候有个值引起了我们的注意 userLevel,用户等级,还记得我们在破解vip投屏功能时候的判断吗 ,非2&&非4,就不给用户投屏权限了,这两个值应该是vip特定字段,我们在破解投屏功能时候并没有修改用户的userLevel,只是注释了代码,所以这个时候取出来的值,还是默认的1(**可能某个时机有对这个level进行写入,但是我们这就先不管了,点击进入这个方法的源码可以知道这个就是sharepreference**)
微信截图_20190603155940.png
为了保险起见,我们debugSmali代码可以看到内存中mUserLevel的值
TIM截图20190531142741.png

是1.所以我们现在总结一下:


**由于我们破解vip功能并没有对用户的等级重新定义,所以用户的等级依然是默认的1,这时候会判断 paramsList1 是否为空,是空的话直接就隐藏布局,显示暂无缓存了。
这个是有bug的,或者是不严谨的,当然用户等级不异常的话没问题,只是我们现在等级是1,虽然我么已下载的数据是空,但是我们正在下载的数据不是空,但是源码里直接没有判断正在下载的数据是否为空,直接就隐藏布局,显示错误界面了,所以我们该怎么改就豁然开朗了。**


修改代码如下:
TIM截图20190531203806.png
将原有的 `((DownloadContract.b)this.mView).Fe();`定义为cond_4, 在下载数据为空且已下载的数据为空时,调用cond_4,否则会将数据**`localArrayList.addAll(paramList1);`**。
至此,打包,验证,就会发现,完美解决问题
TIM截图20190531204757.png
数据可以正常显示了,且数据正常下载。


这期由于涉及了较多的smali语法知识,所以读起来不容易懂,所以smali语法很重要。


**下一期将会介绍开屏广告和弹框广告的删除。未完待续**
TIM截图20190531202759.png
微信截图_20190603155600.png
微信截图_20190603163130.png

免费评分

参与人数 5威望 +2 吾爱币 +15 热心值 +5 收起 理由
看什么看NMSL + 1 谢谢@Thanks!
XhyEax + 1 + 1 我很赞同!
tiger2005 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
多幸运遇见baby + 1 + 1 用心讨论,共获提升!
qtfreet00 + 2 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| nbwzlyd 发表于 2019-6-21 12:21
ly55201 发表于 2019-6-21 10:59
我这个啥也不懂得小白,竟然像看天书一样看完了。。。。。其实也想跟着破解,真的无力解决,就想要个成品给 ...

链接:https://pan.baidu.com/s/14U627I92phgcMuk3NYTUiA&#160;提取码:w2jl&#160;
 楼主| nbwzlyd 发表于 2019-6-21 12:20
ly55201 发表于 2019-6-21 10:59
我这个啥也不懂得小白,竟然像看天书一样看完了。。。。。其实也想跟着破解,真的无力解决,就想要个成品给 ...

会安卓开发了会觉得比较简单
Hmily 发表于 2019-6-14 18:36
FZW云焕 发表于 2019-6-14 19:01
好厉害,谢谢楼主
ly55201 发表于 2019-6-21 10:59
我这个啥也不懂得小白,竟然像看天书一样看完了。。。。。其实也想跟着破解,真的无力解决,就想要个成品给孩子投屏动画片。
ly55201 发表于 2019-6-21 15:33
nbwzlyd 发表于 2019-6-21 12:21
链接:https://pan.baidu.com/s/14U627I92phgcMuk3NYTUiA&#160;提取码:w2jl&#160;

替孩子谢谢了
wanglinok 发表于 2019-6-23 00:11
不错,学习了
longcy 发表于 2019-6-28 17:54
大佬好厉害,不过我都看晕了后面,第一篇还能看的很懂
18386075467 发表于 2019-7-19 16:55
我想要原版没改过的 楼主哒哒
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 18:43

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表