Dalvik下一代壳通用解决方案

Android核心技术:Dalvik 虚拟机移植指导 Dalvik 移植指导Dalvik虚拟机可以在很多平台上运行,这些平台的操作系统至少是一个运行着GNUC编译器的类似于UNIX的平台(Linux,BSD,Mac OS X)。本文档指导读者如何把Dalvik虚拟机移植到一个Linux平台上,本文档假定我们要移植的平台和目前android平台在代码架构上有一定的相似性,可以进行移植。核心库的移植核心库的源代码主要在Dalvik/libco 阅读详情

1. 追根溯源dvmDexFileOpenPartial脱壳点

首先,我们需要知道,Dex加载最后的逻辑实现几乎都是在BaseDexClassLoader

//  http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java#29
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
    
    ...
    
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        //这里就是设置parent的地方
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
    
    ...
}

然后进入到DexPathList

// http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java#85
public DexPathList(ClassLoader definingContext, String dexPath,
           String libraryPath, File optimizedDirectory) {
        
        ...
        
        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions);
        ...
}

再到 makeDexElements,它是创建Dex文件的真正方法.

//http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java#makeDexElements
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
        ArrayList<Element> elements = new ArrayList<Element>();
        /*
         * Open all files and load the (direct or contained) dex files
         * up front.
         */
        for (File file : files) {
            File zip = null;
            DexFile dex = null;
            String name = file.getName();

            if (name.endsWith(DEX_SUFFIX)) {
                // Raw dex file (not inside a zip/jar).
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: " + file, ex);
                }
            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                ...
            } else if (file.isDirectory()) {
                ...
            } else {
                System.logW("Unknown file type for: " + file);
            }

            if ((zip != null) || (dex != null)) {
                elements.add(new Element(file, false, zip, dex));
            }
        }

        return elements.toArray(new Element[elements.size()]);
    }

makeDexElements其实就是不断的去files中取出file,然后构建Element添加到elements中我们的dex文件,紧接着执行loadDexFile。去看看 loadDexFile方法.

// http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java#loadDexFile

private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }

继续跟踪loadDex方法.

// http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#141

 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);
    }

// http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#97

private DexFile(String sourceName, String outputName, int flags) throws IOException {
        ...
		
        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        guard.open("close");
       
    }

看到openDexFile就猜得到,这是打开dex文件的方法.

// http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#openDexFile
// sourceName : 加载路径
// outputName : 输出路径
// flags : 0
private static int openDexFile(String sourceName, String outputName,
        int flags) throws IOException {
        return openDexFileNative(new File(sourceName).getCanonicalPath(),
                                 (outputName == null) ? null : new File(outputName).getCanonicalPath(),
                                 flags);
    }

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

然后看到openDexFileNative也就知道是个native方法了. 所以我们需要去看c++代码。Android源码中native文件命名就是以native方法所在路径命名的

//http://androidxref.com/4.4.4_r1/xref/dalvik/vm/native/dalvik_system_DexFile.cpp

static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
    JValue* pResult)
{
    ...

    /*
     * Try to open it directly as a DEX if the name ends with ".dex".
     * If that fails (or isn't tried in the first place), try it as a
     * Zip with a "classes.dex" inside.
     */
    if (hasDexExtension(sourceName)
            && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
        ALOGV("Opening DEX file '%s' (DEX)", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = true;
        pDexOrJar->pRawDexFile = pRawDexFile;
        pDexOrJar->pDexMemory = NULL;
    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
        ...
    } else {
       ...
    }

    ...
}

hasDexExtension判断文件是否是dex后缀,dvmRawDexFileOpen就是去打开dex文件了

//http://androidxref.com/4.4.2_r2/xref/dalvik/vm/RawDexFile.cpp#109

int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
    RawDexFile** ppRawDexFile, bool isBootstrap)
{
    ...

    dexFd = open(fileName, O_RDONLY);
    ...
    //生成优化后的dex文件路径
    if (odexOutputName == NULL) {
        cachedName = dexOptGenerateCacheFileName(fileName, NULL);
        if (cachedName == NULL)
            goto bail;
    } else {
        cachedName = strdup(odexOutputName);
    }

    ...
    if (newFile) {
        u8 startWhen, copyWhen, endWhen;
        bool result;
        off_t dexOffset;

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

        if (result) {
            startWhen = dvmGetRelativeTimeUsec();
            result = copyFileToFile(optFd, dexFd, fileSize) == 0;
            copyWhen = dvmGetRelativeTimeUsec();
        }

        if (result) {
            //进入对dex文件的优化流程中
            result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
                fileName, modTime, adler32, isBootstrap);
        }

        if (!result) {
            ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
            goto bail;
        }

        endWhen = dvmGetRelativeTimeUsec();
        ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
            fileName,
            (int) (copyWhen - startWhen) / 1000,
            (int) (endWhen - copyWhen) / 1000);
    }

    ...
    return result;
}

