DexClassLoader和PathClassLoader加载Dex流程

0x00

在上一篇文章apk安装和优化原理,在最后我们分析了DexClassLoader和PathClassLoader的构造函数的不同。

PathClassLoader最后调用的是new DexFile(pathFile),而DexClassLoader调用的是DexFile.loadDex(dexPathList[i], outputName, 0)。

0x01

new DexFile(pathFile)对应的代码位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

    public DexFile(String fileName) throws IOException {
        String wantDex = System.getProperty("android.vm.dexfile", "false");
        if (!wantDex.equals("true"))
            throw new UnsupportedOperationException("No dex in this VM");

        mCookie = openDexFile(fileName, null, 0);
        mFileName = fileName;
        //System.out.println("DEX FILE cookie is " + mCookie);
    }

DexFile.loadDex(dexPathList[i], outputName, 0)对应的代码也位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

    static public DexFile loadDex(String sourcePathName, String outputPathName,
        int flags) throws IOException {

        /*
         * TODO: we may want to cache previously-opened DexFile objects.
         * The cache would be synchronized with close().  This would help
         * us avoid mapping the same DEX more than once when an app
         * decided to open it multiple times.  In practice this may not
         * be a real issue.
         */
        return new DexFile(sourcePathName, outputPathName, flags);
    }
    private DexFile(String sourceName, String outputName, int flags)
        throws IOException {

        String wantDex = System.getProperty("android.vm.dexfile", "false");
        if (!wantDex.equals("true"))
            throw new UnsupportedOperationException("No dex in this VM");

        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        //System.out.println("DEX FILE cookie is " + mCookie);
    }

我们可以看到其实两者最终调用的都是openDexFile,只不过DexClassLoader指定了生成优化后的apk路径,而PathClassLoader则不需要,因为在安装阶段已经生成了/data/dalvik-cache/[email protected]。

0x02

我们继续分析openDexFile,这个方法一个JNI方法。

    native private static int openDexFile(String sourceName, String outputName,
        int flags) throws IOException;

代码位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

对应的native方法位于dalvik\vm\native\dalvik_system_DexFile.c。

const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
    { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
        Dalvik_dalvik_system_DexFile_openDexFile },
    { "closeDexFile",       "(I)V",
        Dalvik_dalvik_system_DexFile_closeDexFile },
    { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
        Dalvik_dalvik_system_DexFile_defineClass },
    { "getClassNameList",   "(I)[Ljava/lang/String;",
        Dalvik_dalvik_system_DexFile_getClassNameList },
    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    { NULL, NULL, NULL },
};

我们看到openDexFile对应的是Dalvik_dalvik_system_DexFile_openDexFile方法。

