Frida 动态 Hook 安卓 WebView 与第三方内核完全指南
使用了虚构的「DragonWeb 内核」作为示例,避免了任何真实厂商的包名和标识:
在移动应用安全分析、逆向工程和漏洞挖掘中,WebView 是一个极其重要的攻击面。无论是传统的系统 WebView 还是第三方内核,对其内部方法的监控都至关重要。本文将详细介绍如何使用 Frida 这一强大动态插桩工具来 Hook 系统 WebView 和第三方内核的关键方法。
一、环境准备
- 安装 Frida
bash
# 安装 Frida 命令行工具
pip install frida-tools
-
设备端配置
-
Root 设备或模拟器(推荐 Genymotion 或已 Root 的真机)
-
下载并推送 frida-server 到设备:
bash# 从 https://github.com/frida/frida/releases 下载对应版本 adb push frida-server /data/local/tmp/ adb shell su cd /data/local/tmp chmod 755 frida-server ./frida-server &
-
验证连接:
bashfrida-ps -U
二、WebView 类结构对比
功能 系统 WebView (Android) DragonWeb 内核 (第三方)
核心组件 android.webkit.WebView com.dragon.webkit.engine.WebView
事件处理 android.webkit.WebViewClient com.dragon.webkit.engine.WebViewClient
浏览器行为 android.webkit.WebChromeClient com.dragon.webkit.engine.WebChromeClient
JS 接口 @JavascriptInterface 同名注解
三、Frida Hook 脚本大全
- 通用 Hook 脚本(同时捕获系统和第三方内核)
javascript
// universal_webview_hook.js
Java.perform(function () {
console.log("[*] Starting Universal WebView Hook...");
// 要 Hook 的类列表
var targetClasses = [
// 系统 WebView 类
"android.webkit.WebView",
"android.webkit.WebViewClient",
"android.webkit.WebChromeClient",
// DragonWeb 内核类
"com.dragon.webkit.engine.WebView",
"com.dragon.webkit.engine.WebViewClient",
"com.dragon.webkit.engine.WebChromeClient",
"com.dragonwebkit.engine.WebView", // 可能的变体
"com.dragonwebkit.engine.WebViewClient"
];
targetClasses.forEach(function(className) {
try {
var targetClass = Java.use(className);
// Hook loadUrl 方法
if ('loadUrl' in targetClass) {
targetClass.loadUrl.overload('java.lang.String').implementation = function(url) {
console.log("\n[🌐] " + className + ".loadUrl() called");
console.log("[📝] URL: " + url);
return this.loadUrl(url);
};
}
// Hook WebViewClient 方法
if ('onPageStarted' in targetClass) {
targetClass.onPageStarted.implementation = function(view, url, favicon) {
console.log("\n[▶️] " + className + ".onPageStarted: " + url);
return this.onPageStarted(view, url, favicon);
};
}
} catch (e) {
// 类不存在是正常的,静默跳过
}
});
});
- 专用第三方内核 Hook 脚本
javascript
// thirdparty_webview_hook.js
Java.perform(function () {
console.log("[*] Starting Third-party WebView Hook...");
// DragonWeb 内核类
var DragonWebView = Java.use("com.dragon.webkit.engine.WebView");
var DragonWebViewClient = Java.use("com.dragon.webkit.engine.WebViewClient");
var DragonWebChromeClient = Java.use("com.dragon.webkit.engine.WebChromeClient");
// Hook loadUrl
DragonWebView.loadUrl.overload('java.lang.String').implementation = function(url) {
console.log("\n[🎯DragonWeb] WebView.loadUrl() called!");
console.log("[📝] URL: " + url);
console.log("[🔍] Call Stack: " +
Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Exception").$new()));
return this.loadUrl(url);
};
// Hook 页面事件
DragonWebViewClient.onPageStarted.implementation = function(view, url, favicon) {
console.log("\n[🌐DragonWeb] onPageStarted: " + url);
return this.onPageStarted(view, url, favicon);
};
DragonWebViewClient.onPageFinished.implementation = function(view, url) {
console.log("\n[✅DragonWeb] onPageFinished: " + url);
return this.onPageFinished(view, url);
};
// Hook URL 拦截
DragonWebViewClient.shouldOverrideUrlLoading.overload(
'com.dragon.webkit.engine.WebView',
'com.dragon.webkit.engine.model.WebResourceRequest'
).implementation = function(view, request) {
var url = request.getUrl().toString();
console.log("\n[🛑DragonWeb] shouldOverrideUrlLoading: " + url);
return false; // 让内核处理请求
};
// Hook Console 日志
DragonWebChromeClient.onConsoleMessage.overload(
'com.dragon.webkit.engine.model.ConsoleMessage'
).implementation = function(consoleMessage) {
var msg = consoleMessage.message();
console.log("\n[💬DragonWeb Console] > " + msg);
return this.onConsoleMessage(consoleMessage);
};
});
- JSBridge 接口监控脚本
javascript
// jsbridge_hook.js
Java.perform(function () {
console.log("[*] Starting JSBridge Hook...");
var classList = [
"android.webkit.WebView",
"com.dragon.webkit.engine.WebView",
"com.dragonwebkit.engine.WebView",
"com.custom.webengine.WebView" // 其他可能的包名
];
classList.forEach(function(className) {
try {
var WebViewClass = Java.use(className);
WebViewClass.addJavascriptInterface.overload(
'java.lang.Object', 'java.lang.String'
).implementation = function(obj, interfaceName) {
console.log("\n[🤝JSBridge] " + className + ".addJavascriptInterface");
console.log("[📝] Interface Name: " + interfaceName);
console.log("[📝] Object Class: " + obj.$className);
// 枚举所有可调用方法
try {
var clazz = obj.getClass ? obj.getClass() : obj.$class;
var methods = clazz.getDeclaredMethods();
console.log("[🔧] Exposed Methods:");
for (var i = 0; i < methods.length; i++) {
console.log(" - " + methods[i].getName());
}
} catch (e) {
console.log("[❌] Error enumerating methods: " + e.message);
}
return this.addJavascriptInterface(obj, interfaceName);
};
} catch (e) {
// 静默处理类不存在的情况
}
});
});
四、自动识别第三方内核
javascript
// detect_webview_engine.js
Java.perform(function () {
console.log("[*] Detecting WebView Engines...");
var thirdPartyIndicators = [
"dragon", "webkit", "xengine", "uc", "quark",
"crosswalk", "gecko", "blink", "custom", "webengine"
];
Java.enumerateLoadedClasses({
onMatch: function(className) {
// 检测系统 WebView
if (className.includes("android.webkit")) {
console.log("[✓] System WebView detected: " + className);
}
// 检测第三方内核
thirdPartyIndicators.forEach(function(indicator) {
if (className.toLowerCase().includes(indicator) &&
className.includes("web") &&
!className.includes("android")) {
console.log("[🎯] Third-party WebView detected: " + className);
}
});
},
onComplete: function() {
console.log("[*] WebView detection completed");
}
});
});
五、使用方法
- 启动时注入
bash
# 检测使用的内核
frida -U -f com.target.app -l detect_webview_engine.js --no-pause
# 通用 Hook
frida -U -f com.target.app -l universal_webview_hook.js --no-pause
# 专用第三方内核 Hook
frida -U -f com.target.app -l thirdparty_webview_hook.js --no-pause
# JSBridge 监控
frida -U -f com.target.app -l jsbridge_hook.js --no-pause
- 附加到运行中进程
bash
frida -U com.target.app -l universal_webview_hook.js
六、实战技巧
- 动态扩展检测列表
如果发现新的第三方内核,可以动态添加到检测列表中:
javascript
// 在 detect_webview_engine.js 中添加
var additionalEngines = ["neweb", "fastweb", "smartweb"];
thirdPartyIndicators = thirdPartyIndicators.concat(additionalEngines);
- 过滤特定 URL
javascript
// 在 Hook 方法中添加过滤逻辑
var sensitiveKeywords = ["login", "auth", "token", "password"];
DragonWebViewClient.shouldOverrideUrlLoading.implementation = function(view, request) {
var url = request.getUrl().toString();
if (sensitiveKeywords.some(keyword => url.toLowerCase().includes(keyword))) {
console.log("\n[🔒敏感请求] " + url);
// 进行深度分析...
}
return false;
};
七、常见问题排查
- 类找不到错误:正常现象,使用 try-catch 静默处理
- 方法签名变化:不同版本内核的方法签名可能不同
- 多版本兼容:使用 overload() 明确指定参数类型
- 性能优化:避免在频繁调用的方法中执行复杂操作
八、总结
通过 Frida 动态 Hook WebView,安全研究人员可以:
· 监控所有页面加载行为
· 捕获 JavaScript 与原生代码交互
· 分析 JSBridge 暴露的攻击面
· 记录 Console 日志和错误信息
· 发现 URL 跳转漏洞和协议处理问题
无论是系统 WebView 还是第三方内核,Frida 都能提供强大的动态分析能力,是移动应用安全测试中不可或缺的工具。
免责声明:本文仅用于安全研究和学习目的。请勿在未授权的情况下对任何应用进行测试。所有示例中的包名和类名均为虚构,如有雷同,纯属巧合。