即时运行app的逆向分析以及原理浅析以及谈谈xposed免重启更新是否能够兼容即时运行的可行性

首先即时运行app的即时更新是通过内容提供者的启动而更新的,通过分析源码发现并没有修改的是application节点,也就是说合application multidex的方式不同。

 <provider android:name="com.android.tools.ir.server.InstantRunContentProvider" android:multiprocess="true" android:authorities="cn.qssq666.radiogroupdemo.com.android.tools.ir.server.InstantRunContentProvider" />

image.png

public final class InstantRunContentProvider extends ContentProvider {
    public boolean onCreate() {
        if (isMainProcess()) {
            Log.i("InstantRun", "starting instant run server: is main process");
            Server.create(getContext());
        } else {
            Log.i("InstantRun", "not starting instant run server: not main process");
        }
        return true;
    }

    private boolean isMainProcess() {
        boolean foundPackage = false;
        boolean isMainProcess = false;
        if (AppInfo.applicationId == null) {
            return isMainProcess;
        }
        int pid = Process.myPid();
        for (RunningAppProcessInfo processInfo : ((ActivityManager) getContext().getSystemService("activity")).getRunningAppProcesses()) {
            if (AppInfo.applicationId.equals(processInfo.processName)) {
                foundPackage = true;
                if (processInfo.pid == pid) {
                    isMainProcess = true;
                    break;
                }
            }
        }
        if (isMainProcess || foundPackage) {
            return isMainProcess;
        }
        isMainProcess = true;
        Log.w("InstantRun", "considering this process main process:no process with this package found?!");
        return isMainProcess;
    }

Server源码


public class Server {
    private static final boolean POST_ALIVE_STATUS = false;
    private static final boolean RESTART_LOCALLY = false;
    private static int wrongTokenCount;
    private final Context context;
    private LocalServerSocket serverSocket;

    private Server(java.lang.String r1, android.content.Context r2) {
        /* JADX: method processing error */
/*
Error: jadx.core.utils.exceptions.DecodeException: Load method exception in method: com.android.tools.ir.server.Server.<init>(java.lang.String, android.content.Context):void
    at jadx.core.dex.nodes.MethodNode.load(MethodNode.java:116)
    at jadx.core.dex.nodes.ClassNode.load(ClassNode.java:249)
    at jadx.core.ProcessClass.process(ProcessClass.java:34)
    at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:306)
    at jadx.api.JavaClass.decompile(JavaClass.java:62)
Caused by: java.lang.NullPointerException
    at jadx.core.dex.nodes.MethodNode.addJump(MethodNode.java:370)
    at jadx.core.dex.nodes.MethodNode.initJumps(MethodNode.java:356)
    at jadx.core.dex.nodes.MethodNode.load(MethodNode.java:106)
    ... 4 more
*/
        /*
        r0 = this;
        r4.<init>();
        r4.context = r6;
        r0 = new android.net.LocalServerSocket;  Catch:{ IOException -> 0x005c }
        r0.<init>(r5);   Catch:{ IOException -> 0x005c }
        r4.serverSocket = r0;    Catch:{ IOException -> 0x005c }
        r0 = "InstantRun";   Catch:{ IOException -> 0x005c }
        r1 = 2;  Catch:{ IOException -> 0x005c }
        r0 = android.util.Log.isLoggable(r0, r1);    Catch:{ IOException -> 0x005c }
        if (r0 == 0) goto L_0x0039;  Catch:{ IOException -> 0x005c }
    L_0x0015:
        r0 = "InstantRun";   Catch:{ IOException -> 0x005c }
        r2 = new java.lang.StringBuilder;    Catch:{ IOException -> 0x005c }
        r2.<init>();     Catch:{ IOException -> 0x005c }
        r3 = "Starting server socket listening for package ";    Catch:{ IOException -> 0x005c }
        r2.append(r3);   Catch:{ IOException -> 0x005c }
        r2.append(r5);   Catch:{ IOException -> 0x005c }
        r3 = " on ";     Catch:{ IOException -> 0x005c }
        r2.append(r3);   Catch:{ IOException -> 0x005c }
        r3 = r4.serverSocket;    Catch:{ IOException -> 0x005c }
        r3 = r3.getLocalSocketAddress();     Catch:{ IOException -> 0x005c }
        r2.append(r3);   Catch:{ IOException -> 0x005c }
        r2 = r2.toString();  Catch:{ IOException -> 0x005c }
        android.util.Log.v(r0, r2);  Catch:{ IOException -> 0x005c }
        r4.startServer();
        r0 = "InstantRun";
        r0 = android.util.Log.isLoggable(r0, r1);
        if (r0 == 0) goto L_0x005b;
        r0 = "InstantRun";
        r1 = new java.lang.StringBuilder;
        r1.<init>();
        r2 = "Started server for package ";
        r1.append(r2);
        r1.append(r5);
        r1 = r1.toString();
        android.util.Log.v(r0, r1);
        return;
    L_0x005c:
        r0 = move-exception;
        r1 = "InstantRun";
        r2 = new java.lang.StringBuilder;
        r2.<init>();
        r3 = "IO Error creating local socket at ";
        r2.append(r3);
        r2.append(r5);
        r2 = r2.toString();
        android.util.Log.e(r1, r2, r0);
        return;
        */
        throw new UnsupportedOperationException("Method not decompiled: com.android.tools.ir.server.Server.<init>(java.lang.String, android.content.Context):void");
    }