static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
    JValue* pResult)
{
    ......
    if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
        LOGV("Opening DEX file ‘%s‘ (DEX)\n", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = true;
        pDexOrJar->pRawDexFile = pRawDexFile;
    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
        LOGV("Opening DEX file ‘%s‘ (Jar)\n", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;
    } else {
        LOGV("Unable to open DEX file ‘%s‘\n", sourceName);
        dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");
    }

    ......

    RETURN_PTR(pDexOrJar);
}

代码位于dalvik\vm\native\dalvik_system_DexFile.c。

这里调用dvmJarFileOpen,代码也位于dalvik\vm\JarFile.c中。

int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
    JarFile** ppJarFile, bool isBootstrap)
{
    ZipArchive archive;
    DvmDex* pDvmDex = NULL;
    char* cachedName = NULL;
    bool archiveOpen = false;
    bool locked = false;
    int fd = -1;
    int result = -1;

    /* Even if we‘re not going to look at the archive, we need to
     * open it so we can stuff it into ppJarFile.
     */
    if (dexZipOpenArchive(fileName, &archive) != 0)
        goto bail;
    archiveOpen = true;

    /* If we fork/exec into dexopt, don‘t let it inherit the archive‘s fd.
     */
    dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));

    /* First, look for a ".odex" alongside the jar file.  It will
     * have the same name/path except for the extension.
     */
    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
    if (fd >= 0) {
       .......
    } else {
        ZipEntry entry;

tryArchive:
        /*
         * Pre-created .odex absent or stale.  Look inside the jar for a
         * "classes.dex".
         */
        entry = dexZipFindEntry(&archive, kDexInJarName);
        if (entry != NULL) {
            bool newFile = false;

            /*
             * We‘ve found the one we want.  See if there‘s an up-to-date copy
             * in the cache.
             *
             * On return, "fd" will be seeked just past the "opt" header.
             *
             * If a stale .odex file is present and classes.dex exists in
             * the archive, this will *not* return an fd pointing to the
             * .odex file; the fd will point into dalvik-cache like any
             * other jar.
             */
            if (odexOutputName == NULL) {
                cachedName = dexOptGenerateCacheFileName(fileName,
                                kDexInJarName);
                if (cachedName == NULL)
                    goto bail;
            } else {
                cachedName = strdup(odexOutputName);
            }
            LOGV("dvmDexCacheStatus: Checking cache for %s (%s)\n",
                fileName, cachedName);
            fd = dvmOpenCachedDexFile(fileName, cachedName,
                    dexGetZipEntryModTime(&archive, entry),
                    dexGetZipEntryCrc32(&archive, entry),
                    isBootstrap, &newFile, /*createIfMissing=*/true);
            if (fd < 0) {
                LOGI("Unable to open or create cache for %s (%s)\n",
                    fileName, cachedName);
                goto bail;
            }
            locked = true;

            /*
             * If fd points to a new file (because there was no cached version,
             * or the cached version was stale), generate the optimized DEX.
             * The file descriptor returned is still locked, and is positioned
             * just past the optimization header.
             */
            if (newFile) {
                u8 startWhen, extractWhen, endWhen;
                bool result;
                off_t dexOffset;

                dexOffset = lseek(fd, 0, SEEK_CUR);
                result = (dexOffset > 0);

                if (result) {
                    startWhen = dvmGetRelativeTimeUsec();
                    result = dexZipExtractEntryToFile(&archive, entry, fd) == 0;
                    extractWhen = dvmGetRelativeTimeUsec();
                }
                if (result) {
                    result = dvmOptimizeDexFile(fd, dexOffset,
                                dexGetZipEntryUncompLen(&archive, entry),
                                fileName,
                                dexGetZipEntryModTime(&archive, entry),
                                dexGetZipEntryCrc32(&archive, entry),
                                isBootstrap);
                }

                if (!result) {
                    LOGE("Unable to extract+optimize DEX from ‘%s‘\n",
                        fileName);
                    goto bail;
                }

                endWhen = dvmGetRelativeTimeUsec();
                LOGD("DEX prep ‘%s‘: unzip in %dms, rewrite %dms\n",
                    fileName,
                    (int) (extractWhen - startWhen) / 1000,
                    (int) (endWhen - extractWhen) / 1000);
            }
        } else {
           ......
        }
    }

    /*
     * Map the cached version.  This immediately rewinds the fd, so it
     * doesn‘t have to be seeked anywhere in particular.
     */
    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
        LOGI("Unable to map %s in %s\n", kDexInJarName, fileName);
        goto bail;
    }
    ......
    LOGV("Successfully opened ‘%s‘ in ‘%s‘\n", kDexInJarName, fileName);

    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
    (*ppJarFile)->archive = archive;
    (*ppJarFile)->cacheFileName = cachedName;
    (*ppJarFile)->pDvmDex = pDvmDex;
    cachedName = NULL;      // don‘t free it below
    result = 0;

bail:
    ......
    return result;
}

我们主要关注dvmOpenCachedDexFile方法,首先调用这个方法,尝试根据cachedName所指的优化文件名在cache中查找并读取优化文件,如果没有,则要就对Dex进行优化。我们前面说过对于PathClassLoader,在安装阶段已经生成了/data/dalvik-cache/[email protected],所以dvmOpenCachedDexFile方法返回的newFile为false。而对于DexClassLoader已经指明了要生成优化后的dex的路径,这里dvmOpenCachedDexFile方法返回的newFile为true。

对于PathClassLoader,newFile为false,所以不需要再一次优化dex。而对于DexClassLoader,newFile为true,所以调用dvmOptimizeDexFile来优化dex,dvmOptimizeDexFile代码位于           dalvik\vm\analysis\DexPrepare.c。也就是说对于DexClassLoader也只优化一次,第二次就不会再优化了,优化还是比较耗时的。

bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    ......
    pid = fork();
    if (pid == 0) {
        static const char* kDexOptBin = "/bin/dexopt";
        ......
        androidRoot = getenv("ANDROID_ROOT");
        if (androidRoot == NULL) {
            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
            androidRoot = "/system";
        }
        execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
        strcpy(execFile, androidRoot);
        strcat(execFile, kDexOptBin);

		.....
        if (kUseValgrind)
            execv(kValgrinder, argv);
        else
            execv(execFile, argv);

        LOGE("execv ‘%s‘%s failed: %s\n", execFile,
            kUseValgrind ? " [valgrind]" : "", strerror(errno));
        exit(1);
    } else {
        ......
    }
}

