/** It should be launch earlier in order to be aware of a maximun quantity of file descriptors. @author @FrenchYeti */ Java.perform(function() { // ============= Config var CONFIG = { // if TRUE enable data dump printEnable: true, // if TRUE enable libc.so open/read/write hook printLibc: false, // if TRUE print the stack trace for each hook printStackTrace: false, // to filter the file path whose data want to be dumped in ASCII dump_ascii_If_Path_contains: [".log", ".xml", ".prop"], // to filter the file path whose data want to be NOT dumped in hexdump (useful for big chunk and excessive reads) dump_hex_If_Path_NOT_contains: [".png", "/proc/self/task", "/system/lib", "base.apk", "cacert"], // to filter the file path whose data want to be NOT dumped fron libc read/write (useful for big chunk and excessive reads) dump_raw_If_Path_NOT_contains: [".png", "/proc/self/task", "/system/lib", "base.apk", "cacert"] } // ============= Keep a trace of file descriptor, path, and so var TraceFD = {}; var TraceFS = {}; var TraceFile = {}; var TraceSysFD = {}; // ============= Get classes var CLS = { File: Java.use("java.io.File"), FileInputStream: Java.use("java.io.FileInputStream"), FileOutputStream: Java.use("java.io.FileOutputStream"), String: Java.use("java.lang.String"), FileChannel: Java.use("java.nio.channels.FileChannel"), FileDescriptor: Java.use("java.io.FileDescriptor"), Thread: Java.use("java.lang.Thread"), StackTraceElement: Java.use("java.lang.StackTraceElement"), AndroidDbSQLite: Java.use("android.database.sqlite.SQLiteDatabase") }; var File = { new: [ CLS.File.$init.overload("java.io.File", "java.lang.String"), CLS.File.$init.overload("java.lang.String"), CLS.File.$init.overload("java.lang.String", "java.lang.String"), CLS.File.$init.overload("java.net.URI"), ] }; var FileInputStream = { new: [ CLS.FileInputStream.$init.overload("java.io.File"), CLS.FileInputStream.$init.overload("java.io.FileDescriptor"), CLS.FileInputStream.$init.overload("java.lang.String"), ], read: [ CLS.FileInputStream.read.overload(), CLS.FileInputStream.read.overload("[B"), CLS.FileInputStream.read.overload("[B", "int", "int"), ], }; var FileOuputStream = { new: [ CLS.FileOutputStream.$init.overload("java.io.File"), CLS.FileOutputStream.$init.overload("java.io.File", "boolean"), CLS.FileOutputStream.$init.overload("java.io.FileDescriptor"), CLS.FileOutputStream.$init.overload("java.lang.String"), CLS.FileOutputStream.$init.overload("java.lang.String", "boolean") ], write: [ CLS.FileOutputStream.write.overload("[B"), CLS.FileOutputStream.write.overload("int"), CLS.FileOutputStream.write.overload("[B", "int", "int"), ], }; // ============= Hook implementation File.new[1].implementation = function(a0) { prettyLog("[Java::File.new.1] New file : " + a0); var ret = File.new[1].call(this, a0); var f = Java.cast(this, CLS.File); TraceFile["f" + this.hashCode()] = a0; return ret; } File.new[2].implementation = function(a0, a1) { prettyLog("[Java::File.read.2] New file : " + a0 + "/" + a1); var ret = File.new[2].call(this, a0, a1);; var f = Java.cast(this, CLS.File); TraceFile["f" + this.hashCode()] = a0 + "/" + a1; return ret; } FileInputStream.new[0].implementation = function(a0) { var file = Java.cast(a0, CLS.File); var fname = TraceFile["f" + file.hashCode()]; if (fname == null) { var p = file.getAbsolutePath(); if (p !== null) fname = TraceFile["f" + file.hashCode()] = p; } if (fname == null) fname = "[unknow]" prettyLog("[Java::FileInputStream.new.0] New input stream from file (" + fname + "): "); var fis = FileInputStream.new[0].call(this, a0) var f = Java.cast(this, CLS.FileInputStream); TraceFS["fd" + this.hashCode()] = fname; var fd = Java.cast(this.getFD(), CLS.FileDescriptor); TraceFD["fd" + fd.hashCode()] = fname; return fis; } FileInputStream.read[1].implementation = function(a0) { var fname = TraceFS["fd" + this.hashCode()]; var fd = null; if (fname == null) { fd = Java.cast(this.getFD(), CLS.FileDescriptor); fname = TraceFD["fd" + fd.hashCode()] } if (fname == null) fname = "[unknow]"; var b = Java.array('byte', a0); prettyLog("[Java::FileInputStream.read.1] Read from file,offset (" + fname + "," + a0 + "):\n" + prettyPrint(fname, b)); return FileInputStream.read[1].call(this, a0); } FileInputStream.read[2].implementation = function(a0, a1, a2) { var fname = TraceFS["fd" + this.hashCode()]; var fd = null; if (fname == null) { fd = Java.cast(this.getFD(), CLS.FileDescriptor); fname = TraceFD["fd" + fd.hashCode()] } if (fname == null) fname = "[unknow]"; var b = Java.array('byte', a0); prettyLog("[Java::FileInputStream.read.2] Read from file,offset,len (" + fname + "," + a1 + "," + a2 + ")\n" + prettyPrint(fname, b)); return FileInputStream.read[2].call(this, a0, a1, a2); } // =============== File Output Stream ============ FileOuputStream.new[0].implementation = function(a0) { var file = Java.cast(a0, CLS.File); var fname = TraceFile["f" + file.hashCode()]; if (fname == null) fname = "[unknow]"; prettyLog("[Java::FileOuputStream.new.0] New output stream to file (" + fname + "): "); var fis = FileOuputStream.new[0].call(this, a0); TraceFS["fd" + this.hashCode()] = fname; var fd = Java.cast(this.getFD(), CLS.FileDescriptor); TraceFD["fd" + fd.hashCode()] = fname; return fis; } FileOuputStream.new[1].implementation = function(a0) { var file = Java.cast(a0, CLS.File); var fname = TraceFile["f" + file.hashCode()]; if (fname == null) fname = "[unknow]"; prettyLog("[Java::FileOuputStream.new.1] New output stream to file (" + fname + "): \n"); var fis = FileOuputStream.new[1].call(this, a0); TraceFS["fd" + this.hashCode()] = fname; var fd = Java.cast(this.getFD(), CLS.FileDescriptor); TraceFD["fd" + fd.hashCode()] = fname; return fis; } FileOuputStream.new[2].implementation = function(a0) { var fd = Java.cast(a0, CLS.FileDescriptor); var fname = TraceFD["fd" + fd.hashCode()]; if (fname == null) fname = "[unknow]"; prettyLog("[Java::FileOuputStream.new.2] New output stream to FileDescriptor (" + fname + "): \n"); var fis = FileOuputStream.new[1].call(this, a0) TraceFS["fd" + this.hashCode()] = fname; return fis; } FileOuputStream.new[3].implementation = function(a0) { prettyLog("[Java::FileOuputStream.new.3] New output stream to file (str=" + a0 + "): \n"); var fis = FileOuputStream.new[1].call(this, a0) TraceFS["fd" + this.hashCode()] = a0; var fd = Java.cast(this.getFD(), CLS.FileDescriptor); TraceFD["fd" + fd.hashCode()] = a0; return fis; } FileOuputStream.new[4].implementation = function(a0) { prettyLog("[Java::FileOuputStream.new.4] New output stream to file (str=" + a0 + ",bool): \n"); var fis = FileOuputStream.new[1].call(this, a0) TraceFS["fd" + this.hashCode()] = a0; var fd = Java.cast(this.getFD(), CLS.FileDescriptor); TraceFD["fd" + fd.hashCode()] = a0; return fis; } FileOuputStream.write[0].implementation = function(a0) { var fname = TraceFS["fd" + this.hashCode()]; var fd = null; if (fname == null) { fd = Java.cast(this.getFD(), CLS.FileDescriptor); fname = TraceFD["fd" + fd.hashCode()] } if (fname == null) fname = "[unknow]"; prettyLog("[Java::FileOuputStream.write.0] Write byte array (" + fname + "):\n" + prettyPrint(fname, a0)); return FileOuputStream.write[0].call(this, a0); } FileOuputStream.write[1].implementation = function(a0) { var fname = TraceFS["fd" + this.hashCode()]; var fd = null; if (fname == null) { fd = Java.cast(this.getFD(), CLS.FileDescriptor); fname = TraceFD["fd" + fd.hashCode()] } if (fname == null) fname = "[unknow]"; prettyLog("[Java::FileOuputStream.write.1] Write int (" + fname + "): " + a0); return FileOuputStream.write[1].call(this, a0); } FileOuputStream.write[2].implementation = function(a0, a1, a2) { var fname = TraceFS["fd" + this.hashCode()]; var fd = null; if (fname == null) { fd = Java.cast(this.getFD(), CLS.FileDescriptor); fname = TraceFD["fd" + fd.hashCode()] if (fname == null) fname = "[unknow], fd=" + this.hashCode(); } prettyLog("[Java::FileOuputStream.write.2] Write " + a2 + " bytes from " + a1 + " (" + fname + "):\n" + prettyPrint(fname, a0)); return FileOuputStream.write[2].call(this, a0, a1, a2); } // native hooks Interceptor.attach( Module.findExportByName("libc.so", "read"), { // fd, buff, len onEnter: function(args) { if (CONFIG.printLibc === true) { var bfr = args[1], sz = args[2].toInt32(); var path = (TraceSysFD["fd-" + args[0].toInt32()] != null) ? TraceSysFD["fd-" + args[0].toInt32()] : "[unknow path]"; prettyLog("[Libc::read] Read FD (" + path + "," + bfr + "," + sz + ")\n" + rawPrint(path, Memory.readByteArray(bfr, sz))); } }, onLeave: function(ret) { } } ); Interceptor.attach( Module.findExportByName("libc.so", "open"), { // path, flags, mode onEnter: function(args) { this.path = Memory.readCString(args[0]); }, onLeave: function(ret) { TraceSysFD["fd-" + ret.toInt32()] = this.path; if (CONFIG.printLibc === true) prettyLog("[Libc::open] Open file '" + this.path + "' (fd: " + ret.toInt32() + ")"); } } ); Interceptor.attach( Module.findExportByName("libc.so", "write"), { // fd, buff, count onEnter: function(args) { if (CONFIG.printLibc === true) { var bfr = args[1], sz = args[2].toInt32(); var path = (TraceSysFD["fd-" + args[0].toInt32()] != null) ? TraceSysFD["fd-" + args[0].toInt32()] : "[unknow path]"; prettyLog("[Libc::write] Write FD (" + path + "," + bfr + "," + sz + ")\n" + rawPrint(path, Memory.readByteArray(bfr, sz))); } }, onLeave: function(ret) { } } ); // helper functions function prettyLog(str) { console.log("---------------------------\n" + str); if (CONFIG.printStackTrace === true) { printStackTrace(); } } function prettyPrint(path, buffer) { if (CONFIG.printEnable === false) return ""; if (contains(path, CONFIG.dump_ascii_If_Path_contains)) { return b2s(buffer); } else if (!contains(path, CONFIG.dump_hex_If_Path_NOT_contains)) { return hexdump(b2s(buffer)); } return "[dump skipped by config]"; } function rawPrint(path, buffer) { if (CONFIG.printEnable === false) return ""; if (!contains(path, CONFIG.dump_raw_If_Path_NOT_contains)) { return hexdump(buffer); } return "[dump skipped by config]"; } function contains(path, patterns) { for (var i = 0; i < patterns.length; i++) if (path.indexOf(patterns[i]) > -1) return true; return false; } function printStackTrace() { var th = Java.cast(CLS.Thread.currentThread(), CLS.Thread); var stack = th.getStackTrace(), e = null; for (var i = 0; i < stack.length; i++) { console.log("\t" + stack[i].getClassName() + "." + stack[i].getMethodName() + "(" + stack[i].getFileName() + ")"); } } function isZero(block) { var m = /^[0\s]+$/.exec(block); return m != null && m.length > 0 && (m[0] == block); } function hexdump(buffer, blockSize) { blockSize = blockSize || 16; var lines = []; var hex = "0123456789ABCDEF"; var prevZero = false, ctrZero = 0; for (var b = 0; b < buffer.length; b += blockSize) { var block = buffer.slice(b, Math.min(b + blockSize, buffer.length)); var addr = ("0000" + b.toString(16)).slice(-4); var codes = block.split('').map(function(ch) { var code = ch.charCodeAt(0); return " " + hex[(0xF0 & code) >> 4] + hex[0x0F & code]; }).join(""); codes += " ".repeat(blockSize - block.length); var chars = block.replace(/[\\x00-\\x1F\\x20\n]/g, '.'); chars += " ".repeat(blockSize - block.length); if (isZero(codes)) { ctrZero += blockSize; prevZero = true; } else { if (prevZero) { lines.push("\t [" + ctrZero + "] bytes of zeroes"); } lines.push(addr + " " + codes + " " + chars); prevZero = false; ctrZero = 0; } } if (prevZero) { lines.push("\t [" + ctrZero + "] bytes of zeroes"); } return lines.join("\\n"); } function b2s(array) { var result = ""; for (var i = 0; i < array.length; i++) { result += String.fromCharCode(modulus(array[i], 256)); } return result; } function modulus(x, n) { return ((x % n) + n) % n; } });