12345下一页
返回列表 发新帖

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

  [复制链接]

42

主题

631

回帖

6060

积分

硕士生

小佬

Rank: 6Rank: 6

金币
7002
好评
162
信誉
270

考神MT论坛新人MT论坛最佳新人

QQ
发表于 2025-7-16 22:47:45 来自手机  | 显示全部楼层 | 阅读模式  来自 江苏
本帖最后由 JiGuro 于 2025-7-20 20:08 编辑

今天在 Google Play 里发现了一款应用,名字叫 Kuku TV,还是三哥写的,可惜评分只有0.0

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


好嘛,不就是谷歌服务校验吗?今天我就要手撕它。
第一个弹出的黑色窗口是软件自带的,第二个弹出的白色窗口则是谷歌官方的。两个窗口均用于谷歌服务较验。今天主要分析的是第二个谷歌官方窗口,此窗口在多款 Google Play 软件中均有发现。
贴心地翻译一下
出错了
在打开应用程序之前,请检查您的设备上启用了Google Play,并且您使用的是最新版本。如果问题仍然存在,尝试重新安装应用程序。
关闭

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

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

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

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

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

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

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


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

我们进入 Kuku TV 的日志,可以发现获取到了非常多的 log 记录。不用急,我们先定位到 Google 官方的这条弹窗,点进去发现其调用堆栈为:
at hDuncLjwovQIoC.QX.bAQF.JungeSpoxedBridge$LegacyApiSupport.handleAfter(Unknown Source:33)
        at org.lsposed.lspd.impl.LSPosedBridge$NativeHooker.callback(Unknown Source:279)
        at LSPHooker_.show(Unknown Source:8)
        at android.app.AlertDialog$Builder.show(AlertDialog.java:1131)
        at com.pairip.licensecheck.LicenseActivity.lambda$showErrorDialog$0(LicenseActivity.java:99)
        at com.pairip.licensecheck.LicenseActivity.$r8$lambda$N5_Pzpb-eSKmOONXn3Kn0QvMbys(Unknown Source:0)
        at com.pairip.licensecheck.LicenseActivity$$ExternalSyntheticLambda1.run(D8$$SyntheticClass:0)
        at android.app.Activity.runOnUiThread(Activity.java:7297)
        at com.pairip.licensecheck.LicenseActivity.showErrorDialog(LicenseActivity.java:87)
        at com.pairip.licensecheck.LicenseActivity.onStart(LicenseActivity.java:41)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
        at android.app.Activity.performStart(Activity.java:8273)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3786)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2286)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8292)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

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

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

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

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

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

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

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

此日志传递的参数1为" LicenseClient ",即连接服务,我们重点看参数2:
Error while checking license: com.pairip.licensecheck.LicenseCheckException: Could not bind with the licensing service.
        at com.pairip.licensecheck.LicenseClient.connectToLicensingService(LicenseClient.java:115)
        at com.pairip.licensecheck.LicenseClient.$r8$lambda$tTRuJInP7s484yRu-m6AsnoI1z4(Unknown Source:0)
        at com.pairip.licensecheck.LicenseClient$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8292)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

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

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

