collect stack data
This commit is contained in:
parent
7713f3247a
commit
c11101b46a
5 changed files with 175 additions and 37 deletions
2
frida/.gitignore
vendored
2
frida/.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
__pycache__
|
||||
theseus_frida/StackConsumer.dex.b64
|
||||
consumer/build
|
||||
|
|
|
|||
23
frida/consumer/StackConsumer.java
Normal file
23
frida/consumer/StackConsumer.java
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package theseus.android;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.util.ArrayList;
|
||||
|
||||
class StackConsumer implements Consumer<StackFrame> {
|
||||
|
||||
public ArrayList<StackFrame> stack;
|
||||
|
||||
public StackConsumer() {
|
||||
this.stack = new ArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(StackFrame frame) {
|
||||
stack.add(frame);
|
||||
}
|
||||
|
||||
public StackFrame[] getStack() {
|
||||
return this.stack.toArray(new StackFrame[] {});
|
||||
}
|
||||
}
|
||||
22
frida/consumer/build.sh
Normal file
22
frida/consumer/build.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
JAVAC='/usr/lib/jvm/java-17-openjdk/bin/javac'
|
||||
SDK_TOOLS="${HOME}/Android/Sdk/"
|
||||
VERSION='34.0.0'
|
||||
D8="${SDK_TOOLS}/build-tools/${VERSION}/d8"
|
||||
VERSION_B=$(echo "${VERSION}" | sed 's/\..*//')
|
||||
ANDROID_JAR="${SDK_TOOLS}/platforms/android-${VERSION_B}/android.jar"
|
||||
|
||||
FOLDER=$(dirname "$(realpath $0)")
|
||||
BUILD_F="${FOLDER}/build"
|
||||
OUT_FILE="${FOLDER}/../theseus_frida/StackConsumer.dex.b64"
|
||||
rm -r "${BUILD_F}"
|
||||
mkdir "${BUILD_F}"
|
||||
|
||||
"${JAVAC}" -d "${BUILD_F}" -classpath "${ANDROID_JAR}" "${FOLDER}/StackConsumer.java"
|
||||
|
||||
mkdir "${BUILD_F}/classes"
|
||||
"${D8}" --classpath "${ANDROID_JAR}" "${BUILD_F}/theseus/android/StackConsumer.class" --output "${BUILD_F}/classes"
|
||||
|
||||
base64 "${BUILD_F}/classes/classes.dex" > "${OUT_FILE}"
|
||||
|
|
@ -8,6 +8,7 @@ import frida # type: ignore
|
|||
from androguard.core.apk import get_apkid # type: ignore
|
||||
|
||||
FRIDA_SCRIPT = Path(__file__).parent / "hook.js"
|
||||
STACK_CONSUMER_B64 = Path(__file__).parent / "StackConsumer.dex.b64"
|
||||
|
||||
|
||||
# Define handler to event generated by the scripts
|
||||
|
|
@ -25,52 +26,68 @@ def on_message(message, data):
|
|||
print("[on_message] message:", message)
|
||||
|
||||
|
||||
def get_ty(java_name: str) -> str:
|
||||
"""Return the android name from the java name of a class / type"""
|
||||
# TODO: array
|
||||
# TODO: scalar
|
||||
if java_name == "V": # tmp stub
|
||||
return "V"
|
||||
return f"L{java_name.replace('.', '/')};"
|
||||
def print_stack(stack, prefix: str):
|
||||
for frame in stack:
|
||||
native = ""
|
||||
if frame["is_native"]:
|
||||
native = " (native)"
|
||||
print(f"{prefix}{frame['method']}:{frame['bytecode_index']}{native}")
|
||||
|
||||
|
||||
def get_method_id(method_data) -> str:
|
||||
"""Get a method descriptor from the different elements collected from the methods."""
|
||||
name = method_data["name"]
|
||||
ret = get_ty(method_data["ret"])
|
||||
cls = get_ty(method_data["class"])
|
||||
args = "".join(map(get_ty, method_data["args"]))
|
||||
return f"{cls}->{name}({args}){ret}"
|
||||
# def get_ty(java_name: str) -> str:
|
||||
# """Return the android name from the java name of a class / type"""
|
||||
# # TODO: array
|
||||
# # TODO: scalar
|
||||
# if java_name == "V": # tmp stub
|
||||
# return "V"
|
||||
# return f"L{java_name.replace('.', '/')};"
|
||||
|
||||
|
||||
# def get_method_id(method_data) -> str:
|
||||
# """Get a method descriptor from the different elements collected from the methods."""
|
||||
# name = method_data["name"]
|
||||
# ret = get_ty(method_data["ret"])
|
||||
# cls = get_ty(method_data["class"])
|
||||
# args = "".join(map(get_ty, method_data["args"]))
|
||||
# return f"{cls}->{name}({args}){ret}"
|
||||
|
||||
|
||||
def handle_invoke_data(data):
|
||||
method = get_method_id(data["method"])
|
||||
caller_method = "?" # get_method_id(data["caller_method"])
|
||||
addr = data["addr"]
|
||||
method = data["method"]
|
||||
# caller_method = "?" # get_method_id(data["caller_method"])
|
||||
# addr = data["addr"]
|
||||
print("Method.Invoke:")
|
||||
print(f" called: {method}")
|
||||
print(f" by: {caller_method}")
|
||||
print(f" at: 0x{addr:08x}")
|
||||
print(f" stack:")
|
||||
print_stack(data["stack"], " ")
|
||||
# print(f" by: {caller_method}")
|
||||
# print(f" at: 0x{addr:08x}")
|
||||
|
||||
|
||||
def handle_class_new_inst_data(data):
|
||||
constructor = get_method_id(data["constructor"])
|
||||
caller_method = "?" # get_method_id(data["caller_method"])
|
||||
addr = data["addr"]
|
||||
constructor = data["constructor"]
|
||||
# caller_method = "?" # get_method_id(data["caller_method"])
|
||||
# addr = data["addr"]
|
||||
print("Class.NewInstance:")
|
||||
print(f" called: {constructor}")
|
||||
print(f" by: {caller_method}")
|
||||
print(f" at: 0x{addr:08x}")
|
||||
print(f" stack:")
|
||||
print_stack(data["stack"], " ")
|
||||
# print(f" by: {caller_method}")
|
||||
# print(f" at: 0x{addr:08x}")
|
||||
|
||||
|
||||
def handle_cnstr_new_inst_data(data):
|
||||
constructor = get_method_id(data["constructor"])
|
||||
caller_method = "?" # get_method_id(data["caller_method"])
|
||||
addr = data["addr"]
|
||||
constructor = data["constructor"]
|
||||
if not constructor.startswith("Lcom/example/theseus"):
|
||||
return
|
||||
# caller_method = "?" # get_method_id(data["caller_method"])
|
||||
# addr = data["addr"]
|
||||
print("Constructor.newInstance:")
|
||||
print(f" called: {constructor}")
|
||||
print(f" by: {caller_method}")
|
||||
print(f" at: 0x{addr:08x}")
|
||||
print(f" stack:")
|
||||
print_stack(data["stack"], " ")
|
||||
# print(f" by: {caller_method}")
|
||||
# print(f" at: 0x{addr:08x}")
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -105,6 +122,11 @@ def main():
|
|||
|
||||
with FRIDA_SCRIPT.open("r") as file:
|
||||
script = file.read()
|
||||
with STACK_CONSUMER_B64.open("r") as file:
|
||||
script = script.replace(
|
||||
"<PYTHON REPLACE StackConsumer.dex.b64>",
|
||||
file.read().replace("\n", "").strip(),
|
||||
)
|
||||
|
||||
pid = device.spawn([app])
|
||||
session = device.attach(pid)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,69 @@
|
|||
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");
|
||||
|
|
@ -9,14 +73,14 @@ Java.perform(() => {
|
|||
send({
|
||||
"type": "invoke",
|
||||
"data": {
|
||||
"method": {
|
||||
"method": get_method_dsc(this),
|
||||
/*{
|
||||
"name": this.getName(),
|
||||
"class": this.getDeclaringClass().getName(),
|
||||
"args": this.getParameterTypes().map((argty) => argty.getName() ),
|
||||
"ret": this.getReturnType().getName(),
|
||||
},
|
||||
"caller_method": "?",
|
||||
"addr": 0,
|
||||
},*/
|
||||
"stack": get_stack()
|
||||
}
|
||||
});
|
||||
return this.invoke(obj, args);
|
||||
|
|
@ -26,14 +90,16 @@ Java.perform(() => {
|
|||
send({
|
||||
"type": "class-new-inst",
|
||||
"data": {
|
||||
"constructor": {
|
||||
"constructor": this.descriptorString() + "-><init>()V",
|
||||
/*{
|
||||
"name": "<init>",
|
||||
"class": this.getName(),
|
||||
"args": [],
|
||||
"ret": "V",
|
||||
},
|
||||
},*/
|
||||
"caller_method": "?",
|
||||
"addr": 0,
|
||||
"stack": get_stack()
|
||||
}
|
||||
});
|
||||
return this.newInstance();
|
||||
|
|
@ -44,19 +110,22 @@ Java.perform(() => {
|
|||
send({
|
||||
"type": "cnstr-new-isnt",
|
||||
"data": {
|
||||
"constructor": {
|
||||
"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);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue