如何用Frida hook基于Electron开发的Linux桌面应用FreeTube
背景
我正在学习Frida,已经完成不少Android端应用hook的教程练习,觉得Frida是款强大的工具。现在需要hook基于Electron开发的Linux桌面应用FreeTube里ft-input.js文件中的handleClick函数,函数代码如下:
handleClick: function (e) { // No action if no input text if (!this.inputDataPresent) { return } this.searchState.showOptions = false this.searchState.selectedOption = -1 this.searchState.keyboardSelectedOptionIndex = -1 this.removeButtonSelectedIndex = -1 this.$emit('input', this.inputData) this.$emit('click', this.inputData, { event: e }) },
但网上Electron应用hook的教程很少,Frida官方文档信息也有限。我尝试向FreeTube的渲染进程注入以下hook代码:
// hook.js if (typeof document !== "undefined") { console.log("[Frida] Attached to renderer process"); } else { console.log("[Frida] Attached to main process or invalid context"); }
结果无法获取渲染进程上下文,document未定义,因此有以下疑问:
疑问
- Frida能否hook基于Electron的PC应用?是否支持hook JavaScript?若不支持JS,是否也不支持Python?
- 若支持hook JavaScript,应在哪个层级进行?渲染进程、V8引擎还是其他位置?
- 恳请提供针对Electron应用的hook代码示例。
另外我找到一个Frida的issue,但里面没提到Python版hook的使用方法。我是Frida、JavaScript和Electron新手,如果思路有误,希望能指正。
解答
1. Frida对Electron应用及JS/Python的支持
Frida完全支持hook Electron应用,也支持hook JavaScript代码。Electron本质是基于Chromium和Node.js,Frida可以注入到Electron的各个进程(主进程、渲染进程)中,不管是用Frida的JS脚本还是Python绑定都可以实现hook。
2. 正确的hook层级
Electron应用分为主进程(Node.js环境)和渲染进程(Chromium的V8环境,同时支持浏览器API和Node.js API)。要hook你目标的handleClick函数(属于渲染进程里的前端组件逻辑),必须注入到渲染进程的V8上下文里。
你之前的脚本没获取到document,大概率是因为注入到了主进程,或者注入时机不对(渲染进程的V8上下文还没初始化完成)。
3. 代码示例
方法一:JS脚本直接hook渲染进程
首先,找到FreeTube的渲染进程PID,或者用进程名匹配注入:
# 注入到FreeTube的渲染进程,Linux下可以用进程名过滤 frida -n FreeTube -l hook.js
编写hook.js,确保注入到渲染进程的V8上下文,针对Vue组件的handleClick函数,可通过以下两种思路实现hook:
// 等待渲染进程上下文初始化 setTimeout(() => { if (typeof document !== 'undefined') { console.log('[Frida] 成功进入渲染进程上下文'); // 方法1:拦截Vue组件的$emit,监听handleClick触发的事件 const originalEmit = Vue.prototype.$emit; Vue.prototype.$emit = function(eventName, ...args) { if (eventName === 'click' && this.$options.name === 'ft-input') { console.log('[Frida] 捕获ft-input的handleClick事件'); console.log('inputData:', args[0]); console.log('事件对象:', args[1]); } return originalEmit.call(this, eventName, ...args); }; // 方法2:直接找到ft-input组件的handleClick方法 const FtInputComponent = Vue.options.components['ft-input']; if (FtInputComponent) { const originalHandleClick = FtInputComponent.methods.handleClick; FtInputComponent.methods.handleClick = function(e) { console.log('[Frida] 直接hook到handleClick函数'); console.log('inputDataPresent:', this.inputDataPresent); console.log('inputData:', this.inputData); // 调用原函数 return originalHandleClick.call(this, e); }; } } else { console.log('[Frida] 当前不在渲染进程上下文,可能注入到了主进程'); } }, 3000); // 延迟3秒等待页面加载完成
方法二:Python脚本实现hook
用Frida的Python绑定,可以更灵活地控制注入时机和进程选择:
import frida import sys def on_message(message, data): if message['type'] == 'send': print(f"[Frida] {message['payload']}") elif message['type'] == 'error': print(f"[Frida Error] {message['stack']}") # 找到FreeTube的进程 session = frida.attach("FreeTube") # 脚本内容,和JS版本逻辑一致 script_code = """ setTimeout(() => { if (typeof document !== 'undefined') { send('成功进入渲染进程上下文'); // 拦截$emit来监控handleClick的触发 const originalEmit = Vue.prototype.$emit; Vue.prototype.$emit = function(eventName, ...args) { if (eventName === 'click' && this.$options.name === 'ft-input') { send(`捕获ft-input的handleClick事件,inputData: ${args[0]}`); } return originalEmit.call(this, eventName, ...args); }; } else { send('当前不在渲染进程上下文,请确认注入的是渲染进程'); } }, 3000); """ script = session.create_script(script_code) script.on('message', on_message) script.load() print("按任意键退出...") sys.stdin.read() session.detach()
注意事项
- 确保注入的是渲染进程:Electron应用会有多个进程,主进程和渲染进程,你可以用
frida -n FreeTube --list查看所有进程,选择带有renderer标识的进程注入。 - 注入时机:页面加载需要时间,所以用
setTimeout延迟执行hook逻辑,或者监听DOMContentLoaded事件。 - Vue组件的hook:如果组件是局部注册的,可能需要通过遍历
document元素获取对应Vue实例(vm = document.querySelector('ft-input').__vue__),再hook它的handleClick方法。
内容的提问来源于stack exchange,提问作者dujiajun