也是fork出一个子线程去执行/system/bin/dexopt,也就是OptMain.c中的main方法,往后的执行流程请参考apk安装和优化原理一文。简单的脱壳程序就在dvmDexFileOpenPartial下断点,参考使用IDA Pro进行简单的脱壳。DexClassLoader加载dex首先要进行优化,优化的过程中会调用dvmDexFileOpenPartial。而dvmDexFileOpenPartial的dexAddr是dex在内存的基地址,dexLength是dex在内存中长度,详细请参考apk安装和优化原理一文。这样就可以在内存中把dex dump出来。

我们回到dvmJarFileOpen,继续执行dvmDexFileOpenFromFd,代码位于dalvik\vm\DvmDex.c。

int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
    DvmDex* pDvmDex;
    DexFile* pDexFile;
    MemMapping memMap;
    int parseFlags = kDexParseDefault;
    int result = -1;

    if (gDvm.verifyDexChecksum)
        parseFlags |= kDexParseVerifyChecksum;

    if (lseek(fd, 0, SEEK_SET) < 0) {
        LOGE("lseek rewind failed\n");
        goto bail;
    }

    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
        LOGE("Unable to map file\n");
        goto bail;
    }

    pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
    if (pDexFile == NULL) {
        LOGE("DEX parse failed\n");
        sysReleaseShmem(&memMap);
        goto bail;
    }

    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        sysReleaseShmem(&memMap);
        goto bail;
    }

    /* tuck this into the DexFile so it gets released later */
    sysCopyMap(&pDvmDex->memMap, &memMap);
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}

fd为优化后的dex,对于PathClassLoader,位于/data/dalvik-cache/[email protected]。对于DexClassLoader,就是在new DexClassLoader(String dexPath, String dexOutputDir, String libPath, ClassLoader parent)指定的dexOutputDir路径中。

在dvmDexFileOpenFromFd方法中首先调用sysMapFileInShmemWritableReadOnly方法把优化后的dex加载进内存。


int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
{
#ifdef HAVE_POSIX_FILEMAP
    off_t start;
    size_t length;
    void* memPtr;

    assert(pMap != NULL);

    if (getFileStartAndLength(fd, &start, &length) < 0)
        return -1;

    memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
            fd, start);
    if (memPtr == MAP_FAILED) {
        LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
            fd, (int) start, strerror(errno));
        return -1;
    }
    if (mprotect(memPtr, length, PROT_READ) < 0) {
        /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
        int err = errno;
        LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
            memPtr, length, strerror(err));
        LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
    }

    pMap->baseAddr = pMap->addr = memPtr;
    pMap->baseLength = pMap->length = length;

    return 0;
#else
    return sysFakeMapFile(fd, pMap);
#endif
}

代码位于dalvik/libdex/SysUtil.c。
    MemMapping结构体pMap中baseAddr和addr都指向dex映射到内存的首地址。

返回到dvmDexFileOpenFromFd,继续执行dexFileParse,代码位于dalvik\libdex\DexFile.c。

DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
    DexFile* pDexFile = NULL;
    const DexHeader* pHeader;
    const u1* magic;
    int result = -1;

    if (length < sizeof(DexHeader)) {
        LOGE("too short to be a valid .dex\n");
        goto bail;      /* bad file format */
    }

    pDexFile = (DexFile*) malloc(sizeof(DexFile));
    if (pDexFile == NULL)
        goto bail;      /* alloc failure */
    memset(pDexFile, 0, sizeof(DexFile));

    /*
     * Peel off the optimized header.
     */
    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
        magic = data;
        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
            LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
                 magic[4], magic[5], magic[6], magic[7]);
            goto bail;
        }

        pDexFile->pOptHeader = (const DexOptHeader*) data;
        LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);

        /* parse the optimized dex file tables */
        if (!dexParseOptData(data, length, pDexFile))
            goto bail;
		data += pDexFile->pOptHeader->dexOffset;
        length -= pDexFile->pOptHeader->dexOffset;
        ......
    }

    dexFileSetupBasicPointers(pDexFile, data);
    pHeader = pDexFile->pHeader;

    ......
    result = 0;

bail:
    if (result != 0 && pDexFile != NULL) {
        dexFileFree(pDexFile);
        pDexFile = NULL;
    }
    return pDexFile;
}

