dex保护总结
android程序启动过程
http://blog.csdn.net/qianhaifeng2012/article/details/52039053
1.Launcher.startActivitySafely
点击图标,Launcher的onClick方法相应,然后根据传入参数View v的v.getTag()方法得到被点击应用图标的ShortcutInfo,然后得到Intent数据。通过final Intent intent = ((ShortcutInfo) tag).intent。语句得到数据。有了一个应用的Intent就可以启动一个应用了。
Launcher.startActivitySafely-> startActivity
2.Activity.startActivity
Activity是Launcher父类
Activity.startActivity->Activity.startActivityForResult
3.Activity.startActivityForResult
1 | Instrumentation.ActivityResult ar = |
ActivityInfo aInfo =
resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
...
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
componentSpecified, null, container, inTask);
1 |
|
final int startActivityLocked(IApplicationThread caller,
Intent intent, String resolvedType, ActivityInfo aInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage,
int realCallingPid, int realCallingUid, int startFlags, Bundle options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
ActivityContainer container, TaskRecord inTask)
1 |
|
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, voiceSession != null, this, container, options);
1 |
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options, inTask);
1 | 7.ActivityStackSupervisor.startActivityUncheckedLocked |
当第一次启动应用程序的Activity,取回来的app为null。在Activity应用程序中的AndroidManifest.xml配置文件中,我们没有指定Application标签的process属性,系统就会默认使用package的名称。每一个应用程序都有自己的uid,因此,这里uid + process的组合就可以为每一个应用程序创建一个ProcessRecord。
所以下面if (app != null && app.thread != null)条件不成立,那么条件里面realStartActivityLocked 方法就不会执行,而是执行ActivityManagerService.startProcessLocked函数进行下一步操作。
10.ActivityManagerService.startProcessLocked
app=null
|
V
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
|
V
addProcessNameLocked
|
V
mProcessNames.put(proc.processName, proc.uid, proc);
有了进程信息之后
ActivityManagerService.startProcessLocked
|
V
Process.start
调用Process.start接口来创建一个新的进程,新的进程会导入android.app.ActivityThread类,并且执行它的main函数
1 | entryPoint = "android.app.ActivityThread" |
11.Process.start
1 | public static final ProcessStartResult start(final String processClass, |
startViaZygote会通过zygote机制开启一个新的进程。由于我们导入的类名是android.app.ActivityThread,开启一个ActivityThread进程,这也是为什么一个应用程序只有一个ActivityThread,然后会执行他的main方法。
12.ActivityThread.main
其中调用了
1 | ActivityThread thread = new ActivityThread(); |
1 | private void attach(boolean system) { |
然后会执行mgr.attachApplication方法,mgr = ActivityManagerNative.getDefault(),通过前面分析它是ActivityManagerService的代理对象ActivityManagerProxy对象。所以最终会转向ActivityManagerService的attachApplication方法。
其中mAppThread是ApplicationThread,是一个Binder对象,用于ActivityManagerService与ActivityThread通信。
13.ActivityManagerService.attachApplication
attachApplication->attachApplicationLocked
1 | ProcessRecord app; |
通过pid获取之前由Process.start创建的进程的信息记录,再由mStackSupervisor.attachApplicationLocked(app)
执行
attachApplicationLocked:
1 | try { |
调用mMainStack.realStartActivityLocked执行真正的Activity启动操作。这里要启动的Activity通过调用mMainStack.topRunningActivityLocked(null)从堆栈顶端取回来,这时候在堆栈顶端的Activity就是MainActivity了。
14.ActivityManagerService.realStartActivityLocked
1 | final boolean realStartActivityLocked(ActivityRecord r, |
app.thread是ApplicationThreadProxy对象,是一个代理对象,代理就是ApplicationThread,情况完全类似于ActivityManagerProxy代理类。所以逻辑转向了ApplicationThread的scheduleLaunchActivity方法。
15.ApplicationThread.scheduleLaunchActivity
1 | public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, |
函数首先创建一个ActivityClientRecord实例,并且初始化它的成员变量,然后发送一个启动Activity的消息交给Handler处理.
1 | public void handleMessage(Message msg) { |
16.ActivityThread.handleLaunchActivity
1 | private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
从上面的源码可以看出,performLaunchActivity最终完成了Activity对象的创建和启动过程,并且Activity通过handleResumeActivity方法来调用被启动Activity的onResume这一生命周期方法。而onCreate这个这个生命周期方法在performLaunchActivity方法中被回调。
17.ActivityThread.performLaunchActivity
这个方法主要完成了如下几件事
1)从ActivityClientRecord中获取待启动的Activity的组件信息。
1 | ActivityInfo aInfo = r.activityInfo; |
2)通过Instrumenttation的newActivity方法使用类加载器创建Activity对象。
1 | Activity activity = null; |
3)通过LoadeApk的makeApplication方法来尝试创建Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
makeApplication方法代码如下
1 | public Application makeApplication(boolean forceDefaultAppClass, |
从makeApplication方法可以看出,通过mActivityThread.mInstrumentation.newApplication创建Application对象,然后通过callApplicationOnCreate方法间接回调onCreate方法。
4)创建ContextImpl对象并通过Activity的attach方法来完成一些重要数据的初始化
1 | Context appContext = createBaseContextForActivity(r, activity); |
ContextImpl是一个很重要的数据结构,它是Context的具体实现,Context中的大部分逻辑都是由ContextImpl来完成的。ContextImpl是通过Activity的attach方法来和Activity建立链接的,处理之外,在attach方法中Activity还会完成Window的创建并建立自己和Window的关联,这样当Window接受到外部输入事件后就可以将时间传递给Activity。
5)调用Activity的onCreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
最后回调Activity的onCreate方法。这也意味着应用已经启动起来了。
###总结
Launcher会响应图标点击,开始开启Activity,应用图标的shortcutinfo的intent获得应用信息,通知ActivityManagerServer来启动Activity,首先需要获取Activity的信息,并存放Activity信息,为启动作准备(对应ActivityRecord和Activity栈)。信息有了之后就要为应用准备一个进程,zygotefork出一个进程后,执行ActivityThread的main方法,在该方法里会先准备好Looper和消息队列,再调用attach将这个进程绑定到ActivityManagerServer,之后就要准备启动Activity了,ActivityManagerServer这时候会保存一个应用进程的代理对象,这样ActivityManagerServer就可以对Activity进行管理了,ActivityManagerServer获取Activity栈中的Activity信息,来通知应用进程创建入口Activity的实例。
动态加载类
类加载器:
http://blog.csdn.net/jiangwei0910410003/article/details/41384667
ClassLoader:
代码动态加载:
- 接管系统加载,比源程序优先获取执行权利
- 利用ClassLoader进行源代码加载
- 还原程序执行环境
- 将执行权利移交给源程序
绑定Application
1 | at java.lang.Thread.dumpStack(Thread.java:505) |
在ActivityThread的main方法中会执行ActivityThread对象的attach方法,回调了ActivityManagerService的远程接口本地代理对象ActivityManagerProxy的attachApplication函数通知attachApplication,并传入参数是mAppThread,这是ApplicationThread类型的Binder对象,用来接受ActivityManagerService的进程间消息。
1 | public final class ActivityThread { |
ActivityManagerService在接受到attachApplication函数调用远程消息之后,一系列处理之后,会有两个重要Binder通信,一个就是通过传来的参数Binder参数ApplicationThread来通知ActivityThread中mAppThread远程中调用bindApplication(),另一个是scheduleLaunchActivity。在Ams中收到attachApplication时代码如下:
1 | AMS |
从上午可以看到在attachApplicationLocked中有两个比较重要的方法函数:
1 | thread.bindApplication(…) : 绑定Application到ActivityThread |
动态加载dex
外部类加载
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. Java反射允许我们修改Android中隐藏的类,以及其中的属性。
Android 基于Java开发,因此具有Java拥有的一切优势.因此允许在程序运行起来后动态地加载额外的代码.而这个代码加载器,称为ClassLoader。
时机
ActivityThread.main()
ActivityThread.attach()
ActivityManagerSevice.attachApplication->attachApplicationLocked
ActivityThread.BindApplication– BIND_APPLICATION消息–>handlBindApplication(内有等待java调试器挂接)
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
LoadedApk.java:
makeApplication->newApplication
Application.attach->Application.attachBaseContext
Instrumentation.callApplicationOnCreate(app);
callApplicationOnCreate->Application.onCreate()
Activity类 、Service类 、Application类本质上都是Context子类
创建Application 对象时(而且整个App共一个Application对象),创建Service对象时,创建Activity对象时,都会创建Context实例。
选择attachBaseContext作为修改加载Activity的入口再合适不过了。
实现步骤:
- 新建工程,完成主要代码,build之后提取dex文件(注意mulitDexEnable设置为false)
- 反编译dex,删除不需要的类,对应于smali文件
- 回编译dex,将dex放回工程目录(可对其加密处理)
- 工程中新建入口(即重载attachBaseContext),注意配置文件中启动Activity依然是原类名,添加启动Application类名
代码自修改
在源代码中写入虚假代码,载入内存后,利用先执行的native代码修改字节码实现逻辑代码修改。
在native代码中先获取到当前进程的内存信息(利用/proc/[pid]/maps) ,查找到dex段,搜索”dex\n035”magic值判断dex开头,利用dex文件格式获取对应方法地址,实现内存修改。
函数动态还原
利用外部类加载+native hook实现(对应于java层和native层的类加载部分):
- 抽取dex中需要加密的函数字节码,保存为文件,清空偏移
- 将提取字节码文件和dex文件移入工程,用以上方式加载
- 在native代码中,利用dexlib解析dex
- 利用hook框架实现对libdvm.so的dvmDefineClass
- 实现还原对应方法的字节码
dex文件格式
结构图:
字符串池索引过程:
header.string_ids_off -> string_id_list[0].string_data_off -> string_item[0]|(string_item_size+byte[size])
方法字节码索引过程: