基于multidex的代码注入方案和加固方案

这里说的multidex是指apk包含多个dex。实现这个的技术,除了使用support提供的multidex库,也可以自己开发,实现逻辑不复杂。同时,最新安卓系统已支持自动加载或转换多个dex。在不考虑兼容旧版情况下,甚至可以不用手动加载。当然,若dex需要经过解密后才能加载的话,就得自己实现这个解密和加载过程。

一般我们修改别人的dex,会先转成jar或smali,然后对里面内容进行修改(字节码操作或源码操作),最后重新编译成dex。 但是,这种修改方案有一个不好的地方,就是直接改变了别人代码(我还是很介意代码倾入逻辑,即使是改软件)。嘛,若只是修改一两个dex,这种方案还是可以。但是,若dex转换失败或存在错误(安卓支持的指令并不完全跟java一样,需要兼容处理),重新编译势必存在问题。同时,转换后的代码,源码操作需要再进行转换,失败概率更高(语法很多是错的)。字节码操作好点,但是阅读困难。所以,一些简单的调整,我会选择字节码操作,很少转成源码后再修改。而复杂的调整,基本不会选择这种修改方案,除非有必要,不得不深入分析。

multidex除了把应用代码分包外,同时也用于局部功能升级。 从其技术上说,是利用了虚拟机动态类加载的机制。换句话说,虚拟机在没有使用某个类时,是不会加载相关库。这时,我们就可以在虚拟机加载前,替换这些库,实现代码更新。对于已经加载的类,可以利用加载器的查询逻辑,进行替换,从而实现热更新的目的。我们经常使用的调试就是属于这种热更新。
多个库出现相同类不会有冲突吗?我们在编译源码时,若库出现重复类,打包会失败,这是dx转换问题。但是,对于虚拟机而言,加载的库有没有重复类不是它关心的问题,也不会出现问题(除非库不兼容),它只关心这个库里面是否有需要的类。 这个类一旦加载,在不卸载库的情况下,其它库包含同样的类都不会被使用,即采用先到先用原则。

基于以上multidex的说明,我们可以知道,若我们想要改变已有dex的功能,可以优先加载修改过后的类,继而实现覆盖原有类,替换其功能逻辑。 相比直接修改dex,改变dex加载顺序,同时开发替换的类更加简单。

实现流程:
先开发覆盖原有类的功能代码(这个根据实际需要开发,只要明确覆盖哪个类,替换什么功能就行)。然后,重写ApplicationContext,让其实现multidex,并在加载后,启动原有的ApplicationContext。对于动态替换ApplicationContext和启动其它ApplicationContext的方案,网上有介绍,大家可以搜索看看。但是大部分情况下,我们不用完全替换。ApplicationContext的逻辑一般很简单,编写一个wrapper(开放接口尽量跟对方一致,防止对方写了强制转换的逻辑)就可以兼容全部场景,除非对方的ApplicationContext写得比较特殊,这时才需要完全替换。若修改的应用或系统支持multidex,除非有必要重写ApplicationContext(有些框架初始会放在这里,若需要修改这些,就得替换),不然只要添加自己修改后的dex就行(记得明确自己的dex优先加载,否则必须修改ApplicationContext,改变加载顺序)。

这种方案也是目前加固产品的一种实现逻辑。首先,加固代码优先加载,然后根据加密规则,恢复对方dex,然后加载这些dex,最后启动对方的ApplicationContext。
我经常用这种方案拦截一些框架的日志输出、统计输出、调整一些无法直接处理的问题(别人的东西有时真的不适合自己的需要,不得不调整,但是又不能、不想操作对方代码,这时就可以重新编写这些类)。以下是我做的一些调整例子:

替换eventbus日志输出(输出到自己的日志工具):

package org.greenrobot.eventbus;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;

import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import android.os.Looper;
import android.util.Log;

public class EventBusBuilder
{
	private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
	boolean eventInheritance = true;
	ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
	boolean ignoreGeneratedIndex;
	boolean logNoSubscriberMessages = true;
	boolean logSubscriberExceptions = true;
	Logger logger;
	MainThreadSupport mainThreadSupport;
	boolean sendNoSubscriberEvent = true;
	boolean sendSubscriberExceptionEvent = true;
	boolean strictMethodVerification;
	List<SubscriberInfoIndex> subscriberInfoIndexes;
	boolean throwSubscriberException;

