【全网首发】手撕谷歌服务校验

查看 86|回复 9
作者:JiGuro   
说是全网首发其实不准确,因为本人已经在MT论坛发过了
今天在 Google Play 里发现了一款应用,名字叫 Kuku TV,还是三哥写的,可惜评分只有0.0:


6877723db4f92.jpg (262.83 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

下载下来之后,一点进去就直接给我弹了两个窗


6877b4cbe619f.jpg (120.8 KB, 下载次数: 4)
下载附件
2025-8-1 20:04 上传



221949rlcfgrh4r7aa0zje.jpg (53.5 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

好嘛,不就是谷歌服务校验吗?今天我就要手撕它。
第一个弹出的黑色窗口是软件自带的,第二个弹出的白色窗口则是谷歌官方的。两个窗口均用于谷歌服务较验。今天主要分析的是第二个谷歌官方窗口,此窗口在多款 Google Play 软件中均有发现。
贴心地翻译一下:
"

相信各位平时在使用一些外网软件时,由于手机上未装谷歌服务框架,或框架版本过老,一定也遇到过类似的情况。按照网上的教程折腾很久都搞不好。最恶心人的是,软件有的时候明明不需要用到任何谷歌框架,但是他偏要给你搞一个验证
目前在网络上,还未发现任何相关的逆向教程。现在我就要通过逆向的方法,带着大家一步步去除该弹窗。
看前提醒:本帖子内容可能有亿点长,很多干货,请准备好花生瓜子,耐心阅读。


6877ac18cd080.jpg (98.29 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

事先声明,本人手机上未装谷歌服务套件。由于本人技术能力有限,请各位大佬多多体谅!
老样子,先提取安装包。发现是 apks 包,这倒也不意外,外网很多软件其实都是 apks 格式。我们使用 MT 的转 apk 功能将 apks 格式转换成 apk 格式。


6877ac623ebf1.jpg (184.93 KB, 下载次数: 1)
下载附件
2025-8-1 20:04 上传

什么鬼?居然有谷歌加固,难道我还要脱壳?
网上查询得知,谷歌加固并不会加密 dex,且不会对 hook 做限制。
这方法不就来了吗?我们直接用我们的模块大法。


6877ac933613d.jpg (163.31 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

打开老色逼,启用模块 SimpleHook ,在其模块作用域里勾选 Kuku TV ,并进入模块拓展页面,添加 Kuku TV ,设置如下:


6877acaf67bbf.jpg (210.74 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

我们把复制来的热修复路径粘贴到 MT 管理器的路径栏里,定位到目录,并把原包中的所有 dex 文件复制到该路径中。当我们启动软件时,SimpleHook 就会自动帮我们修复 dex ,使我们的修改生效。这样,我们就能成功绕过签名校验,从而解决 dex 修改的问题。


6877ad2155b86.jpg (219.87 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

那么,现在的问题就变成了怎么修改。
这时,我们就要用到军哥写的神级模块——算法助手


6877acf3d1df3.jpg (164.46 KB, 下载次数: 1)
下载附件
2025-8-1 20:04 上传

像之前的 SimpleHook 一样配置好模块作用域,打开模块,添加 Kuku TV ,设置如下:


6877ad54a170a.png (101.8 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传



6877ad684f9de.png (132.97 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

设置好后,我们直接戳右上角的运行按钮,打开 Kuku TV ,在所有窗口都弹出后,返回算法助手,在日志页面就可以看到从 Kuku TV hook 到的信息。


6877ad8a001da.jpg (217.04 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

我们进入 Kuku TV 的日志,可以发现获取到了非常多的 log 记录。不用急,我们先定位到 Google 官方的这条弹窗,点进去发现其调用堆栈为:
"

我们忽略所有系统底层和模块本身的堆栈,不难发现弹窗方法就是位于 com.pairip.licensecheck.LicenseActivity 类的 lambda$showErrorDialog$0 方法。
我们直接使用 MT 打开之前解压出来的 dex ,定位到方法:


6877adbcdee55.jpg (273.96 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

分析代码后,我们发现,这个方法只是用于构建窗体及文本,并使其弹出。这时可能会有同学说,可以直接清空该方法。但这里是行不通的,如果对该方法进行修改,软件可能会直接闪退,且弹窗背后的 Activity 依然能够正常弹出。
按照正常逆向思路,我们应该查找调用处。但是我替你们试过了,如果直接查找,需要查很多层,最终还是失败。
分析到这里,很多人应该要放弃了吧?
我们不妨回到算法助手的日志页面,再次进行分析。
这时就要向大家介绍一种新方法,即——时间溯源法(名字没听过?因为是我编的)
如果要弹出谷歌服务异常窗口,那么软件必然要检测是否谷歌服务存在。这种检测机制相当于让软件与系统的谷歌服务握手,如果握手成功,则返回成功的消息;如果失败,返回失败的消息,这样窗口才能正常弹出。这条失败的消息就是我们要找的"源"
众所周知,log 是按时间来记录的,越下面的 log 越新。那我们就直接从之前定位到的弹窗记录开始,向上按时间顺序寻找:


6877ae180803c.jpg (219.14 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

很快,我们就能看到这几条 log :


6877ae32d969f.jpg (79.95 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

其实,之前试错的经历也并不是没用的。我们从之前得知的弹窗中可以猜想出," license "这个单词其实指的就是 Google Play 的一项服务。再看 log 信息,发现有三条 log 传递了" Connecting to the licensing "这个参数,结合上面翻译一下就是正在连接谷歌服务。下面的三条则是" License check fail ",即检验失败。我们推测,这个弹窗实际上是一个错误弹窗。
综上,我们可以推导出如下的大致运行流程:


6877ae4a42cc2.png (37.06 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

怎么样,是不是有种豁然开朗的感觉?
那么现在的重点就转移到抛出错误的方法,只要能够找到这个方法,并把它干掉,那么问题就全解决了。
我们可以在算法助手日志页面右上角找到搜索按钮,搜索" Log.e "(全写为 Log.error ,即错误日志),这样就可以找到所有抛出的异常。


6877af314f420.jpg (160.55 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

可以发现,结果只有四个。


6877aea20eb33.jpg (129.66 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

前三个都与找不到谷歌框架的错误无关,第四个就是解题的关键。


6877aeb88c949.jpg (211.19 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

此日志传递的参数1为" LicenseClient ",即连接服务,我们重点看参数2:
"

翻译过来大致是:
在验证谷歌服务时发生错误:com.pairip.licensecheck.LicenseCheckException:无法与谷歌服务建立连接。
我们通过MT管理器定位到这个类,并转成Java :


6877ae6f593e9.jpg (110.01 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

发现 LicenseCheckException 是一个自定义异常类,代码非常简单,用于处理错误消息。
从上述log可知,调用这个异常处理器的是 com.pairip.licensecheck.LicenseClient.connectToLicensingService 这个方法,也就是说,这就是那个抛出错误的方法。


6877aef8b0cb5.jpg (272.09 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

现在的推理的过程其实已经结束了,但为了验证我们的猜想,我们再来分析一下这个抛出错误的方法。
[Java] 纯文本查看 复制代码.method private connectToLicensingService()V
    .registers 4
    .line 100
    const-string v0, "LicenseClient"
    const-string v1, "Connecting to the licensing service..."
    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    .line 101
    new-instance v0, Landroid/content/Intent;
    const-string v1, "com.android.vending.licensing.ILicensingService"
    invoke-direct {v0, v1}, Landroid/content/Intent;->(Ljava/lang/String;)V
    const-string v2, "com.android.vending"
    .line 103
    invoke-virtual {v0, v2}, Landroid/content/Intent;->setPackage(Ljava/lang/String;)Landroid/content/Intent;
    move-result-object v0
    .line 104
    invoke-virtual {v0, v1}, Landroid/content/Intent;->setAction(Ljava/lang/String;)Landroid/content/Intent;
    move-result-object v0
    .line 107
    :try_start_18
    iget-object v1, p0, Lcom/pairip/licensecheck/LicenseClient;->context:Landroid/content/Context;
    const/4 v2, 0x1
    .line 108
    invoke-virtual {v1, v0, p0, v2}, Landroid/content/Context;->bindService(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z
    move-result v0
    :try_end_1f
    .catch Ljava/lang/SecurityException; {:try_start_18 .. :try_end_1f} :catch_2c
    if-nez v0, :cond_2b
    .line 115
    new-instance v0, Lcom/pairip/licensecheck/LicenseCheckException;
    const-string v1, "Could not bind with the licensing service."
    invoke-direct {v0, v1}, Lcom/pairip/licensecheck/LicenseCheckException;->(Ljava/lang/String;)V
    invoke-direct {p0, v0}, Lcom/pairip/licensecheck/LicenseClient;->retryOrThrow(Lcom/pairip/licensecheck/LicenseCheckException;)V
    :cond_2b
    return-void
    :catch_2c
    move-exception v0
    .line 110
    new-instance v1, Lcom/pairip/licensecheck/LicenseCheckException;
    const-string v2, "Not allowed to bind with the licensing service."
    invoke-direct {v1, v2, v0}, Lcom/pairip/licensecheck/LicenseCheckException;->(Ljava/lang/String;Ljava/lang/Throwable;)V
    invoke-direct {p0, v1}, Lcom/pairip/licensecheck/LicenseClient;->retryOrThrow(Lcom/pairip/licensecheck/LicenseCheckException;)V
    return-void
.end method
这个方法唯一目的就是向 Google PlayLicense 服务发起一次 bind,用于检测用户手机是否安装 Google Play 服务,返回值(是否 bind 成功)放在 v0 里,如果系统抛出 SecurityException 会被 :catch_2c 接住。接着,new-instance 在 Java 堆里创建并初始化了一个 LicenseCheckException 对象。SecurityException 被包装成 LicenseCheckException 后,交给 retryOrThrow 决定后续命运。
这与我们的猜想完全吻合,错误信息也是由这个方法提供的。至此,分析过程完全结束。


6877af101c1ec.jpg (262.05 KB, 下载次数: 3)
下载附件
2025-8-1 20:04 上传

所以,我们只需要清空这个方法,谷歌服务验证就不复存在了!!!
总结一下,此方法推导过程虽然历经曲折,最后的方案却十分简单,只需要清空位于 com.pairip.licensecheck.LicenseClient 类的
[color=]connectToLicensingService
方法即可(不知道哪个 sb 闲的蛋疼琢磨了一天)。理论上来说,这个方法适用于所有具有 Something went wrong 弹窗的 APP(本人已测试ChatGPT、Suno,均有效) ,以后再遇到这种情况,就可以直接照搬这个方案。
至于软件自带的弹窗,应该不是很难。这个问题就交给各位来解决,就作为一项作业吧!要求是:用算法助手进行分析,使软件在启动和运行时没有相关弹窗弹出,且功能正常(除谷歌登录等功能外,但这软件好像不支持中国手机号)。
这是完全修改后的结果:


6877b5f390b40.jpg (186.05 KB, 下载次数: 2)
下载附件
2025-8-1 20:04 上传

如果心情好,可能会再出分析思路。所有的工具和原包都放在下面了,感谢大家的耐心阅读,也欢迎各位在评论区分享自己的见解。
如果有大佬有不同意见,或发了作业的标准答案,我会择优置顶
下载链接:
htt删ps://jiguro.lan删zouw.com/igCG63删16zsdc

[color=]码字不易,点赞可有?

下载次数, 下载附件

Yo丨Se7ven   

如果你的手机root了,用blocker禁用LicenseActivity活动页面,就可以绕过谷歌验证。
china08   

厉害👍🏻👍🏻👍🏻👍🏻👍🏻👍🏻👍🏻👍🏻👍🏻👍🏻
cn2jp   

从头到尾看完了,总得来说不明觉厉!
dilla   

重点是如何从这么多日志中发现蛛丝马迹,再从中抓取出那个真正起作用的方法。有意思的!
xiaoshengzi   

请收下我的膝盖!
yjlxn   

谢谢高人分享自己的分析思路
随风呀   

感谢分享
JiGuro
OP
  


Yo丨Se7ven 发表于 2025-8-4 13:02
如果你的手机root了,用blocker禁用LicenseActivity活动页面,就可以绕过谷歌验证。

这个方法很好,但是我就是想找点事做
wycs8866   

新手受教了,感谢大神分享
您需要登录后才可以回帖 登录 | 立即注册