去4某99盒子强制升级
Java层hook初体验(五)app:4e 44 4d 35 4f 65 61 34 75 4f 61 49 6a 2b 65 62 6b 75 57 74 6b 41 3d 3d(base64+hex)
android:versionCode="1664" android:versionName="6.2.5.30"
工具:httpCanary,frida14.2.18,webstorm 2021.1,jadx
设备:root的pixel
本文所有的注入均是spawn方式注入的。
打开app是这样的(第一次打开是没有叉号的,是一个强制升级的弹窗,点击升级,我立马退出之后,再进来就成这样了)
点击叉掉,直接闪退,还不是强制升级,背景一看敬请期待,看来不能光hook弹窗,要返回相应的值了。
怎么办呢?
jadx反编译一下,搜一下更新,发现很多,但没有零流量更新。
那就抓个包看看吧,很不错的是发现
除了738,不知道是什么之外,但估计也是个版本号,其余的不就是我们的versionCode versionName吗
一开始我还没注意到versionName,就直接开始搜versionCode,找到和包名相关的,多次尝试之后(发现调用了此函数)。
let GameCenterConfig = Java.use("com.m4399.gamecenter.GameCenterConfig");
GameCenterConfig["getVersionCode"].implementation = function () {
// console.log('getVersionCode is called');
return this.getVersionCode();
};
会发现确实走了该函数,且其返回值是1664,改成多少呢。查找该函数的用例。
追进去,看到一个lastversioncode,非常符合我的想象,直接hook
let AppUpgradeProvider = Java.use("com.upgrade.provider.AppUpgradeProvider");
AppUpgradeProvider["getLastVersionCode"].implementation = function () {
console.log('getLastVersionCode is called');
let ret = this.getLastVersionCode();
console.log('getLastVersionCode ret value is ' + ret);
return ret;
};
那么1664的修改值也得到了。
此时弹窗已经消失,但没法使用,此外登录,福利等界面都是空的
还得继续,那么738从哪来呢,继续搜versioncode
虽然都是versioncode,但很明显感觉这个和之前取的不是一个值,通过hook发现确实经过这里。
let PluginPackage = Java.use("com.m4399.plugin.PluginPackage");
PluginPackage.getVersionCode.implementation = function () {
// console.log('getVersionCode is called');
console.log(this.getVersionCode());
return this.getVersionCode();
};
那最新的versioncode怎么找呢?也试一下last?发现没找到,最后我尝试了,安装最新版,但最新版明显有检测,frida注入几秒,就被结束进程,使用了葫芦娃改过的frida也无效,,抓包虽然可以抓,但内容明显被混淆过了,但很巧的是刚好可以执行之前获取versioncode的代码
let PluginPackage = Java.use("com.m4399.plugin.PluginPackage");
PluginPackage.getVersionCode.implementation = function () {
// console.log('getVersionCode is called');
console.log(this.getVersionCode());
return this.getVersionCode();
};
返回的值是914.那么return 914
卸掉新app,装回之前老的,这次总该拿下了吧
let PluginPackage = Java.use("com.m4399.plugin.PluginPackage");
PluginPackage.getVersionCode.implementation = function () {
// console.log('getVersionCode is called');
// console.log("PluginPackage.getVersionCode:",this.getVersionCode());
return 914;
};
let GameCenterConfig = Java.use("com.m4399.gamecenter.GameCenterConfig");
GameCenterConfig["getVersionCode"].implementation = function () {
// console.log('getVersionCode is called');
// console.log(this.getVersionCode());
return 2033;
};
一进去,果然没弹窗,也不再是敬请期待,还可以下载游戏,
就在高兴的时候,点到我,还是报错。当时忘了还有version name,就思考了很久是不是还有别的检测点。
这一看是toast ,尝试hook,发现确实是,但没打印出太多有效的
var toast=Java.use("android.widget.Toast");
toast.show.implementation=function (){
showStack();
console.log("toast.show:");
return this.show;
}
一筹莫展的时候,突然想到app进入时候弹出来的7.9.1.16,就想到了包名。那根据老套路就是getVersionName,一搜还真是
GameCenterConfig["getVersionName"].implementation = function () {
console.log('getVersionName is called');
return this.getVersionName();
};
输出就是6.2.5.30
改成“7.9.1.16”,想着这下应该结束了。然而事情并没有结束。
还是没内容,有点费解了,再抓一次包
这里面不都改了吗继续往下看,还是没改全
还有什么地方取值呢,抱着猜测的想法就搜索getvalue,检索和包名相关最好还带有http类似字眼。
果不其然定位到很符合想象的函数
看来参数的传递直接是return的,更为不错的是,其他参数都可以写死。
那就直接替换返回。
let GameCenterHttpAgent = Java.use("com.m4399.gamecenter.GameCenterHttpAgent");
GameCenterHttpAgent["getHttpRequestAgent"].implementation = function () {
// console.log('getHttpRequestAgent is called');
let ret = this.getHttpRequestAgent();
if(ret==="4399GameCenter/6.2.5.30(android;Pixel;10;1080x1794;WIFI;1664.914;upgrade)")
{
return "4399GameCenter/7.9.1.16(android;Pixel;10;1080x1794;WIFI;2033.914;upgrade)";
}
// console.log('getHttpRequestAgent ret value is ' + ret);
return ret;
};
再来尝试一下。
终于没问题了,刷新也可以返回正常的界面了。
至此,去强制升级结束。
Java.perform(function (){
function showStack() {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
}
var str=Java.use("java.lang.String");
str.getBytes .overload().implementation=function () {
var result = this.getBytes();
var strnew = str.$new(result);
if (strnew.equals("6.2.5.30"||"738")) {
// showStack();
// console.log("getBytes.overload():",strnew);
}
return result;
}
str.getBytes.overload('java.lang.String').implementation=function (a){
var result =this.getBytes(a);
var strnew=str.$new(result,a);
if (strnew.equals("6.2.5.30"||"738")) {
// console.log("getBytes.overload('java.lang.String'):",strnew);
}
return result;
}
let PluginPackage = Java.use("com.m4399.plugin.PluginPackage");
PluginPackage.getVersionCode.implementation = function () {
// console.log('getVersionCode is called');
// console.log("PluginPackage.getVersionCode:",this.getVersionCode());
return 914;
};
let GameCenterConfig = Java.use("com.m4399.gamecenter.GameCenterConfig");
GameCenterConfig["getVersionCode"].implementation = function () {
// console.log('getVersionCode is called');
// console.log(this.getVersionCode());
return 2033;
};
GameCenterConfig["getVersionName"].implementation = function () {
console.log('getVersionName is called');
return "7.9.1.16";
};
let GameCenterHttpAgent = Java.use("com.m4399.gamecenter.GameCenterHttpAgent");
GameCenterHttpAgent["getHttpRequestAgent"].implementation = function () {
// console.log('getHttpRequestAgent is called');
let ret = this.getHttpRequestAgent();
if(ret==="4399GameCenter/6.2.5.30(android;Pixel;10;1080x1794;WIFI;1664.914;upgrade)"){
return "4399GameCenter/7.9.1.16(android;Pixel;10;1080x1794;WIFI;2033.914;upgrade)";
}
// console.log('getHttpRequestAgent ret value is ' + ret);
return ret;
};
// let AppUpgradeProvider = Java.use("com.upgrade.provider.AppUpgradeProvider");
// AppUpgradeProvider["getLastVersionCode"].implementation = function () {
// console.log('getLastVersionCode is called');
// let ret = this.getLastVersionCode();
// console.log('getLastVersionCode ret value is ' + ret);
// return ret;//2033
// };
// var toast=Java.use("android.widget.Toast");
// toast.show.implementation=function (){
// showStack();
// console.log("toast.show:");
// return this.show;
// }
})
总结:
感觉这次java层的hook有些神奇,打印的堆栈有些不准确,更多的靠的猜测有运气和一定的经验,有些地方还是没有hook全的,但正常使用没问题 frida的代码用代码块包裹一下会比较美观 童年回忆啊 感谢分享,奇怪的知识增加了 楼主继续加油 膜拜,期待成功 继续加油!!! 看不懂,但是感觉很牛X!! 看不懂,但是感觉很牛X!! 感谢分享 真不错,支持一下