Skip to content

Instantly share code, notes, and snippets.

@SeeFlowerX
Last active March 27, 2024 02:35
Show Gist options
  • Save SeeFlowerX/6bda40e53be51fbb9920aba856fe3584 to your computer and use it in GitHub Desktop.
Save SeeFlowerX/6bda40e53be51fbb9920aba856fe3584 to your computer and use it in GitHub Desktop.
参考了Wallbreaker,把全部父类的的field也打印了,可以直接在hook脚本接入,不需要额外代码
function log(msg) {
console.log(msg);
}
let handleCache = [];
function getRealClassName(object) {
const objClass = Java.use("java.lang.Object").getClass.apply(object);
return Java.use("java.lang.Class").getName.apply(objClass)
}
function getAllFields(cls) {
let fields = [];
let currentClass = cls;
while (currentClass != null) {
fields.push(...currentClass.getDeclaredFields());
currentClass = currentClass.getSuperclass();
}
return fields;
}
function getRealClassNameByHandle(handle) {
let obj = Java.use("java.lang.Object");
let jObject = Java.cast(ptr(handle), obj);
return getRealClassName(jObject);
};
function handledump(handle) {
Java.performNow(function () {
let origClassName = getRealClassNameByHandle(handle);
if (!origClassName) {
return
}
let obj = Java.cast(ptr(handle), Java.use(origClassName));
objectdump(obj);
})
}
function objectdump(obj) {
Java.performNow(function () {
function toHexString(payload) {
let HexDump = Java.use("com.android.internal.util.HexDump");
return HexDump.toHexString(payload);
}
function toHexStringLen(payload, length) {
let HexDump = Java.use("com.android.internal.util.HexDump");
return HexDump.toHexString(payload, 0, length);
}
function hasOwnProperty(obj, name) {
try {
return obj.hasOwnProperty(name) || name in obj;
} catch (e) {
return false;
}
}
function objectToStr(object) {
try {
return Java.use("java.lang.Object").toString.apply(object);
} catch (e) {
return "" + object;
}
}
function getHandle(object) {
try {
object = Java.retain(object);
if (hasOwnProperty(object, '$handle') && object.$handle != undefined) {
handleCache[object.$handle] = object;
return object.$handle;
} else if (hasOwnProperty(object, '$h') && object.$h != undefined) {
handleCache[object.$h] = object;
return object.$h;
} else {
const hashcode = Java.use("java.lang.Object").hashCode.apply(object);
handleCache[hashcode] = object;
return hashcode;
}
} catch (e) {
return null;
}
}
let realClassName = getRealClassName(obj);
obj = Java.cast(obj, Java.use(realClassName));
let clazz = obj.class;
let clazzName = clazz.getSimpleName();
let packageName = clazz.getPackage().getName();
// let clazzFullName = clazz.getCanonicalName();
// let fields = clazz.getDeclaredFields();
let fields = getAllFields(clazz);
let objHandle = getHandle(obj);
let logs = [`[${objHandle}]:${objectToStr(obj)}`, `package ${packageName}`, `class ${clazzName} {`];
let static_fields_logs = [" /* static fields */"];
let instance_fields_logs = [" /* instance fields */"];
let Modifier = Java.use("java.lang.reflect.Modifier");
for (let index = 0; index < fields.length; index++) {
let field = fields[index];
let isStatic = Modifier.isStatic(field.getModifiers());
let fieldType = field.getType().getName();
let fieldName = field.getName();
let fieldValue = typeof obj[fieldName] == "function" ? obj["_" + fieldName] : obj[fieldName];
let fieldObjStr = null;
if (fieldValue !== undefined) {
fieldValue = fieldValue.value;
if (fieldValue != null) {
if (fieldType == "[B") {
// 考虑到数组有时候太大... 根据情况修改脚本进行打印完整的数据吧
if (fieldValue.length > 32) {
fieldObjStr = `len:${fieldValue.length} => ${toHexStringLen(fieldValue, 32)}...`;
} else {
fieldObjStr = `len:${fieldValue.length} => ${toHexString(fieldValue)}`;
}
// fieldObjStr = `${fieldValue}`;
} else {
fieldObjStr = objectToStr(fieldValue);
}
let fieldHandle = getHandle(fieldValue);
if (fieldHandle != null) {
fieldObjStr = `[${fieldHandle}]:${fieldObjStr}`;
}
}
}
if (isStatic) {
static_fields_logs.push(` static ${fieldType} ${fieldName}; => ${fieldObjStr}`);
} else {
instance_fields_logs.push(` ${fieldType} ${fieldName}; => ${fieldObjStr}`);
}
}
if (static_fields_logs.length > 1) {
Array.prototype.push.apply(logs, static_fields_logs);
logs.push("");
}
if (instance_fields_logs.length > 1) {
Array.prototype.push.apply(logs, instance_fields_logs);
logs.push("");
}
logs.push(`}`);
log(`\n[objectdump] => ${logs.join("\n")}`);
})
}
rpc.exports = {
handledump:handledump
}
// 在 objectdump 输出内容后,可以交互式具体打印field的内容,例如`handledump(0x6ad6)`
@SeeFlowerX
Copy link
Author

SeeFlowerX commented May 26, 2023

适合分析某些复杂的Java实例,field巨多的时候,建议在hook implementation中使用objectdump即可,objectdump打印出来的handle,可以用handledump继续打印

不过考虑到某些实例可能会消失,handledump得到的结果中,有的field为null,但在经过被hook的函数时不一定为null,最好是结合objectdump相互印证

效果示意如下:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment