1. 前言
編寫 frida JavaScript 腳本是一件 very 普遍的事情在 Android Reverse 中。為了方便編寫,配置相關的環境使其能夠自動補全是很關鍵的,即通過類名就能夠獲取該類的所有對外接口信息,這是面向對象編程的核心優勢,可惜我沒有對象。
首先我們還是要有一個信仰,相信這是能夠配置好的,這個原因很簡單,因為 frida 的控制臺中就給出了相應的補全操作,只是因為操作不方便我才要去 VSCode 中配置。
2. 配置
In C/C++ 中我們只需要 #include 相應的頭文件就能夠達到在編寫源碼時的自動補全了,后續的補全操作都是由 Microsoft の IntelliSense 組件完成的。
同樣的在 JavaScript 中包含相應的 .ts (TypeScript)文件就能夠達到這樣的效果。當然 .ts 絕對不是用來干這個的,只是我找到的 frida-gum 的相關文件是 .ts 的。相應的下載地址在這 @types/frida-gum。我下載的是 19.0.0 版本的,對應的 frida-17.2.* 這個版本的 API 與 frida-16.1.0 是不同的,配置的時候需要找到對應版本的 index.d.ts 文件。
安裝 node.js 后可以直接使用其附帶的 npm 進行下載。After 下載,@types/frida-gum 目錄下的 index.d.ts 復制到 Frida JavaScript 目錄下即可。在文件開頭添加下面的代碼進行包含即可。
/// <reference path="./frida.d.ts" />
當然如果不 want 下載 node.js 可以直接從網頁端產看代碼,然后全選,復制到文件中保存,這里我要吐槽一下 npm 管理了,代碼都展示出來了卻不提供下載的接口。
完成包含后就可以在 VSCode 使用 Frida 的自動補全了。
[Append]:
我發現一個新的配置方式,能夠將 Java 也配置好 in the meanwhile. Frida JavaScript API 中給出一個配置的方案,只需要去下載相應的 example 就可以了。不過在這里面也需要使用 npm,如果不 want 下載可以直接去看 package_lock.json 文件中的下載地址,只需要下載 frida-gum 和 frida-java-bride 就可以了。
當然需要注意的一點是 TM frida-17 做了大改,把 Java 的相關 API 定義弄到了單獨的文件中去,而在 17 之前則一個 frida-gum 就夠了。
3. API 變化
frida-17.2.5 與 frida-16.1.0 的 API 是不一樣的,這里我只能說我親身體會到的一個例子,這個 Bug 我找了好久,修完之后 I just wanna say sun your mum frida。
下面的代碼是 frida-16.1.0 以及很多網上教程中常用的用來 Hook Android 加載庫時的操作。但在 frida-17.2.5 中這樣的操作是不行的。
Module.findExportByName(null, "dlopen");
當輸入上述 JavaScript 之后會在得到這樣的報錯信息,出現這樣錯誤的原因是 frida-17.2.5 中沒有相應的 API 實現。
When 我去查看相應的 API 描述 in @type/frida-gum-19.0.0 時,發現在 Module 中只有一個 findExportByName 的實現,而其參數列表只接受一個參數,并且函數沒有 static 關鍵字修飾,必須要實例化之后才能使用。
While in @type/frida-gum-15.2.0 中則是另一種情況,在其 Module 中有兩個 findExportByName 的實現,并且其中一個使用 static 關鍵字修飾,即不需要實例化也能使用。這就是代碼 Module.findExportByName(null, "dlopen")
為什么在 frida-16.1.0 中能夠正常使用的原因。
4. Some Code
這部分是我用來記錄自己寫的 JavaScript 代碼的位置,主要是給自己以后查閱。使用的是 frida-17.2.5。包括:查找 API 函數,輸出調用棧,顏色字。
/// <reference path="./frida.d.ts" />
const CMD_RED = "\x1b[31m";
const CMD_GREEN = "\x1b[32m";
const CMD_YELLOW = "\x1b[33m";
const CMD_BLUE = "\x1b[34m";
const CMD_MAGENTA = "\x1b[35m";
const CMD_CYAN = "\x1b[36m";
const CMD_WHITE = "\x1b[37m";
// Store original console.log
const originalLog = console.log;
// Override console.log
console.log = function(...args) {originalLog(args, CMD_WHITE);
};console.log(CMD_GREEN + "[+] Enter Hook");/*** get a export function by its name* @param {string} strName : the function you want to hook* @returns {fnPoint} : the address of the function*/
function FindExportByName(strName) {var fn = null;const Modules = Process.enumerateModules();for(let i = 0; i < Modules.length; i++) {fn = Modules[i].getExportByName(strName);if(fn) {console.log(`In Module : ${Modules[i].name}, Address is : ${fn}`);break;}};return fn;
}/*** print out a thread calling stack * @param {context} Context : the context of current Thread, can be obtained in Interceptor.attach() callback*/
function TraceStack(Context) {try {const backtrace = Thread.backtrace(Context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).map(sym => sym.name + CMD_YELLOW +'[' + sym.moduleName + ']');console.log(CMD_YELLOW + 'Backtrace:');backtrace.forEach((frame, i) => console.log(CMD_GREEN + "\t" + `${i}: ${frame}`));}catch(e) {console.log(CMD_RED + "\tStack trace unavailable");console.log(CMD_RED + `\t${e}`);}console.log(CMD_GREEN + "End Trace");
}// record which lib are loaded
var setLoged = new Set(["libstats_jni.so"]);
/*** if the module isn't record in setLoged, then print it.* @param {string} libPath : the module name that is loaded* @returns : if the first that load it return 1 else return 0.*/
function LogLib(libPath) {if(libPath && !setLoged.has(libPath)) {setLoged.add(libPath);console.log(CMD_CYAN + `Load Module : ${libPath}`);return 1;}return 0;//console.log(CMD_RED + `00: ${this}`);
}function TraceModuleLoad() {var fnOpen = FindExportByName("android_dlopen_ext");if(fnOpen) {var bThere = 0;var libPath;Interceptor.attach(fnOpen, {onEnter(args) {libPath = args[0].readCString();bThere = LogLib(libPath);//Thread.sleep(10);if(bThere) {TraceStack(this.context);}},onLeave(retval) {}})console.log("Complete dlopen hook install");}
}
// get all the load module when apk start
TraceModuleLoad();console.log(CMD_YELLOW + "[-] Hook Installed");