	Object getAndroidMainLooperOrNull()
	{
		Looper localLooper = Looper.getMainLooper();
		return localLooper;
	}

	Logger getLogger()
	{
		if (this.logger != null) return this.logger;
		return new Logger() {
			String tag = "EventBus";

			@Override
			public void log(Level arg0, String arg1) {
				org.ufo.util.Logger.t(this.tag, arg1);
			}

			@Override
			public void log(Level arg0, String arg1, Throwable arg2) {
				StringBuilder localStringBuilder = new StringBuilder();
				localStringBuilder.append(arg1);
				localStringBuilder.append("\n");
				localStringBuilder.append(Log.getStackTraceString(arg2));
				org.ufo.util.Logger.t(this.tag, localStringBuilder.toString());
			}

		};
	}

	MainThreadSupport getMainThreadSupport()
	{
		if (this.mainThreadSupport != null) return this.mainThreadSupport;
		if (Logger.AndroidLogger.isAndroidLogAvailable())
		{
			Object localObject = getAndroidMainLooperOrNull();
			if (localObject == null) return null;
			return new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) localObject);
		}
		return null;
	}
}

替换okhttp默认事件输出(替换NONE就行,用户在没有配置事件输出时,会默认使用这个对象):

package okhttp3;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;

public class EventListener
{
	public static final EventListener NONE = new EventListener();
	public static final LogEventListener LOG = new LogEventListener();

	static Factory factory(final EventListener paramEventListener)
	{
		return new Factory()
		{
			public EventListener create(Call paramCall)
			{
				return paramEventListener;
			}
		};
	}

	public void callEnd(Call paramCall)
	{
	}

	public void callFailed(Call paramCall, IOException paramIOException)
	{
	}

	public void callStart(Call paramCall)
	{
	}

	public void connectEnd(Call paramCall, InetSocketAddress paramInetSocketAddress, Proxy paramProxy, Protocol paramProtocol)
	{
	}

	public void connectFailed(Call paramCall, InetSocketAddress paramInetSocketAddress, Proxy paramProxy, Protocol paramProtocol, IOException paramIOException)
	{
	}

	public void connectStart(Call paramCall, InetSocketAddress paramInetSocketAddress, Proxy paramProxy)
	{
	}

	public void connectionAcquired(Call paramCall, Connection paramConnection)
	{
	}

	public void connectionReleased(Call paramCall, Connection paramConnection)
	{
	}

	public void dnsEnd(Call paramCall, String paramString, List<InetAddress> paramList)
	{
	}

	public void dnsStart(Call paramCall, String paramString)
	{
	}

	public void requestBodyEnd(Call paramCall, long paramLong)
	{
	}

	public void requestBodyStart(Call paramCall)
	{
	}

	public void requestHeadersEnd(Call paramCall, Request paramRequest)
	{
	}

	public void requestHeadersStart(Call paramCall)
	{
	}

	public void responseBodyEnd(Call paramCall, long paramLong)
	{
	}

	public void responseBodyStart(Call paramCall)
	{
	}

	public void responseHeadersEnd(Call paramCall, Response paramResponse)
	{
	}

	public void responseHeadersStart(Call paramCall)
	{
	}

	public void secureConnectEnd(Call paramCall, Handshake paramHandshake)
	{
	}

	public void secureConnectStart(Call paramCall)
	{
	}

	public static abstract interface Factory
	{
		public abstract EventListener create(Call paramCall);
	}
}

替换ReactNative默认Printer(默认printer会通过这个类获取,替换这个类,返回自己的实现即可):

package com.facebook.debug.holder;

public class PrinterHolder
{
	public static Printer getPrinter()
	{
		return DebugPrinter.INSTANCE;
	}
}

替换ReactNative的日志输出:

package com.facebook.common.logging;

import org.ufo.util.Logger;

public class FLog
{
	public static boolean show = false;

