栗子在此

步骤

附加进程后,在libdvm.so中的dvmDexFileOpenPartial函数下断点.

dvmDexFileOpenPartial的函数原型为(位于/dalvik/vm/DvmDex.cpp):

1
2
3
4
5
6
/*
* @addr: 传入参数, dex文件的起始地址
* @len: 传出参数, dex文件的大小
* @ppDvmDex: 传出参数,指向 DvmDex 结构的地址
*/
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)

运行后断下:

得R0为addr,R1为len.

执行脚本:

现在就可以使用baksmali处理dex文件继续分析了.

原理

dalvik虚拟机会把dex文件优化为odex文件,而优化的源代码为/dalvik/dexopt/OptMain.cpp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* Main entry point. Decide where to go.
*/
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);
}
...
}

其中fromzippreopt都会调用processZipFile先将dex文件提取出来,fromDex则直接调用dvmContinueOptimization优化.

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* Common functionality for normal device-side processing as well as
* preoptimization.
*/
static int processZipFile(int zipFd, int cacheFd, const char* zipName,
const char *dexoptFlags)
{
...
int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
bcp, dexoptFlags);
free(bcpCopy);
return result;
}

extractAndProcessZip中处理zip文件并将dex提取出来,随后调用优化函数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
* Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
* up front for the DEX optimization header.
*/
static int extractAndProcessZip(int zipFd, int cacheFd,
const char* debugFileName, bool isBootstrap, const char* bootClassPath,
const char* dexoptFlagStr)
{
...
/*
* Open the zip archive, find the DEX entry.
*/
if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
goto bail;
}
zipEntry = dexZipFindEntry(&zippy, kClassesDex);
if (zipEntry == NULL) {
ALOGW("DexOptZ: zip archive '%s' does not include %s",
debugFileName, kClassesDex);
goto bail;
}
/*
* Extract some info about the zip entry.
*/
if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
&modWhen, &crc32) != 0)
{
ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName);
goto bail;
}
uncompLen = uncompLen;
modWhen = modWhen;
crc32 = crc32;
/*
* Extract the DEX data into the cache file at the current offset.
*/
if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
ALOGW("DexOptZ: extraction of %s from %s failed",
kClassesDex, debugFileName);
goto bail;
}
...
/*
* Prep the VM and perform the optimization.
*/
if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
dexoptFlags) != 0)
{
ALOGE("DexOptZ: VM init failed");
goto bail;
}
//vmStarted = 1;
/* do the optimization */
if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
modWhen, crc32, isBootstrap))
{
ALOGE("Optimization failed");
goto bail;
}
}

dvmContinueOptimization函数位于/dalvik/vm/analysis/DexPrepare.cpp文件,其中调用了dvmDexFileOpenPartial.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* Do the actual optimization. This is executed in the dexopt process.
*
* For best use of disk/memory, we want to extract once and perform
* optimizations in place. If the file has to expand or contract
* to match local structure padding/alignment expectations, we want
* to do the rewrite as part of the extract, rather than extracting
* into a temp file and slurping it back out. (The structure alignment
* is currently correct for all platforms, and this isn't expected to
* change, so we should be okay with having it already extracted.)
*
* Returns "true" on success.
*/
bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
...
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 {
...
}
}
...
}

dvmDexFileOpenPartial调用了dexFileParse,用来解析内存中优化过或未优化过的dex文件,返回dexFile结构.
所以此时dex文件已经被加载进内存,就可以dump出来了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* Create a DexFile structure for a "partial" DEX. This is one that is in
* the process of being optimized. The optimization header isn't finished
* and we won't have any of the auxillary data tables, so we have to do
* the initialization slightly differently.
*
* Returns nonzero on error.
*/
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
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;
}
}