RE env for inspecting APKs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

455 lines
15 KiB

/**
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]<File:" + file.hashCode() + ">";
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;
}
});