	public static void d(Class<?> paramClass, String paramString, Object paramObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, new Object[] { paramObject }));
	}

	public static void d(String paramString1, String paramString2)
	{
		if (show) Logger.t(paramString1, paramString2);
	}

	public static void e(Class<?> paramClass, String paramString)
	{
		if (show) Logger.t(getTag(paramClass), paramString);
	}

	public static void e(Class<?> paramClass, String paramString, Throwable paramThrowable)
	{
		if (show) Logger.t(getTag(paramClass), paramString, paramThrowable);
	}

	public static void e(Class<?> paramClass, String paramString, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, paramArrayOfObject));
	}

	public static void e(Class<?> paramClass, Throwable paramThrowable, String paramString, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, paramArrayOfObject), paramThrowable);
	}

	public static void e(String paramString1, String paramString2)
	{
		if (show) Logger.t(paramString1, paramString2);
	}

	public static void e(String paramString1, String paramString2, Throwable paramThrowable)
	{
		if (show) Logger.t(paramString1, paramString2, paramThrowable);
	}

	public static void e(String paramString1, Throwable paramThrowable, String paramString2, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(paramString1, formatString(paramString2, paramArrayOfObject), paramThrowable);
	}

	private static String formatString(String paramString, Object[] paramArrayOfObject)
	{
		return String.format(null, paramString, paramArrayOfObject);
	}

	private static String getTag(Class<?> paramClass)
	{
		return paramClass.getSimpleName();
	}

	public static void i(Class<?> paramClass, String paramString)
	{
		if (show) Logger.t(getTag(paramClass), paramString);
	}

	public static void i(String paramString1, String paramString2)
	{
		if (show) Logger.t(paramString1, paramString2);
	}

	public static boolean isLoggable(int paramInt)
	{
		return true;
	}

	public static void v(Class<?> paramClass, String paramString)
	{
		if (show) Logger.t(getTag(paramClass), paramString);
	}

	public static void v(Class<?> paramClass, String paramString, Object paramObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, new Object[] { paramObject }));
	}

	public static void v(Class<?> paramClass, String paramString, Object paramObject1, Object paramObject2)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, new Object[] { paramObject1, paramObject2 }));
	}

	public static void v(Class<?> paramClass, String paramString, Object paramObject1, Object paramObject2, Object paramObject3)
	{
		v(paramClass, formatString(paramString, new Object[] { paramObject1, paramObject2, paramObject3 }));
	}

	public static void v(Class<?> paramClass, String paramString, Object paramObject1, Object paramObject2, Object paramObject3, Object paramObject4)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, new Object[] { paramObject1, paramObject2, paramObject3, paramObject4 }));
	}

	public static void v(Class<?> paramClass, String paramString, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, paramArrayOfObject));
	}

	public static void v(String paramString1, String paramString2, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(paramString1, formatString(paramString2, paramArrayOfObject));
	}

	public static void w(Class<?> paramClass, String paramString)
	{
		if (show) Logger.t(getTag(paramClass), paramString);
	}

	public static void w(Class<?> paramClass, String paramString, Throwable paramThrowable)
	{
		if (show) Logger.t(getTag(paramClass), paramString, paramThrowable);
	}

	public static void w(Class<?> paramClass, String paramString, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, paramArrayOfObject));
	}

	public static void w(Class<?> paramClass, Throwable paramThrowable, String paramString, Object[] paramArrayOfObject)
	{
		w(paramClass, formatString(paramString, paramArrayOfObject), paramThrowable);
	}

	public static void w(String paramString1, String paramString2)
	{
		if (show) Logger.t(paramString1, paramString2);
	}

	public static void w(String paramString1, String paramString2, Throwable paramThrowable)
	{
		if (show) Logger.t(paramString1, paramString2, paramThrowable);
	}

	public static void w(String paramString1, String paramString2, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(paramString1, formatString(paramString2, paramArrayOfObject));
	}

	public static void w(String paramString1, Throwable paramThrowable, String paramString2, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(paramString1, formatString(paramString2, paramArrayOfObject), paramThrowable);
	}

	public static void wtf(Class<?> paramClass, String paramString, Throwable paramThrowable)
	{
		if (show) Logger.t(getTag(paramClass), paramString, paramThrowable);
	}

	public static void wtf(Class<?> paramClass, String paramString, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(getTag(paramClass), formatString(paramString, paramArrayOfObject));
	}

	public static void wtf(String paramString1, String paramString2, Object[] paramArrayOfObject)
	{
		if (show) Logger.t(paramString1, formatString(paramString2, paramArrayOfObject));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值