Android
Example tool built for an Android CTF
For this particular example, using an Android 4.4 x86 emulator image is highly recommended. This tool is based on the SECCON Quals CTF 2015 APK1 example, download the APK here.
Save code as ctf.py and run as python ctf.py
.
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
// Whenever button is clicked
var onClick = MainActivity.onClick;
onClick.implementation = function (v) {
// Show a message to know that the function got called
send('onClick');
// Call the original onClick handler
onClick.call(this, v);
// Set our values after running the original onClick handler
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
// Log to the console that it's done, and we should have the flag!
console.log('Done:' + JSON.stringify(this.cnt));
};
});
"""
process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
Note we use this.m.value = 0
instead of this.m = 0
to set the field’s value.
If there is also a method in this class called m
, we need to use
this._m.value = 0
to set the value of field m
. In general, when looking at
the properties of objects it will be necessary to use .value
to access the
values those fields refer to.
Example of what we can do using the Java bridge
Some possibilities with the Java bridge in Frida:
Java.perform(function () {
// Create an instance of java.lang.String and initialize it with a string
var JavaString = Java.use('java.lang.String');
var exampleString1 = JavaString.$new('Hello World, this is an example string in Java.');
console.log('[+] exampleString1: ' + exampleString1);
console.log('[+] exampleString1.length(): ' + exampleString1.length());
// Create an instance of java.nio.charset.Charset, and initialize the default character set
var Charset = Java.use('java.nio.charset.Charset');
var charset = Charset.defaultCharset();
// Create a byte array of a Javascript string
var charArray = 'This is a Javascript string converted to a byte array.'.split('').map(function(c) {
return c.charCodeAt(0);
});
// Create an instance of java.lang.String and initialize it through an overloaded $new,
// with a byte array and a instance of java.nio.charset.Charset
var exampleString2 = JavaString.$new.overload('[B', 'java.nio.charset.Charset').call(JavaString, charArray, charset)
console.log('[+] exampleString2: ' + exampleString2);
console.log('[+] exampleString2.length(): ' + exampleString2.length());
// Intercept the initialization of java.lang.Stringbuilder's overloaded constructor,
// and write the partial argument to the console
var StringBuilder = Java.use('java.lang.StringBuilder');
// We need to replace .$init() instead of .$new(), since .$new() = .alloc() + .init()
var ctor = StringBuilder.$init.overload('java.lang.String');
ctor.implementation = function (arg) {
var partial = '';
var result = ctor.call(this, arg);
if (arg !== null) {
partial = arg.toString().replace('\n', '').slice(0, 10);
}
// console.log('new StringBuilder(java.lang.String); => ' + result);
console.log('new StringBuilder("' + partial + '");');
return result;
};
console.log('[+] new StringBuilder(java.lang.String) hooked');
// Intercept the toString() method of java.lang.StringBuilder and write its partial contents to the console.
var toString = StringBuilder.toString;
toString.implementation = function () {
var result = toString.call(this);
var partial = '';
if (result !== null) {
partial = result.toString().replace('\n', '').slice(0, 10);
}
console.log('StringBuilder.toString(); => ' + partial);
return result;
};
console.log('[+] StringBuilder.toString() hooked');
});
Example of stacktrace usage
Java.perform(function () {
var Cipher = Java.use('javax.crypto.Cipher');
var Exception = Java.use('java.lang.Exception');
var Log = Java.use('android.util.Log');
var init = Cipher.init.overload('int', 'java.security.Key');
init.implementation = function (opmode, key) {
var result = init.call(this, opmode, key);
console.log('Cipher.init() opmode:', opmode, 'key:', key);
console.log(stackTraceHere());
return result;
};
function stackTraceHere() {
return Log.getStackTraceString(Exception.$new());
}
});