PS D:\Users\D1esktop\frida\frida_demo> frida -U -f com.example.frida_demo -l frida_demo.js ____ / _ | Frida 12.8.0 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://www.frida.re/docs/home/ Spawning `com.example.frida_demo`... Enter the Script! Spawned `com.example.frida_demo`. Use %resume to let the main thread start executing! [OPPO PAFM00::com.example.frida_demo]-> %resume [OPPO PAFM00::com.example.frida_demo]-> Inside Java perform by frida_demo.js original call : str:LoWeRcAsE ThIs!!!!!!!!! original call : str:LoWeRcAsE ThIs!!!!!!!!! original call : str:LoWeRcAsE ThIs!!!!!!!!! ......(省略一万行) original call : str:LoWeRcAsE ThIs!!!!!!!!! Process terminated [OPPO PAFM00::com.example.frida_demo]->
PS D:\Users\Desktop\frida\frida_demo> python .\loader.py begin find instance by frida_demo_rpc.js :com.example.frida_demo.MainActivity@9f74e23 result of fun(string):ron_rpc_js end
PS D:\Users\Desktop\frida\frida_demo2> python .\loader.py Script loaded successfully {'type': 'send', 'payload': 'Sending to the server :Um9uOjEyMzQ1Njc4\n'} None Sending to the server :Um9uOjEyMzQ1Njc4
message: {'type': 'send', 'payload': 'Sending to the server :Um9uOjEyMzQ1Njc4\n'} data: b'Ron:12345678' pw: 12345678' encoded data: b'YWRtaW46MTIzNDU2Nzgn' Modified data sent string_to_recv: b'YWRtaW46MTIzNDU2Nzgn' This_Is_What_I_Input_To_Continue
打开 python 可以轻易进行验证。
1 2 3 4 5 6 7
In [1]: from base64 import b64decode, b64encode
In [2]: b64decode("YWRtaW46MTIzNDU2Nzgn") Out[2]: b"admin:12345678'"
In [3]: b64encode(b"Ron:12345678") Out[3]: b'Um9uOjEyMzQ1Njc4'
API List
本部分简要列举了一些常用的(js 的) API 。(其中有些前面已经用到了)
Java.choose(className: string, callbacks: Java.ChooseCallbacks): void 通过扫描 Java VM 的堆来枚举 className 类的 live instance。
Java.perform(fn: () => void): void Function to run while attached to the VM. Ensures that the current thread is attached to the VM and calls fn. (This isn’t necessary in callbacks from Java.) Will defer calling fn if the app’s class loader is not available yet. Use Java.performNow() if access to the app’s classes is not needed.
send(message: any, data?: ArrayBuffer | number[]): void 任何 JSON 可序列化的值。 将 JSON 序列化后的 message 发送到您的基于 Frida 的应用程序,并包含(可选)一些原始二进制数据。 The latter is useful if you e.g. dumped some memory using NativePointer#readByteArray().
recv(callback: MessageCallback): MessageRecvOperation Requests callback to be called on the next message received from your Frida-based application. This will only give you one message, so you need to call recv() again to receive the next one.
Runtime Mobile Exploration by: @leonjza from @sensepost
[tab] for command suggestions com.android.settings on (OPPO: 8.1.0) [usb] #
启动 objection 之后,会出现提示它的 logo ,这时候不知道输入啥命令的话,可以按下空格,有提示的命令及其功能出来;再按空格选中,又会有新的提示命令出来,这时候按回车就可以执行该命令,如下图执行应用环境信息命令 env 和 frida-server 版本信息命令。
提取内存信息
查看内存中加载的库
通过 object 还可以查看内存中加载的库。 运行命令 memory list modules 可以查看到内存中加载的库。
1 2 3 4 5 6 7 8 9
com.android.settings on (OPPO: 8.1.0) [usb] # memory list modules Save the output by adding `--json modules.json` to this command Name Base Size Path ----------------------- ------------ -------------------- ----------------------------------- app_process64 0x64c770a000 32768 (32.0 KiB) /system/bin/app_process64 libandroid_runtime.so 0x7ae7b57000 2154496 (2.1 MiB) /system/lib64/libandroid_runtime.so libbase.so 0x7ae7641000 77824 (76.0 KiB) /system/lib64/libbase.so libbinder.so 0x7ae79e5000 589824 (576.0 KiB) /system/lib64/libbinder.so ......
查看库的导出函数
运行命令 memory list exports libssl.so ,可以查看 libssl.so 库的导出函数。
1 2 3 4 5 6 7 8 9 10
com.android.settings on (OPPO: 8.1.0) [usb] # memory list exports libssl.so Save the output by adding `--json exports.json` to this command Type Name Address -------- ----------------------------------------------------- ------------ function SSL_use_certificate_ASN1 0x7a662ab200 function SSL_CTX_set_dos_protection_cb 0x7a662b3fdc function SSL_SESSION_set_ex_data 0x7a662b628c function SSL_CTX_set_session_psk_dhe_timeout 0x7a662b7144 function SSL_CTX_sess_accept 0x7a662b2394 ......
com.android.settings on (OPPO: 8.1.0) [usb] # memory list exports libart.so --json ./libart.json Writing exports as json to ./libart.json... Wrote exports to: ./libart.json
然后就可以在保存位置查看保存到文件的数据了。
提取整个(或部分)内存
通过命令 memory dump all from_base 可以实现。直接 dump 全部有 1.7 G,会炸。在后文脱壳中再用。
1 2 3 4
com.android.settings on (OPPO: 8.1.0) [usb] # memory dump all from_base Will dump 886 rw- images, totalling 1.7 GiB
(frida:11832): GLib-GIO-WARNING **: 16:54:46.966: _g_dbus_worker_do_read_cb: error determining bytes needed: Blob indicates that message exceeds maximum message length (128MiB)
com.android.settings on (OPPO: 8.1.0) [usb] # android heap search instances com.android.settings.DisplaySettings Using exsiting matches for com.android.settings.DisplaySettings. Use --fresh flag for new instances. Handle Class toString() -------- ------------------------------------ ----------------------------------------- 0x252a com.android.settings.DisplaySettings DisplaySettings{69d91ee #0 id=0x7f0a0231}
但是笔者实际上的结果是这样,至于发生了什么,我暂且梦在蒲里。
1 2
com.android.settings on (OPPO: 8.1.0) [usb] # android heap search instances com.android.settings.DisplaySettings Class instance enumeration complete for com.android.settings.DisplaySettings
com.example.frida_demo on (OPPO: 8.1.0) [usb] # android heap search instances com.example.frida_demo.MainActivity --fresh flag Class instance enumeration complete for com.example.frida_demo.MainActivity Handle Class toString() -------- ----------------------------------- ------------------------------------------- 0x203a com.example.frida_demo.MainActivity com.example.frida_demo.MainActivity@9f74e23 com.example.frida_demo on (OPPO: 8.1.0) [usb] # android heap execute 0x203a JustPrintTest Handle 0x203a is to class com.example.frida_demo.MainActivity Executing method: JustPrintTest() come from JustPrintTest()
键入 ESC 然后回车,可以看到下面的结果,打印了 evaluate result:come from JustPrintTest() 。
1 2 3 4 5 6
com.example.frida_demo on (OPPO: 8.1.0) [usb] # android heap evaluate 0x203a (The handle at `0x203a` will be available as the `clazz` variable.) console.log("evaluate result:"+clazz.JustPrintTest()); JavaScript capture complete. Evaluating... Handle 0x203a is to class com.example.frida_demo.MainActivity evaluate result:come from JustPrintTest()
可以看到,即使我们进行了筛选,终端还是进行了警告。这里我们选择 y 。整个过程明显变慢了很多,因为要一一去遍历所有的方法进行对比。
1 2 3 4 5 6 7 8 9 10 11
com.example.frida_demo on (OPPO: 8.1.0) [usb] # android hooking search methods display Warning, searching all classes may take some time and in some cases, crash the target application. Continue? [y/N]: y Found 5711 classes, searching methods (this may take some time)... android.app.ActionBar.getDisplayOptions android.app.ActionBar.setDefaultDisplayHomeAsUpEnabled ...... androidx.constraintlayout.solver.widgets.analyzer.DependencyGraph.generateDisplayNode An unexpected internal exception has occurred. If this looks like a code related error, please file a bug report!(session detach message) process-terminated
使用 jobs list 命令可以看到 objection 为我们创建的 Hooks 数为 55 ,也就是将 android.bluetooth.BluetoothDevice 类下的所有方法都 hook 了。
1 2 3 4
com.android.settings on (OPPO: 8.1.0) [usb] # jobs list Job ID Hooks Type ---------- ------- -------------------------------------------------- yi6z8sxiz2 55 watch-class for: android.bluetooth.BluetoothDevice
com.android.settings on (google: 9) [usb] # (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService() (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.isConnected() (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService() ...... (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getName() (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService()
Hooks a specified class method and reports on invocations, together with the number of arguments that method was called with. This command will also hook all of the methods available overloads unless a specific overload is specified.
If the --include-backtrace flag is provided, a full stack trace that lead to the methods invocation will also be dumped. This would aid in discovering who called the original method.
var result = this.b(x0,x1); //Suppose the byte[] in the Bytebuffer has a key value of 'hb'. var bytesArray = bytesBuffer2bytesArray(x0, 'hb'); var tmp = gson.$new().toJson(x0); tmp = JSON.parse(tmp); console.log("tmp:"+typeof(tmp)); console.log("decrypt --> message: ",tmp["backingArray"]); // console.log("decrypt --> message*hexdump: ",hexdump(gson.$new().toJson(x0)["backingArray"])); // console.log("decrypt --> decryptTo: ",gson.$new().toJson(x1)); // console.log("decrypt --> decryptTo*hexdump: ",hexdump(gson.$new().toJson(x1)["backingArray"])); return result; }
functionbytesBuffer2bytesArray(bytesBuffer, key) { Java.openClassFile("/data/local/tmp/r0gson.dex").load(); const gson = Java.use('com.r0ysue.gson.Gson'); var tmp = gson.$new().toJson(bytesBuffer); var dataKey = key tmp = JSON.parse(tmp); tmp = newArray(tmp[dataKey]); tmp = tmp.toString(); var tmp_array = tmp.split(","); var tmp_int_array=[]; tmp_int_array=tmp_array.map(function(data){ return +data; }); return tmp_int_array }
打印memorybuffer
1 2 3 4
// 打印内存 var view = newDataView(this.context.r0.readByteArray(12)); var value = '0x' + view.getUint8(11).toString(16) + view.getUint8(10).toString(16) + view.getUint8(9).toString(16) + view.getUint8(8).toString(16);
enumSignal{ GREEN, YELLOW, RED } publicclassTrafficLight{ publicstatic Signal color = Signal.RED; publicstaticvoidmain(){ Log.d("4enum", "enum "+ color.getClass().getName().toString()); switch (color) { case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
/** * Creates a Java array with elements of the specified `type`, from a * JavaScript array `elements`. The resulting Java array behaves like * a JS array, but can be passed by reference to Java APIs in order to * allow them to modify its contents. * * @param type Type name of elements. * @param elements Array of JavaScript values to use for constructing the * Java array. */ functionarray(type: string, elements: any[]): any[];
构建代码如下
1 2 3 4 5 6 7 8
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){ var newCharArray = Java.array('char', [ '一','去','二','三','里' ]); var result = this.toString(newCharArray); console.log("newCharArray,result:",newCharArray,result) console.log("newCharArray Object Object:",gson.$new().toJson(newCharArray)); var newResult = Java.use('java.lang.String').$new(Java.array('char', [ '烟','村','四','五','家'])) return newResult; }
类的多态与转型(Java.cast)
可以通过 getClass().getName().toString() 来查看当前实例的类型。
找到一个 instance ,通过 Java.cast 来强制转换对象的类型。
对 Java.cast 的解释文档
1 2 3 4 5 6 7 8
/** * Creates a JavaScript wrapper given the existing instance at `handle` of * given class `klass` as returned from `Java.use()`. * * @param handle An existing wrapper or a JNI handle. * @param klass Class wrapper for type to cast to. */ functioncast(handle: Wrapper | NativePointerValue, klass: Wrapper): Wrapper;