小柏实战学习安卓图文教程-第十二课-Frida动态脱壳1

本节课主题:实战演示内存扫描脱壳CrackMe.apk

一、实战脱壳(上节课失败了,这次再来,Fighting👊再一决✊)

内存扫描搜索DEX:

先查询我们要找的PID 是哪一个?

frida-ps -U

image

这次我们发现两个进程,因为这次我注册了,脚本账号,只不过没有充钱登录不上去;

2341 com.liushi.nz
2560 球球英雄脚本

image

image

image

 (记得第十课讲过了,雷电模拟器的frida 服务自己记得启动,这里不赘述了)

上节课我们看了一下 2560  , 这次先快速看一下2341,看一下是不是一样的问题:

frida-dexdump -U -p 2341 -d

dump下来了:

image

GDA快速试一下: (也不行)

image

 

 

 

 好了,现在不应该再在各种静态反编译工具上浪费时间了,应该动态分析了;

 

一、动态分析

1:放弃“阅读源码”,改为“监听行为”

忘掉类名和方法名。直接监听它做了什么

运行下面这个脚本,它会监听应用的启动、Activity跳转和网络请求:

先创建这个脚本

// monitor_basic.js
Java.perform(function() {
    console.log("[*] 修复版悬浮窗监控脚本启动");

    // 1. 修复无障碍服务监控 - 监控具体的无障碍服务实现
    try {
        // 查找当前运行的无障碍服务并Hook
        Java.choose("android.accessibilityservice.AccessibilityService", {
            onMatch: function(instance) {
                console.log("[ACCESSIBILITY] 找到无障碍服务实例: " + instance.getClass().getName());
                
                // Hook 具体的onAccessibilityEvent实现
                var clazz = Java.use(instance.getClass().getName());
                if (clazz.onAccessibilityEvent) {
                    clazz.onAccessibilityEvent.implementation = function(event) {
                        var eventType = event.getEventType();
                        var className = event.getClassName();
                        console.log("[ACCESSIBILITY] 事件类型: " + eventType + ", 组件: " + className);
                        return this.onAccessibilityEvent(event);
                    };
                }
            },
            onComplete: function() {
                console.log("[ACCESSIBILITY] 无障碍服务扫描完成");
            }
        });
    } catch (e) {
        console.log("[!] 无障碍服务Hook失败: " + e.message);
    }

    // 2. 修复窗口管理器监控 - 使用正确的实现类
    try {
        var WindowManagerImpl = Java.use("android.view.WindowManagerImpl");
        // 指定addView方法的重载
        WindowManagerImpl.addView.overload('android.view.View', 'android.view.ViewGroup$LayoutParams').implementation = function(view, params) {
            console.log("[WINDOW] 添加视图: " + view.getClass().getName());
            return this.addView(view, params);
        };
    } catch (e) {
        console.log("[!] WindowManagerImpl Hook失败,尝试其他实现: " + e.message);
        
        // 备用方案:Hook View的添加
        try {
            var ViewGroup = Java.use("android.view.ViewGroup");
            ViewGroup.addView.overload('android.view.View', 'android.view.ViewGroup$LayoutParams').implementation = function(view, params) {
                // 过滤悬浮窗相关的视图添加
                if (view.getClass().getName().toLowerCase().indexOf("window") !== -1 || 
                    view.getClass().getName().toLowerCase().indexOf("float") !== -1) {
                    console.log("[WINDOW] 可能添加悬浮窗: " + view.getClass().getName());
                }
                return this.addView(view, params);
            };
        } catch (e2) {
            console.log("[!] ViewGroup Hook也失败: " + e2.message);
        }
    }

    // 3. 修复输入框监控 - 更安全的方法
    try {
        var EditText = Java.use("android.widget.EditText");
        // 指定setOnClickListener的重载
        EditText.setOnClickListener.overload('android.view.View$OnClickListener').implementation = function(listener) {
            console.log("[UI] 输入框设置点击监听器: " + this.toString().substring(0, 100));
            // 调用原始方法
            return this.setOnClickListener(listener);
        };
    } catch (e) {
        console.log("[!] 输入框Hook失败: " + e.message);
    }

    // 4. 修复Activity监控 - 指定onCreate的重载
    try {
        var Activity = Java.use("android.app.Activity");
        // 使用单参数版本的重载(更常见)
        Activity.onCreate.overload('android.os.Bundle').implementation = function(bundle) {
            console.log("[ACTIVITY] 启动: " + this.getClass().getName());
            return this.onCreate(bundle);
        };
    } catch (e) {
        console.log("[!] Activity.onCreate Hook失败: " + e.message);
    }

    // 5. 修复按键事件监控
    try {
        var View = Java.use("android.view.View");
        // 指定dispatchKeyEvent的重载
        View.dispatchKeyEvent.overload('android.view.KeyEvent').implementation = function(event) {
            var keyCode = event.getKeyCode();
            if (keyCode === 4 || keyCode === 3) { // BACK键或HOME键
                console.log("[KEY] 按键事件: keyCode=" + keyCode + ", 在: " + this.getClass().getName());
            }
            return this.dispatchKeyEvent(event);
        };
    } catch (e) {
        console.log("[!] View.dispatchKeyEvent Hook失败: " + e.message);
    }

    console.log("[*] 修复版监控脚本注入完成");
});

 