来关注一下优化dex的函数dvmOptimizeDexFile

// http://androidxref.com/4.4.2_r2/xref/dalvik/vm/analysis/DexPrepare.cpp#351

bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    const char* lastPart = strrchr(fileName, '/');
    if (lastPart != NULL)
        lastPart++;
    else
        lastPart = fileName;

    ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);

    pid_t pid;

    /*
     * This could happen if something in our bootclasspath, which we thought
     * was all optimized, got rejected.
     */
    if (gDvm.optimizing) {
        ALOGW("Rejecting recursive optimization attempt on '%s'", fileName);
        return false;
    }

    pid = fork();
    if (pid == 0) {
        static const int kUseValgrind = 0;
        //需要调用优化的程序dexopt
        static const char* kDexOptBin = "/bin/dexopt";
        static const char* kValgrinder = "/usr/bin/valgrind";
        static const int kFixedArgCount = 10;
        static const int kValgrindArgCount = 5;
        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
        int bcpSize = dvmGetBootPathSize();
        int argc = kFixedArgCount + bcpSize
            + (kValgrindArgCount * kUseValgrind);
        const char* argv[argc+1];             // last entry is NULL
        char values[argc][kMaxIntLen];
        char* execFile;
        const char* androidRoot;
        int flags;

        /* change process groups, so we don't clash with ProcessManager */
        setpgid(0, 0);

        /* full path to optimizer */
        androidRoot = getenv("ANDROID_ROOT");
        if (androidRoot == NULL) {
            ALOGW("ANDROID_ROOT not set, defaulting to /system");
            androidRoot = "/system";
        }
        execFile = (char*)alloca(strlen(androidRoot) + strlen(kDexOptBin) + 1);
        strcpy(execFile, androidRoot);
        strcat(execFile, kDexOptBin);

        /*
         * Create arg vector.
         */
        int curArg = 0;

        if (kUseValgrind) {
            /* probably shouldn't ship the hard-coded path */
            argv[curArg++] = (char*)kValgrinder;
            argv[curArg++] = "--tool=memcheck";
            argv[curArg++] = "--leak-check=yes";        // check for leaks too
            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
            argv[curArg++] = "--num-callers=16";        // default is 12
            assert(curArg == kValgrindArgCount);
        }
        //拼接命令
        argv[curArg++] = execFile;

        argv[curArg++] = "--dex";

        sprintf(values[2], "%d", DALVIK_VM_BUILD);
        argv[curArg++] = values[2];

        sprintf(values[3], "%d", fd);
        argv[curArg++] = values[3];

        sprintf(values[4], "%d", (int) dexOffset);
        argv[curArg++] = values[4];

        sprintf(values[5], "%d", (int) dexLength);
        argv[curArg++] = values[5];

        argv[curArg++] = (char*)fileName;

        sprintf(values[7], "%d", (int) modWhen);
        argv[curArg++] = values[7];

        sprintf(values[8], "%d", (int) crc);
        argv[curArg++] = values[8];

        ...

        if (kUseValgrind)
            execv(kValgrinder, const_cast<char**>(argv));
        else
            //这里执行了/bin/dexopt程序
            execv(execFile, const_cast<char**>(argv));

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

此函数中会拼接命令行,然后调用execv是执行/bin/dexopt程序等待此程序去优化dex文件
它的源码就在/dalvik/dexopt/OptMain.cpp下
它的main函数如下

// http://androidxref.com/4.4.2_r2/xref/dalvik/dexopt/OptMain.cpp

int main(int argc, char* const argv[])
{
    ...

    if (argc > 1) {
        if (strcmp(argv[1], "--zip") == 0)
            return fromZip(argc, argv);
        else if (strcmp(argv[1], "--dex") == 0)
            return fromDex(argc, argv);
        else if (strcmp(argv[1], "--preopt") == 0)
            return preopt(argc, argv);
    }

    ...
    
    

    return 1;
}

此处肯定是调用了fromDex函数了

 // http://androidxref.com/4.4.2_r2/xref/dalvik/dexopt/OptMain.cpp#fromDex
 
static int fromDex(int argc, char* const argv[])
{
    ...

    /* do the optimization */
    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
    {
        ALOGE("Optimization failed");
        goto bail;
    }

    result = 0;

    ...
}

关注dvmContinueOptimization

bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    ...

    {
        /*
         * Map the entire file (so we don't have to worry about page
         * alignment).  The expectation is that the output file contains
         * our DEX data plus room for a small header.
         */
        bool success;
        void* mapAddr;
        //对当前dex进行内存映射
        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
                    MAP_SHARED, fd, 0);
        ...
        //重写dex
        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
                    doVerify, doOpt, &pClassLookup, NULL);

        if (success) {
            DvmDex* pDvmDex = NULL;
            u1* dexAddr = ((u1*) mapAddr) + dexOffset;

            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
                ALOGE("Unable to create DexFile");
                success = false;
            } else {
                /*
                 * If configured to do so, generate register map output
                 * for all verified classes.  The register maps were
                 * generated during verification, and will now be serialized.
                 */
                if (gDvm.generateRegisterMaps) {
                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
                    if (pRegMapBuilder == NULL) {
                        ALOGE("Failed generating register maps");
                        success = false;
                    }
                }

                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
                updateChecksum(dexAddr, dexLength, pHeader);

                dvmDexFileFree(pDvmDex);
            }
        }

       ...
    }

    ...
    return result;
}