现在的推理的过程其实已经结束了,但为了验证我们的猜想,我们再来分析一下这个抛出错误的方法。
  1. .method private connectToLicensingService()V
  2.     .registers 4

  3.     .line 100
  4.     const-string v0, "LicenseClient"

  5.     const-string v1, "Connecting to the licensing service..."

  6.     invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

  7.     .line 101
  8.     new-instance v0, Landroid/content/Intent;

  9.     const-string v1, "com.android.vending.licensing.ILicensingService"

  10.     invoke-direct {v0, v1}, Landroid/content/Intent;-><init>(Ljava/lang/String;)V

  11.     const-string v2, "com.android.vending"

  12.     .line 103
  13.     invoke-virtual {v0, v2}, Landroid/content/Intent;->setPackage(Ljava/lang/String;)Landroid/content/Intent;

  14.     move-result-object v0

  15.     .line 104
  16.     invoke-virtual {v0, v1}, Landroid/content/Intent;->setAction(Ljava/lang/String;)Landroid/content/Intent;

  17.     move-result-object v0

  18.     .line 107
  19.     :try_start_18
  20.     iget-object v1, p0, Lcom/pairip/licensecheck/LicenseClient;->context:Landroid/content/Context;

  21.     const/4 v2, 0x1

  22.     .line 108
  23.     invoke-virtual {v1, v0, p0, v2}, Landroid/content/Context;->bindService(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z

  24.     move-result v0
  25.     :try_end_1f
  26.     .catch Ljava/lang/SecurityException; {:try_start_18 .. :try_end_1f} :catch_2c

  27.     if-nez v0, :cond_2b

  28.     .line 115
  29.     new-instance v0, Lcom/pairip/licensecheck/LicenseCheckException;

  30.     const-string v1, "Could not bind with the licensing service."

  31.     invoke-direct {v0, v1}, Lcom/pairip/licensecheck/LicenseCheckException;-><init>(Ljava/lang/String;)V

  32.     invoke-direct {p0, v0}, Lcom/pairip/licensecheck/LicenseClient;->retryOrThrow(Lcom/pairip/licensecheck/LicenseCheckException;)V

  33.     :cond_2b
  34.     return-void

  35.     :catch_2c
  36.     move-exception v0

  37.     .line 110
  38.     new-instance v1, Lcom/pairip/licensecheck/LicenseCheckException;

  39.     const-string v2, "Not allowed to bind with the licensing service."

  40.     invoke-direct {v1, v2, v0}, Lcom/pairip/licensecheck/LicenseCheckException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V

  41.     invoke-direct {p0, v1}, Lcom/pairip/licensecheck/LicenseClient;->retryOrThrow(Lcom/pairip/licensecheck/LicenseCheckException;)V

  42.     return-void
  43. .end method
复制代码

这个方法唯一目的就是向 Google Play 的 License 服务发起一次 bind,用于检测用户手机是否安装 Google Play 服务,返回值(是否 bind 成功)放在 v0 里,如果系统抛出 SecurityException 会被 :catch_2c 接住。接着,new-instance 在 Java 堆里创建并初始化了一个 LicenseCheckException 对象。SecurityException 被包装成 LicenseCheckException 后,交给 retryOrThrow 决定后续命运。
这与我们的猜想完全吻合,错误信息也是由这个方法提供的。至此,分析过程完全结束。

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

如果心情好,可能会再出分析思路。所有的工具和原包都放在下面了,感谢大家的耐心阅读,也欢迎各位在评论区分享自己的见解。
如果有大佬有不同意见,或发了作业的标准答案,我会择优置顶
下载链接
—————————————————————————
码字不易,点赞可有?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
已有12人评分好评 金币 理由
jay1070 + 1
阿_柒 + 1 + 1
zhiyungezhu + 1 + 1
1604974486 + 1 + 1
一鸢蓝夏 + 1 + 1 很给力!
珂朵莉 + 1
風信子 + 1 + 1 赞一个!
安静的美男子 + 1 + 1
抽刀断水水更流 + 1 + 1
去追随彩虹海 + 1 + 1 分析的很详细
青春向上 + 1 教程很详细。
网暴真的太可怕了 + 1 不错不错

查看全部评分 总评分:好评 +11  金币 +9 

回复

使用道具 举报

35

主题

6576

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
1499
好评
6
信誉
172

考神MT论坛新人MT论坛帅哥MT论坛最佳新人

QQ
发表于 2025-7-16 22:54:29 来自手机  | 显示全部楼层  来自 河北
感谢分享,
回复

使用道具 举报

2

主题

4507

回帖

1万

积分

禁止访问

iKUN

金币
1214
好评
16
信誉
111
发表于 2025-7-16 23:10:37 来自手机  | 显示全部楼层  来自 广西
学习学习
回复

使用道具 举报

3

主题

1147

回帖

3147

积分

大学生

银杏叶

Rank: 5Rank: 5

金币
3081
好评
0
信誉
102

考神MT论坛最佳新人

QQ
发表于 2025-7-16 23:11:10 来自手机  | 显示全部楼层  来自 山西
牛逼,赞了
回复

使用道具 举报

2

主题

3668

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4723
好评
1
信誉
100
发表于 2025-7-16 23:28:10 | 显示全部楼层  来自 浙江
感谢楼主的分享
回复

使用道具 举报

9

主题

5487

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
694
好评
0
信誉
116

MT论坛新人考神MT论坛帅哥

发表于 2025-7-16 23:29:43 来自手机  | 显示全部楼层  来自 安徽
感谢分享
回复

使用道具 举报

229

主题

9646

回帖

2万

积分

博士后

Rank: 8Rank: 8

金币
2984
好评
27
信誉
91

考神MT论坛新人MT论坛活跃会员

发表于 2025-7-16 23:44:30 来自手机  | 显示全部楼层  来自 贵州
辛苦了楼主,可惜不是我要的
回复

使用道具 举报

54

主题

5860

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
2947
好评
31
信誉
100
发表于 2025-7-17 00:00:18 来自手机  | 显示全部楼层  来自 河南
学习一下
回复

使用道具 举报

19

主题

3753

回帖

9550

积分

硕士生

Rank: 6Rank: 6

金币
1514
好评
21
信誉
143
发表于 2025-7-17 00:04:31 来自手机  | 显示全部楼层  来自 广西
感谢分享
回复

使用道具 举报

17

主题

6294

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
2844
好评
6
信誉
100

考神

发表于 2025-7-17 00:10:47 来自手机  | 显示全部楼层  来自 山东
感谢分享
回复

使用道具 举报

78

主题

3271

回帖

9598

积分

硕士生

Rank: 6Rank: 6

金币
3053
好评
2
信誉
100
QQ
发表于 2025-7-17 00:26:20 来自手机  | 显示全部楼层  来自 海南
没root路过
回复

使用道具 举报

66

主题

6471

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
1743
好评
2
信誉
109

考神

发表于 2025-7-17 00:27:20 | 显示全部楼层  来自 江西
路过看看
回复

使用道具 举报

126

主题

3762

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
6574
好评
13
信誉
143

MT论坛新人MT论坛最佳新人考神MT论坛活跃会员

发表于 2025-7-17 00:46:31 来自手机  | 显示全部楼层  来自 四川
厉害,可是咱摆烂了,不想动我还是开Google服务吧
回复

使用道具 举报

24

主题

5216

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4167
好评
3
信誉
94
发表于 2025-7-17 01:08:18 来自手机  | 显示全部楼层  来自 广东
厉害了我的哥
回复

使用道具 举报

3

主题

2647

回帖

6622

积分

硕士生

旮旯

Rank: 6Rank: 6

金币
2454
好评
0
信誉
100
发表于 2025-7-17 02:09:26 来自手机  | 显示全部楼层  来自 广东
回复一下
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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