frida-java-bridge 7.0.8 → 7.0.10
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.d.ts +21 -7
- package/lib/android.js +195 -4
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -415,11 +415,25 @@ declare module "frida-java-bridge" {
|
|
|
415
415
|
[name: string]: any;
|
|
416
416
|
};
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
type IsEmptyArray<T extends any[]> = T extends [] ? true : false;
|
|
419
|
+
|
|
420
|
+
type Overload<Identifiers extends Array<string> = [], Types extends Array<any> = [], Return = any> = [Identifiers, Types, Return];
|
|
421
|
+
|
|
422
|
+
type OverloadsMethods<
|
|
423
|
+
Holder extends Members<Holder> = {},
|
|
424
|
+
OLs extends ReadonlyArray<Overload<any, any, any>> = []
|
|
425
|
+
> = {
|
|
426
|
+
[K in keyof OLs]:
|
|
427
|
+
OLs[K] extends Overload<any, infer A extends any[], infer R>
|
|
428
|
+
? Method<Holder, A, R>
|
|
429
|
+
: never
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
interface MethodDispatcher<Holder extends Members<Holder> = {}, Overloads extends Array<Overload<Array<any>, Array<any>, any>> = []> extends Method<Holder> {
|
|
419
433
|
/**
|
|
420
434
|
* Available overloads.
|
|
421
435
|
*/
|
|
422
|
-
overloads: Array<Method<Holder
|
|
436
|
+
overloads: IsEmptyArray<Overloads> extends true ? Array<Method<Holder>> : OverloadsMethods<Holder, Overloads>;
|
|
423
437
|
|
|
424
438
|
/**
|
|
425
439
|
* Obtains a specific overload.
|
|
@@ -430,8 +444,8 @@ declare module "frida-java-bridge" {
|
|
|
430
444
|
overload(...args: string[]): Method<Holder>;
|
|
431
445
|
}
|
|
432
446
|
|
|
433
|
-
interface Method<Holder extends Members<Holder> = {}> {
|
|
434
|
-
(...params:
|
|
447
|
+
interface Method<Holder extends Members<Holder> = {}, Params extends any[] = any[], Return = any> {
|
|
448
|
+
(...params: Params): Return;
|
|
435
449
|
|
|
436
450
|
/**
|
|
437
451
|
* Name of this method.
|
|
@@ -458,7 +472,7 @@ declare module "frida-java-bridge" {
|
|
|
458
472
|
* replace the original implementation. Assign `null` at a future point
|
|
459
473
|
* to revert back to the original implementation.
|
|
460
474
|
*/
|
|
461
|
-
implementation: MethodImplementation<Holder> | null;
|
|
475
|
+
implementation: MethodImplementation<Holder, Params, Return> | null;
|
|
462
476
|
|
|
463
477
|
/**
|
|
464
478
|
* Method return type.
|
|
@@ -481,10 +495,10 @@ declare module "frida-java-bridge" {
|
|
|
481
495
|
* Useful for e.g. setting `traps: "all"` to perform execution tracing
|
|
482
496
|
* in conjunction with Stalker.
|
|
483
497
|
*/
|
|
484
|
-
clone: (options: NativeFunctionOptions) => Method<Holder>;
|
|
498
|
+
clone: (options: NativeFunctionOptions) => Method<Holder, Params, Return>;
|
|
485
499
|
}
|
|
486
500
|
|
|
487
|
-
type MethodImplementation<This extends Members<This> = {}> = (this: Wrapper<This>, ...params:
|
|
501
|
+
type MethodImplementation<This extends Members<This> = {}, Params extends any[] = any[], Return = any> = (this: Wrapper<This>, ...params: Params) => Return;
|
|
488
502
|
|
|
489
503
|
interface Field<Value = any, Holder extends Members<Holder> = {}> {
|
|
490
504
|
/**
|
package/lib/android.js
CHANGED
|
@@ -88,6 +88,7 @@ const getArtThreadStateTransitionImpl = memoize(_getArtThreadStateTransitionImpl
|
|
|
88
88
|
export const getAndroidVersion = memoize(_getAndroidVersion);
|
|
89
89
|
const getAndroidCodename = memoize(_getAndroidCodename);
|
|
90
90
|
export const getAndroidApiLevel = memoize(_getAndroidApiLevel);
|
|
91
|
+
export const getArtApexVersion = memoize(_getArtApexVersion);
|
|
91
92
|
const getArtQuickFrameInfoGetterThunk = memoize(_getArtQuickFrameInfoGetterThunk);
|
|
92
93
|
|
|
93
94
|
const makeCxxMethodWrapperReturningPointerByValue =
|
|
@@ -253,6 +254,7 @@ function _getApi () {
|
|
|
253
254
|
this['art::StackVisitor::GetCurrentQuickFrameInfo'] = makeArtQuickFrameInfoGetter(address);
|
|
254
255
|
},
|
|
255
256
|
|
|
257
|
+
_ZN3art7Context6CreateEv: ['art::Context::Create', 'pointer', []],
|
|
256
258
|
_ZN3art6Thread18GetLongJumpContextEv: ['art::Thread::GetLongJumpContext', 'pointer', ['pointer']],
|
|
257
259
|
|
|
258
260
|
_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE: function (address) {
|
|
@@ -352,6 +354,7 @@ function _getApi () {
|
|
|
352
354
|
'_ZNK3art12StackVisitor9GetMethodEv',
|
|
353
355
|
'_ZNK3art12StackVisitor16DescribeLocationEv',
|
|
354
356
|
'_ZNK3art12StackVisitor24GetCurrentQuickFrameInfoEv',
|
|
357
|
+
'_ZN3art7Context6CreateEv',
|
|
355
358
|
'_ZN3art6Thread18GetLongJumpContextEv',
|
|
356
359
|
'_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE',
|
|
357
360
|
'_ZN3art6mirror5Class11GetLocationEv',
|
|
@@ -461,6 +464,11 @@ function _getApi () {
|
|
|
461
464
|
const instrumentationOffset = runtimeOffset.instrumentation;
|
|
462
465
|
temporaryApi.artInstrumentation = (instrumentationOffset !== null) ? artRuntime.add(instrumentationOffset) : null;
|
|
463
466
|
|
|
467
|
+
const instrumentationIsPointer = getArtApexVersion() >= 360_000_000;
|
|
468
|
+
if (instrumentationIsPointer && temporaryApi.artInstrumentation != null) {
|
|
469
|
+
temporaryApi.artInstrumentation = temporaryApi.artInstrumentation.readPointer();
|
|
470
|
+
}
|
|
471
|
+
|
|
464
472
|
temporaryApi.artHeap = artRuntime.add(runtimeOffset.heap).readPointer();
|
|
465
473
|
temporaryApi.artThreadList = artRuntime.add(runtimeOffset.threadList).readPointer();
|
|
466
474
|
|
|
@@ -691,7 +699,11 @@ function _getArtRuntimeSpec (api) {
|
|
|
691
699
|
throw new Error('Unable to determine Runtime field offsets');
|
|
692
700
|
}
|
|
693
701
|
|
|
694
|
-
|
|
702
|
+
const instrumentationIsPointer = getArtApexVersion() >= 360_000_000;
|
|
703
|
+
spec.offset.instrumentation = instrumentationIsPointer
|
|
704
|
+
? tryDetectInstrumentationPointer(api)
|
|
705
|
+
: tryDetectInstrumentationOffset(api);
|
|
706
|
+
|
|
695
707
|
spec.offset.jniIdsIndirection = tryDetectJniIdsIndirectionOffset(api);
|
|
696
708
|
|
|
697
709
|
return spec;
|
|
@@ -771,6 +783,78 @@ function parseArm64InstrumentationOffset (insn) {
|
|
|
771
783
|
return offset;
|
|
772
784
|
}
|
|
773
785
|
|
|
786
|
+
const instrumentationPointerParser = {
|
|
787
|
+
ia32: parsex86InstrumentationPointer,
|
|
788
|
+
x64: parsex86InstrumentationPointer,
|
|
789
|
+
arm: parseArmInstrumentationPointer,
|
|
790
|
+
arm64: parseArm64InstrumentationPointer
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
function tryDetectInstrumentationPointer (api) {
|
|
794
|
+
const impl = api['art::Runtime::DeoptimizeBootImage'];
|
|
795
|
+
if (impl === undefined) {
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return parseInstructionsAt(impl, instrumentationPointerParser[Process.arch], { limit: 30 });
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function parsex86InstrumentationPointer (insn) {
|
|
803
|
+
if (insn.mnemonic !== 'mov') {
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const ops = insn.operands;
|
|
808
|
+
|
|
809
|
+
const dst = ops[0];
|
|
810
|
+
if (dst.value !== 'rax') {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const src = ops[1];
|
|
815
|
+
if (src.type !== 'mem') {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const mem = src.value;
|
|
820
|
+
if (mem.base !== 'rdi') {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const offset = mem.disp;
|
|
825
|
+
if (offset < 0x100 || offset > 0x400) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
return offset;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function parseArmInstrumentationPointer (insn) {
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function parseArm64InstrumentationPointer (insn) {
|
|
836
|
+
if (insn.mnemonic !== 'ldr') {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const ops = insn.operands;
|
|
841
|
+
|
|
842
|
+
if (ops[0].value === 'x0') {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const mem = ops[1].value;
|
|
847
|
+
if (mem.base !== 'x0') {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const offset = mem.disp;
|
|
852
|
+
if (offset < 0x100 || offset > 0x400) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
return offset;
|
|
856
|
+
}
|
|
857
|
+
|
|
774
858
|
const jniIdsIndirectionOffsetParsers = {
|
|
775
859
|
ia32: parsex86JniIdsIndirectionOffset,
|
|
776
860
|
x64: parsex86JniIdsIndirectionOffset,
|
|
@@ -1364,6 +1448,39 @@ function _getAndroidApiLevel () {
|
|
|
1364
1448
|
return parseInt(getAndroidSystemProperty('ro.build.version.sdk'), 10);
|
|
1365
1449
|
}
|
|
1366
1450
|
|
|
1451
|
+
function _getArtApexVersion () {
|
|
1452
|
+
try {
|
|
1453
|
+
const mountInfo = File.readAllText('/proc/self/mountinfo');
|
|
1454
|
+
|
|
1455
|
+
let artSource = null;
|
|
1456
|
+
const sourceVersions = new Map();
|
|
1457
|
+
for (const line of mountInfo.trimEnd().split('\n')) {
|
|
1458
|
+
const elements = line.split(' ');
|
|
1459
|
+
|
|
1460
|
+
const mountRoot = elements[4];
|
|
1461
|
+
if (!mountRoot.startsWith('/apex/com.android.art')) {
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
const mountSource = elements[10];
|
|
1466
|
+
if (mountRoot.includes('@')) {
|
|
1467
|
+
sourceVersions.set(mountSource, mountRoot.split('@')[1]);
|
|
1468
|
+
} else {
|
|
1469
|
+
artSource = mountSource;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
const strVersion = sourceVersions.get(artSource);
|
|
1474
|
+
return (strVersion !== undefined) ? parseInt(strVersion) : computeArtApexVersionFromApiLevel();
|
|
1475
|
+
} catch {
|
|
1476
|
+
return computeArtApexVersionFromApiLevel();
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
function computeArtApexVersionFromApiLevel () {
|
|
1481
|
+
return getAndroidApiLevel() * 10_000_000;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1367
1484
|
let systemPropertyGet = null;
|
|
1368
1485
|
const PROP_VALUE_MAX = 92;
|
|
1369
1486
|
|
|
@@ -1749,6 +1866,31 @@ set_replacement_method (gpointer original_method,
|
|
|
1749
1866
|
g_mutex_unlock (&lock);
|
|
1750
1867
|
}
|
|
1751
1868
|
|
|
1869
|
+
void
|
|
1870
|
+
synchronize_replacement_methods (guint quick_code_offset,
|
|
1871
|
+
void * nterp_entrypoint,
|
|
1872
|
+
void * quick_to_interpreter_bridge)
|
|
1873
|
+
{
|
|
1874
|
+
GHashTableIter iter;
|
|
1875
|
+
gpointer hooked_method, replacement_method;
|
|
1876
|
+
|
|
1877
|
+
g_mutex_lock (&lock);
|
|
1878
|
+
|
|
1879
|
+
g_hash_table_iter_init (&iter, methods);
|
|
1880
|
+
while (g_hash_table_iter_next (&iter, &hooked_method, &replacement_method))
|
|
1881
|
+
{
|
|
1882
|
+
void ** quick_code;
|
|
1883
|
+
|
|
1884
|
+
*((uint32_t *) replacement_method) = *((uint32_t *) hooked_method);
|
|
1885
|
+
|
|
1886
|
+
quick_code = hooked_method + quick_code_offset;
|
|
1887
|
+
if (*quick_code == nterp_entrypoint)
|
|
1888
|
+
*quick_code = quick_to_interpreter_bridge;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
g_mutex_unlock (&lock);
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1752
1894
|
void
|
|
1753
1895
|
delete_replacement_method (gpointer original_method)
|
|
1754
1896
|
{
|
|
@@ -1907,6 +2049,7 @@ on_leave_gc_concurrent_copying_copying_phase (GumInvocationContext * ic)
|
|
|
1907
2049
|
isReplacement: new NativeFunction(cm.is_replacement_method, 'bool', ['pointer'], fastOptions),
|
|
1908
2050
|
get: new NativeFunction(cm.get_replacement_method, 'pointer', ['pointer'], fastOptions),
|
|
1909
2051
|
set: new NativeFunction(cm.set_replacement_method, 'void', ['pointer', 'pointer'], fastOptions),
|
|
2052
|
+
synchronize: new NativeFunction(cm.synchronize_replacement_methods, 'void', ['uint', 'pointer', 'pointer'], fastOptions),
|
|
1910
2053
|
delete: new NativeFunction(cm.delete_replacement_method, 'void', ['pointer'], fastOptions),
|
|
1911
2054
|
translate: new NativeFunction(cm.translate_method, 'pointer', ['pointer'], fastOptions),
|
|
1912
2055
|
findReplacementFromQuickCode: cm.find_replacement_method_from_quick_code
|
|
@@ -1940,6 +2083,8 @@ function ensureArtKnowsHowToHandleMethodInstrumentation (vm) {
|
|
|
1940
2083
|
|
|
1941
2084
|
instrumentArtQuickEntrypoints(vm);
|
|
1942
2085
|
instrumentArtMethodInvocationFromInterpreter();
|
|
2086
|
+
instrumentArtGarbageCollection();
|
|
2087
|
+
instrumentArtFixupStaticTrampolines();
|
|
1943
2088
|
}
|
|
1944
2089
|
|
|
1945
2090
|
function instrumentArtQuickEntrypoints (vm) {
|
|
@@ -1991,6 +2136,52 @@ function instrumentArtMethodInvocationFromInterpreter () {
|
|
|
1991
2136
|
}
|
|
1992
2137
|
}
|
|
1993
2138
|
|
|
2139
|
+
function instrumentArtGarbageCollection () {
|
|
2140
|
+
const api = getApi();
|
|
2141
|
+
const art = api.module;
|
|
2142
|
+
|
|
2143
|
+
const gc = art.findSymbolByName('_ZN3art2gc4Heap22CollectGarbageInternalENS0_9collector6GcTypeENS0_7GcCauseEbj');
|
|
2144
|
+
if (gc === null) {
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
const { artNterpEntryPoint, artQuickToInterpreterBridge } = api;
|
|
2149
|
+
const quickCodeOffset = getArtMethodSpec(api.vm).offset.quickCode;
|
|
2150
|
+
Interceptor.attach(gc, {
|
|
2151
|
+
onLeave () {
|
|
2152
|
+
artController.replacedMethods.synchronize(quickCodeOffset, artNterpEntryPoint, artQuickToInterpreterBridge);
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
function instrumentArtFixupStaticTrampolines () {
|
|
2158
|
+
const patterns = [
|
|
2159
|
+
['_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE', 'e90340f8 : ff0ff0ff'],
|
|
2160
|
+
['_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl', '7f0f00f9 : 1ffcffff'],
|
|
2161
|
+
];
|
|
2162
|
+
const api = getApi();
|
|
2163
|
+
const art = api.module;
|
|
2164
|
+
for (const [name, pattern] of patterns) {
|
|
2165
|
+
const base = art.findSymbolByName(name);
|
|
2166
|
+
if (base === null) {
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
const matches = Memory.scanSync(base, 8192, pattern);
|
|
2171
|
+
if (matches.length === 0) {
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
const { artNterpEntryPoint, artQuickToInterpreterBridge } = api;
|
|
2176
|
+
const quickCodeOffset = getArtMethodSpec(api.vm).offset.quickCode;
|
|
2177
|
+
Interceptor.attach(matches[0].address, function () {
|
|
2178
|
+
artController.replacedMethods.synchronize(quickCodeOffset, artNterpEntryPoint, artQuickToInterpreterBridge);
|
|
2179
|
+
});
|
|
2180
|
+
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1994
2185
|
function ensureArtKnowsHowToHandleReplacementMethods (vm) {
|
|
1995
2186
|
if (taughtArtAboutReplacementMethods) {
|
|
1996
2187
|
return;
|
|
@@ -2534,7 +2725,7 @@ extern GumTlsKey current_backtrace;
|
|
|
2534
2725
|
|
|
2535
2726
|
extern void (* perform_art_thread_state_transition) (JNIEnv * env);
|
|
2536
2727
|
|
|
2537
|
-
extern ArtContext *
|
|
2728
|
+
extern ArtContext * art_make_context (ArtThread * thread);
|
|
2538
2729
|
|
|
2539
2730
|
extern void art_stack_visitor_init (ArtStackVisitor * visitor, ArtThread * thread, void * context, StackWalkKind walk_kind,
|
|
2540
2731
|
size_t num_frames, bool check_suspended);
|
|
@@ -2600,7 +2791,7 @@ _on_thread_state_transition_complete (ArtThread * thread)
|
|
|
2600
2791
|
},
|
|
2601
2792
|
};
|
|
2602
2793
|
|
|
2603
|
-
context =
|
|
2794
|
+
context = art_make_context (thread);
|
|
2604
2795
|
|
|
2605
2796
|
art_stack_visitor_init (&visitor, thread, context, STACK_WALK_SKIP_INLINED_FRAMES, 0, true);
|
|
2606
2797
|
visitor.vtable = &visitor.vtable_storage;
|
|
@@ -2885,7 +3076,7 @@ std_string_get_data (StdString * str)
|
|
|
2885
3076
|
`, {
|
|
2886
3077
|
current_backtrace: Memory.alloc(Process.pointerSize),
|
|
2887
3078
|
perform_art_thread_state_transition: performImpl,
|
|
2888
|
-
|
|
3079
|
+
art_make_context: api['art::Thread::GetLongJumpContext'] ?? api['art::Context::Create'],
|
|
2889
3080
|
art_stack_visitor_init: api['art::StackVisitor::StackVisitor'],
|
|
2890
3081
|
art_stack_visitor_walk_stack: api['art::StackVisitor::WalkStack'],
|
|
2891
3082
|
art_stack_visitor_get_method: api['art::StackVisitor::GetMethod'],
|