Files

1888 lines
231 KiB
JavaScript
Raw Permalink Normal View History

2026-07-19 23:33:52 +03:30
📦
236304 /scripts/sample-hook.js
var _i=Object.defineProperty;var mi=(t,e)=>{for(var n in e)_i(t,n,{get:e[n],enumerable:!0})};var we=[],be=[],Gt="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(let t=0,e=Gt.length;t<e;++t)we[t]=Gt[t],be[Gt.charCodeAt(t)]=t;be[45]=62;be[95]=63;function gi(t){let e=t.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4");let n=t.indexOf("=");n===-1&&(n=e);let r=n===e?0:4-n%4;return[n,r]}function bi(t,e,n){return(e+n)*3/4-n}function Cr(t){let e=gi(t),n=e[0],r=e[1],o=new Uint8Array(bi(t,n,r)),i=0,s=r>0?n-4:n,c;for(c=0;c<s;c+=4){let a=be[t.charCodeAt(c)]<<18|be[t.charCodeAt(c+1)]<<12|be[t.charCodeAt(c+2)]<<6|be[t.charCodeAt(c+3)];o[i++]=a>>16&255,o[i++]=a>>8&255,o[i++]=a&255}if(r===2){let a=be[t.charCodeAt(c)]<<2|be[t.charCodeAt(c+1)]>>4;o[i++]=a&255}if(r===1){let a=be[t.charCodeAt(c)]<<10|be[t.charCodeAt(c+1)]<<4|be[t.charCodeAt(c+2)]>>2;o[i++]=a>>8&255,o[i++]=a&255}return o}function yi(t){return we[t>>18&63]+we[t>>12&63]+we[t>>6&63]+we[t&63]}function Ei(t,e,n){let r=[];for(let o=e;o<n;o+=3){let i=(t[o]<<16&16711680)+(t[o+1]<<8&65280)+(t[o+2]&255);r.push(yi(i))}return r.join("")}function $t(t){let e=t.length,n=e%3,r=[],o=16383;for(let i=0,s=e-n;i<s;i+=o)r.push(Ei(t,i,i+o>s?s:i+o));if(n===1){let i=t[e-1];r.push(we[i>>2]+we[i<<4&63]+"==")}else if(n===2){let i=(t[e-2]<<8)+t[e-1];r.push(we[i>>10]+we[i>>4&63]+we[i<<2&63]+"=")}return r.join("")}function tt(t,e,n,r,o){let i,s,c=o*8-r-1,a=(1<<c)-1,l=a>>1,d=-7,p=n?o-1:0,f=n?-1:1,u=t[e+p];for(p+=f,i=u&(1<<-d)-1,u>>=-d,d+=c;d>0;)i=i*256+t[e+p],p+=f,d-=8;for(s=i&(1<<-d)-1,i>>=-d,d+=r;d>0;)s=s*256+t[e+p],p+=f,d-=8;if(i===0)i=1-l;else{if(i===a)return s?NaN:(u?-1:1)*(1/0);s=s+Math.pow(2,r),i=i-l}return(u?-1:1)*s*Math.pow(2,i-r)}function Ht(t,e,n,r,o,i){let s,c,a,l=i*8-o-1,d=(1<<l)-1,p=d>>1,f=o===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=r?0:i-1,_=r?1:-1,h=e<0||e===0&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(c=isNaN(e)?1:0,s=d):(s=Math.floor(Math.log(e)/Math.LN2),e*(a=Math.pow(2,-s))<1&&(s--,a*=2),s+p>=1?e+=f/a:e+=f*Math.pow(2,1-p),e*a>=2&&(s++,a/=2),s+p>=d?(c=0,s=d):s+p>=1?(c=(e*a-1)*Math.pow(2,o),s=s+p):(c=e*Math.pow(2,p-1)*Math.pow(2,o),s=0));o>=8;)t[n+u]=c&255,u+=_,c/=256,o-=8;for(s=s<<o|c,l+=o;l>0;)t[n+u]=s&255,u+=_,s/=256,l-=8;t[n+u-_]|=h*128}var wi={INSPECT_MAX_BYTES:50},Zt=2147483647;m.TYPED_ARRAY_SUPPORT=!0;Object.defineProperty(m.prototype,"parent",{enumerable:!0,get:function(){if(m.isBuffer(this))return this.buffer}});Object.defineProperty(m.prototype,"offset",{enumerable:!0,get:function(){if(m.isBuffer(this))return this.byteOffset}});function Ae(t){if(t>Zt)throw new RangeError('The value "'+t+'" is invalid for option "size"');let e=new Uint8Array(t);return Object.setPrototypeOf(e,m.prototype),e}function m(t,e,n){if(typeof t=="number"){if(typeof e=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return Qt(t)}return xr(t,e,n)}m.poolSize=8192;function xr(t,e,n){if(typeof t=="string")return Ci(t,e);if(ArrayBuffer.isView(t))return Ti(t);if(t==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(t instanceof ArrayBuffer||t&&t.buffer instanceof ArrayBuffer||t instanceof SharedArrayBuffer||t&&t.buffer instanceof SharedArrayBuffer)return Wt(t,e,n);if(typeof t=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');let r=t.valueOf&&t.valueOf();if(r!=null&&r!==t)return m.from(r,e,n);let o=Ai(t);if(o)return o;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof t[Symbol.toPrimitive]=="function")return m.from(t[Symbol.toPrimitive]("string"),e,n);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t)}m.from=function(t,e,n){return xr(t,e,n)};Object.setPrototypeOf(m.prototype,Uint8Array.prototype);Object.setPrototypeOf(m,Uint8Array);function Nr(t){if(typeof t!="number")throw new TypeError('"size" argument must be of type number');if(t<0)throw new RangeError('
`)){let i=o.split(" "),s=i[4];if(!s.startsWith("/apex/com.android.art"))continue;let c=i[10];s.includes("@")?n.set(c,s.split("@")[1]):e=c}let r=n.get(e);return r!==void 0?parseInt(r):to()}catch{return to()}}function to(){return ee()*1e7}var Tn=null,zc=92;function Gn(t){Tn===null&&(Tn=new NativeFunction(Process.getModuleByName("libc.so").getExportByName("__system_property_get"),"int",["pointer","pointer"],W));let e=Memory.alloc(zc);return Tn(Memory.allocUtf8String(t),e),e.readUtf8String()}function ye(t,e,n){let r=cc(t,e),o=Ft(e).toString();if(it[o]=n,r(e.handle),it[o]!==void 0)throw delete it[o],new Error("Unable to perform state transition; please file a bug")}function Vc(t,e){let n=new NativeCallback(Jc,"void",["pointer"]);return go(t,e,n)}function Jc(t){let e=t.toString(),n=it[e];delete it[e],n(t)}function $n(t){let e=J(),n=e.artThreadList;e["art::ThreadList::SuspendAll"](n,Memory.allocUtf8String("frida"),!1?1:0);try{t()}finally{e["art::ThreadList::ResumeAll"](n)}}var kn=class{constructor(e){let n=Memory.alloc(4*v),r=n.add(v);n.writePointer(r);let o=new NativeCallback((i,s)=>e(s)===!0?1:0,"bool",["pointer","pointer"]);r.add(2*v).writePointer(o),this.handle=n,this._onVisit=o}};function Hn(t){return J()["art::ClassLinker::VisitClasses"]instanceof NativeFunction?new kn(t):new NativeCallback(n=>t(n)===!0?1:0,"bool",["pointer","pointer"])}var Mn=class{constructor(e){let n=Memory.alloc(4*v),r=n.add(v);n.writePointer(r);let o=new NativeCallback((i,s)=>{e(s)},"void",["pointer","pointer"]);r.add(2*v).writePointer(o),this.handle=n,this._onVisit=o}};function Zn(t){return new Mn(t)}var Gc={"include-inlined-frames":0,"skip-inlined-frames":1},jn=class{constructor(e,n,r,o=0,i=!0){let s=J(),c=512,a=3*v,l=Memory.alloc(c+a);s["art::StackVisitor::StackVisitor"](l,e,n,Gc[r],o,i?1:0);let d=l.add(c);l.writePointer(d);let p=new NativeCallback(this._visitFrame.bind(this),"bool",["pointer"]);d.add(2*v).writePointer(p),this.handle=l,this._onVisitFrame=p;let f=l.add(v===4?12:24);this._curShadowFrame=f,this._curQuickFrame=f.add(v),this._curQuickFramePc=f.add(2*v),this._curOatQuickMethodHeader=f.add(3*v),this._getMethodImpl=s["art::StackVisitor::GetMethod"],this._descLocImpl=s["art::StackVisitor::DescribeLocation"],this._getCQFIImpl=s["art::StackVisitor::GetCurrentQuickFrameInfo"]}walkStack(e=!1){J()["art::StackVisitor::WalkStack"](this.handle,e?1:0)}_visitFrame(){return this.visitFrame()?1:0}visitFrame(){throw new Error("Subclass must implement visitFrame")}getMethod(){let e=this._getMethodImpl(this.handle);return e.isNull()?null:new Nt(e)}getCurrentQuickFramePc(){return this._curQuickFramePc.readPointer()}getCurrentQuickFrame(){return this._curQuickFrame.readPointer()}getCurrentShadowFrame(){return this._curShadowFrame.readPointer()}describeLocation(){let e=new jt;return this._descLocImpl(e,this.handle),e.disposeToString()}getCurrentOatQuickMethodHeader(){return this._curOatQuickMethodHeader.readPointer()}getCurrentQuickFrameInfo(){return this._getCQFIImpl(this.handle)}},Nt=class{constructor(e){this.handle=e}prettyMethod(e=!0){let n=new jt;return J()["art::ArtMethod::PrettyMethod"](n,this.handle,e?1:0),n.disposeToString()}toString(){return`ArtMethod(handle=${this.handle})`}};function $c(t){return function(e){let n=Memory.alloc(12);return lc(t)(n,e),{frameSizeInBytes:n.readU32(),coreSpillMask:n.add(4).readU32(),fpSpillMask:n.add(8).readU32()}}}function Hc(t){let e=NULL;switch(Process.arch){case"ia32":e=Ze(32,n=>{n.putMovRegRegOffsetPtr("ecx","esp",4),n.putMovRegRegOffsetPtr("edx","esp",8),n.putCallAddressWithArguments(t,["ecx","edx"]),n.putMovRegReg("esp","ebp"),n.putPopReg("ebp"),n.putRet()});break;case"x64":e=Ze(32,n=>{n.putPushReg("rdi"),n.putCallAddressWithArguments(t,["rsi"]),n.putPopReg("rdi"),n.putMovRegPtrReg("rdi","rax"),n.putMovRegOffsetPtrReg("rdi",8,"edx"),n.putRet()});break;case"arm":e=Ze(16,n=>{n.putCallAddressWithArguments(t,["r0","r1"]),n.putPopRegs(["r0","lr"]),n.putMovRegReg("pc","lr")});break;case"arm64":e=Ze(64,n=>{n.putPushRegReg("x0","lr"),n.putCallAddressWithArguments(t,["x1"]),n.putPopRegReg("x2","lr"),n.putStrRegRegOffse
#include <gum/guminterceptor.h>
extern GMutex lock;
extern GHashTable * methods;
extern GHashTable * replacements;
extern gpointer last_seen_art_method;
extern gpointer get_oat_quick_method_header_impl (gpointer method, gpointer pc);
void
init (void)
{
g_mutex_init (&lock);
methods = g_hash_table_new_full (NULL, NULL, NULL, NULL);
replacements = g_hash_table_new_full (NULL, NULL, NULL, NULL);
}
void
finalize (void)
{
g_hash_table_unref (replacements);
g_hash_table_unref (methods);
g_mutex_clear (&lock);
}
gboolean
is_replacement_method (gpointer method)
{
gboolean is_replacement;
g_mutex_lock (&lock);
is_replacement = g_hash_table_contains (replacements, method);
g_mutex_unlock (&lock);
return is_replacement;
}
gpointer
get_replacement_method (gpointer original_method)
{
gpointer replacement_method;
g_mutex_lock (&lock);
replacement_method = g_hash_table_lookup (methods, original_method);
g_mutex_unlock (&lock);
return replacement_method;
}
void
set_replacement_method (gpointer original_method,
gpointer replacement_method)
{
g_mutex_lock (&lock);
g_hash_table_insert (methods, original_method, replacement_method);
g_hash_table_insert (replacements, replacement_method, original_method);
g_mutex_unlock (&lock);
}
void
synchronize_replacement_methods (guint quick_code_offset,
void * nterp_entrypoint,
void * quick_to_interpreter_bridge)
{
GHashTableIter iter;
gpointer hooked_method, replacement_method;
g_mutex_lock (&lock);
g_hash_table_iter_init (&iter, methods);
while (g_hash_table_iter_next (&iter, &hooked_method, &replacement_method))
{
void ** quick_code;
*((uint32_t *) replacement_method) = *((uint32_t *) hooked_method);
quick_code = hooked_method + quick_code_offset;
if (*quick_code == nterp_entrypoint)
*quick_code = quick_to_interpreter_bridge;
}
g_mutex_unlock (&lock);
}
void
delete_replacement_method (gpointer original_method)
{
gpointer replacement_method;
g_mutex_lock (&lock);
replacement_method = g_hash_table_lookup (methods, original_method);
if (replacement_method != NULL)
{
g_hash_table_remove (methods, original_method);
g_hash_table_remove (replacements, replacement_method);
}
g_mutex_unlock (&lock);
}
gpointer
translate_method (gpointer method)
{
gpointer translated_method;
g_mutex_lock (&lock);
translated_method = g_hash_table_lookup (replacements, method);
g_mutex_unlock (&lock);
return (translated_method != NULL) ? translated_method : method;
}
gpointer
find_replacement_method_from_quick_code (gpointer method,
gpointer thread)
{
gpointer replacement_method;
gpointer managed_stack;
gpointer top_quick_frame;
gpointer link_managed_stack;
gpointer * link_top_quick_frame;
replacement_method = get_replacement_method (method);
if (replacement_method == NULL)
return NULL;
/*
* Stack check.
*
* Return NULL to indicate that the original method should be invoked, otherwise
* return a pointer to the replacement ArtMethod.
*
* If the caller is our own JNI replacement stub, then a stack transition must
* have been pushed onto the current thread's linked list.
*
* Therefore, we invoke the original method if the following conditions are met:
* 1- The current managed stack is empty.
* 2- The ArtMethod * inside the linked managed stack's top quick frame is the
* same as our replacement.
*/
managed_stack = thread + ${n.managedStack};
top_quick_frame = *((gpointer *) (managed_stack + ${r.topQuickFrame}));
if (top_quick_frame != NULL)
return replacement_method;
link_managed_stack = *((gpointer *) (managed_stack + ${r.link}));
if (link_managed_stack == NULL)
return replacement_method;
link_top_quick_frame = GSIZE_TO_POINTER (*((gsize *) (link_managed_stack + ${r.topQuickFrame})) & ~((gsize) 1));
if (link_top_quick_frame == NULL || *link_top_quick_frame != replacement_method)
return replacement_method;
return NULL;
}
void
on_interpreter_do_call (GumInvocationContext * ic)
{
gpointer method, replacement_method;
method = gum_invocation_context_get_nth_argument (ic, 0);
replacement_method = get_replacement_method (method);
if (replacement_method != NULL)
gum_invocation_context_replace_nth_argument (ic, 0, replacement_method);
}
gpointer
on_art_method_get_oat_quick_method_header (gpointer method,
gpointer pc)
{
if (is_replacement_method (method))
return NULL;
return get_oat_quick_method_header_impl (method, pc);
}
void
on_art_method_pretty_method (GumInvocationContext * ic)
{
const guint this_arg_index = ${Process.arch==="arm64"?0:1};
gpointer method;
method = gum_invocation_context_get_nth_argument (ic, this_arg_index);
if (method == NULL)
gum_invocation_context_replace_nth_argument (ic, this_arg_index, last_seen_art_method);
else
last_seen_art_method = method;
}
void
on_leave_gc_concurrent_copying_copying_phase (GumInvocationContext * ic)
{
GHashTableIter iter;
gpointer hooked_method, replacement_method;
g_mutex_lock (&lock);
g_hash_table_iter_init (&iter, methods);
while (g_hash_table_iter_next (&iter, &hooked_method, &replacement_method))
*((uint32_t *) replacement_method) = *((uint32_t *) hooked_method);
g_mutex_unlock (&lock);
}
`,i=8,s=v,c=v,a=v,d=Memory.alloc(i+s+c+a),p=d.add(i),f=p.add(s),u=f.add(c),_=t.find(v===4?"_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj":"_ZN3art9ArtMethod23GetOatQuickMethodHeaderEm"),h=new CModule(o,{lock:d,methods:p,replacements:f,last_seen_art_method:u,get_oat_quick_method_header_impl:_??ptr("0xdeadbeef")}),g={exceptions:"propagate",scheduling:"exclusive"};return{handle:h,replacedMethods:{isReplacement:new NativeFunction(h.is_replacement_method,"bool",["pointer"],g),get:new NativeFunction(h.get_replacement_method,"pointer",["pointer"],g),set:new NativeFunction(h.set_replacement_method,"void",["pointer","pointer"],g),synchronize:new NativeFunction(h.synchronize_replacement_methods,"void",["uint","pointer","pointer"],g),delete:new NativeFunction(h.delete_replacement_method,"void",["pointer"],g),translate:new NativeFunction(h.translate_method,"pointer",["pointer"],g),findReplacementFromQuickCode:h.find_replacement_method_from_quick_code},getOatQuickMethodHeaderImpl:_,hooks:{Interpreter:{doCall:h.on_interpreter_do_call},ArtMethod:{getOatQuickMethodHeader:h.on_art_method_get_oat_quick_method_header,prettyMethod:h.on_art_method_pretty_method},Gc:{copyingPhase:{onLeave:h.on_leave_gc_concurrent_copying_copying_phase},runFlip:{onEnter:h.on_leave_gc_concurrent_copying_copying_phase}}}}}function Kc(t){Kr||(Kr=!0,Qc(t),Yc(),Xc(),el())}function Qc(t){let e=J();[e.artQuickGenericJniTrampoline,e.artQuickToInterpreterBridge,e.artQuickResolutionTrampoline].forEach(r=>{Memory.protect(r,32,"rwx");let o=new Mt(r);o.activate(t),fo.push(o)})}function Yc(){let t=J(),e=ee(),{isApiLevel34OrApexEquivalent:n}=t,r;if(e<=22)r=/^_ZN3art11interpreter6DoCallILb[0-1]ELb[0-1]EEEbPNS_6mirror9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE$/;else if(e<=33&&!n)r=/^_ZN3art11interpreter6DoCallILb[0-1]ELb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE$/;else if(n)r=/^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/;else throw new Error("Unable to find method invocation in ART; please file a bug");let o=t.module,i=[...o.enumerateExports(),...o.enumerateSymbols()].filter(s=>r.test(s.name));if(i.length===0)throw new Error("Unable to find method invocation in ART; please file a bug");for(let s of i)Interceptor.attach(s.address,oe.hooks.Interpreter.doCall)}function Xc(){let t=J(),n=t.module.findSymbolByName("_ZN3art2gc4Heap22CollectGarbageInternalENS0_9collector6GcTypeENS0_7GcCauseEbj");if(n===null)return;let{artNterpEntryPoint:r,artQuickToInterpreterBridge:o}=t,i=_e(t.vm).offset.quickCode;Interceptor.attach(n,{onLeave(){oe.replacedMethods.synchronize(i,r,o)}})}function el(){let t=[["_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE","e90340f8 : ff0ff0ff"],["_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl","7f0f00f9 : 1ffcffff"]],e=J(),n=e.module;for(let[r,o]of t){let i=n.findSymbolByName(r);if(i===null)continue;let s=Memory.scanSync(i,8192,o);if(s.length===0)return;let{artNterpEntryPoint:c,artQuickToInterpreterBridge:a}=e,l=_e(e.vm).offset.quickCode;Interceptor.attach(s[0].address,function(){oe.replacedMethods.synchronize(l,c,a)});return}}function tl(t){if(Wr)return;if(Wr=!0,!rl()){let{getOatQuickMethodHeaderImpl:i}=oe;if(i===null)return;try{Interceptor.replace(i,oe.hooks.ArtMethod.getOatQuickMethodHeader)}catch{}}let e=ee(),n=null,r=J();e>28?n=r.find("_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv"):e>22&&(n=r.find("_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv")),n!==null&&Interceptor.attach(n,oe.hooks.Gc.copyingPhase);let o=null;o=r.find("_ZN3art6Thread15RunFlipFunctionEPS0_"),o===null&&(o=r.find("_ZN3art6Thread15RunFlipFunctionEPS0_b")),o!==null&&Interceptor.attach(o,oe.hooks.Gc.runFlip)}var nl={arm:{signatures:[{pattern:["b0 68","01 30","0c d0","1b 98",":","c0 ff","c0 ff","00 ff","00 2f"],validateMatch:An},{pattern:["d8 f8 08 00","01 30","0c d0","1b 98",":","f0 ff ff 0f","ff ff","00 ff","00 2f"],validateMatch:An},{pattern:
#include <glib.h>
#include <stdbool.h>
#include <string.h>
#include <gum/gumtls.h>
#include <json-glib/json-glib.h>
typedef struct _ArtBacktrace ArtBacktrace;
typedef struct _ArtStackFrame ArtStackFrame;
typedef struct _ArtStackVisitor ArtStackVisitor;
typedef struct _ArtStackVisitorVTable ArtStackVisitorVTable;
typedef struct _ArtClass ArtClass;
typedef struct _ArtMethod ArtMethod;
typedef struct _ArtThread ArtThread;
typedef struct _ArtContext ArtContext;
typedef struct _JNIEnv JNIEnv;
typedef struct _StdString StdString;
typedef struct _StdTinyString StdTinyString;
typedef struct _StdLargeString StdLargeString;
typedef enum {
STACK_WALK_INCLUDE_INLINED_FRAMES,
STACK_WALK_SKIP_INLINED_FRAMES,
} StackWalkKind;
struct _StdTinyString
{
guint8 unused;
gchar data[(3 * sizeof (gpointer)) - 1];
};
struct _StdLargeString
{
gsize capacity;
gsize size;
gchar * data;
};
struct _StdString
{
union
{
guint8 flags;
StdTinyString tiny;
StdLargeString large;
};
};
struct _ArtBacktrace
{
GChecksum * id;
GArray * frames;
gchar * frames_json;
};
struct _ArtStackFrame
{
ArtMethod * method;
gsize dexpc;
StdString description;
};
struct _ArtStackVisitorVTable
{
void (* unused1) (void);
void (* unused2) (void);
bool (* visit) (ArtStackVisitor * visitor);
};
struct _ArtStackVisitor
{
ArtStackVisitorVTable * vtable;
guint8 padding[512];
ArtStackVisitorVTable vtable_storage;
ArtBacktrace * backtrace;
};
struct _ArtMethod
{
guint32 declaring_class;
guint32 access_flags;
};
extern GumTlsKey current_backtrace;
extern void (* perform_art_thread_state_transition) (JNIEnv * env);
extern ArtContext * art_make_context (ArtThread * thread);
extern void art_stack_visitor_init (ArtStackVisitor * visitor, ArtThread * thread, void * context, StackWalkKind walk_kind,
size_t num_frames, bool check_suspended);
extern void art_stack_visitor_walk_stack (ArtStackVisitor * visitor, bool include_transitions);
extern ArtMethod * art_stack_visitor_get_method (ArtStackVisitor * visitor);
extern void art_stack_visitor_describe_location (StdString * description, ArtStackVisitor * visitor);
extern ArtMethod * translate_method (ArtMethod * method);
extern void translate_location (ArtMethod * method, guint32 pc, const gchar ** source_file, gint32 * line_number);
extern void get_class_location (StdString * result, ArtClass * klass);
extern void cxx_delete (void * mem);
extern unsigned long strtoul (const char * str, char ** endptr, int base);
static bool visit_frame (ArtStackVisitor * visitor);
static void art_stack_frame_destroy (ArtStackFrame * frame);
static void append_jni_type_name (GString * s, const gchar * name, gsize length);
static void std_string_destroy (StdString * str);
static gchar * std_string_get_data (StdString * str);
void
init (void)
{
current_backtrace = gum_tls_key_new ();
}
void
finalize (void)
{
gum_tls_key_free (current_backtrace);
}
ArtBacktrace *
_create (JNIEnv * env,
guint limit)
{
ArtBacktrace * bt;
bt = g_new (ArtBacktrace, 1);
bt->id = g_checksum_new (G_CHECKSUM_SHA1);
bt->frames = (limit != 0)
? g_array_sized_new (FALSE, FALSE, sizeof (ArtStackFrame), limit)
: g_array_new (FALSE, FALSE, sizeof (ArtStackFrame));
g_array_set_clear_func (bt->frames, (GDestroyNotify) art_stack_frame_destroy);
bt->frames_json = NULL;
gum_tls_key_set_value (current_backtrace, bt);
perform_art_thread_state_transition (env);
gum_tls_key_set_value (current_backtrace, NULL);
return bt;
}
void
_on_thread_state_transition_complete (ArtThread * thread)
{
ArtContext * context;
ArtStackVisitor visitor = {
.vtable_storage = {
.visit = visit_frame,
},
};
context = art_make_context (thread);
art_stack_visitor_init (&visitor, thread, context, STACK_WALK_SKIP_INLINED_FRAMES, 0, true);
visitor.vtable = &visitor.vtable_storage;
visitor.backtrace = gum_tls_key_get_value (current_backtrace);
art_stack_visitor_walk_stack (&visitor, false);
cxx_delete (context);
}
static bool
visit_frame (ArtStackVisitor * visitor)
{
ArtBacktrace * bt = visitor->backtrace;
ArtStackFrame frame;
const gchar * description, * dexpc_part;
frame.method = art_stack_visitor_get_method (visitor);
art_stack_visitor_describe_location (&frame.description, visitor);
description = std_string_get_data (&frame.description);
if (strstr (description, " '<") != NULL)
goto skip;
dexpc_part = strstr (description, " at dex PC 0x");
if (dexpc_part == NULL)
goto skip;
frame.dexpc = strtoul (dexpc_part + 13, NULL, 16);
g_array_append_val (bt->frames, frame);
g_checksum_update (bt->id, (guchar *) &frame.method, sizeof (frame.method));
g_checksum_update (bt->id, (guchar *) &frame.dexpc, sizeof (frame.dexpc));
return true;
skip:
std_string_destroy (&frame.description);
return true;
}
static void
art_stack_frame_destroy (ArtStackFrame * frame)
{
std_string_destroy (&frame->description);
}
void
_destroy (ArtBacktrace * backtrace)
{
g_free (backtrace->frames_json);
g_array_free (backtrace->frames, TRUE);
g_checksum_free (backtrace->id);
g_free (backtrace);
}
const gchar *
_get_id (ArtBacktrace * backtrace)
{
return g_checksum_get_string (backtrace->id);
}
const gchar *
_get_frames (ArtBacktrace * backtrace)
{
GArray * frames = backtrace->frames;
JsonBuilder * b;
guint i;
JsonNode * root;
if (backtrace->frames_json != NULL)
return backtrace->frames_json;
b = json_builder_new_immutable ();
json_builder_begin_array (b);
for (i = 0; i != frames->len; i++)
{
ArtStackFrame * frame = &g_array_index (frames, ArtStackFrame, i);
gchar * description, * ret_type, * paren_open, * paren_close, * arg_types, * token, * method_name, * class_name;
GString * signature;
gchar * cursor;
ArtMethod * translated_method;
StdString location;
gsize dexpc;
const gchar * source_file;
gint32 line_number;
description = std_string_get_data (&frame->description);
ret_type = strchr (description, '\\'') + 1;
paren_open = strchr (ret_type, '(');
paren_close = strchr (paren_open, ')');
*paren_open = '\\0';
*paren_close = '\\0';
arg_types = paren_open + 1;
token = strrchr (ret_type, '.');
*token = '\\0';
method_name = token + 1;
token = strrchr (ret_type, ' ');
*token = '\\0';
class_name = token + 1;
signature = g_string_sized_new (128);
append_jni_type_name (signature, class_name, method_name - class_name - 1);
g_string_append_c (signature, ',');
g_string_append (signature, method_name);
g_string_append (signature, ",(");
if (arg_types != paren_close)
{
for (cursor = arg_types; cursor != NULL;)
{
gsize length;
gchar * next;
token = strstr (cursor, ", ");
if (token != NULL)
{
length = token - cursor;
next = token + 2;
}
else
{
length = paren_close - cursor;
next = NULL;
}
append_jni_type_name (signature, cursor, length);
cursor = next;
}
}
g_string_append_c (signature, ')');
append_jni_type_name (signature, ret_type, class_name - ret_type - 1);
translated_method = translate_method (frame->method);
dexpc = (translated_method == frame->method) ? frame->dexpc : 0;
get_class_location (&location, GSIZE_TO_POINTER (translated_method->declaring_class));
translate_location (translated_method, dexpc, &source_file, &line_number);
json_builder_begin_object (b);
json_builder_set_member_name (b, "signature");
json_builder_add_string_value (b, signature->str);
json_builder_set_member_name (b, "origin");
json_builder_add_string_value (b, std_string_get_data (&location));
json_builder_set_member_name (b, "className");
json_builder_add_string_value (b, class_name);
json_builder_set_member_name (b, "methodName");
json_builder_add_string_value (b, method_name);
json_builder_set_member_name (b, "methodFlags");
json_builder_add_int_value (b, translated_method->access_flags);
json_builder_set_member_name (b, "fileName");
json_builder_add_string_value (b, source_file);
json_builder_set_member_name (b, "lineNumber");
json_builder_add_int_value (b, line_number);
json_builder_end_object (b);
std_string_destroy (&location);
g_string_free (signature, TRUE);
}
json_builder_end_array (b);
root = json_builder_get_root (b);
backtrace->frames_json = json_to_string (root, FALSE);
json_node_unref (root);
return backtrace->frames_json;
}
static void
append_jni_type_name (GString * s,
const gchar * name,
gsize length)
{
gchar shorty = '\\0';
gsize i;
switch (name[0])
{
case 'b':
if (strncmp (name, "boolean", length) == 0)
shorty = 'Z';
else if (strncmp (name, "byte", length) == 0)
shorty = 'B';
break;
case 'c':
if (strncmp (name, "char", length) == 0)
shorty = 'C';
break;
case 'd':
if (strncmp (name, "double", length) == 0)
shorty = 'D';
break;
case 'f':
if (strncmp (name, "float", length) == 0)
shorty = 'F';
break;
case 'i':
if (strncmp (name, "int", length) == 0)
shorty = 'I';
break;
case 'l':
if (strncmp (name, "long", length) == 0)
shorty = 'J';
break;
case 's':
if (strncmp (name, "short", length) == 0)
shorty = 'S';
break;
case 'v':
if (strncmp (name, "void", length) == 0)
shorty = 'V';
break;
}
if (shorty != '\\0')
{
g_string_append_c (s, shorty);
return;
}
if (length > 2 && name[length - 2] == '[' && name[length - 1] == ']')
{
g_string_append_c (s, '[');
append_jni_type_name (s, name, length - 2);
return;
}
g_string_append_c (s, 'L');
for (i = 0; i != length; i++)
{
gchar ch = name[i];
if (ch != '.')
g_string_append_c (s, ch);
else
g_string_append_c (s, '/');
}
g_string_append_c (s, ';');
}
static void
std_string_destroy (StdString * str)
{
bool is_large = (str->flags & 1) != 0;
if (is_large)
cxx_delete (str->large.data);
}
static gchar *
std_string_get_data (StdString * str)
{
bool is_large = (str->flags & 1) != 0;
return is_large ? str->large.data : str->tiny.data;
}
`,{current_backtrace:Memory.alloc(Process.pointerSize),perform_art_thread_state_transition:r,art_make_context:n["art::Thread::GetLongJumpContext"]??n["art::Context::Create"],art_stack_visitor_init:n["art::StackVisitor::StackVisitor"],art_stack_visitor_walk_stack:n["art::StackVisitor::WalkStack"],art_stack_visitor_get_method:n["art::StackVisitor::GetMethod"],art_stack_visitor_describe_location:n["art::StackVisitor::DescribeLocation"],translate_method:oe.replacedMethods.translate,translate_location:n["art::Monitor::TranslateLocation"],get_class_location:n["art::mirror::Class::GetLocation"],cxx_delete:n.$delete,strtoul:Process.getModuleByName("libc.so").getExportByName("strtoul")}),i=new NativeFunction(o._create,"pointer",["pointer","uint"],W),s=new NativeFunction(o._destroy,"void",["pointer"],W),c={exceptions:"propagate",scheduling:"exclusive"},a=new NativeFunction(o._get_id,"pointer",["pointer"],c),l=new NativeFunction(o._get_frames,"pointer",["pointer"],c),d=go(t,e,o._on_thread_state_transition_complete);o._performData=d,r.writePointer(d),o.backtrace=(f,u)=>{let _=i(f,u),h=new On(_);return Script.bindWeak(h,p.bind(null,_)),h};function p(f){s(f)}return o.getId=f=>a(f).readUtf8String(),o.getFrames=f=>JSON.parse(l(f).readUtf8String()),o}var On=class{constructor(e){this.handle=e}get id(){return st.getId(this.handle)}get frames(){return st.getFrames(this.handle)}};function Wn(){xt.forEach(t=>{t.vtablePtr.writePointer(t.vtable),t.vtableCountPtr.writeS32(t.vtableCount)}),xt.clear();for(let t of fo.splice(0))t.deactivate();for(let t of Vn.splice(0))t.revert()}function Kn(t){return _o(t,"art::jni::JniIdManager::DecodeMethodId")}function no(t){return _o(t,"art::jni::JniIdManager::DecodeFieldId")}function _o(t,e){let n=J(),r=lo(n).offset,o=r.jniIdManager,i=r.jniIdsIndirection;if(o!==null&&i!==null){let s=n.artRuntime;if(s.add(i).readInt()!==Oa){let a=s.add(o).readPointer();return n[e](a,t)}}return t}var dl={ia32:ul,x64:pl,arm:fl,arm64:hl};function ul(t,e,n,r,o){let i=qe(o).offset,s=_e(o).offset,c;return Memory.patchCode(t,128,a=>{let l=new X86Writer(a,{pc:t}),d=new X86Relocator(e,l),p=[15,174,4,36],f=[15,174,12,36];l.putPushax(),l.putMovRegReg("ebp","esp"),l.putAndRegU32("esp",4294967280),l.putSubRegImm("esp",512),l.putBytes(p),l.putMovRegFsU32Ptr("ebx",i.self),l.putCallAddressWithAlignedArguments(oe.replacedMethods.findReplacementFromQuickCode,["eax","ebx"]),l.putTestRegReg("eax","eax"),l.putJccShortLabel("je","restore_registers","no-hint"),l.putMovRegOffsetPtrReg("ebp",7*4,"eax"),l.putLabel("restore_registers"),l.putBytes(f),l.putMovRegReg("esp","ebp"),l.putPopax(),l.putJccShortLabel("jne","invoke_replacement","no-hint");do c=d.readOne();while(c<n&&!d.eoi);d.writeAll(),d.eoi||l.putJmpAddress(e.add(c)),l.putLabel("invoke_replacement"),l.putJmpRegOffsetPtr("eax",s.quickCode),l.flush()}),c}function pl(t,e,n,r,o){let i=qe(o).offset,s=_e(o).offset,c;return Memory.patchCode(t,256,a=>{let l=new X86Writer(a,{pc:t}),d=new X86Relocator(e,l),p=[15,174,4,36],f=[15,174,12,36];l.putPushax(),l.putMovRegReg("rbp","rsp"),l.putAndRegU32("rsp",4294967280),l.putSubRegImm("rsp",512),l.putBytes(p),l.putMovRegGsU32Ptr("rbx",i.self),l.putCallAddressWithAlignedArguments(oe.replacedMethods.findReplacementFromQuickCode,["rdi","rbx"]),l.putTestRegReg("rax","rax"),l.putJccShortLabel("je","restore_registers","no-hint"),l.putMovRegOffsetPtrReg("rbp",8*8,"rax"),l.putLabel("restore_registers"),l.putBytes(f),l.putMovRegReg("rsp","rbp"),l.putPopax(),l.putJccShortLabel("jne","invoke_replacement","no-hint");do c=d.readOne();while(c<n&&!d.eoi);d.writeAll(),d.eoi||l.putJmpAddress(e.add(c)),l.putLabel("invoke_replacement"),l.putJmpRegOffsetPtr("rdi",s.quickCode),l.flush()}),c}function fl(t,e,n,r,o){let i=_e(o).offset,s=e.and(Bn),c;return Memory.patchCode(t,128,a=>{let l=new ThumbWriter(a,{pc:t}),d=new ThumbRelocator(s,l),p=[45,237,16,10],f=[189,236,16,10];l.putPushRegs(["r1","r2","r3","r5","r6","r7","r8","r10","r11","lr"]),l.putBytes(p),l.putSubRegRegImm("sp","sp",8),l.putStrRegRegOffset("r0","sp",0),l.putCallAddressWithArguments(oe.replacedMethods.findReplacementFrom
#include <string.h>
#define kAccStatic 0x0008
#define kAccConstructor 0x00010000
typedef struct _Model Model;
typedef struct _EnumerateMethodsContext EnumerateMethodsContext;
typedef struct _JavaApi JavaApi;
typedef struct _JavaClassApi JavaClassApi;
typedef struct _JavaMethodApi JavaMethodApi;
typedef struct _JavaFieldApi JavaFieldApi;
typedef struct _JNIEnv JNIEnv;
typedef guint8 jboolean;
typedef gint32 jint;
typedef jint jsize;
typedef gpointer jobject;
typedef jobject jclass;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jobjectArray;
typedef gpointer jfieldID;
typedef gpointer jmethodID;
typedef struct _jvmtiEnv jvmtiEnv;
typedef enum
{
JVMTI_ERROR_NONE = 0
} jvmtiError;
typedef struct _ArtApi ArtApi;
typedef guint32 ArtHeapReference;
typedef struct _ArtObject ArtObject;
typedef struct _ArtClass ArtClass;
typedef struct _ArtClassLinker ArtClassLinker;
typedef struct _ArtClassVisitor ArtClassVisitor;
typedef struct _ArtClassVisitorVTable ArtClassVisitorVTable;
typedef struct _ArtMethod ArtMethod;
typedef struct _ArtString ArtString;
typedef union _StdString StdString;
typedef struct _StdStringShort StdStringShort;
typedef struct _StdStringLong StdStringLong;
typedef void (* ArtVisitClassesFunc) (ArtClassLinker * linker, ArtClassVisitor * visitor);
typedef const char * (* ArtGetClassDescriptorFunc) (ArtClass * klass, StdString * storage);
typedef void (* ArtPrettyMethodFunc) (StdString * result, ArtMethod * method, jboolean with_signature);
struct _Model
{
GHashTable * members;
};
struct _EnumerateMethodsContext
{
GPatternSpec * class_query;
GPatternSpec * method_query;
jboolean include_signature;
jboolean ignore_case;
jboolean skip_system_classes;
GHashTable * groups;
};
struct _JavaClassApi
{
jmethodID get_declared_methods;
jmethodID get_declared_fields;
};
struct _JavaMethodApi
{
jmethodID get_name;
jmethodID get_modifiers;
};
struct _JavaFieldApi
{
jmethodID get_name;
jmethodID get_modifiers;
};
struct _JavaApi
{
jvmtiEnv * jvmti;
JavaClassApi clazz;
JavaMethodApi method;
JavaFieldApi field;
};
struct _JNIEnv
{
gpointer * functions;
};
struct _jvmtiEnv
{
gpointer * functions;
};
struct _ArtApi
{
gboolean available;
guint class_offset_ifields;
guint class_offset_methods;
guint class_offset_sfields;
guint class_offset_copied_methods_offset;
guint method_size;
guint method_offset_access_flags;
guint field_size;
guint field_offset_access_flags;
guint alignment_padding;
ArtClassLinker * linker;
ArtVisitClassesFunc visit_classes;
ArtGetClassDescriptorFunc get_class_descriptor;
ArtPrettyMethodFunc pretty_method;
void (* free) (gpointer mem);
};
struct _ArtObject
{
ArtHeapReference klass;
ArtHeapReference monitor;
};
struct _ArtClass
{
ArtObject parent;
ArtHeapReference class_loader;
};
struct _ArtClassVisitor
{
ArtClassVisitorVTable * vtable;
gpointer user_data;
};
struct _ArtClassVisitorVTable
{
void (* reserved1) (ArtClassVisitor * self);
void (* reserved2) (ArtClassVisitor * self);
jboolean (* visit) (ArtClassVisitor * self, ArtClass * klass);
};
struct _ArtString
{
ArtObject parent;
gint32 count;
guint32 hash_code;
union
{
guint16 value[0];
guint8 value_compressed[0];
};
};
struct _StdStringShort
{
guint8 size;
gchar data[(3 * sizeof (gpointer)) - sizeof (guint8)];
};
struct _StdStringLong
{
gsize capacity;
gsize size;
gchar * data;
};
union _StdString
{
StdStringShort s;
StdStringLong l;
};
static void model_add_method (Model * self, const gchar * name, jmethodID id, jint modifiers);
static void model_add_field (Model * self, const gchar * name, jfieldID id, jint modifiers);
static void model_free (Model * model);
static jboolean collect_matching_class_methods (ArtClassVisitor * self, ArtClass * klass);
static gchar * finalize_method_groups_to_json (GHashTable * groups);
static GPatternSpec * make_pattern_spec (const gchar * pattern, jboolean ignore_case);
static gchar * class_name_from_signature (const gchar * signature);
static gchar * format_method_signature (const gchar * name, const gchar * signature);
static void append_type (GString * output, const gchar ** type);
static gpointer read_art_array (gpointer object_base, guint field_offset, guint length_size, guint * length);
static void std_string_destroy (StdString * str);
static gchar * std_string_c_str (StdString * self);
extern GMutex lock;
extern GArray * models;
extern JavaApi java_api;
extern ArtApi art_api;
void
init (void)
{
g_mutex_init (&lock);
models = g_array_new (FALSE, FALSE, sizeof (Model *));
}
void
finalize (void)
{
guint n, i;
n = models->len;
for (i = 0; i != n; i++)
{
Model * model = g_array_index (models, Model *, i);
model_free (model);
}
g_array_unref (models);
g_mutex_clear (&lock);
}
Model *
model_new (jclass class_handle,
gpointer class_object,
JNIEnv * env)
{
Model * model;
GHashTable * members;
jvmtiEnv * jvmti = java_api.jvmti;
gpointer * funcs = env->functions;
jmethodID (* from_reflected_method) (JNIEnv *, jobject) = funcs[7];
jfieldID (* from_reflected_field) (JNIEnv *, jobject) = funcs[8];
jobject (* to_reflected_method) (JNIEnv *, jclass, jmethodID, jboolean) = funcs[9];
jobject (* to_reflected_field) (JNIEnv *, jclass, jfieldID, jboolean) = funcs[12];
void (* delete_local_ref) (JNIEnv *, jobject) = funcs[23];
jobject (* call_object_method) (JNIEnv *, jobject, jmethodID, ...) = funcs[34];
jint (* call_int_method) (JNIEnv *, jobject, jmethodID, ...) = funcs[49];
const char * (* get_string_utf_chars) (JNIEnv *, jstring, jboolean *) = funcs[169];
void (* release_string_utf_chars) (JNIEnv *, jstring, const char *) = funcs[170];
jsize (* get_array_length) (JNIEnv *, jarray) = funcs[171];
jobject (* get_object_array_element) (JNIEnv *, jobjectArray, jsize) = funcs[173];
jsize n, i;
model = g_new (Model, 1);
members = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
model->members = members;
if (jvmti != NULL)
{
gpointer * jf = jvmti->functions - 1;
jvmtiError (* deallocate) (jvmtiEnv *, void * mem) = jf[47];
jvmtiError (* get_class_methods) (jvmtiEnv *, jclass, jint *, jmethodID **) = jf[52];
jvmtiError (* get_class_fields) (jvmtiEnv *, jclass, jint *, jfieldID **) = jf[53];
jvmtiError (* get_field_name) (jvmtiEnv *, jclass, jfieldID, char **, char **, char **) = jf[60];
jvmtiError (* get_field_modifiers) (jvmtiEnv *, jclass, jfieldID, jint *) = jf[62];
jvmtiError (* get_method_name) (jvmtiEnv *, jmethodID, char **, char **, char **) = jf[64];
jvmtiError (* get_method_modifiers) (jvmtiEnv *, jmethodID, jint *) = jf[66];
jint method_count;
jmethodID * methods;
jint field_count;
jfieldID * fields;
char * name;
jint modifiers;
get_class_methods (jvmti, class_handle, &method_count, &methods);
for (i = 0; i != method_count; i++)
{
jmethodID method = methods[i];
get_method_name (jvmti, method, &name, NULL, NULL);
get_method_modifiers (jvmti, method, &modifiers);
model_add_method (model, name, method, modifiers);
deallocate (jvmti, name);
}
deallocate (jvmti, methods);
get_class_fields (jvmti, class_handle, &field_count, &fields);
for (i = 0; i != field_count; i++)
{
jfieldID field = fields[i];
get_field_name (jvmti, class_handle, field, &name, NULL, NULL);
get_field_modifiers (jvmti, class_handle, field, &modifiers);
model_add_field (model, name, field, modifiers);
deallocate (jvmti, name);
}
deallocate (jvmti, fields);
}
else if (art_api.available)
{
gpointer elements;
guint n, i;
const guint field_arrays[] = {
art_api.class_offset_ifields,
art_api.class_offset_sfields
};
guint field_array_cursor;
gboolean merged_fields = art_api.class_offset_sfields == 0;
elements = read_art_array (class_object, art_api.class_offset_methods, sizeof (gsize), NULL);
n = *(guint16 *) (class_object + art_api.class_offset_copied_methods_offset);
for (i = 0; i != n; i++)
{
jmethodID id;
guint32 access_flags;
jboolean is_static;
jobject method, name;
const char * name_str;
jint modifiers;
id = elements + (i * art_api.method_size);
access_flags = *(guint32 *) (id + art_api.method_offset_access_flags);
if ((access_flags & kAccConstructor) != 0)
continue;
is_static = (access_flags & kAccStatic) != 0;
method = to_reflected_method (env, class_handle, id, is_static);
name = call_object_method (env, method, java_api.method.get_name);
name_str = get_string_utf_chars (env, name, NULL);
modifiers = access_flags & 0xffff;
model_add_method (model, name_str, id, modifiers);
release_string_utf_chars (env, name, name_str);
delete_local_ref (env, name);
delete_local_ref (env, method);
}
for (field_array_cursor = 0; field_array_cursor != G_N_ELEMENTS (field_arrays); field_array_cursor++)
{
jboolean is_static;
if (field_arrays[field_array_cursor] == 0)
continue;
if (!merged_fields)
is_static = field_array_cursor == 1;
elements = read_art_array (class_object, field_arrays[field_array_cursor], sizeof (guint32), &n);
for (i = 0; i != n; i++)
{
jfieldID id;
guint32 access_flags;
jobject field, name;
const char * name_str;
jint modifiers;
id = elements + (i * art_api.field_size);
access_flags = *(guint32 *) (id + art_api.field_offset_access_flags);
if (merged_fields)
is_static = (access_flags & kAccStatic) != 0;
field = to_reflected_field (env, class_handle, id, is_static);
name = call_object_method (env, field, java_api.field.get_name);
name_str = get_string_utf_chars (env, name, NULL);
modifiers = access_flags & 0xffff;
model_add_field (model, name_str, id, modifiers);
release_string_utf_chars (env, name, name_str);
delete_local_ref (env, name);
delete_local_ref (env, field);
}
}
}
else
{
jobject elements;
elements = call_object_method (env, class_handle, java_api.clazz.get_declared_methods);
n = get_array_length (env, elements);
for (i = 0; i != n; i++)
{
jobject method, name;
const char * name_str;
jmethodID id;
jint modifiers;
method = get_object_array_element (env, elements, i);
name = call_object_method (env, method, java_api.method.get_name);
name_str = get_string_utf_chars (env, name, NULL);
id = from_reflected_method (env, method);
modifiers = call_int_method (env, method, java_api.method.get_modifiers);
model_add_method (model, name_str, id, modifiers);
release_string_utf_chars (env, name, name_str);
delete_local_ref (env, name);
delete_local_ref (env, method);
}
delete_local_ref (env, elements);
elements = call_object_method (env, class_handle, java_api.clazz.get_declared_fields);
n = get_array_length (env, elements);
for (i = 0; i != n; i++)
{
jobject field, name;
const char * name_str;
jfieldID id;
jint modifiers;
field = get_object_array_element (env, elements, i);
name = call_object_method (env, field, java_api.field.get_name);
name_str = get_string_utf_chars (env, name, NULL);
id = from_reflected_field (env, field);
modifiers = call_int_method (env, field, java_api.field.get_modifiers);
model_add_field (model, name_str, id, modifiers);
release_string_utf_chars (env, name, name_str);
delete_local_ref (env, name);
delete_local_ref (env, field);
}
delete_local_ref (env, elements);
}
g_mutex_lock (&lock);
g_array_append_val (models, model);
g_mutex_unlock (&lock);
return model;
}
static void
model_add_method (Model * self,
const gchar * name,
jmethodID id,
jint modifiers)
{
GHashTable * members = self->members;
gchar * key, type;
const gchar * value;
if (name[0] == '$')
key = g_strdup_printf ("_%s", name);
else
key = g_strdup (name);
type = (modifiers & kAccStatic) != 0 ? 's' : 'i';
value = g_hash_table_lookup (members, key);
if (value == NULL)
g_hash_table_insert (members, key, g_strdup_printf ("m:%c0x%zx", type, id));
else
g_hash_table_insert (members, key, g_strdup_printf ("%s:%c0x%zx", value, type, id));
}
static void
model_add_field (Model * self,
const gchar * name,
jfieldID id,
jint modifiers)
{
GHashTable * members = self->members;
gchar * key, type;
if (name[0] == '$')
key = g_strdup_printf ("_%s", name);
else
key = g_strdup (name);
while (g_hash_table_contains (members, key))
{
gchar * new_key = g_strdup_printf ("_%s", key);
g_free (key);
key = new_key;
}
type = (modifiers & kAccStatic) != 0 ? 's' : 'i';
g_hash_table_insert (members, key, g_strdup_printf ("f:%c0x%zx", type, id));
}
static void
model_free (Model * model)
{
g_hash_table_unref (model->members);
g_free (model);
}
gboolean
model_has (Model * self,
const gchar * member)
{
return g_hash_table_contains (self->members, member);
}
const gchar *
model_find (Model * self,
const gchar * member)
{
return g_hash_table_lookup (self->members, member);
}
gchar *
model_list (Model * self)
{
GString * result;
GHashTableIter iter;
guint i;
const gchar * name;
result = g_string_sized_new (128);
g_string_append_c (result, '[');
g_hash_table_iter_init (&iter, self->members);
for (i = 0; g_hash_table_iter_next (&iter, (gpointer *) &name, NULL); i++)
{
if (i > 0)
g_string_append_c (result, ',');
g_string_append_c (result, '"');
g_string_append (result, name);
g_string_append_c (result, '"');
}
g_string_append_c (result, ']');
return g_string_free (result, FALSE);
}
gchar *
enumerate_methods_art (const gchar * class_query,
const gchar * method_query,
jboolean include_signature,
jboolean ignore_case,
jboolean skip_system_classes)
{
gchar * result;
EnumerateMethodsContext ctx;
ArtClassVisitor visitor;
ArtClassVisitorVTable visitor_vtable = { NULL, };
ctx.class_query = make_pattern_spec (class_query, ignore_case);
ctx.method_query = make_pattern_spec (method_query, ignore_case);
ctx.include_signature = include_signature;
ctx.ignore_case = ignore_case;
ctx.skip_system_classes = skip_system_classes;
ctx.groups = g_hash_table_new_full (NULL, NULL, NULL, NULL);
visitor.vtable = &visitor_vtable;
visitor.user_data = &ctx;
visitor_vtable.visit = collect_matching_class_methods;
art_api.visit_classes (art_api.linker, &visitor);
result = finalize_method_groups_to_json (ctx.groups);
g_hash_table_unref (ctx.groups);
g_pattern_spec_free (ctx.method_query);
g_pattern_spec_free (ctx.class_query);
return result;
}
static jboolean
collect_matching_class_methods (ArtClassVisitor * self,
ArtClass * klass)
{
EnumerateMethodsContext * ctx = self->user_data;
const char * descriptor;
StdString descriptor_storage = { 0, };
gchar * class_name = NULL;
gchar * class_name_copy = NULL;
const gchar * normalized_class_name;
JsonBuilder * group;
size_t class_name_length;
GHashTable * seen_method_names;
gpointer elements;
guint n, i;
if (ctx->skip_system_classes && klass->class_loader == 0)
goto skip_class;
descriptor = art_api.get_class_descriptor (klass, &descriptor_storage);
if (descriptor[0] != 'L')
goto skip_class;
class_name = class_name_from_signature (descriptor);
if (ctx->ignore_case)
{
class_name_copy = g_utf8_strdown (class_name, -1);
normalized_class_name = class_name_copy;
}
else
{
normalized_class_name = class_name;
}
if (!g_pattern_match_string (ctx->class_query, normalized_class_name))
goto skip_class;
group = NULL;
class_name_length = strlen (class_name);
seen_method_names = ctx->include_signature ? NULL : g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
elements = read_art_array (klass, art_api.class_offset_methods, sizeof (gsize), NULL);
n = *(guint16 *) ((gpointer) klass + art_api.class_offset_copied_methods_offset);
for (i = 0; i != n; i++)
{
ArtMethod * method;
guint32 access_flags;
jboolean is_constructor;
StdString method_name = { 0, };
const gchar * bare_method_name;
gchar * bare_method_name_copy = NULL;
const gchar * normalized_method_name;
gchar * normalized_method_name_copy = NULL;
method = elements + (i * art_api.method_size);
access_flags = *(guint32 *) ((gpointer) method + art_api.method_offset_access_flags);
is_constructor = (access_flags & kAccConstructor) != 0;
art_api.pretty_method (&method_name, method, ctx->include_signature);
bare_method_name = std_string_c_str (&method_name);
if (ctx->include_signature)
{
const gchar * return_type_end, * name_begin;
GString * name;
return_type_end = strchr (bare_method_name, ' ');
name_begin = return_type_end + 1 + class_name_length + 1;
if (is_constructor && g_str_has_prefix (name_begin, "<clinit>"))
goto skip_method;
name = g_string_sized_new (64);
if (is_constructor)
{
g_string_append (name, "$init");
g_string_append (name, strchr (name_begin, '>') + 1);
}
else
{
g_string_append (name, name_begin);
}
g_string_append (name, ": ");
g_string_append_len (name, bare_method_name, return_type_end - bare_method_name);
bare_method_name_copy = g_string_free (name, FALSE);
bare_method_name = bare_method_name_copy;
}
else
{
const gchar * name_begin;
name_begin = bare_method_name + class_name_length + 1;
if (is_constructor && strcmp (name_begin, "<clinit>") == 0)
goto skip_method;
if (is_constructor)
bare_method_name = "$init";
else
bare_method_name += class_name_length + 1;
}
if (seen_method_names != NULL && g_hash_table_contains (seen_method_names, bare_method_name))
goto skip_method;
if (ctx->ignore_case)
{
normalized_method_name_copy = g_utf8_strdown (bare_method_name, -1);
normalized_method_name = normalized_method_name_copy;
}
else
{
normalized_method_name = bare_method_name;
}
if (!g_pattern_match_string (ctx->method_query, normalized_method_name))
goto skip_method;
if (group == NULL)
{
group = g_hash_table_lookup (ctx->groups, GUINT_TO_POINTER (klass->class_loader));
if (group == NULL)
{
group = json_builder_new_immutable ();
g_hash_table_insert (ctx->groups, GUINT_TO_POINTER (klass->class_loader), group);
json_builder_begin_object (group);
json_builder_set_member_name (group, "loader");
json_builder_add_int_value (group, klass->class_loader);
json_builder_set_member_name (group, "classes");
json_builder_begin_array (group);
}
json_builder_begin_object (group);
json_builder_set_member_name (group, "name");
json_builder_add_string_value (group, class_name);
json_builder_set_member_name (group, "methods");
json_builder_begin_array (group);
}
json_builder_add_string_value (group, bare_method_name);
if (seen_method_names != NULL)
g_hash_table_add (seen_method_names, g_strdup (bare_method_name));
skip_method:
g_free (normalized_method_name_copy);
g_free (bare_method_name_copy);
std_string_destroy (&method_name);
}
if (seen_method_names != NULL)
g_hash_table_unref (seen_method_names);
if (group == NULL)
goto skip_class;
json_builder_end_array (group);
json_builder_end_object (group);
skip_class:
g_free (class_name_copy);
g_free (class_name);
std_string_destroy (&descriptor_storage);
return TRUE;
}
gchar *
enumerate_methods_jvm (const gchar * class_query,
const gchar * method_query,
jboolean include_signature,
jboolean ignore_case,
jboolean skip_system_classes,
JNIEnv * env)
{
gchar * result;
GPatternSpec * class_pattern, * method_pattern;
GHashTable * groups;
gpointer * ef = env->functions;
jobject (* new_global_ref) (JNIEnv *, jobject) = ef[21];
void (* delete_local_ref) (JNIEnv *, jobject) = ef[23];
jboolean (* is_same_object) (JNIEnv *, jobject, jobject) = ef[24];
jvmtiEnv * jvmti = java_api.jvmti;
gpointer * jf = jvmti->functions - 1;
jvmtiError (* deallocate) (jvmtiEnv *, void * mem) = jf[47];
jvmtiError (* get_class_signature) (jvmtiEnv *, jclass, char **, char **) = jf[48];
jvmtiError (* get_class_methods) (jvmtiEnv *, jclass, jint *, jmethodID **) = jf[52];
jvmtiError (* get_class_loader) (jvmtiEnv *, jclass, jobject *) = jf[57];
jvmtiError (* get_method_name) (jvmtiEnv *, jmethodID, char **, char **, char **) = jf[64];
jvmtiError (* get_loaded_classes) (jvmtiEnv *, jint *, jclass **) = jf[78];
jint class_count, class_index;
jclass * classes;
class_pattern = make_pattern_spec (class_query, ignore_case);
method_pattern = make_pattern_spec (method_query, ignore_case);
groups = g_hash_table_new_full (NULL, NULL, NULL, NULL);
if (get_loaded_classes (jvmti, &class_count, &classes) != JVMTI_ERROR_NONE)
goto emit_results;
for (class_index = 0; class_index != class_count; class_index++)
{
jclass klass = classes[class_index];
jobject loader = NULL;
gboolean have_loader = FALSE;
char * signature = NULL;
gchar * class_name = NULL;
gchar * class_name_copy = NULL;
const gchar * normalized_class_name;
jint method_count, method_index;
jmethodID * methods = NULL;
JsonBuilder * group = NULL;
GHashTable * seen_method_names = NULL;
if (skip_system_classes)
{
if (get_class_loader (jvmti, klass, &loader) != JVMTI_ERROR_NONE)
goto skip_class;
have_loader = TRUE;
if (loader == NULL)
goto skip_class;
}
if (get_class_signature (jvmti, klass, &signature, NULL) != JVMTI_ERROR_NONE)
goto skip_class;
class_name = class_name_from_signature (signature);
if (ignore_case)
{
class_name_copy = g_utf8_strdown (class_name, -1);
normalized_class_name = class_name_copy;
}
else
{
normalized_class_name = class_name;
}
if (!g_pattern_match_string (class_pattern, normalized_class_name))
goto skip_class;
if (get_class_methods (jvmti, klass, &method_count, &methods) != JVMTI_ERROR_NONE)
goto skip_class;
if (!include_signature)
seen_method_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (method_index = 0; method_index != method_count; method_index++)
{
jmethodID method = methods[method_index];
const gchar * method_name;
char * method_name_value = NULL;
char * method_signature_value = NULL;
gchar * method_name_copy = NULL;
const gchar * normalized_method_name;
gchar * normalized_method_name_copy = NULL;
if (get_method_name (jvmti, method, &method_name_value, include_signature ? &method_signature_value : NULL, NULL) != JVMTI_ERROR_NONE)
goto skip_method;
method_name = method_name_value;
if (method_name[0] == '<')
{
if (strcmp (method_name, "<init>") == 0)
method_name = "$init";
else if (strcmp (method_name, "<clinit>") == 0)
goto skip_method;
}
if (include_signature)
{
method_name_copy = format_method_signature (method_name, method_signature_value);
method_name = method_name_copy;
}
if (seen_method_names != NULL && g_hash_table_contains (seen_method_names, method_name))
goto skip_method;
if (ignore_case)
{
normalized_method_name_copy = g_utf8_strdown (method_name, -1);
normalized_method_name = normalized_method_name_copy;
}
else
{
normalized_method_name = method_name;
}
if (!g_pattern_match_string (method_pattern, normalized_method_name))
goto skip_method;
if (group == NULL)
{
if (!have_loader && get_class_loader (jvmti, klass, &loader) != JVMTI_ERROR_NONE)
goto skip_method;
if (loader == NULL)
{
group = g_hash_table_lookup (groups, NULL);
}
else
{
GHashTableIter iter;
jobject cur_loader;
JsonBuilder * cur_group;
g_hash_table_iter_init (&iter, groups);
while (g_hash_table_iter_next (&iter, (gpointer *) &cur_loader, (gpointer *) &cur_group))
{
if (cur_loader != NULL && is_same_object (env, cur_loader, loader))
{
group = cur_group;
break;
}
}
}
if (group == NULL)
{
jobject l;
gchar * str;
l = (loader != NULL) ? new_global_ref (env, loader) : NULL;
group = json_builder_new_immutable ();
g_hash_table_insert (groups, l, group);
json_builder_begin_object (group);
json_builder_set_member_name (group, "loader");
str = g_strdup_printf ("0x%" G_GSIZE_MODIFIER "x", GPOINTER_TO_SIZE (l));
json_builder_add_string_value (group, str);
g_free (str);
json_builder_set_member_name (group, "classes");
json_builder_begin_array (group);
}
json_builder_begin_object (group);
json_builder_set_member_name (group, "name");
json_builder_add_string_value (group, class_name);
json_builder_set_member_name (group, "methods");
json_builder_begin_array (group);
}
json_builder_add_string_value (group, method_name);
if (seen_method_names != NULL)
g_hash_table_add (seen_method_names, g_strdup (method_name));
skip_method:
g_free (normalized_method_name_copy);
g_free (method_name_copy);
deallocate (jvmti, method_signature_value);
deallocate (jvmti, method_name_value);
}
skip_class:
if (group != NULL)
{
json_builder_end_array (group);
json_builder_end_object (group);
}
if (seen_method_names != NULL)
g_hash_table_unref (seen_method_names);
deallocate (jvmti, methods);
g_free (class_name_copy);
g_free (class_name);
deallocate (jvmti, signature);
if (loader != NULL)
delete_local_ref (env, loader);
delete_local_ref (env, klass);
}
deallocate (jvmti, classes);
emit_results:
result = finalize_method_groups_to_json (groups);
g_hash_table_unref (groups);
g_pattern_spec_free (method_pattern);
g_pattern_spec_free (class_pattern);
return result;
}
static gchar *
finalize_method_groups_to_json (GHashTable * groups)
{
GString * result;
GHashTableIter iter;
guint i;
JsonBuilder * group;
result = g_string_sized_new (1024);
g_string_append_c (result, '[');
g_hash_table_iter_init (&iter, groups);
for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &group); i++)
{
JsonNode * root;
gchar * json;
if (i > 0)
g_string_append_c (result, ',');
json_builder_end_array (group);
json_builder_end_object (group);
root = json_builder_get_root (group);
json = json_to_string (root, FALSE);
g_string_append (result, json);
g_free (json);
json_node_unref (root);
g_object_unref (group);
}
g_string_append_c (result, ']');
return g_string_free (result, FALSE);
}
static GPatternSpec *
make_pattern_spec (const gchar * pattern,
jboolean ignore_case)
{
GPatternSpec * spec;
if (ignore_case)
{
gchar * str = g_utf8_strdown (pattern, -1);
spec = g_pattern_spec_new (str);
g_free (str);
}
else
{
spec = g_pattern_spec_new (pattern);
}
return spec;
}
static gchar *
class_name_from_signature (const gchar * descriptor)
{
gchar * result, * c;
result = g_strdup (descriptor + 1);
for (c = result; *c != '\\0'; c++)
{
if (*c == '/')
*c = '.';
}
c[-1] = '\\0';
return result;
}
static gchar *
format_method_signature (const gchar * name,
const gchar * signature)
{
GString * sig;
const gchar * cursor;
gint arg_index;
sig = g_string_sized_new (128);
g_string_append (sig, name);
cursor = signature;
arg_index = -1;
while (TRUE)
{
const gchar c = *cursor;
if (c == '(')
{
g_string_append_c (sig, c);
cursor++;
arg_index = 0;
}
else if (c == ')')
{
g_string_append_c (sig, c);
cursor++;
break;
}
else
{
if (arg_index >= 1)
g_string_append (sig, ", ");
append_type (sig, &cursor);
if (arg_index != -1)
arg_index++;
}
}
g_string_append (sig, ": ");
append_type (sig, &cursor);
return g_string_free (sig, FALSE);
}
static void
append_type (GString * output,
const gchar ** type)
{
const gchar * cursor = *type;
switch (*cursor)
{
case 'Z':
g_string_append (output, "boolean");
cursor++;
break;
case 'B':
g_string_append (output, "byte");
cursor++;
break;
case 'C':
g_string_append (output, "char");
cursor++;
break;
case 'S':
g_string_append (output, "short");
cursor++;
break;
case 'I':
g_string_append (output, "int");
cursor++;
break;
case 'J':
g_string_append (output, "long");
cursor++;
break;
case 'F':
g_string_append (output, "float");
cursor++;
break;
case 'D':
g_string_append (output, "double");
cursor++;
break;
case 'V':
g_string_append (output, "void");
cursor++;
break;
case 'L':
{
gchar ch;
cursor++;
for (; (ch = *cursor) != ';'; cursor++)
{
g_string_append_c (output, (ch != '/') ? ch : '.');
}
cursor++;
break;
}
case '[':
*type = cursor + 1;
append_type (output, type);
g_string_append (output, "[]");
return;
default:
g_string_append (output, "BUG");
cursor++;
}
*type = cursor;
}
void
dealloc (gpointer mem)
{
g_free (mem);
}
static gpointer
read_art_array (gpointer object_base,
guint field_offset,
guint length_size,
guint * length)
{
gpointer result, header;
guint n;
header = GSIZE_TO_POINTER (*(guint64 *) (object_base + field_offset));
if (header != NULL)
{
result = header + length_size;
if (length_size == sizeof (guint32))
n = *(guint32 *) header;
else
n = *(guint64 *) header;
}
else
{
result = NULL;
n = 0;
}
if (length != NULL)
*length = n;
return result;
}
static void
std_string_destroy (StdString * str)
{
if ((str->l.capacity & 1) != 0)
art_api.free (str->l.data);
}
static gchar *
std_string_c_str (StdString * self)
{
if ((self->l.capacity & 1) != 0)
return self->l.data;
return self->s.data;
}
`,ld=/(.+)!([^/]+)\/?([isu]+)?/,Ee=null,Mo=null,Ue=class t{static build(e,n){return ko(n),Mo(e,n,r=>new t(Ee.new(e,r,n)))}static enumerateMethods(e,n,r){ko(r);let o=e.match(ld);if(o===null)throw new Error("Invalid query; format is: class!method -- see documentation of Java.enumerateMethods(query) for details");let i=Memory.allocUtf8String(o[1]),s=Memory.allocUtf8String(o[2]),c=!1,a=!1,l=!1,d=o[3];d!==void 0&&(c=d.indexOf("s")!==-1,a=d.indexOf("i")!==-1,l=d.indexOf("u")!==-1);let p;if(n.jvmti!==null){let f=Ee.enumerateMethodsJvm(i,s,We(c),We(a),We(l),r);try{p=JSON.parse(f.readUtf8String()).map(u=>{let _=ptr(u.loader);return u.loader=_.isNull()?null:_,u})}finally{Ee.dealloc(f)}}else ye(r.vm,r,f=>{let u=Ee.enumerateMethodsArt(i,s,We(c),We(a),We(l));try{let _=n["art::JavaVMExt::AddGlobalRef"],{vm:h}=n;p=JSON.parse(u.readUtf8String()).map(g=>{let b=g.loader;return g.loader=b!==0?_(h,f,ptr(b)):null,g})}finally{Ee.dealloc(u)}});return p}constructor(e){this.handle=e}has(e){return Ee.has(this.handle,Memory.allocUtf8String(e))!==0}find(e){return Ee.find(this.handle,Memory.allocUtf8String(e)).readUtf8String()}list(){let e=Ee.list(this.handle);try{return JSON.parse(e.readUtf8String())}finally{Ee.dealloc(e)}}};function ko(t){Ee===null&&(Ee=dd(t),Mo=ud(Ee,t.vm))}function dd(t){let e=ft(),{jvmti:n=null}=e,{pointerSize:r}=Process,o=8,i=r,s=7*r,c=10*4+5*r,a=o+i+s+c,d=Memory.alloc(a),p=d.add(o),f=p.add(i),{getDeclaredMethods:u,getDeclaredFields:_}=t.javaLangClass(),h=t.javaLangReflectMethod(),g=t.javaLangReflectField(),b=f;[n!==null?n:NULL,u,_,h.getName,h.getModifiers,g.getName,g.getModifiers].forEach(k=>{b=b.writePointer(k).add(r)});let E=f.add(s),{vm:C}=t;if(e.flavor==="art"){let k;if(n!==null)k=[0,0,0,0];else{let j=Jn(C).offset;k=[j.ifields,j.methods,j.sfields,j.copiedMethodsOffset]}let N=_e(C),S=Pt(C),L=E;[1,...k,N.size,N.offset.accessFlags,S.size,S.offset.accessFlags,4294967295].forEach(j=>{L=L.writeUInt(j).add(4)}),[e.artClassLinker.address,e["art::ClassLinker::VisitClasses"],e["art::mirror::Class::GetDescriptor"],e["art::ArtMethod::PrettyMethod"],Process.getModuleByName("libc.so").getExportByName("free")].forEach((j,w)=>{j===void 0&&(j=NULL),L=L.writePointer(j).add(r)})}let A=new CModule(cd,{lock:d,models:p,java_api:f,art_api:E}),M={exceptions:"propagate"},R={exceptions:"propagate",scheduling:"exclusive"};return{handle:A,new:new NativeFunction(A.model_new,"pointer",["pointer","pointer","pointer"],M),has:new NativeFunction(A.model_has,"bool",["pointer","pointer"],R),find:new NativeFunction(A.model_find,"pointer",["pointer","pointer"],R),list:new NativeFunction(A.model_list,"pointer",["pointer"],R),enumerateMethodsArt:new NativeFunction(A.enumerate_methods_art,"pointer",["pointer","pointer","bool","bool","bool"],M),enumerateMethodsJvm:new NativeFunction(A.enumerate_methods_jvm,"pointer",["pointer","pointer","bool","bool","bool","pointer"],M),dealloc:new NativeFunction(A.dealloc,"void",["pointer"],R)}}function ud(t,e){let n=ft();if(n.flavor!=="art")return pd;let r=n["art::JavaVMExt::DecodeGlobal"];return function(o,i,s){let c;return ye(e,i,a=>{let l=r(e,a,o);c=s(l)}),c}}function pd(t,e,n){return n(NULL)}function We(t){return t?1:0}var ht=class{constructor(e,n){this.items=new Map,this.capacity=e,this.destroy=n}dispose(e){let{items:n,destroy:r}=this;n.forEach(o=>{r(o,e)}),n.clear()}get(e){let{items:n}=this,r=n.get(e);return r!==void 0&&(n.delete(e),n.set(e,r)),r}set(e,n,r){let{items:o}=this,i=o.get(e);if(i!==void 0)o.delete(e),this.destroy(i,r);else if(o.size===this.capacity){let s=o.keys().next().value,c=o.get(s);o.delete(s),this.destroy(c,r)}o.set(e,n)}};var _t=1,ar=256,jo=65536,fd=305419896,Ro=32,Oo=12,Po=8,Fo=8,Do=4,Uo=4,Bo=12,hd=0,_d=1,md=2,gd=3,bd=4,yd=5,Ed=6,vd=4096,Sd=4097,wd=4099,Id=8192,Cd=8193,Td=8194,Ad=8195,Ld=8196,xd=8198,Nd=24,kd=28,Md=2,jd=24,zo=m.from([3,0,7,14,0]),ir="Ldalvik/annotation/Throws;",Rd=m.from([0]);function Od(t){let e=new cr,n=Object.assign({},t);return e.addClass(n),e.build()}var cr=class{constructor(){this.classes=[]}addClass(e){this.classes.push(e)}build(){let e=Dd(this.classes),{classes:n,interfaces:r
035`),I.writeUInt32LE(Je,32),I.writeUInt32LE(E,36),I.writeUInt32LE(fd,40),I.writeUInt32LE(Se,44),I.writeUInt32LE(Xe,48),I.writeUInt32LE($,52),I.writeUInt32LE(f.length,56),I.writeUInt32LE(C,60),I.writeUInt32LE(p.length,64),I.writeUInt32LE(M,68),I.writeUInt32LE(s.length,72),I.writeUInt32LE(k,76),I.writeUInt32LE(o.length,80),I.writeUInt32LE(o.length>0?S:0,84),I.writeUInt32LE(i.length,88),I.writeUInt32LE(j,92),I.writeUInt32LE(n.length,96),I.writeUInt32LE(O,100),I.writeUInt32LE(ke,104),I.writeUInt32LE(U,108),Q.forEach((T,P)=>{I.writeUInt32LE(T,C+P*Uo)}),p.forEach((T,P)=>{I.writeUInt32LE(T,M+P*Do)}),s.forEach((T,P)=>{let[B,H,q]=T,Z=k+P*Oo;I.writeUInt32LE(B,Z),I.writeUInt32LE(H,Z+4),I.writeUInt32LE(q!==null?q.offset:0,Z+8)}),o.forEach((T,P)=>{let[B,H,q]=T,Z=S+P*Po;I.writeUInt16LE(B,Z),I.writeUInt16LE(H,Z+2),I.writeUInt32LE(q,Z+4)}),i.forEach((T,P)=>{let[B,H,q]=T,Z=j+P*Fo;I.writeUInt16LE(B,Z),I.writeUInt16LE(H,Z+2),I.writeUInt32LE(q,Z+4)}),n.forEach((T,P)=>{let{interfaces:B,annotationsDirectory:H}=T,q=B!==null?B.offset:0,Z=H!==null?H.offset:0,et=0,ge=O+P*Ro;I.writeUInt32LE(T.index,ge),I.writeUInt32LE(T.accessFlags,ge+4),I.writeUInt32LE(T.superClassIndex,ge+8),I.writeUInt32LE(q,ge+12),I.writeUInt32LE(T.sourceFileIndex,ge+16),I.writeUInt32LE(Z,ge+20),I.writeUInt32LE(T.classData.offset,ge+24),I.writeUInt32LE(et,ge+28)}),l.forEach((T,P)=>{let{items:B}=T,H=F[P];I.writeUInt32LE(B.length,H),B.forEach((q,Z)=>{I.writeUInt32LE(q.offset,H+4+Z*4)})}),z.forEach((T,P)=>{let{offset:B,superConstructor:H}=T,q=1,Z=1,et=1,ge=0,bt=4;I.writeUInt16LE(q,B),I.writeUInt16LE(Z,B+2),I.writeUInt16LE(et,B+4),I.writeUInt16LE(ge,B+6),I.writeUInt32LE(ie[P],B+8),I.writeUInt32LE(bt,B+12),I.writeUInt16LE(4208,B+16),I.writeUInt16LE(H,B+18),I.writeUInt16LE(0,B+20),I.writeUInt16LE(14,B+22)}),a.forEach(T=>{let P=T.offset,B=0,H=0,q=T.methods.length,Z=0;I.writeUInt32LE(B,P),I.writeUInt32LE(H,P+4),I.writeUInt32LE(q,P+8),I.writeUInt32LE(Z,P+12),T.methods.forEach((et,ge)=>{let bt=P+16+ge*8,[fi,hi]=et;I.writeUInt32LE(fi,bt),I.writeUInt32LE(hi.offset,bt+4)})}),r.forEach((T,P)=>{let B=X[P];I.writeUInt32LE(T.types.length,B),T.types.forEach((H,q)=>{I.writeUInt16LE(H,B+4+q*2)})}),c.forEach((T,P)=>{let B=te[P];I.writeUInt32LE(T.types.length,B),T.types.forEach((H,q)=>{I.writeUInt16LE(H,B+4+q*2)})}),ae.forEach((T,P)=>{T.copy(I,Q[P])}),ie.forEach(T=>{zo.copy(I,T)}),Y.forEach((T,P)=>{T.copy(I,d[P].offset)}),ne.forEach((T,P)=>{T.copy(I,n[P].classData.offset)}),I.writeUInt32LE(Te,$);let ce=[[hd,1,_],[_d,f.length,C],[md,p.length,M],[gd,s.length,k]];o.length>0&&ce.push([bd,o.length,S]),ce.push([yd,i.length,j]),ce.push([Ed,n.length,O]),l.forEach((T,P)=>{ce.push([wd,T.items.length,F[P]])}),z.forEach(T=>{ce.push([Cd,1,T.offset])}),a.forEach(T=>{ce.push([xd,1,T.offset])}),he>0&&ce.push([Sd,he,X.concat(te)[0]]),ce.push([Td,f.length,Q[0]]),ie.forEach(T=>{ce.push([Ad,1,T])}),d.forEach(T=>{ce.push([Ld,1,T.offset])}),n.forEach(T=>{ce.push([Id,1,T.classData.offset])}),ce.push([vd,1,$]),ce.forEach((T,P)=>{let[B,H,q]=T,Z=$+4+P*Bo;I.writeUInt16LE(B,Z),I.writeUInt32LE(H,Z+4),I.writeUInt32LE(q,Z+8)});let Ir=new Checksum("sha1");return Ir.update(I.slice(g+b)),m.from(Ir.getDigest()).copy(I,g),I.writeUInt32LE(Gd(I,g),h),I}};function Pd(t){let{instanceFields:e,constructorMethods:n,virtualMethods:r}=t.classData;return m.from([0].concat(me(e.length)).concat(me(n.length)).concat(me(r.length)).concat(e.reduce((i,[s,c])=>i.concat(me(s)).concat(me(c)),[])).concat(n.reduce((i,[s,c,,a])=>i.concat(me(s)).concat(me(c)).concat(me(a||0)),[])).concat(r.reduce((i,[s,c])=>i.concat(me(s)).concat(me(c)).concat([0]),[])))}function Fd(t){let{thrownTypes:e}=t;return m.from([Md].concat(me(t.type)).concat([1]).concat(me(t.value)).concat([kd,e.length]).concat(e.reduce((n,r)=>(n.push(Nd,r),n),[])))}function Dd(t){let e=new Set,n=new Set,r={},o=[],i=[],s={},c=new Set,a=new Set;t.forEach(w=>{let{name:O,superClass:D,sourceFileName:U}=w;e.add("this"),e.add(O),n.add(O),e.add(D),n.add(D),e.add(U),w.interfaces.forEach(F=>{e.add(F),n.add(F)}),w.fields.forEach(F=>{let[z,X]=F;e.add(z),e.add(X),n.add(X),o.push([w.name,X,z])}),w.methods.so
${o.join(`
`)}`)}function Qe(t,e,n,r,o,i,s,c){let a=o.type,l=i.map(f=>f.type);s===null&&(s=G.getEnv());let d,p;return n===Ne?(d=s.vaMethod(a,l,c),p=s.nonvirtualVaMethod(a,l,c)):n===gt?(d=s.staticVaMethod(a,l,c),p=d):(d=s.constructor(l,c),p=d),hu([t,e,n,r,o,i,d,p])}function hu(t){let e=_u();return Object.setPrototypeOf(e,oi),e._p=t,e}function _u(){let t=function(){return t.invoke(this,arguments)};return t}oi=Object.create(Function.prototype,{methodName:{enumerable:!0,get(){return this._p[0]}},holder:{enumerable:!0,get(){return this._p[1]}},type:{enumerable:!0,get(){return this._p[2]}},handle:{enumerable:!0,get(){return this._p[3]}},implementation:{enumerable:!0,get(){let t=this._r;return t!==void 0?t:null},set(t){let e=this._p,n=e[1];if(e[2]===hr)throw new Error("Reimplementing $new is not possible; replace implementation of $init instead");let o=this._r;if(o!==void 0&&(n.$f._patchedMethods.delete(this),o._m.revert(G),this._r=void 0),t!==null){let[i,s,c,a,l,d]=e,p=si(i,s,c,l,d,t,this),f=Xo(a);p._m=f,this._r=p,f.replace(p,c===Ne,d,G,K),n.$f._patchedMethods.add(this)}}},returnType:{enumerable:!0,get(){return this._p[4]}},argumentTypes:{enumerable:!0,get(){return this._p[5]}},canInvokeWith:{enumerable:!0,value(t){let e=this._p[5];return t.length!==e.length?!1:e.every((n,r)=>n.isCompatible(t[r]))}},clone:{enumerable:!0,value(t){let e=this._p.slice(0,6);return Qe(...e,null,t)}},invoke:{value(t,e){let n=G.getEnv(),r=this._p,o=r[2],i=r[4],s=r[5],c=this._r,a=o===Ne,l=e.length,d=2+l;n.pushLocalFrame(d);let p=null;try{let f;a?f=t.$getHandle():(p=t.$borrowClassHandle(n),f=p.value);let u,_=t.$t;c===void 0?u=r[3]:(u=c._m.resolveTarget(t,a,n,K),ti&&c._c.has(Jt())&&(_=ei));let h=[n.handle,f,u];for(let E=0;E!==l;E++)h.push(s[E].toJni(e[E],n));let g;_===zt?g=r[6]:(g=r[7],a&&h.splice(2,0,t.$copyClassHandle(n)));let b=g.apply(null,h);return n.throwIfExceptionPending(),i.fromJni(b,n,!0)}finally{p!==null&&p.unref(n),n.popLocalFrame(NULL)}}},toString:{enumerable:!0,value(){return`function ${this.methodName}(${this.argumentTypes.map(t=>t.className).join(", ")}): ${this.returnType.className}`}}});function si(t,e,n,r,o,i,s=null){let c=new Set,a=mu([t,e,n,r,o,i,s,c]),l=new NativeCallback(a,r.type,["pointer","pointer"].concat(o.map(d=>d.type)));return l._c=c,l}function mu(t){return function(){return gu(arguments,t)}}function gu(t,e){let n=new y(t[0],G),[r,o,i,s,c,a,l,d]=e,p=[],f;if(i===Ne){let h=o.$C;f=new h(t[1],zt,n,!1)}else f=o;let u=Jt();n.pushLocalFrame(3);let _=!0;G.link(u,n);try{d.add(u);let h;l===null||!Ye.has(u)?h=a:h=l;let g=[],b=t.length-2;for(let A=0;A!==b;A++){let R=c[A].fromJni(t[2+A],n,!1);g.push(R),p.push(R)}let E=h.apply(f,g);if(!s.isCompatible(E))throw new Error(`Implementation for ${r} expected return value compatible with ${s.className}`);let C=s.toJni(E,n);return s.type==="pointer"&&(C=n.popLocalFrame(C),_=!1,p.push(E)),C}catch(h){let g=h.$h;return g!==void 0?n.throw(g):Script.nextTick(()=>{throw h}),s.defaultValue}finally{G.unlink(u),_&&n.popLocalFrame(NULL),d.delete(u),p.forEach(h=>{if(h===null)return;let g=h.$dispose;g!==void 0&&g.call(h)})}}function bu(t){let{holder:e,type:n}=t[0];t.some(o=>o.type===n&&o.argumentTypes.length===0)||t.push(yu([e,n]))}function yu(t){let e=Eu();return Object.setPrototypeOf(e,ii),e._p=t,e}function Eu(){return function(){return this}}ii=Object.create(Function.prototype,{methodName:{enumerable:!0,get(){return"valueOf"}},holder:{enumerable:!0,get(){return this._p[0]}},type:{enumerable:!0,get(){return this._p[1]}},handle:{enumerable:!0,get(){return NULL}},implementation:{enumerable:!0,get(){return null},set(t){}},returnType:{enumerable:!0,get(){let t=this.holder;return t.$f.use(t.$n)}},argumentTypes:{enumerable:!0,get(){return[]}},canInvokeWith:{enumerable:!0,value(t){return t.length===0}},clone:{enumerable:!0,value(t){throw new Error("Invalid operation")}}});function vu(t,e,n,r,o){let i=e[2]==="s"?pr:_r,s=ptr(e.substr(3)),{$f:c}=r,a,l=o.toReflectedField(n,s,i===pr?1:0);try{a=o.vaMethod("pointer",[])(o.handle,l,o.javaLangReflectField().getGenericType),o.throwIfExceptionPending()}finally{o.deleteLocalRef(l)}l
holder: ${this.holder},
fieldType: ${this.fieldType},
fieldReturnType: ${this.fieldReturnType},
value: ${this.value},
}`.split(`
`).map(n=>n.length>200?n.slice(0,n.indexOf(" ")+1)+"...,":n).join(`
`)}}});var Vt=class t{static fromBuffer(e,n){let r=Yo(n),o=r.getCanonicalPath().toString(),i=new File(o,"w");return i.write(e.buffer),i.close(),wu(o,n),new t(o,r,n)}constructor(e,n,r){this.path=e,this.file=n,this._factory=r}load(){let{_factory:e}=this,{codeCacheDir:n}=e,r=e.use("dalvik.system.DexClassLoader"),o=e.use("java.io.File"),i=this.file;if(i===null&&(i=e.use("java.io.File").$new(this.path)),!i.exists())throw new Error("File not found");o.$new(n).mkdirs(),e.loader=r.$new(i.getCanonicalPath(),n,null,e.loader),G.preventDetachDueToClassLoader()}getClassNames(){let{_factory:e}=this,n=e.use("dalvik.system.DexFile"),r=Yo(e),o=n.loadDex(this.path,r.getCanonicalPath(),0),i=[],s=o.entries();for(;s.hasMoreElements();)i.push(s.nextElement().toString());return i}};function Yo(t){let{cacheDir:e,tempFileNaming:n}=t,r=t.use("java.io.File"),o=r.$new(e);return o.mkdirs(),r.createTempFile(n.prefix,n.suffix+".dex",o)}function wu(t,e){e.use("java.io.File").$new(t).setWritable(!1,!1)}function Iu(){switch(ue.state){case"empty":{ue.state="pending";let t=ue.factories[0],e=t.use("java.util.HashMap"),n=t.use("java.lang.Integer");ue.loaders=e.$new(),ue.Integer=n;let r=t.loader;return r!==null&&br(t,r),ue.state="ready",ue}case"pending":do Thread.sleep(.05);while(ue.state==="pending");return ue;case"ready":return ue}}function br(t,e){let{factories:n,loaders:r,Integer:o}=ue,i=o.$new(n.indexOf(t));r.put(e,i);for(let s=e.getParent();s!==null&&!r.containsKey(s);s=s.getParent())r.put(s,i)}function ci(t){let e=Ye.get(t);e===void 0&&(e=0),e++,Ye.set(t,e)}function li(t){let e=Ye.get(t);if(e===void 0)throw new Error(`Thread ${t} is not ignored`);e--,e===0?Ye.delete(t):Ye.set(t,e)}function Cu(t){return t.slice(t.lastIndexOf(".")+1)}function yr(t,e){let n=[],r=t.getArrayLength(e);for(let o=0;o!==r;o++){let i=t.getObjectArrayElement(e,o);try{n.push(t.getTypeName(i))}finally{t.deleteLocalRef(i)}}return n}function Tu(t){let e=t.split(".");return e[e.length-1]+".java"}var Au=4,di=Process.pointerSize,Sr=class{ACC_PUBLIC=1;ACC_PRIVATE=2;ACC_PROTECTED=4;ACC_STATIC=8;ACC_FINAL=16;ACC_SYNCHRONIZED=32;ACC_BRIDGE=64;ACC_VARARGS=128;ACC_NATIVE=256;ACC_ABSTRACT=1024;ACC_STRICT=2048;ACC_SYNTHETIC=4096;constructor(){this.classFactory=null,this.ClassFactory=je,this.vm=null,this.api=null,this._initialized=!1,this._apiError=null,this._wakeupHandler=null,this._pollListener=null,this._pendingMainOps=[],this._pendingVmOps=[],this._cachedIsAppProcess=null;try{this._tryInitialize()}catch{}}_tryInitialize(){if(this._initialized)return!0;if(this._apiError!==null)throw this._apiError;let e;try{e=ft(),this.api=e}catch(r){throw this._apiError=r,r}if(e===null)return!1;let n=new Ie(e);return this.vm=n,$o(n),je._initialize(n,e),this.classFactory=new je,this._initialized=!0,!0}_dispose(){if(this.api===null)return;let{vm:e}=this;e.perform(n=>{je._disposeAll(n),y.dispose(n)}),Script.nextTick(()=>{Ie.dispose(e)})}get available(){return this._tryInitialize()}get androidVersion(){return ut()}synchronized(e,n){let{$h:r=e}=e;if(!(r instanceof NativePointer))throw new Error("Java.synchronized: the first argument `obj` must be either a pointer or a Java instance");let o=this.vm.getEnv();de("VM::MonitorEnter",o.monitorEnter(r));try{n()}finally{o.monitorExit(r)}}enumerateLoadedClasses(e){this._checkAvailable();let{flavor:n}=this.api;n==="jvm"?this._enumerateLoadedClassesJvm(e):n==="art"?this._enumerateLoadedClassesArt(e):this._enumerateLoadedClassesDalvik(e)}enumerateLoadedClassesSync(){let e=[];return this.enumerateLoadedClasses({onMatch(n){e.push(n)},onComplete(){}}),e}enumerateClassLoaders(e){this._checkAvailable();let{flavor:n}=this.api;if(n==="jvm")this._enumerateClassLoadersJvm(e);else if(n==="art")this._enumerateClassLoadersArt(e);else throw new Error("Enumerating class loaders is not supported on Dalvik")}enumerateClassLoadersSync(){let e=[];return this.enumerateClassLoaders({onMatch(n){e.push(n)},onComplete(){}}),e}_enumerateLoadedClassesJvm(e){let{api:n,vm:r}=this,{jvmti:o}=n,i=r.getEnv(),s=Memory.alloc(Au),c=Memory.alloc(di);o.getLoadedClasses(s,c);let a=s.readS32(),l=c.readPointer()