吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4149|回复: 25
收起左侧

[Android 原创] 某瓣去广告实战与签名对抗

[复制链接]
天空宫阙 发表于 2023-10-14 13:23

某瓣去广告实战与签名对抗

写在前面

这应该是我第一次成功修改app,以前主要是调用app的接口实现数据采集(网络爬虫),这次尝试给app去广告,最后成功了很开心,主要是跟正己大佬学的,在此特别感谢!

郑重申明,本文仅用于学习交流,禁止用于非法途径,另外也不提供成品软件

使用的工具

名称 描述
某瓣apk 版本7.18.1 更高版本可能有壳
雷电模拟器或安卓真机 需要root
jadx 反编译工具
np管理器或mT管理器 反编译工具 ,smali代码修改
frida hook工具,验证想法
其他工具可参考正己大佬安卓逆向课程前四课的工具,如开发助手 https://www.52pojie.cn/thread-1695141-1-1.html

去除开屏广告

使用mt管理器的activity记录功能定位到开屏广告所在类
image-20231013162828627.png

image-20231013163231532.png

定位到开屏广告的包在com.douban.frodo.activity.SplashActivity,直接去这个类找了好久也没有找到关键的方法,用frida hook了一下发现好多方法在开屏阶段没有调用,表现为方法hook住了但没有输出。

没有找到从SplashActivity跳转到其他Activity的关键方法,我卡住了,我通过阅读了大量源码定位到以下这个方法requestSplashShow这个方法,这个方法位于douban.ad.api,用frida 先hook了一下发现这个方法确实有被调用,我注意到这个方法进行了异常捕获,直接return也是没有关系的。

Snipaste_2023-10-13_10-07-25.png
先用frida hook这个方法,直接返回null试一下,发现真的就没有广告了,这个方法成功被我嚯嚯掉了【狗头】以下是frida hook的代码。

    let AdApi = Java.use("com.douban.ad.api.AdApi");
    AdApi["requestSplashShow"].implementation = function (str, z, z2, str2) {
    console.log(`AdApi.requestSplashShow is called: str=${str}, z=${z}, z2=${z2}, str2=${str2}`);
    // let result = this["requestSplashShow"](str, z, z2, str2);
    // console.log(`AdApi.requestSplashShow result=${result}`);
    // return result;
    return null;
};

使用np管理器修改对应的smali代码

image-20231014124515093.png
虽然这样修改后这个方法似乎没有编译通过(np不能转成java代码),有大佬知道的话可以指点一下,我smali学的有点差,不过最起码我把这个请求广告的函数搞坏了。
image-20231014124932896.png

修改保存后用np管理器重新签名,发现确实没有开屏广告了,但是请求非法104,这个app算是废了。
Snipaste_2023-10-13_10-13-25.png

但是我之前研究过某瓣的网络请求签名“_sig”,它的签名方式是hmachash,而hmachash的密钥(secretkey)是根据app的签名信息动态生成的,我们对app进行了签名,生成的密钥(secretkey)不对了最后发生请求的“_sig”自然也不对了。

签名对抗

因为知道是请求"_sig"的有问题,可以按以下方式进行定位。

搜索关键字"_sig"

01.png
发现_sig 是a3

02.png
com.douban.frodo.network.ApiSignatureHelper发现了以下代码,其中str4就是hmachash 密钥,如果为了构造请求的签名直接hook str4的值即可,但我们还可以继续跟。

03-16972536126415.png
我们的目的是弄清楚str4具体的生成方法

String str4 = FrodoApi.a().e.b;

上面代码的意思是它是FrodoApi这个类调用了a方法后e属性的b属性,可能有点绕,我们可以在jadx中双击e

image-20231014114706449.png

发现e是一个ZenoConfig类,再双击ZenoConfig

image-20231014114926620.png

发现ZenoConfig的b是这个方法的第三个参数,也就是我们要的e.b是这个方法的第三个参数,我们可以查找用例

image-20231014115325336.png

最终定位到这个方法

@SuppressLint({"PackageManagerGetSignatures"})
    public static void a(boolean z) {
        if (TextUtils.isEmpty(b)) {
            b = "74CwfJd4+7LYgFhXi1cx0IQC35UQqYVFycCE+EVyw1E=";
        }
        if (TextUtils.isEmpty(c)) {
            c = "bHUvfbiVZUmm2sQRKwiAcw==";
        }
        if (z) {
            try {
                String encodeToString = Base64.encodeToString(AppContext.a().getPackageManager().getPackageInfo(AppContext.a().getPackageName(), 64).signatures[0].toByteArray(), 0);
                b = AES.a(b, encodeToString);
                c = AES.a(c, encodeToString); # hmachash的密钥(secretkey)即c
            } catch (PackageManager.NameNotFoundException e2) {
                e2.printStackTrace();
            }
        }
    }