再运行以下命令

frida -U -p 2341 -l monitor_basic.js

结果如下: 点一下登录,app 下方弹了一个悬浮气泡: 会员已到期; 监听到layout

image

 很明显下一步我们要 精准定位验证逻辑

监控Toast和对话框创建,创建如下脚本

// login_monitor.js
Java.perform(function() {
    console.log("[*] 登录验证专项监控启动");
    
    // 1. 监控Toast显示("会员已到期"很可能是Toast)
    var Toast = Java.use("android.widget.Toast");
    Toast.makeText.overload('android.content.Context', 'java.lang.CharSequence', 'int').implementation = function(context, text, duration) {
        var message = text.toString();
        console.log("[TOAST] 显示提示: " + message);
        
        // 打印调用栈,找到是谁调用了这个Toast
        console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
        
        return this.makeText(context, text, duration);
    };
    
    // 2. 监控按钮点击事件
    var Button = Java.use("android.widget.Button");
    Button.performClick.implementation = function() {
        var buttonText = this.getText();
        console.log("[BUTTON] 按钮被点击: " + buttonText);
        
        // 如果是"登录"按钮
        if (buttonText.toString().indexOf("登录") !== -1) {
            console.log("[!!!] 登录按钮被点击!");
            // 打印调用栈,找到登录处理逻辑
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
        }
        
        return this.performClick();
    };
    
    // 3. 监控网络请求(登录验证可能调用API)
    var HttpURLConnection = Java.use("com.android.okhttp.internal.huc.HttpURLConnectionImpl");
    HttpURLConnection.getInputStream.implementation = function() {
        var url = this.getURL().toString();
        console.log("[NETWORK] 请求URL: " + url);
        return this.getInputStream();
    };
    
    // 4. 查找包含"会员"、"到期"、"vip"等关键词的类
    Java.enumerateLoadedClasses({
        onMatch: function(className) {
            if (className.toLowerCase().indexOf("vip") !== -1 || 
                className.toLowerCase().indexOf("member") !== -1 ||
                className.indexOf("到期") !== -1 ||
                className.indexOf("会员") !== -1) {
                console.log("[CLASS] 发现相关类: " + className);
            }
        },
        onComplete: function() {}
    });
});

image

 

上一个脚本的监听先停一下:

先Ctrl+c 在输入下面的退出命令

exit

image

 重新执行 登录的监听:

frida -U -p 2341 -l login_monitor.js

结果如下:

image

再重新按一下登录按钮: (哦,发现报错了)

image

