首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
0
0
[原创]某记账APP的永久VIP功能和签名绕过等
发表于: 2026-10-2 22:41
246
[原创]某记账APP的永久VIP功能和签名绕过等
Topdayplus
2026-10-2 22:41
246
直接应用商场下载,MT看了下没加固  打开提示要更新,而且没有关闭键,是强制更新,这不得看看啥逻辑  # 绕过强制更新 因为弹窗在app启动就检测了,通过了才加载主页,那基本上就是启动开始阶段就去调用了,现在看到的页面已经是检测完的弹窗。那这时候的思路可以是 1、定位当前弹窗,定位弹窗调用的判断逻辑(关键词、算法助手定位堆栈、资源id定位等,当然也有可能关键词是接口返回的) 2、直接搜更新相关的关键词,尝试缕清逻辑 我这里用第二个方法,先尝试找到更新相关的方法 简单搜下更新相关的关键词 (systemUpgrade|forceUpdate|checkVersion)  这里说个思路,大致定位缩小范围再逐步定位 ``` package ├── .bean.* → 数据模型(Bean类,字段定义) ├── .network.* / .api.* → 接口定义(Retrofit/OkHttp的Service) ├── .p768ui. │ ├── .activity.* → 页面入口(功能触发点) │ ├── .fragment.* → 页面片段(功能触发点) │ ├── .model.* → Model层(接口封装+回调转发) │ ├── .adapter.* → 列表适配器(UI渲染,非核心逻辑) │ ├── .receiver.* → ContentProvider/BroadcastReceiver(跨进程/广播) │ └── .view.* → 自定义View(UI控件,非核心逻辑) ├── .base.* → 基类(缓存/公共逻辑) ├── .databinding.* → ViewBinding生成类(布局绑定,含控件ID映射) └── .R → 资源ID定义(只用来反查,不是逻辑) anp.kad.sdk ├── C[数字][字母] → 混淆回调类(Lambda/Function1编译产物) ├── AbstractC09xxBg → 工具类(Toast/比较/类型转换/日志) ├── AbstractC0xxx → 抽象基类(公共逻辑) └── C2972xC → Unit占位类(Kotlin Unit的Java包装) 第三方SDK(直接跳过) ├── com.bytedance.* → 字节广告SDK ├── com.baidu.mobads.* → 百度广告SDK ├── com.kwad.* → 快手广告SDK ├── com.huawei.hms.* → 华为推送/更新SDK ├── com.alibaba.* → 阿里推送SDK ├── com.alipay.sdk.* → 支付宝SDK ├── com.tencent.mmkv.* → MMKV存储库 ├── androidx.* → AndroidX系统库 ├── android.* → Android系统类 └── cn.hutool.* → Hutool工具库 ``` 所以基于这个思路,基本上,第三方SDK,bean,model等优先级先调低,先看自身包名调用的,那能不能找到蛛丝马迹。 最终定位到 **forceUpdate** 方法,这里实在不行用update去搜,再用上面的方法去缩小范围定位也是可以的  forceUpdate获取布尔值后用做判断,zisShowing的值为false并且forceUpdate的值为true时,走else逻辑,实例化Alert弹窗,猜测这就是提示更新的逻辑,直接hook getforceUpdate返回false ```js Java.perform(function(){ var AppInfos = Java.use("com.xxx.bean.AppInfos"); AppInfos["getForceUpdate"].implementation = function () { console.log(`AppInfos.getForceUpdate is called`); let result = false; console.log(`AppInfos.getForceUpdate result=${result}`); return result; }; }) ``` 没有符合的就大范围地搜,根据文件路径自行过滤 (Upgrade|Update),然后大略看一下, 比如看到forceUpdate这样就可以重点关注 # 绕过VIP 登录后大致有这些功能,各个地方都在提示开通VIP,就按这些功能来吧,看哪些功能触发的时候提示要开通VIP。  ## 聊天记账 先点击聊天记账,看到有每日试用次数,并提示开通会员  直接搜 **今日试用** 关键词,几个都一样点进去看  这里讲一下,整体的逻辑先不看,先关注重点参数,不然一股脑地就分析各个调用逻辑容易被绕进去。这里着重关注还剩多少次这个多少,就 f32935i0 参数,往上追这个参数是 aiVipCount.getReceiveTrialTimes() - aiVipCount.getUsedTrialTimes(),这两个都是get方法,那思路就有了。  hook getReceiveTrialTimes和getUsedTrialTimes,一个直接为超大数字,一个为0。 ```javascript Java.perform(function(){ var AiVipCount = Java.use("com.jxm.bill.anxin.bean.AiVipCount"); AiVipCount.getReceiveTrialTimes.implementation = function() { // 返回一个很大的试用次数,永远 > usedTrialTimes return Java.use("java.lang.Integer").valueOf(999999); }; AiVipCount.getUsedTrialTimes.implementation = function() { return Java.use("java.lang.Integer").valueOf(0); }; }) ``` 这里有个小坑,一开始为直接返回 9999 不行,因为这两个参数定义为 Integer (包装类型/对象) ,不是 int (基本类型)。Frida 在桥接 JavaScript → Java 时,需要保证返回值 类型兼容 。方法声明的返回类型是 java.lang.Integer (对象),而 JavaScript 的 9999 是一个 JS 原始 number。Frida 的 Java 桥接层 不会自动把 JS number 装箱为 Java Integer 对象。 小计: ``` Java返回类型是 int/boolean/long → 直接 return JS 值 Java返回类型是 Integer/Boolean/Long → 必须用 Java.use().valueOf() 或 .$new() Java返回类型是 String → 直接 return JS 字符串(String本就是对象) ``` 然后就可以了  ## 自动记账 自动记账默认是关的,点击开启会直接弹出开通VIP的按钮。  这是算法助手抓 onclick 的,但实际看不到细节的方法调用 ``` androidx.constraintlayout.widget.ConstraintLayout 控件Id:7f0905b3 控件标题:自动记账 回调类:anp.kad.sdk.n2 返回结果类型:void 返回结果值:void 调用堆栈: at D.ADLdiqn.iTQ.djJXV.gw.XposedBridge$LegacyApiSupport.handleAfter(Unknown Source:33) at J.callback(Unknown Source:292) at LSPHooker_.performClick(Unknown Source:8) at android.view.View.performClickInternal(View.java:6576) at android.view.View.access$3100(View.java:780) at android.view.View$PerformClick.run(View.java:25899) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) 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 ``` 从这里可能看到回调类 = anp.kad.sdk.n2。搜索 7f0905b3 得到控件名 layout_switch 。触发的功能是"自动记账",最可能是 ActivityAutomateBinding。看一下谁在使用这个Binding  这个 this.f31135l = constraintLayout10; 这里默认点ctrl+鼠标左键 / 选中按x,都查不到,要有有意识地去查找这个值  然后就得找实际触发的方法了。 理论上通过封装好的什么方法都是用 setOnClickListener ,可以直接全局搜 f311351.setOnClickListener / setOnClickListener 再一个个去定位  这里找到对应的方法,但实际触发的是case 1检查悬浮窗,这很明显不是我们要找的。之前绑定事件是嵌套式的子父结构,算法助手hook的是默认父节点,constraintLayout10 继续往下是 constraintLayout11,对应的 f31136m,就是上面截图中 i2 = 1上面那个,对应的ViewOnClickListenerC2532n2方法,第一个参数是 8,看下case 8分支。  这里就开始判断分支了 m80f 方法进去判断等于,其实就是判断用户类型是否为 3,猜测就是判断用户是否为 VIP,第一个分支是不等于,第二个分支有 automateActivity 去调用方法,猜测第二个分支是判断用户为 VIP的情况。实在不行直接hook 返回true/false判断即可 ```javascript Java.perform(function(){ console.log("[*] 自动记账 VIP 绕过(仅 case 8)启动..."); try { var BaseActivity = Java.use("com.jxm.bill.anxin.base.BaseActivity"); // 这里直接用jadx的frida会报错cannot set property 'implementation' of undefined // 应该混淆后运行时真实方法名是 N,不是 JADX 显示的 m34835N,jadx里有 BaseActivity.N.implementation = function() { return "3"; }; console.log("[+] BaseActivity.N → 返回 '3'(VIP)"); } catch (e) { console.log("[!] Hook 失败:" + e); } console.log("[*] 脚本加载完成"); console.log("[!] 请在系统设置中手动开启:无障碍服务 + 悬浮窗权限"); } ```  # 修改smali代码 现在是hook可以,也知道要修改哪几个值,那就来修改静态 smali 代码,打包apk 这一块MT管理器确实不错,但这次我用电脑操作,反编译和打包 apk 用 apktool,签名用 apksigner。 ## 反编译、打包、签名 ``` # 1. 反编译 java -jar apktool.jar d -f xxx.apk -o xxx_decompiled # 2. 打包 java -jar apktool.jar b xxx_decompiled -o xxx_unsigned.apk # 3. 签名 java -jar apksigner.jar --apks xxx_unsigned.apk ``` ## 修改 1:AppInfos.getForceUpdate() → 返回 false 文件路径: smali/com/jxm/bill/anxin/bean/AppInfos.smali 搜索 getForceUpdate ,找到: ``` .method public final getForceUpdate()Ljava/lang/Boolean; .locals 1 iget-object v0, p0, Lcom/jxm/bill/anxin/bean/AppInfos;->forceUpdate:Ljava/lang/Boolean; return-object v0 .end method ``` 替换为: ``` .method public final getForceUpdate()Ljava/lang/Boolean; .locals 1 sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean; return-object v0 .end method ``` ## 修改 2:AiVipCount.getReceiveTrialTimes() → 返回 999999 文件路径: smali/com/jxm/bill/anxin/bean/AiVipCount.smali 搜索 getReceiveTrialTimes ,找到: ``` .method public final getReceiveTrialTimes()Ljava/lang/Integer; .locals 1 iget-object v0, p0, Lcom/jxm/bill/anxin/bean/AiVipCount;->receiveTrialTimes:Ljava/lang/Integer; return-object v0 .end method ``` 替换为: ``` .method public final getReceiveTrialTimes()Ljava/lang/Integer; .locals 1 const v0, 0xf423f invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; move-result-object v0 return-object v0 .end method ``` ## 修改 3:AiVipCount.getUsedTrialTimes() → 返回 0 搜索 getUsedTrialTimes ,找到: ``` .method public final getUsedTrialTimes()Ljava/lang/Integer; .locals 1 iget-object v0, p0, Lcom/jxm/bill/anxin/bean/AiVipCount;->usedTrialTimes:Ljava/lang/Integer; return-object v0 .end method ``` 替换为: ``` .method public final getUsedTrialTimes()Ljava/lang/Integer; .locals 1 const/4 v0, 0x0 invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; move-result-object v0 return-object v0 .end method ``` ## 修改 4:BaseActivity.N() → 返回 "3" 文件路径: smali/com/jxm/bill/anxin/base/BaseActivity.smali 注意 :smali 里方法名是 N ,不是 m34835N 。 搜索 .method public final N()Ljava/lang/String; ,找到类似: ``` .method public final N()Ljava/lang/String; .locals 2 iget-object v0, p0, Lcom/jxm/bill/anxin/base/BaseActivity;->G:Ljava/lang/String; if-eqz v0, :cond_0 return-object v0 :cond_0 const-string v0, "userType" invoke-static {v0}, Lanp/kad/sdk/AbstractC0942Bg;->N0(Ljava/lang/String;)V const/4 v0, 0x0 throw v0 .end method ``` 替换为: ``` .method public final N()Ljava/lang/String; .locals 1 const-string v0, "3" return-object v0 .end method ``` # 过签名校验 这里忘记截图了,签名打包后,直接安装启动提示 **"非法请求"**。基本上就是 APK 有做**签名校验**了,毕竟hook没问题重新打包就出问题了,代码中也搜不到提示的关键词,猜测是服务器端的签名校验,这里可以用MT的去签名校验试试,但默认只能过客户端校验的,思路也是修改 smali 代码绕过校验。 但如果是服务器端校验,就要找校验逻辑,看看哪里取的证书。 **混淆只能改类名方法名, 改不了 Android 系统 API 的名字 。系统 API 调用是代码的"指纹"。** ``` // 旧版 API(API < 28) PackageManager.getPackageInfo(pkg, PackageManager.GET_SIGNATURES) // flag = 0x40 = 64 packageInfo.signatures // Signature[] // 新版 API(API 28+) PackageManager.getPackageInfo(pkg, PackageManager.GET_SIGNING_CERTIFICATES) // flag = 0x2000000 = 134217728 packageInfo.signingInfo SigningInfo.getApkContentsSigners() // Signature[] SigningInfo.hasPastSigningCertificates() // 指纹计算 MessageDigest.getInstance("SHA-256" / "SHA-1" / "MD5") Signature.toByteArray() / toCharsString() ``` 所以这里直接搜 **getPackageInfo**  这里有个小技巧 ``` ✓ 要重点看的: C[数字][字母] → 混淆回调/工具类,可能含核心逻辑 AbstractC0[数字] → 混淆抽象工具类 [A-Z]开头无意义短名 → 混淆业务类 ✗ 可以跳过的: XxxActivity / XxxFragment → 页面入口(除非要找触发点) XxxAdapter → 列表适配器(UI,非核心) XxxBinding → ViewBinding(无业务逻辑) XxxProvider / XxxReceiver → 跨进程/广播(特定场景才看) *Helper / *Util / *Manager → 工具类(可能有用但优先级低) ``` 定位到了 anp.kad.sdk 的 AbstractC1830zg 方法 ProGuard/R8 有一条规则叫 `-repackageclasses` (或`-flattenpackagehierarchy` ),作用是把所有类的包名统一改成一个短包名。anp.kad.sdk 这种就是 ProGuard 混淆的产物,而混淆的往往是**业务代码**。  这个方法里有 getPackageInfo、SigningInfo.getApkContentsSigners() 、MessageDigest.getInstance,基本上确定是取签名指纹的方法 后续就开始找这个方法的实际调用,静态分析的话直接在 jadx 按 x,这里我使用 hook,因为这是大概是在 app 发起请求时触发的,不一定是通过常规的方法调用。静态方法不能直接查到反射调用、接口多态、动态代理的调用。 ```javascript Java.perform(function(){ var AbstractC3068zg = Java.use("anp.kad.sdk.zg"); // 1. 加上 overload(防御多重载) AbstractC3068zg.s.overload('android.content.ContextWrapper').implementation = function(contextWrapper) { console.log("s is called"); // 2. 用 .call(this, ...) 调原方法,避免递归 let result = this.s.overload('android.content.ContextWrapper').call(this, contextWrapper); // 3. Java 对象转字符串再打印 var set = Java.cast(result, Java.use("java.util.Set")); console.log("s result: " + set.toString()); // 4. 打调用栈(自己抛异常取栈) console.log(Java.use("android.util.Log").getStackTraceString( Java.use("java.lang.Exception").$new() )); return result; }; } ``` 返回的值是这样的 ``` java.lang.Exception at anp.kad.sdk.zg.s(Native Method) at com.xxx.xxx.xxx.App.onCreate(SourceFile:24) ... ```  看下 onCreate 方法,调用方法获取签名方法后,签名值放进本地 SharedPreferences 文件中,这个直接在手机 apk 路径就可以找到。对应的 key 为 app_sign,看下哪里代码里哪里获取了这个 key,最终在 HeaderInterceptor 找到了获取 app_sign,基本就可以推断是拦截器在每个请求之前加了 app_sign。 这里思路很多,直接去 hook 这个生成签名的 s 方法,读取本地的 SharedPreferences 文件,用 apktool 查看证书,甚至是抓个包看看。 这里我选择直接读取本地的 SharedPreferences 文件 ``` adb shell su -c "cat /data/data/com.xxx.xxxx.xxxxx/shared_prefs/phone_info.xml" | findstr app_sign ```  ``` keytool -printcert -jarfile xxxv2.1.16.apk ```  可以看到格式有差异但内容相同,以 phone_info.xml 文件里格式为准,**毕竟这是代码实际去读取的**。 修改思路是,修改 smali 代码,让获取签名的方法固定返回这个签名值,打包后安装,再去读取这个文件看是不是跟原apk一模一样,内容格式都要完全一样,过程中就遇到不少坑,两边多了[]或是中间多了:之类的。实际去读取就知道了。 开始修改 smali 代码获取签名的方法,就是上面那个我们通过 **getPackageInfo**定位到的 s 方法,jadx切换smali可以看实际方法名,通过文件路径在反编译的源码中定位方法进行修改 把整个方法体替换成: ``` .method public static final s(Landroid/content/ContextWrapper;)Ljava/util/Set; .locals 3 new-instance v0, Ljava/util/HashSet; invoke-direct {v0}, Ljava/util/HashSet;-><init>()V # 换成原始签名指纹(保留冒号,注意 smali 字符串里冒号无需转义) const-string v1, "25A26462F42FB0C1CF98A95828038D1CCD88C918B43098EC940280019CF25A3B" invoke-interface {v0, v1}, Ljava/util/Set;->add(Ljava/lang/Object;)Z return-object v0 .end method ``` 再去执行反编译、打包、签名即可。 # LSPosed 脚本 这里再补充下 LSPosed 脚本吧 ```java private static final String TARGET_PKG = "com.xxx.xxx.xxx"; @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { if (!TARGET_PKG.equals(lpparam.packageName)) { return; } ClassLoader cl = lpparam.classLoader; XposedBridge.log("[*] 安心记账 Hook 模块加载..."); // ============================================================ // ② 强制更新:AppInfos.getForceUpdate() 返回 false // Frida: AppInfos["getForceUpdate"].implementation = () => false // 无参方法 → findAndHookMethod 里不写任何参数类型 // ============================================================ XposedHelpers.findAndHookMethod( "com.jxm.bill.anxin.bean.AppInfos", cl, "getForceUpdate", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { XposedBridge.log("AppInfos.getForceUpdate is called → false"); // setResult 会直接替换返回值,原方法体不再执行 // 返回类型是 Boolean,传 false 会自动装箱成 Boolean.FALSE param.setResult(false); } }); // ============================================================ // ③ 聊天记账:AiVipCount 试用次数 // Frida: getReceiveTrialTimes → 999999; getUsedTrialTimes → 0 // ============================================================ XposedHelpers.findAndHookMethod( "com.jxm.bill.anxin.bean.AiVipCount", cl, "getReceiveTrialTimes", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { param.setResult(999999); // int 自动装箱为 Integer } }); XposedHelpers.findAndHookMethod( "com.jxm.bill.anxin.bean.AiVipCount", cl, "getUsedTrialTimes", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { param.setResult(0); } }); // ============================================================ // ④ 自动记账:BaseActivity.N() 返回 "3"(运行时真名,不是 m34835N) // ============================================================ XposedHelpers.findAndHookMethod( "com.jxm.bill.anxin.base.BaseActivity", cl, "N", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { XposedBridge.log("[+] BaseActivity.N → '3'"); param.setResult("3"); } }); // ============================================================ // ⑤ 签名 zg.s(ContextWrapper) —— 你的 Frida 只打印日志 // 有参方法:在回调前写上参数类型 ContextWrapper.class // beforeHookedMethod 不 setResult → 只观察,不改变行为 // ============================================================ XposedHelpers.findAndHookMethod( "anp.kad.sdk.zg", cl, "s", ContextWrapper.class, // ← 参数类型,用来匹配重载 new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { // param.args[0] 取第1个参数 XposedBridge.log("zg.s called, ctx=" + param.args[0]); } @Override protected void afterHookedMethod(MethodHookParam param) { // after 里 param.getResult() 是原方法返回值 XposedBridge.log("zg.s result=" + String.valueOf(param.getResult())); } }); XposedBridge.log("[*] 全部 Hook 安装完成"); } ``` # 补充 ## 事件绑定 科普下ID 事件绑定方式 常规绑定: ``` Activity.java ├─ View btn = findViewById(R.id.layout_switch); ← 直接在Activity里找控件 └─ btn.setOnClickListener(new OnClickListener() {...}); ``` ViewBinding方式: ``` 编译器生成 → ActivityAutomateBinding.java(只有控件引用,无逻辑) Activity.java ├─ binding.f31129f ← 通过Binding字段间接拿控件 └─ binding.f31129f.setOnClickListener(...) 搜索 layout_switch → 先搜到Binding类(只是控件声明) → 需要再搜Binding类名引用 → 才找到使用它的Activity → 多了一层间接 ``` 这里就搜索 ActivityAutomateBinding 的引用,返回了11个类: 其中有一个anp.kad.sdk.ViewOnClickListenerC2532n2,注意这个名字: ViewOnClickListenerC2532 + n2 = 这就是算法助手报告的回调类 anp.kad.sdk.n2。jadx对部分类方法会加字符,路径也会,比如算法助手里看到的是com.xxx.xxx.xxx.ui.方法,jadx里看的可能是com.xxx.xxx.xxx.p768ui.方法 总结, ``` 任何按钮点击定位(通用流程): 第1步:算法助手/布局工具 → 获取控件ID(十六进制如0x7F0905B3) 第2步:R类中搜十六进制值 → 找到资源名(如 layout_switch) 第3步:搜索资源名 → 得到N个结果,分两种情况: 情况A:结果中直接有Activity/Fragment(传统findViewById方式) └─ 直接打开Activity → 搜索资源名 → 找到findViewById → 往下看setOnClickListener 情况B:结果中只有Binding类(ViewBinding方式,这个app用的)← 多一步 ├─ 按类名/控件名判断选哪个Binding ├─ 搜索该Binding类名的引用 → 找到使用它的Activity/Fragment + 回调类 └─ 打开Activity → 搜索字段名(如f31129f)→ 找到setOnClickListener 第4步:setOnClickListener的第二个参数 = 点击回调类 第5步:打开回调类 → 看onClick → 找到业务逻辑 ``` 其实这就类似于某种框架的事件绑定使用方法,换做以前就得各种去翻文档,找到某个用法或特性,现在AI的知识广度可以弥补很好地弥补这一点。 ## 系统 API **混淆只能改类名方法名, 改不了 Android 系统 API 的名字 。系统 API 调用是代码的"指纹"。** | 功能 | API 指纹 | | -------- | ------------------------------------------------------------ | | 取签名 | getPackageInfo + GET_SIGNING_CERTIFICATES / 0x40 / 0x2000000 + MessageDigest | | 版本更新 | getPackageInfo + versionName / versionCode + PackageManager | | 网络请求 | OkHttpClient/HttpURLConnection/Retrofit/Request$Builder | | 加解密 | Cipher.getInstance/MessageDigest/Mac.getInstance/SecretKeySpec | | 存储 | SharedPreferences / MMKV / SQLiteDatabase / FileOutputStream | | 反射 | Class.forName / Method.invoke / getDeclaredMethod | | 动态加载 | DexClassLoader/PathClassLoader/loadClass | | 权限 | checkSelfPermission / requestPermissions / PackageManager |
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
Topdayplus
1
发帖
1
回帖
0
RANK
关注
私信
他的文章
[原创]某记账APP的永久VIP功能和签名绕过等
246
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部