frida-java-bridge 5.3.0 → 6.0.2

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/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
 
@@ -1199,6 +1200,11 @@ function withRunnableArtThread (vm, env, fn) {
1199
1200
  }
1200
1201
  }
1201
1202
 
1203
+ function _getArtThreadStateTransitionImpl (vm, env) {
1204
+ const callback = new NativeCallback(onThreadStateTransitionComplete, 'void', ['pointer']);
1205
+ return makeArtThreadStateTransitionImpl(vm, env, callback);
1206
+ }
1207
+
1202
1208
  function onThreadStateTransitionComplete (thread) {
1203
1209
  const id = thread.toString();
1204
1210
 
@@ -1815,87 +1821,510 @@ function translateMethod (methodId) {
1815
1821
  return artController.replacedMethods.translate(methodId);
1816
1822
  }
1817
1823
 
1818
- class BacktraceVisitor extends ArtStackVisitor {
1819
- constructor (thread, limit) {
1820
- const api = getApi();
1821
-
1822
- super(thread, api['art::Thread::GetLongJumpContext'](thread), 'include-inlined-frames');
1824
+ function backtrace (vm, options = {}) {
1825
+ const { limit = 16 } = options;
1823
1826
 
1824
- this.frames = [];
1825
- this.limit = limit;
1827
+ const env = vm.getEnv();
1826
1828
 
1827
- this._translateLocation = api['art::Monitor::TranslateLocation'];
1829
+ if (backtraceModule === null) {
1830
+ backtraceModule = makeBacktraceModule(vm, env);
1828
1831
  }
1829
1832
 
1830
- visitFrame () {
1831
- this._collectFrame(this.describeLocation());
1833
+ return backtraceModule.backtrace(env, limit);
1834
+ }
1832
1835
 
1833
- return this.frames.length < this.limit;
1834
- }
1836
+ function makeBacktraceModule (vm, env) {
1837
+ const api = getApi();
1835
1838
 
1836
- _collectFrame (location) {
1837
- if (location === 'upcall') {
1838
- return;
1839
- }
1839
+ const performImpl = Memory.alloc(Process.pointerSize);
1840
1840
 
1841
- const tokens = location.split(/['"]/, 3);
1842
- const rawMethodSignature = tokens[1];
1843
- if (rawMethodSignature.startsWith('<')) {
1844
- return;
1845
- }
1846
- const details = tokens[2];
1847
-
1848
- const separatorIndex = rawMethodSignature.indexOf(' ');
1849
- const returnType = rawMethodSignature.substring(0, separatorIndex);
1850
- const rest = rawMethodSignature.substring(separatorIndex + 1);
1851
- const argsStartIndex = rest.indexOf('(');
1852
- const argsEndIndex = rest.indexOf(')', argsStartIndex + 1);
1853
- const rawArgumentTypes = rest.substring(argsStartIndex + 1, argsEndIndex);
1854
- const argumentTypes = (rawArgumentTypes !== '') ? rawArgumentTypes.split(', ') : [];
1855
-
1856
- const classAndMethodName = rest.substring(0, argsStartIndex);
1857
- const methodNameStartIndex = classAndMethodName.lastIndexOf('.');
1858
- const className = classAndMethodName.substring(0, methodNameStartIndex);
1859
- const methodName = classAndMethodName.substring(methodNameStartIndex + 1);
1860
- let dexPc = parseInt(details.substring(13), 16);
1861
-
1862
- const actualMethod = this.getMethod();
1863
- const translatedMethod = translateMethod(actualMethod);
1864
- if (!translatedMethod.equals(actualMethod)) {
1865
- dexPc = 0;
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
+ }
1866
2138
  }
1867
- const fileNamePtr = Memory.alloc(16);
1868
- const lineNumberPtr = fileNamePtr.add(8);
1869
- this._translateLocation(translatedMethod, dexPc, fileNamePtr, lineNumberPtr);
1870
- const fileName = fileNamePtr.readPointer().readUtf8String();
1871
- const lineNumber = lineNumberPtr.readS32();
1872
-
1873
- this.frames.push({
1874
- method: {
1875
- handle: translatedMethod,
1876
- name: methodName,
1877
- returnType,
1878
- argumentTypes
1879
- },
1880
- className,
1881
- fileName,
1882
- lineNumber
1883
- });
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);
1884
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;
1885
2178
  }
1886
2179
 
1887
- function backtrace (vm, options = {}) {
1888
- const { limit = 16 } = options;
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;
1889
2187
 
1890
- let frames = null;
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
+ }
1891
2262
 
1892
- withRunnableArtThread(vm, vm.getEnv(), thread => {
1893
- const visitor = new BacktraceVisitor(thread, limit);
1894
- visitor.walkStack(true);
1895
- frames = visitor.frames;
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')
1896
2281
  });
1897
2282
 
1898
- return frames;
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
+ }
1899
2328
  }
1900
2329
 
1901
2330
  function revertGlobalPatches () {
@@ -2278,7 +2707,7 @@ function writeArtQuickCodePrologueArm64 (target, trampoline, redirectSize) {
2278
2707
 
2279
2708
  const artQuickCodeHookRedirectSize = {
2280
2709
  ia32: 5,
2281
- x64: 14,
2710
+ x64: 16,
2282
2711
  arm: 8,
2283
2712
  arm64: 16
2284
2713
  };
@@ -2941,7 +3370,7 @@ const threadStateTransitionRecompilers = {
2941
3370
  arm64: recompileExceptionClearForArm64
2942
3371
  };
2943
3372
 
2944
- function _getArtThreadStateTransitionImpl (vm, env) {
3373
+ function makeArtThreadStateTransitionImpl (vm, env, callback) {
2945
3374
  const envVtable = env.handle.readPointer();
2946
3375
  const exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
2947
3376
  const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
@@ -2952,7 +3381,6 @@ function _getArtThreadStateTransitionImpl (vm, env) {
2952
3381
  }
2953
3382
 
2954
3383
  let perform = null;
2955
- const callback = new NativeCallback(onThreadStateTransitionComplete, 'void', ['pointer']);
2956
3384
 
2957
3385
  const threadOffsets = getArtThreadSpec(vm).offset;
2958
3386
 
@@ -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/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,6 +1,6 @@
1
1
  {
2
2
  "name": "frida-java-bridge",
3
- "version": "5.3.0",
3
+ "version": "6.0.2",
4
4
  "description": "Java runtime interop from Frida",
5
5
  "main": "index.js",
6
6
  "files": [