frida-java-bridge 5.2.1 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -5,6 +5,7 @@ const {
5
5
  withRunnableArtThread,
6
6
  makeArtClassVisitor,
7
7
  makeArtClassLoaderVisitor,
8
+ backtrace,
8
9
  deoptimizeEverything,
9
10
  deoptimizeBootImage,
10
11
  deoptimizeMethod
@@ -478,6 +479,10 @@ class Runtime {
478
479
  return this.classFactory.array(type, elements);
479
480
  }
480
481
 
482
+ backtrace (options) {
483
+ return backtrace(this.vm, options);
484
+ }
485
+
481
486
  // Reference: http://stackoverflow.com/questions/2848575/how-to-detect-ui-thread-on-android
482
487
  isMainThread () {
483
488
  const Looper = this.classFactory.use('android.os.Looper');
@@ -0,0 +1,33 @@
1
+ export function getApi(): Api;
2
+ export function withRunnableArtThread(vm: VM, env: Env, callback: (thread: Thread) => void): void;
3
+ export function translateMethod(methodId: NativePointerValue): NativePointer;
4
+
5
+ export class ArtStackVisitor {
6
+ constructor(thread: Thread, context: Context, walkKind: WalkKind, numFrames?: number, checkSuspended?: boolean);
7
+ walkStack(includeTransitions?: boolean): void;
8
+ getMethod(): ArtMethod | null;
9
+ getCurrentQuickFramePc(): NativePointer;
10
+ getCurrentQuickFrame(): NativePointer;
11
+ getCurrentShadowFrame(): NativePointer;
12
+ describeLocation(): string;
13
+ getCurrentOatQuickMethodHeader(): NativePointer;
14
+ getCurrentQuickFrameInfo(): QuickFrameInfo;
15
+ }
16
+
17
+ export type WalkKind = "include-inlined-frames" | "skip-inlined-frames";
18
+
19
+ export interface ArtMethod extends ObjectWrapper {
20
+ prettyMethod(withSignature?: boolean): string;
21
+ }
22
+
23
+ export interface QuickFrameInfo {
24
+ frameSizeInBytes: number;
25
+ coreSpillMask: number;
26
+ fpSpillMask: number;
27
+ }
28
+
29
+ export type Api = any;
30
+ export type VM = any;
31
+ export type Env = any;
32
+ export type Thread = any;
33
+ export type Context = any;
package/lib/android.js CHANGED
@@ -105,6 +105,7 @@ let thunkPage = null;
105
105
  let thunkOffset = 0;
106
106
  let taughtArtAboutReplacementMethods = false;
107
107
  let taughtArtAboutMethodInstrumentation = false;
108
+ let backtraceModule = null;
108
109
  const jdwpSessions = [];
109
110
  let socketpair = null;
110
111
 
@@ -285,7 +286,9 @@ function _getApi () {
285
286
 
286
287
  // Android >= 11
287
288
  _ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID: ['art::jni::JniIdManager::DecodeMethodId', 'pointer', ['pointer', 'pointer']],
288
- _ZN3art11interpreter18GetNterpEntryPointEv: ['art::interpreter::GetNterpEntryPoint', 'pointer', []]
289
+ _ZN3art11interpreter18GetNterpEntryPointEv: ['art::interpreter::GetNterpEntryPoint', 'pointer', []],
290
+
291
+ _ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi: ['art::Monitor::TranslateLocation', 'void', ['pointer', 'uint32', 'pointer', 'pointer']]
289
292
  },
290
293
  variables: {
291
294
  _ZN3art3Dbg9gRegistryE: function (address) {
@@ -337,7 +340,8 @@ function _getApi () {
337
340
  '_ZN3art3Dbg20ManageDeoptimizationEv',
338
341
  '_ZN3art3Dbg9gRegistryE',
339
342
  '_ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID',
340
- '_ZN3art11interpreter18GetNterpEntryPointEv'
343
+ '_ZN3art11interpreter18GetNterpEntryPointEv',
344
+ '_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi'
341
345
  ]
342
346
  }]
343
347
  : [{
@@ -1196,6 +1200,11 @@ function withRunnableArtThread (vm, env, fn) {
1196
1200
  }
1197
1201
  }
1198
1202
 
1203
+ function _getArtThreadStateTransitionImpl (vm, env) {
1204
+ const callback = new NativeCallback(onThreadStateTransitionComplete, 'void', ['pointer']);
1205
+ return makeArtThreadStateTransitionImpl(vm, env, callback);
1206
+ }
1207
+
1199
1208
  function onThreadStateTransitionComplete (thread) {
1200
1209
  const id = thread.toString();
1201
1210
 
@@ -1812,6 +1821,512 @@ function translateMethod (methodId) {
1812
1821
  return artController.replacedMethods.translate(methodId);
1813
1822
  }
1814
1823
 
1824
+ function backtrace (vm, options = {}) {
1825
+ const { limit = 16 } = options;
1826
+
1827
+ const env = vm.getEnv();
1828
+
1829
+ if (backtraceModule === null) {
1830
+ backtraceModule = makeBacktraceModule(vm, env);
1831
+ }
1832
+
1833
+ return backtraceModule.backtrace(env, limit);
1834
+ }
1835
+
1836
+ function makeBacktraceModule (vm, env) {
1837
+ const api = getApi();
1838
+
1839
+ const performImpl = Memory.alloc(Process.pointerSize);
1840
+
1841
+ const cm = new CModule(`
1842
+ #include <glib.h>
1843
+ #include <stdbool.h>
1844
+ #include <string.h>
1845
+ #include <gum/gumtls.h>
1846
+ #include <json-glib/json-glib.h>
1847
+
1848
+ typedef struct _ArtBacktrace ArtBacktrace;
1849
+ typedef struct _ArtStackFrame ArtStackFrame;
1850
+
1851
+ typedef struct _ArtStackVisitor ArtStackVisitor;
1852
+ typedef struct _ArtStackVisitorVTable ArtStackVisitorVTable;
1853
+
1854
+ typedef struct _ArtMethod ArtMethod;
1855
+ typedef struct _ArtThread ArtThread;
1856
+ typedef struct _ArtContext ArtContext;
1857
+
1858
+ typedef struct _JNIEnv JNIEnv;
1859
+
1860
+ typedef struct _StdString StdString;
1861
+ typedef struct _StdTinyString StdTinyString;
1862
+ typedef struct _StdLargeString StdLargeString;
1863
+
1864
+ typedef enum {
1865
+ STACK_WALK_INCLUDE_INLINED_FRAMES,
1866
+ STACK_WALK_SKIP_INLINED_FRAMES,
1867
+ } StackWalkKind;
1868
+
1869
+ struct _StdTinyString
1870
+ {
1871
+ guint8 unused;
1872
+ gchar data[(3 * sizeof (gpointer)) - 1];
1873
+ };
1874
+
1875
+ struct _StdLargeString
1876
+ {
1877
+ gsize capacity;
1878
+ gsize size;
1879
+ gchar * data;
1880
+ };
1881
+
1882
+ struct _StdString
1883
+ {
1884
+ union
1885
+ {
1886
+ guint8 flags;
1887
+ StdTinyString tiny;
1888
+ StdLargeString large;
1889
+ };
1890
+ };
1891
+
1892
+ struct _ArtBacktrace
1893
+ {
1894
+ GChecksum * id;
1895
+ GArray * frames;
1896
+ gchar * frames_json;
1897
+ };
1898
+
1899
+ struct _ArtStackFrame
1900
+ {
1901
+ ArtMethod * method;
1902
+ gsize dexpc;
1903
+ StdString description;
1904
+ };
1905
+
1906
+ struct _ArtStackVisitorVTable
1907
+ {
1908
+ void (* unused1) (void);
1909
+ void (* unused2) (void);
1910
+ bool (* visit) (ArtStackVisitor * visitor);
1911
+ };
1912
+
1913
+ struct _ArtStackVisitor
1914
+ {
1915
+ ArtStackVisitorVTable * vtable;
1916
+
1917
+ guint8 padding[512];
1918
+
1919
+ ArtStackVisitorVTable vtable_storage;
1920
+
1921
+ ArtBacktrace * backtrace;
1922
+ };
1923
+
1924
+ extern GumTlsKey current_backtrace;
1925
+
1926
+ extern void (* perform_art_thread_state_transition) (JNIEnv * env);
1927
+
1928
+ extern ArtContext * art_thread_get_long_jump_context (ArtThread * thread);
1929
+
1930
+ extern void art_stack_visitor_init (ArtStackVisitor * visitor, ArtThread * thread, void * context, StackWalkKind walk_kind,
1931
+ size_t num_frames, bool check_suspended);
1932
+ extern void art_stack_visitor_walk_stack (ArtStackVisitor * visitor, bool include_transitions);
1933
+ extern ArtMethod * art_stack_visitor_get_method (ArtStackVisitor * visitor);
1934
+ extern void art_stack_visitor_describe_location (StdString * description, ArtStackVisitor * visitor);
1935
+ extern ArtMethod * translate_method (ArtMethod * method);
1936
+ extern void translate_location (ArtMethod * method, guint32 pc, const gchar ** source_file, gint32 * line_number);
1937
+ extern void cxx_delete (void * mem);
1938
+ extern unsigned long strtoul (const char * str, char ** endptr, int base);
1939
+
1940
+ extern void _frida_log (const gchar * message);
1941
+
1942
+ static bool visit_frame (ArtStackVisitor * visitor);
1943
+ static void art_stack_frame_destroy (ArtStackFrame * frame);
1944
+
1945
+ static void append_jni_type_name (GString * s, const gchar * name, gsize length);
1946
+
1947
+ static void std_string_destroy (StdString * str);
1948
+ static gchar * std_string_get_data (StdString * str);
1949
+
1950
+ static void frida_log (const char * format, ...);
1951
+
1952
+ void
1953
+ init (void)
1954
+ {
1955
+ current_backtrace = gum_tls_key_new ();
1956
+ }
1957
+
1958
+ void
1959
+ finalize (void)
1960
+ {
1961
+ gum_tls_key_free (current_backtrace);
1962
+ }
1963
+
1964
+ ArtBacktrace *
1965
+ _create (JNIEnv * env,
1966
+ guint limit)
1967
+ {
1968
+ ArtBacktrace * bt;
1969
+
1970
+ bt = g_new (ArtBacktrace, 1);
1971
+ bt->id = g_checksum_new (G_CHECKSUM_SHA1);
1972
+ bt->frames = (limit != 0)
1973
+ ? g_array_sized_new (FALSE, FALSE, sizeof (ArtStackFrame), limit)
1974
+ : g_array_new (FALSE, FALSE, sizeof (ArtStackFrame));
1975
+ g_array_set_clear_func (bt->frames, (GDestroyNotify) art_stack_frame_destroy);
1976
+ bt->frames_json = NULL;
1977
+
1978
+ gum_tls_key_set_value (current_backtrace, bt);
1979
+
1980
+ perform_art_thread_state_transition (env);
1981
+
1982
+ gum_tls_key_set_value (current_backtrace, NULL);
1983
+
1984
+ return bt;
1985
+ }
1986
+
1987
+ void
1988
+ _on_thread_state_transition_complete (ArtThread * thread)
1989
+ {
1990
+ ArtContext * context;
1991
+ ArtStackVisitor visitor = {
1992
+ .vtable_storage = {
1993
+ .visit = visit_frame,
1994
+ },
1995
+ };
1996
+
1997
+ context = art_thread_get_long_jump_context (thread);
1998
+
1999
+ art_stack_visitor_init (&visitor, thread, context, STACK_WALK_SKIP_INLINED_FRAMES, 0, true);
2000
+ visitor.vtable = &visitor.vtable_storage;
2001
+ visitor.backtrace = gum_tls_key_get_value (current_backtrace);
2002
+
2003
+ art_stack_visitor_walk_stack (&visitor, false);
2004
+
2005
+ cxx_delete (context);
2006
+ }
2007
+
2008
+ static bool
2009
+ visit_frame (ArtStackVisitor * visitor)
2010
+ {
2011
+ ArtBacktrace * bt = visitor->backtrace;
2012
+ ArtStackFrame frame;
2013
+ const gchar * description, * dexpc_part;
2014
+
2015
+ frame.method = art_stack_visitor_get_method (visitor);
2016
+
2017
+ art_stack_visitor_describe_location (&frame.description, visitor);
2018
+
2019
+ description = std_string_get_data (&frame.description);
2020
+ if (strstr (description, " '<") != NULL)
2021
+ goto skip;
2022
+
2023
+ dexpc_part = strstr (description, " at dex PC 0x");
2024
+ if (dexpc_part == NULL)
2025
+ goto skip;
2026
+ frame.dexpc = strtoul (dexpc_part + 13, NULL, 16);
2027
+
2028
+ g_array_append_val (bt->frames, frame);
2029
+
2030
+ g_checksum_update (bt->id, (guchar *) &frame.method, sizeof (frame.method));
2031
+ g_checksum_update (bt->id, (guchar *) &frame.dexpc, sizeof (frame.dexpc));
2032
+
2033
+ return true;
2034
+
2035
+ skip:
2036
+ std_string_destroy (&frame.description);
2037
+ return true;
2038
+ }
2039
+
2040
+ static void
2041
+ art_stack_frame_destroy (ArtStackFrame * frame)
2042
+ {
2043
+ std_string_destroy (&frame->description);
2044
+ }
2045
+
2046
+ void
2047
+ _destroy (ArtBacktrace * backtrace)
2048
+ {
2049
+ g_free (backtrace->frames_json);
2050
+ g_array_free (backtrace->frames, TRUE);
2051
+ g_checksum_free (backtrace->id);
2052
+ g_free (backtrace);
2053
+ }
2054
+
2055
+ const gchar *
2056
+ _get_id (ArtBacktrace * backtrace)
2057
+ {
2058
+ return g_checksum_get_string (backtrace->id);
2059
+ }
2060
+
2061
+ const gchar *
2062
+ _get_frames (ArtBacktrace * backtrace)
2063
+ {
2064
+ GArray * frames = backtrace->frames;
2065
+ JsonBuilder * b;
2066
+ guint i;
2067
+ JsonNode * root;
2068
+
2069
+ if (backtrace->frames_json != NULL)
2070
+ return backtrace->frames_json;
2071
+
2072
+ b = json_builder_new_immutable ();
2073
+
2074
+ json_builder_begin_array (b);
2075
+
2076
+ for (i = 0; i != frames->len; i++)
2077
+ {
2078
+ ArtStackFrame * frame = &g_array_index (frames, ArtStackFrame, i);
2079
+ gchar * description, * ret_type, * paren_open, * paren_close, * arg_types, * token, * method_name, * class_name;
2080
+ GString * signature;
2081
+ gchar * cursor;
2082
+ ArtMethod * translated_method;
2083
+ gsize dexpc;
2084
+ const gchar * source_file;
2085
+ gint32 line_number;
2086
+
2087
+ description = std_string_get_data (&frame->description);
2088
+
2089
+ ret_type = strchr (description, '\\'') + 1;
2090
+
2091
+ paren_open = strchr (ret_type, '(');
2092
+ paren_close = strchr (paren_open, ')');
2093
+ *paren_open = '\\0';
2094
+ *paren_close = '\\0';
2095
+
2096
+ arg_types = paren_open + 1;
2097
+
2098
+ token = strrchr (ret_type, '.');
2099
+ *token = '\\0';
2100
+
2101
+ method_name = token + 1;
2102
+
2103
+ token = strrchr (ret_type, ' ');
2104
+ *token = '\\0';
2105
+
2106
+ class_name = token + 1;
2107
+
2108
+ signature = g_string_sized_new (128);
2109
+
2110
+ append_jni_type_name (signature, class_name, method_name - class_name - 1);
2111
+ g_string_append_c (signature, ',');
2112
+ g_string_append (signature, method_name);
2113
+ g_string_append (signature, ",(");
2114
+
2115
+ if (arg_types != paren_close)
2116
+ {
2117
+ for (cursor = arg_types; cursor != NULL;)
2118
+ {
2119
+ gsize length;
2120
+ gchar * next;
2121
+
2122
+ token = strstr (cursor, ", ");
2123
+ if (token != NULL)
2124
+ {
2125
+ length = token - cursor;
2126
+ next = token + 2;
2127
+ }
2128
+ else
2129
+ {
2130
+ length = paren_close - cursor;
2131
+ next = NULL;
2132
+ }
2133
+
2134
+ append_jni_type_name (signature, cursor, length);
2135
+
2136
+ cursor = next;
2137
+ }
2138
+ }
2139
+
2140
+ g_string_append_c (signature, ')');
2141
+
2142
+ append_jni_type_name (signature, ret_type, class_name - ret_type - 1);
2143
+
2144
+ translated_method = translate_method (frame->method);
2145
+ dexpc = (translated_method == frame->method) ? frame->dexpc : 0;
2146
+
2147
+ translate_location (translated_method, dexpc, &source_file, &line_number);
2148
+
2149
+ json_builder_begin_object (b);
2150
+
2151
+ json_builder_set_member_name (b, "signature");
2152
+ json_builder_add_string_value (b, signature->str);
2153
+
2154
+ json_builder_set_member_name (b, "className");
2155
+ json_builder_add_string_value (b, class_name);
2156
+
2157
+ json_builder_set_member_name (b, "methodName");
2158
+ json_builder_add_string_value (b, method_name);
2159
+
2160
+ json_builder_set_member_name (b, "fileName");
2161
+ json_builder_add_string_value (b, source_file);
2162
+
2163
+ json_builder_set_member_name (b, "lineNumber");
2164
+ json_builder_add_int_value (b, line_number);
2165
+
2166
+ json_builder_end_object (b);
2167
+
2168
+ g_string_free (signature, TRUE);
2169
+ }
2170
+
2171
+ json_builder_end_array (b);
2172
+
2173
+ root = json_builder_get_root (b);
2174
+ backtrace->frames_json = json_to_string (root, FALSE);
2175
+ json_node_unref (root);
2176
+
2177
+ return backtrace->frames_json;
2178
+ }
2179
+
2180
+ static void
2181
+ append_jni_type_name (GString * s,
2182
+ const gchar * name,
2183
+ gsize length)
2184
+ {
2185
+ gchar shorty = '\\0';
2186
+ gsize i;
2187
+
2188
+ switch (name[0])
2189
+ {
2190
+ case 'b':
2191
+ if (strncmp (name, "boolean", length) == 0)
2192
+ shorty = 'Z';
2193
+ else if (strncmp (name, "byte", length) == 0)
2194
+ shorty = 'B';
2195
+ break;
2196
+ case 'c':
2197
+ if (strncmp (name, "char", length) == 0)
2198
+ shorty = 'C';
2199
+ break;
2200
+ case 'd':
2201
+ if (strncmp (name, "double", length) == 0)
2202
+ shorty = 'D';
2203
+ break;
2204
+ case 'f':
2205
+ if (strncmp (name, "float", length) == 0)
2206
+ shorty = 'F';
2207
+ break;
2208
+ case 'i':
2209
+ if (strncmp (name, "int", length) == 0)
2210
+ shorty = 'I';
2211
+ break;
2212
+ case 'l':
2213
+ if (strncmp (name, "long", length) == 0)
2214
+ shorty = 'J';
2215
+ break;
2216
+ case 's':
2217
+ if (strncmp (name, "short", length) == 0)
2218
+ shorty = 'S';
2219
+ break;
2220
+ case 'v':
2221
+ if (strncmp (name, "void", length) == 0)
2222
+ shorty = 'V';
2223
+ break;
2224
+ }
2225
+
2226
+ if (shorty != '\\0')
2227
+ {
2228
+ g_string_append_c (s, shorty);
2229
+
2230
+ return;
2231
+ }
2232
+
2233
+ if (length > 2 && name[length - 2] == '[' && name[length - 1] == ']')
2234
+ {
2235
+ g_string_append_c (s, '[');
2236
+ append_jni_type_name (s, name, length - 2);
2237
+
2238
+ return;
2239
+ }
2240
+
2241
+ g_string_append_c (s, 'L');
2242
+
2243
+ for (i = 0; i != length; i++)
2244
+ {
2245
+ gchar ch = name[i];
2246
+ if (ch != '.')
2247
+ g_string_append_c (s, ch);
2248
+ else
2249
+ g_string_append_c (s, '/');
2250
+ }
2251
+
2252
+ g_string_append_c (s, ';');
2253
+ }
2254
+
2255
+ static void
2256
+ std_string_destroy (StdString * str)
2257
+ {
2258
+ bool is_large = (str->flags & 1) != 0;
2259
+ if (is_large)
2260
+ cxx_delete (str->large.data);
2261
+ }
2262
+
2263
+ static gchar *
2264
+ std_string_get_data (StdString * str)
2265
+ {
2266
+ bool is_large = (str->flags & 1) != 0;
2267
+ return is_large ? str->large.data : str->tiny.data;
2268
+ }
2269
+ `, {
2270
+ current_backtrace: Memory.alloc(Process.pointerSize),
2271
+ perform_art_thread_state_transition: performImpl,
2272
+ art_thread_get_long_jump_context: api['art::Thread::GetLongJumpContext'],
2273
+ art_stack_visitor_init: api['art::StackVisitor::StackVisitor'],
2274
+ art_stack_visitor_walk_stack: api['art::StackVisitor::WalkStack'],
2275
+ art_stack_visitor_get_method: api['art::StackVisitor::GetMethod'],
2276
+ art_stack_visitor_describe_location: api['art::StackVisitor::DescribeLocation'],
2277
+ translate_method: artController.replacedMethods.translate,
2278
+ translate_location: api['art::Monitor::TranslateLocation'],
2279
+ cxx_delete: api.$delete,
2280
+ strtoul: Module.getExportByName('libc.so', 'strtoul')
2281
+ });
2282
+
2283
+ const _create = new NativeFunction(cm._create, 'pointer', ['pointer', 'uint'], nativeFunctionOptions);
2284
+ const _destroy = new NativeFunction(cm._destroy, 'void', ['pointer'], nativeFunctionOptions);
2285
+
2286
+ const fastOptions = { exceptions: 'propagate', scheduling: 'exclusive' };
2287
+ const _getId = new NativeFunction(cm._get_id, 'pointer', ['pointer'], fastOptions);
2288
+ const _getFrames = new NativeFunction(cm._get_frames, 'pointer', ['pointer'], fastOptions);
2289
+
2290
+ const performThreadStateTransition = makeArtThreadStateTransitionImpl(vm, env, cm._on_thread_state_transition_complete);
2291
+ cm._performData = performThreadStateTransition;
2292
+ performImpl.writePointer(performThreadStateTransition);
2293
+
2294
+ cm.backtrace = (env, limit) => {
2295
+ const handle = _create(env, limit);
2296
+ const bt = new Backtrace(handle);
2297
+ Script.bindWeak(bt, destroy.bind(null, handle));
2298
+ return bt;
2299
+ };
2300
+
2301
+ function destroy (handle) {
2302
+ _destroy(handle);
2303
+ }
2304
+
2305
+ cm.getId = handle => {
2306
+ return _getId(handle).readUtf8String();
2307
+ };
2308
+
2309
+ cm.getFrames = handle => {
2310
+ return JSON.parse(_getFrames(handle).readUtf8String());
2311
+ };
2312
+
2313
+ return cm;
2314
+ }
2315
+
2316
+ class Backtrace {
2317
+ constructor (handle) {
2318
+ this.handle = handle;
2319
+ }
2320
+
2321
+ get id () {
2322
+ return backtraceModule.getId(this.handle);
2323
+ }
2324
+
2325
+ get frames () {
2326
+ return backtraceModule.getFrames(this.handle);
2327
+ }
2328
+ }
2329
+
1815
2330
  function revertGlobalPatches () {
1816
2331
  patchedClasses.forEach(entry => {
1817
2332
  entry.vtablePtr.writePointer(entry.vtable);
@@ -2855,7 +3370,7 @@ const threadStateTransitionRecompilers = {
2855
3370
  arm64: recompileExceptionClearForArm64
2856
3371
  };
2857
3372
 
2858
- function _getArtThreadStateTransitionImpl (vm, env) {
3373
+ function makeArtThreadStateTransitionImpl (vm, env, callback) {
2859
3374
  const envVtable = env.handle.readPointer();
2860
3375
  const exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
2861
3376
  const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
@@ -2866,7 +3381,6 @@ function _getArtThreadStateTransitionImpl (vm, env) {
2866
3381
  }
2867
3382
 
2868
3383
  let perform = null;
2869
- const callback = new NativeCallback(onThreadStateTransitionComplete, 'void', ['pointer']);
2870
3384
 
2871
3385
  const threadOffsets = getArtThreadSpec(vm).offset;
2872
3386
 
@@ -4065,6 +4579,7 @@ module.exports = {
4065
4579
  ArtMethod,
4066
4580
  makeMethodMangler,
4067
4581
  translateMethod,
4582
+ backtrace,
4068
4583
  revertGlobalPatches,
4069
4584
  deoptimizeEverything,
4070
4585
  deoptimizeBootImage,
@@ -1680,6 +1680,8 @@ function handleMethodInvocation (jniArgs, params) {
1680
1680
  env.pushLocalFrame(3);
1681
1681
  let haveFrame = true;
1682
1682
 
1683
+ vm.link(tid, env);
1684
+
1683
1685
  try {
1684
1686
  pendingCalls.add(tid);
1685
1687
 
@@ -1727,6 +1729,8 @@ function handleMethodInvocation (jniArgs, params) {
1727
1729
 
1728
1730
  return retType.defaultValue;
1729
1731
  } finally {
1732
+ vm.unlink(tid);
1733
+
1730
1734
  if (haveFrame) {
1731
1735
  env.popLocalFrame(NULL);
1732
1736
  }
package/lib/jvm.js CHANGED
@@ -811,8 +811,8 @@ function _getJvmMethodSpec () {
811
811
 
812
812
  const getAdapterPointer = adapterInConstMethod
813
813
  ? function (method, constMethod) {
814
- return constMethod.add(constantPoolOffset + 2 * pointerSize);
815
- }
814
+ return constMethod.add(constantPoolOffset + 2 * pointerSize);
815
+ }
816
816
  : function (method, constMethod) {
817
817
  return method.add(i2iEntryOffset + pointerSize);
818
818
  };
package/lib/vm.js CHANGED
@@ -6,15 +6,14 @@ const JNI_VERSION_1_6 = 0x00010006;
6
6
  const pointerSize = Process.pointerSize;
7
7
 
8
8
  const jsThreadID = Process.getCurrentThreadId();
9
- let jsThreadDetachAllowed = true;
10
- let jsEnv = null;
9
+ const attachedThreads = new Map();
10
+ const activeEnvs = new Map();
11
11
 
12
12
  function VM (api) {
13
13
  const handle = api.vm;
14
14
  let attachCurrentThread = null;
15
15
  let detachCurrentThread = null;
16
16
  let getEnv = null;
17
- const attachedThreads = new Map();
18
17
 
19
18
  function initialize () {
20
19
  const vtable = handle.readPointer();
@@ -31,25 +30,29 @@ function VM (api) {
31
30
  this.perform = function (fn) {
32
31
  const threadId = Process.getCurrentThreadId();
33
32
 
34
- const isJsThread = threadId === jsThreadID;
35
- if (isJsThread && jsEnv !== null) {
36
- return fn(jsEnv);
33
+ const cachedEnv = tryGetCachedEnv(threadId);
34
+ if (cachedEnv !== null) {
35
+ return fn(cachedEnv);
37
36
  }
38
37
 
39
- let env = this.tryGetEnv();
38
+ let env = this._tryGetEnv();
40
39
  const alreadyAttached = env !== null;
41
40
  if (!alreadyAttached) {
42
41
  env = this.attachCurrentThread();
43
- if (isJsThread) {
44
- jsEnv = env;
45
- } else {
46
- attachedThreads.set(threadId, true);
47
- }
42
+ attachedThreads.set(threadId, true);
48
43
  }
49
44
 
45
+ this.link(threadId, env);
46
+
50
47
  try {
51
48
  return fn(env);
52
49
  } finally {
50
+ const isJsThread = threadId === jsThreadID;
51
+
52
+ if (!isJsThread) {
53
+ this.unlink(threadId);
54
+ }
55
+
53
56
  if (!alreadyAttached && !isJsThread) {
54
57
  const allowedToDetach = attachedThreads.get(threadId);
55
58
  attachedThreads.delete(threadId);
@@ -74,14 +77,17 @@ function VM (api) {
74
77
  this.preventDetachDueToClassLoader = function () {
75
78
  const threadId = Process.getCurrentThreadId();
76
79
 
77
- if (threadId === jsThreadID) {
78
- jsThreadDetachAllowed = false;
79
- } else if (attachedThreads.has(threadId)) {
80
+ if (attachedThreads.has(threadId)) {
80
81
  attachedThreads.set(threadId, false);
81
82
  }
82
83
  };
83
84
 
84
85
  this.getEnv = function () {
86
+ const cachedEnv = tryGetCachedEnv(Process.getCurrentThreadId());
87
+ if (cachedEnv !== null) {
88
+ return cachedEnv;
89
+ }
90
+
85
91
  const envBuf = Memory.alloc(pointerSize);
86
92
  const result = getEnv(handle, envBuf, JNI_VERSION_1_6);
87
93
  if (result === -2) {
@@ -92,6 +98,15 @@ function VM (api) {
92
98
  };
93
99
 
94
100
  this.tryGetEnv = function () {
101
+ const cachedEnv = tryGetCachedEnv(Process.getCurrentThreadId());
102
+ if (cachedEnv !== null) {
103
+ return cachedEnv;
104
+ }
105
+
106
+ return this._tryGetEnv();
107
+ };
108
+
109
+ this._tryGetEnv = function () {
95
110
  const envBuf = Memory.alloc(pointerSize);
96
111
  const result = getEnv(handle, envBuf, JNI_VERSION_1_6);
97
112
  if (result !== JNI_OK) {
@@ -108,12 +123,38 @@ function VM (api) {
108
123
  };
109
124
  };
110
125
 
126
+ this.link = function (tid, env) {
127
+ const entry = activeEnvs.get(tid);
128
+ if (entry === undefined) {
129
+ activeEnvs.set(tid, [env, 1]);
130
+ } else {
131
+ entry[1]++;
132
+ }
133
+ };
134
+
135
+ this.unlink = function (tid) {
136
+ const entry = activeEnvs.get(tid);
137
+ if (entry[1] === 1) {
138
+ activeEnvs.delete(tid);
139
+ } else {
140
+ entry[1]--;
141
+ }
142
+ };
143
+
144
+ function tryGetCachedEnv (threadId) {
145
+ const entry = activeEnvs.get(threadId);
146
+ if (entry === undefined) {
147
+ return null;
148
+ }
149
+ return entry[0];
150
+ }
151
+
111
152
  initialize.call(this);
112
153
  }
113
154
 
114
155
  VM.dispose = function (vm) {
115
- if (jsThreadDetachAllowed && jsEnv !== null) {
116
- jsEnv = null;
156
+ if (attachedThreads.get(jsThreadID) === true) {
157
+ attachedThreads.delete(jsThreadID);
117
158
  vm.detachCurrentThread();
118
159
  }
119
160
  };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "frida-java-bridge",
3
- "version": "5.2.1",
3
+ "version": "6.0.1",
4
4
  "description": "Java runtime interop from Frida",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "/index.js",
8
- "/lib/**/*.js"
8
+ "/lib/**/*.js",
9
+ "/lib/**/*.d.ts"
9
10
  ],
10
11
  "repository": {
11
12
  "type": "git",