在完善一下脚本,再重新执行一下下面这个脚本

// expanded_monitor.js
Java.perform(function() {
    console.log("[*] 扩大监控范围脚本启动");
    
    // 1. 监控所有View的点击事件(不仅仅是Button)
    try {
        var View = Java.use("android.view.View");
        View.performClick.implementation = function() {
            var viewClass = this.getClass().getName();
            var viewId = this.getId();
            
            // 尝试获取资源ID名称
            try {
                var resources = this.getResources();
                var entryName = resources.getResourceEntryName(viewId);
                console.log("[CLICK] 视图被点击: " + viewClass + " ID: " + entryName);
            } catch(e) {
                console.log("[CLICK] 视图被点击: " + viewClass + " ID: 0x" + viewId.toString(16));
            }
            
            // 如果是登录相关视图
            if (viewClass.toLowerCase().indexOf("login") !== -1 || 
                viewClass.toLowerCase().indexOf("sign") !== -1) {
                console.log("[!!!] 可能登录相关视图被点击!");
            }
            
            return this.performClick();
        };
        console.log("[+] View点击监控就绪");
    } catch(e) {
        console.log("[-] View点击监控失败: " + e.message);
    }
    
    // 2. 监控触摸事件(备用方案)
    try {
        var View = Java.use("android.view.View");
        View.onTouchEvent.implementation = function(event) {
            var action = event.getAction();
            if (action === 1) { // ACTION_UP (手指抬起)
                var viewClass = this.getClass().getName();
                console.log("[TOUCH] 触摸事件: " + viewClass);
            }
            return this.onTouchEvent(event);
        };
        console.log("[+] 触摸事件监控就绪");
    } catch(e) {
        console.log("[-] 触摸事件监控失败: " + e.message);
    }
    
    // 3. 监控对话框和弹出窗口
    try {
        var Dialog = Java.use("android.app.Dialog");
        Dialog.show.implementation = function() {
            console.log("[DIALOG] 显示对话框: " + this.getClass().getName());
            return this.show();
        };
        console.log("[+] 对话框监控就绪");
    } catch(e) {
        console.log("[-] 对话框监控失败: " + e.message);
    }
    
    // 4. 监控所有Activity生命周期
    try {
        var Activity = Java.use("android.app.Activity");
        var methods = ["onCreate", "onResume", "onPause", "onDestroy"];
        
        methods.forEach(function(methodName) {
            if (Activity[methodName]) {
                Activity[methodName].overloads.forEach(function(overload, index) {
                    overload.implementation = function() {
                        console.log("[ACTIVITY] " + methodName + ": " + this.getClass().getName());
                        return overload.apply(this, arguments);
                    };
                });
            }
        });
        console.log("[+] Activity生命周期监控就绪");
    } catch(e) {
        console.log("[-] Activity监控失败: " + e.message);
    }
    
    console.log("[*] 扩大监控脚本注入完成");
});

点一下登录按钮

image

当前情况分析

成功捕获:脚本检测到了TextView的点击事件

⚠️ 但缺少关键信息:没有看到登录验证逻辑的执行,也没有看到"会员已到期"的Toast提示

下一步行动:深入追踪验证逻辑

我们需要找到为什么点击事件被捕获了,但登录验证逻辑没有被触发。

方案一:增强Toast监控(最直接) (创建脚本并执行这个脚本,不赘述了,和上面的流程一样)

