Last active
March 27, 2024 02:35
-
-
Save SeeFlowerX/6bda40e53be51fbb9920aba856fe3584 to your computer and use it in GitHub Desktop.
参考了Wallbreaker,把全部父类的的field也打印了,可以直接在hook脚本接入,不需要额外代码
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
适合分析某些复杂的Java实例,field巨多的时候,建议在hook implementation中使用objectdump即可,objectdump打印出来的handle,可以用handledump继续打印
不过考虑到某些实例可能会消失,handledump得到的结果中,有的field为null,但在经过被hook的函数时不一定为null,最好是结合objectdump相互印证
效果示意如下: