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 and run as python

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))

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

    // Call the original onClick handler, 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')

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 =, 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 =;
    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');