// toast_focused.js
Java.perform(function() {
    console.log("[*] Toast专项监控启动");
    
    // 1. 精准监控Toast显示
    var Toast = Java.use("android.widget.Toast");
    var showOverloads = Toast.show.overloads;
    
    showOverloads.forEach(function(overload, index) {
        overload.implementation = function() {
            // 获取Toast的文本内容
            try {
                var view = this.getView();
                if (view) {
                    var textView = view.findViewById(0x102000b); // android.R.id.message
                    if (textView) {
                        var message = textView.getText().toString();
                        console.log("[TOAST!!!] 显示内容: " + message);
                        
                        // 如果是会员相关提示,打印完整调用栈
                        if (message.indexOf("会员") !== -1 || message.indexOf("到期") !== -1) {
                            console.log("[!!!] 发现会员验证Toast,追踪调用栈:");
                            Java.perform(function() {
                                var Thread = Java.use("java.lang.Thread");
                                var stackTrace = Thread.currentThread().getStackTrace();
                                for (var i = 0; i < Math.min(stackTrace.length, 12); i++) {
                                    var stackElement = stackTrace[i].toString();
                                    // 过滤掉系统类,只显示应用相关调用
                                    if (stackElement.indexOf("android.") === -1 && 
                                        stackElement.indexOf("java.") === -1 &&
                                        stackElement.indexOf("com.android.") === -1) {
                                        console.log("  [" + i + "] " + stackElement);
                                    }
                                }
                            });
                        }
                    }
                }
            } catch(e) {
                // 备用方案:通过反射获取文本
                try {
                    var mNextView = this.getClass().getDeclaredField("mNextView");
                    mNextView.setAccessible(true);
                    var view = mNextView.get(this);
                    if (view) {
                        var textView = view.findViewById(0x102000b);
                        if (textView) {
                            var message = textView.getText().toString();
                            console.log("[TOAST-REFLECT] 内容: " + message);
                        }
                    }
                } catch(e2) {}
            }
            
            return overload.apply(this, arguments);
        };
    });
    
    // 2. 监控可能触发Toast的方法
    var Log = Java.use("android.util.Log");
    Log.w.overload('java.lang.String', 'java.lang.String').implementation = function(tag, msg) {
        if (msg.indexOf("会员") !== -1 || msg.indexOf("到期") !== -1) {
            console.log("[LOG-W] 警告日志 - Tag: " + tag + ", Msg: " + msg);
        }
        return this.w(tag, msg);
    };
    
    console.log("[*] Toast监控就绪");
});

执行后结果如下:

image

问题分析

从我们之前的监控结果看,点击事件被捕获了([CLICK] 视图被点击: android.widget.TextView),但没有Toast输出,这说明:

  1. 提示可能不是标准Toast:应用可能使用了自定义对话框、PopupWindow、Snackbar或其他UI组件

  2. 提示可能来自WebView或混合开发:如果是Web内容,不会触发原生Toast

  3. 可能有反检测机制:应用可能检测到Frida后避免了标准API调用

解决方案:扩大UI监控范围

以下是增强版脚本,监控所有可能的提示显示方式:

 创建脚本

