android_of_theseus/frida/theseus_frida/hook.js

264 lines
8.6 KiB
JavaScript

Java.perform(() => {
/*
//const StackFrameInfo = Java.use('java.lang.StackFrameInfo');
const Consumer = Java.use('java.util.function.Consumer');
const System = Java.use('java.lang.System');
*/
const StackWalker = Java.use('java.lang.StackWalker');
const StackWalkerOptions = Java.use('java.lang.StackWalker$Option');
const StackWalkerOptionsShowHidden = StackWalkerOptions.valueOf("SHOW_HIDDEN_FRAMES");
const StackWalkerOptionsShowReflect = StackWalkerOptions.valueOf("SHOW_REFLECT_FRAMES");
const StackWalkerOptionsRetainClassReference = StackWalkerOptions.valueOf("RETAIN_CLASS_REFERENCE");
const StackFrame = Java.use('java.lang.StackWalker$StackFrame');
const Base64 = Java.use("android.util.Base64");
const InMemoryDexClassLoader = Java.use("dalvik.system.InMemoryDexClassLoader");
const ByteBuffer = Java.use("java.nio.ByteBuffer");
const myClassLoader = InMemoryDexClassLoader.$new(
ByteBuffer.wrap(Base64.decode("<PYTHON REPLACE StackConsumer.dex.b64>", Base64.DEFAULT.value)),
null
);
const StackConsumer = Java.ClassFactory.get(myClassLoader).use("theseus.android.StackConsumer");
const get_stack = function () {
var stackConsumer = StackConsumer.$new();
var walker = StackWalker.getInstance(StackWalkerOptionsRetainClassReference);
walker.forEach(stackConsumer);
//send({"type": "stack", "data": stackConsumer.getStack()});
return stackConsumer.getStack().map((frame) => {
return {
"bytecode_index": frame.getByteCodeIndex(),
"is_native": frame.isNativeMethod(),
"method": frame.getDeclaringClass().descriptorString() + "->" + frame.getMethodName() + frame.getDescriptor(),
//{
//"descriptor": frame.getDescriptor(),
//"name": frame.getMethodName(),
//"class": frame.getDeclaringClass().descriptorString(),
// Broken for some reason
//"args": frame.getMethodType().parameterArray().map((argty) => argty.getName()),
//"ret": frame.getMethodType().returnType().getName(),
//}
};
});
};
const get_method_dsc = function (mth) {
// TODO: find a way to use MethodType (https://developer.android.com/reference/java/lang/invoke/MethodType)
// MethodType.descriptorString()
return mth.getDeclaringClass().descriptorString() +
"->" +
mth.getName() +
"(" +
mth.getParameterTypes().map((argty) => argty.descriptorString()).join('') +
")" +
mth.getReturnType().descriptorString();
};
const get_constr_dsc = function (cnstr) {
// TODO: find a way to use MethodType (https://developer.android.com/reference/java/lang/invoke/MethodType)
// MethodType.descriptorString()
return cnstr.getDeclaringClass().descriptorString() +
"->" +
"<init>" +
"(" +
cnstr.getParameterTypes().map((argty) => argty.descriptorString()).join('') +
")V";
};
const Method = Java.use("java.lang.reflect.Method");
const Class = Java.use("java.lang.Class");
const Constructor = Java.use("java.lang.reflect.Constructor");
const Modifier = Java.use("java.lang.reflect.Modifier");
const DexFile = Java.use("dalvik.system.DexFile");
const File = Java.use('java.io.File');
const Files = Java.use('java.nio.file.Files');
const Path = Java.use('java.nio.file.Path');
const System = Java.use('java.lang.System');
const Arrays = Java.use('java.util.Arrays');
// ****** Reflexive Method Calls ******
// Method.invoke(obj, ..args)
Method.invoke.overload(
"java.lang.Object", "[Ljava.lang.Object;" // the Frida type parser is so cursted...
).implementation = function (obj, args) {
send({
"type": "invoke",
"data": {
"method": get_method_dsc(this),
/*{
"name": this.getName(),
"class": this.getDeclaringClass().getName(),
"args": this.getParameterTypes().map((argty) => argty.getName() ),
"ret": this.getReturnType().getName(),
},*/
"stack": get_stack(),
"is_static": Modifier.isStatic(this.getModifiers()),
}
});
return this.invoke(obj, args);
};
// ****** Reflexive Class Instantiation ******
// Class.newInstance()
Class.newInstance.overload(
).implementation = function () {
send({
"type": "class-new-inst",
"data": {
"constructor": this.descriptorString() + "-><init>()V",
/*{
"name": "<init>",
"class": this.getName(),
"args": [],
"ret": "V",
},*/
"caller_method": "?",
"addr": 0,
"stack": get_stack()
}
});
return this.newInstance();
};
// Constructor.newInstance(..args)
Constructor.newInstance.overload(
"[Ljava.lang.Object;"
).implementation = function (args) {
send({
"type": "cnstr-new-isnt",
"data": {
"constructor": get_constr_dsc(this),
/*
{
"name": "<init>",
"class": this.getDeclaringClass().getName(),
"args": this.getParameterTypes().map((argty) => argty.getName()),
"ret": "V",
},
*/
"caller_method": "?",
"addr": 0,
"stack": get_stack()
}
});
return this.newInstance(args);
};
// ****** Dynamic Class Loading ******
// DexFile.openDexFileNative(sourceName, outputName, flags, loader, elements): load .dex from file
// See https://cs.android.com/android/platform/superproject/main/+/main:libcore/dalvik/src/main/java/dalvik/system/DexFile.java;drc=2f8a31e93fc238a88a48bfeed82557e07e1d5003;l=477
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/native/dalvik_system_DexFile.cc;drc=3d19fbcc09b1b44928639b06cd0b88f735cd988d;l=368
DexFile.openDexFileNative.overload(
'java.lang.String',
'java.lang.String',
'int',
'java.lang.ClassLoader',
'[Ldalvik.system.DexPathList$Element;',
).implementation = function (
sourceName,
outputName,
flags,
loader,
elements,
) {
let file = File.$new(sourceName);
let path = Path.of(sourceName, []);
let dex = Files.readAllBytes(path);
let b64 = Base64.encodeToString(dex, Base64.DEFAULT.value);
let classloader_class = "";
let classloader_id = System.identityHashCode(loader);
if (loader !== null) {
classloader_class = loader.getClass().descriptorString();
}
send({
"type": "load-dex",
"data": {
"dex": [b64],
"classloader_class": classloader_class,
"classloader": classloader_id,
}
});
let is_wr = file.canWrite();
if (is_wr) {
file.setReadOnly();
}
let result = this.openDexFileNative(
sourceName,
outputName,
flags,
loader,
elements,
);
/* TODO: FIX
if (is_wr) {
file.setWritable(true, false);
}
*/
return result;
};
// DexFile.openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader,elements): load .dex from memory
// See https://cs.android.com/android/platform/superproject/main/+/main:libcore/dalvik/src/main/java/dalvik/system/DexFile.java;drc=2f8a31e93fc238a88a48bfeed82557e07e1d5003;l=431
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/native/dalvik_system_DexFile.cc;l=253;drc=3d19fbcc09b1b44928639b06cd0b88f735cd988d
DexFile.openInMemoryDexFilesNative.overload(
'[Ljava.nio.ByteBuffer;',
'[[B',
'[I',
'[I',
'java.lang.ClassLoader',
'[Ldalvik.system.DexPathList$Element;',
).implementation = function (
bufs,
arrays,
starts,
ends,
loader,
elements,
) {
let dex = [];
// openInMemoryDexFilesNative() checks bufs.length == arrays.length == starts.length === ends.length
for (let i = 0; i < bufs.length; i++) {
let s = starts[i];
let e = starts[i];
// openInMemoryDexFilesNative() checks s < e
let array = arrays[i];
let buf = bufs[i];
let raw = [];
// match code from art/runtime/native/dalvik_system_DexFile.cc commit 3d19fbcc09b1b44928639b06cd0b88f735cd988d
if (array === null) {
raw = Arrays.copyOf([], e-s);
raw = buf.get(s, raw, 0, e-s);
} else {
raw = Arrays.copyOfRange(array, s, e);
}
let b64 = Base64.encodeToString(raw, Base64.DEFAULT.value);
dex.push(b64);
}
let classloader_class = "";
let classloader_id = System.identityHashCode(loader);
if (loader !== null) {
classloader_class = loader.getClass().descriptorString();
}
send({
"type": "load-dex",
"data": {
"dex": dex,
"classloader_class": classloader_class,
"classloader": classloader_id,
}
});
return this.openInMemoryDexFilesNative(
bufs,
arrays,
starts,
ends,
loader,
elements,
);
};
});