发挥一目十行的能力, 看到rewriteDex


// http://androidxref.com/4.4.2_r2/xref/dalvik/vm/analysis/DexPrepare.cpp#rewriteDex

/**
第一个参数:dex的起始地址
第二个参数:dex的字节数
**/
static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
{
    DexClassLookup* pClassLookup = NULL;
    u8 prepWhen, loadWhen, verifyOptWhen;
    DvmDex* pDvmDex = NULL;
    bool result = false;
    const char* msgStr = "???";

    /* if the DEX is in the wrong byte order, swap it now */
    if (dexSwapAndVerify(addr, len) != 0)
        goto bail;

    /*
     * Now that the DEX file can be read directly, create a DexFile struct
     * for it.
     */
     //此函数也有我们需要的dex的起始地址和字节数
    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
        ALOGE("Unable to create DexFile");
        goto bail;
    }

    ...

    result = true;

bail:
   ...
    return result;
}

这个函数就有我们想要的东西了,就是dex的起始地址和dex的字节数
其中dvmDexFileOpenPartial方法就是一个脱壳点,其代码如下

// http://androidxref.com/4.4.2_r2/xref/dalvik/vm/DvmDex.cpp#146

int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
   ...

    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
   ...
    result = 0;

bail:
    return result;
}

于是乎我们可以修改这边的代码为:

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
	char dexfilepath[100] = {0};
	int pid = getpid();
	sprintf(dexfilepath,"/sdcard/%d_%d_dvmDexFileOpenPartial.dex",len,pid);
	//fopen
	int fd = open(dexfilepath,O_CREAT|O_RDWR,0666);
	if (fd>0){
		write(fd,addr,len);
		close(fd);
		
	}

	
    DvmDex* pDvmDex;
    DexFile* pDexFile;
    int parseFlags = kDexParseDefault;
    int result = -1;

    /* -- file is incomplete, new checksum has not yet been calculated
    if (gDvm.verifyDexChecksum)
        parseFlags |= kDexParseVerifyChecksum;
    */

    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
    if (pDexFile == NULL) {
        ALOGE("DEX parse failed");
        goto bail;
    }
    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        goto bail;
    }

    pDvmDex->isMappedReadOnly = false;
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}