    static /* synthetic */ int access$208() {
        int i = wrongTokenCount;
        wrongTokenCount = i + 1;
        return i;
    }

    public static Server create(Context context) {
        return new Server(context.getPackageName(), context);
    }

    private void startServer() {
        try {
            new Thread(new SocketServerThread(this, null)).start();
        } catch (Throwable e) {
            if (Log.isLoggable("InstantRun", 6)) {
                Log.e("InstantRun", "Fatal error starting Instant Run server", e);
            }
        }
    }

    public void shutdown() {
        if (this.serverSocket != null) {
            try {
                this.serverSocket.close();
            } catch (IOException e) {
            }
            this.serverSocket = null;
        }
    }

    private static boolean isResourcePath(String path) {
        if (!path.equals("resources.ap_")) {
            if (!path.startsWith("res/")) {
                return false;
            }
        }
        return true;
    }

    private static boolean hasResources(List<ApplicationPatch> changes) {
        for (ApplicationPatch change : changes) {
            if (isResourcePath(change.getPath())) {
                return true;
            }
        }
        return false;
    }

    private int handlePatches(List<ApplicationPatch> changes, boolean hasResources, int updateMode) {
        if (hasResources) {
            FileManager.startUpdate();
        }
        for (ApplicationPatch change : changes) {
            String path = change.getPath();
            if (path.equals("classes.dex.3")) {
                updateMode = handleHotSwapPatch(updateMode, change);
            } else if (isResourcePath(path)) {
                updateMode = handleResourcePatch(updateMode, change, path);
            }
        }
        if (hasResources) {
            FileManager.finishUpdate(true);
        }
        return updateMode;
    }

    private static int handleResourcePatch(int updateMode, ApplicationPatch patch, String path) {
        if (Log.isLoggable("InstantRun", 2)) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("Received resource changes (");
            stringBuilder.append(path);
            stringBuilder.append(")");
            Log.v("InstantRun", stringBuilder.toString());
        }
        FileManager.writeAaptResources(path, patch.getBytes());
        return Math.max(updateMode, 2);
    }

