nbwzlyd 发表于 2019-6-14 18:31

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

本帖最后由 nbwzlyd 于 2019-6-17 14:11 编辑

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


和投屏功能一样,一个下载还要VIP,不能忍,点击如下图

简直不能忍,与破解搜索功能一模一样的逻辑,搜索下载功能文案,搜索到命名 dialog_download_for_vip
再次搜索,得到id 0x7f0f00aa,搜索id,查到引用的地方,打开java源码,搜索到调用的地方

然后搜索FH()的引用,

可以看到,如果用户等级不符合的情况将会弹框,然后return,方法如出一辙,返回AK,查看smali代码,然后将其进行屏蔽,打包,



运行,发现,崩了~


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





选择我们应用的进程,点击开始,然后运行应用让他崩掉,就可以看到崩溃日志了,原因如下

具体是啥原因呢,咱也不敢问,但是一定是我们的屏蔽代码出了问题,导致代码运行出错,所以,我们返回ak,查看smali文件,因为我们要的效果是不弹框,然后能下载,所以我们只需要屏蔽掉这段,然后将跳转逻辑进行更改,如下图

运行,完美

但是你以为这样就完成了么,其实并没有,点击我的----->我的缓存,发现里面赫然写着

已缓存完成,这绝对不可能,肯定是有bug了,难道有服务端校验?我们排查一下,首先想到的是抓包看看在点击缓存的时候有没有再次请求服务器,所以我们要进行抓包,配置好代{过}{滤}理,开搞
WTF
出现了再次弹框

看来app为了防止装包又再次进行了检测,判断开了代{过}{滤}理连数据都不下发了,兵来将挡,水来土掩,我们接下来破解代{过}{滤}理检测
## 代{过}{滤}理检测的破解
话说你写了这个判断逻辑不让我抓包,那我就破解掉


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


.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即可。打包验证,完美获得数据

这时候我们再进行抓包,就没问题了。
~~果然,在点击下载按钮后,会请求网络,然后真VIP用户会不断下发.ts结尾的视频流网址 如图~~

