Android逆向实战-FridaHook破解极简记账VIP
# 前言
**本文中所有内容仅供研究与学习使用,请勿用于任何商业用途和非法用途,否则后果自负!**
最近在学习frida,平常用极简记账记录日常流水,正好实战一波VIP破解
样本使用了360加固,但java代码没有混淆,总体难度不大适合新手上路
基本思路:
1. 通过vip相关组件定位关键代码所在类
2. frida打印关键代码所在类信息
3. dump dex文件,分析关键代码
4. hook vip判断逻辑
# 一. 相关环境
测试机: Pixel 3XL Android12
主机环境: Windows10 frida-16.0.19 python3.10 Java8
相关工具: MT管理器 Jeb4.32
App版本: 2.4.4 (360加固)
# 二. VIP代码定位
使用MT管理器记录活动,点击已经购买,定位vip检测的相关类
可以发现关键类为**com.luyun.simpleaccout.ui.VipActivity**
使用frida hook打印VipActivity相关信息
```js
function main(){
Java.perform(function (){
Java.choose("com.luyun.simpleaccout.ui.VipActivity",{
onMatch:function(instance){
console.log("instance:",instance);
var methods=instance.class.getDeclaredMethods(); //方法
var fields=instance.class.getDeclaredFields(); //字段
console.log("\n==========methods==========");
for(let i=0;i<methods.length;i++){
var methodParams=methods.getParameterTypes()
var methodReturnType=methods.getReturnType()
var methodName=methods.getName();
console.log(methodReturnType+" "+methodName+"("+methodParams+")");
}
console.log("\n==========fields==========");
for(let i=0;i<fields.length;i++){
var fieldName=fields.getName();
var fieldType=fields.getType();
var fieldValue=instance.value;
console.log(fieldType,fieldName,"=",fieldValue);
}
},
onComplete:function(instance){
}
})
})
}
setTimeout(main,500)
```
发现和支付/vip相关的几个方法
# 三. DumpDex
使用frida-dexdump 拉取dex文件
将所有dex文件拖进jeb工具分析(jadx无法正确反编译VipActivity相关部分代码)
# 四. 代码分析
反编译完成后搜索VipActivity,发现alreadyBuy()方法,该方法即为点击"已经购买"后的检测方法
内部主要通过调用isVip()判断是否为vip用户
跟进发现isVip方法是继承自BaseActivity
该方法内部目测是向服务器拉取vip的起始和终止时间判断是否为有效vip,最终返回值为bool类型
VipActivity中还能看到getVipType()不同返回值对应的vip类型
type=0时未解锁,type=1时可续订,type=2时是永久vip
最初hook了VipActivity的isVip()和getVipType()方法,发现ui显示为永久会员但是仍然需要解锁
于是搜索所有isVip()和getVipType()方法实现
# 五. Hook脚本
将以上所有类的isVip和getVipType方法实现全部hook
```js
function main(){
Java.perform(function (){
var baseActivity=Java.use("com.luyun.simpleaccout.ui.BaseActivity")
baseActivity.isVip.implementation=function(){
console.log("Hook isVip() method!Origin result=",this.isVip())
return true;
}
baseActivity.getVipType.implementation=function(){
console.log("Hook getVipType() method!Origin result=",this.getVipType())
return 2;
}
var baseFragment=Java.use("com.luyun.simpleaccout.ui.fragment.BaseFragment")
baseFragment.isVip.implementation=function(){
console.log("Hook BaseFragment isVip() Succeeded!Origin result=",this.isVip());
return true;
}
baseFragment.getVipType.implementation=function(){
console.log("Hook BaseFragment getVipType() Succeeded!Origin result=",this.getVipType())
return 2;
}
var splashActivity=Java.use("com.luyun.simpleaccout.ui.SplashActivity");
splashActivity.isVip.implementation=function(){
console.log("Hook SplashActivity isVip() Succeeded!Origin result=",this.isVip());
return true;
}
var accountWidgetProvider=Java.use("com.luyun.simpleaccout.provider.AccountWidgetProvider")
accountWidgetProvider.isVip.implementation=function(){
console.log("Hook AccountWidgetProvider isVip() Succeeded!Origin result=",this.isVip());
return true;
}
})
}
setTimeout(main,500)
```
# 六. 效果演示
Hook成功,并且是永久会员
vip的导出账本功能正常使用
经过下面这个网站的学习,可算是把frida的js脚本移植到xposed中了.
https://forum.butian.net/share/2248
// 解决动态加载dex文件hook不到问题
XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// XposedBridge.log(TAG + " clazz => " + clazz);
Class<?> clazz = (Class<?>) param.getResult();
if (clazz != null) {
switch (clazz.getName()) {
case "com.luyun.simpleaccout.ui.SplashActivity":
XposedBridge.hookAllMethods(clazz, "isVip", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + "Hook SplashActivity isVip()");
Object context = XposedHelpers.callMethod(param.thisObject, "getApplicationContext");
Class<?> sharedPreferencesUtilsClass = XposedHelpers.findClass("com.luyun.simpleaccout.utils.SharedPreferencesUtils", loadPackageParam.classLoader);
Class<?> stubAppClass = XposedHelpers.findClass("com.stub.StubApp", loadPackageParam.classLoader);
Object applicationContext = XposedHelpers.callStaticMethod(stubAppClass, "getOrigApplicationContext", context);
Object paramValue = XposedHelpers.callStaticMethod(sharedPreferencesUtilsClass, "getParam", applicationContext, "is_privacy_show", Boolean.valueOf(false));
boolean bl = (Boolean) XposedHelpers.callMethod(paramValue, "booleanValue");
XposedBridge.log(TAG + " Hook SplashActivity isVip() Succeeded! Origin result=" + param.getResult() + " bl=" + bl);
param.setResult(bl);
}
});
XposedBridge.hookAllMethods(clazz, "popPrivacyDialog", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log(TAG + " Hook popPrivacyDialog 确认弹窗,执行相关初始化操作并跳转到主界面");
Object context = XposedHelpers.callMethod(param.thisObject, "getApplicationContext");
Class<?> sharedPreferencesUtilsClass = XposedHelpers.findClass("com.luyun.simpleaccout.utils.SharedPreferencesUtils", loadPackageParam.classLoader);
Class<?> stubAppClass = XposedHelpers.findClass("com.stub.StubApp", loadPackageParam.classLoader);
Object origContext = XposedHelpers.callStaticMethod(stubAppClass, "getOrigApplicationContext", context);
XposedHelpers.callStaticMethod(sharedPreferencesUtilsClass, "setParam", origContext, "is_privacy_show", true);
XposedBridge.log(TAG + " SharedPreferencesUtils.setParam() 已执行");
XposedHelpers.callMethod(param.thisObject, "goToMainActivity");
param.setResult(null);
}
});
}
}
}
});
} 为什么这个代码转成xp框架代码的时候,报错,提示找不到对应的类,好奇怪
LSPosed-Bridge EKWs.VughjCuW.z.gAm.sQH.XposedHelpers$ClassNotFoundError: java.lang.ClassNotFoundException: com.luyun.simpleaccout.ui.SplashActivity
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findClass(Unknown Source:12)
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findAndHookMethod(SourceFile:6)
at yuu.xposed.jjjz.Hook.handleLoadPackage(Hook.java:20)
at KWs.VughjCuW.z.gAm.sQH.IXposedHookLoadPackage$Wrapper.handleLoadPackage(Unknown Source:2)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XC_LoadPackage.call(Unknown Source:6)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XCallback.callAll(Unknown Source:26)
at h0.a(Unknown Source:320)
at java.lang.reflect.Method.invoke(Native Method)
at J.callback(Unknown Source:253)
at LSPHooker_.createOrUpdateClassLoaderLocked(Unknown Source:11)
at android.app.LoadedApk.getClassLoader(LoadedApk.java:810)
at android.app.LoadedApk.getResources(LoadedApk.java:1032)
at android.app.ContextImpl.createAppContext(ContextImpl.java:2345)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5907)
at android.app.ActivityThread.access$1100(ActivityThread.java:207)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1663)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6840)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)
Caused by: java.lang.ClassNotFoundException: com.luyun.simpleaccout.ui.SplashActivity
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:470)
at q.a(Unknown Source:16)
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findClass(Unknown Source:4)
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findAndHookMethod(SourceFile:6)
at yuu.xposed.jjjz.Hook.handleLoadPackage(Hook.java:20)
at KWs.VughjCuW.z.gAm.sQH.IXposedHookLoadPackage$Wrapper.handleLoadPackage(Unknown Source:2)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XC_LoadPackage.call(Unknown Source:6)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XCallback.callAll(Unknown Source:26)
at h0.a(Unknown Source:320)
at java.lang.reflect.Method.invoke(Native Method)
at J.callback(Unknown Source:253)
at LSPHooker_.createOrUpdateClassLoaderLocked(Unknown Source:11)
at android.app.LoadedApk.getClassLoader(LoadedApk.java:810)
at android.app.LoadedApk.getResources(LoadedApk.java:1032)
at android.app.ContextImpl.createAppContext(ContextImpl.java:2345)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5907)
at android.app.ActivityThread.access$1100(ActivityThread.java:207)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1663)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6840)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.luyun.simpleaccout.ui.SplashActivity" on path: DexPathList[,nativeLibraryDirectories=]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:470)
at q.a(Unknown Source:16)
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findClass(Unknown Source:4)
at KWs.VughjCuW.z.gAm.sQH.XposedHelpers.findAndHookMethod(SourceFile:6)
at yuu.xposed.jjjz.Hook.handleLoadPackage(Hook.java:20)
at KWs.VughjCuW.z.gAm.sQH.IXposedHookLoadPackage$Wrapper.handleLoadPackage(Unknown Source:2)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XC_LoadPackage.call(Unknown Source:6)
at KWs.VughjCuW.z.gAm.sQH.callbacks.XCallback.callAll(Unknown Source:26)
at h0.a(Unknown Source:320)
at java.lang.reflect.Method.invoke(Native Method)
at J.callback(Unknown Source:253)
at LSPHooker_.createOrUpdateClassLoaderLocked(Unknown Source:11)
at android.app.LoadedApk.getClassLoader(LoadedApk.java:810)
at android.app.LoadedApk.getResources(LoadedApk.java:1032)
at android.app.ContextImpl.createAppContext(ContextImpl.java:2345)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5907)
at android.app.ActivityThread.access$1100(ActivityThread.java:207)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1663)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6840)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)
推荐使用beancount,复式记账,相当好用 看看好不好,学习一下
感谢分享,学习 最近也遇到类似的apk,这就试验一下
牛批,刚好在学习frida,我也去试一下 谢谢分享,学习了 学习到了,感谢楼主分享 cuteapi 发表于 2024-8-20 10:44
推荐使用beancount,复式记账,相当好用
这个安卓好像不方便下载把 感谢分享。
我有一个问题想问一下:最后给出的hook脚本,需要如何运行?比方说是将该段代码嵌入到原本的代码中,再打包运行?还是有别的运行方式呢?