2. 追根溯源dexFileParse脱壳点

// http://androidxref.com/4.4.2_r2/xref/dalvik/vm/DvmDex.cpp#146

int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
   ...

    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
   ...
    result = 0;

bail:
    return result;
}

关注dexFileParse, 它也能作为一个脱壳点.

// http://androidxref.com/4.4.2_r2/xref/dalvik/libdex/DexFile.cpp#289

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)) {
        ALOGE("too short to be a valid .dex");
        goto bail;      /* bad file format */
    }
    ...
}

修改这边的代码为:

DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
	char dexfilepath[100] = {0};
	int pid = getpid();
	sprintf(dexfilepath,"/sdcard/%d_%d_dexFileParse.dex",length,pid);
	//fopen
	int fd = open(dexfilepath,O_CREAT|O_RDWR,0666);
	if (fd>0){
		write(fd,data,length);
		close(fd);
		
	}
	
	
	
    DexFile* pDexFile = NULL;
    const DexHeader* pHeader;
    const u1* magic;
    int result = -1;

    if (length < sizeof(DexHeader)) {
        ALOGE("too short to be a valid .dex");
        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) {
            ALOGE("bad opt version (0x%02x %02x %02x %02x)",
                 magic[4], magic[5], magic[6], magic[7]);
            goto bail;
        }

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

        /* parse the optimized dex file tables */
        if (!dexParseOptData(data, length, pDexFile))
            goto bail;

        /* ignore the opt header and appended data from here on out */
        data += pDexFile->pOptHeader->dexOffset;
        length -= pDexFile->pOptHeader->dexOffset;
        if (pDexFile->pOptHeader->dexLength > length) {
            ALOGE("File truncated? stored len=%d, rem len=%d",
                pDexFile->pOptHeader->dexLength, (int) length);
            goto bail;
        }
        length = pDexFile->pOptHeader->dexLength;
    }

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

    if (!dexHasValidMagic(pHeader)) {
        goto bail;
    }

    /*
     * Verify the checksum(s).  This is reasonably quick, but does require
     * touching every byte in the DEX file.  The base checksum changes after
     * byte-swapping and DEX optimization.
     */
    if (flags & kDexParseVerifyChecksum) {
        u4 adler = dexComputeChecksum(pHeader);
        if (adler != pHeader->checksum) {
            ALOGE("ERROR: bad checksum (%08x vs %08x)",
                adler, pHeader->checksum);
            if (!(flags & kDexParseContinueOnError))
                goto bail;
        } else {
            ALOGV("+++ adler32 checksum (%08x) verified", adler);
        }

        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
        if (pOptHeader != NULL) {
            adler = dexComputeOptChecksum(pOptHeader);
            if (adler != pOptHeader->checksum) {
                ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
                    adler, pOptHeader->checksum);
                if (!(flags & kDexParseContinueOnError))
                    goto bail;
            } else {
                ALOGV("+++ adler32 opt checksum (%08x) verified", adler);
            }
        }
    }

    /*
     * Verify the SHA-1 digest.  (Normally we don't want to do this --
     * the digest is used to uniquely identify the original DEX file, and
     * can't be computed for verification after the DEX is byte-swapped
     * and optimized.)
     */
    if (kVerifySignature) {
        unsigned char sha1Digest[kSHA1DigestLen];
        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
                            kSHA1DigestLen;

        dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
        if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
            char tmpBuf1[kSHA1DigestOutputLen];
            char tmpBuf2[kSHA1DigestOutputLen];
            ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
            if (!(flags & kDexParseContinueOnError))
                goto bail;
        } else {
            ALOGV("+++ sha1 digest verified");
        }
    }

    if (pHeader->fileSize != length) {
        ALOGE("ERROR: stored file size (%d) != expected (%d)",
            (int) pHeader->fileSize, (int) length);
        if (!(flags & kDexParseContinueOnError))
            goto bail;
    }

    if (pHeader->classDefsSize == 0) {
        ALOGE("ERROR: DEX file has no classes in it, failing");
        goto bail;
    }

    /*
     * Success!
     */
    result = 0;

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

3.编译系统.

这边我使用的是ubuntu16.04的系统. 具体其他配置就不多说了.