// enhanced_ui_monitor.js
Java.perform(function() {
    console.log("[*] 增强版UI监控启动 - 覆盖所有提示类型");

    // 1. 监控Dialog(对话框)
    try {
        var Dialog = Java.use("android.app.Dialog");
        Dialog.show.implementation = function() {
            console.log("[DIALOG] 显示对话框: " + this.getClass().getName());
            
            // 尝试获取对话框内容
            try {
                var window = this.getWindow();
                if (window) {
                    var decorView = window.getDecorView();
                    scanViewText(decorView, "DIALOG");
                }
            } catch(e) {}
            
            return this.show();
        };
        console.log("[+] Dialog监控就绪");
    } catch(e) {
        console.log("[-] Dialog监控失败: " + e.message);
    }

    // 2. 监控PopupWindow(弹出窗口)
    try {
        var PopupWindow = Java.use("android.widget.PopupWindow");
        PopupWindow.showAtLocation.overload('android.view.View', 'int', 'int', 'int').implementation = function(parent, gravity, x, y) {
            console.log("[POPUP] 显示弹出窗口: " + this.getClass().getName());
            return this.showAtLocation(parent, gravity, x, y);
        };
        console.log("[+] PopupWindow监控就绪");
    } catch(e) {
        console.log("[-] PopupWindow监控失败: " + e.message);
    }

    // 3. 监控Snackbar(Material Design提示)
    try {
        var Snackbar = Java.use("com.google.android.material.snackbar.Snackbar");
        Snackbar.show.implementation = function() {
            console.log("[SNACKBAR] 显示Snackbar提示");
            return this.show();
        };
        console.log("[+] Snackbar监控就绪");
    } catch(e) {
        console.log("[-] Snackbar监控失败(可能未使用): " + e.message);
    }

    // 4. 监控TextView文本变化(动态内容更新)
    try {
        var TextView = Java.use("android.widget.TextView");
        TextView.setText.overload('java.lang.CharSequence').implementation = function(text) {
            var textStr = text.toString();
            if (textStr.indexOf("会员") !== -1 || textStr.indexOf("到期") !== -1) {
                console.log("[!!!] TextView设置会员相关文本: " + textStr);
                // 打印调用栈找到来源
                printFilteredStackTrace();
            }
            return this.setText(text);
        };
        console.log("[+] TextView文本监控就绪");
    } catch(e) {
        console.log("[-] TextView监控失败: " + e.message);
    }

    // 5. 监控WebView(如果提示来自网页)
    try {
        var WebView = Java.use("android.webkit.WebView");
        WebView.loadUrl.overload('java.lang.String').implementation = function(url) {
            if (url.indexOf("http") !== -1) {
                console.log("[WEBVIEW] 加载URL: " + url);
            }
            return this.loadUrl(url);
        };
        console.log("[+] WebView监控就绪");
    } catch(e) {
        console.log("[-] WebView监控失败: " + e.message);
    }

    // 6. 保留原有的Toast监控(备用)
    try {
        var Toast = Java.use("android.widget.Toast");
        var showOverloads = Toast.show.overloads;
        showOverloads.forEach(function(overload) {
            overload.implementation = function() {
                console.log("[TOAST] 显示Toast");
                return overload.apply(this, arguments);
            };
        });
        console.log("[+] Toast监控就绪");
    } catch(e) {
        console.log("[-] Toast监控失败: " + e.message);
    }

    console.log("[*] 增强版UI监控注入完成");
});

// 辅助函数:扫描View中的文本内容
function scanViewText(view, prefix) {
    try {
        if (view.getClass().getName().indexOf("TextView") !== -1) {
            var text = view.getText().toString();
            if (text.length > 0) {
                console.log("[" + prefix + "] 发现文本: " + text);
                
                if (text.indexOf("会员") !== -1 || text.indexOf("到期") !== -1) {
                    console.log("[!!!] 发现会员相关提示: " + text);
                    printFilteredStackTrace();
                }
            }
        }
        
        // 如果是ViewGroup,递归扫描子View
        if (view.getClass().getName().indexOf("ViewGroup") !== -1) {
            var childCount = view.getChildCount();
            for (var i = 0; i < childCount; i++) {
                try {
                    var child = view.getChildAt(i);
                    scanViewText(child, prefix);
                } catch(e) {}
            }
        }
    } catch(e) {}
}

// 辅助函数:打印过滤后的调用栈(只显示应用相关)
function printFilteredStackTrace() {
    Java.perform(function() {
        var Thread = Java.use("java.lang.Thread");
        var stackTrace = Thread.currentThread().getStackTrace();
        
        console.log("[STACK] 调用栈追踪:");
        for (var i = 0; i < Math.min(stackTrace.length, 15); i++) {
            var stackElement = stackTrace[i].toString();
            // 过滤掉系统框架调用,只显示应用相关
            if (stackElement.indexOf("android.") === -1 && 
                stackElement.indexOf("java.") === -1 &&
                stackElement.indexOf("com.android.") === -1 &&
                stackElement.indexOf("dalvik.") === -1 &&
                stackElement.indexOf("libcore.") === -1) {
                console.log("  [" + i + "] " + stackElement);
            }
        }
    });
}

执行:

frida -U -p 2341 -l enhanced_ui_monitor.js

结果:

image