这个方法主要是生成DexFile结构,我们首先来看DexFile结构是什么样子的?

typedef struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
} DexFile;

代码位于dalvik\libdex\DexFile.h中。

dexFileParse这个方法首先为pDexFile->pOptHeader赋值,将优化文件头部与DexFile数据结构下的pOptHeader变量进行关联。

然后调用dexParseOptData函数对优化数据进行处理,其作用也是将各个优化数据与DexFile数据结构中的相应成员变量进行关联,这里关联的是pClassLookup和pRegisterMapPool。

最后调用dexFileSetupBasicPointers将Dex文件中其他各部分数据与DexFile数据结构建立完整的映射关系,代码位于dalvik\libdex\DexFile.c。

void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
    DexHeader *pHeader = (DexHeader*) data;

    pDexFile->baseAddr = data;
    pDexFile->pHeader = pHeader;
    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}

重点说一下baseAddr指向dex文件的头部。

执行完dexFileParse生成了DexFile结构,返回到dvmDexFileOpenFromFd,继续执行allocateAuxStructures方法来生成DvmDex结构pDvmDex。代码位于dalvik\vm\DvmDex.c。

static DvmDex* allocateAuxStructures(DexFile* pDexFile)
{
    DvmDex* pDvmDex;
    const DexHeader* pHeader;
    u4 stringCount, classCount, methodCount, fieldCount;

    pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
    if (pDvmDex == NULL)
        return NULL;

    pDvmDex->pDexFile = pDexFile;
    pDvmDex->pHeader = pDexFile->pHeader;

    pHeader = pDvmDex->pHeader;

    stringCount = pHeader->stringIdsSize;
    classCount = pHeader->typeIdsSize;
    methodCount = pHeader->methodIdsSize;
    fieldCount = pHeader->fieldIdsSize;

    pDvmDex->pResStrings = (struct StringObject**)
        calloc(stringCount, sizeof(struct StringObject*));

    pDvmDex->pResClasses = (struct ClassObject**)
        calloc(classCount, sizeof(struct ClassObject*));

    pDvmDex->pResMethods = (struct Method**)
        calloc(methodCount, sizeof(struct Method*));

    pDvmDex->pResFields = (struct Field**)
        calloc(fieldCount, sizeof(struct Field*));

    LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes\n",
        pDvmDex, stringCount, classCount, methodCount, fieldCount,
        (stringCount + classCount + methodCount + fieldCount) * 4);

    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);

    if (pDvmDex->pResStrings == NULL ||
        pDvmDex->pResClasses == NULL ||
        pDvmDex->pResMethods == NULL ||
        pDvmDex->pResFields == NULL ||
        pDvmDex->pInterfaceCache == NULL)
    {
        LOGE("Alloc failure in allocateAuxStructures\n");
        free(pDvmDex->pResStrings);
        free(pDvmDex->pResClasses);
        free(pDvmDex->pResMethods);
        free(pDvmDex->pResFields);
        free(pDvmDex);
        return NULL;
    }

    return pDvmDex;

}

然后返回到dvmDexFileOpenFromFd,执行sysCopyMap把DvmDex结构pDvmDex的memMap附上值,最后返回DvmDex结构pDvmDex。

执行完dvmDexFileOpenFromFd,返回到dvmJarFileOpen,生成了一个JarFile结构体ppJarFile,并赋值如下:

    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
    (*ppJarFile)->archive = archive;
    (*ppJarFile)->cacheFileName = cachedName;
    (*ppJarFile)->pDvmDex = pDvmDex;

在返回到Dalvik_dalvik_system_DexFile_openDexFile中,生成一个DexOrJar结构体pDexOrJar,并赋值如下:

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;

然后RETURN_PTR(pDexOrJar)返回给Java层的openDexFile,并赋值给mCookie,如下:

mCookie = openDexFile(fileName, null, 0);

下次通过mCookie,我们就能一步一步找到DexFile结构体(DexOrJar->JarFile->DvmDex->DexFile)。

0x03

最后附上一张DexFile的结构图:

图中有一处错误,baseAddr和pHeader其实都指向DexHeader,也就是dex文件头在内存中的虚拟地址。

时间: 2024-08-25 03:08:18

DexClassLoader和PathClassLoader加载Dex流程的相关文章

Android插件化开发之DexClassLoader动态加载dex、jar小Demo