操作步骤:

  1. 设置环境 [source build/envsetup.sh]
  2. 选择目标 [lunch]
  3. 这里编译的是nexus5, 所以选择 7 (aosp_hammerhead-userdebug) [7]
  4. 编译代码 -j4 意味着4线程, 根据自己cpu而定. [time make -j4]

4. 刷机

编译出来的文件在 ~/SourceCode/Android-4.4.4_r1/out/target/product/hammerhead
拿出 boot.img/cache.img/system.img/userdata.img

然后执行命令

fastboot flash boot boot.img
fastboot flash cache cache.img
fastboot flash system system.img
fastboot flash userdata userdata.img
RTC 实时时钟实验 如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个TR_CLK 周期中 RTC 产生一个中断(秒中断)。void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data) // 往备份区域写用户数据的函数 (这个函数的第一个参数就是寄存器的标号了,这个是通过宏定义定义的)存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。寄存器操作完成,并同步之后,设置秒钟中断。 阅读详情

相关推荐

基于Landsat-8数据的山西省吕梁市地表温度反演研究过程(大气校正法)

1.数据下载:本实验采用的数据是 ①从地理空间数据云下载的来自landsat8 卫星,云量<=1%,时间为2017年5月12日的山西省吕梁市影像数据。为全面覆盖吕梁市,选用了两片影像。 ②吕梁市边界矢量图 2.数据预处理 ...

weixin_45747376的博客 6601

Android 热修复实例

1.原理 JAVA虚拟机JVM在运行时,加载的是.classes的字节码文件。Android有自己的虚拟机Dalvik/ART虚拟机,他们加载的是dex文件,但是他们的工作原理都一样,都是经过ClassLoader类加载器。Android在ClassLoader的基础上又定义类DexClassLoader用来加载jar、apk、dex文件、加载jar、apk也是最终抽取里面的Dex文件进行加载. 2.实例 (1)核心类:FixDexUtil package com....

zhang106209的博客 334

ubuntu20.04开机黑屏卡在/dev/sda1:clean,***files,***blocks,解决心得记录

这个问题如果你百度,会发现目前有很多种解决方法。 有更新显卡驱动的,有卸载上一次安装软件的,还有各种奇奇怪怪的故障原因。 如果你是属于上述情况,那么按照百度和论坛的各种指引操作一般都能解决。 但我这里主要说一种特殊情况,就是我什么也没有操作,我什么也没干。就是正常使用系统关了机突然就报错了。 这也是自己亲自遇到的问题,做个小记录方便以后自己回顾。 上一次出现这种情况我重装系统了,这次用了两个月又出现了,重装成本太高,我接受不了,于是找到解决办法。 首先找个终端进入,进入终端的方法很多,用安装u盘

qq_41517017的博客 2万+

android中出现at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)

前段时间做android项目 在xp系统下运行正常,在win7 64-bit和ubuntu12.04LTS下面始终出现如下异常: 总之运行程序是就直接奔溃。找了半天资料始终没法解决。万般无奈之下,点击Android Tools --> Add Suport Library 发现新生成了一个libs文件夹,于是毫不犹豫的把引用的jar从 lib下面拷贝到生成的libs下面。 运行通过。我也不知

9169

Vivado IP核与约束文件管理指南:解决OOC警告、COE文件丢失与Block Design复用

本文详细介绍了Vivado IP核与约束文件管理的系统化方法,包括解决OOC警告、COE文件丢失与Block Design复用等常见问题。通过IP核锁定机制、分层约束架构和动态约束技术,提升FPGA工程的健壮性和团队协作效率,特别适合中大型项目开发。

weixin_30887919的博客 430

ubuntu20.4源码安装最新gcc(gcc V12.2)

Ubuntu20.4自带的gcc为V9.4.0的版本,老版本的gcc存在不支持新版本C语言规范,在配置、编译、安装应用软件或工具时,可能存在不能正确安装的问题。(比如:libpqxx-7.7.4在配置时要求c++17版本的支持,但gcc V9.4.0版本不支持c++17)。因此本文介绍ubuntu20.4的系统下载最新gcc12.2版本源码,并进行本地编译、安装。本节内容包括gcc12.2的必备库安装和gcc12.2安装两部分内容组成。

