Files
FridaBox/scripts/dist/guest-bootstrap.js
T

1888 lines
232 KiB
JavaScript
Raw Normal View History

2026-07-19 23:33:52 +03:30
📦
237003 /scripts/guest-bootstrap.js
✄
var vs=Object.defineProperty;var Ss=(t,e)=>{for(var n in e)vs(t,n,{get:e[n],enumerable:!0})};var Ie=[],be=[],Ht="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(let t=0,e=Ht.length;t<e;++t)Ie[t]=Ht[t],be[Ht.charCodeAt(t)]=t;be[45]=62;be[95]=63;function ws(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 Is(t,e,n){return(e+n)*3/4-n}function Lr(t){let e=ws(t),n=e[0],r=e[1],o=new Uint8Array(Is(t,n,r)),s=0,i=r>0?n-4:n,l;for(l=0;l<i;l+=4){let a=be[t.charCodeAt(l)]<<18|be[t.charCodeAt(l+1)]<<12|be[t.charCodeAt(l+2)]<<6|be[t.charCodeAt(l+3)];o[s++]=a>>16&255,o[s++]=a>>8&255,o[s++]=a&255}if(r===2){let a=be[t.charCodeAt(l)]<<2|be[t.charCodeAt(l+1)]>>4;o[s++]=a&255}if(r===1){let a=be[t.charCodeAt(l)]<<10|be[t.charCodeAt(l+1)]<<4|be[t.charCodeAt(l+2)]>>2;o[s++]=a>>8&255,o[s++]=a&255}return o}function Cs(t){return Ie[t>>18&63]+Ie[t>>12&63]+Ie[t>>6&63]+Ie[t&63]}function Ts(t,e,n){let r=[];for(let o=e;o<n;o+=3){let s=(t[o]<<16&16711680)+(t[o+1]<<8&65280)+(t[o+2]&255);r.push(Cs(s))}return r.join("")}function Zt(t){let e=t.length,n=e%3,r=[],o=16383;for(let s=0,i=e-n;s<i;s+=o)r.push(Ts(t,s,s+o>i?i:s+o));if(n===1){let s=t[e-1];r.push(Ie[s>>2]+Ie[s<<4&63]+"==")}else if(n===2){let s=(t[e-2]<<8)+t[e-1];r.push(Ie[s>>10]+Ie[s>>4&63]+Ie[s<<2&63]+"=")}return r.join("")}function rt(t,e,n,r,o){let s,i,l=o*8-r-1,a=(1<<l)-1,c=a>>1,d=-7,p=n?o-1:0,f=n?-1:1,u=t[e+p];for(p+=f,s=u&(1<<-d)-1,u>>=-d,d+=l;d>0;)s=s*256+t[e+p],p+=f,d-=8;for(i=s&(1<<-d)-1,s>>=-d,d+=r;d>0;)i=i*256+t[e+p],p+=f,d-=8;if(s===0)s=1-c;else{if(s===a)return i?NaN:(u?-1:1)*(1/0);i=i+Math.pow(2,r),s=s-c}return(u?-1:1)*i*Math.pow(2,s-r)}function qt(t,e,n,r,o,s){let i,l,a,c=s*8-o-1,d=(1<<c)-1,p=d>>1,f=o===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=r?0:s-1,_=r?1:-1,h=e<0||e===0&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(l=isNaN(e)?1:0,i=d):(i=Math.floor(Math.log(e)/Math.LN2),e*(a=Math.pow(2,-i))<1&&(i--,a*=2),i+p>=1?e+=f/a:e+=f*Math.pow(2,1-p),e*a>=2&&(i++,a/=2),i+p>=d?(l=0,i=d):i+p>=1?(l=(e*a-1)*Math.pow(2,o),i=i+p):(l=e*Math.pow(2,p-1)*Math.pow(2,o),i=0));o>=8;)t[n+u]=l&255,u+=_,l/=256,o-=8;for(i=i<<o|l,c+=o;c>0;)t[n+u]=i&255,u+=_,i/=256,c-=8;t[n+u-_]|=h*128}var xs={INSPECT_MAX_BYTES:50},Wt=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 xe(t){if(t>Wt)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 Xt(t)}return Mr(t,e,n)}m.poolSize=8192;function Mr(t,e,n){if(typeof t=="string")return ks(t,e);if(ArrayBuffer.isView(t))return Ms(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 Qt(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=js(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 Mr(t,e,n)};Object.setPrototypeOf(m.prototype,Uint8Array.prototype);Object.setPrototypeOf(m,Uint8Array);function jr(t){if(typeof t!="number")throw new TypeError('"size" argument must be of type number');if(t<0)throw new RangeError('
`)){let s=o.split(" "),i=s[4];if(!i.startsWith("/apex/com.android.art"))continue;let l=s[10];i.includes("@")?n.set(l,i.split("@")[1]):e=l}let r=n.get(e);return r!==void 0?parseInt(r):oo()}catch{return oo()}}function oo(){return ee()*1e7}var Ln=null,Zl=92;function Hn(t){Ln===null&&(Ln=new NativeFunction(Process.getModuleByName("libc.so").getExportByName("__system_property_get"),"int",["pointer","pointer"],W));let e=Memory.alloc(Zl);return Ln(Memory.allocUtf8String(t),e),e.readUtf8String()}function ye(t,e,n){let r=hl(t,e),o=Ut(e).toString();if(at[o]=n,r(e.handle),at[o]!==void 0)throw delete at[o],new Error("Unable to perform state transition; please file a bug")}function ql(t,e){let n=new NativeCallback(Wl,"void",["pointer"]);return Eo(t,e,n)}function Wl(t){let e=t.toString(),n=at[e];delete at[e],n(t)}function Zn(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 jn=class{constructor(e){let n=Memory.alloc(4*v),r=n.add(v);n.writePointer(r);let o=new NativeCallback((s,i)=>e(i)===!0?1:0,"bool",["pointer","pointer"]);r.add(2*v).writePointer(o),this.handle=n,this._onVisit=o}};function qn(t){return J()["art::ClassLinker::VisitClasses"]instanceof NativeFunction?new jn(t):new NativeCallback(n=>t(n)===!0?1:0,"bool",["pointer","pointer"])}var On=class{constructor(e){let n=Memory.alloc(4*v),r=n.add(v);n.writePointer(r);let o=new NativeCallback((s,i)=>{e(i)},"void",["pointer","pointer"]);r.add(2*v).writePointer(o),this.handle=n,this._onVisit=o}};function Wn(t){return new On(t)}var Kl={"include-inlined-frames":0,"skip-inlined-frames":1},Rn=class{constructor(e,n,r,o=0,s=!0){let i=J(),l=512,a=3*v,c=Memory.alloc(l+a);i["art::StackVisitor::StackVisitor"](c,e,n,Kl[r],o,s?1:0);let d=c.add(l);c.writePointer(d);let p=new NativeCallback(this._visitFrame.bind(this),"bool",["pointer"]);d.add(2*v).writePointer(p),this.handle=c,this._onVisitFrame=p;let f=c.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=i["art::StackVisitor::GetMethod"],this._descLocImpl=i["art::StackVisitor::DescribeLocation"],this._getCQFIImpl=i["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 Mt(e)}getCurrentQuickFramePc(){return this._curQuickFramePc.readPointer()}getCurrentQuickFrame(){return this._curQuickFrame.readPointer()}getCurrentShadowFrame(){return this._curShadowFrame.readPointer()}describeLocation(){let e=new Rt;return this._descLocImpl(e,this.handle),e.disposeToString()}getCurrentOatQuickMethodHeader(){return this._curOatQuickMethodHeader.readPointer()}getCurrentQuickFrameInfo(){return this._getCQFIImpl(this.handle)}},Mt=class{constructor(e){this.handle=e}prettyMethod(e=!0){let n=new Rt;return J()["art::ArtMethod::PrettyMethod"](n,this.handle,e?1:0),n.disposeToString()}toString(){return`ArtMethod(handle=${this.handle})`}};function Ql(t){return function(e){let n=Memory.alloc(12);return _l(t)(n,e),{frameSizeInBytes:n.readU32(),coreSpillMask:n.add(4).readU32(),fpSpillMask:n.add(8).readU32()}}}function Yl(t){let e=NULL;switch(Process.arch){case"ia32":e=qe(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=qe(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=qe(16,n=>{n.putCallAddressWithArguments(t,["r0","r1"]),n.putPopRegs(["r0","lr"]),n.putMovRegReg("pc","lr")});break;case"arm64":e=qe(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);
}
`,s=8,i=v,l=v,a=v,d=Memory.alloc(s+i+l+a),p=d.add(s),f=p.add(i),u=f.add(l),_=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 nc(t){Xr||(Xr=!0,rc(t),oc(),sc(),ic())}function rc(t){let e=J();[e.artQuickGenericJniTrampoline,e.artQuickToInterpreterBridge,e.artQuickResolutionTrampoline].forEach(r=>{Memory.protect(r,32,"rwx");let o=new Ot(r);o.activate(t),mo.push(o)})}function oc(){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,s=[...o.enumerateExports(),...o.enumerateSymbols()].filter(i=>r.test(i.name));if(s.length===0)throw new Error("Unable to find method invocation in ART; please file a bug");for(let i of s)Interceptor.attach(i.address,oe.hooks.Interpreter.doCall)}function sc(){let t=J(),n=t.module.findSymbolByName("_ZN3art2gc4Heap22CollectGarbageInternalENS0_9collector6GcTypeENS0_7GcCauseEbj");if(n===null)return;let{artNterpEntryPoint:r,artQuickToInterpreterBridge:o}=t,s=_e(t.vm).offset.quickCode;Interceptor.attach(n,{onLeave(){oe.replacedMethods.synchronize(s,r,o)}})}function ic(){let t=[["_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE","e90340f8 : ff0ff0ff"],["_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl","7f0f00f9 : 1ffcffff"]],e=J(),n=e.module;for(let[r,o]of t){let s=n.findSymbolByName(r);if(s===null)continue;let i=Memory.scanSync(s,8192,o);if(i.length===0)return;let{artNterpEntryPoint:l,artQuickToInterpreterBridge:a}=e,c=_e(e.vm).offset.quickCode;Interceptor.attach(i[0].address,function(){oe.replacedMethods.synchronize(c,l,a)});return}}function ac(t){if(Yr)return;if(Yr=!0,!cc()){let{getOatQuickMethodHeaderImpl:s}=oe;if(s===null)return;try{Interceptor.replace(s,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 lc={arm:{signatures:[{pattern:["b0 68","01 30","0c d0","1b 98",":","c0 ff","c0 ff","00 ff","00 2f"],validateMatch:xn},{pattern:["d8 f8 08 00","01 30","0c d0","1b 98",":","f0 ff ff 0f","ff ff","00 ff","00 2f"],validateMatch:xn},{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")}),s=new NativeFunction(o._create,"pointer",["pointer","uint"],W),i=new NativeFunction(o._destroy,"void",["pointer"],W),l={exceptions:"propagate",scheduling:"exclusive"},a=new NativeFunction(o._get_id,"pointer",["pointer"],l),c=new NativeFunction(o._get_frames,"pointer",["pointer"],l),d=Eo(t,e,o._on_thread_state_transition_complete);o._performData=d,r.writePointer(d),o.backtrace=(f,u)=>{let _=s(f,u),h=new Fn(_);return Script.bindWeak(h,p.bind(null,_)),h};function p(f){i(f)}return o.getId=f=>a(f).readUtf8String(),o.getFrames=f=>JSON.parse(c(f).readUtf8String()),o}var Fn=class{constructor(e){this.handle=e}get id(){return lt.getId(this.handle)}get frames(){return lt.getFrames(this.handle)}};function Qn(){kt.forEach(t=>{t.vtablePtr.writePointer(t.vtable),t.vtableCountPtr.writeS32(t.vtableCount)}),kt.clear();for(let t of mo.splice(0))t.deactivate();for(let t of Gn.splice(0))t.revert()}function Yn(t){return bo(t,"art::jni::JniIdManager::DecodeMethodId")}function so(t){return bo(t,"art::jni::JniIdManager::DecodeFieldId")}function bo(t,e){let n=J(),r=fo(n).offset,o=r.jniIdManager,s=r.jniIdsIndirection;if(o!==null&&s!==null){let i=n.artRuntime;if(i.add(s).readInt()!==za){let a=i.add(o).readPointer();return n[e](a,t)}}return t}var mc={ia32:gc,x64:bc,arm:yc,arm64:Ec};function gc(t,e,n,r,o){let s=We(o).offset,i=_e(o).offset,l;return Memory.patchCode(t,128,a=>{let c=new X86Writer(a,{pc:t}),d=new X86Relocator(e,c),p=[15,174,4,36],f=[15,174,12,36];c.putPushax(),c.putMovRegReg("ebp","esp"),c.putAndRegU32("esp",4294967280),c.putSubRegImm("esp",512),c.putBytes(p),c.putMovRegFsU32Ptr("ebx",s.self),c.putCallAddressWithAlignedArguments(oe.replacedMethods.findReplacementFromQuickCode,["eax","ebx"]),c.putTestRegReg("eax","eax"),c.putJccShortLabel("je","restore_registers","no-hint"),c.putMovRegOffsetPtrReg("ebp",7*4,"eax"),c.putLabel("restore_registers"),c.putBytes(f),c.putMovRegReg("esp","ebp"),c.putPopax(),c.putJccShortLabel("jne","invoke_replacement","no-hint");do l=d.readOne();while(l<n&&!d.eoi);d.writeAll(),d.eoi||c.putJmpAddress(e.add(l)),c.putLabel("invoke_replacement"),c.putJmpRegOffsetPtr("eax",i.quickCode),c.flush()}),l}function bc(t,e,n,r,o){let s=We(o).offset,i=_e(o).offset,l;return Memory.patchCode(t,256,a=>{let c=new X86Writer(a,{pc:t}),d=new X86Relocator(e,c),p=[15,174,4,36],f=[15,174,12,36];c.putPushax(),c.putMovRegReg("rbp","rsp"),c.putAndRegU32("rsp",4294967280),c.putSubRegImm("rsp",512),c.putBytes(p),c.putMovRegGsU32Ptr("rbx",s.self),c.putCallAddressWithAlignedArguments(oe.replacedMethods.findReplacementFromQuickCode,["rdi","rbx"]),c.putTestRegReg("rax","rax"),c.putJccShortLabel("je","restore_registers","no-hint"),c.putMovRegOffsetPtrReg("rbp",8*8,"rax"),c.putLabel("restore_registers"),c.putBytes(f),c.putMovRegReg("rsp","rbp"),c.putPopax(),c.putJccShortLabel("jne","invoke_replacement","no-hint");do l=d.readOne();while(l<n&&!d.eoi);d.writeAll(),d.eoi||c.putJmpAddress(e.add(l)),c.putLabel("invoke_replacement"),c.putJmpRegOffsetPtr("rdi",i.quickCode),c.flush()}),l}function yc(t,e,n,r,o){let s=_e(o).offset,i=e.and(Vn),l;return Memory.patchCode(t,128,a=>{let c=new ThumbWriter(a,{pc:t}),d=new ThumbRelocator(i,c),p=[45,237,16,10],f=[189,236,16,10];c.putPushRegs(["r1","r2","r3","r5","r6","r7","r8","r10","r11","lr"]),c.putBytes(p),c.putSubRegRegImm("sp","sp",8),c.putStrRegRegOffset("r0","sp",0),c.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;
}
`,_d=/(.+)!([^/]+)\/?([isu]+)?/,Ee=null,Ro=null,ze=class t{static build(e,n){return Oo(n),Ro(e,n,r=>new t(Ee.new(e,r,n)))}static enumerateMethods(e,n,r){Oo(r);let o=e.match(_d);if(o===null)throw new Error("Invalid query; format is: class!method -- see documentation of Java.enumerateMethods(query) for details");let s=Memory.allocUtf8String(o[1]),i=Memory.allocUtf8String(o[2]),l=!1,a=!1,c=!1,d=o[3];d!==void 0&&(l=d.indexOf("s")!==-1,a=d.indexOf("i")!==-1,c=d.indexOf("u")!==-1);let p;if(n.jvmti!==null){let f=Ee.enumerateMethodsJvm(s,i,Ke(l),Ke(a),Ke(c),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(s,i,Ke(l),Ke(a),Ke(c));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 Oo(t){Ee===null&&(Ee=md(t),Ro=gd(Ee,t.vm))}function md(t){let e=_t(),{jvmti:n=null}=e,{pointerSize:r}=Process,o=8,s=r,i=7*r,l=10*4+5*r,a=o+s+i+l,d=Memory.alloc(a),p=d.add(o),f=p.add(s),{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(i),{vm:C}=t;if(e.flavor==="art"){let k;if(n!==null)k=[0,0,0,0];else{let j=$n(C).offset;k=[j.ifields,j.methods,j.sfields,j.copiedMethodsOffset]}let N=_e(C),S=Dt(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(hd,{lock:d,models:p,java_api:f,art_api:E}),M={exceptions:"propagate"},O={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"],O),find:new NativeFunction(A.model_find,"pointer",["pointer","pointer"],O),list:new NativeFunction(A.model_list,"pointer",["pointer"],O),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"],O)}}function gd(t,e){let n=_t();if(n.flavor!=="art")return bd;let r=n["art::JavaVMExt::DecodeGlobal"];return function(o,s,i){let l;return ye(e,s,a=>{let c=r(e,a,o);l=i(c)}),l}}function bd(t,e,n){return n(NULL)}function Ke(t){return t?1:0}var mt=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,s=o.get(e);if(s!==void 0)o.delete(e),this.destroy(s,r);else if(o.size===this.capacity){let i=o.keys().next().value,l=o.get(i);o.delete(i),this.destroy(l,r)}o.set(e,n)}};var gt=1,cr=256,Po=65536,yd=305419896,Fo=32,Do=12,Uo=8,Bo=8,zo=4,Vo=4,Jo=12,Ed=0,vd=1,Sd=2,wd=3,Id=4,Cd=5,Td=6,Ad=4096,Ld=4097,xd=4099,Nd=8192,kd=8193,Md=8194,jd=8195,Od=8196,Rd=8198,Pd=24,Fd=28,Dd=2,Ud=24,Go=m.from([3,0,7,14,0]),ar="Ldalvik/annotation/Throws;",Bd=m.from([0]);function zd(t){let e=new dr,n=Object.assign({},t);return e.addClass(n),e.build()}var dr=class{constructor(){this.classes=[]}addClass(e){this.classes.push(e)}build(){let e=Gd(this.classes),{classes:n,interfaces:r
035`),I.writeUInt32LE(Ge,32),I.writeUInt32LE(E,36),I.writeUInt32LE(yd,40),I.writeUInt32LE(we,44),I.writeUInt32LE(tt,48),I.writeUInt32LE($,52),I.writeUInt32LE(f.length,56),I.writeUInt32LE(C,60),I.writeUInt32LE(p.length,64),I.writeUInt32LE(M,68),I.writeUInt32LE(i.length,72),I.writeUInt32LE(k,76),I.writeUInt32LE(o.length,80),I.writeUInt32LE(o.length>0?S:0,84),I.writeUInt32LE(s.length,88),I.writeUInt32LE(j,92),I.writeUInt32LE(n.length,96),I.writeUInt32LE(R,100),I.writeUInt32LE(je,104),I.writeUInt32LE(U,108),Q.forEach((T,P)=>{I.writeUInt32LE(T,C+P*Vo)}),p.forEach((T,P)=>{I.writeUInt32LE(T,M+P*zo)}),i.forEach((T,P)=>{let[B,H,q]=T,Z=k+P*Do;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*Uo;I.writeUInt16LE(B,Z),I.writeUInt16LE(H,Z+2),I.writeUInt32LE(q,Z+4)}),s.forEach((T,P)=>{let[B,H,q]=T,Z=j+P*Bo;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,nt=0,ge=R+P*Fo;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(nt,ge+28)}),c.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,nt=1,ge=0,Et=4;I.writeUInt16LE(q,B),I.writeUInt16LE(Z,B+2),I.writeUInt16LE(nt,B+4),I.writeUInt16LE(ge,B+6),I.writeUInt32LE(se[P],B+8),I.writeUInt32LE(Et,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((nt,ge)=>{let Et=P+16+ge*8,[ys,Es]=nt;I.writeUInt32LE(ys,Et),I.writeUInt32LE(Es.offset,Et+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)})}),l.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])}),se.forEach(T=>{Go.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(Le,$);let le=[[Ed,1,_],[vd,f.length,C],[Sd,p.length,M],[wd,i.length,k]];o.length>0&&le.push([Id,o.length,S]),le.push([Cd,s.length,j]),le.push([Td,n.length,R]),c.forEach((T,P)=>{le.push([xd,T.items.length,F[P]])}),z.forEach(T=>{le.push([kd,1,T.offset])}),a.forEach(T=>{le.push([Rd,1,T.offset])}),he>0&&le.push([Ld,he,X.concat(te)[0]]),le.push([Md,f.length,Q[0]]),se.forEach(T=>{le.push([jd,1,T])}),d.forEach(T=>{le.push([Od,1,T.offset])}),n.forEach(T=>{le.push([Nd,1,T.classData.offset])}),le.push([Ad,1,$]),le.forEach((T,P)=>{let[B,H,q]=T,Z=$+4+P*Jo;I.writeUInt16LE(B,Z),I.writeUInt32LE(H,Z+4),I.writeUInt32LE(q,Z+8)});let Ar=new Checksum("sha1");return Ar.update(I.slice(g+b)),m.from(Ar.getDigest()).copy(I,g),I.writeUInt32LE(Kd(I,g),h),I}};function Vd(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((s,[i,l])=>s.concat(me(i)).concat(me(l)),[])).concat(n.reduce((s,[i,l,,a])=>s.concat(me(i)).concat(me(l)).concat(me(a||0)),[])).concat(r.reduce((s,[i,l])=>s.concat(me(i)).concat(me(l)).concat([0]),[])))}function Jd(t){let{thrownTypes:e}=t;return m.from([Dd].concat(me(t.type)).concat([1]).concat(me(t.value)).concat([Fd,e.length]).concat(e.reduce((n,r)=>(n.push(Pd,r),n),[])))}function Gd(t){let e=new Set,n=new Set,r={},o=[],s=[],i={},l=new Set,a=new Set;t.forEach(w=>{let{name:R,superClass:D,sourceFileName:U}=w;e.add("this"),e.add(R),n.add(R),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 Ye(t,e,n,r,o,s,i,l){let a=o.type,c=s.map(f=>f.type);i===null&&(i=G.getEnv());let d,p;return n===Me?(d=i.vaMethod(a,c,l),p=i.nonvirtualVaMethod(a,c,l)):n===yt?(d=i.staticVaMethod(a,c,l),p=d):(d=i.constructor(c,l),p=d),Eu([t,e,n,r,o,s,d,p])}function Eu(t){let e=vu();return Object.setPrototypeOf(e,as),e._p=t,e}function vu(){let t=function(){return t.invoke(this,arguments)};return t}as=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]===mr)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[s,i,l,a,c,d]=e,p=cs(s,i,l,c,d,t,this),f=ns(a);p._m=f,this._r=p,f.replace(p,l===Me,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 Ye(...e,null,t)}},invoke:{value(t,e){let n=G.getEnv(),r=this._p,o=r[2],s=r[4],i=r[5],l=this._r,a=o===Me,c=e.length,d=2+c;n.pushLocalFrame(d);let p=null;try{let f;a?f=t.$getHandle():(p=t.$borrowClassHandle(n),f=p.value);let u,_=t.$t;l===void 0?u=r[3]:(u=l._m.resolveTarget(t,a,n,K),os&&l._c.has($t())&&(_=rs));let h=[n.handle,f,u];for(let E=0;E!==c;E++)h.push(i[E].toJni(e[E],n));let g;_===Jt?g=r[6]:(g=r[7],a&&h.splice(2,0,t.$copyClassHandle(n)));let b=g.apply(null,h);return n.throwIfExceptionPending(),s.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 cs(t,e,n,r,o,s,i=null){let l=new Set,a=Su([t,e,n,r,o,s,i,l]),c=new NativeCallback(a,r.type,["pointer","pointer"].concat(o.map(d=>d.type)));return c._c=l,c}function Su(t){return function(){return wu(arguments,t)}}function wu(t,e){let n=new y(t[0],G),[r,o,s,i,l,a,c,d]=e,p=[],f;if(s===Me){let h=o.$C;f=new h(t[1],Jt,n,!1)}else f=o;let u=$t();n.pushLocalFrame(3);let _=!0;G.link(u,n);try{d.add(u);let h;c===null||!Xe.has(u)?h=a:h=c;let g=[],b=t.length-2;for(let A=0;A!==b;A++){let O=l[A].fromJni(t[2+A],n,!1);g.push(O),p.push(O)}let E=h.apply(f,g);if(!i.isCompatible(E))throw new Error(`Implementation for ${r} expected return value compatible with ${i.className}`);let C=i.toJni(E,n);return i.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}),i.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 Iu(t){let{holder:e,type:n}=t[0];t.some(o=>o.type===n&&o.argumentTypes.length===0)||t.push(Cu([e,n]))}function Cu(t){let e=Tu();return Object.setPrototypeOf(e,ls),e._p=t,e}function Tu(){return function(){return this}}ls=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 Au(t,e,n,r,o){let s=e[2]==="s"?hr:gr,i=ptr(e.substr(3)),{$f:l}=r,a,c=o.toReflectedField(n,i,s===hr?1:0);try{a=o.vaMethod("pointer",[])(o.handle,c,o.javaLangReflectField().getGenericType),o.throwIfExceptionPending()}finally{o.deleteLocalRef(c)}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 Gt=class t{static fromBuffer(e,n){let r=ts(n),o=r.getCanonicalPath().toString(),s=new File(o,"w");return s.write(e.buffer),s.close(),xu(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"),s=this.file;if(s===null&&(s=e.use("java.io.File").$new(this.path)),!s.exists())throw new Error("File not found");o.$new(n).mkdirs(),e.loader=r.$new(s.getCanonicalPath(),n,null,e.loader),G.preventDetachDueToClassLoader()}getClassNames(){let{_factory:e}=this,n=e.use("dalvik.system.DexFile"),r=ts(e),o=n.loadDex(this.path,r.getCanonicalPath(),0),s=[],i=o.entries();for(;i.hasMoreElements();)s.push(i.nextElement().toString());return s}};function ts(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 xu(t,e){e.use("java.io.File").$new(t).setWritable(!1,!1)}function Nu(){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&&Er(t,r),ue.state="ready",ue}case"pending":do Thread.sleep(.05);while(ue.state==="pending");return ue;case"ready":return ue}}function Er(t,e){let{factories:n,loaders:r,Integer:o}=ue,s=o.$new(n.indexOf(t));r.put(e,s);for(let i=e.getParent();i!==null&&!r.containsKey(i);i=i.getParent())r.put(i,s)}function us(t){let e=Xe.get(t);e===void 0&&(e=0),e++,Xe.set(t,e)}function ps(t){let e=Xe.get(t);if(e===void 0)throw new Error(`Thread ${t} is not ignored`);e--,e===0?Xe.delete(t):Xe.set(t,e)}function ku(t){return t.slice(t.lastIndexOf(".")+1)}function vr(t,e){let n=[],r=t.getArrayLength(e);for(let o=0;o!==r;o++){let s=t.getObjectArrayElement(e,o);try{n.push(t.getTypeName(s))}finally{t.deleteLocalRef(s)}}return n}function Mu(t){let e=t.split(".");return e[e.length-1]+".java"}var ju=4,fs=Process.pointerSize,Ir=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=Re,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=_t(),this.api=e}catch(r){throw this._apiError=r,r}if(e===null)return!1;let n=new Ce(e);return this.vm=n,qo(n),Re._initialize(n,e),this.classFactory=new Re,this._initialized=!0,!0}_dispose(){if(this.api===null)return;let{vm:e}=this;e.perform(n=>{Re._disposeAll(n),y.dispose(n)}),Script.nextTick(()=>{Ce.dispose(e)})}get available(){return this._tryInitialize()}get androidVersion(){return ft()}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,s=r.getEnv(),i=Memory.alloc(ju),l=Memory.alloc(fs);o.getLoadedClasses(i,l);let a=i.readS32(),c=l.readPointer()