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 +5 -0
- package/lib/android.d.ts +33 -0
- package/lib/android.js +519 -4
- package/lib/class-factory.js +4 -0
- package/lib/jvm.js +2 -2
- package/lib/vm.js +58 -17
- package/package.json +3 -2
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');
|
package/lib/android.d.ts
ADDED
|
@@ -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
|
|
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,
|
package/lib/class-factory.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
35
|
-
if (
|
|
36
|
-
return fn(
|
|
33
|
+
const cachedEnv = tryGetCachedEnv(threadId);
|
|
34
|
+
if (cachedEnv !== null) {
|
|
35
|
+
return fn(cachedEnv);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
let env = this.
|
|
38
|
+
let env = this._tryGetEnv();
|
|
40
39
|
const alreadyAttached = env !== null;
|
|
41
40
|
if (!alreadyAttached) {
|
|
42
41
|
env = this.attachCurrentThread();
|
|
43
|
-
|
|
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
|
|
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 (
|
|
116
|
-
|
|
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": "
|
|
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",
|