一.温故动态加载ClassLoader机制 如果对Android的ClassLoader加载机制不熟悉,猛戳Android插件化开发动态加载基础之ClassLoader工作机制 http://blog.csdn.net/u011068702/article/details/53248960 二.介绍 我们知道在Android中可以跟java一样实现动态加载jar,但是Android使用德海Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik

Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)

一.前言 时隔半年,困扰的问题始终是需要解决的,之前也算是没时间弄,今天因为有人在此提起这个问题,那么就不能不解决了,这里写一篇文章记录一下吧.那么是什么问题呢? 就是关于之前的一个话题:Android中apk加固技术实现 关于这个问题,之前的一篇文章已经说过了,没有了解的同学可以点击这里:Android中apk加固技术实现 请务必仔细的看完这篇文章,不然今天说的内容会感觉很蛋疼的,因为今天的文章就是为了解决当初的加固技术遗留的问题,这里先大致来说一下加固apk的原理吧,先来看一张图: 看到这张

Android动态加载Dex机制解析

1.什么是类加载器? 类加载器(class loader)是 Java?中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中. Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例.每个这样的实例用来表示一个 Java 类.通过此实例的 newInstance()

关于apk加壳之动态加载dex文件

由于自己之前做了一个关于手机令牌的APK软件,在实现的过程中尽管使用了native so进行一定的逻辑算法保护,但是在自己逆向破解的过程中发现我的手机令牌关键数据能够“轻易地”暴露出来,所以我就想进一步的对其进行加固.于是,我使用的网上常用的梆梆加固.爱加密和阿里的聚安全应用来对我的apk进行一个加固保护.加固后,出于好奇心,我想对这些加固的原理进行一个了解,便于我自己能够实现这个加固的方法.于是开始了网上关于这方面的学习,我将这些加固的大致原理进行了一个总结,发现它们实现的最主要的方法就是利用

android源码解析(十九)--&gt;Dialog加载绘制流程

前面两篇文章,我们分析了Activity的布局文件加载.绘制流程,算是对整个Android系统中界面的显示流程有了一个大概的了解,其实Android系统中所有的显示控件(注意这里是控件,而不是组件)的加载绘制流程都是类似的,包括:Dialog的加载绘制流程,PopupWindow的加载绘制流程,Toast的显示原理等,上一篇文章中,我说在介绍了Activity界面的加载绘制流程之后,就会分析一下剩余几个控件的显示控制流程,这里我打算先分析一下Dialog的加载绘制流程. 可能有的同学问这里为什么

android源码解析(二十二)--&gt;Toast加载绘制流程

前面我们分析了Activity.Dialog.PopupWindow的加载绘制流程,相信大家对整个Android系统中的窗口绘制流程已经有了一个比较清晰的认识了,这里最后再给大家介绍一下Toast的加载绘制流程. 其实Toast窗口和Activity.Dialog.PopupWindow有一个不太一样的地方,就是Toast窗口是属于系统级别的窗口,他和输入框等类似的,不属于某一个应用,即不属于某一个进程,所以自然而然的,一旦涉及到Toast的加载绘制流程就会涉及到进程间通讯,看过前面系列文章的同

android源码解析(二十一)--&gt;PopupWindow加载绘制流程

在前面的几篇文章中我们分析了Activity与Dialog的加载绘制流程,取消绘制流程,相信大家对Android系统的窗口绘制机制有了一个感性的认识了,这篇文章我们将继续分析一下PopupWindow加载绘制流程. 在分析PopupWindow之前,我们将首先说一下什么是PopupWindow?理解一个类最好的方式就是看一下这个类的定义,这里我们摘要了一下Android系统中PopupWindow的类的说明: A popup window that can be used to display

利用DexClassLoader动态加载dex文件

Java中也有类加载器ClassLoader,其作用是动态装载Class文件,当我们从网络下载Class文件,或者在编译时不参与而在运行时动态调用时就需要用类加载器.由于Android对class文件进行了重新打包和优化,最终APK文件中包含的是dex文件,加载这种文件就需要用到DexClassLoader. DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent) dexPath:目标类所在的APK或者jar包,/.../

动态加载dex的两种方式

DexClassLoader 加载的类是没有组件生命周期的,也就是说即使DexClassLoader通过对dex的动态加载完成了对组件的加载,当系统启动该组件时,还会出现加载类失败的异常.有两种方式可以解决上面出现的问题: 方法一:http://blog.csdn.net/androidsecurity/article/details/8809542,更改系统的classloader使其为自定义的加载器. 特点:两个dex具有明显的分割线,第一个dex只起启动作用,后面不会出现第一个dex的类加