import android.app.ActivityManager;
import android.content.Context;
import android.text.TextUtils;
import com.zhangyue.iReader.app.APP;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
public final class LoadedApkHuaWei {
private static final HuaWeiVerifier IMPL;
static {
final int version = android.os.Build.VERSION.SDK_INT;
if (version >= 28) {
IMPL = new V28VerifierImpl();
} else if (version >= 26) {
IMPL = new V26VerifierImpl();
} else if (version >= 24) {
IMPL = new V24VerifierImpl();
} else {
IMPL = new BaseVerifierImpl();
}
}
public static void hookHuaWeiVerifier() {
hookHuaWeiVerifier(null);
}
public static void hookHuaWeiVerifier(TooManyBroadcastCallback callback) {
try {
if (null != APP.getAppBaseContext()) {
IMPL.verifier(APP.getAppBaseContext(), callback);
} else {
LOG.W(LoadedApkHuaWei.class.getSimpleName(), "application context is null !!!");
}
} catch (Throwable ignored) {
}
}
private static class V28VerifierImpl extends V26VerifierImpl {
private static final int MAX_BROADCAST_COUNT = 1000;
private static final String REGISTER_RECEIVER = "registerReceiver";
private static final String UNREGISTER_RECEIVER = "unregisterReceiver";
private static final String ACTIVITY_MANAGER = "android.app.IActivityManager";
@Override
public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
final boolean verified = super.verifier(baseContext, callback);
if (verified) {
hookActivityManagerService(baseContext.getClassLoader(), callback);
}
return verified;
}
private void hookActivityManagerService(ClassLoader loader, TooManyBroadcastCallback callback) {
try {
Object IActivityManagerSingletonObject = readStaticField(ActivityManager.class.getName(), "IActivityManagerSingleton");
if (null != IActivityManagerSingletonObject) {
Object IActivityManagerObject = readField(IActivityManagerSingletonObject, "mInstance");
if (null != IActivityManagerObject) {
AmsInvocationHandler handler = new AmsInvocationHandler(IActivityManagerObject, callback);
Class<?> IActivityManagerClass = Class.forName(ACTIVITY_MANAGER);
Object proxy = Proxy.newProxyInstance(loader, new Class<?>[]{IActivityManagerClass}, handler);
writeField(IActivityManagerSingletonObject, "mInstance", proxy);
}
}
} catch (Throwable ignored) {
}
}
private static class AmsInvocationHandler implements InvocationHandler {
private Object mIActivityManagerObject;
private TooManyBroadcastCallback mCallback;
private volatile int mCurrentBroadcastCount;
private AmsInvocationHandler(Object mIActivityManagerObject, TooManyBroadcastCallback callback) {
this.mCallback = callback;
this.mIActivityManagerObject = mIActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final String methodName = method.getName();
if (TextUtils.equals(REGISTER_RECEIVER, methodName)) {
if (mCurrentBroadcastCount >= MAX_BROADCAST_COUNT) {
if (null != mCallback) {
mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
}
return null;
}
mCurrentBroadcastCount++;
if (null != mCallback) {
mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
}
} else if (TextUtils.equals(UNREGISTER_RECEIVER, methodName)) {
mCurrentBroadcastCount--;
mCurrentBroadcastCount = mCurrentBroadcastCount < 0 ? 0 : mCurrentBroadcastCount;
if (null != mCallback) {
mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
}
}
try {
return method.invoke(mIActivityManagerObject, args);
} catch (Exception e) {
return null;
}
}
}
}
private static class V26VerifierImpl extends BaseVerifierImpl {
private static final String WHITE_LIST = "mWhiteListMap";
@Override
public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
Object whiteListMapObject = getWhiteListObject(baseContext, WHITE_LIST);
if (whiteListMapObject instanceof Map) {
Map whiteListMap = (Map) whiteListMapObject;
List whiteList = (List) whiteListMap.get(0);
if (null == whiteList) {
whiteList = new ArrayList<>();
whiteListMap.put(0, whiteList);
}
whiteList.add(baseContext.getPackageName());
return true;
}
return false;
}
}
private static class V24VerifierImpl extends BaseVerifierImpl {
private static final String WHITE_LIST = "mWhiteList";
@Override
public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
Object whiteListObject = getWhiteListObject(baseContext, WHITE_LIST);
if (whiteListObject instanceof List) {
List whiteList = (List) whiteListObject;
whiteList.add(baseContext.getPackageName());
return true;
}
return false;
}
}
private static class BaseVerifierImpl implements HuaWeiVerifier {
private static final String WHITE_LIST = "mWhiteList";
@Override
public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
Object receiverResourceObject = getReceiverResourceObject(baseContext);
Object whiteListObject = getWhiteListObject(receiverResourceObject, WHITE_LIST);
if (whiteListObject instanceof String[]) {
String[] whiteList = (String[]) whiteListObject;
List<String> newWhiteList = new ArrayList<>();
newWhiteList.add(baseContext.getPackageName());
Collections.addAll(newWhiteList, whiteList);
writeField(receiverResourceObject, WHITE_LIST, newWhiteList.toArray(new String[newWhiteList.size()]));
return true;
} else {
if (null != receiverResourceObject) {
writeField(receiverResourceObject, "mResourceConfig", null);
}
}
return false;
}
Object getWhiteListObject(Context baseContext, String whiteList) {
return getWhiteListObject(getReceiverResourceObject(baseContext), whiteList);
}
private Object getWhiteListObject(Object receiverResourceObject, String whiteList) {
try {
if (null != receiverResourceObject) {
return readField(receiverResourceObject, whiteList);
}
} catch (Throwable ignored) {
}
return null;
}
private Object getReceiverResourceObject(Context baseContext) {
try {
Field receiverResourceField = getDeclaredField("android.app.LoadedApk", "mReceiverResource", true);
if (null != receiverResourceField) {
Field packageInfoField = getDeclaredField("android.app.ContextImpl", "mPackageInfo", true);
if (null != packageInfoField) {
Object packageInfoObject = readField(packageInfoField, baseContext);
if (null != packageInfoObject) {
return readField(receiverResourceField, packageInfoObject, true);
}
}
}
} catch (Throwable ignored) {
}
return null;
}
}
private interface HuaWeiVerifier {
boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable;
}
public interface TooManyBroadcastCallback {
void tooManyBroadcast(int registedCount, int totalCount);
}
private static final Map<String, Field> sFieldCache = new HashMap<String, Field>();
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
private static String getKey(Class<?> cls, String fieldName) {
StringBuilder sb = new StringBuilder();
sb.append(cls.toString()).append("#").append(fieldName);
return sb.toString();
}
private static Field getField(Class<?> cls, String fieldName, final boolean forceAccess) {
isTrue(cls != null, "The class must not be null");
isTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
String key = getKey(cls, fieldName);
Field cachedField;
synchronized (sFieldCache) {
cachedField = sFieldCache.get(key);
}
if (cachedField != null) {
if (forceAccess && !cachedField.isAccessible()) {
cachedField.setAccessible(true);
}
return cachedField;
}
for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
try {
final Field field = acls.getDeclaredField(fieldName);
if (!Modifier.isPublic(field.getModifiers())) {
if (forceAccess) {
field.setAccessible(true);
} else {
continue;
}
}
synchronized (sFieldCache) {
sFieldCache.put(key, field);
}
return field;
} catch (final NoSuchFieldException ex) {
}
}
Field match = null;
for (final Class<?> class1 : getAllInterfaces(cls)) {
try {
final Field test = class1.getField(fieldName);
isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
+ "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
match = test;
} catch (final NoSuchFieldException ex) {
}
}
synchronized (sFieldCache) {
sFieldCache.put(key, match);
}
return match;
}
private static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
isTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
setAccessibleWorkaround(field);
}
return field.get(target);
}
private static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
throws IllegalAccessException {
isTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
setAccessibleWorkaround(field);
}
field.set(target, value);
}
private static Object readField(final Field field, final Object target) throws IllegalAccessException {
return readField(field, target, true);
}
private static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
return readField(field, target, false);
}
private static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
writeField(target, fieldName, value, true);
}
private static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess) throws IllegalAccessException {
isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
writeField(field, target, value, forceAccess);
}
private static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
isTrue(field != null, "The field must not be null");
isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
return readField(field, (Object) null, forceAccess);
}
private static Object readStaticField(final String cls, final String fieldName) throws IllegalAccessException {
try {
return readStaticField(Class.forName(cls), fieldName);
} catch (Throwable ignored) {
print(ignored);
}
return null;
}
private static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
final Field field = getField(cls, fieldName, true);
isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
return readStaticField(field, true);
}
private static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
isTrue(field != null, "The field must not be null");
isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
field.getName());
writeField(field, (Object) null, value, forceAccess);
}
private static Field getDeclaredField(final String cls, final String fieldName, final boolean forceAccess) {
try {
return getDeclaredField(Class.forName(cls), fieldName, forceAccess);
} catch (Throwable ignore) {
print(ignore);
}
return null;
}
private static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
isTrue(cls != null, "The class must not be null");
isTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
try {
final Field field = cls.getDeclaredField(fieldName);
if (!isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (final NoSuchFieldException e) {
}
return null;
}
private static boolean isPackageAccess(final int modifiers) {
return (modifiers & ACCESS_TEST) == 0;
}
static boolean isAccessible(final Member m) {
return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
}
static boolean setAccessibleWorkaround(final AccessibleObject o) {
if (o == null || o.isAccessible()) {
return false;
}
final Member m = (Member) o;
if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
try {
o.setAccessible(true);
return true;
} catch (final SecurityException e) {
}
}
return false;
}
public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
if (cls == null) {
return null;
}
final LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();
getAllInterfaces(cls, interfacesFound);
return new ArrayList<Class<?>>(interfacesFound);
}
private static void getAllInterfaces(Class<?> cls, final HashSet<Class<?>> interfacesFound) {
while (cls != null) {
final Class<?>[] interfaces = cls.getInterfaces();
for (final Class<?> i : interfaces) {
if (interfacesFound.add(i)) {
getAllInterfaces(i, interfacesFound);
}
}
cls = cls.getSuperclass();
}
}
public static void print(Throwable throwable) {
if (null != throwable) {
throwable.printStackTrace();
}
}
private static void isTrue(final boolean expression, final String message, final Object... values) {
if (!expression) {
throw new IllegalArgumentException(String.format(message, values));
}
}
}