privateArtMethod(Constructor constructor) { if (constructor == null) { thrownewIllegalArgumentException("constructor can not be null"); } this.constructor = constructor; init(); }
privateArtMethod(Method method, long address) { if (method == null) { thrownewIllegalArgumentException("method can not be null"); } this.method = method; if (address != -1) { this.address = address; } else { init(); } }
try { invoke(null); Logger.d(TAG, "ensure resolved"); } catch (Exception ignored) { // we should never make a successful call. } finally { EpicNative.MakeInitializedClassVisibilyInitialized(); } }
public ArtMethod backup() { try { // Before Oreo, it is: java.lang.reflect.AbstractMethod // After Oreo, it is: java.lang.reflect.Executable Class<?> abstractMethodClass = Method.class.getSuperclass();
Objectexecutable=this.getExecutable(); ArtMethod artMethod; if (Build.VERSION.SDK_INT < 23) { Class<?> artMethodClass = Class.forName("java.lang.reflect.ArtMethod"); //Get the original artMethod field FieldartMethodField= abstractMethodClass.getDeclaredField("artMethod"); if (!artMethodField.isAccessible()) { artMethodField.setAccessible(true); } ObjectsrcArtMethod= artMethodField.get(executable);
//Fill the fields to the new method we created for (Field field : artMethodClass.getDeclaredFields()) { if (!field.isAccessible()) { field.setAccessible(true); } field.set(destArtMethod, field.get(srcArtMethod)); } MethodnewMethod= Method.class.getConstructor(artMethodClass).newInstance(destArtMethod); newMethod.setAccessible(true); artMethod = ArtMethod.of(newMethod);
artMethod.setEntryPointFromQuickCompiledCode(getEntryPointFromQuickCompiledCode()); artMethod.setEntryPointFromJni(getEntryPointFromJni()); } else { Constructor<Method> constructor = Method.class.getDeclaredConstructor(); // we can't use constructor.setAccessible(true); because Google does not like it // AccessibleObject.setAccessible(new AccessibleObject[]{constructor}, true); Fieldoverride= AccessibleObject.class.getDeclaredField( Build.VERSION.SDK_INT == Build.VERSION_CODES.M ? "flag" : "override"); override.setAccessible(true); override.set(constructor, true);
Methodm= constructor.newInstance(); m.setAccessible(true); for (Field field : abstractMethodClass.getDeclaredFields()) { field.setAccessible(true); field.set(m, field.get(executable)); } FieldartMethodField= abstractMethodClass.getDeclaredField("artMethod"); artMethodField.setAccessible(true); intartMethodSize= getArtMethodSize(); longmemoryAddress= EpicNative.map(artMethodSize);
byte[] data = EpicNative.get(address, artMethodSize); EpicNative.put(data, memoryAddress); artMethodField.set(m, memoryAddress); // From Android R, getting method address may involve the jni_id_manager which uses // ids mapping instead of directly returning the method address. During resolving the // id->address mapping, it will assume the art method to be from the "methods_" array // in class. However this address may be out of the range of the methods array. Thus // it will cause a crash during using the method offset to resolve method array. artMethod = ArtMethod.of(m, memoryAddress); } artMethod.makePrivate(); artMethod.setAccessible(true); artMethod.origin = this; // save origin method. return artMethod;
// fetch the array, we can not call this when thread suspend(may lead deadlock) jbyte *srcPnt = env->GetByteArrayElements(code, 0); jsize length = env->GetArrayLength(code);
jlong cookie = 0; bool isNougat = api_level >= 24; // #1 挂起所有的线程(>= SDK 24) if (isNougat) { // We do thus things: // 1. modify the code mprotect // 2. modify the code
// Ideal, this two operation must be atomic. Below N, this is safe, because no one // modify the code except ourselves; // But in Android N, When the jit is working, between our step 1 and step 2, // if we modity the mprotect of the code, and planning to write the code, // the jit thread may modify the mprotect of the code meanwhile // we must suspend all thread to ensure the atomic operation.
LOGV("suspend all thread."); cookie = epic_suspendAll(env, jclazz); }
// #2 使用mprotect调整内存权限 jboolean result = epic_munprotect(env, jclazz, jumpToAddress, sizeOfDirectJump); if (result) { // 使用一层跳板覆盖原始artMethod的前4个指令 unsignedchar *destPnt = (unsignedchar *) jumpToAddress; for (int i = 0; i < length; ++i) { destPnt[i] = (unsignedchar) srcPnt[i]; } // 缓存刷新 jboolean ret = epic_cacheflush(env, jclazz, pc, sizeOfBridgeJump); if (!ret) { LOGV("cache flush failed!!"); } } else { // 异常case打印异常日志 LOGV("Writing hook failed: Unable to unprotect memory at %d", jumpToAddress); } // 恢复执行 if (cookie != 0) { LOGV("resume all thread."); epic_resumeAll(env, jclazz, cookie); }
// Entry64.java //region ---------------bridge--------------- privatestaticvoidvoidBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticbooleanbooleanBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Boolean) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticbytebyteBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Byte) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticshortshortBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Short) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticcharcharBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Character) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticintintBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Integer) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticlonglongBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Long) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticfloatfloatBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Float) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticdoubledoubleBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Double) referenceBridge(r1, self, struct, x4, x5, x6, x7); } //endregion
// reset to last result (ignoring what the unexpectedly exiting callback did) if (lastThrowable == null) param.setResult(lastResult); else param.setThrowable(lastThrowable); } } while (--afterIdx >= 0);
// 异常处理 & 返回结果。 if (param.hasThrowable()) { finalThrowablethrowable= param.getThrowable(); if (throwable instanceof IllegalAccessException || throwable instanceof InvocationTargetException || throwable instanceof InstantiationException) { // reflect exception, get the origin cause finalThrowablecause= throwable.getCause();
// We can not change the exception flow of origin call, rethrow // Logger.e(TAG, "origin call throw exception (not a real crash, just record for debug):", cause); DexposedBridge.<RuntimeException>throwNoCheck(param.getThrowable().getCause(), null); returnnull; //never reach. } else { // the exception cause by epic self, just log. Logger.e(TAG, "epic cause exception in call bridge!!", throwable); } returnnull; // never reached. } else { finalObjectresult= param.getResult(); //Logger.d(TAG, "return :" + result); return result; } }
uint32_tGetCodeSize()const{ // ART compiled method are prefixed with header, but we can also easily // accidentally use a function pointer to one of the stubs/trampolines. // We prefix those with 0xFF in the aseembly so that we can do DCHECKs. CHECK_NE(code_size_, 0xFFFFFFFF) << code_size_; return code_size_ & kCodeSizeMask; } // ......
// The offset in bytes from the start of the vmap table to the end of the header. uint32_t vmap_table_offset_ = 0u; // The code size in bytes. The highest bit is used to signify if the compiled // code with the method header has should_deoptimize flag. uint32_t code_size_ = 0u; // The actual code. uint8_t code_[0]; };
privatestaticvoidvoidBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticbooleanbooleanBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Boolean) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticbytebyteBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Byte) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticshortshortBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Short) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticcharcharBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Character) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticintintBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Integer) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticlonglongBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Long) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticfloatfloatBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Float) referenceBridge(r1, self, struct, x4, x5, x6, x7); }
privatestaticdoubledoubleBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { return (Double) referenceBridge(r1, self, struct, x4, x5, x6, x7); }