/**
* OpenAtlasForAndroid Project
* The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author BunnyBlue
**/
package com.openatlas.hack;
import android.app.Application;
import android.app.Instrumentation;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import com.openatlas.hack.Hack.HackDeclaration.HackAssertionException;
import com.openatlas.runtime.DelegateClassLoader;
import com.openatlas.runtime.DelegateResources;
import com.openatlas.runtime.RuntimeVariables;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
public class AndroidHack {
private static Object _mLoadedApk;
private static Object _sActivityThread;
static final class HandlerHack implements Callback {
final Object activityThread;
final Handler handler;
HandlerHack(Handler handler, Object obj) {
this.handler = handler;
this.activityThread = obj;
}
@Override
public boolean handleMessage(Message message) {
try {
AndroidHack.ensureLoadedApk();
this.handler.handleMessage(message);
AndroidHack.ensureLoadedApk();
} catch (Throwable th) {
Throwable th2 = th;
th.printStackTrace();
RuntimeException runtimeException;
if ((th2 instanceof ClassNotFoundException)
|| th2.toString().contains("ClassNotFoundException")) {
if (message.what != 113) {
Object loadedApk = AndroidHack.getLoadedApk(
RuntimeVariables.androidApplication,
this.activityThread,
RuntimeVariables.androidApplication
.getPackageName());
if (loadedApk == null) {
runtimeException = new RuntimeException(
"loadedapk is null");
} else {
ClassLoader classLoader = OpenAtlasHacks.LoadedApk_mClassLoader
.get(loadedApk);
if (classLoader instanceof DelegateClassLoader) {
runtimeException = new RuntimeException(
"From Atlas:classNotFound ---", th2);
} else {
RuntimeException runtimeException2 = new RuntimeException(
"wrong classloader in loadedapk---"
+ classLoader.getClass()
.getName(), th2);
}
}
}
} else if ((th2 instanceof ClassCastException)
|| th2.toString().contains("ClassCastException")) {
Process.killProcess(Process.myPid());
} else {
runtimeException = new RuntimeException(th2);
}
}
return true;
}
}
static class ActvityThreadGetter implements Runnable {
ActvityThreadGetter() {
}
@Override
public void run() {
try {
AndroidHack._sActivityThread = OpenAtlasHacks.ActivityThread_currentActivityThread
.invoke(OpenAtlasHacks.ActivityThread.getmClass()
);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (OpenAtlasHacks.ActivityThread_currentActivityThread) {
OpenAtlasHacks.ActivityThread_currentActivityThread.notify();
}
}
}
static {
_sActivityThread = null;
_mLoadedApk = null;
}
public static Object getActivityThread() throws Exception {
if (_sActivityThread == null) {
if (Thread.currentThread().getId() == Looper.getMainLooper()
.getThread().getId()) {
_sActivityThread = OpenAtlasHacks.ActivityThread_currentActivityThread
.invoke(null);
} else {
Handler handler = new Handler(Looper.getMainLooper());
synchronized (OpenAtlasHacks.ActivityThread_currentActivityThread) {
handler.post(new ActvityThreadGetter());
OpenAtlasHacks.ActivityThread_currentActivityThread.wait();
}
}
}
return _sActivityThread;
}
public static Handler hackH() throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
try {
Handler handler = (Handler) OpenAtlasHacks.ActivityThread
.field("mH")
.ofType(Hack.into("android.app.ActivityThread$H")
.getmClass()).get(activityThread);
Field declaredField = Handler.class.getDeclaredField("mCallback");
declaredField.setAccessible(true);
declaredField.set(handler, new HandlerHack(handler,
activityThread));
} catch (HackAssertionException e) {
e.printStackTrace();
}
return null;
}
public static void ensureLoadedApk() throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
Object loadedApk = getLoadedApk(RuntimeVariables.androidApplication,
activityThread,
RuntimeVariables.androidApplication.getPackageName());
if (loadedApk == null) {
loadedApk = createNewLoadedApk(RuntimeVariables.androidApplication,
activityThread);
if (loadedApk == null) {
throw new RuntimeException("can't create loadedApk");
}
}
activityThread = loadedApk;
if (!((OpenAtlasHacks.LoadedApk_mClassLoader
.get(activityThread)) instanceof DelegateClassLoader)) {
OpenAtlasHacks.LoadedApk_mClassLoader.set(activityThread,
RuntimeVariables.delegateClassLoader);
OpenAtlasHacks.LoadedApk_mResources.set(activityThread,
RuntimeVariables.delegateResources);
}
}
public static Object getLoadedApk(Application application, Object obj,
String str) {
WeakReference weakReference = (WeakReference) ((Map) OpenAtlasHacks.ActivityThread_mPackages
.get(obj)).get(str);
if (weakReference == null || weakReference.get() == null) {
return null;
}
_mLoadedApk = weakReference.get();
return _mLoadedApk;
}
public static Object createNewLoadedApk(Application application, Object obj) {
try {
Method declaredMethod;
ApplicationInfo applicationInfo = application.getPackageManager()
.getApplicationInfo(application.getPackageName(), 1152);
application.getPackageManager();
Resources resources = application.getResources();
if (resources instanceof DelegateResources) {
declaredMethod = resources
.getClass()
.getSuperclass()
.getDeclaredMethod("getCompatibilityInfo");
} else {
declaredMethod = resources.getClass().getDeclaredMethod(
"getCompatibilityInfo");
}
declaredMethod.setAccessible(true);
Class cls = Class.forName("android.content.res.CompatibilityInfo");
Object invoke = declaredMethod.invoke(application.getResources()
);
Method declaredMethod2 = OpenAtlasHacks.ActivityThread.getmClass()
.getDeclaredMethod("getPackageInfoNoCheck",
ApplicationInfo.class, cls);
declaredMethod2.setAccessible(true);
invoke = declaredMethod2.invoke(obj, applicationInfo, invoke);
_mLoadedApk = invoke;
return invoke;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void injectClassLoader(String str, ClassLoader classLoader)
throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
Object loadedApk = getLoadedApk(RuntimeVariables.androidApplication,
activityThread, str);
if (loadedApk == null) {
loadedApk = createNewLoadedApk(RuntimeVariables.androidApplication,
activityThread);
}
if (loadedApk == null) {
throw new Exception("Failed to get ActivityThread.mLoadedApk");
}
OpenAtlasHacks.LoadedApk_mClassLoader.set(loadedApk, classLoader);
}
public static void injectApplication(String str, Application application)
throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
Object loadedApk = getLoadedApk(application, activityThread,
application.getPackageName());
if (loadedApk == null) {
throw new Exception("Failed to get ActivityThread.mLoadedApk");
}
OpenAtlasHacks.LoadedApk_mApplication.set(loadedApk, application);
OpenAtlasHacks.ActivityThread_mInitialApplication.set(activityThread,
application);
}
public static void injectResources(Application application,
Resources resources) throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
Object loadedApk = getLoadedApk(application, activityThread,
application.getPackageName());
if (loadedApk == null) {
activityThread = createNewLoadedApk(application, activityThread);
if (activityThread == null) {
throw new RuntimeException(
"Failed to get ActivityThread.mLoadedApk");
}
if (!((OpenAtlasHacks.LoadedApk_mClassLoader
.get(activityThread)) instanceof DelegateClassLoader)) {
OpenAtlasHacks.LoadedApk_mClassLoader.set(activityThread,
RuntimeVariables.delegateClassLoader);
}
loadedApk = activityThread;
}
OpenAtlasHacks.LoadedApk_mResources.set(loadedApk, resources);
OpenAtlasHacks.ContextImpl_mResources.set(application.getBaseContext(),
resources);
OpenAtlasHacks.ContextImpl_mTheme.set(application.getBaseContext(), null);
}
public static Instrumentation getInstrumentation() throws Exception {
Object activityThread = getActivityThread();
if (activityThread != null) {
return OpenAtlasHacks.ActivityThread_mInstrumentation
.get(activityThread);
}
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
public static void injectInstrumentationHook(Instrumentation instrumentation)
throws Exception {
Object activityThread = getActivityThread();
if (activityThread == null) {
throw new Exception(
"Failed to get ActivityThread.sCurrentActivityThread");
}
OpenAtlasHacks.ActivityThread_mInstrumentation.set(activityThread,
instrumentation);
}
public static void injectContextHook(ContextWrapper contextWrapper,
ContextWrapper contextWrapper2) {
OpenAtlasHacks.ContextWrapper_mBase.set(contextWrapper, contextWrapper2);
}
}