diff --git a/src/vdex/vdex_027.c b/src/vdex/vdex_027.c new file mode 100644 index 0000000..3c7c681 --- /dev/null +++ b/src/vdex/vdex_027.c @@ -0,0 +1,220 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#include "vdex_027.h" + +#include "../out_writer.h" +#include "../utils.h" +#include "vdex_backend_027.h" + +bool vdex_027_isMagicValid(const u1 *cursor) { + const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; + return (memcmp(pVdexHeader->magic, kVdexMagic, sizeof(kVdexMagic)) == 0); +} + +bool vdex_027_IsVdexVersionValid(const u1 *cursor) { + const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; + return (memcmp(pVdexHeader->vdexVersion, kVdexVersion_027, sizeof(kVdexVersion_027)) == 0); +} + +bool vdex_027_isValidVdex(const u1 *cursor) { + return vdex_027_isMagicValid(cursor) && vdex_027_IsVdexVersionValid(cursor); +} + +const vdexSectionHeader_027 *vdex_027_GetSectionHeader(const u1 *cursor, u4 index) { + const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; + CHECK_LT(index, pVdexHeader->numberOfSections); + return (vdexSectionHeader_027 *)(cursor + sizeof(vdexHeader_027) + index * + sizeof(vdexSectionHeader_027)); +} + +bool vdex_027_hasDexSection(const u1 *cursor) { + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); + return pSectHeader->sectionSize != 0u; +} + +u4 vdex_027_GetNumberOfDexFiles(const u1 *cursor) { + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kChecksumSection); + return pSectHeader->sectionSize / sizeof(VdexChecksum); +} + +const VdexChecksum *vdex_027_GetDexChecksumsArray(const u1 *cursor) { + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kChecksumSection); + return (VdexChecksum *)(cursor + pSectHeader->sectionOffset); +} + +const u1 *vdex_027_DexBegin(const u1 *cursor) { + CHECK(vdex_027_hasDexSection(cursor)); + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); + return cursor + pSectHeader->sectionOffset; +} + +u4 vdex_027_DexBeginOffset(const u1 *cursor) { + CHECK(vdex_027_hasDexSection(cursor)); + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); + return pSectHeader->sectionOffset; +} + +const u1 *vdex_027_DexEnd(const u1 *cursor) { + CHECK(vdex_027_hasDexSection(cursor)); + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); + return vdex_027_DexBegin(cursor) + pSectHeader->sectionSize; +} + +u4 vdex_027_DexEndOffset(const u1 *cursor) { + CHECK(vdex_027_hasDexSection(cursor)); + const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); + return vdex_027_DexBeginOffset(cursor) + pSectHeader->sectionSize; +} + +u4 vdex_027_GetLocationChecksum(const u1 *cursor, u4 fileIdx) { + CHECK_LT(fileIdx, vdex_027_GetNumberOfDexFiles(cursor)); + u4 *checksums = (u4 *)vdex_027_GetDexChecksumsArray(cursor); + return checksums[fileIdx]; +} + +void vdex_027_SetLocationChecksum(const u1 *cursor, u4 fileIdx, u4 value) { + CHECK_LT(fileIdx, vdex_027_GetNumberOfDexFiles(cursor)); + u4 *checksums = (u4 *)vdex_027_GetDexChecksumsArray(cursor); + checksums[fileIdx] = value; +} + +void vdex_027_dumpHeaderInfo(const u1 *cursor) { + const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; + u4 numberOfDexFiles = vdex_027_GetNumberOfDexFiles(cursor); + const vdexSectionHeader_027 *pDepsSectHeader = vdex_027_GetSectionHeader(cursor, + kVerifierDepsSection); + const vdexSectionHeader_027 *pTypeSectHeader = vdex_027_GetSectionHeader(cursor, + kTypeLookupTableSection); + const vdexSectionHeader_027 *pDexSectHeader = vdex_027_GetSectionHeader(cursor, + kDexFileSection); + + LOGMSG_RAW(l_DEBUG, "------ Vdex Header Info -------\n"); + LOGMSG_RAW(l_DEBUG, "magic header : %.4s\n", pVdexHeader->magic); + LOGMSG_RAW(l_DEBUG, "vdex version : %.4s\n", pVdexHeader->vdexVersion); + LOGMSG_RAW(l_DEBUG, "number of dex files : %" PRIx32 " (%" PRIu32 ")\n", + numberOfDexFiles, numberOfDexFiles); + LOGMSG_RAW(l_DEBUG, "dex file section size : %" PRIx32 " (%" PRIu32 ")\n", + pDexSectHeader->sectionSize, pDexSectHeader->sectionSize); + LOGMSG_RAW(l_DEBUG, "dex file section offset : %" PRIx32 " (%" PRIu32 ")\n", + pDexSectHeader, pDexSectHeader->sectionOffset); + LOGMSG_RAW(l_DEBUG, "verifier dependencies size : %" PRIx32 " (%" PRIu32 ")\n", + pDepsSectHeader->sectionSize, pDepsSectHeader->sectionSize); + LOGMSG_RAW(l_DEBUG, "verifier dependencies offset : %" PRIx32 " (%" PRIu32 ")\n", + pDepsSectHeader->sectionOffset, pDepsSectHeader->sectionOffset); + LOGMSG_RAW(l_DEBUG, "type lookup table size : %" PRIx32 " (%" PRIu32 ")\n", + pTypeSectHeader->sectionSize, pTypeSectHeader->sectionSize); + LOGMSG_RAW(l_DEBUG, "type lookup table offset : %" PRIx32 " (%" PRIu32 ")\n", + pTypeSectHeader->sectionOffset, pTypeSectHeader->sectionOffset); + if (vdex_027_hasDexSection(cursor)) { + LOGMSG_RAW(l_DEBUG, "dex files info :\n"); + for (u4 i = 0; i < numberOfDexFiles; ++i) { + LOGMSG_RAW(l_DEBUG, " [%" PRIu32 "] location checksum : %" PRIx32 " (%" PRIu32 ")\n", i, + vdex_027_GetLocationChecksum(cursor, i), vdex_027_GetLocationChecksum(cursor, i)); + } + } + LOGMSG_RAW(l_DEBUG, "---- EOF Vdex Header Info ----\n"); +} + +const u1 *vdex_027_GetNextDexFileData(const u1 *vdexCursor, u4 *curDexEndOff) { + if (*curDexEndOff == 0) { + if (vdex_027_hasDexSection(vdexCursor)) { + // dex[0] + const u1 *dexBuf = vdex_027_DexBegin(vdexCursor); + LOGMSG(l_DEBUG, "Processing first Dex file at offset:0x%x", dexBuf - vdexCursor); + + // Adjust curDexEndOff to point at the end of the current Dex + *curDexEndOff = dexBuf - vdexCursor + dex_getFileSize(dexBuf); + + return dexBuf; + } else { + LOGMSG(l_ERROR, "Vdex file has no Dex entries to process"); + return NULL; + } + } else { + // dex[i] + const u1 *dexBuf = vdexCursor + *curDexEndOff; + + // Dex files are required to be 4 byte aligned + // dexBuf = (u1*)utils_allignUp((uintptr_t)dexBuf, 4); // TODO: We shouldn't need to repair + if ((uintptr_t)dexBuf & 0x3) { + LOGMSG(l_ERROR, "Dex file in offset '0x%x' is not 4 byte aligned", *curDexEndOff); + return NULL; + } + + // Check boundaries + const u1 *dexBufMax = dexBuf + dex_getFileSize(dexBuf); + if (dexBufMax == vdex_027_DexEnd(vdexCursor)) { + LOGMSG(l_DEBUG, "Processing last Dex file at offset:0x%x", *curDexEndOff); + } else if (dexBufMax < vdex_027_DexEnd(vdexCursor)) { + LOGMSG(l_DEBUG, "Processing Dex file at offset:0x%x", *curDexEndOff); + } else { + LOGMSG(l_ERROR, "Invalid cursor offset '0x%x'", *curDexEndOff); + return NULL; + } + + // Adjust curDexEndOff to point at the end of the current Dex + *curDexEndOff += dex_getFileSize(dexBuf); + + return dexBuf; + } +} + +bool vdex_027_SanityCheck(const u1 *cursor, size_t bufSz) { + // Check that verifier deps section doesn't point past the end of file. We expect at least one + // byte (the number of entries) per struct. + const vdexSectionHeader_027 *pDepsSectHeader = vdex_027_GetSectionHeader(cursor, + kVerifierDepsSection); + if (pDepsSectHeader->sectionOffset && pDepsSectHeader->sectionSize && + ((pDepsSectHeader->sectionOffset + 7) > bufSz)) { + LOGMSG(l_ERROR, + "Verifier dependencies section points past the end of file (%" PRIx32 " + %" PRIx32 + " > %" PRIx32 ")", + pDepsSectHeader->sectionOffset, pDepsSectHeader->sectionSize, bufSz); + return false; + } + + return true; +} + +int vdex_027_process(const char *VdexFileName, + const u1 *cursor, + size_t bufSz, + const runArgs_t *pRunArgs) { + // Update Dex disassembler engine status + dex_setDisassemblerStatus(pRunArgs->enableDisassembler); + + // Measure time spend to process all Dex files of a Vdex file + struct timespec timer; + utils_startTimer(&timer); + + // Process Vdex file + int ret = vdex_backend_027_process(VdexFileName, cursor, bufSz, pRunArgs); + + // Get elapsed time in ns + long timeSpend = utils_endTimer(&timer); + LOGMSG(l_DEBUG, "Took %ld ms to process Vdex file", timeSpend / 1000000); + + return ret; +} + +void vdex_027_dumpDepsInfo(const u1 *vdexFileBuf) { vdex_backend_027_dumpDepsInfo(vdexFileBuf); } diff --git a/src/vdex/vdex_027.h b/src/vdex/vdex_027.h new file mode 100644 index 0000000..9a378e1 --- /dev/null +++ b/src/vdex/vdex_027.h @@ -0,0 +1,160 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _VDEX_027_H_ +#define _VDEX_027_H_ + +#include "../common.h" +#include "../dex.h" +#include "vdex_common.h" + +static const u1 kVdexVersion_027[] = { '0', '2', '7', '\0' }; +static const u4 kNumberOfSections_027 = 4; + +// VDEX files contain extracted DEX files. The VdexFile class maps the file to +// memory and provides tools for accessing its individual sections. +// +// In the description below, D is the number of dex files. +// +// File format: +// VdexFileHeader fixed-length header +// VdexSectionHeader[kNumberOfSections] +// +// Checksum section +// VdexChecksum[D] +// +// Optionally: +// DexSection +// DEX[0] array of the input DEX files +// DEX[1] +// ... +// DEX[D-1] +// +// VerifierDeps +// 4-byte alignment +// uint32[D] DexFileDeps offsets for each dex file +// DexFileDeps[D][] verification dependencies +// 4-byte alignment +// uint32[class_def_size] TypeAssignability offsets (kNotVerifiedMarker for a class +// that isn't verified) +// uint32 Offset of end of AssignabilityType sets +// uint8[] AssignabilityType sets +// 4-byte alignment +// uint32 Number of strings +// uint32[] String data offsets for each string +// uint8[] String data + +typedef struct __attribute__((packed)) { + u1 magic[4]; + u1 vdexVersion[4]; + u4 numberOfSections; +} vdexHeader_027; + +typedef struct __attribute__((packed)) { + u4 sectionKind; + u4 sectionOffset; + u4 sectionSize; +} vdexSectionHeader_027; + +typedef struct __attribute__((packed)) { + vdexHeader_027 *pVdexHeader; + dexHeader *pDexFiles; +} vdexFile_027; + +typedef struct __attribute__((packed)) { + u4 numberOfStrings; + const char **strings; +} vdexDepStrings_027; + +typedef struct __attribute__((packed)) { + u4 dstIndex; + u4 srcIndex; +} vdexDepSet_027; + +typedef struct __attribute__((packed)) { + u2 typeIdx; + u2 accessFlags; +} vdexDepClassRes_027; + +typedef struct __attribute__((packed)) { + u4 numberOfEntries; + vdexDepSet_027 *pVdexDepSets; +} vdexDepTypeSet_027; + +typedef struct __attribute__((packed)) { + u4 fieldIdx; + u2 accessFlags; + u4 declaringClassIdx; +} vdexDepFieldRes_027; + +typedef struct __attribute__((packed)) { + u4 methodIdx; + u2 accessFlags; + u4 declaringClassIdx; +} vdexDepMethodRes_027; + +typedef struct __attribute__((packed)) { + u2 typeIdx; +} vdexDepUnvfyClass_027; + +typedef struct __attribute__((packed)) { + u4 numberOfEntries; + vdexDepClassRes_027 *pVdexDepClasses; +} vdexDepClassResSet_027; + +typedef struct __attribute__((packed)) { + u4 numberOfEntries; + vdexDepFieldRes_027 *pVdexDepFields; +} vdexDepFieldResSet_027; + +typedef struct __attribute__((packed)) { + u4 numberOfEntries; + vdexDepMethodRes_027 *pVdexDepMethods; +} vdexDepMethodResSet_027; + +typedef struct __attribute__((packed)) { + u4 numberOfEntries; + vdexDepUnvfyClass_027 *pVdexDepUnvfyClasses; +} vdexDepUnvfyClassesSet_027; + +// Verify if valid Vdex file +bool vdex_027_isValidVdex(const u1 *); +bool vdex_027_isMagicValid(const u1 *); +bool vdex_027_isVersionValid(const u1 *); + +const vdexSectionHeader_027 *vdex_027_GetSectionHeader(const u1 *, u4); +bool vdex_027_hasDexSection(const u1 *); +u4 vdex_027_GetNumberOfDexFiles(const u1 *); +const u1 *vdex_027_DexBegin(const u1 *); +u4 vdex_027_DexBeginOffset(const u1 *); +const u1 *vdex_027_DexEnd(const u1 *); +u4 vdex_027_DexEndOffset(const u1 *); +const u1 *vdex_027_GetNextDexFileData(const u1 *, u4 *); +u4 vdex_027_GetLocationChecksum(const u1 *, u4); +void vdex_027_SetLocationChecksum(const u1 *, u4, u4); + +void vdex_027_dumpHeaderInfo(const u1 *); +void vdex_027_dumpDepsInfo(const u1 *); +bool vdex_027_SanityCheck(const u1 *, size_t); +int vdex_027_process(const char *, const u1 *, size_t, const runArgs_t *); + +#endif diff --git a/src/vdex/vdex_backend_021.c b/src/vdex/vdex_backend_021.c index 8e65643..088f1ea 100644 --- a/src/vdex/vdex_backend_021.c +++ b/src/vdex/vdex_backend_021.c @@ -27,19 +27,19 @@ #include "../utils.h" #include "vdex_decompiler_021.h" -const u4 *pCompactOffsetTable_21; -u4 compactOffsetMinOffset_21; -const u1 *pCompactOffsetDataBegin_21; +const u4 *pCompactOffsetTable_021; +u4 compactOffsetMinOffset_021; +const u1 *pCompactOffsetDataBegin_021; static inline int POPCOUNT(uintptr_t x) { return (sizeof(uintptr_t) == sizeof(u4)) ? __builtin_popcount(x) : __builtin_popcountll(x); } static void initCompactOffset(const u1 *cursor) { - pCompactOffsetDataBegin_21 = cursor + (2 * sizeof(u4)); - compactOffsetMinOffset_21 = ((u4 *)cursor)[0]; // First 4 bytes are are the minimum offset + pCompactOffsetDataBegin_021 = cursor + (2 * sizeof(u4)); + compactOffsetMinOffset_021 = ((u4 *)cursor)[0]; // First 4 bytes are are the minimum offset u4 tableOffset = ((u4 *)cursor)[1]; // Next 4 bytes are the table offset - pCompactOffsetTable_21 = (u4 *)(pCompactOffsetDataBegin_21 + tableOffset); + pCompactOffsetTable_021 = (u4 *)(pCompactOffsetDataBegin_021 + tableOffset); } // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the @@ -51,10 +51,10 @@ static const size_t kElementsPerIndex = 16; // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset // changes compared to the previous index. static u4 getOffset(u4 index) { - const u4 offset = pCompactOffsetTable_21[index / kElementsPerIndex]; + const u4 offset = pCompactOffsetTable_021[index / kElementsPerIndex]; const size_t bit_index = index % kElementsPerIndex; - const u1 *block = pCompactOffsetDataBegin_21 + offset; + const u1 *block = pCompactOffsetDataBegin_021 + offset; u2 bit_mask = *block; ++block; bit_mask = (bit_mask << kBitsPerByte) | *block; @@ -67,7 +67,7 @@ static u4 getOffset(u4 index) { // lebs we need to decode. size_t count = POPCOUNT((uintptr_t)(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index)); CHECK_GT(count, 0u); - u4 current_offset = compactOffsetMinOffset_21; + u4 current_offset = compactOffsetMinOffset_021; do { current_offset += dex_readULeb128(&block); --count; diff --git a/src/vdex/vdex_backend_027.c b/src/vdex/vdex_backend_027.c new file mode 100644 index 0000000..aade1b0 --- /dev/null +++ b/src/vdex/vdex_backend_027.c @@ -0,0 +1,499 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#include "vdex_backend_027.h" + +#include "../hashset/hashset.h" +#include "../out_writer.h" +#include "../utils.h" +#include "vdex_decompiler_027.h" + +static inline int POPCOUNT(uintptr_t x) { + return (sizeof(uintptr_t) == sizeof(u4)) ? __builtin_popcount(x) : __builtin_popcountll(x); +} + +static inline u4 decodeUint32WithOverflowCheck(const u1 **in, const u1 *end) { + CHECK_LT(*in, end); + return dex_readULeb128(in); +} + +static void decodeDepStrings(const u1 **in, const u1 *end, vdexDepStrings_027 *depStrings) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + depStrings->strings = utils_calloc(numOfEntries * sizeof(char *)); + depStrings->numberOfStrings = numOfEntries; + for (u4 i = 0; i < numOfEntries; ++i) { + CHECK_LT(*in, end); + const char *stringStart = (const char *)(*in); + depStrings->strings[i] = stringStart; + *in += strlen(stringStart) + 1; + } +} + +static void decodeDepTypeSet(const u1 **in, const u1 *end, vdexDepTypeSet_027 *pVdexDepTypeSet) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + pVdexDepTypeSet->pVdexDepSets = utils_malloc(numOfEntries * sizeof(vdexDepSet_027)); + pVdexDepTypeSet->numberOfEntries = numOfEntries; + for (u4 i = 0; i < numOfEntries; ++i) { + pVdexDepTypeSet->pVdexDepSets[i].dstIndex = decodeUint32WithOverflowCheck(in, end); + pVdexDepTypeSet->pVdexDepSets[i].srcIndex = decodeUint32WithOverflowCheck(in, end); + } +} + +static void decodeDepClasses(const u1 **in, + const u1 *end, + vdexDepClassResSet_027 *pVdexDepClassResSet) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + pVdexDepClassResSet->pVdexDepClasses = utils_malloc(numOfEntries * sizeof(vdexDepClassRes_027)); + pVdexDepClassResSet->numberOfEntries = numOfEntries; + for (u4 i = 0; i < numOfEntries; ++i) { + pVdexDepClassResSet->pVdexDepClasses[i].typeIdx = decodeUint32WithOverflowCheck(in, end); + pVdexDepClassResSet->pVdexDepClasses[i].accessFlags = decodeUint32WithOverflowCheck(in, end); + } +} + +static void decodeDepFields(const u1 **in, + const u1 *end, + vdexDepFieldResSet_027 *pVdexDepFieldResSet) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + pVdexDepFieldResSet->pVdexDepFields = utils_malloc(numOfEntries * sizeof(vdexDepFieldRes_027)); + pVdexDepFieldResSet->numberOfEntries = numOfEntries; + for (u4 i = 0; i < pVdexDepFieldResSet->numberOfEntries; ++i) { + pVdexDepFieldResSet->pVdexDepFields[i].fieldIdx = decodeUint32WithOverflowCheck(in, end); + pVdexDepFieldResSet->pVdexDepFields[i].accessFlags = decodeUint32WithOverflowCheck(in, end); + pVdexDepFieldResSet->pVdexDepFields[i].declaringClassIdx = + decodeUint32WithOverflowCheck(in, end); + } +} + +static void decodeDepMethods(const u1 **in, + const u1 *end, + vdexDepMethodResSet_027 *pVdexDepMethodResSet) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + pVdexDepMethodResSet->pVdexDepMethods = utils_malloc(numOfEntries * sizeof(vdexDepMethodRes_027)); + pVdexDepMethodResSet->numberOfEntries = numOfEntries; + for (u4 i = 0; i < numOfEntries; ++i) { + pVdexDepMethodResSet->pVdexDepMethods[i].methodIdx = decodeUint32WithOverflowCheck(in, end); + pVdexDepMethodResSet->pVdexDepMethods[i].accessFlags = decodeUint32WithOverflowCheck(in, end); + pVdexDepMethodResSet->pVdexDepMethods[i].declaringClassIdx = + decodeUint32WithOverflowCheck(in, end); + } +} + +static void decodeDepUnvfyClasses(const u1 **in, + const u1 *end, + vdexDepUnvfyClassesSet_027 *pVdexDepUnvfyClassesSet) { + u4 numOfEntries = decodeUint32WithOverflowCheck(in, end); + pVdexDepUnvfyClassesSet->pVdexDepUnvfyClasses = + utils_malloc(numOfEntries * sizeof(vdexDepUnvfyClass_027)); + pVdexDepUnvfyClassesSet->numberOfEntries = numOfEntries; + for (u4 i = 0; i < numOfEntries; ++i) { + pVdexDepUnvfyClassesSet->pVdexDepUnvfyClasses[i].typeIdx = + decodeUint32WithOverflowCheck(in, end); + } +} + +static const char *getStringFromId(const vdexDepData_027 *pVdexDepData, + u4 stringId, + const u1 *dexFileBuf) { + vdexDepStrings_027 extraStrings = pVdexDepData->extraStrings; + u4 numIdsInDex = dex_getStringIdsSize(dexFileBuf); + if (stringId < numIdsInDex) { + return dex_getStringDataByIdx(dexFileBuf, stringId); + } else { + // Adjust offset + stringId -= numIdsInDex; + CHECK_LT(stringId, extraStrings.numberOfStrings); + return extraStrings.strings[stringId]; + } +} + +static vdexDeps_027 *initDepsInfo(const u1 *vdexFileBuf) { + const vdexSectionHeader_027 *pDepsSectHeader = vdex_027_GetSectionHeader(vdexFileBuf, + kVerifierDepsSection); + if (pDepsSectHeader->sectionSize == 0) { + // Return early, as the first thing we expect from VerifierDeps data is + // the number of created strings, even if there is no dependency. + return NULL; + } + + vdexDeps_027 *pVdexDeps = utils_malloc(sizeof(vdexDeps_027)); + pVdexDeps->numberOfDexFiles = vdex_027_GetNumberOfDexFiles(vdexFileBuf); + pVdexDeps->pVdexDepData = utils_malloc(sizeof(vdexDepData_027) * pVdexDeps->numberOfDexFiles); + + const u1 *dexFileBuf = NULL; + u4 offset = 0; + + const u1 *depsDataStart = vdexFileBuf + pDepsSectHeader->sectionOffset; + const u1 *depsDataEnd = depsDataStart + pDepsSectHeader->sectionSize; + + for (u4 i = 0; i < pVdexDeps->numberOfDexFiles; ++i) { + dexFileBuf = vdex_027_GetNextDexFileData(vdexFileBuf, &offset); + if (dexFileBuf == NULL) { + LOGMSG(l_FATAL, "Failed to extract Dex file buffer from loaded Vdex"); + } + + // Process encoded extra strings + decodeDepStrings(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].extraStrings); + + // Process encoded assignable types + decodeDepTypeSet(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].assignTypeSets); + + // Process encoded unassignable types + decodeDepTypeSet(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].unassignTypeSets); + + // Process encoded classes + decodeDepClasses(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].classes); + + // Process encoded fields + decodeDepFields(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].fields); + + // Process encoded methods + decodeDepMethods(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].methods); + + // Process encoded unverified classes + decodeDepUnvfyClasses(&depsDataStart, depsDataEnd, &pVdexDeps->pVdexDepData[i].unvfyClasses); + } + CHECK_LE(depsDataStart, depsDataEnd); + return pVdexDeps; +} + +static bool hasDepsData(vdexDeps_027 *pVdexDeps) { + for (u4 i = 0; i < pVdexDeps->numberOfDexFiles; ++i) { + const vdexDepData_027 *pVdexDepData = &pVdexDeps->pVdexDepData[i]; + if (pVdexDepData->extraStrings.numberOfStrings > 0 || + pVdexDepData->assignTypeSets.numberOfEntries > 0 || + pVdexDepData->unassignTypeSets.numberOfEntries > 0 || + pVdexDepData->classes.numberOfEntries > 0 || pVdexDepData->fields.numberOfEntries > 0 || + pVdexDepData->methods.numberOfEntries > 0 || + pVdexDepData->unvfyClasses.numberOfEntries > 0) { + return true; + } + } + + return false; +} + +static void destroyDepsInfo(const vdexDeps_027 *pVdexDeps) { + for (u4 i = 0; i < pVdexDeps->numberOfDexFiles; ++i) { + free((void *)pVdexDeps->pVdexDepData[i].extraStrings.strings); + free((void *)pVdexDeps->pVdexDepData[i].assignTypeSets.pVdexDepSets); + free((void *)pVdexDeps->pVdexDepData[i].unassignTypeSets.pVdexDepSets); + free((void *)pVdexDeps->pVdexDepData[i].classes.pVdexDepClasses); + free((void *)pVdexDeps->pVdexDepData[i].fields.pVdexDepFields); + free((void *)pVdexDeps->pVdexDepData[i].methods.pVdexDepMethods); + free((void *)pVdexDeps->pVdexDepData[i].unvfyClasses.pVdexDepUnvfyClasses); + } + free((void *)pVdexDeps->pVdexDepData); + free((void *)pVdexDeps); +} + +void vdex_backend_027_dumpDepsInfo(const u1 *vdexFileBuf) { + // Not all Vdex files have Dex data to process + if (!vdex_027_hasDexSection(vdexFileBuf)) { + LOGMSG(l_DEBUG, "Vdex has no Dex data - skipping"); + return; + } + + // Initialize depsInfo structs + vdexDeps_027 *pVdexDeps = initDepsInfo(vdexFileBuf); + if (pVdexDeps == NULL) { + LOGMSG(l_WARN, "Malformed verified dependencies data"); + return; + } + + if (!hasDepsData(pVdexDeps)) { + LOGMSG(l_DEBUG, "Empty verified dependencies data"); + goto cleanup; + } + + log_dis("------- Vdex Deps Info -------\n"); + + const u1 *dexFileBuf = NULL; + u4 offset = 0; + for (u4 i = 0; i < pVdexDeps->numberOfDexFiles; ++i) { + const vdexDepData_027 *pVdexDepData = &pVdexDeps->pVdexDepData[i]; + log_dis("dex file #%" PRIu32 "\n", i); + dexFileBuf = vdex_027_GetNextDexFileData(vdexFileBuf, &offset); + if (dexFileBuf == NULL) { + LOGMSG(l_ERROR, "Failed to extract Dex file buffer from loaded Vdex"); + return; + } + + vdexDepStrings_027 strings = pVdexDepData->extraStrings; + log_dis(" extra strings: number_of_strings=%" PRIu32 "\n", strings.numberOfStrings); + for (u4 i = 0; i < strings.numberOfStrings; ++i) { + log_dis(" %04" PRIu32 ": '%s'\n", i, strings.strings[i]); + } + + vdexDepTypeSet_027 aTypes = pVdexDepData->assignTypeSets; + log_dis(" assignable type sets: number_of_sets=%" PRIu32 "\n", aTypes.numberOfEntries); + for (u4 i = 0; i < aTypes.numberOfEntries; ++i) { + log_dis(" %04" PRIu32 ": '%s' must be assignable to '%s'\n", i, + getStringFromId(pVdexDepData, aTypes.pVdexDepSets[i].srcIndex, dexFileBuf), + getStringFromId(pVdexDepData, aTypes.pVdexDepSets[i].dstIndex, dexFileBuf)); + } + + vdexDepTypeSet_027 unTypes = pVdexDepData->unassignTypeSets; + log_dis(" unassignable type sets: number_of_sets=%" PRIu32 "\n", unTypes.numberOfEntries); + for (u4 i = 0; i < unTypes.numberOfEntries; ++i) { + log_dis(" %04" PRIu32 ": '%s' must not be assignable to '%s'\n", i, + getStringFromId(pVdexDepData, unTypes.pVdexDepSets[i].srcIndex, dexFileBuf), + getStringFromId(pVdexDepData, unTypes.pVdexDepSets[i].dstIndex, dexFileBuf)); + } + + log_dis(" class dependencies: number_of_classes=%" PRIu32 "\n", + pVdexDepData->classes.numberOfEntries); + for (u4 i = 0; i < pVdexDepData->classes.numberOfEntries; ++i) { + u2 accessFlags = pVdexDepData->classes.pVdexDepClasses[i].accessFlags; + log_dis(" %04" PRIu32 ": '%s' '%s' be resolved with access flags '%" PRIu16 "'\n", i, + dex_getStringByTypeIdx(dexFileBuf, pVdexDepData->classes.pVdexDepClasses[i].typeIdx), + accessFlags == kUnresolvedMarker ? "must not" : "must", accessFlags); + } + + log_dis(" field dependencies: number_of_fields=%" PRIu32 "\n", + pVdexDepData->fields.numberOfEntries); + for (u4 i = 0; i < pVdexDepData->fields.numberOfEntries; ++i) { + vdexDepFieldRes_027 fieldRes = pVdexDepData->fields.pVdexDepFields[i]; + const dexFieldId *pDexFieldId = dex_getFieldId(dexFileBuf, fieldRes.fieldIdx); + log_dis(" %04" PRIu32 ": '%s'->'%s':'%s' is expected to be ", i, + dex_getFieldDeclaringClassDescriptor(dexFileBuf, pDexFieldId), + dex_getFieldName(dexFileBuf, pDexFieldId), + dex_getFieldTypeDescriptor(dexFileBuf, pDexFieldId)); + if (fieldRes.accessFlags == kUnresolvedMarker) { + log_dis("unresolved\n"); + } else { + log_dis("in class '%s' and have the access flags '%" PRIu16 "'\n", + getStringFromId(pVdexDepData, fieldRes.declaringClassIdx, dexFileBuf), + fieldRes.accessFlags); + } + } + + log_dis(" method dependencies: number_of_methods=%" PRIu32 "\n", + pVdexDepData->methods.numberOfEntries); + for (u4 i = 0; i < pVdexDepData->methods.numberOfEntries; ++i) { + const dexMethodId *pDexMethodId = + dex_getMethodId(dexFileBuf, pVdexDepData->methods.pVdexDepMethods[i].methodIdx); + u2 accessFlags = pVdexDepData->methods.pVdexDepMethods[i].accessFlags; + const char *methodSig = dex_getMethodSignature(dexFileBuf, pDexMethodId); + log_dis(" %04" PRIu32 ": '%s'->'%s':'%s' is expected to be ", i, + dex_getMethodDeclaringClassDescriptor(dexFileBuf, pDexMethodId), + dex_getMethodName(dexFileBuf, pDexMethodId), methodSig); + free((void *)methodSig); + if (accessFlags == kUnresolvedMarker) { + log_dis("unresolved\n"); + } else { + log_dis( + "in class '%s', have the access flags '%" PRIu16 "'\n", + getStringFromId(pVdexDepData, + pVdexDepData->methods.pVdexDepMethods[i].declaringClassIdx, dexFileBuf), + accessFlags); + } + } + + log_dis(" unverified classes: number_of_classes=%" PRIu32 "\n", + pVdexDepData->unvfyClasses.numberOfEntries); + for (u4 i = 0; i < pVdexDepData->unvfyClasses.numberOfEntries; ++i) { + log_dis(" %04" PRIu32 ": '%s' is expected to be verified at runtime\n", i, + dex_getStringByTypeIdx(dexFileBuf, + pVdexDepData->unvfyClasses.pVdexDepUnvfyClasses[i].typeIdx)); + } + } + log_dis("----- EOF Vdex Deps Info -----\n"); + +// Cleanup +cleanup: + destroyDepsInfo(pVdexDeps); +} + +int vdex_backend_027_process(const char *VdexFileName, + const u1 *cursor, + size_t bufSz, + const runArgs_t *pRunArgs) { + int ret = 0; + + // Basic size checks + if (!vdex_027_SanityCheck(cursor, bufSz)) { + LOGMSG(l_ERROR, "Malformed Vdex file"); + return -1; + } + + // Not all Vdex files have Dex data to process + if (!vdex_027_hasDexSection(cursor)) { + LOGMSG(l_DEBUG, "Vdex has no Dex data - skipping"); + return 0; + } + + u4 numberOfDexFiles = vdex_027_GetNumberOfDexFiles(cursor); + const u1 *dexFileBuf = NULL; + u4 offset = 0; + + // For each Dex file + for (size_t dex_file_idx = 0; dex_file_idx < numberOfDexFiles; ++dex_file_idx) { + dexFileBuf = vdex_027_GetNextDexFileData(cursor, &offset); + if (dexFileBuf == NULL) { + LOGMSG(l_ERROR, "Failed to extract 'classes%zu.dex' - skipping", dex_file_idx); + continue; + } + + // Check if valid Dex or CompactDex file + dex_dumpHeaderInfo(dexFileBuf); + if (!dex_isValidDex(dexFileBuf) && !dex_isValidCDex(dexFileBuf)) { + LOGMSG(l_ERROR, "'classes%zu.dex' is an invalid Dex file - skipping", dex_file_idx); + continue; + } + + // For each class + log_dis("file #%zu: classDefsSize=%" PRIu32 "\n", dex_file_idx, + dex_getClassDefsSize(dexFileBuf)); + for (u4 i = 0; i < dex_getClassDefsSize(dexFileBuf); ++i) { + const dexClassDef *pDexClassDef = dex_getClassDef(dexFileBuf, i); + + dex_dumpClassInfo(dexFileBuf, i); + + // Last read field or method index to apply delta to + u4 lastIdx = 0; + + // Cursor for currently processed class data item + const u1 *curClassDataCursor; + if (pDexClassDef->classDataOff == 0) { + continue; + } else { + curClassDataCursor = dex_getDataAddr(dexFileBuf) + pDexClassDef->classDataOff; + } + + dexClassDataHeader pDexClassDataHeader; + memset(&pDexClassDataHeader, 0, sizeof(dexClassDataHeader)); + dex_readClassDataHeader(&curClassDataCursor, &pDexClassDataHeader); + + // Skip static fields + for (u4 j = 0; j < pDexClassDataHeader.staticFieldsSize; ++j) { + dexField pDexField; + memset(&pDexField, 0, sizeof(dexField)); + dex_readClassDataField(&curClassDataCursor, &pDexField); + + // APIs are unhidden regardless if we're decompiling or not + dex_unhideAccessFlags((u1 *)curClassDataCursor, + dex_decodeAccessFlagsFromDex(pDexField.accessFlags), false); + } + + // Skip instance fields + for (u4 j = 0; j < pDexClassDataHeader.instanceFieldsSize; ++j) { + dexField pDexField; + memset(&pDexField, 0, sizeof(dexField)); + dex_readClassDataField(&curClassDataCursor, &pDexField); + + // APIs are unhidden regardless if we're decompiling or not + dex_unhideAccessFlags((u1 *)curClassDataCursor, + dex_decodeAccessFlagsFromDex(pDexField.accessFlags), false); + } + + // For each direct method + lastIdx = 0; + for (u4 j = 0; j < pDexClassDataHeader.directMethodsSize; ++j) { + dexMethod curDexMethod; + memset(&curDexMethod, 0, sizeof(dexMethod)); + dex_readClassDataMethod(&curClassDataCursor, &curDexMethod); + dex_dumpMethodInfo(dexFileBuf, &curDexMethod, lastIdx, "direct"); + + // APIs are unhidden regardless if we're decompiling or not + dex_unhideAccessFlags((u1 *)curClassDataCursor, + dex_decodeAccessFlagsFromDex(curDexMethod.accessFlags), true); + + // Skip empty methods + if (curDexMethod.codeOff == 0) { + // Update lastIdx since followings delta_idx are based on 1st elements idx + lastIdx += curDexMethod.methodIdx; + } else { + vdex_decompiler_027_walk(dexFileBuf, &curDexMethod); + } + } // EOF direct methods iterator + + // For each virtual method + lastIdx = 0; + for (u4 j = 0; j < pDexClassDataHeader.virtualMethodsSize; ++j) { + dexMethod curDexMethod; + memset(&curDexMethod, 0, sizeof(dexMethod)); + dex_readClassDataMethod(&curClassDataCursor, &curDexMethod); + dex_dumpMethodInfo(dexFileBuf, &curDexMethod, lastIdx, "virtual"); + + // APIs are unhidden regardless if we're decompiling or not + dex_unhideAccessFlags((u1 *)curClassDataCursor, + dex_decodeAccessFlagsFromDex(curDexMethod.accessFlags), true); + + // Skip native or abstract methods + if (curDexMethod.codeOff == 0) { + // Update lastIdx since followings delta_idx are based on 1st elements idx + lastIdx += curDexMethod.methodIdx; + } else { + vdex_decompiler_027_walk(dexFileBuf, &curDexMethod); + } + } // EOF virtual methods iterator + } + + // Some adjustments that are needed for the deduplicated shared data section + const u1 *dataBuf = NULL; + u4 dataSize = 0; + if (dex_checkType(dexFileBuf) == kCompactDex) { + cdexHeader *pCdexHeader = (cdexHeader *)dexFileBuf; + u4 mainSectionSize = pCdexHeader->fileSize; + u4 shared_section_size = pCdexHeader->dataSize; + const u1 *origDataAddr = dex_getDataAddr(dexFileBuf); + + // The shared section will be serialized right after the dex file. + pCdexHeader->dataOff = pCdexHeader->fileSize; + pCdexHeader->fileSize += shared_section_size; + + // Allocate a new map + const u1 *cdexBuf = utils_malloc(pCdexHeader->fileSize); + + // Copy main section + memcpy((void *)cdexBuf, dexFileBuf, mainSectionSize); + + // Copy data section + memcpy((void *)(cdexBuf + mainSectionSize), origDataAddr, shared_section_size); + + dataBuf = cdexBuf; + dataSize = pCdexHeader->fileSize; + } else { + dataBuf = dexFileBuf; + dataSize = dex_getFileSize(dexFileBuf); + } + + // Repair CRC if not decompiling so we can still run Dex parsing tools against output + dex_repairDexCRC(dataBuf, dataSize); + + if (!outWriter_DexFile(pRunArgs, VdexFileName, dex_file_idx, dataBuf, dataSize)) { + ret = -1; + } + + if (dex_checkType(dataBuf) == kCompactDex) { + free((void *)dataBuf); + } + + // Check if we have a cached error from current dexFile + if (ret != 0) { + return ret; + } + } // EOF of dex file iterator + + return numberOfDexFiles; +} diff --git a/src/vdex/vdex_backend_027.h b/src/vdex/vdex_backend_027.h new file mode 100644 index 0000000..5be4278 --- /dev/null +++ b/src/vdex/vdex_backend_027.h @@ -0,0 +1,48 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _VDEX_BACKEND_027_H_ +#define _VDEX_BACKEND_027_H_ + +#include "../common.h" +#include "../dex.h" +#include "vdex_027.h" + +typedef struct __attribute__((packed)) { + vdexDepStrings_027 extraStrings; + vdexDepTypeSet_027 assignTypeSets; + vdexDepTypeSet_027 unassignTypeSets; + vdexDepClassResSet_027 classes; + vdexDepFieldResSet_027 fields; + vdexDepMethodResSet_027 methods; + vdexDepUnvfyClassesSet_027 unvfyClasses; +} vdexDepData_027; + +typedef struct __attribute__((packed)) { + u4 numberOfDexFiles; + vdexDepData_027 *pVdexDepData; +} vdexDeps_027; + +void vdex_backend_027_dumpDepsInfo(const u1 *); +int vdex_backend_027_process(const char *, const u1 *, size_t, const runArgs_t *); + +#endif diff --git a/src/vdex/vdex_common.h b/src/vdex/vdex_common.h index 0fc9697..9d3b012 100644 --- a/src/vdex/vdex_common.h +++ b/src/vdex/vdex_common.h @@ -29,6 +29,11 @@ static const u1 kVdexMagic[] = { 'v', 'd', 'e', 'x' }; +static const u4 kChecksumSection = 0; +static const u4 kDexFileSection = 1; +static const u4 kVerifierDepsSection = 2; +static const u4 kTypeLookupTableSection = 3; + typedef u4 VdexChecksum; typedef u4 QuickeningTableOffsetType; diff --git a/src/vdex/vdex_decompiler_027.c b/src/vdex/vdex_decompiler_027.c new file mode 100644 index 0000000..a1b4336 --- /dev/null +++ b/src/vdex/vdex_decompiler_027.c @@ -0,0 +1,68 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#include "vdex_decompiler_027.h" + +#include "../utils.h" + +static u2 *code_ptr; +static u2 *code_end; +static u4 dex_pc; +static u4 cur_code_off; + +static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { + code_ptr = pCode; + code_end = pCode + codeSize; + dex_pc = 0; + cur_code_off = startCodeOff; +} + +static bool isCodeIteratorDone() { return code_ptr >= code_end; } + +static void codeIteratorAdvance() { + u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); + code_ptr += instruction_size; + dex_pc += instruction_size; + cur_code_off += instruction_size * sizeof(u2); +} + +void vdex_decompiler_027_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { + // We have different code items in Standard Dex and Compact Dex + u2 *pCode = NULL; + u4 codeSize = 0; + if (dex_checkType(dexFileBuf) == kNormalDex) { + dexCode *pDexCode = (dexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); + pCode = pDexCode->insns; + codeSize = pDexCode->insnsSize; + } else { + cdexCode *pCdexCode = (cdexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); + pCode = pCdexCode->insns; + dex_DecodeCDexFields(pCdexCode, &codeSize, NULL, NULL, NULL, NULL, true); + } + + u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); + initCodeIterator(pCode, codeSize, startCodeOff); + while (isCodeIteratorDone() == false) { + dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); + codeIteratorAdvance(); + } +} diff --git a/src/vdex/vdex_decompiler_027.h b/src/vdex/vdex_decompiler_027.h new file mode 100644 index 0000000..34e0f06 --- /dev/null +++ b/src/vdex/vdex_decompiler_027.h @@ -0,0 +1,34 @@ +/* + + vdexExtractor + ----------------------------------------- + + Anestis Bechtsoudis + Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _VDEX_DECOMPILER_027_H_ +#define _VDEX_DECOMPILER_027_H_ + +#include "../common.h" +#include "../dex.h" +#include "../dex_instruction.h" +#include "vdex_common.h" + +// Dex decompiler walk method that simply disassembles code blocks +void vdex_decompiler_027_walk(const u1 *, dexMethod *); + +#endif diff --git a/src/vdex_api.c b/src/vdex_api.c index 73a90e8..ebc8af0 100644 --- a/src/vdex_api.c +++ b/src/vdex_api.c @@ -31,6 +31,7 @@ #include "vdex/vdex_010.h" #include "vdex/vdex_019.h" #include "vdex/vdex_021.h" +#include "vdex/vdex_027.h" bool vdexApi_initEnv(const u1 *cursor, vdex_api_env_t *env) { // Check if a supported Vdex version is found @@ -54,6 +55,11 @@ bool vdexApi_initEnv(const u1 *cursor, vdex_api_env_t *env) { env->dumpHeaderInfo = vdex_021_dumpHeaderInfo; env->dumpDepsInfo = vdex_021_dumpDepsInfo; env->process = vdex_021_process; + } else if (vdex_027_isValidVdex(cursor)) { + LOGMSG(l_DEBUG, "Initializing environment for Vdex version '027'"); + env->dumpHeaderInfo = vdex_027_dumpHeaderInfo; + env->dumpDepsInfo = vdex_027_dumpDepsInfo; + env->process = vdex_027_process; } else { LOGMSG(l_ERROR, "Unsupported Vdex version"); return false; @@ -121,6 +127,17 @@ bool vdexApi_updateChecksums(const char *inVdexFileName, for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { vdex_021_SetLocationChecksum(buf, i, checksums[i]); } + } else if (vdex_027_isValidVdex(buf)) { + u4 numberOfDexFiles = vdex_027_GetNumberOfDexFiles(buf); + if ((u4)nCsums != numberOfDexFiles) { + LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", + nCsums, numberOfDexFiles) + goto fini; + } + + for (u4 i = 0; i < numberOfDexFiles; ++i) { + vdex_027_SetLocationChecksum(buf, i, checksums[i]); + } } else { LOGMSG(l_ERROR, "Unsupported Vdex version - updateChecksums failed"); goto fini; @@ -159,6 +176,8 @@ bool vdexApi_printApiLevel(const char *inVdexFileName) { log_raw("API-28\n"); } else if (vdex_021_isValidVdex(buf)) { log_raw("API-29\n"); + } else if (vdex_027_isValidVdex(buf)) { + log_raw("API-31\n"); } else { goto fini; }