于成功捕获到了关键信息:"会员已到期"的提示是通过TextView设置的,调用栈指向com.nx.assist.ToastUtil

这是一个重大突破!

当前情况分析

从调用栈可以看出:

  • 提示来源com.nx.assist.ToastUtil(Toast工具类)

  • 调用路径ILilIL1IiiiI1i丨I.run(可能是异步任务,代码被混淆了)

  • 关键信息:提示显示在ToastUtil.java的第21行左右

 

关键信息:

  • 真实类名com.nx.assist.ILL丨Ii(混淆后的)

  • 方法名ILilIL1Iiirun

  • 源文件ToastUtil.java(但类名已被混淆)

 

 基于这个发现,我们就需要精准hook了:

创建专门的Hook脚本,针对ToastUtil进行监控:

// fixed_toast_util_hook.js
Java.perform(function() {
    console.log("[*] 修正版ToastUtil Hook脚本启动");
    
    // 1. 使用正确的混淆类名
    try {
        var ToastUtilClass = Java.use("com.nx.assist.ILL丨Ii");
        console.log("[+] 成功加载混淆后的ToastUtil类: com.nx.assist.ILL丨Ii");
        
        // 获取类的所有方法
        var methods = ToastUtilClass.class.getDeclaredMethods();
        console.log("[*] 类方法列表:");
        
        methods.forEach(function(method) {
            var methodName = method.getName();
            var parameterTypes = method.getParameterTypes();
            var params = [];
            
            for (var i = 0; i < parameterTypes.length; i++) {
                params.push(parameterTypes[i].getSimpleName());
            }
            
            console.log(" - " + methodName + "(" + params.join(", ") + ")");
            
            // Hook所有可能显示Toast的方法(基于方法名特征)
            if (methodName.toLowerCase().indexOf("show") !== -1 || 
                methodName.toLowerCase().indexOf("toast") !== -1 ||
                methodName.length <= 5) { // 混淆方法通常名字很短
                
                try {
                    // 获取方法的所有重载
                    var overloads = ToastUtilClass[methodName].overloads;
                    overloads.forEach(function(overload, index) {
                        overload.implementation = function() {
                            console.log("[!!!] ToastUtil." + methodName + " 被调用!");
                            
                            // 打印参数
                            for (var i = 0; i < arguments.length; i++) {
                                try {
                                    var argValue = arguments[i] ? arguments[i].toString() : "null";
                                    if (argValue.length > 100) argValue = argValue.substring(0, 100) + "...";
                                    console.log("   参数" + i + ": " + argValue);
                                } catch(e) {
                                    console.log("   参数" + i + ": [无法转换]");
                                }
                            }
                            
                            // 调用原始方法
                            var result = overload.apply(this, arguments);
                            console.log("   返回: " + result);
                            return result;
                        };
                    });
                    console.log("[+] 已Hook方法: " + methodName);
                } catch(e) {
                    console.log("[-] Hook方法失败: " + methodName + " - " + e.message);
                }
            }
        });
        
    } catch(e) {
        console.log("[-] 直接Hook失败: " + e.message);
        // 备用方案:动态查找
        dynamicFindToastClass();
    }
    
    // 2. 也尝试另一个可能的类
    try {
        var AnotherToastClass = Java.use("com.nx.assist.iI1i丨I");
        console.log("[+] 成功加载另一个Toast相关类: com.nx.assist.iI1i丨I");
        
        // Hook run方法(从调用栈中看到)
        if (AnotherToastClass.run) {
            AnotherToastClass.run.implementation = function() {
                console.log("[!!!] iI1i丨I.run 被调用");
                return this.run();
            };
        }
    } catch(e) {
        console.log("[-] 第二个类Hook失败: " + e.message);
    }
});