~~但是非VIP用户在请求完数据后,这些数据将不再下发,所以这个功能破解失败。(只是暂时的,剩下的我们需要动态调试smali才行,前期准备工作参考[《教我兄弟学Android逆向04 动态调试smali代码 》](https://www.52pojie.cn/thread-658865-1-1.html)
其实这个说好弄也好弄,说不好弄也不好弄,就是查看VIP的请求数据~~

~~GET方式请求,其中有个wsSecet参数,很可疑,我们复制出来在AK中进行搜索:
查到如下代码~~

~~所以我们就可以在这里作为突破点进行测试,把值写死。这是一个解决方案,但是查看这个接口返回,并没有值,所以我怀疑是其他地方的原因,至于是哪里,就需要动态调试smali参数才知道了,这个还没调试出来,目前搁置~~
## 解决应用的VIP下载功能存在的逻辑bug
在不断的调试过程中,发现vip用户和非vip用户下发的数据是一致的,之前不一致可能是因为网络的原因,囧。
是怎么发现的呢?在调试过程中发现,突然有文件下载成功了,如图

我一度怀疑是假数据,于是断网看了一下,发现观看一点问题没有,为了保险起见,我又进行了抓包,发现不断有数据的请求
如图

于是我就断定,原来数据在后台是一直在下载的,只不过我们没有看见!那么,问题会出在哪里呢?有两种猜测:
1.列表组件被Gone或者Invisible了。
2.某一段代码有对权限判断,但是判断的逻辑又不完整,存在bug。
可是这么多代码我们怎么去找问题?这个时候就需要借助一下DDMS工具,这个工具在SDK目录下的tools文件夹下,双击monitor.bat,启动DDMS

点击上方的手机图案,进行界面分析

通过这个界面得到这个布局的id后,我们和之前一样,将id复制进AK,进行搜索,然后就可以得到这个布局的变量引用

然后再输入该变量名进行搜索 ,可以看到代码中引用的地方了,通过jd-gui进行查看源码可以看到,在Fe方法中,会将


该布局进行显示,同时会将数据布局进行隐藏

所以关键就在Fe()方法的调用处了,这个方法是public的方法,调用的地方大概率是在其他地方调用的,这个调用方式不像我们的AS那么容易,一点就可以自动定位到调用的地方,我们依然需要全局搜索该方法。可以看到,我们总共有3个地方调用了该方法

第一个就不用关心了,点击第二个进去看看,发现这个只是个抽象方法, 点击第三个文件,发现这个就是我们要找的调用的地方,那么,我们怎么判断出来的呢?

这个Fe()方法并没有显眼的DownLoadActivity字眼,但是有个DownloadContract$b的字眼,前面的只是他的包名,这个是smali语法。我们这时候切换回DownLoadActivity源码进行查看

看到了吗,DownLoadActivity是继承自DownloadContract.b的,我们就可以大胆的判定这就是我们要找的引用的地方了。
通过jd-gui进行查看

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---->复制代码,

粘贴到smali代码中合适的位置。这个就对你的smali语法要求比较高了,否则你根本不知道插入到哪里。插入结果如下图

意思就是在下载数据为空的时候,我们打印个log,显示“kongle”,运行app,那么。怎么查看打印的log呢?
方法如下图:


打开日志---->选择PID---->选中要调试的app进行----->确定---->点击开始按钮 即可,如果自己写的代码没问题,会显示打印log

可以看到,我们在没做任何操作的情况下,确实打印了log,走进了`if ((localList1.isEmpty()) && (localList2.isEmpty()))`分支,这个逻辑没有问题,但是如果我们下载一个视频后再看日志,发现这个日志并没有再次打印,这就说明其中有个数组肯定是不为空的,那么哪个数组不为空呢?同样,打印log来看:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190531201048305.png)
(具体log代码不再贴出来,自己分析代码)
所以通过这个我们可以得到两个信息:
**1.我么点击下载后,数组确实不为空了,说明数据源上没有大问题
2.paramList1 是已下载的数据,paramList2是正在下载的数据。**
那么问题应该不是出现在这里了,别忘了,我们还有一个地方调用了Fe()方法,很可能,就是这里出的问题,

可以看到,在case0 和case1 时候,如果已经下载的数据为空,就直接显示没有缓存,隐藏RV了。那么问题就出现在case 这个分支上,0,和1 是哪里来的呢? 这时候我们就得查找这个方法引用的地方,通过搜索发现在`void getM3U8Tasks(String paramString)`调用了这个方法,那么`void getM3U8Tasks(String paramString)`又是在哪里调用的呢?
在DownLoadActivity里面

这时候有个值引起了我们的注意 userLevel,用户等级,还记得我们在破解vip投屏功能时候的判断吗 ,非2&&非4,就不给用户投屏权限了,这两个值应该是vip特定字段,我们在破解投屏功能时候并没有修改用户的userLevel,只是注释了代码,所以这个时候取出来的值,还是默认的1(**可能某个时机有对这个level进行写入,但是我们这就先不管了,点击进入这个方法的源码可以知道这个就是sharepreference**)

为了保险起见,我们debugSmali代码可以看到内存中mUserLevel的值


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


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


修改代码如下:

将原有的 `((DownloadContract.b)this.mView).Fe();`定义为cond_4, 在下载数据为空且已下载的数据为空时,调用cond_4,否则会将数据**`localArrayList.addAll(paramList1);`**。
至此,打包,验证,就会发现,完美解决问题

数据可以正常显示了,且数据正常下载。


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


**下一期将会介绍开屏广告和弹框广告的删除。未完待续**

nbwzlyd 发表于 2019-6-21 12:21

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

链接:https://pan.baidu.com/s/14U627I92phgcMuk3NYTUiA 提取码:w2jl 

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 提取码:w2jl 

替孩子谢谢了{:1_893:}

wanglinok 发表于 2019-6-23 00:11

不错,学习了

longcy 发表于 2019-6-28 17:54

大佬好厉害,不过我都看晕了后面,第一篇还能看的很懂

18386075467 发表于 2019-7-19 16:55

我想要原版没改过的 楼主哒哒
页: [1] 2
查看完整版本: 记录一次xx应用的伪破解(二)(利用代码注入和远程调试smali解决下载bug)