q544344318的博客 1万+

关于DexClassLoader和PathClassLoader,以及Dalvik加载类的过程

android中,dalvik虚拟机加载的是dex文件,用于加载类的ClassLoader是PathClassLoader和DexClassLoader。PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,它们的父ClassLoader为BootClassLoader。 回顾在JVM中,自定义的ClassLoader一般直接继承自ClassLo...

Francisco_c82的博客 1440

IDEA 导入 jbolt项目(实验室非公开项目)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录准备一、准备数据库二、IDEA导入jbolt项目 准备 在实验室群文件中下载jbolt.zip和mysql.sql两个文件 将其解压到自己的目录下 一、准备数据库 打开Navicat新建一个jbolt数据库(字符集为utf8) 导入mysql.sql文件 数据库导入完毕 二、IDEA导入jbolt项目 点击打开一个项目 选择jbolt文件夹 以Maven形式打开 打开pom.xml 在里面添加jetty-server依赖

weixin_44094842的博客 976

android 之解决Accessing hidden method问题

android 之解决Accessing hidden method问题

qq_27840511的博客 4477

Android 10.0 SystemUI 屏蔽状态栏消息推送弹出的悬浮通知

在状态栏中,有一些通知类似 消息推送 弹出的 悬浮通知,弹出30秒之后会消失掉 要想屏蔽掉这些通知,首选知道这些通知是怎么产生的 通知类型 NotificationContentInflater.java 详细的介绍各种通知的类型 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java /** * The defau

安卓兼职framework和app工程师的博客 1542

Vue3 - 详细实现将多个文件批量导出为ZIP压缩包格式并下载功能,vue3将文件批量下载打包成ZIP下载保存本地,下载多个文件打包输出成zip格式(支持二进制文件流/base64图片/文件URL)

vue3,nuxt3,vue3批量导出文件打包成压缩包,Vue3中如何批量导出文件(图片/PDF)并打包压缩成ZIP,实现多张图片导出zip文件,vue3导出pdf文件很多如何变成导出一个zip文件,jszip插件文档流,vue3中下载多个文件打包为一个zip,Vue3中将文件批量下载打包成zip格式,批量下载图片并打包成压缩包,vue3导出多个pdf或excel打包成zip压缩包进行下载,批量下载文件并打压缩包,vue3中如何批量下载文件并生成zip格式文件,文件通过服务端接口返回二进制文件流如何处理,文

🏆 前端领域优质创作者 1万+

BaseDexClassLoader的正确使用方式

BaseDexClassLoader的正确使用方式

分享+记录 1381

用 SAP ABAP ALV 重塑 SM30:可扩展的自定义表维护视图与自动传输实践

文章摘要: 本文提出了一种基于ALV Grid的动态表维护框架,用于替代传统SM30维护视图。该方案通过输入表名自动生成维护界面,支持动态筛选、批量操作和自动传输管理。核心设计包括:利用BAdI实现扩展点、通过行状态字段简化数据变更跟踪、使用Style Table控制单元格编辑权限、集成CTS实现自动传输。相比SM30标准维护,该框架显著提升了交互体验(支持ALV排序/过滤/汇总)和业务扩展性(统一校验与权限控制),同时解决了"维护+传输"流程割裂的问题。文中详细阐述了技术实现方案,并提

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 318

第十二章 软件壳(四)(代码抽取型壳)

文章目录代码抽取型壳内存重组脱壳法Hook 脱壳法系统定制脱壳法脱壳工具 代码抽取型壳 即第二代壳 主要特点 即使 DEX 已加载到内存,仍处于加密状态(所有 DEX 方法都在运行时解密) 比第一代壳难脱 内存重组脱壳法 代码抽取型壳经历多次技术迭代 最初是将 DEX 的 DexCode 提取后填 0,将 DEX 的所有内容保存在 APK 中,APK 运行时会在内存中动态解密,所有解密的方法内容指针位于 DEX 文件结构体外部的内存中,从而有效避免了只知道 DEX 的起始地址即可快速 D

zlmm741的博客 1845

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

什么是Dalvik虚拟机

Google于2007年底正式发布了Android SDK,作为Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野。它对内存的高效使用,和在低速CPU上表现出的高性能,确实令人刮目相看。依赖于底层Posix兼容的操作系统,它可以简单的完成进程隔离和线程管理。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。 很多人认为D

又见水出处的专栏 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android Dalvik虚拟机概述

Dalvik虚拟机概述

Apple_hsp的专栏 1651

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Dalvik和ART的区别

Dalvik和ART的区别 Dalvik环境下,应用每次运行时,字节码都需要通过即时编译器(Just In Time,JIT)转换为机器码。ART环境中,应用会在安装的时候,就将字节码预编译(Ahead of Time,AOT)成机器码,使其成为真正的本地应用。 ATR占用的空间比Dalvik大,就是用空间换时间,大概多占用内存10%~20%。 ART不用每次运行时都重复编译,减少了CPU的使用频率,降低了能耗。 ...

xuyin1204的博客 3242

Dalvik虚拟机为新创建对象分配内存的过程分析

在前面一文中,我们分析了Dalvik虚拟机创建Java堆的过程。有了Java堆之后,Dalvik虚拟机就可以在上面为对象分配内存了。在Java堆为对象分配内存需要解决内存碎片和内存不足两个问题。要解决内存碎片问题,就要找到一块大小最合适的空闲内存分配给对象使用。而内存不足有可能是内存配额用完引起的,也有可能是垃圾没有及时回收引起的,要区别对待。本文就详细分析Dalvik虚拟机是如何解决这些问题的。

老罗的Android之旅 2万+

JVM和Dalvik的区别

Dalvik Dalvik虚拟机是Google等厂商合作开发的Android移-动设备平台的核心组成部分之一。 它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。(dx 是一套工具,可以将 Java .class 转换成 .dex 格式.

luojiayuan4523的专栏 379

android的Dalvik虚拟机简介

Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野。它对内存的高效使用,和在低速CPU上表现出的高性能,确实令人刮目相看。依赖于底层Posix兼容的操作系统,它可以简单的完成进程隔离和线程管理。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释...

weixin_30911451的博客 253

How dalvik call external library ?

/* *Copyright(C)2008TheAndroidOpenSourceProject * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotuset...

cicuinie0996的博客 159

Dalvik

The Dalvik virtual machine is intended to run on a variety of platforms.The baseline system is expected to be a variant of UNIX (Linux, BSD, MacOS X) running the GNU C compiler. Little-endian CPUs hav

Kuangjp的专栏 1571

虚拟机Dalvik

Google的Android SDK的发布 ,关于它的API以及在移动电话领域所带来的预期影响这些方面的讨论不胜枚举。不过,其中的一个话题在Java社区是一石激起千层浪,这就是Android平台 的基础——Dalvik虚拟机 。 Dalvik和标准Java虚拟机(JVM)之间的首要差别之一,就是Dalvik基于寄存器,而JVM基于栈。一直以来都有人在猜测,选择基于寄存器的方式是因为它对提前优化(ahead-of-time optimization)提供了更好的支持 ,而这对类似于移动电话

ironurbane的专栏 92

Dalvik虚拟机、ART虚拟机与JVM的核心区别

​​:寄存器架构与栈架构的本质差异导致Dalvik在移动端更高效,但JVM的跨平台能力不可替代。​​:AOT预编译彻底解决JIT的运行时开销,但牺牲存储空间换取性能飞跃。​​适用场景​​:JVM适合服务器和桌面应用,Dalvik/ART专为Android移动生态优化。ART的混合编译模式(AOT+JIT)平衡了安装时间与运行效率,成为Android 5.0+的性能基石。

qq_45906596的博客 1083

dalvik 字节码解析

  以下为从官网复制的一份,方便不翻墙查看。官方地址:https://source.android.com/devices/tech/dalvik/instruction-formatshttps://source.android.com/devices/tech/dalvik/dalvik-bytecodeDalvik 字节码通用设计·        机器模型和调用规范旨在大致模仿常见的真实架构...

peikm的博客 1988

Android的Dalvik和ART

Dalvik和ART的比较

月笼寒沙 1058
上一篇: 一二三代壳和加壳技术分类识别
下一篇: Js逆向之猿人学比赛第一题
Qiled
博客等级 码龄11年 1053粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值