标签:ppc 成员 null debug 应用 tor fill component 自己的
静态代理模式就是我们常说的代理设计模式,我们采用一个代理类调用原有的方法,且对产生的结果进行控制;举个例子:我们现在在玩一款网络游戏,需要打怪升级;太累就找个代理吧,一觉醒来就会发现我们已经当上CEO,迎娶白富美,天下第一了!
本来我们只能打怪,打怪…,但经过代理类增强,我们不仅可以打怪,还可以升级拿装备。就这样子了!
上代码:
* 同一功能接口
public interface PlayNetGame {
String beatMonster();
}
public class PlayNetGameImpl implements PlayNetGame {
@Override
public String beatMonster() {
return "我们要去打怪兽了";
}
public class PlayNetGameProxy implements PlayNetGame {
private PlayNetGame mPlayNetGame;
public PlayNetGameProxy(PlayNetGame playNetGame){
mPlayNetGame = playNetGame;
}
@Override
public String beatMonster() {
String weapon = "找到屠龙刀";
String beatMonster = mPlayNetGame.beatMonster();
String upGrade = "我升级了";
String wuDi = "当上CEO,迎娶白富美----无敌了";
return weapon+"\n"+"\r"+beatMonster+"\n"+"\r"+upGrade+"\n"+"\r"+wuDi;
}
}
public class MainActivity extends AppCompatActivity {
private TextView mTvPlay;
private TextView mTvProxy;
private Button mBtPlay;
private Button mBtProxy;
private PlayNetGame mPlayNetGame;
private PlayNetGame mPlayNetGameProxy;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initData() {
mPlayNetGame = new PlayNetGameImpl();
mPlayNetGameProxy = new PlayNetGameProxy(mPlayNetGame);
mBtPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//未被代理时结果
mTvPlay.setText(mPlayNetGame.beatMonster());
}
});
mBtProxy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//代理以后的结果
mTvProxy.setText(mPlayNetGameProxy.beatMonster());
}
});
}
private void initView() {
mTvPlay = (TextView) findViewById(R.id.tv_play);
mTvProxy = (TextView) findViewById(R.id.tv_proxy);
mBtPlay = (Button) findViewById(R.id.bt_play);
mBtProxy = (Button) findViewById(R.id.bt_proxy);
}
}
public class DynamicProxy implements InvocationHandler {
private Object mObject;
public DynamicProxy(Object object) {
mObject = object;
}
/**
* 因为我要增强的方法会返回String字符串,这里Return一个增强的字符串
* @param proxy proxy: 指代我们所代理的那个真实对象
* @param method 指代的是我们所要调用真实对象的某个方法的Method对象
* @param args args: 指代的是调用真实对象某个方法时接受的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String weapon = "找到屠龙刀";
String ing=null;
if(method.getName().equals("beatMonster")){
ing = "遇到怪物";
method.invoke(mObject, args);
}else {
method.invoke(mObject, args);
}
String upGrade = "我升级了";
String wuDi = "当上CEO,迎娶白富美----无敌了";
return weapon+"\n"+"\r"+ing+"\n"+"\r"+ method.invoke(mObject, args)+"\n"+"\r"+upGrade+"\n"+"\r"+wuDi;
}
}
mPlayNetGame = new PlayNetGameImpl();
// mPlayNetGameProxy = new PlayNetGameProxy(mPlayNetGame);
DynamicProxy dynamicProxy = new DynamicProxy(mPlayNetGame);
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
final PlayNetGame playNetGame = (PlayNetGame) Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(),
mPlayNetGame.getClass().getInterfaces(), dynamicProxy);
其实所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
代理对象就是把被代理对象包装一层,在其内部做一些额外的工作,比如用户需要上facebook,而普通网络无法直接访问,网络代理帮助用户先FQ,然后再访问facebook。这就是代理的作用了。
Activity生命周期管理
Activity,Service等组件是有生命周期的,它们统一由系统服务AMS(ActivityManagerService)管理;
Activity启动过程
通信过程原理:(1)App进程会委托AMS进程完成Activity生命周期的管理以及任务栈的管理;这个通信过程AMS是Server端,App进程通过持有AMS的client代理ActivityManagerNative完成通信过程;
(2)AMS进程完成生命周期管理以及任务栈管理后,会把控制权交给App进程,让App进程完成Activity类对象的创建,以及生命周期回调;这个通信过程也是通过Binder完成的,App进程变为server端,Binder对象存在于ActivityThread的内部类ApplicationThread中;AMS所在client通过持有IApplicationThread的代理对象完成对于App进程的通信。
完成启动:handler类里直接调用了ActivityThread的handleLaunchActivity方法,这个方法做了两件很重要的事情:
(1)使用ClassLoader加载并通过反射创建Activity对象
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
(2)如果Application还没有创建,那么创建Application对象并回调相应的生命周期方法
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// ... 省略
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
盗图总结-0-
<!-- 替身Activity, 用来欺骗AMS -->
<activity android:name=".StubActivity"/>
startActivity(new Intent(MainActivity.this, TargetActivity.class));
ps1: 这个TargetActivity继承的是Activity,不是AppCompatActivity,因为startActivity()是调用的Activity内的方法
ps2:TargetActivity和StubActivity这俩个类文件要放在根包下,不要放到分包里,若把他们放在根目录下新建的包内(会替换不了)
经过debug调试,替代类包名路径和目标类包名路径不同,会替换失败?但是我改成下图就会成功
3.使用替身Activity绕过AMS:由于AMS进程会对Activity做显式声明验证,因此在
启动Activity的控制权转移到AMS进程之前,我们需要想办法临时把TargetActivity替换成替身StubActivity;
/**
* 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity"
*/
public static void hookActivityManagerNative(){
try {
Class<?> mActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = mActivityManagerNative.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> mSingletion = Class.forName("android.util.Singleton");
Field mInstanceField = mSingletion.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象
Object mIActivityManager = mInstanceField.get(gDefault);
// 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> iActivityManagerInterfance = Class.forName("android.app.IActivityManager");
Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{iActivityManagerInterfance}, new IActivityManagerHandler(mIActivityManager));
mInstanceField.set(gDefault,proxyInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
动态代理类IActivityManagerHandler
class IActivityManagerHandler implements InvocationHandler {
private static final String TAG = "IActivityManagerHandler";
private Object mBase;
public IActivityManagerHandler(Object base) {
mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 只拦截这个方法
// 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
// API 23:
// public final Activity startActivityNow(Activity parent, String id,
// Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
// Activity.NonConfigurationInstances lastNonConfigurationInstances) {
// 找到参数里面的第一个Intent 对象
if ("startActivity".equals(method.getName())) {
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
Intent newIntent = new Intent();
// 替身Activity的包名, 也就是我们自己的包名
String stubPackage = "com.example.happyghost.proxystudy";
// 这里我们把启动的Activity临时替换为 StubActivity
ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
newIntent.setComponent(componentName);
// 把我们原始要启动的TargetActivity先存起来
newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
// 替换掉Intent, 达到欺骗AMS的目的
args[index] = newIntent;
Log.d(TAG, "hook success");
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
(1)Handler是如何处理接收到的Message的
1. 如果传递的Message本身就有callback,那么直接使用Message对象的callback方法;
2. 如果Handler类的成员变量mCallback存在,那么首先执行这个mCallback回调;
3. 如果mCallback的回调返回true,那么表示消息已经成功处理;直接结束。
4. 如果mCallback的回调返回false,那么表示消息没有处理完毕,会继续使用Handler类的handleMessage方法处理消息。
(2)Handler.Callback是一个接口,我们可以使用动态代理或者普通代理完成Hook,这里我们使用普通的静态代理方式;创建一个自定义的Callback类:
class ActivityThreadHandlerCallback implements Handler.Callback {
Handler mBase;
public ActivityThreadHandlerCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
// 根据源码:
// 这个对象是 ActivityClientRecord 类型
// 我们修改它的intent字段为我们原来保存的即可.
/* switch (msg.what) {
/ case LAUNCH_ACTIVITY: {
/ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
/ final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
/
/ r.packageInfo = getPackageInfoNoCheck(
/ r.activityInfo.applicationInfo, r.compatInfo);
/ handleLaunchActivity(r, null);
*/
try {
// 把替身恢复成真身
Field intent = obj.getClass().getDeclaredField("intent");
intent.setAccessible(true);
Intent raw = (Intent) intent.get(obj);
Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT);
raw.setComponent(target.getComponent());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity
* <p/>
* 不然就真的启动替身了, 狸猫换太子...
* <p/>
* 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成
* H 会完成这个消息转发; 最终调用它的callback
*/
public static void hookActivityThreadHandler() throws Exception {
// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);
// 设置它的回调, 根据源码:
// 我们自己给他设置一个回调,就会替代之前的回调;
// public void dispatchMessage(Message msg) {
// if (msg.callback != null) {
// handleCallback(msg);
// } else {
// if (mCallback != null) {
// if (mCallback.handleMessage(msg)) {
// return;
// }
// }
// handleMessage(msg);
// }
// }
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH));
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
AMSHookHelper.hookActivityManagerNative();
AMSHookHelper.hookActivityThreadHandler();
} catch (Throwable throwable) {
throw new RuntimeException("hook failed", throwable);
}
}
将插件的dex或者apk文件告诉『合适的』DexClassLoader,借助它完成插件类的加载
Activity的创建过程的代码
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
ClassLoader对象的获取
加载插件类的第一种方案:Hook掉ClassLoader,自己操刀
在获取LoadedApk的过程中使用了一份缓存数据;这个缓存数据是一个Map,从包名到LoadedApk的一个映射。正常情况下,我们的插件肯定不会存在于这个对象里面;但是如果我们手动把我们插件的信息添加到里面呢?系统在查找缓存的过程中,会直接找到缓存!进而使用我们添加进去的LoadedApk的ClassLoader来加载这个特定的Activity类!这样我们就能接管我们自己插件类的加载过程了!
这个缓存对象mPackages存在于ActivityThread类中
// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 获取到 mPackages 这个静态成员变量, 这里缓存了dex包的信息
Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
Map mPackages = (Map) mPackagesField.get(currentActivityThread);
我们把插件的信息塞进这个map里面,以便系统在查找的时候能找到插件缓存。填充这个Map我们出了需要包名之外,还需要一个LoadedApk对象(可以直接反射调用它的构造函数直接创建出需要的对象,不严谨)
(1)我们在这里选择使用 **getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo)** 获取LoadedApk信息。为了调用这个函数,我们需要构造两个参数。其一是ApplicationInfo,其二是CompatibilityInfo;第二个参数顾名思义,代表这个App的兼容性信息,比如targetSDK版本等等,这里我们只需要提取出app的信息,因此直接使用默认的兼容性即可;在CompatibilityInfo类里面有一个公有字段DEFAULT_COMPATIBILITY_INFO代表默认兼容性信息
(2) 获取这个ApplicationInfo信息,这个类就是AndroidManifest.xml里面的 这个标签下面的信息;这个AndroidManifest.xml无疑是一个标准的xml文件,因此我们完全可以自己使用parse来解析这个信息。
(3) 使用PackageParser类的 generateApplicationInfo(Package p, int flags, PackageUserState state) 方法获取ApplicationInfo信息,反射调用其
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");
// 首先拿到我们得终极目标: generateApplicationInfo方法
// API 23 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// public static ApplicationInfo generateApplicationInfo(Package p, int flags,
// PackageUserState state) {
// 其他Android版本不保证也是如此.
Class<?> packageParser$PackageClass = Class.forName("android.content.pm.PackageParser$Package");
Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo",
packageParser$PackageClass,
int.class,
packageUserStateClass);
要成功调用这个方法,还需要三个参数;因此接下来我们需要一步一步构建调用此函数的参数信息。
// 首先, 我们得创建出一个Package对象出来供这个方法调用
// 而这个需要得对象可以通过 android.content.pm.PackageParser#parsePackage 这个方法返回得 Package对象得字段获取得到
// 创建出一个PackageParser对象供使用
Object packageParser = packageParserClass.newInstance();
// 调用 PackageParser.parsePackage 解析apk的信息
Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
// 实际上是一个 android.content.pm.PackageParser.Package 对象
Object packageObj = parsePackageMethod.invoke(packageParser, apkFile, 0);
这样,我们就得到了generateApplicationInfo的第一个参数;第二个参数是解析包使用的flag,我们直接选择解析全部信息,也就是0;
// 第三个参数 mDefaultPackageUserState 我们直接使用默认构造函数构造一个出来即可
Object defaultPackageUserState = packageUserStateClass.newInstance();
// 万事具备!!!!!!!!!!!!!!
ApplicationInfo applicationInfo = (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser,
packageObj, 0, defaultPackageUserState);
String apkPath = apkFile.getPath();
applicationInfo.sourceDir = apkPath;
applicationInfo.publicSourceDir = apkPath;
// android.content.res.CompatibilityInfo
Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod("getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);
Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
defaultCompatibilityInfoField.setAccessible(true);
Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
ApplicationInfo applicationInfo = generateApplicationInfo(apkFile);
Object loadedApk = getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);
String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath();
String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath();
ClassLoader classLoader = new CustomClassLoader(apkFile.getPath(), odexPath, libDir, ClassLoader.getSystemClassLoader());
Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
mClassLoaderField.set(loadedApk, classLoader);
// 由于是弱引用, 因此我们必须在某个地方存一份, 不然容易被GC; 那么就前功尽弃了.
sLoadedApk.put(applicationInfo.packageName, loadedApk);
WeakReference weakReference = new WeakReference(loadedApk);
mPackages.put(applicationInfo.packageName, weakReference);
private static void hookPackageManager() throws Exception {
// 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
// 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查.
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 获取ActivityThread里面原始的 sPackageManager
Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
sPackageManagerField.setAccessible(true);
Object sPackageManager = sPackageManagerField.get(currentActivityThread);
// 准备好代理对象, 用来替换原始的对象
Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(),
new Class<?>[] { iPackageManagerInterface },
new IPackageManagerHookHandler(sPackageManager));
// 1. 替换掉ActivityThread里面的 sPackageManager 字段
sPackageManagerField.set(currentActivityThread, proxy);
}
加载插件类的第二种方案:委托系统,让系统帮忙加载
public static void patchClassLoader(ClassLoader cl, File apkFile, File optDexFile)
throws IllegalAccessException, NoSuchMethodException, IOException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// 获取 BaseDexClassLoader : pathList
Field pathListField = DexClassLoader.class.getSuperclass().getDeclaredField("pathList");
pathListField.setAccessible(true);
Object pathListObj = pathListField.get(cl);
// 获取 PathList: Element[] dexElements
Field dexElementArray = pathListObj.getClass().getDeclaredField("dexElements");
dexElementArray.setAccessible(true);
Object[] dexElements = (Object[]) dexElementArray.get(pathListObj);
// Element 类型
Class<?> elementClass = dexElements.getClass().getComponentType();
// 创建一个数组, 用来替换原始的数组
Object[] newElements = (Object[]) Array.newInstance(elementClass, dexElements.length + 1);
// 构造插件Element(File file, boolean isDirectory, File zip, DexFile dexFile) 这个构造函数
Constructor<?> constructor = elementClass.getConstructor(File.class, boolean.class, File.class, DexFile.class);
Object o = constructor.newInstance(apkFile, false, apkFile, DexFile.loadDex(apkFile.getCanonicalPath(), optDexFile.getAbsolutePath(), 0));
Object[] toAddElementArray = new Object[] { o };
// 把原始的elements复制进去
System.arraycopy(dexElements, 0, newElements, 0, dexElements.length);
// 插件的那个element复制进去
System.arraycopy(toAddElementArray, 0, newElements, dexElements.length, toAddElementArray.length);
// 替换
dexElementArray.set(pathListObj, newElements);
}
Android知识体系梳理笔记三:动态代理模式---插件加载机制学习笔记
标签:ppc 成员 null debug 应用 tor fill component 自己的
原文地址:http://www.cnblogs.com/xiaoliu500/p/7348412.html