以上代码说明,hmachash的密钥(secretkey)通过aes加密了apk的签名信息生成,这就解释了重新签名后网络请求非法。

我想到破解的思路就是直接将正确的AES.a(c, encodeToString)加密结果写死;比较通用的方法还有io重定向,签名验证的时候还是让app还是读原包,运行的时候运行修改后的包,这个方法比较高端我还没学会,所以就用前者。

关于正确值怎么来?可以安装未修改的app然后用frida hook AES.a这个函数,以下是hook代码(可以直接用jadx生成的)

let AES = Java.use("com.douban.frodo.utils.crypto.AES");
AES["a"].overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) {
    console.log(`AES.a is called: str=${str}, str2=${str2}`);
    let result = this["a"](str, str2);
    console.log(`AES.a result=${result}`);
    return result;
};

hook结果

AES.a is called: str=74CwfJd4+7LYgFhXi1cx0IQC35UQqYVFycCE+EVyw1E=, str2=MIICUjCCAbsCBEty1MMwDQYJKoZIhvcNAQEEBQAwcDELMAkGA1UEBhMCemgxEDAOBgNVBAgTB0Jl
aWppbmcxEDAOBgNVBAcTB0JlaWppbmcxEzARBgNVBAoTCkRvdWJhbiBJbmMxFDASBgNVBAsTC0Rv
dWJhbiBJbmMuMRIwEAYDVQQDEwlCZWFyIFR1bmcwHhcNMTAwMjEwMTU0NjExWhcNMzcwNjI3MTU0
NjExWjBwMQswCQYDVQQGEwJ6aDEQMA4GA1UECBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzET
MBEGA1UEChMKRG91YmFuIEluYzEUMBIGA1UECxMLRG91YmFuIEluYy4xEjAQBgNVBAMTCUJlYXIg
VHVuZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAg622fxLuwQtC8KLYp5gHk0OmfrFiIisz
kzPLBhKPZDHjYS1URhQpzf00T8qg2oEwJPPELjN2Q7YOoax8UINXLhMgFQkyAvMfjdEOSfoKH93p
v2d4n/IjQc/TaDKu6yb53DOq76HTUYLcfLKOXaGwGjAp3QqTqP9LnjJjGZCdSvMCAwEAATANBgkq
hkiG9w0BAQQFAAOBgQA3MovcB3Hv4bai7OYHU+gZcGQ/8sOLAXGD/roWPX3gm9tyERpGztveH35p
aI3BrUWg2Vir0DRjbR48b2HxQidQTVIH/HOJHV0jgYNDviD18/cBwKuLiBvdzc2Fte+zT0nnHXMy
E6tVeW3UdHC1UvzyB7Qcxiu4sBiEO1koToQTWw==

AES.a result=0dad551ec0f84ed02907ff5c42e8ec70
AES.a is called: str=bHUvfbiVZUmm2sQRKwiAcw==, str2=MIICUjCCAbsCBEty1MMwDQYJKoZIhvcNAQEEBQAwcDELMAkGA1UEBhMCemgxEDAOBgNVBAgTB0Jl
aWppbmcxEDAOBgNVBAcTB0JlaWppbmcxEzARBgNVBAoTCkRvdWJhbiBJbmMxFDASBgNVBAsTC0Rv
dWJhbiBJbmMuMRIwEAYDVQQDEwlCZWFyIFR1bmcwHhcNMTAwMjEwMTU0NjExWhcNMzcwNjI3MTU0
NjExWjBwMQswCQYDVQQGEwJ6aDEQMA4GA1UECBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzET
MBEGA1UEChMKRG91YmFuIEluYzEUMBIGA1UECxMLRG91YmFuIEluYy4xEjAQBgNVBAMTCUJlYXIg
VHVuZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAg622fxLuwQtC8KLYp5gHk0OmfrFiIisz
kzPLBhKPZDHjYS1URhQpzf00T8qg2oEwJPPELjN2Q7YOoax8UINXLhMgFQkyAvMfjdEOSfoKH93p
v2d4n/IjQc/TaDKu6yb53DOq76HTUYLcfLKOXaGwGjAp3QqTqP9LnjJjGZCdSvMCAwEAATANBgkq
hkiG9w0BAQQFAAOBgQA3MovcB3Hv4bai7OYHU+gZcGQ/8sOLAXGD/roWPX3gm9tyERpGztveH35p
aI3BrUWg2Vir0DRjbR48b2HxQidQTVIH/HOJHV0jgYNDviD18/cBwKuLiBvdzc2Fte+zT0nnHXMy
E6tVeW3UdHC1UvzyB7Qcxiu4sBiEO1koToQTWw==

