案例 车智赢 的hook 脚本 和 总体的java逻辑的提取
- iOS破解
- 2025-02-04
- 37热度
- 0评论
hook的逻辑还原
function UpgradeBlock() {
var UpgradeAppBean = Java.use('com.che168.autotradercloud.upgradeapp.UpgradeAppBean');
// Hook isForce 方法
UpgradeAppBean.isForce.implementation = function () {
send('Hooked isForce');
return false; // 强制返回 false,跳过强制更新
};
// Hook isLatestVersion 方法
UpgradeAppBean.isLatestVersion.implementation = function () {
send('Hooked isLatestVersion');
return true; // 强制返回 true,表示已是最新版本
};
}
// 把最开始的 登陆的参数的 给 hook 出来.
// 通过 tradercloud/sealed/login/login.ashx 这个 URI 搜索的到内部的加密算法的位置 loginByPassword() 函数中的 最后的一个参数加密的位置. encodeMD5()
// 密码 被 MD5 重新加密了.
function encodeMD5() {
var SecurityUtil = Java.use("com.autohome.ahkit.utils.SecurityUtil");
SecurityUtil.encodeMD5.implementation = function ( str ){
console.log("md5 加密, 明文---> ", str);
var result = this.encodeMD5(str);
console.log(" md5 加密后的秘闻 是---> ",result);
return result;
};
}
// 把最开始的 登陆的参数的 给 hook 出来.
// 通过 tradercloud/sealed/login/login.ashx 这个 URI 搜索的到内部的加密算法的位置 loginByPassword()
function loginByPassword_bakcup() {
let UserModel = Java.use("com.che168.autotradercloud.user.model.UserModel");
UserModel["loginByPassword"].implementation = function (str, str2, str3, responseCallback) {
console.log("tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过 \n");
console.log(`UserModel.loginByPassword is called: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`);
// 调用原方法并捕获返回值
var result = this.loginByPassword(str, str2, str3, responseCallback);
// 执行后打印结果
console.log(`After Execution: result=${result}`);
// 返回原方法的结果
return result;
};
}
function loginByPassword() {
let UserModel = Java.use("com.che168.autotradercloud.user.model.UserModel");
// 指定的手机号和密码
const TARGET_PHONE = "13541722917";
const TARGET_PASSWORD = "aA.11111";
// Hook the loginByPassword method
UserModel.loginByPassword.implementation = function (str, str2, str3, responseCallback) {
console.log("tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过 \n");
// 打印原始参数
console.log(`原始参数: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`);
// 强制替换为指定的手机号和密码
str2 = TARGET_PHONE;
str3 = TARGET_PASSWORD;
console.log(`强制替换后的参数: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`);
// 调用原方法并捕获返回值
var result = this.loginByPassword(str, str2, str3, responseCallback);
// 打印原方法的返回值
console.log(`After Execution: result=${result}`);
// 返回原方法的结果
return result;
};
console.log("[*] UserModel.loginByPassword hook installed successfully!");
// let UserModel = Java.use("com.che168.autotradercloud.user.model.UserModel");
// // 指定的手机号和密码
// const TARGET_PHONE = "13541722917";
// const TARGET_PASSWORD = "aA.11111";
// // Hook the loginByPassword method
// UserModel.loginByPassword.implementation = function (str, str2, str3, responseCallback) {
// console.log("tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过 \n");
// console.log(`UserModel.loginByPassword is called: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`);
//
// // 检查是否匹配指定的手机号和密码
// if (str2 === TARGET_PHONE && str3 === TARGET_PASSWORD) {
// console.log(`匹配到指定的手机号和密码: Phone=${str2}, Password=${str3}`);
// console.log("直接返回指定的手机号和密码,不调用原始方法。");
// // 直接返回指定的手机号和密码
// return { phone: str2, password: str3 };
// }
//
// // 如果不匹配,调用原始方法并捕获返回值
// console.log("未匹配到指定的手机号和密码,继续调用原始方法。");
// var result = this.loginByPassword(str, str2, str3, responseCallback);
//
// // 打印原方法的返回值
// console.log(`After Execution: result=${result}`);
//
// // 返回原方法的结果
// return result;
// };
// console.log("[*] UserModel.loginByPassword hook installed successfully!");
}
// UserModel.loginByPassword is called: str=com.che168.autotradercloud.user.LoginActivity@4ea788e, str2=18881121153, str3=123123, responseCallback=[object Object]
// 这里的 3 个重载[函数名相同--> 参数个数不同,或者类型不同.] 的方法并没有生效 , 也就是说, 我们并没有找对地方.
// toSigndMap(context, treemap)
// toSigndMap(context, treeMap , str)
// toSignedMap(context, z, treeMap)
// 不是这个地方,位置找错了 <--------------- [目标检测 _Sign 参数的位置]
function toSign() {
var AHAPIHelper = Java.use("com.autohome.ahkit.AHAPIHelper");
// 在这里, 没有hook 到数据, 证明位置有问题. 重新换个地方,就可以找到了.
AHAPIHelper.toSign.implementation = function (context, map){
console.log("请求来了-->")
console.log("map的值是: ", map);
// 执行原来的方法
var res = this.toSign(context, map);
console.log("sign的签名 ---> ", res);
return res;
};
}
function treemap_display() {
Java.perform(function () {
var TreeMap = Java.use('java.util.TreeMap');
// Hook the put method of TreeMap
TreeMap.put.implementation = function (key, value) {
// Check if the key is "data"
if (key == "data") {
console.log("[*] TreeMap.put called with key: " + key + ", value: " + value);
}
// Call the original put method
var result = this.put(key, value);
// Return the result of the original put method
return result;
};
console.log("[*] TreeMap.put hook installed successfully!");
});
}
// Call the function to install the hook
treemap_display();
function encode3des() {
var SecurityUtil = Java.use("com.autohome.ahkit.utils.SecurityUtil");
SecurityUtil.encode3Des.implementation = function(ctx,arg){
console.log("拼接后的第二个参数:", arg);
var res = this.encode3Des(ctx,arg); // 别人如果有返回值,那么就直接 先 res 接收一下,再 return 一下。
// 如果原函数没有 返回值,直接 this.encode3Des(ctx,arg) 当前的这个函数继续让她执行走!
console.log("UDID的值:",res);
return res;
// return "305eb636305eb636305eb636305eb636305eb636305eb636";
};
}
function SignManager_signByType() {
Java.perform(function () {
var SignManager = Java.use("com.che168.atclibrary.base.SignManager");
// Hook the signByType method
SignManager.signByType.implementation = function (i, TreeMap) {
console.log("执行了, 参数 i = ", i);
console.log("执行了, 参数 Treemap = ", TreeMap.toString());
// 执行原来的方法并获取返回值
var res = this.signByType(i, TreeMap);
console.log("执行了, 返回值:", res);
// 返回原始方法的返回值
return res;
};
console.log("[*] SignManager.signByType hook installed successfully!");
});
}
// 寻找的路径---> 通过上一个, toSign---> 三个重载的函数, 并没有任何的输出,所以位置不对.
// --> 再次搜索到,, -----> public static final String KEY_SIGN = "_sign";
// --------------------> 右键查找 KEY_SIGN 的被引用的位置.
// --------------------->找到了, lambda$initRequestCommonParams$0 , 包含了 channelid,AppVersion,Udid,UserKey,以及包含的 sign的引用, 所以,推测是这里,hook 一下就明白是否是这里.
// ---------------------> 最后的, treeMap.put(KEY_SIGN, SignManager.INSTANCE.signByType(i, treeMap) ); --------> 这里面的算法,就是 登陆加密协议的重要实现都在这里.这是一个
function appid_channelid_UDID_userkey_KEY_SIGN() {
var LaunchModel = Java.use("com.che168.autotradercloud.launch.model.LaunchModel");
// hook 替换的位置
LaunchModel.lambda$initRequestCommonParams$0.implementation = function (i, treeMap) {
console.log(`lambda$initRequestCommonParam执行了参数---前面,参数 i=${i}, \n treeMap= ${treeMap.toString}`);
// 执行原来的方法
var res = this.lambda$initRequestCommonParams$0(i, treeMap);
console.log("lambda$initRequestCommonParam执行后, ---> 后面----> 返回值是---->\n", res.toString());
return res;
};
}
// 个推相关
// 登录时,未携带或携带固定的20期是否可以正常运行
// 注册设备,返回格式错误。
function push_deviceID_RegDevice() {
var PushModel = Java.use("com.che168.autotradercloud.base.push.model.PushModel");
PushModel.regDevice.implementation = function(str){
console.log(" 个推相关--push_deviceID_RegDevice ---> 注册前 " + str);
var result = this.regDevice(str);
console.log("个推相关--push_deviceID_RegDevice--注册结果 " + result);
return result
// this.regDevice("");
};
}
function Des3_Serious_liucheng() {
// Hook the getDesKey method in AHAPIHelper
var AHAPIHelper = Java.use('com.autohome.ahkit.AHAPIHelper');
AHAPIHelper.getDesKey.implementation = function (context) {
console.log("[*] AHAPIHelper.getDesKey called with context: " + context);
var result = this.getDesKey(context);
console.log("[*] AHAPIHelper.getDesKey returned: " + result);
return result;
};
// Hook the getSignKey method in AHAPIHelper
AHAPIHelper.getSignKey.implementation = function (context) {
console.log("[*] AHAPIHelper.getSignKey called with context: " + context);
var result = this.getSignKey(context);
console.log("[*] AHAPIHelper.getSignKey returned: " + result);
return result;
};
// Hook the getSignDesKey method in AHAPIHelper
AHAPIHelper.getSignDesKey.implementation = function (context) {
console.log("[*] AHAPIHelper.getSignDesKey called with context: " + context);
this.getSignDesKey(context);
};
// Hook the get3desKey method in CheckSignUtil
var CheckSignUtil = Java.use('com.autohome.ahkit.jni.CheckSignUtil');
CheckSignUtil.get3desKey.implementation = function (context) {
console.log("[*] CheckSignUtil.get3desKey called with context: " + context);
var result = this.get3desKey(context);
console.log("[*] CheckSignUtil.get3desKey returned: " + result);
return result;
};
}
// 寻找的路径---> 通过上一个, toSign---> 三个重载的函数, 并没有任何的输出,所以位置不对.
// --> 再次搜索到,, -----> public static final String KEY_SIGN = "_sign";
// --------------------> 右键查找 KEY_SIGN 的被引用的位置.
// --------------------->找到了, lambda$initRequestCommonParams$0 , 包含了 channelid,AppVersion,Udid,UserKey,以及包含的 sign的引用, 所以,推测是这里,hook 一下就明白是否是这里.
// ---------------------> 最后的, treeMap.put(KEY_SIGN, SignManager.INSTANCE.signByType(i, treeMap) ); --------> 这里面的算法,就是 登陆加密协议的重要实现都在这里.
// hook _sign算法
// 终于找到这个位置了,接下来就可以围绕此处的代码来进行逆向 `udid` 和 `_sign` 的算法了。
// 重要的结构, _sign ----->等于 ---> KEY_SIGN,----> 依赖, SignManager.INSTANCE.signByType(i, treeMap)---> 所以包含 TreeMap, 其中包括了 UDID.
// 通过 这个函数 lambda$initRequestCommonParams$0 中的一句话, treeMap.put(UDID, AppUtils.getUDID(ContextProvider.getContext()))
// ----> 通过摸瓜找到了, getUDID 这个函数, ----> 函数内部调用了 3DES 函数 . 那么通过守株待兔, hook ,就能够等到 参数加密前,和参数 加密后.
function encode3Des() {
var SecurityUtil = Java.use("com.autohome.ahkit.utils.SecurityUtil");
SecurityUtil.encode3Des.implementation = function(ctx,str){
console.log("3DES 的输入数据---> ", str);
var res = this.encode3Des(ctx,str);
console.log("3DES 的加密结果---> ", res);
return res;
};
}
// public static String getUDID(Context context) {
// return SecurityUtil.encode3Des(
// context, getIMEI(context) + // ------> getIMEI ---> 本地读取 xml 文件. 最后, 根据 uuid 生成.-->getIMEIByAndroidIDandUUID ( context )--> UUID.randomUUID().toString()
// HiAnalyticsConstant.REPORT_VAL_SEPARATOR +
// System.nanoTime() +
// HiAnalyticsConstant.REPORT_VAL_SEPARATOR +
// SPUtils.getDeviceId() // -----> getDeviceID() --> saveDeviceId()---> 他们是成对的出现.-
// ----> 发现只去XML文件中读取,那么一定会有人先在XML中设置,此处才能读取到。
// ); -----> 接下来,可以去hook `saveDeviceId`,这个大概率是设置的位置,通过hook此方法可以看到【值 + 调用栈 】。
// }
function SPUtils_saveDeviceId_getDeviceId_saveIMEI_getIMEI() { // 设备注册 --->相关联的.
// 包.类
var SPUtils = Java.use("com.che168.autotradercloud.util.SPUtils");
// Hook,替换
SPUtils.saveDeviceId.implementation = function (str) {
console.log("来设置device_id了___SPUtils_saveDeviceId(), 参数 str ----> \n", str);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); // 打印调用栈
// 执行原来的方法
var res = this.saveDeviceId(str);
console.log("SPUtils_saveDeviceId()最后输出的结果是 ----->\n", res);
return res;
};
// 获取设备 ID --- 设别注册
SPUtils.getDeviceId.implementation = function(){
var res = this.getDeviceId();
console.log("获取 DeviceId ",res);
return res;
};
// getIMEI
SPUtils.getIMEI.implementation = function(){
var res = this.getIMEI();
console.log(" 获取IMEI ",res);
return res;
};
// saveIMEI
SPUtils.saveIMEI.implementation = function (str) {
console.log("来设置 SPUtils_saveIMEI(), 参数 str ----> \n", str);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); // 打印调用栈
// 执行原来的方法
var res = this.saveIMEI(str);
console.log("SPUtils_saveIMEI()最后输出的结果是 ----->\n", res);
return res;
};
}
// 又回到了, 最开始请求的地方. Loginbypassword 的地方.
// 这里, 回到了 encode3DES 的加密函数. 找到 python 版本的 3DES 的 代码, 直接整合,就可以拿到他的逻辑.
function SharedPreferencesUtil_saveString_getString() {
var SharedPreferencesUtil = Java.use("com.che168.atclibrary.utils.SharedPreferencesUtil")
SharedPreferencesUtil.saveString.implementation = function (str, str2) {
console.log(" SharedPreferencesUtil.saveString ----> ", str, str2);
var res = this.saveString(str, str2);
console.log("SharedPreferencesUtil.saveString 最后输出的结果是 ----->\n", res);
return res;
}
SharedPreferencesUtil.getString.implementation = function (str, str2) {
console.log(" SharedPreferencesUtil.getString ----> ", str, str2);
var res = this.getString(str, str2);
console.log("SharedPreferencesUtil.getString 最后输出的结果是 ----->\n", res);
return res;
}
}
function getUDID_getIMEI() {
var AppUtils = Java.use("com.che168.autotradercloud.util.AppUtils");
AppUtils.getUDID.implementation = function (context) {
console.log("SecurityUtil.encode3Des(context, getIMEI(context) + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + System.nanoTime() + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + SPUtils.getDeviceId());\n");
console.log(" Hook,替换 getUDID 的值 ---> ", context);
var res = this.getUDID(context);
console.log(" 返回 getUDID 的值是: --->", res );
// return "305eb636-eb15-4e24-a29d-9fd60fbc91bf"; // 通过替换测试是否可以 通过 ???
return res;
}
AppUtils.getIMEI.implementation = function (context) {
console.log("getIMEI 输入值是一下---> ", context);
var res = this.getIMEI(context);
console.log("getIMEI 最后的值是 --->", res );
return res;
}
}
Java.perform(function () {
UpgradeBlock(); // 屏蔽升级
// md5加密 +
encodeMD5();
// tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过
loginByPassword();
// 处理请求体中的 _Sign 的来源 ==处理 3个重载的 函数调用的 Sign是否会走这里 ?
// toSign(); // 最后不走这里
// Des3_Serious_liucheng();
// SignManager_signByType();
// SharedPreferencesUtil_saveString_getString();
// SPUtils_saveDeviceId_getDeviceId_saveIMEI_getIMEI(); // 设备指纹注册 和 保存 XML 验证
//
// push_deviceID_RegDevice(); // 设备注册 ==> 没有输出!!!
// // getUDID = encode3Des(getIMEI() + | + NanoTime() + | + getDeviceId())
// encode3Des();// getUDID 通过这里加密
// getUDID_getIMEI();
//
//
// _sign 算法=============
// 分析 KEY_SIGN 是怎样来的
// appid + channelid + UDID + userkey =>最后 通过 signByType 加密
// com.che168.autotradercloud.launch.model.LaunchModel.lambda$initRequestCommonParams$0 treeMap把他们全部都集中起来了.
// appid_channelid_UDID_userkey_KEY_SIGN();
//
// treemap_display();
});
//7bt6kLQ la3esZnRfXjvfVT0LtaJh+pXd48wnstT7whH0lhA+I39s5/wJIBJz oRTMVwV67Xe6DCTnZPr7wEn0cg==
//udi la3esZnRfXjvfVT0LtaJh+pXd48wnstT7whH0lhA+I39s5/wJIBJz oRTMVwV67Xe6DCTnZPr7wEn0cg==
// adb -s e265872e install '/home/calleng/桌面/xinyue/season5/车智赢-v2.8.2.apk'
// adb -s e265872e shell pm clear com.che168.autotradercloud
// adb -s e265872e push '/home/calleng/桌面/old_Desktop/新月部分/逆向课程笔记和代码(第10期)/day03-抓包和f编译/软件roid-arm64/frida-server-16.1.3-android-arm64' /data/local/tmp/
// /home/calleng/桌面/old_Desktop/新月部分/逆向课程笔记和代码(第11期)/day13-JNI开发/软件/ida相关/jni-include/include/
// 以上是JNI 的 头文件 .
// frida -D 10.10.10.200:5555 -f com.che168.autotradercloud -l '/home/calleng/p9/Mikrom2.0/JS_editor/com_che168_autotradercloud_hook_获取设备注册.js'
// frida -D 9B011FFAZ00E06 -f com.che168.autotradercloud -l /home/calleng/p9/Mikrom2.0/JS_editor/com_che168_autotradercloud_hook_获取设备注册.js
// frida -D 9B011FFAZ00E06 -F -l /home/calleng/p9/Mikrom2.0/JS_editor/com_che168_autotradercloud_hook_获取设备注册.js
// frida -D e265872e -F -l JS_editor/com_che168_autotradercloud_hook_md5.js
// frida -D 9B011FFAZ00E06 -f com.che168.autotradercloud -l '/home/calleng/p9/Mikrom2.0/JS_editor/com_che168_autotradercloud_hook_md5.js'
// Spawned `com.che168.autotradercloud`. Resuming main thread!
// [AOSP on flame::com.che168.autotradercloud]-> [*] TreeMap.put hook installed successfully!
// [*] SignManager.signByType hook installed successfully!
// 执行了, 参数 i = 0
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, udid=fU1LJm2NAdHKtoF5qqPjlKsTKP6y9oPl}
// 执行了, 返回值: BA2991D1F6629B83AFA96C8D37869C86
// 执行了, 参数 i = 0
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, specid=58532,101784,1014248, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVrtnAb1r1x7MDKCgxC nTPm1f/0ybB+2Pk=}
// 执行了, 返回值: E2D512057453EE3EBFBB99DF4D23DB4F
// 执行了, 参数 i = 0
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVrtnAb1r1x7FAuLVsp zkoX+taZbYwxKtE=}
// 执行了, 返回值: D15611755A3C1048974735CD963F38E8
// 执行了, 参数 i = 0
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, type=1, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVrtnAb1r1x7N8yMvBU y3p2qgQ2e2LMfsY=}
// 执行了, 返回值: 3248B54CDB51B3C45A671172EBBEBDAB
// 执行了, 参数 i = 0
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, buildcode=243, channelid=csy, plaform=atc.android.csy, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVrtnAb1r1x7K0Uoa/T UIPgWP7IjOAWO4c=, version=2.8.2}
// 执行了, 返回值: C3C3536BB1A539FA9F38AD364C938E16
// message: {'type': 'send', 'payload': 'Hooked isForce'} data: None
// 执行了, 参数 i = 1
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, pwd=4297f44b13955235245b2497399d7a93, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVyWcnmYdRgeUF8JohF uFK6iXPgdZJbKw4=, username=18881121153}
// 执行了, 返回值: 0A4F2B1E33437DEC1605B5E73A60EE57
// [*] TreeMap.put hook installed successfully!
// [*] AHAPIHelper.getDesKey called with context: com.che168.autotradercloud.ATCApplication@78e67d7
// [*] AHAPIHelper.getDesKey returned: appapiche168comappapiche168comap
// [*] TreeMap.put hook installed successfully!
// [AOSP on flame::com.che168.autotradercloud]-> md5 加密, 明文---> 123123
// md5 加密后的秘闻 是---> 4297f44b13955235245b2497399d7a93
// md5 加密, 明文---> W@oC!AH_6Ew1f6%8_appidatc.androidappversion2.8.2channelidcsypwd4297f44b13955235245b2497399d7a93udidV6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUX3GzVf37U56tAWRxCg etzsajcP9Ccoh1M=username18881121153W@oC!AH_6Ew1f6%8
// md5 加密后的秘闻 是---> b106ef85a0f2bdac47557f322287eb8d
// [*] TreeMap.put hook installed successfully!
// UserModel.loginByPassword is called: str=com.che168.autotradercloud.user.LoginActivity@4ea788e, str2=18881121153, str3=123123, responseCallback=[object Object]
// After Execution: result=undefined
// [*] TreeMap.put hook installed successfully!
// tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过
//
// UserModel.loginByPassword is called: str=com.che168.autotradercloud.user.LoginActivity@4ea788e, str2=18881121153, str3=123123, responseCallback=[object Object]
// After Execution: result=undefined
// [*] TreeMap.put hook installed successfully!
// [*] TreeMap.put hook installed successfully!
// [*] AHAPIHelper.getDesKey called with context: com.che168.autotradercloud.ATCApplication@78e67d7
// [*] AHAPIHelper.getDesKey returned: appapiche168comappapiche168comap
// [*] TreeMap.put hook installed successfully!
// [*] SignManager.signByType hook installed successfully!
// 执行了, 参数 i = 1
// 执行了, 参数 Treemap = {_appid=atc.android, appversion=2.8.2, channelid=csy, pwd=4297f44b13955235245b2497399d7a93, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVWB13GOdAnQl0Zvwii lBJLvnUy3hDEIho=, username=18881121153}
// 执行了, 返回值: 9ABDF3B8071D5E0E39FFF3C457E47CAF
// [*] TreeMap.put hook installed successfully!
// SharedPreferencesUtil.getString ----> imei
// SharedPreferencesUtil.getString 最后输出的结果是 ----->
// f5001045-faf3-3136-8188-f2144ede22b4
// SharedPreferencesUtil.getString ----> KEY_DEVICE_ID
// SharedPreferencesUtil.getString 最后输出的结果是 ----->
//
// SharedPreferencesUtil.getString ----> loginUserInfo
// SharedPreferencesUtil.getString 最后输出的结果是 ----->
//
// [*] TreeMap.put hook installed successfully!
// 获取IMEI f5001045-faf3-3136-8188-f2144ede22b4
// 获取 DeviceId
// [*] TreeMap.put hook installed successfully!
// [*] TreeMap.put hook installed successfully!
// [*] TreeMap.put hook installed successfully!
// 3DES 的输入数据---> f5001045-faf3-3136-8188-f2144ede22b4|3190170556808|
// 3DES 的加密结果---> V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUV3NalWSKr2e+yO6mX9 kmYzEl8H8X1q/Kg=
// [*] TreeMap.put hook installed successfully!
// SecurityUtil.encode3Des(context, getIMEI(context) + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + System.nanoTime() + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + SPUtils.getDeviceId());
//
// Hook,替换 getUDID 的值 ---> com.che168.autotradercloud.ATCApplication@78e67d7
// getIMEI 输入值是一下---> com.che168.autotradercloud.ATCApplication@78e67d7
// getIMEI 最后的值是 ---> f5001045-faf3-3136-8188-f2144ede22b4
// 返回 getUDID 的值是: ---> V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUVgrCf3T4GHtv6lh3KZ qeJyHHXpj0rckEw=
// [*] TreeMap.put hook installed successfully!
// lambda$initRequestCommonParam执行了参数---前面,参数 i=1,
// treeMap= function e() {
// [native code]
// }
// lambda$initRequestCommonParam执行后, ---> 后面----> 返回值是---->
// {_appid=atc.android, _sign=BEA52BCACBE799C9D1A3753CC230CF68, appversion=2.8.2, channelid=csy, pwd=4297f44b13955235245b2497399d7a93, udid=V6RGSxwRGumecbnKDiFB5ZXQrA5vkDHRfWSpstj8WUUZfhUr/q0z1fxqk/LW qlicLHVqDaCcNwk=, username=18881121153}
反编译,Java的逻辑提取
public class 车智赢 {
}
// ============================用户名 和 密码 =================================
public class com.che168.autotradercloud.user.model.UserModel extends BaseModel {
public static final String LOGIN_URL = "/tradercloud/sealed/login/login.ashx";
public static void loginByPassword(String str, String str2, String str3, ResponseCallback<UserBean> responseCallback) {
HttpUtil.Builder builder = new HttpUtil.Builder();
builder.tag(str) // 设置请求标记
.method(HttpUtil.Method.POST) // 请求方式为 post
.signType(1) // 使用签名类型
.url(LOGIN_URL) // 指定登陆的 URL
.param("username", str2) // 添加用户名参数
.param("pwd", SecurityUtil.encodeMD5(str3)); // 添加密码参数,进过 md5 加密
doRequest(builder, responseCallback, new TypeToken<BaseResult<UserBean>>() {/* class com.che168.autotradercloud.user.model.UserModel.AnonymousClass5 */}.getType()); // 发起请求
// 调用 doRequest 方法, 发起http请求
}
}
public class com.autohome.ahkit.utils.SecurityUtil {
private static final String encoding = "UTF-8";
private static final String iv = "appapich";
private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
public static String encode3Des (Context context, String str){
String desKey = AHAPIHelper.getDesKey(context);
byte[] bArr = null;
if (TextUtils.isEmpty(desKey)) {
return null;
}
try {
SecretKey generateSecret = SecretKeyFactory.getInstance("desede").generateSecret(new DESedeKeySpec(desKey.getBytes()));
Cipher instance = Cipher.getInstance("desede/CBC/PKCS5Padding");
instance.init(1, generateSecret, new IvParameterSpec(iv.getBytes()));
bArr = instance.doFinal(str.getBytes("UTF-8"));
} catch (Exception unused) {
}
return encode(bArr).toString();
}
public static String decode3Des (Context context, String str){
String desKey = AHAPIHelper.getDesKey(context);
if (TextUtils.isEmpty(desKey)) {
return null;
}
try {
SecretKey generateSecret = SecretKeyFactory.getInstance("desede").generateSecret(new DESedeKeySpec(desKey.getBytes()));
Cipher instance = Cipher.getInstance("desede/CBC/PKCS5Padding");
instance.init(2, generateSecret, new IvParameterSpec(iv.getBytes()));
return new String(instance.doFinal(decode(str)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static final String encodeMD5 (String str){ // 定义一个静态的 encodeMD5 方法,接收一个字符串作为参数
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; // 定义一个字符数组,用于存储十六进制字符
try {
byte[] bytes = str.getBytes(); // 将输入的字符串转换为字节数组
MessageDigest instance = MessageDigest.getInstance("MD5"); // 获取 MD5 的 MessageDigest 实例
instance.update(bytes); // 将字节数组更新到 MessageDigest 中
byte[] digest = instance.digest(); // 计算消息摘要,返回 MD5 哈希值的字节数组
int length = digest.length; // 获取 MD5 哈希值字节数组的长度
char[] cArr2 = new char[(length * 2)]; // 定义一个字符数组,用于存储转换后的十六进制字符
int i = 0; // 初始化索引 i
for (byte b : digest) { // 遍历每一个字节
int i2 = i + 1; // 计算下一个索引值
cArr2[i] = cArr[(b >>> 4) & 15]; // 取当前字节的高 4 位,转换为对应的十六进制字符
i = i2 + 1; // 更新索引值
cArr2[i2] = cArr[b & 15]; // 取当前字节的低 4 位,转换为对应的十六进制字符
}
return new String(cArr2).toLowerCase(); // 将字符数组转换为字符串并转换为小写后返回
} catch (Exception e) { // 捕获异常
e.printStackTrace(); // 打印异常堆栈信息
return null; // 返回 null 表示处理失败
}
}
}
public class com.autohome.ahkit.jni.CheckSignUtil {
public static native String get3desKey(Context context); // =================> 第 三点五 步
static {
System.loadLibrary("native-lib");
}
}
public class com.autohome.ahkit.AHAPIHelper {
public static final String APP_ID = "2scapp.android";
private static final String PARAM_APPID = "_appid";
private static final String PARAM_APPVERSION = "appversion";
private static final String PARAM_CHANNELID = "channelid";
private static final String PARAM_SIGN = "_sign";
private static final String PARAM_UDID = "udid";
private static final String PARAM_USERKEY = "userkey";
public static final String[] URL_FILTER_USEDKEY_UDID = {"apirnappusc.che168.com"};
private static String mDesKey = "";
private static String mSignKey = "";
public static String getDesKey(Context context) { // =================> 第一步
if (TextUtils.isEmpty(mDesKey)) {
getSignDesKey(context);
}
return mDesKey; // =================> 第四步
}
public static String getSignKey(Context context) {
if (TextUtils.isEmpty(mSignKey)) {
getSignDesKey(context); // =================> 第二步
}
return mSignKey;
}
private static void getSignDesKey(Context context) { // =================> 第三步
mDesKey = CheckSignUtil.get3desKey(context);
}
public static TreeMap<String, String> toSigndMap(Context context, TreeMap<String, String> treeMap) { // 重载 方法 1
if (treeMap == null) {
return null;
}
appendPublicParam(context, treeMap);
appendUserKey(context, treeMap);
appendUdid(context, treeMap);
treeMap.put("_sign", toSign(context, treeMap));
return treeMap;
}
public static TreeMap<String, String> toSigndMap(Context context, TreeMap<String, String> treeMap, String str) { // 重载 方法 2
if (treeMap == null) {
return null;
}
appendPublicParam(context, treeMap);
if (isAddUsedkeyAndUdid(str)) {
appendUserKey(context, treeMap);
appendUdid(context, treeMap);
}
treeMap.put("_sign", toSign(context, treeMap));
return treeMap;
}
public static TreeMap<String, String> toSignedMap(Context context, boolean z, TreeMap<String, String> treeMap) { // 重载 方法 3
if (treeMap == null) {
return null;
}
appendPublicParam(context, treeMap);
if (z) {
appendUdid(context, treeMap);
}
treeMap.put("_sign", toSign(context, treeMap));
return treeMap;
}
private static void appendPublicParam(Context context, Map<String, String> map) {
map.put("_appid", APP_ID);
map.put("channelid", AHAppInfoUtil.getUMSChannelId(context));
map.put("appversion", AHAppInfoUtil.getAppVersionName(context));
}
public static void appendUdid(Context context, Map<String, String> map) {
map.put("udid", getUDID(context));
}
public static synchronized String getUDID(Context context) {
String encode3Des;
synchronized (AHAPIHelper.class) {
encode3Des = SecurityUtil.encode3Des(context, AHDeviceUtil.getDeviceId(context) + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + System.nanoTime() + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + ((long) UserInfoUtil.getUserDeviceId(context)));
}
return encode3Des;
}
public static String toSign(Context context, Map<String, String> map) { // 最后的方法, 最后没有走到这里... ===> 最后的担心的事情, 却是没有走这里!!
if (map == null) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append(getSignKey(context));
for (Map.Entry<String, String> entry : map.entrySet()) {
sb.append(entry.getKey() + entry.getValue());
}
sb.append(getSignKey(context));
return SecurityUtil.encodeMD5(sb.toString()).toUpperCase();
}
}
public class com.che168.autotradercloud.launch.model.LaunchModel extends BaseModel {
public static final String KEY_SIGN = "_sign";
static /* synthetic */ TreeMap lambda$initRequestCommonParams$0 ( int i, TreeMap treeMap){ // 传入一个map, 在 map中 put Appid
if (!treeMap.containsKey(KEY_APP_ID)) {
treeMap.put(KEY_APP_ID, Constants.APP_ID);
}
if (!treeMap.containsKey("channelid")) {
treeMap.put("channelid", AppUtils.getChannelId(ContextProvider.getContext()));
}
if (!treeMap.containsKey(KEY_APP_VERSION)) {
treeMap.put(KEY_APP_VERSION, SystemUtil.getAppVersionName(ContextProvider.getContext()));
}
if (!treeMap.containsKey("udid")) {
treeMap.put("udid", AppUtils.getUDID(ContextProvider.getContext()));
}
String userKey = UserModel.getUserKey();
if (!ATCEmptyUtil.isEmpty((CharSequence) userKey)) {
treeMap.put("userkey", userKey);
}
checkNullParams(treeMap);
treeMap.put(KEY_SIGN, SignManager.INSTANCE.signByType(i, treeMap));
return treeMap;
}
}
public final class com.che168.atclibrary.base.SignManager { // 定义一个名为 SignManager 的最终公共类。
public static final SignManager INSTANCE = new SignManager(); // 创建一个名为 INSTANCE 的公共静态最终 SignManager 实例。
public static final String KEY_AUTOHOME = "@7U$aPOE@$"; // 定义一个名为 KEY_AUTOHOME 的公共静态最终字符串,值为 "@7U$aPOE@$"。
public static final String KEY_SHARE = "moc.861ehc.relaed.bup.wyfv"; // 定义一个名为 KEY_SHARE 的公共静态最终字符串,值为 "moc.861ehc.relaed.bup.wyfv"。
public static final String KEY_V1 = "com.che168.www"; // 定义一个名为 KEY_V1 的公共静态最终字符串,值为 "com.che168.www"。
public static final String KEY_V2 = "W@oC!AH_6Ew1f6%8"; // 定义一个名为 KEY_V2 的公共静态最终字符串,值为 "W@oC!AH_6Ew1f6%8"。------------------------------
private SignManager() { // 定义一个私有的 SignManager 构造函数,防止外部实例化。
}
public final String signByType(@SignType int i, TreeMap<String, String> paramMap) { // 定义一个名为 signByType 的公共最终方法,接受一个 @SignType 注释的整数和一个 TreeMap<String, String> 作为参数,返回一个字符串。-----------------
Intrinsics.checkParameterIsNotNull(paramMap, "paramMap"); // 检查 paramMap 是否为空,如果为空则抛出异常。
StringBuilder sb = new StringBuilder(); // 创建一个 StringBuilder 对象来构建签名字符串。
String str = KEY_V1; // 初始化一个字符串变量 str 为 KEY_V1。
if (i != 0) { // 检查 i 是否不等于 0。
if (i == 1) { // 如果 i 等于 1,将 str 设置为 KEY_V2。
str = KEY_V2;
} else if (i == 2) { // 如果 i 等于 2,将 str 设置为 KEY_SHARE。
str = KEY_SHARE;
} else if (i == 3) { // 如果 i 等于 3,将 str 设置为 KEY_AUTOHOME。
str = KEY_AUTOHOME;
}
}
sb.append(str); // 将 str 追加到 StringBuilder。 ------前面加上这个玩意儿---定义一个名为 KEY_V2 的公共静态最终字符串,值为 "W@oC!AH_6Ew1f6%8"。-------------
for (String str2 : paramMap.keySet()) { // 遍历 paramMap 的键集。-----------------
sb.append(str2); // 将每个键追加到 StringBuilder。---------------
sb.append(paramMap.get(str2)); // 将每个键对应的值追加到 StringBuilder。-------------
}
sb.append(str); // 再次将 str 追加到 StringBuilder。 -----再次将这个放到末尾 定义一个名为 KEY_V2 的公共静态最终字符串,值为 "W@oC!AH_6Ew1f6%8"。-----------------
String encodeMD5 = SecurityUtil.encodeMD5(sb.toString()); // 使用 SecurityUtil.encodeMD5 方法对 StringBuilder 的内容进行 MD5 编码,并将结果存储在 encodeMD5 中。--将内容再次MD5运算------------
if (encodeMD5 != null) { // 检查 encodeMD5 是否不为空。
Locale locale = Locale.ROOT; // 获取默认的 Locale。
Intrinsics.checkExpressionValueIsNotNull(locale, "Locale.ROOT"); // 检查 locale 是否不为空,如果为空则抛出异常。
if (encodeMD5 == null) { // 再次检查 encodeMD5 是否为空,如果为空则抛出类型转换异常。
throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
}
String upperCase = encodeMD5.toUpperCase(locale); // 将 encodeMD5 转换为大写,并将结果存储在 upperCase 中。---------------------
Intrinsics.checkExpressionValueIsNotNull(upperCase, "(this as java.lang.String).toUpperCase(locale)"); // 检查 upperCase 是否不为空,如果为空则抛出异常。
if (upperCase != null) { // 再次检查 upperCase 是否不为空,如果不为空则返回 upperCase。
return upperCase;// 变成 大写-----------------------------
}
}
return ""; // 如果 encodeMD5 为空或 upperCase 为空,则返回一个空字符串。
}
}
public class com.che168.autotradercloud.util.AppUtils {
public static String getIMEI (Context context){
String str;
if (!PermissionsCheckerUtil.hasReadPhoenStatePermission(context)) {
return "sssss";
}
String imei = SPUtils.getIMEI(); // 1, 本地 xml中 读取
if (imeiIsNull(imei)) {
imei = ((TelephonyManager) context.getSystemService("phone")).getDeviceId(); // 2, 读取设备 id
if (imeiIsNull(imei)) {
String macAddress = ((WifiManager) context.getSystemService(NetworkUtil.NETWORK_TYPE_WIFI)).getConnectionInfo().getMacAddress(); // 3, 读取 Mac 地址
if (macAddress != null) {
try {
if (macAddress.length() > 0 && !isInBlackList(macAddress)) {
str = UUID.nameUUIDFromBytes(macAddress.getBytes("utf8")).toString();
imei = str;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
str = getIMEIbyAndroidIDandUUID(context);
}
}
str = getIMEIbyAndroidIDandUUID(context); // 4, 根据 uuid 生成
imei = str;
}
if (!imeiIsNull(imei)) {
SPUtils.saveIMEI(imei);
}
}
return imei;
}
private static String getIMEIbyAndroidIDandUUID (Context context){
String string = Settings.Secure.getString(context.getContentResolver(), SocializeProtocolConstants.PROTOCOL_KEY_ANDROID_ID);
if (imeiIsNull(string)) {
return UUID.randomUUID().toString(); // 生成 UUID这个值
}
try {
return UUID.nameUUIDFromBytes(string.getBytes("utf8")).toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return string;
}
}
public static String getUDID (Context context){
return SecurityUtil.encode3Des(context, getIMEI(context) + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + System.nanoTime() + HiAnalyticsConstant.REPORT_VAL_SEPARATOR + SPUtils.getDeviceId());
}
}
public class com.che168.autotradercloud.util.SPUtils {
public static void saveDeviceId(String str) {
getSpUtil().saveString(KEY_DEVICE_ID, str);
}
public static String getDeviceId() {
return getSpUtil().getString(KEY_DEVICE_ID, "");
}
public static void saveIMEI(String str) {
if (!ATCEmptyUtil.isEmpty((CharSequence) str)) {
getSpUtil().saveString("imei", str);
}
}
public static String getIMEI() {
return getSpUtil().getString("imei", "");
}
}
public class com.che168.atclibrary.utils.SharedPreferencesUtil {
public void saveString(String str, String str2) {
SharedPreferences sharedPreferences = this.mPreferences;
if (sharedPreferences != null) {
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putString(str, str2);
edit.commit();
}
}
public String getString(String str, String str2) {
SharedPreferences sharedPreferences = this.mPreferences;
return sharedPreferences != null ? sharedPreferences.getString(str, str2) : str2;
}
}
public class com.che168.autotradercloud.base.push.model.PushModel extends BaseModel {
private static final String REG_DEVICE_URL = "/tradercloud/v100/push/regdevice.ashx";
public static void regDevice(String str) {
HttpUtil.Builder builder = new HttpUtil.Builder();
builder.url(REG_DEVICE_URL)
.method(HttpUtil.Method.POST)
// 固定参数
.param("pid", "")
.param("cid", "")
// 动态参数
.param(ai.a, str) // 设备注册, 个推 参数
// 设备信息
.param("syssn", SystemUtil.getModel())
.param("sysversion", SystemUtil.getOSVersion(ContextProvider.getContext()))
.param("manufacturer", SystemUtil.getBrand())
// 应用信息
.param("appname", SystemUtil.getAppName());
doRequest(builder, new ResponseCallback<RegDeviceResult>() {
/* class com.che168.autotradercloud.base.push.model.PushModel.AnonymousClass1 */
public void success(RegDeviceResult regDeviceResult) {
if (regDeviceResult != null && !ATCEmptyUtil.isEmpty((CharSequence) regDeviceResult.deviceid)) {
SPUtils.saveDeviceId(regDeviceResult.deviceid);
PushModel.regPush();
}
}
@Override // com.che168.autotradercloud.base.httpNew.ResponseCallback
public void failed(int i, ApiException apiException) {
LogUtil.e(PushModel.TAG, apiException.toString());
}
}, new TypeToken<BaseResult<RegDeviceResult>>() {
/* class com.che168.autotradercloud.base.push.model.PushModel.AnonymousClass2 */
}.getType());
LogUtil.d(TAG, "regDevice invoke...");
}
}
public class com.che168.autotradercloud.base.push.ReceivePushService extends GTIntentService { // 个推相关
private static final int MAX_COUNT = 10;
private static final String TAG = "ReceivePushService";
private static int sRequestCode;
@Override // com.igexin.sdk.GTIntentService
public void onReceiveClientId (Context context, String str){
String clientId = SPUtils.getClientId();
if (ATCEmptyUtil.isEmpty((CharSequence) clientId) || !clientId.equals(str)) {
SPUtils.saveClientId(str);
}
PushModel.regDevice(str);
LogUtil.d(TAG, "onReceiveClientId:" + str);
}
}