// 动态查找Toast相关类
function dynamicFindToastClass() {
    console.log("[*] 开始动态查找Toast相关类...");
    
    Java.enumerateLoadedClasses({
        onMatch: function(className) {
            // 查找包含关键字的类
            if (className.indexOf("com.nx.assist") !== -1) {
                console.log("[CLASS] 发现辅助类: " + className);
                
                // 如果类名包含Toast相关关键词
                if (className.toLowerCase().indexOf("toast") !== -1 || 
                    className.toLowerCase().indexOf("util") !== -1) {
                    console.log("[!!!] 可能找到ToastUtil类: " + className);
                    
                    try {
                        var clazz = Java.use(className);
                        var methods = clazz.class.getDeclaredMethods();
                        
                        methods.forEach(function(method) {
                            var methodName = method.getName();
                            if (methodName.toLowerCase().indexOf("show") !== -1) {
                                console.log("  [METHOD] 可能显示方法: " + methodName);
                            }
                        });
                    } catch(e) {}
                }
            }
        },
        onComplete: function() {
            console.log("[*] 动态查找完成");
        }
    });
}

 

运行这个脚本:

frida -U -p 2341 -l toast_util_hook.js

结果如下:

image

 点一下登录按钮,监听结果如下:

image

记住可读性差,不是不可读,耐心慢慢读就行了;

完整的调用流程分析如下:

1. [!!!] iI1i丨I.run 被调用           ← 异步任务启动
2. [!!!] ToastUtil.ILil 被调用         ← 显示Toast
   参数0: 会员已到期                   ← 验证失败提示
   参数1: null
   参数2: null  
   参数3: 22                          ← 显示时长(22毫秒)
3. [!!!] ToastUtil.ILil 被调用         ← 创建UI组件
   参数0: com.nx.assist.ILL丨Ii@fa46306
   返回: android.widget.RelativeLayout
4. [!!!] ToastUtil.I1I 被调用         ← 其他UI操作

绕过会员验证

直接修改Toast内容(最直接,不过可能没有用)

// bypass_toast.js
Java.perform(function() {
    console.log("[*] 开始绕过会员验证 - Toast内容修改方案");
    
    // Hook ToastUtil.ILil 方法(显示Toast的核心方法)
    var ToastUtil = Java.use("com.nx.assist.ILL丨Ii");
    
    // 针对显示文本的ILil重载(参数为String, int, int, int)
    ToastUtil.ILil.overload('java.lang.String', 'int', 'int', 'int').implementation = function(message, duration, x, y) {
        console.log("[TOAST] 原始消息: " + message);
        
        // 如果检测到会员到期提示,修改为成功消息
        if (message.indexOf("会员已到期") !== -1 || message.indexOf("会员") !== -1) {
            console.log("[!!!] 检测到会员验证失败,修改提示为成功!");
            message = "登录成功!"; // 修改提示内容
            // 或者完全禁止显示:return; 
        }
        
        return this.ILil(message, duration, x, y);
    };
    
    console.log("[+] Toast内容修改Hook就绪");
});

执行脚本:

frida -U -p 2341 -l bypass_toast.js

结果:文言描述改为我们想要的了,但是功能还是无法使用

image

结论: 说明仅仅修改Toast提示是不够的,我们需要绕过实际的验证逻辑

 

想一想,这种脚本大概率都是网络验证,所以我们有以下思路跳过验证:

1.Hook验证逻辑的源头,跳过验证逻辑,直接返回成功;

2.各种验证逻辑类:auth,valid,verify,check等 里面的验证方法强制修改成 true , 1 success

3.网络请求拦截: 如果发现验证请求,要么直接监控这个请求,并修改请求后的参数; 要么找到他的请求url本地修改host替换一个假的在线服务;

 

那就耐心点,一个个尝试呗!

本节课,因篇幅有限,先到这里,下节课继续,(我上个厕所0.o);

 

二、常见问题与解决方案 (没问题,可跳过)

 

 

注意:所有技术仅用于学习和安全研究,请遵守相关法律法规

 

posted on 2026-01-19 16:27  shaun88  阅读(3)  评论(0)    收藏  举报

导航