之前在github看了r0ysue大佬的frida系列教程,於是想來實踐一下。
DDCTF2018-HelloBabyDex
這道題涉及到了APK的熱修復,目前還沒有怎么接觸,之后得深入一下。

這道題在Mainactivity的onCreate函數中調用了Joseph函數,並把它的返回值拼接作為flag
String v9 = this.Joseph(1, 2); super.onCreate(arg13); this.setContentView(0x7F09001B); // layout:activity_main this.runRobust(); SignCheck v10 = new SignCheck(this, this, "1B:D0:4A:9D:B5:A9:84:93:7E:79:27:9C:6C:C4:14:AB:DD:B0:75:7F"); v10.check(); Debug.isDebuggerConnected(); View v8 = this.findViewById(0x7F07003D); // id:input_text EditText v8_1 = (EditText)v8; View v7 = this.findViewById(0x7F070026); // id:check_btn Button v7_1 = (Button)v7; cn.chaitin.geektan.crackme.MainActivity.1 v0_3 = new View.OnClickListener() { public static ChangeQuickRedirect changeQuickRedirect; @Override // android.view.View$OnClickListener public void onClick(View arg9) { Object[] v0 = new Object[1]; v0[0] = arg9; ChangeQuickRedirect v2 = cn.chaitin.geektan.crackme.MainActivity.1.changeQuickRedirect; Class[] v5 = new Class[1]; Class v1 = View.class; v5[0] = v1; Class v6 = Void.TYPE; boolean v0_1 = PatchProxy.isSupport(v0, this, v2, false, 18, v5, v6); if(v0_1) { Object[] v0_2 = new Object[1]; v0_2[0] = arg9; ChangeQuickRedirect v2_1 = cn.chaitin.geektan.crackme.MainActivity.1.changeQuickRedirect; Class[] v5_1 = new Class[1]; Class v1_1 = View.class; v5_1[0] = v1_1; Class v6_1 = Void.TYPE; PatchProxy.accessDispatch(v0_2, this, v2_1, false, 18, v5_1, v6_1); } else { EditText v0_3 = v8_1; Editable v0_4 = v0_3.getText(); boolean v0_5 = TextUtils.isEmpty(v0_4); if(v0_5) { label_31: MainActivity v0_12 = MainActivity.this; Toast v0_13 = Toast.makeText(v0_12, "大佬莫急!再試試!", 0); v0_13.show(); } else { EditText v0_6 = v8_1; Editable v0_7 = v0_6.getText(); String v0_8 = v0_7.toString(); StringBuilder v1_2 = new StringBuilder(); StringBuilder v1_3 = v1_2.append("DDCTF{"); String v2_2 = v9; StringBuilder v1_4 = v1_3.append(v2_2); StringBuilder v1_5 = v1_4.append("}"); String v1_6 = v1_5.toString(); boolean v0_9 = v0_8.equals(v1_6); if(!v0_9) { goto label_31; } MainActivity v0_10 = MainActivity.this; Toast v0_11 = Toast.makeText(v0_10, "恭喜大佬!密碼正確!", 0); v0_11.show(); } } } }; v7_1.setOnClickListener(v0_3);
可以看到v9參與了之后的拼接以及equals操作,於是我們這里可以使用frida直接來進行hook
hook equals函數的話,可以直接出flag,hook Joseph函數的話,可以出flag中的字符串,拼接起來。
上腳本:
import frida, sys source = """ Java.perform(function() { var clazz = Java.use('cn.chaitin.geektan.crackme.MainActivity'); clazz.Joseph.implementation = function() { var msg = clazz.Joseph.apply(this, arguments); send(msg); return msg; } var clazzz = Java.use('java.lang.String'); clazzz.equals.implementation = function() { var msg = clazzz.equals.apply(this, arguments); var ret = clazzz.valueOf.apply(this,arguments); if(ret.indexOf("DDCTF")!=-1) send(ret); return msg; } }); """ def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) process = frida.get_usb_device().attach('cn.chaitin.geektan.crackme') script = process.create_script(source) script.on('message', on_message) script.load() sys.stdin.read()

可以看到,Joseph函數只調用了一次,但輸出了兩次,這就是熱補丁的結果。
hook equals函數后,再加以篩選,直接出flag!
RCTF2015(FlagSystem)
這道題看了奈沙夜影的wp,他是用xposed實現的,這里我使用了frida
引用大佬的分析過程,這里只給出frida實現。
https://blog.csdn.net/whklhhhh/article/details/89118707
上腳本:
#!/usr/bin/env python2 # -*- coding: utf-8 -*- import frida , sys#select book_author,book_name from books_table jscode = """ Java.perform(function () { send("Hook start.."); var test = Java.use('com.example.mybackup.Test'); var k; test.getSign.implementation = function () { send("getSign Function implemented"); k = test.getSign.apply(this,arguments); send("Password is : " + k); return k; } var demo = Java.use('com.example.mybackup.BooksDB'); var db; demo.getReadableDatabase.implementation = function (k) { send("getReadableDatabase Function implemented"); db = this.getReadableDatabase(k); if(db != null) send('DB got'); var S = Java.use("java.lang.String"); var sql = S.$new("select book_author,book_name from books_table"); var cursor = db.rawQuery(sql,null); if(cursor!=null) send('cursor got'); /*cursor.getString(0); while(cursor.moveToNext()) { send("Result : " + cursor.getString(0)); } Java.choose("net.sqlcipher.Cursor" , { onMatch : function(instance){ console.log("Found instance: "+instance); }, onComplete:function(){} });*/ var class_cursor = Java.use("android.database.Cursor"); cursor = Java.cast(cursor,class_cursor); while(cursor.moveToNext()) { send("Result : " + cursor.getString(0)); } return db; } demo.$init.implementation = function (a) { send("$init Function implemented"); return this.$init(a); } }); """ def on_message(message, data): if message['type']=='send': print("[*] {0}".format(message['payload'])) else: print(message) device = frida.get_usb_device() pid = device.spawn(['com.example.mybackup']) process = device.attach(pid) script = process.create_script(jscode) script.on("message", on_message) script.load() device.resume(pid) sys.stdin.read()
這里說明一下與上一個frida腳本的區別,這道題需要拿到sqlite的db,所以需要hook getReadableDatabase這個函數,這個函數在構造函數中所以需要在APK剛運行時插樁,使用了spawn。
拿到db之后就容易了,再獲取到cursor,讀取數據庫就好了。
這里還有一個坑點,由於這個apk使用了sqlcipher庫,這個庫中的cursor是經過包裝的接口,真正的Cursor.moveToNext等函數在AbstractCursor中實現,這里直接拿到cursor的話,調用
cursor.moveToNext會報錯,所以需要強制cast成android.database.Cursor這個類型才行。當時嘗試了直接獲取AbstractCursor的instance和獲取cursor的instance都不可以。。
最終效果:

不得不說,frida真的是神器。
