轉載自Sakura的博客:https://eternalsakura13.com/2020/07/04/fridajava
致謝
本篇文章學到的內容來自且徹底來自r0ysue的知識星球,推薦一下(這個男人啥都會,還能陪你在線撩騷)。
python
Frida環境
https://github.com/frida/fridaandroid
pyenv
python全版本隨機切換,這裏提供macOS上的配置方法c++
1brew update
2brew install pyenv
3echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile
1下載一個3.8.2,下載真的很慢,要慢慢等
2pyenv install 3.8.2
3
4pyenv versions
5sakura@sakuradeMacBook-Pro:~$ pyenv versions
6 system
7* 3.8.2 (set by /Users/sakura/.python-version)
8切換到咱們裝的
9pyenv local 3.8.2
10python -V
11pip -V
12本來系統自帶的
13python local system
14python -V
另外當你須要臨時禁用pyenv的時候
git
把這個註釋了而後另開終端就行了。github
關於卸載某個python版本web
1Uninstalling Python Versions
2As time goes on, you will accumulate Python versions in your $(pyenv root)/versions directory.
3
4To remove old Python versions, pyenv uninstall command to automate the removal process.
5
6Alternatively, simply rm -rf the directory of the version you want to remove. You can find the directory of a particular Python version with the pyenv prefix command, e.g. pyenv prefix 2.6.8.
frida安裝
若是直接按下述安裝則會直接安裝frida和frida-tools的最新版本。sql
1pip install frida-tools
2frida --version
3frida-ps --version
咱們也能夠自由安裝舊版本的frida,例如12.8.0shell
1pyenv install 3.7.7
2pyenv local 3.7.7
3pip install frida==12.8.0
4pip install frida-tools==5.3.0
老版本frida和對應關係
對應關係很好找
數據庫
安裝objection
1pyenv local 3.8.2
2pip install objection
3objection -h
1pyenv local 3.7.7
2pip install objection==1.8.4
3objection -h
frida使用
下載frida-server並解壓,在這裏下載frida-server-12.8.0
先adb shell,而後切換到root權限,把以前push進來的frida server改個名字叫fs
而後運行frida
1adb push /Users/sakura/Desktop/lab/alpha/tools/android/frida-server-12.8.0-android-arm64 /data/local/tmp
2chmod +x fs
3./fs
若是要監聽端口,就
1./fs -l 0.0.0.0:8888
frida開發環境搭建
安裝
1git clone https://github.com/oleavr/frida-agent-example.git
2cd frida-agent-example/
3npm install
使用vscode打開此工程,在agent文件夾下編寫js,會有智能提示。
npm run watch
會監控代碼修改自動編譯生成js文件python腳本或者cli加載_agent.js
frida -U -f com.example.android --no-pause -l _agent.js
下面是測試腳本
s1.js
1function main() {
2 Java.perform(function x() {
3 console.log("sakura")
4 })
5}
6setImmediate(main)
loader.py
1import time
2import frida
3
4device8 = frida.get_device_manager().add_remote_device("192.168.0.9:8888")
5pid = device8.spawn("com.android.settings")
6device8.resume(pid)
7time.sleep(1)
8session = device8.attach(pid)
9with open("si.js") as f:
10 script = session.create_script(f.read())
11script.load()
12input() #等待輸入
解釋一下,這個腳本就是先經過frida.get_device_manager().add_remote_device
來找到device,而後spawn方式啓動settings,而後attach到上面,並執行frida腳本。
FRIDA基礎
frida查看當前存在的進程
frida-ps -U
查看經過usb鏈接的android手機上的進程。
1sakura@sakuradeMacBook-Pro:~$ frida-ps --help
2Usage: frida-ps [options]
3
4Options:
5 --version show program's version number and exit
6 -h, --help show this help message and exit
7 -D ID, --device=ID connect to device with the given ID
8 -U, --usb connect to USB device
9 -R, --remote connect to remote frida-server
10 -H HOST, --host=HOST connect to remote frida-server on HOST
11 -a, --applications list only applications
12 -i, --installed include all installed applications
1sakura@sakuradeMacBook-Pro:~$ frida-ps -U
2 PID Name
3----- ---------------------------------------------------
4 3640 ATFWD-daemon
5 707 adbd
6 728 adsprpcd
726041 android.hardware.audio@2.0-service
8 741 android.hardware.biometrics.fingerprint@
經過grep過濾就能夠找到咱們想要的包名。
frida打印參數和修改返回值
1package myapplication.example.com.frida_demo;
2
3import android.support.v7.app.AppCompatActivity;
4import android.os.Bundle;
5import android.util.Log;
6
7public class MainActivity extends AppCompatActivity {
8
9 private String total = "@@@###@@@";
10
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 super.onCreate(savedInstanceState);
14 setContentView(R.layout.activity_main);
15
16 while (true){
17
18 try {
19 Thread.sleep(1000);
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23
24 fun(50,30);
25 Log.d("sakura.string" , fun("LoWeRcAsE Me!!!!!!!!!"));
26 }
27 }
28
29 void fun(int x , int y ){
30 Log.d("sakura.Sum" , String.valueOf(x+y));
31 }
32
33 String fun(String x){
34 total +=x;
35 return x.toLowerCase();
36 }
37
38 String secret(){
39 return total;
40 }
41}
1function main() {
2 console.log("Enter the Script!");
3 Java.perform(function x() {
4 console.log("Inside Java perform");
5 var MainActivity = Java.use("myapplication.example.com.frida_demo.MainActivity");
6 // 重載找到指定的函數
7 MainActivity.fun.overload('java.lang.String').implementation = function (str) {
8 //打印參數
9 console.log("original call : str:" + str);
10 //修改結果
11 var ret_value = "sakura";
12 return ret_value;
13 };
14 })
15}
16setImmediate(main);
1sakura@sakuradeMacBook-Pro:~$ frida-ps -U | grep frida
28738 frida-helper-32
38897 myapplication.example.com.frida_demo
4
5// -f是經過spawn,也就是重啓apk注入js
6sakura@sakuradeMacBook-Pro:~$ frida -U -f myapplication.example.com.frida_demo -l frida_demo.js
7...
8original call : str:LoWeRcAsE Me!!!!!!!!!
912-21 04:46:49.875 9594-9594/myapplication.example.com.frida_demo D/sakura.string: sakura
frida尋找instance,主動調用。
1function main() {
2 console.log("Enter the Script!");
3 Java.perform(function x() {
4 console.log("Inside Java perform");
5 var MainActivity = Java.use("myapplication.example.com.frida_demo.MainActivity");
6 //overload 選擇被重載的對象
7 MainActivity.fun.overload('java.lang.String').implementation = function (str) {
8 //打印參數
9 console.log("original call : str:" + str);
10 //修改結果
11 var ret_value = "sakura";
12 return ret_value;
13 };
14 // 尋找類型爲classname的實例
15 Java.choose("myapplication.example.com.frida_demo.MainActivity", {
16 onMatch: function (x) {
17 console.log("find instance :" + x);
18 console.log("result of secret func:" + x.secret());
19 },
20 onComplete: function () {
21 console.log("end");
22 }
23 });
24 });
25}
26setImmediate(main);
frida rpc
1function callFun() {
2 Java.perform(function fn() {
3 console.log("begin");
4 Java.choose("myapplication.example.com.frida_demo.MainActivity", {
5 onMatch: function (x) {
6 console.log("find instance :" + x);
7 console.log("result of fun(string) func:" + x.fun(Java.use("java.lang.String").$new("sakura")));
8 },
9 onComplete: function () {
10 console.log("end");
11 }
12 })
13 })
14}
15rpc.exports = {
16 callfun: callFun
17};
1import time
2import frida
3
4device = frida.get_usb_device()
5pid = device.spawn(["myapplication.example.com.frida_demo"])
6device.resume(pid)
7time.sleep(1)
8session = device.attach(pid)
9with open("frida_demo_rpc_call.js") as f:
10 script = session.create_script(f.read())
11
12def my_message_handler(message, payload):
13 print(message)
14 print(payload)
15
16script.on("message", my_message_handler)
17script.load()
18
19script.exports.callfun()
1sakura@sakuradeMacBook-Pro:~/gitsource/frida-agent-example/agent$ python frida_demo_rpc_loader.py
2begin
3find instance :myapplication.example.com.frida_demo.MainActivity@1d4b09d
4result of fun(string):sakura
5end
frida動態修改
即將手機上的app的內容發送到PC上的frida python程序,而後處理後返回給app,而後app再作後續的流程,核心是理解send/recv
函數
1<TextView
2 android:id="@+id/textView"
3 android:layout_width="wrap_content"
4 android:layout_height="wrap_content"
5 android:text="please input username and password"
6 app:layout_constraintBottom_toBottomOf="parent"
7 app:layout_constraintLeft_toLeftOf="parent"
8 app:layout_constraintRight_toRightOf="parent"
9 app:layout_constraintTop_toTopOf="parent" />
10
11
12 <EditText
13 android:id="@+id/editText"
14 android:layout_width="fill_parent"
15 android:layout_height="40dp"
16 android:hint="username"
17 android:maxLength="20"
18 app:layout_constraintBottom_toBottomOf="parent"
19 app:layout_constraintEnd_toEndOf="parent"
20 app:layout_constraintHorizontal_bias="1.0"
21 app:layout_constraintStart_toStartOf="parent"
22 app:layout_constraintTop_toTopOf="parent"
23 app:layout_constraintVertical_bias="0.095" />
24
25 <EditText
26 android:id="@+id/editText2"
27 android:layout_width="fill_parent"
28 android:layout_height="40dp"
29 android:hint="password"
30 android:maxLength="20"
31 app:layout_constraintBottom_toBottomOf="parent"
32 app:layout_constraintTop_toTopOf="parent"
33 app:layout_constraintVertical_bias="0.239" />
34
35 <Button
36 android:id="@+id/button"
37 android:layout_width="100dp"
38 android:layout_height="35dp"
39 android:layout_gravity="right|center_horizontal"
40 android:text="提交"
41 android:visibility="visible"
42 app:layout_constraintBottom_toBottomOf="parent"
43 app:layout_constraintEnd_toEndOf="parent"
44 app:layout_constraintStart_toStartOf="parent"
45 app:layout_constraintTop_toTopOf="parent"
46 app:layout_constraintVertical_bias="0.745" />
1public class MainActivity extends AppCompatActivity {
2
3 EditText username_et;
4 EditText password_et;
5 TextView message_tv;
6
7 @Override
8 protected void onCreate(Bundle savedInstanceState) {
9 super.onCreate(savedInstanceState);
10 setContentView(R.layout.activity_main);
11
12 password_et = (EditText) this.findViewById(R.id.editText2);
13 username_et = (EditText) this.findViewById(R.id.editText);
14 message_tv = ((TextView) findViewById(R.id.textView));
15
16 this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
17 @Override
18 public void onClick(View v) {
19
20 if (username_et.getText().toString().compareTo("admin") == 0) {
21 message_tv.setText("You cannot login as admin");
22 return;
23 }
24 //hook target
25 message_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT));
26
27 }
28 });
29
30 }
31}
先分析問題,個人最終目標是讓message_tv.setText能夠"發送"username爲admin的base64字符串。
那確定是hook TextView.setText這個函數。
1console.log("Script loaded successfully ");
2Java.perform(function () {
3 var tv_class = Java.use("android.widget.TextView");
4 tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
5 var string_to_send = x.toString();
6 var string_to_recv;
7 send(string_to_send); // send data to python code
8 recv(function (received_json_object) {
9 string_to_recv = received_json_object.my_data
10 console.log("string_to_recv: " + string_to_recv);
11 }).wait(); //block execution till the message is received
12 var my_string = Java.use("java.lang.String").$new(string_to_recv);
13 this.setText(my_string);
14 }
15});
1import time
2import frida
3import base64
4
5def my_message_handler(message, payload):
6 print(message)
7 print(payload)
8 if message["type"] == "send":
9 print(message["payload"])
10 data = message["payload"].split(":")[1].strip()
11 print( 'message:', message)
12 #data = data.decode("base64")
13 #data = data
14 data = str(base64.b64decode(data))
15 print( 'data:',data)
16 user, pw = data.split(":")
17 print( 'pw:',pw)
18 #data = ("admin" + ":" + pw).encode("base64")
19 data = str(base64.b64encode(("admin" + ":" + pw).encode()))
20 print( "encoded data:", data)
21 script.post({"my_data": data}) # send JSON object
22 print( "Modified data sent")
23
24device = frida.get_usb_device()
25pid = device.spawn(["myapplication.example.com.frida_demo"])
26device.resume(pid)
27time.sleep(1)
28session = device.attach(pid)
29with open("frida_demo2.js") as f:
30 script = session.create_script(f.read())
31script.on("message", my_message_handler)
32script.load()
33input()
1sakura@sakuradeMacBook-Pro:~/gitsource/frida-agent-example/agent$ python frida_demo_rpc_loader2.py
2Script loaded successfully
3{'type': 'send', 'payload': 'Sending to the server :c2FrdXJhOjEyMzQ1Ng==\n'}
4None
5Sending to the server :c2FrdXJhOjEyMzQ1Ng==
6
7message: {'type': 'send', 'payload': 'Sending to the server :c2FrdXJhOjEyMzQ1Ng==\n'}
8data: b'sakura:123456'
9pw: 123456'
10encoded data: b'YWRtaW46MTIzNDU2Jw=='
11Modified data sent
12string_to_recv: b'YWRtaW46MTIzNDU2Jw=='
13
參考連接:https://github.com/Mind0xP/Frida-Python-Binding
API List
Java.choose(className: string, callbacks: Java.ChooseCallbacks): void
經過掃描Java VM的堆來枚舉className類的live instance。Java.use(className: string): Java.Wrapper<{}>
動態爲className生成JavaScript Wrapper,能夠經過調用$new()
來調用構造函數來實例化對象。
在實例上調用$dispose()
以對其進行顯式清理,或者等待JavaScript對象被gc。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.wait(): void
堵塞,直到message已經receive而且callback已經執行完畢並返回
Frida動靜態結合分析
Objection
參考這篇文章
實用FRIDA進階:內存漫遊、hook anywhere、抓包objection
https://pypi.org/project/objection/
objection啓動並注入內存
objection -d -g package_name explore
1sakura@sakuradeMacBook-Pro:~$ objection -d -g com.android.settings explore
2[debug] Agent path is: /Users/sakura/.pyenv/versions/3.7.7/lib/python3.7/site-packages/objection/agent.js
3[debug] Injecting agent...
4Using USB device `Google Pixel`
5[debug] Attempting to attach to process: `com.android.settings`
6[debug] Process attached!
7Agent injected and responds ok!
8
9 _ _ _ _
10 ___| |_|_|___ ___| |_|_|___ ___
11| . | . | | -_| _| _| | . | |
12|___|___| |___|___|_| |_|___|_|_|
13 |___|(object)inject(ion) v1.8.4
14
15 Runtime Mobile Exploration
16 by: @leonjza from @sensepost
17
18[tab] for command suggestions
19com.android.settings on (google: 8.1.0) [usb] #
objection memory
查看內存中加載的module `memory list modules`
1com.android.settings on (google: 8.1.0) [usb] # memory list modules
2Save the output by adding `--json modules.json` to this command
3Name Base Size Path
4----------------------------------------------- ------------ -------------------- ---------------------------------------------------------------
5app_process64 0x64ce143000 32768 (32.0 KiB) /system/bin/app_process64
6libandroid_runtime.so 0x7a90bc3000 1990656 (1.9 MiB) /system/lib64/libandroid_runtime.so
7libbinder.so 0x7a9379f000 557056 (544.0 KiB) /system/lib64/libbinder.so
查看庫的導出函數 `memory list exports libssl.so`
1com.android.settings on (google: 8.1.0) [usb] # memory list exports libssl.so
2Save the output by adding `--json exports.json` to this command
3Type Name Address
4-------- ----------------------------------------------------- ------------
5function SSL_use_certificate_ASN1 0x7c8ff006f8
6function SSL_CTX_set_dos_protection_cb 0x7c8ff077b8
7function SSL_SESSION_set_ex_data 0x7c8ff098f4
8function SSL_CTX_set_session_psk_dhe_timeout 0x7c8ff0a754
9function SSL_CTX_sess_accept 0x7c8ff063b8
10function SSL_select_next_proto 0x7c8ff06a74
dump內存空間
memory dump all 文件名
memory dump from_base 起始地址 字節數 文件名
搜索內存空間
Usage: memory search "
objection android
內存堆搜索實例 `android heap search instances 類名`
在堆上搜索類的實例
1sakura@sakuradeMacBook-Pro:~$ objection -g myapplication.example.com.frida_demo explore
2Using USB device `Google Pixel`
3Agent injected and responds ok!
4
5[usb] # android heap search instances myapplication.example.com.frida_demo
6.MainActivity
7Class instance enumeration complete for myapplication.example.com.frida_demo.MainActivity
8Handle Class toString()
9-------- ------------------------------------------------- ---------------------------------------------------------
100x2102 myapplication.example.com.frida_demo.MainActivity myapplication.example.com.frida_demo.MainActivity@5b1b0af
調用實例的方法 `android heap execute 實例ID 實例方法`
查看當前可用的activity或者service `android hooking list activities/services`
直接啓動activity或者服務 `android intent launch_activity/launch_service activity/服務`
android intent launch_activity com.android.settings.DisplaySettings
這個命令比較有趣的是用在若是有些設計的很差,可能就直接繞過了密碼鎖屏等直接進去。
1com.android.settings on (google: 8.1.0) [usb] # android hooking list services
2com.android.settings.SettingsDumpService
3com.android.settings.TetherService
4com.android.settings.bluetooth.BluetoothPairingService
列出內存中全部的類 `android hooking list classes`
在內存中全部已加載的類中搜索包含特定關鍵詞的類。`android hooking search classes display`
1com.android.settings on (google: 8.1.0) [usb] # android hooking search classes display
2[Landroid.icu.text.DisplayContext$Type;
3[Landroid.icu.text.DisplayContext;
4[Landroid.view.Display$Mode;
5android.hardware.display.DisplayManager
6android.hardware.display.DisplayManager$DisplayListener
7android.hardware.display.DisplayManagerGlobal
內存中搜索指定類的全部方法 `android hooking list class_methods 類名`
1com.android.settings on (google: 8.1.0) [usb] # android hooking list class_methods java.nio.charset.Charset
2private static java.nio.charset.Charset java.nio.charset.Charset.lookup(java.lang.String)
3private static java.nio.charset.Charset java.nio.charset.Charset.lookup2(java.lang.String)
4private static java.nio.charset.Charset java.nio.charset.Charset.lookupViaProviders(java.lang.String)
在內存中全部已加載的類的方法中搜索包含特定關鍵詞的方法 `android hooking search methods display`
知道名字開始在內存裏搜就頗有用
1com.android.settings on (google: 8.1.0) [usb] # android hooking search methods display
2Warning, searching all classes may take some time and in some cases, crash the target application.
3Continue? [y/N]: y
4Found 5529 classes, searching methods (this may take some time)...
5android.app.ActionBar.getDisplayOptions
6android.app.ActionBar.setDefaultDisplayHomeAsUpEnabled
7android.app.ActionBar.setDisplayHomeAsUpEnabled
hook類的方法(hook類裏的全部方法/具體某個方法)
android hooking watch class 類名
這樣就能夠hook這個類裏面的全部方法,每次調用都會被log出來。android hooking watch class 類名 --dump-args --dump-backtrace --dump-return
在上面的基礎上,額外dump參數,棧回溯,返回值
1android hooking watch class xxx.MainActivity --dump-args --dump-backtrace --dump-return
android hooking watch class_method 方法名
1//能夠直接hook到全部重載
2android hooking watch class_method xxx.MainActivity.fun --dump-args --dump-backtrace --dump-return
grep trick和文件保存
objection log默認是不能用grep過濾的,可是能夠經過objection run xxx | grep yyy的
方式,從終端經過管道來過濾。
用法以下
1sakura@sakuradeMacBook-Pro:~$ objection -g com.android.settings run memory list modules | grep libc
2Warning: Output is not to a terminal (fd=1).
3libcutils.so 0x7a94a1c000 81920 (80.0 KiB) /system/lib64/libcutils.so
4libc++.so 0x7a9114e000 983040 (960.0 KiB) /system/lib64/libc++.so
5libc.so 0x7a9249d000 892928 (872.0 KiB) /system/lib64/libc.so
6libcrypto.so 0x7a92283000 1155072 (1.1 MiB) /system/lib64/libcrypto.so
有的命令後面能夠經過--json logfile
來直接保存結果到文件裏。
有的能夠經過查看.objection
文件裏的輸出log來查看結果。
1sakura@sakuradeMacBook-Pro:~/.objection$ cat *log | grep -i display
2android.hardware.display.DisplayManager
3android.hardware.display.DisplayManager$DisplayListener
4android.hardware.display.DisplayManagerGlobal
案例學習
案例學習case1:《仿VX數據庫原型取證逆向分析》
附件連接
android-backup-extractor工具連接
1sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn$ java -version
2java version "1.8.0_141"
3
4sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn$ java -jar abe-all.jar unpack 1.ab 1.tar
50% 1% 2% 3% 4% 5% 6% 7% 8% 9% 10% 11% 12% 13% 14% 15% 16% 17% 18% 19% 20% 21% 22% 23% 24% 25% 26% 27% 28% 29% 30% 31% 32% 33% 34% 35% 36% 37% 38% 39% 40% 41% 42% 43% 44% 45% 46% 47% 48% 49% 50% 51% 52% 53% 54% 55% 56% 57% 58% 59% 60% 61% 62% 63% 64% 65% 66% 67% 68% 69% 70% 71% 72% 73% 74% 75% 76% 77% 78% 79% 80% 81% 82% 83% 84% 85% 86% 87% 88% 89% 90% 91% 92% 93% 94% 95% 96% 97% 98% 99% 100%
69097216 bytes written to 1.tar.
7
8...
9sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn/apps/com.example.yaphetshan.tencentwelcome$ ls
10Encryto.db _manifest a db
裝個夜神模擬器玩
1sakura@sakuradeMacBook-Pro:/Applications/NoxAppPlayer.app/Contents/MacOS$ ./adb connect 127.0.0.1:62001
2* daemon not running. starting it now on port 5037 *
3adb E 5139 141210 usb_osx.cpp:138] Unable to create an interface plug-in (e00002be)
4* daemon started successfully *
5connected to 127.0.0.1:62001
6sakura@sakuradeMacBook-Pro:/Applications/NoxAppPlayer.app/Contents/MacOS$ ./adb shell
7dream2qltechn:/ # whoami
8root
9dream2qltechn:/ # uname -a
10Linux localhost 4.0.9+ #222 SMP PREEMPT Sat Mar 14 18:24:36 HKT 2020 i686
確定仍是先定位目標字符串Wait a Minute,What was happend?
jadx搜索字符串
重點在a()代碼裏,實際上是根據明文的name和password,而後aVar.a(a2 + aVar.b(a2, contentValues.getAsString("password"))).substring(0, 7)
再作一遍複雜的計算並截取7位當作密碼,傳入getWritableDatabase去解密demo.db數據庫。
因此咱們hook一下getWritableDatabase便可。
1frida-ps -U
2...
35662 com.example.yaphetshan.tencentwelcome
4
5
6objection -d -g com.example.yaphetshan.tencentwelcome explore
看一下源碼
1package net.sqlcipher.database;
2...
3public abstract class SQLiteOpenHelper {
4 ...
5 public synchronized SQLiteDatabase getWritableDatabase(char[] cArr) {
也能夠objection search一下這個method
1...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2) [usb] # android hooking search methods getWritableDatabase
2Warning, searching all classes may take some time and in some cases, crash the target application.
3Continue? [y/N]: y
4Found 4650 classes, searching methods (this may take some time)...
5
6android.database.sqlite.SQLiteOpenHelper.getWritableDatabase
7...
8net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase
hook一下這個method
1[usb] # android hooking watch class_method net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase --dump-args --dump-backtrace --dump-return
2- [incoming message] ------------------
3{
4 "payload": "Attempting to watch class \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m and method \u001b[32mgetWritableDatabase\u001b[39m.",
5 "type": "send"
6}
7- [./incoming message] ----------------
8(agent) Attempting to watch class net.sqlcipher.database.SQLiteOpenHelper and method getWritableDatabase.
9- [incoming message] ------------------
10{
11 "payload": "Hooking \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m.\u001b[92mgetWritableDatabase\u001b[39m(\u001b[31mjava.lang.String\u001b[39m)",
12 "type": "send"
13}
14- [./incoming message] ----------------
15(agent) Hooking net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(java.lang.String)
16- [incoming message] ------------------
17{
18 "payload": "Hooking \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m.\u001b[92mgetWritableDatabase\u001b[39m(\u001b[31m[C\u001b[39m)",
19 "type": "send"
20}
21- [./incoming message] ----------------
22(agent) Hooking net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase([C)
23- [incoming message] ------------------
24{
25 "payload": "Registering job \u001b[94mjytq1qeyllq\u001b[39m. Type: \u001b[92mwatch-method for: net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase\u001b[39m",
26 "type": "send"
27}
28- [./incoming message] ----------------
29(agent) Registering job jytq1qeyllq. Type: watch-method for: net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase
30...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2) [usb] #
hook好以後再打開這個apk
1(agent) [1v488x28gcs] Called net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(java.lang.String)
2...
3(agent) [1v488x28gcs] Backtrace:
4 net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(Native Method)
5 com.example.yaphetshan.tencentwelcome.MainActivity.a(MainActivity.java:55)
6 com.example.yaphetshan.tencentwelcome.MainActivity.onCreate(MainActivity.java:42)
7 android.app.Activity.performCreate(Activity.java:6692)
8...
9(agent) [1v488x28gcs] Arguments net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(ae56f99)
10
11...
12...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2) [usb] # jobs list
13Job ID Hooks Type
14----------- ------- -----------------------------------------------------------------------------
151v488x28gcs 2 watch-method for: net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase
找到參數ae56f99
剩下的就是用這個密碼去打開加密的db。
而後base64解密一下就行了。
還有一種策略是主動調用,基於數據流的主動調用分析是很是有意思的。
即本身去調用a函數以觸發getWritableDatabase的數據庫解密。
先尋找a所在類的實例,而後hook getWritableDatabase,最終主動調用a。
這裏幸運的是a沒有什麼奇奇怪怪的參數須要咱們傳入,主動調用這種策略在循環註冊等地方可能就會有需求8.
1 [usb] # android heap search instances com.example.yaphetshan.tencentwelcome.MainActivity
2Class instance enumeration complete for com.example.yaphetshan.tencentwelcome.MainActivity
3Handle Class toString()
4-------- -------------------------------------------------- ----------------------------------------------------------
50x20078a com.example.yaphetshan.tencentwelcome.MainActivity com.example.yaphetshan.tencentwelcome.MainActivity@1528f80
6
7 [usb] # android hooking watch class_method net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase --dump-args --dump-backtrace --dump-return
8
9[usb] # android heap execute 0x20078a a
10
11(agent) [taupgwkum4h] Arguments net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(ae56f99)
案例學習case2:主動調用爆破密碼
附件連接
由於直接找Unfortunately,note the right PIN :(
找不到,多是把字符串藏在什麼資源文件裏了。
review代碼以後找到校驗的核心函數,邏輯就是將input編碼一下以後和密碼比較,這確定是什麼不可逆的加密。
1 public static boolean verifyPassword(Context context, String input) {
2 if (input.length() != 4) {
3 return false;
4 }
5 byte[] v = encodePassword(input);
6 byte[] p = "09042ec2c2c08c4cbece042681caf1d13984f24a".getBytes();
7 if (v.length != p.length) {
8 return false;
9 }
10 for (int i = 0; i < v.length; i++) {
11 if (v[i] != p[i]) {
12 return false;
13 }
14 }
15 return true;
16 }
這裏就爆破一下密碼。
1frida-ps -U | grep qualification
27660 org.teamsik.ahe17.qualification.easy
3
4frida -U org.teamsik.ahe17.qualification.easy -l force.js
1function main() {
2 Java.perform(function x() {
3 console.log("In Java perform")
4 var verify = Java.use("org.teamsik.ahe17.qualification.Verifier")
5 var stringClass = Java.use("java.lang.String")
6 var p = stringClass.$new("09042ec2c2c08c4cbece042681caf1d13984f24a")
7 var pSign = p.getBytes()
8 // var pStr = stringClass.$new(pSign)
9 // console.log(parseInt(pStr))
10 for (var i = 999; i < 10000; i++){
11 var v = stringClass.$new(String(i))
12 var vSign = verify.encodePassword(v)
13 if (parseInt(stringClass.$new(pSign)) == parseInt(stringClass.$new(vSign))) {
14 console.log("yes: " + v)
15 break
16 }
17 console.log("not :" + v)
18 }
19 })
20}
21setImmediate(main)
1...
2not :9080
3not :9081
4not :9082
5yes: 9083
這裏注意parseInt
關注小白技術社,開啓爬蟲工程師的app逆向之路。
恭喜你完成Frida三部曲的第一部,點個在看告訴你們吧
本文分享自微信公衆號 - 小白技術社(xbjss123)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。