1.unity获取动画参数
import "frida-il2cpp-bridge";
declare const console: any;
Il2Cpp.perform(() => {
console.log("🔥 DOTween Full Trace Start");
// =========================================================
// 配置
// =========================================================
const MAX_PRINT_PER_METHOD = 20;
const PRINT_EVERY_AFTER_LIMIT = 200;
const PRINT_ARGS = true;
const PRINT_RET = false;
const callCount: any = {};
// =========================================================
// 工具函数
// =========================================================
function shortClassName(cls: any) {
try {
const ns = cls.namespace;
const name = cls.name;
if (ns && ns.length > 0) {
return ns + "." + name;
}
return name;
} catch (e) {
return "UnknownClass";
}
}
function methodSignature(m: any) {
try {
const params = m.parameters
.map((p: any) => {
try {
return p.type.name + " " + p.name;
} catch (e) {
return "?";
}
})
.join(", ");
const ret = (() => {
try {
return m.returnType.name;
} catch (e) {
return "void";
}
})();
return `${ret} ${m.name}(${params})`;
} catch (e) {
return `${m.name}(?)`;
}
}
function fmtValue(v: any): string {
try {
if (v === null) return "null";
if (v === undefined) return "undefined";
const t = typeof v;
if (
t === "number" ||
t === "boolean" ||
t === "bigint"
) {
return String(v);
}
if (t === "string") {
return `"${v}"`;
}
// Il2Cpp.Object
try {
if (v.class && v.handle) {
const clsName =
shortClassName(v.class);
return `${clsName}@${v.handle}`;
}
} catch (e) {}
// NativePointer
try {
if (v.isNull !== undefined) {
return `${v}`;
}
} catch (e) {}
return String(v);
} catch (e) {
return "<fmt failed>";
}
}
function shouldPrint(key: string): boolean {
if (callCount[key] === undefined) {
callCount[key] = 0;
}
callCount[key]++;
const n = callCount[key];
if (n <= MAX_PRINT_PER_METHOD) {
return true;
}
if (n % PRINT_EVERY_AFTER_LIMIT === 0) {
return true;
}
return false;
}
function findClass(className: string): any {
for (const asm of Il2Cpp.domain.assemblies) {
try {
return asm.image.class(className);
} catch (e) {}
}
return null;
}
// =========================================================
// Unity Object Helpers
// =========================================================
function unityInvoke(
obj: any,
methodName: string
): any {
try {
return obj
.method(methodName)
.invoke();
} catch (e) {
return null;
}
}
function unityName(obj: any): string {
try {
const n =
unityInvoke(obj, "get_name");
if (n) return String(n);
} catch (e) {}
try {
return obj.name;
} catch (e) {}
return "Unknown";
}
function unityGameObject(obj: any): any {
try {
return unityInvoke(
obj,
"get_gameObject"
);
} catch (e) {
return null;
}
}
function unityTransform(obj: any): any {
try {
return unityInvoke(
obj,
"get_transform"
);
} catch (e) {
return null;
}
}
function unityParent(transform: any): any {
try {
return unityInvoke(
transform,
"get_parent"
);
} catch (e) {
return null;
}
}
function getTransformPath(
transform: any
): string {
try {
let path =
unityName(transform);
let parent =
unityParent(transform);
while (parent) {
path =
unityName(parent) +
"/" +
path;
parent =
unityParent(parent);
}
return path;
} catch (e) {
return "UnknownPath";
}
}
function dumpUnityObject(obj: any) {
try {
if (!obj) return;
// =================================================
// Transform
// =================================================
try {
if (
obj.class &&
obj.class.name.indexOf(
"Transform"
) >= 0
) {
console.log(
"🎯 Transform.name =",
unityName(obj)
);
// console.log(
// "🎯 Transform.path =",
// getTransformPath(obj)
// );
return;
}
} catch (e) {}
// =================================================
// Component
// =================================================
try {
const go =
unityGameObject(obj);
if (go) {
console.log(
"🎯 GameObject.name =",
unityName(go)
);
const tr =
unityTransform(obj);
if (tr) {
console.log(
"🎯 GameObject.path =",
getTransformPath(tr)
);
}
return;
}
} catch (e) {}
// =================================================
// Tween.target
// =================================================
try {
const target =
obj.target;
if (target) {
console.log(
"🎯 Tween.target =",
fmtValue(target)
);
const go =
unityGameObject(target);
if (go) {
console.log(
"🎯 Tween.target.name =",
unityName(go)
);
const tr =
unityTransform(target);
if (tr) {
console.log(
"🎯 Tween.target.path =",
getTransformPath(tr)
);
}
}
}
} catch (e) {}
} catch (e) {
console.log(
"❌ dumpUnityObject failed"
);
}
}
// =========================================================
// invoke original
// =========================================================
function invokeOriginal(
method: any,
self: any,
args: any[]
) {
// ctor 不 invoke
if (method.name === ".ctor") {
return;
}
try {
if (method.isStatic) {
return method.invoke(...args);
}
return method.invoke(
self,
...args
);
} catch (e) {
console.log(
"❌ invoke original failed:",
e
);
}
}
// =========================================================
// 方法过滤
// =========================================================
function shouldHook(
className: string,
methodName: string
): boolean {
// getter 太吵
if (methodName.startsWith("get_")) {
return false;
}
// 高频查询方法
const noisy = [
"Elapsed",
"ElapsedPercentage",
"Duration",
"IsPlaying",
"IsActive",
"Update",
"Evaluate"
];
if (
noisy.indexOf(methodName) >= 0
) {
return false;
}
// =====================================================
// DOTween
// =====================================================
if (
className ===
"DG.Tweening.DOTween"
) {
const names = [
"To",
"Sequence",
"DelayedCall",
"Kill",
"Play",
"Pause",
"Restart",
"Clear",
"Init"
];
return (
names.indexOf(methodName) >= 0
);
}
// =====================================================
// ShortcutExtensions
// =====================================================
if (
className.indexOf(
"DG.Tweening.ShortcutExtensions"
) >= 0
) {
return methodName.startsWith(
"DO"
);
}
// =====================================================
// TweenSettingsExtensions
// =====================================================
if (
className ===
"DG.Tweening.TweenSettingsExtensions"
) {
if (
methodName.startsWith("Set")
) {
return true;
}
if (
methodName.startsWith("On")
) {
return true;
}
const names = [
"Append",
"Join",
"Insert",
"Prepend",
"AppendCallback",
"InsertCallback",
"From"
];
return (
names.indexOf(methodName) >= 0
);
}
// =====================================================
// TweenExtensions
// =====================================================
if (
className ===
"DG.Tweening.TweenExtensions"
) {
const names = [
"Play",
"Pause",
"Restart",
"Rewind",
"Kill",
"Complete",
"Goto",
"Flip",
"ForceInit"
];
return (
names.indexOf(methodName) >= 0
);
}
// =====================================================
// TweenManager
// =====================================================
if (
className ===
"DG.Tweening.Core.TweenManager"
) {
const names = [
"AddActiveTween",
"Despawn",
"DoGoto",
"Complete",
"Kill",
"ForceInit"
];
return (
names.indexOf(methodName) >= 0
);
}
return false;
}
// =========================================================
// Hook Class
// =========================================================
function hookClass(className: string) {
const cls =
findClass(className);
if (!cls) {
console.log(
"⚠️ Class not found:",
className
);
return;
}
console.log(
"✅ Found class:",
className
);
for (const m of cls.methods) {
const methodName = m.name;
if (
!shouldHook(
className,
methodName
)
) {
continue;
}
const sig =
methodSignature(m);
const key =
`${className}.${sig}`;
try {
console.log(
"🎯 Hook:",
key
);
m.implementation = function (
...args: any[]
) {
const print =
shouldPrint(key);
const index =
callCount[key];
if (print) {
console.log(
"\n=============================="
);
console.log(
`🔥 [${index}] ${className}.${sig}`
);
// =====================================
// this
// =====================================
try {
// if (this) {
//
// console.log(
// "this =",
// fmtValue(this)
// );
//
// dumpUnityObject(
// this
// );
// }
} catch (e) {}
// =====================================
// args
// =====================================
if (PRINT_ARGS) {
for (
let i = 0;
i < args.length;
i++
) {
const a =
args[i];
console.log(
`arg[${i}] = ${fmtValue(a)}`
);
dumpUnityObject(
a
);
}
}
}
const ret =
invokeOriginal(
m,
this,
args
);
if (
print &&
PRINT_RET
) {
console.log(
"ret =",
fmtValue(ret)
);
dumpUnityObject(ret);
}
if (print) {
console.log(
"==============================\n"
);
}
return ret;
};
} catch (e) {
console.log(
"❌ Hook failed:",
key,
e
);
}
}
}
// =========================================================
// 目标类
// =========================================================
const targetClasses = [
"DG.Tweening.DOTween",
"DG.Tweening.ShortcutExtensions",
"DG.Tweening.ShortcutExtensions46",
"DG.Tweening.ShortcutExtensions43",
"DG.Tweening.ShortcutExtensionsTMPText",
"DG.Tweening.ShortcutExtensionsTextMeshPro",
"DG.Tweening.ShortcutExtensionsModuleUI",
"DG.Tweening.ShortcutExtensionsModulePhysics",
"DG.Tweening.ShortcutExtensionsModulePhysics2D",
"DG.Tweening.ShortcutExtensionsModuleSprite",
"DG.Tweening.ShortcutExtensionsModuleAudio",
"DG.Tweening.TweenSettingsExtensions",
"DG.Tweening.TweenExtensions",
"DG.Tweening.Core.TweenManager"
];
for (const clsName of targetClasses) {
hookClass(clsName);
}
console.log(
"✅ DOTween Full Trace Ready"
);
});