今日水印相机自定义水印
apk没有加壳,还是比较好分析的
抓包
打开app开始抓包,有以下几个api
image-20210810004740692.png (391.59 KB, 下载次数: 0)
下载附件
2021-8-10 02:26 上传
逆向分析
分析天气参数的获取
jeb分析
jeb中搜索相应的URI
image-20210810004938096.png (19.83 KB, 下载次数: 0)
下载附件
2021-8-10 02:26 上传
选择第二个来到 一个所有request方法的类
image-20210810005040994.png (53.65 KB, 下载次数: 0)
下载附件
2021-8-10 02:26 上传
摁X进行交叉引用
参数1和参数2即为经纬度坐标
image-20210810005122554.png (22.83 KB, 下载次数: 0)
下载附件
2021-8-10 02:26 上传
来到这里
image-20210810005156768.png (40.1 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
发现内部类对其进行了解析,强转为WeatherInfo
public class WeatherInfo {
public String apparentTemperature; // 体感温度:30
public String aqi; // 空气质量指数:37
public String aqiLevel; // 空气质量等级:优
public String humidity; // 湿度:75%
public String pressure; // 压强:1001
public String temperature; // 温度:27
public String weather; // 天气:晴
public String winddirection; // 风向:西南风
public String windpower; // 风力:1级
}
接下来就是如何hook该内部类了
调试验证
运行fridaserver
使用frida的objection工具
先启动目标app
objection -N -h 192.168.0.104 -p 8888 -d -g com.xhey.xcamera explore
然后查找com.xhey.xcamera.e的类
com.xhey.xcamera on (google: 8.1.0) [net] # android hooking search classes com.xhey.xcamera.e
com.xhey.xcamera.e
com.xhey.xcamera.e$1
com.xhey.xcamera.e$5
com.xhey.xcamera.e$6
com.xhey.xcamera.e$8
com.xhey.xcamera.e$a
com.xhey.xcamera.e$b
Found 7 classes
挨个查看每个内部类的方法
com.xhey.xcamera on (google: 8.1.0) [net] # android hooking list class_methods com.xhey.xcamera.e$1
private static void com.xhey.xcamera.e$1.b(xhey.com.network.model.BaseResponse)
public static void com.xhey.xcamera.e$1.lambda$Wc1CpEPvMWi1606k4HUcLAjQgiw(xhey.com.network.model.BaseResponse)
public void com.xhey.xcamera.e$1.a(xhey.com.network.model.BaseResponse)
public void com.xhey.xcamera.e$1.onError(java.lang.Throwable)
public void com.xhey.xcamera.e$1.onSuccess(java.lang.Object)
Found 5 method(s)
发现e$1,e$5,e$8具有相同的方法,且与上反编译源码中方法类似
function main(){
Java.perform(function () {
var clazz = Java.use('com.xhey.xcamera.e$5');
clazz.a.implementation = function (response_argu) {
var result = clazz.a.apply(this, arguments);
var response = Java.cast(response_argu, Java.use("xhey.com.network.model.BaseResponse"));
console.log("BaseResponse.code", response.code.value);
console.log("BaseResponse.data", response.data.value);
console.log("BaseResponse.msg", response.msg.value);
console.log("BaseResponse.toast_msg", response.toast_msg.value);
return result;
}
});
}
setImmediate(main)
frida来运行调试
frida -H 192.168.0.104:8888 -f com.xhey.xcamera -l syxj.js --no-pause
打印出如下信息
[Remote::com.xhey.xcamera]-> BaseResponse.code 200
BaseResponse.data com.xhey.xcamera.data.model.bean.WeatherInfo@1d5fef8
BaseResponse.msg success
BaseResponse.toast_msg
发现WeatherInfo,就是解密后的类
分析时间的获取
部分同天气获取,定位到
image-20210810012037704.png (34.96 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
其中v1.build()调用了aes,来对json字符串进行加密
image-20210810012124362.png (23.61 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
得到key和iv均为"xhey-cc4275fd-43",AES模式为"AES/CBC/PKCS5Padding"
package xhey.com.network.retrofit2;
public enum AESUtil {
private static final String ENCRYPTION_IV = "xhey-cc4275fd-43";
private static final String ENCRYPTION_KEY = "xhey-cc4275fd-43";
public static String decrypt(String arg4) {
}
public static String encrypt(String arg5) {
if(TextUtils.isEmpty(arg5)) {
return arg5;
}
try {
Cipher v0 = Cipher.getInstance("AES/CBC/PKCS5Padding");
v0.init(1, AESUtil.makeKey(), AESUtil.makeIv());
Log.e("time", "==" + Base64.encode(v0.doFinal(arg5.getBytes()), 0));
return new String(Base64.encode(v0.doFinal(arg5.getBytes()), 2)).trim();
}
catch(Exception v5) {
Log.e("AESUtil", "=encrypt=" + v5.getMessage());
return "";
}
}
static AlgorithmParameterSpec makeIv() {
return new IvParameterSpec("xhey-cc4275fd-43".getBytes("utf-8"));
}
static SecretKeySpec makeKey() {
return new SecretKeySpec("xhey-cc4275fd-43".getBytes("utf-8"), "AES");
}
}
此时我们就可以对Fiddler抓到的包来进行加解密了。
requestTimeInChina继续查看上层引用,同样来到了com.xhey.xcamera.e类中
image-20210810012605219.png (65.44 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
同样frida分析后发现是e$8
分析定位
当程序获取天气参数的时候,使用了定位
交叉引用向上追溯
com.xhey.xcamera.network.service.NetWorkServiceKt.requestWeatherInfocom.xhey.xcamera.e.b(java.lang.String[])+2Ahcom.xhey.xcamera.e.a(boolean, com.baidu.location.BDLocation)+5D8h
找到赋值的地方
image-20210810013246491.png (19.75 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
hook这两个方法就可以修改经纬度也就是定位了
而BDLocation是百度地图的api
同理速度也可以hookBDLocation.getSpeed来修改
插件编写
Frida hook脚本
(换行全部都没了,重新格式化下是可以的)
function printStack(name) { Java.perform(function () { var Exception = Java.use("java.lang.Exception"); var ins = Exception.$new("Exception"); var straces = ins.getStackTrace(); if (straces != undefined && straces != null) { var strace = straces.toString(); var replaceStr = strace.replace(/,/g, "\\n"); console.log("=============================" + name + " Stack strat======================="); console.log(replaceStr); console.log("=============================" + name + " Stack end=======================\r\n"); Exception.$dispose(); } });}function main() { Java.perform(function () { /** * 解析 /next/android/config */ var clazz = Java.use('com.xhey.xcamera.e$1'); clazz.a.implementation = function (response_argu) { var response = Java.cast(response_argu, Java.use("xhey.com.network.model.BaseResponse")); // console.log("BaseResponse.code",response.code.value); // console.log("BaseResponse.data",response.data.value); // console.log("BaseResponse.msg",response.msg.value); // console.log("BaseResponse.toast_msg",response.toast_msg.value); var result = clazz.a.apply(this, arguments); return result; } }); Java.perform(function () { Java.openClassFile("/data/local/tmp/r0gson.dex").load(); const gson = Java.use('com.r0ysue.gson.Gson'); /** * 解析天气 /next/GetCurrentWeather * public class WeatherInfo { * public String apparentTemperature; // 体感温度:30 * public String aqi; // 空气质量指数:37 * public String aqiLevel; // 空气质量等级:优 * public String humidity; // 湿度:75% * public String pressure; // 压强:1001 * public String temperature; // 温度:27 * public String weather; // 天气:晴 * public String winddirection; // 风向:西南风 * public String windpower; // 风力:1级 * } */ var clazz = Java.use('com.xhey.xcamera.e$5'); clazz.a.implementation = function (response_argu) { var response = Java.cast(response_argu, Java.use("xhey.com.network.model.BaseResponse")); var weather_info = Java.cast(response.data.value, Java.use("com.xhey.xcamera.data.model.bean.WeatherInfo")) // 对值做修改 weather_info.temperature.value = Java.use("java.lang.String").$new("-99"); // // console.log("BaseResponse.code", response.code.value); // console.log("weather_info", gson.$new().toJson(weather_info)); // console.log("BaseResponse.msg", response.msg.value); // console.log("BaseResponse.toast_msg", response.toast_msg.value); var result = clazz.a.apply(this, arguments); return result; } }); Java.perform(function () { Java.openClassFile("/data/local/tmp/r0gson.dex").load(); const gson = Java.use('com.r0ysue.gson.Gson'); /** * 解析时间 /next/inchina * public class TimeStatus { * private boolean in_china; // 是否在中国:true * private boolean stopDevice; // false * private int timestamp; // 时间戳 * } */ var clazz = Java.use('com.xhey.xcamera.e$8'); clazz.a.implementation = function (response_argu) { var response = Java.cast(response_argu, Java.use("xhey.com.network.model.BaseResponse")); var time_info = Java.cast(response.data.value, Java.use("com.xhey.xcamera.data.model.bean.TimeStatus")); // console.log("=== TIME ===") // console.log("BaseResponse.code", response.code.value); // console.log("BaseResponse.data", response.data.value); // console.log("BaseResponse.msg", response.msg.value); // console.log("BaseResponse.toast_msg", response.toast_msg.value); // console.log("Time", gson.$new().toJson(time_info)); // console.log("TimeStatus.timestamp", time_info.timestamp.value); time_info.timestamp.value = Java.use("java.lang.Integer").$new("1912521600").intValue(); var result = clazz.a.apply(this, arguments); return result; } }); Java.perform(function () { var clazz = Java.use('com.baidu.location.BDLocation'); // 纬度 clazz.getLatitude.implementation = function () { var result = clazz.getLatitude.apply(this, arguments); // console.log("Latitude", result); result = Java.use("java.lang.Double").$new("36.269893").doubleValue(); // console.log("Change Latitude", result); return result; } // 经度 clazz.getLongitude.implementation = function () { var result = clazz.getLongitude.apply(this, arguments); // console.log("Longitude", result); result = Java.use("java.lang.Double").$new("117.094738").doubleValue(); // console.log("Change Longitude", result); return result; } // 速度 clazz.getSpeed.implementation = function () { var result = clazz.getSpeed.apply(this, arguments); // console.log("Speed", result); result = Java.use("java.lang.Float").$new("99.99").floatValue(); // console.log("Change Speed", result); return result; } });}function func() { Java.perform(function () { var clazz = Java.use('xhey.com.network.retrofit2.AESUtil'); clazz.decrypt.implementation = function (data) { var result = clazz.decrypt.apply(this, arguments) printStack("AES decrypt"); console.log("aes decrypt data:", data); console.log("aes decrypt result: ", result); return result; } }); Java.perform(function () { var clazz = Java.use('xhey.com.network.retrofit2.AESUtil'); clazz.encrypt.implementation = function (data) { var result = clazz.encrypt.apply(this, arguments) printStack("AES encrypt"); console.log("aes encrypt data:", data); console.log("aes encrypt result: ", result); return result; } });}setImmediate(main)
Xposed插件开发
hook的类已经分析完了,懒得造轮子了
效果图
image-20210810022207796.png (212.94 KB, 下载次数: 0)
下载附件
2021-8-10 02:27 上传
总结
这算是自己分析的第一个app。花了差不多一天的时间,第一次接触这么大的代码量,刚开始有些懵逼。
但分析完发现这个app还是很简单的,没有壳也没有混淆,比较适合我这样的新手。
Frida啥的还是不熟练,疯狂找bug。
刚开始修改时间没有效果,最后发现是因为先执行了原方法result = clazz.a.apply(this, arguments);,导致时间在方法体内被直接应用了。
该文章也同步上传至我的GitHub博客 Example(0) 今日水印相机自定义水印 | Forgo7ten'blog
中间也多次向@闷骚小贱男 寻求指导,再次感谢