AES.a result=bf7dddc7c9cfe6f7

因此只要对代码进行如下修改就行

//修改前
b = AES.a(b, encodeToString);
c = AES.a(c, encodeToString);
//修改后
b = "0dad551ec0f84ed02907ff5c42e8ec70"
c = "bf7dddc7c9cfe6f7"

但是java代码是不能直接改的,需要改smali代码

image-20231014122928111.png

保存后重新安装app,发现正常请求到数据,并且也没有开屏广告。

去除推荐页的广告

Snipaste_2023-10-13_15-54-54.png
最终定位到onADLoaded,先用frida hook验证一下

let GdtFetcher = Java.use("com.douban.frodo.baseproject.ad.GdtFetcher");
GdtFetcher["onADLoaded"].implementation = function (list) {
    console.log(`GdtFetcher.onADLoaded is called: list=${list}`);
    this["onADLoaded"](null);// 发现传入null后就没有推荐页的广告了,此处应该直接什么都不做也可以
 };

同样的用np管理器搞坏这个方法就好了,我的话是直接在方法最前面return了,似乎这个方法也没有编译通过,不过应该这个获取广告的函数坏了,所以也就没有广告了。

免费评分

参与人数 9威望 +1 吾爱币 +33 热心值 +9 收起 理由
junjia215 + 1 + 1 用心讨论,共获提升!
杨辣子 + 1 + 1 谢谢@Thanks!
Yangzaipython + 1 + 1 用心讨论,共获提升!
qiaoyong + 1 + 1 热心回复!
5omggx + 1 + 1 用心讨论,共获提升!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
mrjackliu + 1 + 1 打码打了个寂寞,哈哈
BonnieRan + 1 + 1 谢谢@Thanks!
GenW + 6 + 1 用心讨论,共获提升!

查看全部评分

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

正己 发表于 2023-10-14 18:07
在非static函数中,p0代指“this",p1表示函数的第一个参数
在你这个方法里的第一个参数是个字符串类型,你给他赋值个0,那编译器肯定要坏掉
感谢支持,对你们有所帮助便是对本教程最大的回馈
如果爱忘了 发表于 2023-10-14 18:45
正己 发表于 2023-10-16 15:55
天空宫阙 发表于 2023-10-16 09:35
所以底下有大佬提示的const-string p1,"",然后返回就好了是吧。感谢大佬回复,我smali还得学习。

也不对,你这样返回值就变字符串类型,他是自定义的一个类型
应该是
const v0,0x0
return v0

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
天空宫阙 + 2 + 1 好的,谢谢@Thanks!

查看全部评分

tian0505 发表于 2023-10-14 14:11
没看懂但是照做可以用,哈哈

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
天空宫阙 + 2 + 1 确实这样,大部分都在分析,实际的修改很少

查看全部评分

月清晖 发表于 2023-10-14 15:02
最重要的就是定位签名信息生成_sig,这个过了就随便改了
BonnieRan 发表于 2023-10-14 15:42
用软件签名生成请求数据的参数,又学到了一点
侃遍天下无二人 发表于 2023-10-14 15:51
去开屏广告那个只要用MT管理器清空你找的方法就行了,具体是点击指南针,找到对应的方法长按,从菜单中点清空方法,它就会变成直接return null了,很好用。
去签名从你的操作上看似乎用MT的普通版去签就能搞定,早期版本的原理是hook,现在改成了通过反射修改字符串,效率更高了
 楼主| 天空宫阙 发表于 2023-10-14 16:12
侃遍天下无二人 发表于 2023-10-14 15:51
去开屏广告那个只要用MT管理器清空你找的方法就行了,具体是点击指南针,找到对应的方法长按,从菜单中点清 ...

学到了,高级的工具让逆向变简单了
 楼主| 天空宫阙 发表于 2023-10-14 16:14
BonnieRan 发表于 2023-10-14 15:42
用软件签名生成请求数据的参数,又学到了一点

是的,这也算是一种防护
lhlking 发表于 2023-10-14 18:24
学习一下,恨不得把手机上的app挨个搞一遍。启动广告太烦人
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-24 12:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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