目錄:andorid jar/庫源碼解析 Frida體驗: 作用: android手機上可以對,java和so層代碼,進行hook.監控數據和處理記憶體數據。 官譯:面向開發人員、逆向工程師和安全研究人員的動態工具工具包。 慄子: 運行步驟: 1、https://github.com/frida/fr ...
Frida體驗:
作用:
android手機上可以對,java和so層代碼,進行hook.監控數據和處理記憶體數據。
官譯:面向開發人員、逆向工程師和安全研究人員的動態工具工具包。
慄子:
運行步驟:
1、https://github.com/frida/frida/releases 下載適合需要運行環境的可執行程式。我這裡是arm64,所以下載了一個最新版本的frida64位。
2、PC端,安裝python環境,直接從官網下載一個python安裝包安裝即可,我這裡安裝的是python3, 安裝包會自己配置環境變數,cmd運行 python,看看有沒有,有就是安裝上了環境變數了。
3、使用 pip install frida 安裝 frida 環境,基於 python的。 再安裝 pip install frida-tools ,我看大部分都是按照了。tools的。
4、一切ok。開始跑了,,,把步驟1中得到的,可執行文件,拷貝到手機上面 adb push D:\xxx\xxx\frixxxx /data/local/tmp/
5、因為push上去的可執行文件預設沒有許可權,下一步授權。。adb shell 進入手機 su,得到root許可權。。cd /data/local 進入 local文件夾 chmod 777 -R * 授權可執行。。 再 cd tmp 進入 tmp 執行 :./frXXX回車運行exe
效果如下圖:
6、埠轉發,因為要把手機的數據和本地電腦互通,需要設置埠轉發,兩個埠:執行兩條命令
adb forward tcp:27042 tcp:27042 adb forward tcp:27043 tcp:27043
7、接著就可以允許python了。
8、代碼示例:
import frida, sys jsCode = """ Java.perform(function () { var impl = Java.use("com.android.test.TestUtil"); impl.test1.implementation = function () { send("test1 called!"); this.test1(); }; impl.test2.implementation = function () { send("test2 called!"); }; impl.test3.implementation = function (a) { send("test3 called!"); }; impl.test4.implementation = function (a,b,c) { send("test4 called!"); return this.test4(a,b,c); }; impl.test5.overload("int").implementation = function (a) { send("test5 1 called!"); }; impl.test5.overload("int", "java.lang.String").implementation = function (a,b) { send("test5 2 called!"); }; }); """ def message(message, data): if message["type"] == 'send': print(u"[*] {0}".format(message['payload'])) else: print(message) process = frida.get_remote_device().attach("com.android.test") script= process.create_script(jsCode) script.on("message", message) script.load() sys.stdin.read()
代碼中,把js寫到了一個string變數中。多行都是用這個固定格式。
對test1方法進行了hook同時,在後面也對 test1方法進行了調用,如果不調用,方法就不會執行了。參考 test2
test3針對單個參數的情況,test4針對多個參數。test5,針對不同參數進行了重載,需要指明參數類型,上面做了處理。
執行js的send方法,內容回唄,發往,scritp的綁定方法 。def message(message, data)在裡面,進行輸出,你也可以在js中使用 console.log進行輸出參數。
針對是複雜類型的情況,可以直接a.XXXX拿到複雜類型的變數。
8.2、其他js
// 遍歷所有js.指定名稱,過濾 Java.perform(function(){ Java.enumerateLoadedClasses({ onMatch: function(className) { if(className.toString().indexOf("down") >= 0){ send(className+'\"));'); } }, onComplete:function(){ send("done"); } }); });
9:so,hook處理。
import frida, sys jsCode3 = """ Java.perform(function(){ var exports = Module.enumerateExportsSync("xxx.so"); for(var i = 0; i < exports.length; i++) { if(exports[i] && exports[i].name != undefined){ if(exports[i].name.indexOf("rapidjson")>=0){ var nativePointer = new NativePointer(exports[i].address); send("exports " + exports[i].name + " nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send(this.context.pc + " called! "); send("context " + JSON.stringify(this.context)); // 保存長度,在返回方法進行調用 this.fd = args[1]; // send(nativePointer + " arg0 " +args[0] + " arg1 " +args[1] + " arg2 " +args[2]); }, onLeave:function(retval){ send(this.context.pc + " retval " +retval); // dump出內容 var size = parseInt(this.fd); if(size > 0){ const r = Memory.alloc(); // 複製以module.base地址開始的10個位元組 那肯定會是7F 45 4C 46...因為一個ELF文件的Magic屬性如此。 Memory.copy(r,ptr(retval),size); console.log(hexdump(r, { offset: 0, length: size, header: true, ansi: false })); } } }); } } } }); """; jsCode2 = """ Java.perform(function(){ // 遍歷所有模塊 Process.enumerateModules({ onMatch: function(exp){ //先獲取so的module對象 var module = Process.findModuleByName(exp.name); //??是通配符 var pattern = "11 22 33 44 55 66"; // //基址 // console.log("base:" + module.base) //從so的基址開始搜索,搜索大小為so文件的大小,搜指定條件03 49 ?? 50 20 44的數據 var res = Memory.scan(module.base, module.size, pattern, { onMatch: function(address, size){ //搜索成功 console.log('搜索到 ' + pattern +" 地址是:"+ address.toString()); }, onError: function(reason){ //搜索失敗 // console.log('搜索失敗 ' + reason); }, onComplete: function(){ //搜索完畢 // console.log("搜索完畢") } }); }, onComplete: function(){ send('stop'); } }); // 導入函數 var imports = Module.enumerateImportsSync("xxx.so"); for(var i = 0; i < imports.length; i++) { if(imports[i].name == 'strncat'){ send(imports[i].name + ": " + imports[i].address); break; } } // 導出函數 var exports = Module.enumerateExportsSync("xxx.so"); for(var i = 0; i < exports.length; i++) { if(exports[i].name.indexOf('add') != -1){ send(exports[i].name + ": " + exports[i].address); break; } } }); """; jsCode = """ var module = Process.findModuleByName("xxx.so"); var nativePointer = new NativePointer(module.base.add(0x1740)); send("nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send("mou sub_method called!"); }, onLeave:function(retval){ } }); var nativePointer = new NativePointer(ptr(0x00001122)); send("nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send("mou sub_method 2 called!"); }, onLeave:function(retval){ } }); """ def message(message, data): if message["type"] == 'send': print(u"[*] {0}".format(message['payload'])) else: print(message) process = frida.get_remote_device().attach("com.android.test") script= process.create_script(jsCode2) script.on("message", message) script.load() sys.stdin.read()
提供了多種方法:
a>遍歷所有so模塊,查找指定關鍵字。
b>遍歷某個模塊的所有導出函數,名稱包含
c>遍歷某個模塊的所有導入函數,名稱包含。
d>直接通過,模塊得到記憶體地址和偏移,進行hook.
e>通過利用 this.的成員變數,在結果中,dump出,入參的參數的某個值進行dump內容。
f>輸出 this.context對象。
源碼解讀:
待補充
參考:
https://www.anquanke.com/post/id/195215
https://www.anquanke.com/member/131652