    private int handleHotSwapPatch(int updateMode, ApplicationPatch patch) {
        if (Log.isLoggable("InstantRun", 2)) {
            Log.v("InstantRun", "Received incremental code patch");
        }
        try {
            String dexFile = FileManager.writeTempDexFile(patch.getBytes());
            if (dexFile == null) {
                Log.e("InstantRun", "No file to write the code to");
                return updateMode;
            }
            if (Log.isLoggable("InstantRun", 2)) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append("Reading live code from ");
                stringBuilder.append(dexFile);
                Log.v("InstantRun", stringBuilder.toString());
            }
            Class<?> aClass = Class.forName("com.android.tools.ir.runtime.AppPatchesLoaderImpl", true, new DexClassLoader(dexFile, this.context.getCacheDir().getPath(), FileManager.getNativeLibraryFolder().getPath(), getClass().getClassLoader()));
            if (Log.isLoggable("InstantRun", 2)) {
                StringBuilder stringBuilder2 = new StringBuilder();
                stringBuilder2.append("Got the patcher class ");
                stringBuilder2.append(aClass);
                Log.v("InstantRun", stringBuilder2.toString());
            }
            PatchesLoader loader = (PatchesLoader) aClass.newInstance();
            if (Log.isLoggable("InstantRun", 2)) {
                StringBuilder stringBuilder3 = new StringBuilder();
                stringBuilder3.append("Got the patcher instance ");
                stringBuilder3.append(loader);
                Log.v("InstantRun", stringBuilder3.toString());
            }
            int i = 0;
            String[] getPatchedClasses = (String[]) aClass.getDeclaredMethod("getPatchedClasses", new Class[0]).invoke(loader, new Object[0]);
            if (Log.isLoggable("InstantRun", 2)) {
                Log.v("InstantRun", "Got the list of classes ");
                int length = getPatchedClasses.length;
                while (i < length) {
                    String getPatchedClass = getPatchedClasses[i];
                    StringBuilder stringBuilder4 = new StringBuilder();
                    stringBuilder4.append("class ");
                    stringBuilder4.append(getPatchedClass);
                    Log.v("InstantRun", stringBuilder4.toString());
                    i++;
                }
            }
            if (!loader.load()) {
                updateMode = 3;
            }
            return updateMode;
        } catch (Exception e) {
            Log.e("InstantRun", "Couldn't apply code changes", e);
            e.printStackTrace();
            updateMode = 3;
        } catch (Throwable e2) {
            Log.e("InstantRun", "Couldn't apply code changes", e2);
            updateMode = 3;
        }
    }

    private void restart(int updateMode, boolean incrementalResources, boolean toast) {
        if (Log.isLoggable("InstantRun", 2)) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("Finished loading changes; update mode =");
            stringBuilder.append(updateMode);
            Log.v("InstantRun", stringBuilder.toString());
        }
        if (updateMode != 0) {
            if (updateMode != 1) {
                StringBuilder stringBuilder2;
                List<Activity> activities = Restarter.getActivities(this.context, false);
                if (incrementalResources && updateMode == 2) {
                    File file = FileManager.getExternalResourceFile();
                    if (Log.isLoggable("InstantRun", 2)) {
                        stringBuilder2 = new StringBuilder();
                        stringBuilder2.append("About to update resource file=");
                        stringBuilder2.append(file);
                        stringBuilder2.append(", activities=");
                        stringBuilder2.append(activities);
                        Log.v("InstantRun", stringBuilder2.toString());
                    }
                    if (file != null) {
                        MonkeyPatcher.monkeyPatchExistingResources(this.context, file.getPath(), activities);
                    } else {
                        Log.e("InstantRun", "No resource file found to apply");
                        updateMode = 3;
                    }
                }
                Activity activity = Restarter.getForegroundActivity(this.context);
                if (updateMode == 2) {
                    if (activity != null) {
                        if (Log.isLoggable("InstantRun", 2)) {
                            Log.v("InstantRun", "Restarting activity only!");
                        }
                        boolean handledRestart = false;
                        try {
                            Object result = activity.getClass().getMethod("onHandleCodeChange", new Class[]{Long.TYPE}).invoke(activity, new Object[]{Long.valueOf(0)});
                            if (Log.isLoggable("InstantRun", 2)) {
                                stringBuilder2 = new StringBuilder();
                                stringBuilder2.append("Activity ");
                                stringBuilder2.append(activity);
                                stringBuilder2.append(" provided manual restart method; return ");
                                stringBuilder2.append(result);
                                Log.v("InstantRun", stringBuilder2.toString());
                            }
                            if (Boolean.TRUE.equals(result)) {
                                handledRestart = true;
                                if (toast) {
                                    Restarter.showToast(activity, "Applied changes");
                                }
                            }
                        } catch (Throwable th) {
                        }
                        if (!handledRestart) {
                            if (toast) {
                                Restarter.showToast(activity, "Applied changes, restarted activity");
                            }
                            Restarter.restartActivityOnUiThread(activity);
                        }
                        return;
                    }
                    if (Log.isLoggable("InstantRun", 2)) {
                        Log.v("InstantRun", "No activity found, falling through to do a full app restart");
                    }
                    updateMode = 3;
                }
                if (updateMode != 3) {
                    if (Log.isLoggable("InstantRun", 6)) {
                        StringBuilder stringBuilder3 = new StringBuilder();
                        stringBuilder3.append("Unexpected update mode: ");
                        stringBuilder3.append(updateMode);
                        Log.e("InstantRun", stringBuilder3.toString());
                    }
                    return;
                }
                if (Log.isLoggable("InstantRun", 2)) {
                    Log.v("InstantRun", "Waiting for app to be killed and restarted by the IDE...");
                }
                return;
            }
        }
        if (Log.isLoggable("InstantRun", 2)) {
            Log.v("InstantRun", "Applying incremental code without restart");
        }
        if (toast) {
            Activity foreground = Restarter.getForegroundActivity(this.context);
            if (foreground != null) {
                Restarter.showToast(foreground, "Applied code changes without activity restart");
            } else if (Log.isLoggable("InstantRun", 2)) {
                Log.v("InstantRun", "Couldn't show toast: no activity found");
            }
        }
    }
}

重点代码

    private void startServer() {
        try {
            new Thread(new SocketServerThread(this, null)).start();
        } catch (Throwable e) {
            if (Log.isLoggable("InstantRun", 6)) {
                Log.e("InstantRun", "Fatal error starting Instant Run server", e);
            }
        }
    }

base.apk

image.png

base.apk就有很多个apk。那么到底是如何实现多dex加载的呢?不通过multidex技术,仅仅是通过内容提供者.

而上面的几个dex并不是主程序的,也就是说真正的代码被分割在data/app/包名/下,
包含了如下:split_lib_dependencies_apk.apk以及 split_lib_slice_[0_9]_.apk的10个apk中。

ok,下面的分析交给大家了,我只是遇到了一个问题,就是一个插件apk里面多个dex 在解压后加载然后融合出现了问题,融合貌似成功了,但是还是找不到,我特么快疯了。 各位大佬懂得求支个招。哈哈

另外懂了大概即时运行原理,那么也自然可以让xposed免重启技术兼容即时运行,但是无奈老夫功力欠缺,暂时先放一放,等功力提升的时候再研究研究。 另外对于官方的即时运行apk我都没玩过,也许我这分析半天还没有一个官方的demo详细,逆向只是野路子,哈哈。

另外我还发愁的一个问题就是开发工具不能自动识别设备、或针对部分项目不开启即时运行,对不同设备自动切换非即时运行,搞的我搞逆向插件研究的时候总是要把即时运行给关了。不然死活都激活不了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容