大神论坛

找回密码
快速注册
查看: 454 | 回复: 0

[原创] 豆瓣app去广告实战与签名对抗

主题

帖子

0

积分

初入江湖

UID
583
积分
0
精华
威望
0 点
违规
大神币
68 枚
注册时间
2023-09-16 15:07
发表于 2023-10-14 16:46
本帖最后由 wa_j 于 2023-10-14 16:46 编辑

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

写在前面

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

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

使用的工具

去除开屏广告

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

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

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

先用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代码

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

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

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

签名对抗

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

搜索关键字"_sig"

发现_sig 是a3

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

我们的目的是弄清楚str4具体的生成方法

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

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

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

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

最终定位到这个方法

@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代码

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

去除推荐页的广告

最终定位到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了,似乎这个方法也没有编译通过,不过应该这个获取广告的函数坏了,所以也就没有广告了。


注:若转载请注明大神论坛来源(本贴地址)与作者信息。

返回顶部