├── tools └── deodex │ ├── .gitignore │ ├── constants.sh │ └── run.sh ├── .gitignore ├── bin └── .gitignore ├── jni ├── Application.mk └── Android.mk ├── src ├── vdex │ ├── vdex_decompiler_027.h │ ├── vdex_decompiler_006.h │ ├── vdex_decompiler_010.h │ ├── vdex_decompiler_019.h │ ├── vdex_decompiler_021.h │ ├── vdex_common.h │ ├── vdex_backend_010.h │ ├── vdex_backend_019.h │ ├── vdex_backend_021.h │ ├── vdex_backend_027.h │ ├── vdex_backend_006.h │ ├── vdex_decompiler_027.c │ ├── vdex_006.h │ ├── vdex_010.h │ ├── vdex_027.h │ ├── vdex_019.h │ ├── vdex_021.h │ ├── vdex_decompiler_010.c │ ├── vdex_decompiler_006.c │ ├── vdex_010.c │ ├── vdex_006.c │ ├── vdex_decompiler_019.c │ ├── vdex_decompiler_021.c │ └── vdex_027.c ├── out_writer.h ├── vdex_api.h ├── log.h ├── utils.h ├── hashset │ ├── hashset.h │ └── hashset.c ├── Makefile ├── common.h ├── out_writer.c ├── log.c ├── vdex_api.c ├── dex_modifiers.h ├── vdexExtractor.c ├── dex_instruction.h └── utils.c ├── scripts ├── update-vdex-location-checksums.sh └── extract-apps-from-device.sh ├── make.sh └── LICENSE /tools/deodex/.gitignore: -------------------------------------------------------------------------------- 1 | hostTools 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dSYM 3 | src/vdexExtractor 4 | obj 5 | libs 6 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | # vdexExtractor 2 | # ----------------------------------------- 3 | # 4 | # Anestis Bechtsoudis 5 | # Copyright 2015-2017 by CENSUS S.A. All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | APP_PLATFORM := android-26 20 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 21 | -------------------------------------------------------------------------------- /tools/deodex/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ensure script is sourced 4 | [[ "${BASH_SOURCE[0]}" == "${0}" ]] && exit 1 5 | 6 | # compact_dex_converter dependencies URLs as compiled from AOSP matching API levels 7 | readonly L_DEPS_URL_API_28='https://onedrive.live.com/download?cid=D1FAC8CC6BE2C2B0&resid=D1FAC8CC6BE2C2B0%21581&authkey=AE_kzPqzG_-R4T0' 8 | readonly D_DEPS_URL_API_28='https://onedrive.live.com/download?cid=D1FAC8CC6BE2C2B0&resid=D1FAC8CC6BE2C2B0%21580&authkey=ADMmFqIo6bj7X5Y' 9 | readonly L_DEPS_URL_API_29='https://onedrive.live.com/download?cid=D1FAC8CC6BE2C2B0&resid=D1FAC8CC6BE2C2B0%21603&authkey=AA1Uig7ufSzi6Sw' 10 | 11 | readonly L_DEPS_API_28_SIG='fa722f44dea926fbe019c2fa520cc7d5e3cf9dd0cd59ff32d7189e8102977118' 12 | readonly D_DEPS_API_28_SIG='f3b5005a608d4ce12234f4cce307ecd74c9f88dde57e2af4c4df1c29a79de196' 13 | readonly L_DEPS_API_29_SIG='64a2103254c97377f356daa7432023ef823b42ae090e2ad577b83991cb4005e2' 14 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_027.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_DECOMPILER_027_H_ 24 | #define _VDEX_DECOMPILER_027_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "../dex_instruction.h" 29 | #include "vdex_common.h" 30 | 31 | // Dex decompiler walk method that simply disassembles code blocks 32 | void vdex_decompiler_027_walk(const u1 *, dexMethod *); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/out_writer.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _OUT_WRITER_H_ 24 | #define _OUT_WRITER_H_ 25 | 26 | #include "common.h" 27 | 28 | void outWriter_formatName(char *, size_t, const char *, const char *, size_t, const char *); 29 | 30 | bool outWriter_DexFile(const runArgs_t *, const char *, size_t, const u1 *, size_t); 31 | 32 | bool outWriter_VdexFile(const runArgs_t *, const char *, u1 *, off_t); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_006.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_DECOMPILER_006_H_ 24 | #define _VDEX_DECOMPILER_006_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "../dex_instruction.h" 29 | 30 | // Dex decompiler driver function using quicken_info data 31 | bool vdex_decompiler_006_decompile(const u1 *, dexMethod *, const u1 *, u4, bool); 32 | 33 | // Dex decompiler walk method that simply disassembles code blocks 34 | void vdex_decompiler_006_walk(const u1 *, dexMethod *); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/vdex_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_API_H_ 24 | #define _VDEX_API_H_ 25 | 26 | #include "common.h" 27 | 28 | typedef struct { 29 | void (*dumpHeaderInfo)(const u1 *); 30 | void (*dumpDepsInfo)(const u1 *); 31 | int (*process)(const char *, const u1 *, size_t, const runArgs_t *); 32 | } vdex_api_env_t; 33 | 34 | bool vdexApi_initEnv(const u1 *, vdex_api_env_t *); 35 | bool vdexApi_updateChecksums(const char *, int, u4 *, const runArgs_t *); 36 | bool vdexApi_printApiLevel(const char *); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_010.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_DECOMPILER_010_H_ 24 | #define _VDEX_DECOMPILER_010_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "../dex_instruction.h" 29 | #include "vdex_common.h" 30 | 31 | // Dex decompiler driver function using quicken_info data 32 | bool vdex_decompiler_010_decompile(const u1 *, dexMethod *, const vdex_data_array_t *, bool); 33 | 34 | // Dex decompiler walk method that simply disassembles code blocks 35 | void vdex_decompiler_010_walk(const u1 *, dexMethod *); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_019.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_DECOMPILER_019_H_ 24 | #define _VDEX_DECOMPILER_019_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "../dex_instruction.h" 29 | #include "vdex_common.h" 30 | 31 | // Dex decompiler driver function using quicken_info data 32 | bool vdex_decompiler_019_decompile(const u1 *, dexMethod *, const vdex_data_array_t *, bool); 33 | 34 | // Dex decompiler walk method that simply disassembles code blocks 35 | void vdex_decompiler_019_walk(const u1 *, dexMethod *); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_021.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_DECOMPILER_021_H_ 24 | #define _VDEX_DECOMPILER_021_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "../dex_instruction.h" 29 | #include "vdex_common.h" 30 | 31 | // Dex decompiler driver function using quicken_info data 32 | bool vdex_decompiler_021_decompile(const u1 *, dexMethod *, const vdex_data_array_t *, bool); 33 | 34 | // Dex decompiler walk method that simply disassembles code blocks 35 | void vdex_decompiler_021_walk(const u1 *, dexMethod *); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | # vdexExtractor 2 | # ----------------------------------------- 3 | # 4 | # Anestis Bechtsoudis 5 | # Copyright 2017 by CENSUS S.A. All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | LOCAL_PATH := $(call my-dir) 20 | 21 | # Main module 22 | include $(CLEAR_VARS) 23 | LOCAL_MODULE := vdexExtractor 24 | SRC := ../src 25 | SRC_FILE_LIST := $(sort $(wildcard $(LOCAL_PATH)/$(SRC)/*.c)) $(sort $(wildcard $(LOCAL_PATH)/$(SRC)/*/*.c)) 26 | LOCAL_SRC_FILES := $(SRC_FILE_LIST:$(LOCAL_PATH)/%=%) 27 | LOCAL_CFLAGS += -c -std=c11 -D_GNU_SOURCE \ 28 | -Wall -Wextra -Werror 29 | LOCAL_LDFLAGS += -lm -lz 30 | 31 | GIT_VERSION := $(shell git rev-parse --short HEAD | tr -d "\n") 32 | LOCAL_CFLAGS += -DVERSION=\"dev-$(GIT_VERSION)\" 33 | 34 | include $(BUILD_EXECUTABLE) 35 | -------------------------------------------------------------------------------- /src/vdex/vdex_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_COMMON_H_ 24 | #define _VDEX_COMMON_H_ 25 | 26 | #include "../common.h" 27 | 28 | #define kUnresolvedMarker (u2)(-1) 29 | 30 | static const u1 kVdexMagic[] = { 'v', 'd', 'e', 'x' }; 31 | 32 | static const u4 kChecksumSection = 0; 33 | static const u4 kDexFileSection = 1; 34 | static const u4 kVerifierDepsSection = 2; 35 | static const u4 kTypeLookupTableSection = 3; 36 | 37 | typedef u4 VdexChecksum; 38 | typedef u4 QuickeningTableOffsetType; 39 | 40 | typedef struct { 41 | const u1 *data; // Pointer to data begin 42 | u4 size; // Size of data (in bytes) 43 | u4 offset; // Offset from Vdex begin 44 | } vdex_data_array_t; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/vdex/vdex_backend_010.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_BACKEND_010_H_ 24 | #define _VDEX_BACKEND_010_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_010.h" 29 | 30 | typedef struct __attribute__((packed)) { 31 | vdexDepStrings_010 extraStrings; 32 | vdexDepTypeSet_010 assignTypeSets; 33 | vdexDepTypeSet_010 unassignTypeSets; 34 | vdexDepClassResSet_010 classes; 35 | vdexDepFieldResSet_010 fields; 36 | vdexDepMethodResSet_010 methods; 37 | vdexDepUnvfyClassesSet_010 unvfyClasses; 38 | } vdexDepData_010; 39 | 40 | typedef struct __attribute__((packed)) { 41 | u4 numberOfDexFiles; 42 | vdexDepData_010 *pVdexDepData; 43 | } vdexDeps_010; 44 | 45 | void vdex_backend_010_dumpDepsInfo(const u1 *); 46 | int vdex_backend_010_process(const char *, const u1 *, size_t, const runArgs_t *); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/vdex/vdex_backend_019.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_BACKEND_019_H_ 24 | #define _VDEX_BACKEND_019_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_019.h" 29 | 30 | typedef struct __attribute__((packed)) { 31 | vdexDepStrings_019 extraStrings; 32 | vdexDepTypeSet_019 assignTypeSets; 33 | vdexDepTypeSet_019 unassignTypeSets; 34 | vdexDepClassResSet_019 classes; 35 | vdexDepFieldResSet_019 fields; 36 | vdexDepMethodResSet_019 methods; 37 | vdexDepUnvfyClassesSet_019 unvfyClasses; 38 | } vdexDepData_019; 39 | 40 | typedef struct __attribute__((packed)) { 41 | u4 numberOfDexFiles; 42 | vdexDepData_019 *pVdexDepData; 43 | } vdexDeps_019; 44 | 45 | void vdex_backend_019_dumpDepsInfo(const u1 *); 46 | int vdex_backend_019_process(const char *, const u1 *, size_t, const runArgs_t *); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/vdex/vdex_backend_021.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_BACKEND_021_H_ 24 | #define _VDEX_BACKEND_021_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_021.h" 29 | 30 | typedef struct __attribute__((packed)) { 31 | vdexDepStrings_021 extraStrings; 32 | vdexDepTypeSet_021 assignTypeSets; 33 | vdexDepTypeSet_021 unassignTypeSets; 34 | vdexDepClassResSet_021 classes; 35 | vdexDepFieldResSet_021 fields; 36 | vdexDepMethodResSet_021 methods; 37 | vdexDepUnvfyClassesSet_021 unvfyClasses; 38 | } vdexDepData_021; 39 | 40 | typedef struct __attribute__((packed)) { 41 | u4 numberOfDexFiles; 42 | vdexDepData_021 *pVdexDepData; 43 | } vdexDeps_021; 44 | 45 | void vdex_backend_021_dumpDepsInfo(const u1 *); 46 | int vdex_backend_021_process(const char *, const u1 *, size_t, const runArgs_t *); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/vdex/vdex_backend_027.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_BACKEND_027_H_ 24 | #define _VDEX_BACKEND_027_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_027.h" 29 | 30 | typedef struct __attribute__((packed)) { 31 | vdexDepStrings_027 extraStrings; 32 | vdexDepTypeSet_027 assignTypeSets; 33 | vdexDepTypeSet_027 unassignTypeSets; 34 | vdexDepClassResSet_027 classes; 35 | vdexDepFieldResSet_027 fields; 36 | vdexDepMethodResSet_027 methods; 37 | vdexDepUnvfyClassesSet_027 unvfyClasses; 38 | } vdexDepData_027; 39 | 40 | typedef struct __attribute__((packed)) { 41 | u4 numberOfDexFiles; 42 | vdexDepData_027 *pVdexDepData; 43 | } vdexDeps_027; 44 | 45 | void vdex_backend_027_dumpDepsInfo(const u1 *); 46 | int vdex_backend_027_process(const char *, const u1 *, size_t, const runArgs_t *); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/vdex/vdex_backend_006.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_BACKEND_006_H_ 24 | #define _VDEX_BACKEND_006_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_006.h" 29 | 30 | typedef struct __attribute__((packed)) { 31 | vdexDepStrings_006 extraStrings; 32 | vdexDepTypeSet_006 assignTypeSets; 33 | vdexDepTypeSet_006 unassignTypeSets; 34 | vdexDepClassResSet_006 classes; 35 | vdexDepFieldResSet_006 fields; 36 | vdexDepMethodResSet_006 directMethods; 37 | vdexDepMethodResSet_006 virtualMethods; 38 | vdexDepMethodResSet_006 interfaceMethods; 39 | vdexDepUnvfyClassesSet_006 unvfyClasses; 40 | } vdexDepData_006; 41 | 42 | typedef struct __attribute__((packed)) { 43 | u4 numberOfDexFiles; 44 | vdexDepData_006 *pVdexDepData; 45 | } vdexDeps_006; 46 | 47 | void vdex_backend_006_dumpDepsInfo(const u1 *); 48 | int vdex_backend_006_process(const char *, const u1 *, size_t, const runArgs_t *); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _LOG_H_ 24 | #define _LOG_H_ 25 | 26 | #include "common.h" 27 | 28 | typedef enum { l_FATAL = 0, l_ERROR, l_WARN, l_INFO, l_DEBUG, l_MAX_LEVEL } log_level_t; 29 | 30 | void log_setMinLevel(log_level_t); 31 | void log_setDisStatus(bool); 32 | bool log_getDisStatus(); 33 | bool log_initLogFile(const char *); 34 | void log_closeLogFile(); 35 | 36 | void log_msg(log_level_t, bool, bool, bool, const char *, const char *, int, const char *, ...); 37 | void log_dis(const char *fmt, ...); 38 | void log_raw(const char *fmt, ...); 39 | 40 | #define LOGMSG(ll, ...) \ 41 | log_msg(ll, false, false, false, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 42 | #define LOGMSG_P(ll, ...) \ 43 | log_msg(ll, true, false, false, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 44 | #define LOGMSG_RAW(ll, ...) \ 45 | log_msg(ll, false, true, false, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 46 | #define DISPLAY(ll, ...) \ 47 | log_msg(ll, false, false, true, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _UTILS_H_ 24 | #define _UTILS_H_ 25 | 26 | #include 27 | 28 | #include "common.h" 29 | 30 | bool utils_init(infiles_t *); 31 | u1 *utils_mapFileToRead(const char *, off_t *, int *); 32 | bool utils_writeToFd(int, const u1 *, off_t); 33 | void utils_hexDump(char *, const u1 *, int); 34 | char *utils_bin2hex(const unsigned char *, const size_t); 35 | void *utils_malloc(size_t); 36 | void *utils_calloc(size_t); 37 | void *utils_realloc(void *, size_t); 38 | void *utils_crealloc(void *ptr, size_t, size_t); 39 | 40 | // To simplify api, all errors are treated as fatal 41 | void utils_pseudoStrAppend(const char **, size_t *, size_t *, const char *); 42 | 43 | void utils_startTimer(struct timespec *); 44 | long utils_endTimer(struct timespec *); 45 | 46 | u4 *utils_processFileWithCsums(const char *, int *); 47 | 48 | char *utils_fileBasename(char const *); 49 | bool utils_isValidDir(const char *); 50 | 51 | uintptr_t utils_roundDown(uintptr_t, uintptr_t); 52 | uintptr_t utils_roundUp(uintptr_t, uintptr_t); 53 | uintptr_t utils_allignUp(uintptr_t, uintptr_t); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/hashset/hashset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Couchbase, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef HASHSET_H 18 | #define HASHSET_H 1 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | struct hashset_st { 27 | size_t nbits; 28 | size_t mask; 29 | 30 | size_t capacity; 31 | size_t *items; 32 | size_t nitems; 33 | size_t n_deleted_items; 34 | }; 35 | 36 | typedef struct hashset_st *hashset_t; 37 | 38 | /* create hashset instance */ 39 | hashset_t hashset_create(void); 40 | 41 | /* destroy hashset instance */ 42 | void hashset_destroy(hashset_t set); 43 | 44 | size_t hashset_num_items(hashset_t set); 45 | 46 | /* add item into the hashset. 47 | * 48 | * @note 0 and 1 is special values, meaning nil and deleted items. the 49 | * function will return -1 indicating error. 50 | * 51 | * returns zero if the item already in the set and non-zero otherwise 52 | */ 53 | int hashset_add(hashset_t set, void *item); 54 | 55 | /* remove item from the hashset 56 | * 57 | * returns non-zero if the item was removed and zero if the item wasn't 58 | * exist 59 | */ 60 | int hashset_remove(hashset_t set, void *item); 61 | 62 | /* check if existence of the item 63 | * 64 | * returns non-zero if the item exists and zero otherwise 65 | */ 66 | int hashset_is_member(hashset_t set, void *item); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # vdexExtractor 2 | # ----------------------------------------- 3 | # 4 | # Anestis Bechtsoudis 5 | # Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | # Default to gcc 20 | CC ?= gcc 21 | DEBUG ?= false 22 | 23 | TARGET = vdexExtractor 24 | CFLAGS += -c -std=c11 -D_GNU_SOURCE \ 25 | -Wall -Wextra -Werror 26 | LDFLAGS += -lm -lz 27 | 28 | ifeq ($(DEBUG),true) 29 | CFLAGS += -g -ggdb 30 | LDFLAGS += -g -ggdb 31 | endif 32 | 33 | LBITS := $(shell getconf LONG_BIT) 34 | ifeq ($(LBITS),32) 35 | # If compiling in 32bit system 36 | CFLAGS += -Wno-pointer-to-int-cast 37 | else 38 | ifneq (,$(findstring -m32,$(CFLAGS))) 39 | # If cross-compiling for a 32bit target from a 64bit system 40 | CFLAGS += -Wno-pointer-to-int-cast 41 | endif 42 | endif 43 | 44 | GIT_VERSION := $(shell git rev-parse --short HEAD | tr -d "\n") 45 | CFLAGS += -DVERSION=\"dev-$(GIT_VERSION)\" 46 | 47 | .PHONY: default all clean 48 | 49 | default: $(TARGET) 50 | all: default 51 | 52 | OBJECTS = $(patsubst %.c, %.o, $(sort $(wildcard *.c)) $(sort $(wildcard */*.c))) 53 | HEADERS = $(wildcard *.h) 54 | 55 | %.o: %.c $(HEADERS) 56 | $(CC) $(CFLAGS) -c $< -o $@ 57 | 58 | .PRECIOUS: $(TARGET) $(OBJECTS) 59 | 60 | $(TARGET): $(OBJECTS) 61 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ 62 | cp $(TARGET) ../bin/$(TARGET) 63 | 64 | clean: 65 | -rm -f *.o 66 | -rm -f */*.o 67 | -rm -f $(TARGET) 68 | 69 | format: 70 | clang-format-mp-9.0 -style="{BasedOnStyle: Google, \ 71 | IndentWidth: 2, Cpp11BracedListStyle: false, \ 72 | BinPackParameters: false, ColumnLimit: 100}" -i -sort-includes *.c *.h */*.c */*.h 73 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_027.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_decompiler_027.h" 24 | 25 | #include "../utils.h" 26 | 27 | static u2 *code_ptr; 28 | static u2 *code_end; 29 | static u4 dex_pc; 30 | static u4 cur_code_off; 31 | 32 | static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { 33 | code_ptr = pCode; 34 | code_end = pCode + codeSize; 35 | dex_pc = 0; 36 | cur_code_off = startCodeOff; 37 | } 38 | 39 | static bool isCodeIteratorDone() { return code_ptr >= code_end; } 40 | 41 | static void codeIteratorAdvance() { 42 | u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); 43 | code_ptr += instruction_size; 44 | dex_pc += instruction_size; 45 | cur_code_off += instruction_size * sizeof(u2); 46 | } 47 | 48 | void vdex_decompiler_027_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { 49 | // We have different code items in Standard Dex and Compact Dex 50 | u2 *pCode = NULL; 51 | u4 codeSize = 0; 52 | if (dex_checkType(dexFileBuf) == kNormalDex) { 53 | dexCode *pDexCode = (dexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 54 | pCode = pDexCode->insns; 55 | codeSize = pDexCode->insnsSize; 56 | } else { 57 | cdexCode *pCdexCode = (cdexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 58 | pCode = pCdexCode->insns; 59 | dex_DecodeCDexFields(pCdexCode, &codeSize, NULL, NULL, NULL, NULL, true); 60 | } 61 | 62 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 63 | initCodeIterator(pCode, codeSize, startCodeOff); 64 | while (isCodeIteratorDone() == false) { 65 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 66 | codeIteratorAdvance(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _COMMON_H_ 24 | #define _COMMON_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "log.h" 39 | 40 | #ifndef LIKELY 41 | #define LIKELY(x) __builtin_expect(!!(x), 1) 42 | #endif 43 | #ifndef UNLIKELY 44 | #define UNLIKELY(x) __builtin_expect(!!(x), 0) 45 | #endif 46 | 47 | typedef uint8_t u1; 48 | typedef uint16_t u2; 49 | typedef uint32_t u4; 50 | typedef uint64_t u8; 51 | typedef int8_t s1; 52 | typedef int16_t s2; 53 | typedef int32_t s4; 54 | typedef int64_t s8; 55 | 56 | typedef __attribute__((__aligned__(1))) uint32_t unaligned_u4; 57 | typedef __attribute__((__aligned__(1))) int32_t unaligned_s4; 58 | 59 | #define CHECK_IMPL(c1, op, c2) \ 60 | do { \ 61 | u8 v1 = (u8)(c1); \ 62 | u8 v2 = (u8)(c2); \ 63 | if (UNLIKELY(!(v1 op v2))) LOGMSG(l_FATAL, "(" #c1 ") " #op " (" #c2 ")", v1, v2); \ 64 | } while (false) /**/ 65 | 66 | #define CHECK(a) CHECK_IMPL((a), !=, 0) 67 | #define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) 68 | #define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) 69 | #define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) 70 | #define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) 71 | #define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) 72 | #define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) 73 | 74 | //#define IS_DEV_VER // Comment when release build 75 | #define PROG_NAME "vdexExtractor" 76 | #ifdef IS_DEV_VER 77 | #define PROG_VERSION VERSION 78 | #else 79 | #define PROG_VERSION "0.6.0" 80 | #endif 81 | #define PROG_AUTHORS \ 82 | " Anestis Bechtsoudis \n" \ 83 | " Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved." 84 | 85 | typedef struct { 86 | char *inputFile; 87 | char **files; 88 | size_t fileCnt; 89 | } infiles_t; 90 | 91 | typedef struct { 92 | char *outputDir; 93 | bool fileOverride; 94 | bool unquicken; 95 | bool enableDisassembler; 96 | bool ignoreCrc; 97 | bool dumpDeps; 98 | char *newCrcFile; 99 | bool getApi; 100 | } runArgs_t; 101 | 102 | extern void exitWrapper(int); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /scripts/update-vdex-location-checksums.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # vdexExtractor 4 | # ----------------------------------------- 5 | # 6 | # Anestis Bechtsoudis 7 | # Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | set -e # fail on unhandled error 23 | set -u # fail on undefined variable 24 | #set -x # debug 25 | 26 | readonly TOOL_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 27 | readonly TMP_WORK_DIR=$(mktemp -d /tmp/vdex-extractor.XXXXXX) || exit 1 28 | readonly VDEX_EXTRACTOR_BIN="$TOOL_ROOT/../bin/vdexExtractor" 29 | 30 | declare -ar SYS_TOOLS=("mkdir" "dirname" "sed" "grep" "unzip") 31 | 32 | info() { echo -e "[INFO]: $*" 1>&2; } 33 | warn() { echo -e "[WARN]: $*" 1>&2; } 34 | error() { echo -e "[ERR ]: $*" 1>&2; } 35 | debug() { echo -e "[DBG ]: $*" 1>&2; } 36 | log() { echo -e " $*" 1>&2; } 37 | userIn() { echo -en "[IN ]: $*" 1>&2; } 38 | 39 | abort() { 40 | rm -rf "$TMP_WORK_DIR" 41 | exit "$1" 42 | } 43 | 44 | usage() { 45 | cat <<_EOF 46 | Usage: $(basename "$0") [options] 47 | options: 48 | -i|--input : Input Vdex file to repair location checksum(s) within 49 | -a|--app : Input Apk file to extract location checksum(s) from 50 | -o|--output : Directory to save updated Vdex file (default is '.') 51 | -h|--help : This help message 52 | _EOF 53 | abort 1 54 | } 55 | 56 | commandExists() { 57 | type "$1" &> /dev/null 58 | } 59 | 60 | trap "abort 1" SIGHUP SIGINT SIGTERM 61 | 62 | INPUT_VDEX="" 63 | INPUT_BC="" 64 | OUTPUT_DIR="$(pwd)" 65 | 66 | for i in "${SYS_TOOLS[@]}" 67 | do 68 | if ! commandExists "$i"; then 69 | error "'$i' command not found" 70 | abort 1 71 | fi 72 | done 73 | 74 | while [[ $# -gt 0 ]] 75 | do 76 | arg="$1" 77 | case $arg in 78 | -o|--output) 79 | OUTPUT_DIR="$2" 80 | shift 81 | ;; 82 | -i|--input) 83 | INPUT_VDEX="$2" 84 | shift 85 | ;; 86 | -a|--app) 87 | INPUT_BC="$2" 88 | shift 89 | ;; 90 | -h|--help) 91 | usage 92 | ;; 93 | *) 94 | error "Invalid argument '$1'" 95 | usage 96 | ;; 97 | esac 98 | shift 99 | done 100 | 101 | if [[ "$INPUT_VDEX" == "" || "$INPUT_BC" == "" ]]; then 102 | error "Missing input arguments" 103 | usage 104 | fi 105 | 106 | checksumsFile="$TMP_WORK_DIR/checksums.txt" 107 | unzip -vl "$INPUT_BC" | grep "dex$" | sed 's/ */ /g' | cut -d " " -f8 > "$checksumsFile" || { 108 | error "Input bytecode source if not a Zip archive" 109 | } 110 | 111 | # TODO: Add support for Dex CRC extraction 112 | 113 | $VDEX_EXTRACTOR_BIN -i "$INPUT_VDEX" -o "$OUTPUT_DIR" --new-crc "$checksumsFile" || { 114 | error "vdexExtractor execution failed" 115 | abort 1 116 | } 117 | 118 | abort 0 119 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # vdexExtractor 4 | # ----------------------------------------- 5 | # 6 | # Anestis Bechtsoudis 7 | # Copyright 2017 by CENSUS S.A. All Rights Reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | set -e # fail on unhandled error 23 | set -u # fail on undefined variable 24 | #set -x # debug 25 | 26 | declare -a sysTools=( "make" ) 27 | 28 | readonly MODULE_NAME="vdexExtractor" 29 | 30 | function commandExists() 31 | { 32 | type "$1" &> /dev/null 33 | } 34 | 35 | function usage() 36 | { 37 | echo "$(basename "$0") [gcc|clang|cross-android|clean] (default is gcc)" 38 | exit 1 39 | } 40 | 41 | function build_cross_android() 42 | { 43 | local cpu cpuBaseDir 44 | if [[ -z ${NDK+x} ]]; then 45 | # Search in $PATH 46 | if [[ $(which ndk-build) != "" ]]; then 47 | NDK=$(dirname "$(which ndk-build)") 48 | else 49 | echo "[-] Could not detect Android NDK dir" 50 | exit 1 51 | fi 52 | fi 53 | 54 | local ndk_extra_args="" 55 | if [ "$DEBUG_BUILD" = true ]; then 56 | ndk_extra_args+="V=1 NDK_DEBUG=1 APP_OPTIM=debug" 57 | fi 58 | 59 | "$NDK/ndk-build" clean 60 | "$NDK/ndk-build" $ndk_extra_args || { 61 | echo "[-] android build failed" 62 | exit 1 63 | } 64 | 65 | local baseDir=libs 66 | if [ "$DEBUG_BUILD" = true ]; then 67 | baseDir=obj/local 68 | fi 69 | 70 | find libs -mindepth 1 -maxdepth 1 -type d | while read -r cpuBaseDir 71 | do 72 | cpu=$(basename "$cpuBaseDir") 73 | cp "$baseDir/$cpu/$MODULE_NAME" "bin/$MODULE_NAME-android-$cpu" 74 | done 75 | } 76 | 77 | function build() 78 | { 79 | local compiler="$1" 80 | if [[ "$compiler" == "" ]]; then 81 | if [[ -z ${CC+x} || "$CC" == "" ]]; then 82 | compiler="gcc" 83 | else 84 | compiler="$CC" 85 | fi 86 | fi 87 | 88 | make clean -C src || { 89 | echo "[-] make clean failed" 90 | exit 1 91 | } 92 | 93 | CC=$compiler DEBUG=$DEBUG_BUILD make -C src || { 94 | echo "[-] build failed" 95 | exit 1 96 | } 97 | } 98 | 99 | function clean() 100 | { 101 | make clean -C src || { 102 | echo "[-] make clean failed" 103 | exit 1 104 | } 105 | 106 | if [[ -z ${NDK+x} ]]; then 107 | # Search in $PATH 108 | if [[ $(which ndk-build) != "" ]]; then 109 | NDK=$(dirname "$(which ndk-build)") 110 | "$NDK/ndk-build" clean 111 | fi 112 | fi 113 | } 114 | 115 | # Check that common system tools exist 116 | for i in "${sysTools[@]}" 117 | do 118 | if ! commandExists "$i"; then 119 | echo "[-] '$i' command not found" 120 | exit 1 121 | fi 122 | done 123 | 124 | if [ $# -gt 1 ]; then 125 | echo "[-] Invalid args" 126 | exit 1 127 | fi 128 | 129 | if [ $# -eq 0 ]; then 130 | target="" 131 | else 132 | target="$1" 133 | fi 134 | 135 | if [[ -z ${DEBUG+x} || $DEBUG != true ]]; then 136 | DEBUG_BUILD=false 137 | else 138 | DEBUG_BUILD=true 139 | fi 140 | 141 | case "$target" in 142 | "") build "";; 143 | "gcc") build "gcc";; 144 | "clang") build "clang";; 145 | "cross-android") build_cross_android;; 146 | "clean") clean;; 147 | *) usage;; 148 | esac 149 | 150 | exit 0 151 | -------------------------------------------------------------------------------- /src/hashset/hashset.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Couchbase, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "hashset.h" 18 | 19 | #include 20 | 21 | static const unsigned int prime_1 = 73; 22 | static const unsigned int prime_2 = 5009; 23 | 24 | hashset_t hashset_create() { 25 | hashset_t set = calloc(1, sizeof(struct hashset_st)); 26 | 27 | if (set == NULL) { 28 | return NULL; 29 | } 30 | set->nbits = 3; 31 | set->capacity = (size_t)(1 << set->nbits); 32 | set->mask = set->capacity - 1; 33 | set->items = calloc(set->capacity, sizeof(size_t)); 34 | if (set->items == NULL) { 35 | hashset_destroy(set); 36 | return NULL; 37 | } 38 | set->nitems = 0; 39 | set->n_deleted_items = 0; 40 | return set; 41 | } 42 | 43 | size_t hashset_num_items(hashset_t set) { return set->nitems; } 44 | 45 | void hashset_destroy(hashset_t set) { 46 | if (set) { 47 | free(set->items); 48 | } 49 | free(set); 50 | } 51 | 52 | static int hashset_add_member(hashset_t set, void *item) { 53 | size_t value = (size_t)item; 54 | size_t ii; 55 | 56 | if (value == 0 || value == 1) { 57 | return -1; 58 | } 59 | 60 | ii = set->mask & (prime_1 * value); 61 | 62 | while (set->items[ii] != 0 && set->items[ii] != 1) { 63 | if (set->items[ii] == value) { 64 | return 0; 65 | } else { 66 | /* search free slot */ 67 | ii = set->mask & (ii + prime_2); 68 | } 69 | } 70 | set->nitems++; 71 | if (set->items[ii] == 1) { 72 | set->n_deleted_items--; 73 | } 74 | set->items[ii] = value; 75 | return 1; 76 | } 77 | 78 | static void maybe_rehash(hashset_t set) { 79 | size_t *old_items; 80 | size_t old_capacity, ii; 81 | 82 | if (set->nitems + set->n_deleted_items >= (double)set->capacity * 0.85) { 83 | old_items = set->items; 84 | old_capacity = set->capacity; 85 | set->nbits++; 86 | set->capacity = (size_t)(1 << set->nbits); 87 | set->mask = set->capacity - 1; 88 | set->items = calloc(set->capacity, sizeof(size_t)); 89 | set->nitems = 0; 90 | set->n_deleted_items = 0; 91 | assert(set->items); 92 | for (ii = 0; ii < old_capacity; ii++) { 93 | hashset_add_member(set, (void *)old_items[ii]); 94 | } 95 | free(old_items); 96 | } 97 | } 98 | 99 | int hashset_add(hashset_t set, void *item) { 100 | int rv = hashset_add_member(set, item); 101 | maybe_rehash(set); 102 | return rv; 103 | } 104 | 105 | int hashset_remove(hashset_t set, void *item) { 106 | size_t value = (size_t)item; 107 | size_t ii = set->mask & (prime_1 * value); 108 | 109 | while (set->items[ii] != 0) { 110 | if (set->items[ii] == value) { 111 | set->items[ii] = 1; 112 | set->nitems--; 113 | set->n_deleted_items++; 114 | return 1; 115 | } else { 116 | ii = set->mask & (ii + prime_2); 117 | } 118 | } 119 | return 0; 120 | } 121 | 122 | int hashset_is_member(hashset_t set, void *item) { 123 | size_t value = (size_t)item; 124 | size_t ii = set->mask & (prime_1 * value); 125 | 126 | while (set->items[ii] != 0) { 127 | if (set->items[ii] == value) { 128 | return 1; 129 | } else { 130 | ii = set->mask & (ii + prime_2); 131 | } 132 | } 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /src/out_writer.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "out_writer.h" 24 | 25 | #include "dex.h" 26 | #include "utils.h" 27 | 28 | void outWriter_formatName(char *outBuf, 29 | size_t outBufLen, 30 | const char *rootPath, 31 | const char *fName, 32 | size_t classId, 33 | const char *suffix) { 34 | // Trim Vdex extension and replace with Apk 35 | const char *fileExt = strrchr(fName, '.'); 36 | int fNameLen = strlen(fName); 37 | if (fileExt) { 38 | fNameLen = fileExt - fName; 39 | } 40 | char formattedName[PATH_MAX] = { 0 }; 41 | if (classId == 0) { 42 | snprintf(formattedName, sizeof(formattedName), "%.*s_classes.%s", fNameLen, fName, suffix); 43 | } else { 44 | snprintf(formattedName, sizeof(formattedName), "%.*s_classes%zu.%s", fNameLen, fName, 45 | classId + 1, suffix); 46 | } 47 | 48 | if (rootPath == NULL) { 49 | // Save to same directory as input file 50 | snprintf(outBuf, outBufLen, "%s", formattedName); 51 | } else { 52 | const char *pFileBaseName = utils_fileBasename(formattedName); 53 | snprintf(outBuf, outBufLen, "%s/%s", rootPath, pFileBaseName); 54 | free((void *)pFileBaseName); 55 | } 56 | } 57 | 58 | bool outWriter_DexFile(const runArgs_t *pRunArgs, 59 | const char *VdexFileName, 60 | size_t dexIdx, 61 | const u1 *buf, 62 | size_t bufSize) { 63 | char outFile[PATH_MAX] = { 0 }; 64 | outWriter_formatName(outFile, sizeof(outFile), pRunArgs->outputDir, VdexFileName, dexIdx, 65 | dex_checkType(buf) == kNormalDex ? "dex" : "cdex"); 66 | 67 | // Write Dex file 68 | int fileFlags = O_CREAT | O_RDWR; 69 | if (pRunArgs->fileOverride == false) { 70 | fileFlags |= O_EXCL; 71 | } 72 | int dstfd = -1; 73 | dstfd = open(outFile, fileFlags, 0644); 74 | if (dstfd == -1) { 75 | LOGMSG_P(l_ERROR, "Couldn't create output file '%s' - skipping 'classes%zu.dex'", outFile, 76 | dexIdx); 77 | return false; 78 | } 79 | 80 | if (!utils_writeToFd(dstfd, buf, bufSize)) { 81 | close(dstfd); 82 | LOGMSG(l_ERROR, "Couldn't write '%s' file - skipping 'classes%zu.dex'", outFile, dexIdx); 83 | return false; 84 | } 85 | 86 | close(dstfd); 87 | return true; 88 | } 89 | 90 | bool outWriter_VdexFile(const runArgs_t *pRunArgs, const char *VdexFileName, u1 *buf, off_t bufSz) { 91 | const char *fileExt = strrchr(VdexFileName, '.'); 92 | int fNameLen = strlen(VdexFileName); 93 | if (fileExt) { 94 | fNameLen = fileExt - VdexFileName; 95 | } 96 | char outFileName[PATH_MAX] = { 0 }; 97 | if (pRunArgs->outputDir == NULL) { 98 | snprintf(outFileName, sizeof(outFileName), "%.*s_updated.vdex", fNameLen, VdexFileName); 99 | } else { 100 | const char *pFileBaseName = utils_fileBasename(VdexFileName); 101 | snprintf(outFileName, sizeof(outFileName), "%s/%s_updated.vdex", pRunArgs->outputDir, 102 | pFileBaseName); 103 | free((void *)pFileBaseName); 104 | } 105 | 106 | int dstfd = -1; 107 | dstfd = open(outFileName, O_CREAT | O_RDWR, 0644); 108 | if (dstfd == -1) { 109 | LOGMSG_P(l_ERROR, "Couldn't create output file '%s'", outFileName); 110 | return false; 111 | } 112 | 113 | if (!utils_writeToFd(dstfd, buf, bufSz)) { 114 | close(dstfd); 115 | LOGMSG(l_ERROR, "Couldn't write '%s' file", outFileName); 116 | return false; 117 | } 118 | 119 | close(dstfd); 120 | return true; 121 | } 122 | -------------------------------------------------------------------------------- /src/vdex/vdex_006.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_006_H_ 24 | #define _VDEX_006_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_common.h" 29 | 30 | static const u1 kVdex006[] = { '0', '0', '6', '\0' }; 31 | 32 | typedef struct __attribute__((packed)) { 33 | u1 magic[4]; 34 | u1 version[4]; 35 | u4 numberOfDexFiles; 36 | u4 dexSize; 37 | u4 verifierDepsSize; 38 | u4 quickeningInfoSize; 39 | } vdexHeader_006; 40 | 41 | // VDEX files contain extracted DEX files. The VdexFile class maps the file to 42 | // memory and provides tools for accessing its individual sections. 43 | // 44 | // File format: 45 | // VdexFile::Header fixed-length header 46 | // 47 | // DEX[0] array of the input DEX files 48 | // DEX[1] the bytecode may have been quickened 49 | // ... 50 | // DEX[D] 51 | // QuickeningInfo 52 | // uint8[] quickening data 53 | // unaligned_uint32_t[2][] table of offsets pair: 54 | // uint32_t[0] contains code_item_offset 55 | // uint32_t[1] contains quickening data offset from the start 56 | // of QuickeningInfo 57 | // unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous 58 | // table for each dex file 59 | 60 | typedef struct __attribute__((packed)) { 61 | vdexHeader_006 *pVdexHeader; 62 | dexHeader *pDexFiles; 63 | } vdexFile_006; 64 | 65 | typedef struct __attribute__((packed)) { 66 | u4 numberOfStrings; 67 | const char **strings; 68 | } vdexDepStrings_006; 69 | 70 | typedef struct __attribute__((packed)) { 71 | u4 dstIndex; 72 | u4 srcIndex; 73 | } vdexDepSet_006; 74 | 75 | typedef struct __attribute__((packed)) { 76 | u2 typeIdx; 77 | u2 accessFlags; 78 | } vdexDepClassRes_006; 79 | 80 | typedef struct __attribute__((packed)) { 81 | u4 numberOfEntries; 82 | vdexDepSet_006 *pVdexDepSets; 83 | } vdexDepTypeSet_006; 84 | 85 | typedef struct __attribute__((packed)) { 86 | u4 fieldIdx; 87 | u2 accessFlags; 88 | u4 declaringClassIdx; 89 | } vdexDepFieldRes_006; 90 | 91 | typedef struct __attribute__((packed)) { 92 | u4 methodIdx; 93 | u2 accessFlags; 94 | u4 declaringClassIdx; 95 | } vdexDepMethodRes_006; 96 | 97 | typedef struct __attribute__((packed)) { 98 | u2 typeIdx; 99 | } vdexDepUnvfyClass_006; 100 | 101 | typedef struct __attribute__((packed)) { 102 | u4 numberOfEntries; 103 | vdexDepClassRes_006 *pVdexDepClasses; 104 | } vdexDepClassResSet_006; 105 | 106 | typedef struct __attribute__((packed)) { 107 | u4 numberOfEntries; 108 | vdexDepFieldRes_006 *pVdexDepFields; 109 | } vdexDepFieldResSet_006; 110 | 111 | typedef struct __attribute__((packed)) { 112 | u4 numberOfEntries; 113 | vdexDepMethodRes_006 *pVdexDepMethods; 114 | } vdexDepMethodResSet_006; 115 | 116 | typedef struct __attribute__((packed)) { 117 | u4 numberOfEntries; 118 | vdexDepUnvfyClass_006 *pVdexDepUnvfyClasses; 119 | } vdexDepUnvfyClassesSet_006; 120 | 121 | // Verify if valid Vdex file 122 | bool vdex_006_isValidVdex(const u1 *); 123 | bool vdex_006_isMagicValid(const u1 *); 124 | bool vdex_006_isVersionValid(const u1 *); 125 | 126 | bool vdex_006_hasDexSection(const u1 *); 127 | u4 vdex_006_GetSizeOfChecksumsSection(const u1 *); 128 | const u1 *vdex_006_DexBegin(const u1 *); 129 | u4 vdex_006_DexBeginOffset(const u1 *); 130 | const u1 *vdex_006_DexEnd(const u1 *); 131 | u4 vdex_006_DexEndOffset(const u1 *); 132 | const u1 *vdex_006_GetNextDexFileData(const u1 *, u4 *); 133 | u4 vdex_006_GetLocationChecksum(const u1 *, u4); 134 | void vdex_006_SetLocationChecksum(const u1 *, u4, u4); 135 | void vdex_006_GetVerifierDeps(const u1 *, vdex_data_array_t *); 136 | void vdex_006_GetQuickeningInfo(const u1 *, vdex_data_array_t *); 137 | 138 | void vdex_006_dumpHeaderInfo(const u1 *); 139 | void vdex_006_dumpDepsInfo(const u1 *); 140 | bool vdex_006_SanityCheck(const u1 *, size_t); 141 | int vdex_006_process(const char *, const u1 *, size_t, const runArgs_t *); 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/vdex/vdex_010.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_010_H_ 24 | #define _VDEX_010_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_common.h" 29 | 30 | static const u1 kVdex010[] = { '0', '1', '0', '\0' }; 31 | 32 | typedef struct __attribute__((packed)) { 33 | u1 magic[4]; 34 | u1 version[4]; 35 | u4 numberOfDexFiles; 36 | u4 dexSize; 37 | u4 verifierDepsSize; 38 | u4 quickeningInfoSize; 39 | } vdexHeader_010; 40 | 41 | // VDEX files contain extracted DEX files. The VdexFile class maps the file to 42 | // memory and provides tools for accessing its individual sections. 43 | // 44 | // File format: 45 | // VdexFile::Header fixed-length header 46 | // 47 | // DEX[0] array of the input DEX files 48 | // DEX[1] the bytecode may have been quickened 49 | // ... 50 | // DEX[D] 51 | // QuickeningInfo 52 | // uint8[] quickening data 53 | // unaligned_uint32_t[2][] table of offsets pair: 54 | // uint32_t[0] contains code_item_offset 55 | // uint32_t[1] contains quickening data offset from the start 56 | // of QuickeningInfo 57 | // unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous 58 | // table for each dex file 59 | 60 | typedef struct __attribute__((packed)) { 61 | vdexHeader_010 *pVdexHeader; 62 | dexHeader *pDexFiles; 63 | } vdexFile_010; 64 | 65 | typedef struct __attribute__((packed)) { 66 | u4 numberOfStrings; 67 | const char **strings; 68 | } vdexDepStrings_010; 69 | 70 | typedef struct __attribute__((packed)) { 71 | u4 dstIndex; 72 | u4 srcIndex; 73 | } vdexDepSet_010; 74 | 75 | typedef struct __attribute__((packed)) { 76 | u2 typeIdx; 77 | u2 accessFlags; 78 | } vdexDepClassRes_010; 79 | 80 | typedef struct __attribute__((packed)) { 81 | u4 numberOfEntries; 82 | vdexDepSet_010 *pVdexDepSets; 83 | } vdexDepTypeSet_010; 84 | 85 | typedef struct __attribute__((packed)) { 86 | u4 fieldIdx; 87 | u2 accessFlags; 88 | u4 declaringClassIdx; 89 | } vdexDepFieldRes_010; 90 | 91 | typedef struct __attribute__((packed)) { 92 | u4 methodIdx; 93 | u2 accessFlags; 94 | u4 declaringClassIdx; 95 | } vdexDepMethodRes_010; 96 | 97 | typedef struct __attribute__((packed)) { 98 | u2 typeIdx; 99 | } vdexDepUnvfyClass_010; 100 | 101 | typedef struct __attribute__((packed)) { 102 | u4 numberOfEntries; 103 | vdexDepClassRes_010 *pVdexDepClasses; 104 | } vdexDepClassResSet_010; 105 | 106 | typedef struct __attribute__((packed)) { 107 | u4 numberOfEntries; 108 | vdexDepFieldRes_010 *pVdexDepFields; 109 | } vdexDepFieldResSet_010; 110 | 111 | typedef struct __attribute__((packed)) { 112 | u4 numberOfEntries; 113 | vdexDepMethodRes_010 *pVdexDepMethods; 114 | } vdexDepMethodResSet_010; 115 | 116 | typedef struct __attribute__((packed)) { 117 | u4 numberOfEntries; 118 | vdexDepUnvfyClass_010 *pVdexDepUnvfyClasses; 119 | } vdexDepUnvfyClassesSet_010; 120 | 121 | // Verify if valid Vdex file 122 | bool vdex_010_isValidVdex(const u1 *); 123 | bool vdex_010_isMagicValid(const u1 *); 124 | bool vdex_010_isVersionValid(const u1 *); 125 | 126 | bool vdex_010_hasDexSection(const u1 *); 127 | u4 vdex_010_GetSizeOfChecksumsSection(const u1 *); 128 | const u1 *vdex_010_DexBegin(const u1 *); 129 | u4 vdex_010_DexBeginOffset(const u1 *); 130 | const u1 *vdex_010_DexEnd(const u1 *); 131 | u4 vdex_010_DexEndOffset(const u1 *); 132 | const u1 *vdex_010_GetNextDexFileData(const u1 *, u4 *); 133 | u4 vdex_010_GetLocationChecksum(const u1 *, u4); 134 | void vdex_010_SetLocationChecksum(const u1 *, u4, u4); 135 | void vdex_010_GetVerifierDeps(const u1 *, vdex_data_array_t *); 136 | void vdex_010_GetQuickeningInfo(const u1 *, vdex_data_array_t *); 137 | 138 | void vdex_010_dumpHeaderInfo(const u1 *); 139 | void vdex_010_dumpDepsInfo(const u1 *); 140 | bool vdex_010_SanityCheck(const u1 *, size_t); 141 | int vdex_010_process(const char *, const u1 *, size_t, const runArgs_t *); 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "common.h" 30 | 31 | static unsigned int log_minLevel; 32 | static bool log_isTTY; 33 | static bool inside_line; 34 | static bool dis_enabled; 35 | static int log_fd; 36 | static FILE *log_disOut; 37 | 38 | __attribute__((constructor)) void log_init(void) { 39 | log_minLevel = l_INFO; 40 | log_fd = STDOUT_FILENO; 41 | log_isTTY = isatty(log_fd); 42 | log_disOut = stdout; 43 | } 44 | 45 | void log_setMinLevel(log_level_t dl) { log_minLevel = dl; } 46 | void log_setDisStatus(bool status) { dis_enabled = status; } 47 | bool log_getDisStatus() { return dis_enabled; } 48 | 49 | bool log_initLogFile(const char *logFile) { 50 | if (logFile == NULL) { 51 | return true; 52 | } 53 | 54 | log_disOut = fopen(logFile, "ab+"); 55 | if (log_disOut == NULL) { 56 | log_disOut = stdout; 57 | LOGMSG_P(l_ERROR, "Couldn't open logFile '%s'", logFile); 58 | return false; 59 | } 60 | return true; 61 | } 62 | 63 | void log_closeLogFile() { 64 | fflush(log_disOut); 65 | if (log_disOut != stdout) { 66 | fclose(log_disOut); 67 | } 68 | } 69 | 70 | void log_msg(log_level_t dl, 71 | bool perr, 72 | bool raw_print, 73 | bool is_display, 74 | const char *file, 75 | const char *func, 76 | int line, 77 | const char *fmt, 78 | ...) { 79 | struct { 80 | char *descr; 81 | char *prefix; 82 | } logLevels[] = { { "[FATAL]", "\033[1;31m" }, 83 | { "[ERROR]", "\033[1;35m" }, 84 | { "[WARNING]", "\033[1;33m" }, 85 | { "[INFO]", "\033[1m" }, 86 | { "[DEBUG]", "\033[0;37m" } }; 87 | 88 | char strerr[512]; 89 | if (perr) { 90 | snprintf(strerr, sizeof(strerr), "%s", strerror(errno)); 91 | } 92 | 93 | if (dl > log_minLevel) return; 94 | 95 | // stdout might be used from disassembler output. If so, flush before writing generic log entry 96 | if (dis_enabled && log_disOut == stdout) fflush(log_disOut); 97 | 98 | // Explicitly print display messages always to stdout and not to log file (if set) 99 | int curLogFd = log_fd; 100 | if (is_display) { 101 | curLogFd = STDOUT_FILENO; 102 | } 103 | 104 | struct tm tm; 105 | struct timeval tv; 106 | 107 | gettimeofday(&tv, NULL); 108 | localtime_r((const time_t *)&tv.tv_sec, &tm); 109 | 110 | if (inside_line && !raw_print) { 111 | dprintf(curLogFd, "\n"); 112 | } 113 | 114 | if (log_isTTY) { 115 | dprintf(curLogFd, "%s", logLevels[dl].prefix); 116 | } 117 | 118 | if (raw_print) { 119 | int fmtLen = strlen(fmt); 120 | if (fmtLen > 0 && fmt[fmtLen - 1] == '\n') { 121 | inside_line = false; 122 | } else { 123 | inside_line = true; 124 | } 125 | } else { 126 | if (!is_display && (log_minLevel >= l_DEBUG || !log_isTTY)) { 127 | dprintf(curLogFd, "%s [%d] %d/%02d/%02d %02d:%02d:%02d (%s:%d %s) ", logLevels[dl].descr, 128 | getpid(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, 129 | tm.tm_sec, file, line, func); 130 | } else { 131 | dprintf(curLogFd, "%s ", logLevels[dl].descr); 132 | } 133 | } 134 | 135 | va_list args; 136 | va_start(args, fmt); 137 | vdprintf(curLogFd, fmt, args); 138 | va_end(args); 139 | 140 | if (perr) { 141 | dprintf(curLogFd, ": %s", strerr); 142 | } 143 | 144 | if (log_isTTY) { 145 | dprintf(curLogFd, "\033[0m"); 146 | } 147 | 148 | if (!raw_print) dprintf(curLogFd, "\n"); 149 | 150 | if (dl == l_FATAL) { 151 | exitWrapper(EXIT_FAILURE); 152 | } 153 | } 154 | 155 | void log_dis(const char *fmt, ...) { 156 | if (!dis_enabled) return; 157 | va_list args; 158 | va_start(args, fmt); 159 | vfprintf(log_disOut, fmt, args); 160 | va_end(args); 161 | } 162 | 163 | void log_raw(const char *fmt, ...) { 164 | va_list args; 165 | va_start(args, fmt); 166 | vfprintf(stdout, fmt, args); 167 | va_end(args); 168 | } 169 | -------------------------------------------------------------------------------- /src/vdex/vdex_027.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_027_H_ 24 | #define _VDEX_027_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_common.h" 29 | 30 | static const u1 kVdexVersion_027[] = { '0', '2', '7', '\0' }; 31 | static const u4 kNumberOfSections_027 = 4; 32 | 33 | // VDEX files contain extracted DEX files. The VdexFile class maps the file to 34 | // memory and provides tools for accessing its individual sections. 35 | // 36 | // In the description below, D is the number of dex files. 37 | // 38 | // File format: 39 | // VdexFileHeader fixed-length header 40 | // VdexSectionHeader[kNumberOfSections] 41 | // 42 | // Checksum section 43 | // VdexChecksum[D] 44 | // 45 | // Optionally: 46 | // DexSection 47 | // DEX[0] array of the input DEX files 48 | // DEX[1] 49 | // ... 50 | // DEX[D-1] 51 | // 52 | // VerifierDeps 53 | // 4-byte alignment 54 | // uint32[D] DexFileDeps offsets for each dex file 55 | // DexFileDeps[D][] verification dependencies 56 | // 4-byte alignment 57 | // uint32[class_def_size] TypeAssignability offsets (kNotVerifiedMarker for a class 58 | // that isn't verified) 59 | // uint32 Offset of end of AssignabilityType sets 60 | // uint8[] AssignabilityType sets 61 | // 4-byte alignment 62 | // uint32 Number of strings 63 | // uint32[] String data offsets for each string 64 | // uint8[] String data 65 | 66 | typedef struct __attribute__((packed)) { 67 | u1 magic[4]; 68 | u1 vdexVersion[4]; 69 | u4 numberOfSections; 70 | } vdexHeader_027; 71 | 72 | typedef struct __attribute__((packed)) { 73 | u4 sectionKind; 74 | u4 sectionOffset; 75 | u4 sectionSize; 76 | } vdexSectionHeader_027; 77 | 78 | typedef struct __attribute__((packed)) { 79 | vdexHeader_027 *pVdexHeader; 80 | dexHeader *pDexFiles; 81 | } vdexFile_027; 82 | 83 | typedef struct __attribute__((packed)) { 84 | u4 numberOfStrings; 85 | const char **strings; 86 | } vdexDepStrings_027; 87 | 88 | typedef struct __attribute__((packed)) { 89 | u4 dstIndex; 90 | u4 srcIndex; 91 | } vdexDepSet_027; 92 | 93 | typedef struct __attribute__((packed)) { 94 | u2 typeIdx; 95 | u2 accessFlags; 96 | } vdexDepClassRes_027; 97 | 98 | typedef struct __attribute__((packed)) { 99 | u4 numberOfEntries; 100 | vdexDepSet_027 *pVdexDepSets; 101 | } vdexDepTypeSet_027; 102 | 103 | typedef struct __attribute__((packed)) { 104 | u4 fieldIdx; 105 | u2 accessFlags; 106 | u4 declaringClassIdx; 107 | } vdexDepFieldRes_027; 108 | 109 | typedef struct __attribute__((packed)) { 110 | u4 methodIdx; 111 | u2 accessFlags; 112 | u4 declaringClassIdx; 113 | } vdexDepMethodRes_027; 114 | 115 | typedef struct __attribute__((packed)) { 116 | u2 typeIdx; 117 | } vdexDepUnvfyClass_027; 118 | 119 | typedef struct __attribute__((packed)) { 120 | u4 numberOfEntries; 121 | vdexDepClassRes_027 *pVdexDepClasses; 122 | } vdexDepClassResSet_027; 123 | 124 | typedef struct __attribute__((packed)) { 125 | u4 numberOfEntries; 126 | vdexDepFieldRes_027 *pVdexDepFields; 127 | } vdexDepFieldResSet_027; 128 | 129 | typedef struct __attribute__((packed)) { 130 | u4 numberOfEntries; 131 | vdexDepMethodRes_027 *pVdexDepMethods; 132 | } vdexDepMethodResSet_027; 133 | 134 | typedef struct __attribute__((packed)) { 135 | u4 numberOfEntries; 136 | vdexDepUnvfyClass_027 *pVdexDepUnvfyClasses; 137 | } vdexDepUnvfyClassesSet_027; 138 | 139 | // Verify if valid Vdex file 140 | bool vdex_027_isValidVdex(const u1 *); 141 | bool vdex_027_isMagicValid(const u1 *); 142 | bool vdex_027_isVersionValid(const u1 *); 143 | 144 | const vdexSectionHeader_027 *vdex_027_GetSectionHeader(const u1 *, u4); 145 | bool vdex_027_hasDexSection(const u1 *); 146 | u4 vdex_027_GetNumberOfDexFiles(const u1 *); 147 | const u1 *vdex_027_DexBegin(const u1 *); 148 | u4 vdex_027_DexBeginOffset(const u1 *); 149 | const u1 *vdex_027_DexEnd(const u1 *); 150 | u4 vdex_027_DexEndOffset(const u1 *); 151 | const u1 *vdex_027_GetNextDexFileData(const u1 *, u4 *); 152 | u4 vdex_027_GetLocationChecksum(const u1 *, u4); 153 | void vdex_027_SetLocationChecksum(const u1 *, u4, u4); 154 | 155 | void vdex_027_dumpHeaderInfo(const u1 *); 156 | void vdex_027_dumpDepsInfo(const u1 *); 157 | bool vdex_027_SanityCheck(const u1 *, size_t); 158 | int vdex_027_process(const char *, const u1 *, size_t, const runArgs_t *); 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /src/vdex/vdex_019.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_019_H_ 24 | #define _VDEX_019_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_common.h" 29 | 30 | static const u1 kVdexDepsVer_019[] = { '0', '1', '9', '\0' }; 31 | static const u1 kVdexDexSectVer_019[] = { '0', '0', '2', '\0' }; 32 | static const u1 kDexSectVerEmpty_019[] = { '0', '0', '0', '\0' }; 33 | 34 | typedef struct __attribute__((packed)) { 35 | u1 magic[4]; 36 | u1 verifierDepsVersion[4]; 37 | u1 dexSectionVersion[4]; 38 | u4 numberOfDexFiles; 39 | u4 verifierDepsSize; 40 | } vdexHeader_019; 41 | 42 | typedef struct __attribute__((packed)) { 43 | u4 dexSize; 44 | u4 dexSharedDataSize; 45 | u4 quickeningInfoSize; 46 | } vdexDexSectHeader_019; 47 | 48 | // VDEX files contain extracted DEX files. The VdexFile class maps the file to 49 | // memory and provides tools for accessing its individual sections. 50 | // 51 | // File format: 52 | // VdexFile::VerifierDepsHeader fixed-length header 53 | // Dex file checksums 54 | // 55 | // Optionally: 56 | // VdexFile::DexSectionHeader fixed-length header 57 | // 58 | // quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. 59 | // DEX[0] array of the input DEX files, the bytecode may have been quickened. 60 | // quicken_table_off[1] 61 | // DEX[1] 62 | // ... 63 | // DEX[D] 64 | // 65 | // VerifierDeps 66 | // uint8[D][] verification dependencies 67 | // 68 | // Optionally: 69 | // QuickeningInfo 70 | // uint8[D][] quickening data 71 | // uint32[D][] quickening data offset tables 72 | 73 | typedef struct __attribute__((packed)) { 74 | vdexHeader_019 *pVdexHeader; 75 | dexHeader *pDexFiles; 76 | } vdexFile_019; 77 | 78 | typedef struct __attribute__((packed)) { 79 | u4 numberOfStrings; 80 | const char **strings; 81 | } vdexDepStrings_019; 82 | 83 | typedef struct __attribute__((packed)) { 84 | u4 dstIndex; 85 | u4 srcIndex; 86 | } vdexDepSet_019; 87 | 88 | typedef struct __attribute__((packed)) { 89 | u2 typeIdx; 90 | u2 accessFlags; 91 | } vdexDepClassRes_019; 92 | 93 | typedef struct __attribute__((packed)) { 94 | u4 numberOfEntries; 95 | vdexDepSet_019 *pVdexDepSets; 96 | } vdexDepTypeSet_019; 97 | 98 | typedef struct __attribute__((packed)) { 99 | u4 fieldIdx; 100 | u2 accessFlags; 101 | u4 declaringClassIdx; 102 | } vdexDepFieldRes_019; 103 | 104 | typedef struct __attribute__((packed)) { 105 | u4 methodIdx; 106 | u2 accessFlags; 107 | u4 declaringClassIdx; 108 | } vdexDepMethodRes_019; 109 | 110 | typedef struct __attribute__((packed)) { 111 | u2 typeIdx; 112 | } vdexDepUnvfyClass_019; 113 | 114 | typedef struct __attribute__((packed)) { 115 | u4 numberOfEntries; 116 | vdexDepClassRes_019 *pVdexDepClasses; 117 | } vdexDepClassResSet_019; 118 | 119 | typedef struct __attribute__((packed)) { 120 | u4 numberOfEntries; 121 | vdexDepFieldRes_019 *pVdexDepFields; 122 | } vdexDepFieldResSet_019; 123 | 124 | typedef struct __attribute__((packed)) { 125 | u4 numberOfEntries; 126 | vdexDepMethodRes_019 *pVdexDepMethods; 127 | } vdexDepMethodResSet_019; 128 | 129 | typedef struct __attribute__((packed)) { 130 | u4 numberOfEntries; 131 | vdexDepUnvfyClass_019 *pVdexDepUnvfyClasses; 132 | } vdexDepUnvfyClassesSet_019; 133 | 134 | // Verify if valid Vdex file 135 | bool vdex_019_isValidVdex(const u1 *); 136 | bool vdex_019_isMagicValid(const u1 *); 137 | bool vdex_019_isVersionValid(const u1 *); 138 | 139 | bool vdex_019_hasDexSection(const u1 *); 140 | u4 vdex_019_GetSizeOfChecksumsSection(const u1 *); 141 | const u1 *vdex_019_DexBegin(const u1 *); 142 | u4 vdex_019_DexBeginOffset(const u1 *); 143 | const u1 *vdex_019_DexEnd(const u1 *); 144 | u4 vdex_019_DexEndOffset(const u1 *); 145 | const u1 *vdex_019_GetNextDexFileData(const u1 *, u4 *); 146 | u4 vdex_019_GetLocationChecksum(const u1 *, u4); 147 | void vdex_019_SetLocationChecksum(const u1 *, u4, u4); 148 | void vdex_019_GetVerifierDeps(const u1 *, vdex_data_array_t *); 149 | void vdex_019_GetQuickeningInfo(const u1 *, vdex_data_array_t *); 150 | 151 | u4 vdex_019_GetDexSectionHeaderOffset(const u1 *); 152 | const vdexDexSectHeader_019 *vdex_019_GetDexSectionHeader(const u1 *); 153 | 154 | // Vdex 019 introduces an intermediate set of tables that contain the QuickeningInfo offsets for 155 | // each Dex file in the container 156 | void vdex_019_GetQuickenInfoOffsetTable(const u1 *, const vdex_data_array_t *, vdex_data_array_t *); 157 | 158 | void vdex_019_dumpHeaderInfo(const u1 *); 159 | void vdex_019_dumpDepsInfo(const u1 *); 160 | bool vdex_019_SanityCheck(const u1 *, size_t); 161 | int vdex_019_process(const char *, const u1 *, size_t, const runArgs_t *); 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /src/vdex/vdex_021.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _VDEX_021_H_ 24 | #define _VDEX_021_H_ 25 | 26 | #include "../common.h" 27 | #include "../dex.h" 28 | #include "vdex_common.h" 29 | 30 | static const u1 kVdexDepsVer_021[] = { '0', '2', '1', '\0' }; 31 | static const u1 kVdexDexSectVer_021[] = { '0', '0', '2', '\0' }; 32 | static const u1 kDexSectVerEmpty_021[] = { '0', '0', '0', '\0' }; 33 | 34 | typedef struct __attribute__((packed)) { 35 | u1 magic[4]; 36 | u1 verifierDepsVersion[4]; 37 | u1 dexSectionVersion[4]; 38 | u4 numberOfDexFiles; 39 | u4 verifierDepsSize; 40 | u4 bootclasspathChecksumsSize; 41 | u4 classLoaderContextSize; 42 | } vdexHeader_021; 43 | 44 | typedef struct __attribute__((packed)) { 45 | u4 dexSize; 46 | u4 dexSharedDataSize; 47 | u4 quickeningInfoSize; 48 | } vdexDexSectHeader_021; 49 | 50 | // VDEX files contain extracted DEX files. The VdexFile class maps the file to 51 | // memory and provides tools for accessing its individual sections. 52 | // 53 | // File format: 54 | // VdexFile::VerifierDepsHeader fixed-length header 55 | // Dex file checksums 56 | // 57 | // Optionally: 58 | // VdexFile::DexSectionHeader fixed-length header 59 | // 60 | // quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. 61 | // DEX[0] array of the input DEX files, the bytecode may have been quickened. 62 | // quicken_table_off[1] 63 | // DEX[1] 64 | // ... 65 | // DEX[D] 66 | // 67 | // VerifierDeps 68 | // uint8[D][] verification dependencies 69 | // 70 | // Optionally: 71 | // QuickeningInfo 72 | // uint8[D][] quickening data 73 | // uint32[D][] quickening data offset tables 74 | 75 | typedef struct __attribute__((packed)) { 76 | vdexHeader_021 *pVdexHeader; 77 | dexHeader *pDexFiles; 78 | } vdexFile_021; 79 | 80 | typedef struct __attribute__((packed)) { 81 | u4 numberOfStrings; 82 | const char **strings; 83 | } vdexDepStrings_021; 84 | 85 | typedef struct __attribute__((packed)) { 86 | u4 dstIndex; 87 | u4 srcIndex; 88 | } vdexDepSet_021; 89 | 90 | typedef struct __attribute__((packed)) { 91 | u2 typeIdx; 92 | u2 accessFlags; 93 | } vdexDepClassRes_021; 94 | 95 | typedef struct __attribute__((packed)) { 96 | u4 numberOfEntries; 97 | vdexDepSet_021 *pVdexDepSets; 98 | } vdexDepTypeSet_021; 99 | 100 | typedef struct __attribute__((packed)) { 101 | u4 fieldIdx; 102 | u2 accessFlags; 103 | u4 declaringClassIdx; 104 | } vdexDepFieldRes_021; 105 | 106 | typedef struct __attribute__((packed)) { 107 | u4 methodIdx; 108 | u2 accessFlags; 109 | u4 declaringClassIdx; 110 | } vdexDepMethodRes_021; 111 | 112 | typedef struct __attribute__((packed)) { 113 | u2 typeIdx; 114 | } vdexDepUnvfyClass_021; 115 | 116 | typedef struct __attribute__((packed)) { 117 | u4 numberOfEntries; 118 | vdexDepClassRes_021 *pVdexDepClasses; 119 | } vdexDepClassResSet_021; 120 | 121 | typedef struct __attribute__((packed)) { 122 | u4 numberOfEntries; 123 | vdexDepFieldRes_021 *pVdexDepFields; 124 | } vdexDepFieldResSet_021; 125 | 126 | typedef struct __attribute__((packed)) { 127 | u4 numberOfEntries; 128 | vdexDepMethodRes_021 *pVdexDepMethods; 129 | } vdexDepMethodResSet_021; 130 | 131 | typedef struct __attribute__((packed)) { 132 | u4 numberOfEntries; 133 | vdexDepUnvfyClass_021 *pVdexDepUnvfyClasses; 134 | } vdexDepUnvfyClassesSet_021; 135 | 136 | // Verify if valid Vdex file 137 | bool vdex_021_isValidVdex(const u1 *); 138 | bool vdex_021_isMagicValid(const u1 *); 139 | bool vdex_021_isVersionValid(const u1 *); 140 | 141 | bool vdex_021_hasDexSection(const u1 *); 142 | u4 vdex_021_GetSizeOfChecksumsSection(const u1 *); 143 | const u1 *vdex_021_DexBegin(const u1 *); 144 | u4 vdex_021_DexBeginOffset(const u1 *); 145 | const u1 *vdex_021_DexEnd(const u1 *); 146 | u4 vdex_021_DexEndOffset(const u1 *); 147 | const u1 *vdex_021_GetNextDexFileData(const u1 *, u4 *); 148 | u4 vdex_021_GetLocationChecksum(const u1 *, u4); 149 | void vdex_021_SetLocationChecksum(const u1 *, u4, u4); 150 | void vdex_021_GetVerifierDeps(const u1 *, vdex_data_array_t *); 151 | void vdex_021_GetQuickeningInfo(const u1 *, vdex_data_array_t *); 152 | void vdex_021_GetBootClassPathChecksumData(const u1 *, vdex_data_array_t *); 153 | void vdex_021_GetClassLoaderContextData(const u1 *, vdex_data_array_t *); 154 | 155 | u4 vdex_021_GetDexSectionHeaderOffset(const u1 *); 156 | const vdexDexSectHeader_021 *vdex_021_GetDexSectionHeader(const u1 *); 157 | 158 | // Vdex 021 introduces an intermediate set of tables that contain the QuickeningInfo offsets for 159 | // each Dex file in the container 160 | void vdex_021_GetQuickenInfoOffsetTable(const u1 *, const vdex_data_array_t *, vdex_data_array_t *); 161 | 162 | void vdex_021_dumpHeaderInfo(const u1 *); 163 | void vdex_021_dumpDepsInfo(const u1 *); 164 | bool vdex_021_SanityCheck(const u1 *, size_t); 165 | int vdex_021_process(const char *, const u1 *, size_t, const runArgs_t *); 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /src/vdex_api.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_api.h" 24 | 25 | #include 26 | 27 | #include "log.h" 28 | #include "out_writer.h" 29 | #include "utils.h" 30 | #include "vdex/vdex_006.h" 31 | #include "vdex/vdex_010.h" 32 | #include "vdex/vdex_019.h" 33 | #include "vdex/vdex_021.h" 34 | #include "vdex/vdex_027.h" 35 | 36 | bool vdexApi_initEnv(const u1 *cursor, vdex_api_env_t *env) { 37 | // Check if a supported Vdex version is found 38 | if (vdex_006_isValidVdex(cursor)) { 39 | LOGMSG(l_DEBUG, "Initializing environment for Vdex version '006'"); 40 | env->dumpHeaderInfo = vdex_006_dumpHeaderInfo; 41 | env->dumpDepsInfo = vdex_006_dumpDepsInfo; 42 | env->process = vdex_006_process; 43 | } else if (vdex_010_isValidVdex(cursor)) { 44 | LOGMSG(l_DEBUG, "Initializing environment for Vdex version '010'"); 45 | env->dumpHeaderInfo = vdex_010_dumpHeaderInfo; 46 | env->dumpDepsInfo = vdex_010_dumpDepsInfo; 47 | env->process = vdex_010_process; 48 | } else if (vdex_019_isValidVdex(cursor)) { 49 | LOGMSG(l_DEBUG, "Initializing environment for Vdex version '019'"); 50 | env->dumpHeaderInfo = vdex_019_dumpHeaderInfo; 51 | env->dumpDepsInfo = vdex_019_dumpDepsInfo; 52 | env->process = vdex_019_process; 53 | } else if (vdex_021_isValidVdex(cursor)) { 54 | LOGMSG(l_DEBUG, "Initializing environment for Vdex version '021'"); 55 | env->dumpHeaderInfo = vdex_021_dumpHeaderInfo; 56 | env->dumpDepsInfo = vdex_021_dumpDepsInfo; 57 | env->process = vdex_021_process; 58 | } else if (vdex_027_isValidVdex(cursor)) { 59 | LOGMSG(l_DEBUG, "Initializing environment for Vdex version '027'"); 60 | env->dumpHeaderInfo = vdex_027_dumpHeaderInfo; 61 | env->dumpDepsInfo = vdex_027_dumpDepsInfo; 62 | env->process = vdex_027_process; 63 | } else { 64 | LOGMSG(l_ERROR, "Unsupported Vdex version"); 65 | return false; 66 | } 67 | 68 | return true; 69 | } 70 | 71 | bool vdexApi_updateChecksums(const char *inVdexFileName, 72 | int nCsums, 73 | u4 *checksums, 74 | const runArgs_t *pRunArgs) { 75 | bool ret = false; 76 | off_t fileSz = 0; 77 | int srcfd = -1; 78 | u1 *buf = NULL; 79 | 80 | buf = utils_mapFileToRead(inVdexFileName, &fileSz, &srcfd); 81 | if (buf == NULL) { 82 | LOGMSG(l_ERROR, "'%s' open & map failed", inVdexFileName); 83 | return ret; 84 | } 85 | 86 | if (vdex_006_isValidVdex(buf)) { 87 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)buf; 88 | if ((u4)nCsums != pVdexHeader->numberOfDexFiles) { 89 | LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", 90 | nCsums, pVdexHeader->numberOfDexFiles) 91 | goto fini; 92 | } 93 | 94 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 95 | vdex_006_SetLocationChecksum(buf, i, checksums[i]); 96 | } 97 | } else if (vdex_010_isValidVdex(buf)) { 98 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)buf; 99 | if ((u4)nCsums != pVdexHeader->numberOfDexFiles) { 100 | LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", 101 | nCsums, pVdexHeader->numberOfDexFiles) 102 | goto fini; 103 | } 104 | 105 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 106 | vdex_010_SetLocationChecksum(buf, i, checksums[i]); 107 | } 108 | } else if (vdex_019_isValidVdex(buf)) { 109 | const vdexHeader_019 *pVdexHeader = (const vdexHeader_019 *)buf; 110 | if ((u4)nCsums != pVdexHeader->numberOfDexFiles) { 111 | LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", 112 | nCsums, pVdexHeader->numberOfDexFiles) 113 | goto fini; 114 | } 115 | 116 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 117 | vdex_019_SetLocationChecksum(buf, i, checksums[i]); 118 | } 119 | } else if (vdex_021_isValidVdex(buf)) { 120 | const vdexHeader_021 *pVdexHeader = (const vdexHeader_021 *)buf; 121 | if ((u4)nCsums != pVdexHeader->numberOfDexFiles) { 122 | LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", 123 | nCsums, pVdexHeader->numberOfDexFiles) 124 | goto fini; 125 | } 126 | 127 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 128 | vdex_021_SetLocationChecksum(buf, i, checksums[i]); 129 | } 130 | } else if (vdex_027_isValidVdex(buf)) { 131 | u4 numberOfDexFiles = vdex_027_GetNumberOfDexFiles(buf); 132 | if ((u4)nCsums != numberOfDexFiles) { 133 | LOGMSG(l_ERROR, "%d checksums loaded from file, although Vdex has %" PRIu32 " Dex entries", 134 | nCsums, numberOfDexFiles) 135 | goto fini; 136 | } 137 | 138 | for (u4 i = 0; i < numberOfDexFiles; ++i) { 139 | vdex_027_SetLocationChecksum(buf, i, checksums[i]); 140 | } 141 | } else { 142 | LOGMSG(l_ERROR, "Unsupported Vdex version - updateChecksums failed"); 143 | goto fini; 144 | } 145 | 146 | if (!outWriter_VdexFile(pRunArgs, inVdexFileName, buf, fileSz)) { 147 | LOGMSG(l_ERROR, "Failed to write updated Vdex file"); 148 | goto fini; 149 | } 150 | 151 | ret = true; 152 | 153 | fini: 154 | munmap(buf, fileSz); 155 | close(srcfd); 156 | return ret; 157 | } 158 | 159 | bool vdexApi_printApiLevel(const char *inVdexFileName) { 160 | bool ret = false; 161 | off_t fileSz = 0; 162 | int srcfd = -1; 163 | u1 *buf = NULL; 164 | 165 | buf = utils_mapFileToRead(inVdexFileName, &fileSz, &srcfd); 166 | if (buf == NULL) { 167 | LOGMSG(l_ERROR, "'%s' open & map failed", inVdexFileName); 168 | return ret; 169 | } 170 | 171 | if (vdex_006_isValidVdex(buf)) { 172 | log_raw("API-26\n"); 173 | } else if (vdex_010_isValidVdex(buf)) { 174 | log_raw("API-27\n"); 175 | } else if (vdex_019_isValidVdex(buf)) { 176 | log_raw("API-28\n"); 177 | } else if (vdex_021_isValidVdex(buf)) { 178 | log_raw("API-29\n"); 179 | } else if (vdex_027_isValidVdex(buf)) { 180 | log_raw("API-31\n"); 181 | } else { 182 | goto fini; 183 | } 184 | 185 | ret = true; 186 | 187 | fini: 188 | munmap(buf, fileSz); 189 | close(srcfd); 190 | return ret; 191 | } 192 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_010.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_decompiler_010.h" 24 | 25 | #include "../utils.h" 26 | 27 | static const u1 *quicken_info_ptr; 28 | static size_t quicken_info_number_of_indices; 29 | static size_t quicken_index; 30 | 31 | static u2 GetData(size_t index) { 32 | return quicken_info_ptr[index * 2] | ((u2)(quicken_info_ptr[index * 2 + 1]) << 8); 33 | } 34 | 35 | static size_t NumberOfIndices(size_t bytes) { return bytes / sizeof(u2); } 36 | 37 | static u2 *code_ptr; 38 | static u2 *code_end; 39 | static u4 dex_pc; 40 | static u4 cur_code_off; 41 | 42 | static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { 43 | code_ptr = pCode; 44 | code_end = pCode + codeSize; 45 | dex_pc = 0; 46 | cur_code_off = startCodeOff; 47 | } 48 | 49 | static bool isCodeIteratorDone() { return code_ptr >= code_end; } 50 | 51 | static void codeIteratorAdvance() { 52 | u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); 53 | code_ptr += instruction_size; 54 | dex_pc += instruction_size; 55 | cur_code_off += instruction_size * sizeof(u2); 56 | } 57 | 58 | static u2 NextIndex() { 59 | CHECK_LT(quicken_index, quicken_info_number_of_indices); 60 | const u2 ret = GetData(quicken_index); 61 | quicken_index++; 62 | return ret; 63 | } 64 | 65 | static bool DecompileNop(u2 *insns) { 66 | const u2 reference_index = NextIndex(); 67 | if (reference_index == kDexNoIndex16) { 68 | // This means it was a normal nop and not a check-cast. 69 | return false; 70 | } 71 | const u2 type_index = NextIndex(); 72 | dexInstr_SetOpcode(insns, CHECK_CAST); 73 | dexInstr_SetVRegA_21c(insns, reference_index); 74 | dexInstr_SetVRegB_21c(insns, type_index); 75 | 76 | return true; 77 | } 78 | 79 | static void DecompileInstanceFieldAccess(u2 *insns, Code new_opcode) { 80 | u2 index = NextIndex(); 81 | dexInstr_SetOpcode(insns, new_opcode); 82 | dexInstr_SetVRegC_22c(insns, index); 83 | } 84 | 85 | static void DecompileInvokeVirtual(u2 *insns, Code new_opcode, bool is_range) { 86 | u2 index = NextIndex(); 87 | dexInstr_SetOpcode(insns, new_opcode); 88 | if (is_range) { 89 | dexInstr_SetVRegB_3rc(insns, index); 90 | } else { 91 | dexInstr_SetVRegB_35c(insns, index); 92 | } 93 | } 94 | 95 | bool vdex_decompiler_010_decompile(const u1 *dexFileBuf, 96 | dexMethod *pDexMethod, 97 | const vdex_data_array_t *pQuickInfo, 98 | bool decompile_return_instruction) { 99 | if (pQuickInfo->size == 0 && !decompile_return_instruction) { 100 | return true; 101 | } 102 | 103 | dexCode *pDexCode = (dexCode *)(dexFileBuf + pDexMethod->codeOff); 104 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 105 | 106 | quicken_info_ptr = pQuickInfo->data; 107 | quicken_index = 0; 108 | quicken_info_number_of_indices = NumberOfIndices(pQuickInfo->size); 109 | 110 | log_dis(" quickening_size=%" PRIx32 " (%" PRIu32 ")\n", pQuickInfo->size, pQuickInfo->size); 111 | initCodeIterator(pDexCode->insns, pDexCode->insnsSize, startCodeOff); 112 | 113 | while (isCodeIteratorDone() == false) { 114 | bool hasCodeChange = true; 115 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 116 | switch (dexInstr_getOpcode(code_ptr)) { 117 | case RETURN_VOID_NO_BARRIER: 118 | if (decompile_return_instruction) { 119 | dexInstr_SetOpcode(code_ptr, RETURN_VOID); 120 | } 121 | break; 122 | case NOP: 123 | if (quicken_info_number_of_indices > 0) { 124 | // Only try to decompile NOP if there are more than 0 indices. Not having 125 | // any index happens when we unquicken a code item that only has 126 | // RETURN_VOID_NO_BARRIER as quickened instruction. 127 | hasCodeChange = DecompileNop(code_ptr); 128 | } 129 | break; 130 | case IGET_QUICK: 131 | DecompileInstanceFieldAccess(code_ptr, IGET); 132 | break; 133 | case IGET_WIDE_QUICK: 134 | DecompileInstanceFieldAccess(code_ptr, IGET_WIDE); 135 | break; 136 | case IGET_OBJECT_QUICK: 137 | DecompileInstanceFieldAccess(code_ptr, IGET_OBJECT); 138 | break; 139 | case IGET_BOOLEAN_QUICK: 140 | DecompileInstanceFieldAccess(code_ptr, IGET_BOOLEAN); 141 | break; 142 | case IGET_BYTE_QUICK: 143 | DecompileInstanceFieldAccess(code_ptr, IGET_BYTE); 144 | break; 145 | case IGET_CHAR_QUICK: 146 | DecompileInstanceFieldAccess(code_ptr, IGET_CHAR); 147 | break; 148 | case IGET_SHORT_QUICK: 149 | DecompileInstanceFieldAccess(code_ptr, IGET_SHORT); 150 | break; 151 | case IPUT_QUICK: 152 | DecompileInstanceFieldAccess(code_ptr, IPUT); 153 | break; 154 | case IPUT_BOOLEAN_QUICK: 155 | DecompileInstanceFieldAccess(code_ptr, IPUT_BOOLEAN); 156 | break; 157 | case IPUT_BYTE_QUICK: 158 | DecompileInstanceFieldAccess(code_ptr, IPUT_BYTE); 159 | break; 160 | case IPUT_CHAR_QUICK: 161 | DecompileInstanceFieldAccess(code_ptr, IPUT_CHAR); 162 | break; 163 | case IPUT_SHORT_QUICK: 164 | DecompileInstanceFieldAccess(code_ptr, IPUT_SHORT); 165 | break; 166 | case IPUT_WIDE_QUICK: 167 | DecompileInstanceFieldAccess(code_ptr, IPUT_WIDE); 168 | break; 169 | case IPUT_OBJECT_QUICK: 170 | DecompileInstanceFieldAccess(code_ptr, IPUT_OBJECT); 171 | break; 172 | case INVOKE_VIRTUAL_QUICK: 173 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL, false); 174 | break; 175 | case INVOKE_VIRTUAL_RANGE_QUICK: 176 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL_RANGE, true); 177 | break; 178 | default: 179 | hasCodeChange = false; 180 | break; 181 | } 182 | 183 | if (hasCodeChange) { 184 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, true); 185 | } 186 | codeIteratorAdvance(); 187 | } 188 | 189 | if (quicken_index != quicken_info_number_of_indices) { 190 | if (quicken_index == 0) { 191 | LOGMSG(l_ERROR, 192 | "Failed to use any value in quickening info, potentially due to duplicate methods."); 193 | } else { 194 | LOGMSG(l_ERROR, "Failed to use all values in quickening info, '%zx' items not processed", 195 | quicken_info_number_of_indices - quicken_index); 196 | return false; 197 | } 198 | } 199 | 200 | return true; 201 | } 202 | 203 | void vdex_decompiler_010_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { 204 | dexCode *pDexCode = (dexCode *)(dexFileBuf + pDexMethod->codeOff); 205 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 206 | initCodeIterator(pDexCode->insns, pDexCode->insnsSize, startCodeOff); 207 | while (isCodeIteratorDone() == false) { 208 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 209 | codeIteratorAdvance(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_006.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_decompiler_006.h" 24 | 25 | #include "../utils.h" 26 | 27 | static const u1 *quickening_info_ptr; 28 | static const u1 *quickening_info_end; 29 | 30 | static u2 *code_ptr; 31 | static u2 *code_end; 32 | static u4 dex_pc; 33 | static u4 cur_code_off; 34 | 35 | static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { 36 | code_ptr = pCode; 37 | code_end = pCode + codeSize; 38 | dex_pc = 0; 39 | cur_code_off = startCodeOff; 40 | } 41 | 42 | static bool isCodeIteratorDone() { return code_ptr >= code_end; } 43 | 44 | static void codeIteratorAdvance() { 45 | u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); 46 | code_ptr += instruction_size; 47 | dex_pc += instruction_size; 48 | cur_code_off += instruction_size * sizeof(u2); 49 | } 50 | 51 | static u2 GetIndexAt(u4 dex_pc) { 52 | // Note that as a side effect, dex_readULeb128 update the given pointer 53 | // to the new position in the buffer. 54 | CHECK_LT(quickening_info_ptr, quickening_info_end); 55 | u4 quickened_pc = dex_readULeb128(&quickening_info_ptr); 56 | CHECK_LT(quickening_info_ptr, quickening_info_end); 57 | u2 index = dex_readULeb128(&quickening_info_ptr); 58 | CHECK_LE(quickening_info_ptr, quickening_info_end); 59 | CHECK_EQ(quickened_pc, dex_pc); 60 | return index; 61 | } 62 | 63 | static bool DecompileNop(u2 *insns, u4 dex_pc) { 64 | if (quickening_info_ptr == quickening_info_end) { 65 | return false; 66 | } 67 | const u1 *temporary_pointer = quickening_info_ptr; 68 | u4 quickened_pc = dex_readULeb128(&temporary_pointer); 69 | if (quickened_pc != dex_pc) { 70 | return false; 71 | } 72 | u2 reference_index = GetIndexAt(dex_pc); 73 | u2 type_index = GetIndexAt(dex_pc); 74 | dexInstr_SetOpcode(insns, CHECK_CAST); 75 | dexInstr_SetVRegA_21c(insns, reference_index); 76 | dexInstr_SetVRegB_21c(insns, type_index); 77 | 78 | return true; 79 | } 80 | 81 | static void DecompileInstanceFieldAccess(u2 *insns, u4 dex_pc, Code new_opcode) { 82 | u2 index = GetIndexAt(dex_pc); 83 | dexInstr_SetOpcode(insns, new_opcode); 84 | dexInstr_SetVRegC_22c(insns, index); 85 | } 86 | 87 | static void DecompileInvokeVirtual(u2 *insns, u4 dex_pc, Code new_opcode, bool is_range) { 88 | u2 index = GetIndexAt(dex_pc); 89 | dexInstr_SetOpcode(insns, new_opcode); 90 | if (is_range) { 91 | dexInstr_SetVRegB_3rc(insns, index); 92 | } else { 93 | dexInstr_SetVRegB_35c(insns, index); 94 | } 95 | } 96 | 97 | bool vdex_decompiler_006_decompile(const u1 *dexFileBuf, 98 | dexMethod *pDexMethod, 99 | const u1 *quickening_info, 100 | u4 quickening_size, 101 | bool decompile_return_instruction) { 102 | if (quickening_size == 0 && !decompile_return_instruction) { 103 | return true; 104 | } 105 | 106 | dexCode *pDexCode = (dexCode *)(dexFileBuf + pDexMethod->codeOff); 107 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 108 | 109 | quickening_info_ptr = quickening_info; 110 | quickening_info_end = quickening_info + quickening_size; 111 | log_dis(" quickening_size=%" PRIx32 " (%" PRIu32 ")\n", quickening_size, quickening_size); 112 | initCodeIterator(pDexCode->insns, pDexCode->insnsSize, startCodeOff); 113 | 114 | while (isCodeIteratorDone() == false) { 115 | bool hasCodeChange = true; 116 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 117 | switch (dexInstr_getOpcode(code_ptr)) { 118 | case RETURN_VOID_NO_BARRIER: 119 | if (decompile_return_instruction) { 120 | dexInstr_SetOpcode(code_ptr, RETURN_VOID); 121 | } 122 | break; 123 | case NOP: 124 | hasCodeChange = DecompileNop(code_ptr, dex_pc); 125 | break; 126 | case IGET_QUICK: 127 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET); 128 | break; 129 | case IGET_WIDE_QUICK: 130 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_WIDE); 131 | break; 132 | case IGET_OBJECT_QUICK: 133 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_OBJECT); 134 | break; 135 | case IGET_BOOLEAN_QUICK: 136 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_BOOLEAN); 137 | break; 138 | case IGET_BYTE_QUICK: 139 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_BYTE); 140 | break; 141 | case IGET_CHAR_QUICK: 142 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_CHAR); 143 | break; 144 | case IGET_SHORT_QUICK: 145 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IGET_SHORT); 146 | break; 147 | case IPUT_QUICK: 148 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT); 149 | break; 150 | case IPUT_BOOLEAN_QUICK: 151 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_BOOLEAN); 152 | break; 153 | case IPUT_BYTE_QUICK: 154 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_BYTE); 155 | break; 156 | case IPUT_CHAR_QUICK: 157 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_CHAR); 158 | break; 159 | case IPUT_SHORT_QUICK: 160 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_SHORT); 161 | break; 162 | case IPUT_WIDE_QUICK: 163 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_WIDE); 164 | break; 165 | case IPUT_OBJECT_QUICK: 166 | DecompileInstanceFieldAccess(code_ptr, dex_pc, IPUT_OBJECT); 167 | break; 168 | case INVOKE_VIRTUAL_QUICK: 169 | DecompileInvokeVirtual(code_ptr, dex_pc, INVOKE_VIRTUAL, false); 170 | break; 171 | case INVOKE_VIRTUAL_RANGE_QUICK: 172 | DecompileInvokeVirtual(code_ptr, dex_pc, INVOKE_VIRTUAL_RANGE, true); 173 | break; 174 | default: 175 | hasCodeChange = false; 176 | break; 177 | } 178 | 179 | if (hasCodeChange) { 180 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, true); 181 | } 182 | codeIteratorAdvance(); 183 | } 184 | 185 | if (quickening_info_ptr != quickening_info_end) { 186 | if (quickening_info_ptr == quickening_info_end) { 187 | LOGMSG(l_ERROR, 188 | "Failed to use any value in quickening info, potentially due to duplicate methods."); 189 | } else { 190 | LOGMSG(l_ERROR, "Failed to use all values in quickening info, '%zx' items not processed", 191 | quickening_info_end - quickening_info_ptr); 192 | return false; 193 | } 194 | } 195 | 196 | return true; 197 | } 198 | 199 | void vdex_decompiler_006_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { 200 | dexCode *pDexCode = (dexCode *)(dexFileBuf + pDexMethod->codeOff); 201 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 202 | initCodeIterator(pDexCode->insns, pDexCode->insnsSize, startCodeOff); 203 | while (isCodeIteratorDone() == false) { 204 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 205 | codeIteratorAdvance(); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /tools/deodex/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # vdexExtractor 4 | # ----------------------------------------- 5 | # 6 | # Anestis Bechtsoudis 7 | # Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | set -e # fail on unhandled error 23 | set -u # fail on undefined variable 24 | # set -x # debug 25 | 26 | readonly TOOL_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 27 | readonly CONSTS_SCRIPT="$TOOL_ROOT/constants.sh" 28 | readonly VDEX_EXTRACTOR_BIN="$TOOL_ROOT/../../bin/vdexExtractor" 29 | 30 | declare -a SYS_TOOLS=("mkdir" "dirname" "wget" "shasum" "unzip") 31 | readonly HOST_OS="$(uname -s)" 32 | 33 | info() { echo -e "[INFO]: $*" 1>&2; } 34 | warn() { echo -e "[WARN]: $*" 1>&2; } 35 | error() { echo -e "[ERR ]: $*" 1>&2; } 36 | debug() { echo -e "[DBG ]: $*" 1>&2; } 37 | log() { echo -e " $*" 1>&2; } 38 | userIn() { echo -en "[IN ]: $*" 1>&2; } 39 | 40 | abort() { 41 | exit "$1" 42 | } 43 | 44 | usage() { 45 | cat <<_EOF 46 | Usage: $(basename "$0") [options] 47 | options: 48 | -i|--input : Directory with Vdex files or single file 49 | -o|--output : Directory to save deodex'ed resources (default is '.') 50 | -k|--keep : Keep intermediate files (default 'false') 51 | -h|--help : This help message 52 | _EOF 53 | abort 1 54 | } 55 | 56 | commandExists() { 57 | type "$1" &> /dev/null 58 | } 59 | 60 | deps_download() { 61 | local api_level="$1" 62 | 63 | local download_url 64 | local out_file="$TOOL_ROOT/hostTools/$HOST_OS/api-$api_level/deps.zip" 65 | mkdir -p "$(dirname "$out_file")" 66 | 67 | 68 | if [[ "$HOST_OS" == "Darwin" ]]; then 69 | download_url="D_DEPS_URL_$api_level" 70 | else 71 | download_url="L_DEPS_URL_$api_level" 72 | fi 73 | 74 | wget -O "$out_file" "${!download_url}" || { 75 | echo "dependencies download failed" 76 | abort 1 77 | } 78 | 79 | unzip -qq -o "$out_file" -d "$TOOL_ROOT/hostTools/$HOST_OS/api-$api_level" || { 80 | echo "dependencies unzip failed" 81 | abort 1 82 | } 83 | } 84 | 85 | needs_deps_update() { 86 | local api_level="$1" 87 | local deps_zip deps_cur_sig deps_latest_sig 88 | 89 | deps_zip="$TOOL_ROOT/hostTools/$HOST_OS/api-$api_level/deps.zip" 90 | deps_cur_sig=$(shasum -a256 "$deps_zip" | cut -d ' ' -f1) 91 | if [[ "$HOST_OS" == "Darwin" ]]; then 92 | deps_latest_sig="D_DEPS_$api_level""_SIG" 93 | else 94 | deps_latest_sig="L_DEPS_$api_level""_SIG" 95 | fi 96 | 97 | if [[ "${!deps_latest_sig}" == "$deps_cur_sig" ]]; then 98 | return 1 99 | else 100 | return 0 101 | fi 102 | } 103 | 104 | deps_prepare_env() { 105 | local api_level="$1" 106 | if [ ! -f "$TOOL_ROOT/hostTools/$HOST_OS/api-$api_level/bin/compact_dex_converter" ]; then 107 | echo "First run detected - downloading compact_dex_converter bin & lib dependencies" 108 | deps_download "$api_level" 109 | fi 110 | 111 | if needs_deps_update "$api_level"; then 112 | echo "Outdated version detected - downloading compact_dex_converter bin & lib dependencies" 113 | deps_download "$api_level" 114 | fi 115 | } 116 | 117 | trap "abort 1" SIGINT SIGTERM 118 | . "$CONSTS_SCRIPT" 119 | 120 | INPUT_DIR="" 121 | OUTPUT_DIR="$(pwd)" 122 | KEEP_TMP=false 123 | 124 | for i in "${SYS_TOOLS[@]}" 125 | do 126 | if ! commandExists "$i"; then 127 | error "'$i' command not found" 128 | abort 1 129 | fi 130 | done 131 | 132 | while [[ $# -gt 0 ]] 133 | do 134 | arg="$1" 135 | case $arg in 136 | -o|--output) 137 | OUTPUT_DIR="$2" 138 | shift 139 | ;; 140 | -i|--input) 141 | INPUT_DIR="$2" 142 | shift 143 | ;; 144 | -k|--keep) 145 | KEEP_TMP=true 146 | ;; 147 | -h|--help) 148 | usage 149 | ;; 150 | *) 151 | error "Invalid argument '$1'" 152 | usage 153 | ;; 154 | esac 155 | shift 156 | done 157 | 158 | if [[ "$INPUT_DIR" == "" ]]; then 159 | error "Missing input directory" 160 | usage 161 | fi 162 | 163 | # Prepare output directory 164 | if [ ! -d "$OUTPUT_DIR" ]; then 165 | mkdir -p "$OUTPUT_DIR" || { 166 | error "Failed to create output directory" 167 | abort 1 168 | } 169 | fi 170 | 171 | # Intermediate files as exported from vdexExtractor 172 | decompiled_output="$OUTPUT_DIR/vdexExtractor_decompiled" 173 | mkdir -p "$decompiled_output" 174 | rm -rf "${decompiled_output:?}"/* 175 | 176 | # Final output 177 | deodexed_output="$OUTPUT_DIR/vdexExtractor_deodexed" 178 | mkdir -p "$deodexed_output" 179 | rm -rf "${deodexed_output:?}"/* 180 | 181 | # Check if tool found 182 | if [ ! -f "$VDEX_EXTRACTOR_BIN" ]; then 183 | error "vdexExtractor binary not found in '$VDEX_EXTRACTOR_BIN'" 184 | abort 1 185 | fi 186 | 187 | vdexFound=$(find "$INPUT_DIR" -type f -name "*.vdex" | wc -l | tr -d ' ') 188 | info "Processing $vdexFound input Vdex files" 189 | 190 | # Manually iterate input since we want to take API-level decisions while also supporting reading 191 | # mixed-API files in same input 192 | find "$INPUT_DIR" -name "*.vdex" | while read -r file 193 | do 194 | binName=$(basename "$file" ".vdex") 195 | outDecBase="$decompiled_output/$binName" 196 | outDec="$outDecBase/decompiled" 197 | mkdir -p "$outDec" 198 | decLog="$outDecBase/dec_log.txt" 199 | convLog="$outDecBase/conv_log.txt" 200 | 201 | outDeodexBase="$deodexed_output/$binName" 202 | mkdir -p "$outDeodexBase" 203 | 204 | $VDEX_EXTRACTOR_BIN -i "$file" -o "$outDec" --ignore-crc-error &> "$decLog" || { 205 | error "vdexExtractor execution failed" 206 | cat "$decLog" 207 | abort 1 208 | } 209 | 210 | dexFound=0 211 | dexFound=$(find "$outDec" -type f -name "*.dex" | wc -l | tr -d ' ') 212 | if [ "$dexFound" -eq 0 ]; then 213 | dexFound=$(find "$outDec" -type f -name "*.cdex" | wc -l | tr -d ' ') 214 | if [ "$dexFound" -eq 0 ]; then 215 | warn "Skipping '$binName' since no decompiled resources found" 216 | rm -rf "$outDeodexBase" 217 | continue 218 | fi 219 | # If CompactDex files, we need to convert first to standard Dex 220 | # First detect the API level 221 | apiLevel=$($VDEX_EXTRACTOR_BIN --get-api -i "$file" || echo "") 222 | if ! echo "$apiLevel" | grep -qoE 'API-[0-9]{1,2}'; then 223 | echo "Invalid Android API level '$apiLevel'" 224 | abort 1 225 | fi 226 | apiLevel=$(echo "${apiLevel//-/_}") 227 | 228 | # Then check if the corresponding precompiled hostUtils have been downloaded 229 | deps_prepare_env "$apiLevel" 230 | cdexConvBin="$TOOL_ROOT/hostTools/$HOST_OS/api-$apiLevel/bin/compact_dex_converter" 231 | 232 | # Then convert each CompactDex file 233 | cdexFiles=($(find "$outDec" -type f -name "*.cdex")) 234 | $cdexConvBin -w "$outDeodexBase" "${cdexFiles[@]}" &> "$convLog" || { 235 | error "CompactDex conversation failed for '$file'" 236 | cat "$convLog" 237 | abort 1 238 | } 239 | 240 | # Finally change file extension to dex 241 | find "$outDeodexBase" -type f | while read -r cdexFile 242 | do 243 | fileName=$(basename "$cdexFile" .cdex) 244 | mv "$cdexFile" "$outDeodexBase/$fileName.dex" 245 | done 246 | else 247 | # If StandardDex files, copy as is since they're fully deodexed 248 | cp "$outDec"/* "$outDeodexBase"/ 249 | fi 250 | done 251 | 252 | appsDeodexed=$(find "$deodexed_output" -maxdepth 1 ! -path "$deodexed_output" -type d | wc -l | tr -d ' ') 253 | info "$appsDeodexed binaries have been successfully deodexed" 254 | 255 | if [ $KEEP_TMP = false ]; then 256 | rm -rf "$decompiled_output" 257 | fi 258 | 259 | abort 0 260 | -------------------------------------------------------------------------------- /src/dex_modifiers.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _DEX_MODIFIERS_H_ 24 | #define _DEX_MODIFIERS_H_ 25 | 26 | #include "common.h" 27 | 28 | static const u4 kAccPublic = 0x0001; // class, field, method, ic 29 | static const u4 kAccPrivate = 0x0002; // field, method, ic 30 | static const u4 kAccProtected = 0x0004; // field, method, ic 31 | static const u4 kAccStatic = 0x0008; // field, method, ic 32 | static const u4 kAccFinal = 0x0010; // class, field, method, ic 33 | static const u4 kAccSynchronized = 0x0020; // method (only allowed on natives) 34 | static const u4 kAccSuper = 0x0020; // class (not used in dex) 35 | static const u4 kAccVolatile = 0x0040; // field 36 | static const u4 kAccBridge = 0x0040; // method (1.5) 37 | static const u4 kAccTransient = 0x0080; // field 38 | static const u4 kAccVarargs = 0x0080; // method (1.5) 39 | static const u4 kAccNative = 0x0100; // method 40 | static const u4 kAccInterface = 0x0200; // class, ic 41 | static const u4 kAccAbstract = 0x0400; // class, method, ic 42 | static const u4 kAccStrict = 0x0800; // method 43 | static const u4 kAccSynthetic = 0x1000; // class, field, method, ic 44 | static const u4 kAccAnnotation = 0x2000; // class, ic (1.5) 45 | static const u4 kAccEnum = 0x4000; // class, field, ic (1.5) 46 | 47 | static const u4 kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16) 48 | 49 | // The following flags are used to insert hidden API access flags into boot 50 | // class path dex files. They are decoded by DexFile::ClassDataItemIterator and 51 | // removed from the access flags before used by the runtime. 52 | static const u4 kAccDexHiddenBit = 0x00000020; // field, method (not native) 53 | static const u4 kAccDexHiddenBitNative = 0x00000200; // method (native) 54 | 55 | static const u4 kAccConstructor = 0x00010000; // method (dex only) <(cl)init> 56 | static const u4 kAccDeclaredSynchronized = 0x00020000; // method (dex only) 57 | static const u4 kAccClassIsProxy = 0x00040000; // class (dex only) 58 | // Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its 59 | // declaring class. This flag may only be applied to methods. 60 | static const u4 kAccObsoleteMethod = 0x00040000; // method (runtime) 61 | // Used by a method to denote that its execution does not need to go through slow path interpreter. 62 | static const u4 kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) 63 | // Used by a class to denote that the verifier has attempted to check it at least once. 64 | static const u4 kAccVerificationAttempted = 0x00080000; // class (runtime) 65 | static const u4 kAccSkipHiddenApiChecks = 0x00100000; // class (runtime) 66 | // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent 67 | // that it was copied from its declaring class into another class. All methods marked kAccMiranda 68 | // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ 69 | // array of a concrete class will also have this bit set. 70 | static const u4 kAccCopied = 0x00100000; // method (runtime) 71 | static const u4 kAccMiranda = 0x00200000; // method (runtime, not native) 72 | static const u4 kAccDefault = 0x00400000; // method (runtime) 73 | // Native method flags are set when linking the methods based on the presence of the 74 | // @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility. 75 | // Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods. 76 | static const u4 kAccFastNative = 0x00080000; // method (runtime; native only) 77 | static const u4 kAccCriticalNative = 0x00200000; // method (runtime; native only) 78 | 79 | // Set by the JIT when clearing profiling infos to denote that a method was previously warm. 80 | static const u4 kAccPreviouslyWarm = 0x00800000; // method (runtime) 81 | 82 | // This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know 83 | // if any particular method needs to be a default conflict. Used to figure out at runtime if 84 | // invoking this method will throw an exception. 85 | static const u4 kAccDefaultConflict = 0x01000000; // method (runtime) 86 | 87 | // Set by the verifier for a method we do not want the compiler to compile. 88 | static const u4 kAccCompileDontBother = 0x02000000; // method (runtime) 89 | 90 | // Set by the verifier for a method that could not be verified to follow structured locking. 91 | static const u4 kAccMustCountLocks = 0x04000000; // method (runtime) 92 | 93 | // Set by the class linker for a method that has only one implementation for a 94 | // virtual call. 95 | static const u4 kAccSingleImplementation = 0x08000000; // method (runtime) 96 | 97 | static const u4 kAccHiddenApiBits = 0x30000000; // field, method 98 | 99 | // Not currently used, except for intrinsic methods where these bits 100 | // are part of the intrinsic ordinal. 101 | static const u4 kAccMayBeUnusedBits = 0x40000000; 102 | 103 | // Set by the compiler driver when compiling boot classes with instrinsic methods. 104 | static const u4 kAccIntrinsic = 0x80000000; // method (runtime) 105 | 106 | // Special runtime-only flags. 107 | // Interface and all its super-interfaces with default methods have been recursively initialized. 108 | static const u4 kAccRecursivelyInitialized = 0x20000000; 109 | // Interface declares some default method. 110 | static const u4 kAccHasDefaultMethod = 0x40000000; 111 | // class/ancestor overrides finalize() 112 | static const u4 kAccClassIsFinalizable = 0x80000000; 113 | 114 | // Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags 115 | // which overlap are not valid when kAccIntrinsic is set. 116 | #define kAccIntrinsicBits \ 117 | (kAccMayBeUnusedBits | kAccHiddenApiBits | kAccSingleImplementation | kAccMustCountLocks | \ 118 | kAccCompileDontBother | kAccDefaultConflict | kAccPreviouslyWarm) 119 | 120 | // Valid (meaningful) bits for a field. 121 | #define kAccValidFieldFlags \ 122 | (kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccVolatile | \ 123 | kAccTransient | kAccSynthetic | kAccEnum) 124 | 125 | // Valid (meaningful) bits for a method. 126 | #define kAccValidMethodFlags \ 127 | (kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccSynchronized | \ 128 | kAccBridge | kAccVarargs | kAccNative | kAccAbstract | kAccStrict | kAccSynthetic | \ 129 | kAccConstructor | kAccDeclaredSynchronized) 130 | 131 | // Valid (meaningful) bits for a class (not interface). 132 | // Note 1. These are positive bits. Other bits may have to be zero. 133 | // Note 2. Inner classes can expose more access flags to Java programs. That is handled by libcore. 134 | #define kAccValidClassFlags \ 135 | (kAccPublic | kAccFinal | kAccSuper | kAccAbstract | kAccSynthetic | kAccEnum) 136 | 137 | // Valid (meaningful) bits for an interface. 138 | // Note 1. Annotations are interfaces. 139 | // Note 2. These are positive bits. Other bits may have to be zero. 140 | // Note 3. Inner classes can expose more access flags to Java programs. That is handled by libcore. 141 | #define kAccValidInterfaceFlags \ 142 | (kAccPublic | kAccInterface | kAccAbstract | kAccSynthetic | kAccAnnotation) 143 | 144 | #define kAccVisibilityFlags (kAccPublic | kAccPrivate | kAccProtected) 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /src/vdex/vdex_010.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_010.h" 24 | 25 | #include "../out_writer.h" 26 | #include "../utils.h" 27 | #include "vdex_backend_010.h" 28 | 29 | bool vdex_010_isMagicValid(const u1 *cursor) { 30 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 31 | return (memcmp(pVdexHeader->magic, kVdexMagic, sizeof(kVdexMagic)) == 0); 32 | } 33 | 34 | bool vdex_010_isVersionValid(const u1 *cursor) { 35 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 36 | return (memcmp(pVdexHeader->version, kVdex010, sizeof(kVdex010)) == 0); 37 | } 38 | 39 | bool vdex_010_isValidVdex(const u1 *cursor) { 40 | return vdex_010_isMagicValid(cursor) && vdex_010_isVersionValid(cursor); 41 | } 42 | 43 | bool vdex_010_hasDexSection(const u1 *cursor) { 44 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 45 | return pVdexHeader->dexSize != 0; 46 | } 47 | 48 | u4 vdex_010_GetSizeOfChecksumsSection(const u1 *cursor) { 49 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 50 | return sizeof(VdexChecksum) * pVdexHeader->numberOfDexFiles; 51 | } 52 | 53 | const u1 *vdex_010_DexBegin(const u1 *cursor) { 54 | return cursor + sizeof(vdexHeader_010) + vdex_010_GetSizeOfChecksumsSection(cursor); 55 | } 56 | 57 | u4 vdex_010_DexBeginOffset(const u1 *cursor) { 58 | return sizeof(vdexHeader_010) + vdex_010_GetSizeOfChecksumsSection(cursor); 59 | } 60 | 61 | const u1 *vdex_010_DexEnd(const u1 *cursor) { 62 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 63 | return vdex_010_DexBegin(cursor) + pVdexHeader->dexSize; 64 | } 65 | 66 | u4 vdex_010_DexEndOffset(const u1 *cursor) { 67 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 68 | return vdex_010_DexBeginOffset(cursor) + pVdexHeader->dexSize; 69 | } 70 | 71 | const u1 *vdex_010_GetNextDexFileData(const u1 *cursor, u4 *curOffset) { 72 | if (*curOffset == 0) { 73 | if (vdex_010_hasDexSection(cursor)) { 74 | const u1 *dexBuf = vdex_010_DexBegin(cursor); 75 | *curOffset = sizeof(vdexHeader_010) + vdex_010_GetSizeOfChecksumsSection(cursor); 76 | LOGMSG(l_DEBUG, "Processing first Dex file at offset:0x%x", *curOffset); 77 | 78 | // Adjust offset to point at the end of current Dex file 79 | *curOffset += dex_getFileSize(dexBuf); 80 | return dexBuf; 81 | } else { 82 | LOGMSG(l_ERROR, "Vdex file has no Dex entries to process"); 83 | return NULL; 84 | } 85 | } else { 86 | // Check boundaries 87 | const u1 *dexBuf = cursor + *curOffset; 88 | const u1 *dexBufMax = dexBuf + dex_getFileSize(dexBuf); 89 | if (dexBufMax == vdex_010_DexEnd(cursor)) { 90 | LOGMSG(l_DEBUG, "Processing last Dex file at offset:0x%x", *curOffset); 91 | } else if (dexBufMax < vdex_010_DexEnd(cursor)) { 92 | LOGMSG(l_DEBUG, "Processing Dex file at offset:0x%x", *curOffset); 93 | } else { 94 | LOGMSG(l_ERROR, "Invalid cursor offset '0x%x'", *curOffset); 95 | return NULL; 96 | } 97 | 98 | // Adjust curOffset to point at the end of current Dex file 99 | *curOffset += dex_getFileSize(dexBuf); 100 | return dexBuf; 101 | } 102 | } 103 | 104 | u4 vdex_010_GetLocationChecksum(const u1 *cursor, u4 fileIdx) { 105 | u4 *checksums = (u4 *)(cursor + sizeof(vdexHeader_010)); 106 | return checksums[fileIdx]; 107 | } 108 | 109 | void vdex_010_SetLocationChecksum(const u1 *cursor, u4 fileIdx, u4 value) { 110 | u4 *checksums = (u4 *)(cursor + sizeof(vdexHeader_010)); 111 | checksums[fileIdx] = value; 112 | } 113 | 114 | void vdex_010_GetVerifierDeps(const u1 *cursor, vdex_data_array_t *pVerifierDeps) { 115 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 116 | pVerifierDeps->data = vdex_010_DexBegin(cursor) + pVdexHeader->dexSize; 117 | pVerifierDeps->offset = vdex_010_DexBeginOffset(cursor) + pVdexHeader->dexSize; 118 | pVerifierDeps->size = pVdexHeader->verifierDepsSize; 119 | } 120 | 121 | void vdex_010_GetQuickeningInfo(const u1 *cursor, vdex_data_array_t *pQuickInfo) { 122 | vdex_data_array_t vDeps; 123 | vdex_010_GetVerifierDeps(cursor, &vDeps); 124 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 125 | pQuickInfo->data = vDeps.data + pVdexHeader->verifierDepsSize; 126 | pQuickInfo->offset = vDeps.offset + pVdexHeader->verifierDepsSize; 127 | pQuickInfo->size = pVdexHeader->quickeningInfoSize; 128 | } 129 | 130 | void vdex_010_dumpHeaderInfo(const u1 *cursor) { 131 | const vdexHeader_010 *pVdexHeader = (const vdexHeader_010 *)cursor; 132 | vdex_data_array_t vDeps; 133 | vdex_010_GetVerifierDeps(cursor, &vDeps); 134 | vdex_data_array_t quickInfo; 135 | vdex_010_GetQuickeningInfo(cursor, &quickInfo); 136 | 137 | LOGMSG_RAW(l_DEBUG, "------ Vdex Header Info ------\n"); 138 | LOGMSG_RAW(l_DEBUG, "magic header & version : %.4s-%.4s\n", pVdexHeader->magic, 139 | pVdexHeader->version); 140 | LOGMSG_RAW(l_DEBUG, "number of dex files : %" PRIx32 " (%" PRIu32 ")\n", 141 | pVdexHeader->numberOfDexFiles, pVdexHeader->numberOfDexFiles); 142 | LOGMSG_RAW(l_DEBUG, "dex size (overall) : %" PRIx32 " (%" PRIu32 ")\n", 143 | pVdexHeader->dexSize, pVdexHeader->dexSize); 144 | LOGMSG_RAW(l_DEBUG, "verifier dependencies size : %" PRIx32 " (%" PRIu32 ")\n", vDeps.size, 145 | vDeps.size); 146 | LOGMSG_RAW(l_DEBUG, "verifier dependencies offset: %" PRIx32 " (%" PRIu32 ")\n", vDeps.offset, 147 | vDeps.offset); 148 | LOGMSG_RAW(l_DEBUG, "quickening info size : %" PRIx32 " (%" PRIu32 ")\n", quickInfo.size, 149 | quickInfo.size); 150 | LOGMSG_RAW(l_DEBUG, "quickening info offset : %" PRIx32 " (%" PRIu32 ")\n", quickInfo.offset, 151 | quickInfo.offset); 152 | LOGMSG_RAW(l_DEBUG, "dex files info :\n"); 153 | 154 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 155 | LOGMSG_RAW(l_DEBUG, " [%" PRIu32 "] location checksum : %" PRIx32 " (%" PRIu32 ")\n", i, 156 | vdex_010_GetLocationChecksum(cursor, i), vdex_010_GetLocationChecksum(cursor, i)); 157 | } 158 | LOGMSG_RAW(l_DEBUG, "---- EOF Vdex Header Info ----\n"); 159 | } 160 | 161 | bool vdex_010_SanityCheck(const u1 *cursor, size_t bufSz) { 162 | // Check that verifier deps section doesn't point past the end of file. We expect at least one 163 | // byte (the number of entries) per struct. 164 | vdex_data_array_t vDeps; 165 | vdex_010_GetVerifierDeps(cursor, &vDeps); 166 | if (vDeps.offset && vDeps.size && ((vDeps.offset + 7) > bufSz)) { 167 | LOGMSG(l_ERROR, 168 | "Verifier dependencies section points past the end of file (%" PRIx32 " + %" PRIx32 169 | " > %" PRIx32 ")", 170 | vDeps.offset, vDeps.size, bufSz); 171 | return false; 172 | } 173 | 174 | // Check that quickening info section doesn't point past the end of file 175 | vdex_data_array_t quickInfo; 176 | vdex_010_GetQuickeningInfo(cursor, &quickInfo); 177 | if (quickInfo.size && ((quickInfo.offset + quickInfo.size) > bufSz)) { 178 | LOGMSG(l_ERROR, 179 | "Quickening info section points past the end of file (%" PRIx32 " + %" PRIx32 180 | " > %" PRIx32 ")", 181 | quickInfo.offset, quickInfo.size, bufSz); 182 | return false; 183 | } 184 | return true; 185 | } 186 | 187 | int vdex_010_process(const char *VdexFileName, 188 | const u1 *cursor, 189 | size_t bufSz, 190 | const runArgs_t *pRunArgs) { 191 | // Update Dex disassembler engine status 192 | dex_setDisassemblerStatus(pRunArgs->enableDisassembler); 193 | 194 | // Measure time spend to process all Dex files of a Vdex file 195 | struct timespec timer; 196 | utils_startTimer(&timer); 197 | 198 | // Process Vdex file 199 | int ret = vdex_backend_010_process(VdexFileName, cursor, bufSz, pRunArgs); 200 | 201 | // Get elapsed time in ns 202 | long timeSpend = utils_endTimer(&timer); 203 | LOGMSG(l_DEBUG, "Took %ld ms to process Vdex file", timeSpend / 1000000); 204 | 205 | return ret; 206 | } 207 | 208 | void vdex_010_dumpDepsInfo(const u1 *vdexFileBuf) { vdex_backend_010_dumpDepsInfo(vdexFileBuf); } 209 | -------------------------------------------------------------------------------- /src/vdex/vdex_006.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_006.h" 24 | 25 | #include "../out_writer.h" 26 | #include "../utils.h" 27 | #include "vdex_backend_006.h" 28 | 29 | bool vdex_006_isMagicValid(const u1 *cursor) { 30 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 31 | return (memcmp(pVdexHeader->magic, kVdexMagic, sizeof(kVdexMagic)) == 0); 32 | } 33 | 34 | bool vdex_006_isVersionValid(const u1 *cursor) { 35 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 36 | return (memcmp(pVdexHeader->version, kVdex006, sizeof(kVdex006)) == 0); 37 | } 38 | 39 | bool vdex_006_isValidVdex(const u1 *cursor) { 40 | return vdex_006_isMagicValid(cursor) && vdex_006_isVersionValid(cursor); 41 | } 42 | 43 | bool vdex_006_hasDexSection(const u1 *cursor) { 44 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 45 | return pVdexHeader->dexSize != 0; 46 | } 47 | 48 | u4 vdex_006_GetSizeOfChecksumsSection(const u1 *cursor) { 49 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 50 | return sizeof(VdexChecksum) * pVdexHeader->numberOfDexFiles; 51 | } 52 | 53 | const u1 *vdex_006_DexBegin(const u1 *cursor) { 54 | return cursor + sizeof(vdexHeader_006) + vdex_006_GetSizeOfChecksumsSection(cursor); 55 | } 56 | 57 | u4 vdex_006_DexBeginOffset(const u1 *cursor) { 58 | return sizeof(vdexHeader_006) + vdex_006_GetSizeOfChecksumsSection(cursor); 59 | } 60 | 61 | const u1 *vdex_006_DexEnd(const u1 *cursor) { 62 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 63 | return vdex_006_DexBegin(cursor) + pVdexHeader->dexSize; 64 | } 65 | 66 | u4 vdex_006_DexEndOffset(const u1 *cursor) { 67 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 68 | return vdex_006_DexBeginOffset(cursor) + pVdexHeader->dexSize; 69 | } 70 | 71 | const u1 *vdex_006_GetNextDexFileData(const u1 *cursor, u4 *curOffset) { 72 | if (*curOffset == 0) { 73 | if (vdex_006_hasDexSection(cursor)) { 74 | const u1 *dexBuf = vdex_006_DexBegin(cursor); 75 | *curOffset = sizeof(vdexHeader_006) + vdex_006_GetSizeOfChecksumsSection(cursor); 76 | LOGMSG(l_DEBUG, "Processing first Dex file at offset:0x%x", *curOffset); 77 | 78 | // Adjust curOffset to point at the end of current Dex file 79 | *curOffset += dex_getFileSize(dexBuf); 80 | return dexBuf; 81 | } else { 82 | LOGMSG(l_ERROR, "Vdex file has no Dex entries to process"); 83 | return NULL; 84 | } 85 | } else { 86 | // Check boundaries 87 | const u1 *dexBuf = cursor + *curOffset; 88 | const u1 *dexBufMax = dexBuf + dex_getFileSize(dexBuf); 89 | if (dexBufMax == vdex_006_DexEnd(cursor)) { 90 | LOGMSG(l_DEBUG, "Processing last Dex file at offset:0x%x", *curOffset); 91 | } else if (dexBufMax < vdex_006_DexEnd(cursor)) { 92 | LOGMSG(l_DEBUG, "Processing Dex file at offset:0x%x", *curOffset); 93 | } else { 94 | LOGMSG(l_ERROR, "Invalid cursor offset '0x%x'", *curOffset); 95 | return NULL; 96 | } 97 | 98 | // Adjust curOffset to point at the end of current Dex file 99 | *curOffset += dex_getFileSize(dexBuf); 100 | return dexBuf; 101 | } 102 | } 103 | 104 | u4 vdex_006_GetLocationChecksum(const u1 *cursor, u4 fileIdx) { 105 | u4 *checksums = (u4 *)(cursor + sizeof(vdexHeader_006)); 106 | return checksums[fileIdx]; 107 | } 108 | 109 | void vdex_006_SetLocationChecksum(const u1 *cursor, u4 fileIdx, u4 value) { 110 | u4 *checksums = (u4 *)(cursor + sizeof(vdexHeader_006)); 111 | checksums[fileIdx] = value; 112 | } 113 | 114 | void vdex_006_GetVerifierDeps(const u1 *cursor, vdex_data_array_t *pVerifierDeps) { 115 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 116 | pVerifierDeps->data = vdex_006_DexBegin(cursor) + pVdexHeader->dexSize; 117 | pVerifierDeps->offset = vdex_006_DexBeginOffset(cursor) + pVdexHeader->dexSize; 118 | pVerifierDeps->size = pVdexHeader->verifierDepsSize; 119 | } 120 | 121 | void vdex_006_GetQuickeningInfo(const u1 *cursor, vdex_data_array_t *pQuickInfo) { 122 | vdex_data_array_t vDeps; 123 | vdex_006_GetVerifierDeps(cursor, &vDeps); 124 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 125 | pQuickInfo->data = vDeps.data + pVdexHeader->verifierDepsSize; 126 | pQuickInfo->offset = vDeps.offset + pVdexHeader->verifierDepsSize; 127 | pQuickInfo->size = pVdexHeader->quickeningInfoSize; 128 | } 129 | 130 | void vdex_006_dumpHeaderInfo(const u1 *cursor) { 131 | const vdexHeader_006 *pVdexHeader = (const vdexHeader_006 *)cursor; 132 | vdex_data_array_t vDeps; 133 | vdex_006_GetVerifierDeps(cursor, &vDeps); 134 | vdex_data_array_t quickInfo; 135 | vdex_006_GetQuickeningInfo(cursor, &quickInfo); 136 | 137 | LOGMSG_RAW(l_DEBUG, "------ Vdex Header Info ------\n"); 138 | LOGMSG_RAW(l_DEBUG, "magic header & version : %.4s-%.4s\n", pVdexHeader->magic, 139 | pVdexHeader->version); 140 | LOGMSG_RAW(l_DEBUG, "number of dex files : %" PRIx32 " (%" PRIu32 ")\n", 141 | pVdexHeader->numberOfDexFiles, pVdexHeader->numberOfDexFiles); 142 | LOGMSG_RAW(l_DEBUG, "dex size (overall) : %" PRIx32 " (%" PRIu32 ")\n", 143 | pVdexHeader->dexSize, pVdexHeader->dexSize); 144 | LOGMSG_RAW(l_DEBUG, "verifier dependencies size : %" PRIx32 " (%" PRIu32 ")\n", vDeps.size, 145 | vDeps.size); 146 | LOGMSG_RAW(l_DEBUG, "verifier dependencies offset: %" PRIx32 " (%" PRIu32 ")\n", vDeps.offset, 147 | vDeps.offset); 148 | LOGMSG_RAW(l_DEBUG, "quickening info size : %" PRIx32 " (%" PRIu32 ")\n", quickInfo.size, 149 | quickInfo.size); 150 | LOGMSG_RAW(l_DEBUG, "quickening info offset : %" PRIx32 " (%" PRIu32 ")\n", quickInfo.offset, 151 | quickInfo.offset); 152 | LOGMSG_RAW(l_DEBUG, "dex files info :\n"); 153 | 154 | for (u4 i = 0; i < pVdexHeader->numberOfDexFiles; ++i) { 155 | LOGMSG_RAW(l_DEBUG, " [%" PRIu32 "] location checksum : %" PRIx32 " (%" PRIu32 ")\n", i, 156 | vdex_006_GetLocationChecksum(cursor, i), vdex_006_GetLocationChecksum(cursor, i)); 157 | } 158 | LOGMSG_RAW(l_DEBUG, "---- EOF Vdex Header Info ----\n"); 159 | } 160 | 161 | bool vdex_006_SanityCheck(const u1 *cursor, size_t bufSz) { 162 | // Check that verifier deps section doesn't point past the end of file. We expect at least one 163 | // byte (the number of entries) per struct. 164 | vdex_data_array_t vDeps; 165 | vdex_006_GetVerifierDeps(cursor, &vDeps); 166 | if (vDeps.offset && vDeps.size && ((vDeps.offset + 9) > bufSz)) { 167 | LOGMSG(l_ERROR, 168 | "Verifier dependencies section points past the end of file (%" PRIx32 " + %" PRIx32 169 | " > %" PRIx32 ")", 170 | vDeps.offset, vDeps.size, bufSz); 171 | return false; 172 | } 173 | 174 | // Check that quickening info section doesn't point past the end of file 175 | vdex_data_array_t quickInfo; 176 | vdex_006_GetQuickeningInfo(cursor, &quickInfo); 177 | if (quickInfo.size && ((quickInfo.offset + quickInfo.size) > bufSz)) { 178 | LOGMSG(l_ERROR, 179 | "Quickening info section points past the end of file (%" PRIx32 " + %" PRIx32 180 | " > %" PRIx32 ")", 181 | quickInfo.offset, quickInfo.size, bufSz); 182 | return false; 183 | } 184 | return true; 185 | } 186 | 187 | int vdex_006_process(const char *VdexFileName, 188 | const u1 *cursor, 189 | size_t bufSz, 190 | const runArgs_t *pRunArgs) { 191 | // Update Dex disassembler engine status 192 | dex_setDisassemblerStatus(pRunArgs->enableDisassembler); 193 | 194 | // Measure time spend to process all Dex files of a Vdex file 195 | struct timespec timer; 196 | utils_startTimer(&timer); 197 | 198 | // Process Vdex file 199 | int ret = vdex_backend_006_process(VdexFileName, cursor, bufSz, pRunArgs); 200 | 201 | // Get elapsed time in ns 202 | long timeSpend = utils_endTimer(&timer); 203 | LOGMSG(l_DEBUG, "Took %ld ms to process Vdex file", timeSpend / 1000000); 204 | 205 | return ret; 206 | } 207 | 208 | void vdex_006_dumpDepsInfo(const u1 *vdexFileBuf) { vdex_backend_006_dumpDepsInfo(vdexFileBuf); } 209 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_019.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_decompiler_019.h" 24 | 25 | #include "../utils.h" 26 | 27 | static const u1 *quicken_info_ptr; 28 | static size_t quicken_info_number_of_indices; 29 | static size_t quicken_index; 30 | 31 | static u2 GetData(size_t index) { 32 | return quicken_info_ptr[index * 2] | ((u2)(quicken_info_ptr[index * 2 + 1]) << 8); 33 | } 34 | 35 | static u4 NumberOfIndices(const u1 **data, u4 data_size) { 36 | return data_size != 0 ? dex_readULeb128(data) : 0u; 37 | } 38 | 39 | static u2 *code_ptr; 40 | static u2 *code_end; 41 | static u4 dex_pc; 42 | static u4 cur_code_off; 43 | 44 | static void initQuickenInfoTable(const vdex_data_array_t *quickenData) { 45 | quicken_info_ptr = quickenData->data; 46 | quicken_index = 0; 47 | quicken_info_number_of_indices = NumberOfIndices(&quicken_info_ptr, quickenData->size); 48 | } 49 | 50 | static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { 51 | code_ptr = pCode; 52 | code_end = pCode + codeSize; 53 | dex_pc = 0; 54 | cur_code_off = startCodeOff; 55 | } 56 | 57 | static bool isCodeIteratorDone() { return code_ptr >= code_end; } 58 | 59 | static void codeIteratorAdvance() { 60 | u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); 61 | code_ptr += instruction_size; 62 | dex_pc += instruction_size; 63 | cur_code_off += instruction_size * sizeof(u2); 64 | } 65 | 66 | static u2 NextIndex() { 67 | CHECK_LT(quicken_index, quicken_info_number_of_indices); 68 | const u2 ret = GetData(quicken_index); 69 | quicken_index++; 70 | return ret; 71 | } 72 | 73 | static bool DecompileNop(u2 *insns) { 74 | const u2 reference_index = NextIndex(); 75 | if (reference_index == kDexNoIndex16) { 76 | // This means it was a normal nop and not a check-cast. 77 | return false; 78 | } 79 | const u2 type_index = NextIndex(); 80 | dexInstr_SetOpcode(insns, CHECK_CAST); 81 | dexInstr_SetVRegA_21c(insns, reference_index); 82 | dexInstr_SetVRegB_21c(insns, type_index); 83 | 84 | return true; 85 | } 86 | 87 | static void DecompileInstanceFieldAccess(u2 *insns, Code new_opcode) { 88 | u2 index = NextIndex(); 89 | dexInstr_SetOpcode(insns, new_opcode); 90 | dexInstr_SetVRegC_22c(insns, index); 91 | } 92 | 93 | static void DecompileInvokeVirtual(u2 *insns, Code new_opcode, bool is_range) { 94 | u2 index = NextIndex(); 95 | dexInstr_SetOpcode(insns, new_opcode); 96 | if (is_range) { 97 | dexInstr_SetVRegB_3rc(insns, index); 98 | } else { 99 | dexInstr_SetVRegB_35c(insns, index); 100 | } 101 | } 102 | 103 | bool vdex_decompiler_019_decompile(const u1 *dexFileBuf, 104 | dexMethod *pDexMethod, 105 | const vdex_data_array_t *quickenData, 106 | bool decompile_return_instruction) { 107 | if (quickenData->size == 0 && !decompile_return_instruction) { 108 | return true; 109 | } 110 | 111 | // Get method's CodeItem information 112 | u2 *pCode = NULL; 113 | u4 codeSize = 0; 114 | dex_getCodeItemInfo(dexFileBuf, pDexMethod, &pCode, &codeSize); 115 | 116 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 117 | 118 | // Initialize global data for every method that is decompiled 119 | initQuickenInfoTable(quickenData); 120 | initCodeIterator(pCode, codeSize, startCodeOff); 121 | 122 | log_dis(" quickening_size=%" PRIx32 " (%" PRIu32 ")\n", quickenData->size, quickenData->size); 123 | 124 | while (isCodeIteratorDone() == false) { 125 | bool hasCodeChange = true; 126 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 127 | switch (dexInstr_getOpcode(code_ptr)) { 128 | case RETURN_VOID_NO_BARRIER: 129 | if (decompile_return_instruction) { 130 | dexInstr_SetOpcode(code_ptr, RETURN_VOID); 131 | } 132 | break; 133 | case NOP: 134 | if (quicken_info_number_of_indices > 0) { 135 | // Only try to decompile NOP if there are more than 0 indices. Not having 136 | // any index happens when we unquicken a code item that only has 137 | // RETURN_VOID_NO_BARRIER as quickened instruction. 138 | hasCodeChange = DecompileNop(code_ptr); 139 | } 140 | break; 141 | case IGET_QUICK: 142 | CHECK_GT(quicken_info_number_of_indices, 0); 143 | DecompileInstanceFieldAccess(code_ptr, IGET); 144 | break; 145 | case IGET_WIDE_QUICK: 146 | CHECK_GT(quicken_info_number_of_indices, 0); 147 | DecompileInstanceFieldAccess(code_ptr, IGET_WIDE); 148 | break; 149 | case IGET_OBJECT_QUICK: 150 | CHECK_GT(quicken_info_number_of_indices, 0); 151 | DecompileInstanceFieldAccess(code_ptr, IGET_OBJECT); 152 | break; 153 | case IGET_BOOLEAN_QUICK: 154 | CHECK_GT(quicken_info_number_of_indices, 0); 155 | DecompileInstanceFieldAccess(code_ptr, IGET_BOOLEAN); 156 | break; 157 | case IGET_BYTE_QUICK: 158 | CHECK_GT(quicken_info_number_of_indices, 0); 159 | DecompileInstanceFieldAccess(code_ptr, IGET_BYTE); 160 | break; 161 | case IGET_CHAR_QUICK: 162 | CHECK_GT(quicken_info_number_of_indices, 0); 163 | DecompileInstanceFieldAccess(code_ptr, IGET_CHAR); 164 | break; 165 | case IGET_SHORT_QUICK: 166 | CHECK_GT(quicken_info_number_of_indices, 0); 167 | DecompileInstanceFieldAccess(code_ptr, IGET_SHORT); 168 | break; 169 | case IPUT_QUICK: 170 | CHECK_GT(quicken_info_number_of_indices, 0); 171 | DecompileInstanceFieldAccess(code_ptr, IPUT); 172 | break; 173 | case IPUT_BOOLEAN_QUICK: 174 | CHECK_GT(quicken_info_number_of_indices, 0); 175 | DecompileInstanceFieldAccess(code_ptr, IPUT_BOOLEAN); 176 | break; 177 | case IPUT_BYTE_QUICK: 178 | CHECK_GT(quicken_info_number_of_indices, 0); 179 | DecompileInstanceFieldAccess(code_ptr, IPUT_BYTE); 180 | break; 181 | case IPUT_CHAR_QUICK: 182 | CHECK_GT(quicken_info_number_of_indices, 0); 183 | DecompileInstanceFieldAccess(code_ptr, IPUT_CHAR); 184 | break; 185 | case IPUT_SHORT_QUICK: 186 | CHECK_GT(quicken_info_number_of_indices, 0); 187 | DecompileInstanceFieldAccess(code_ptr, IPUT_SHORT); 188 | break; 189 | case IPUT_WIDE_QUICK: 190 | CHECK_GT(quicken_info_number_of_indices, 0); 191 | DecompileInstanceFieldAccess(code_ptr, IPUT_WIDE); 192 | break; 193 | case IPUT_OBJECT_QUICK: 194 | CHECK_GT(quicken_info_number_of_indices, 0); 195 | DecompileInstanceFieldAccess(code_ptr, IPUT_OBJECT); 196 | break; 197 | case INVOKE_VIRTUAL_QUICK: 198 | CHECK_GT(quicken_info_number_of_indices, 0); 199 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL, false); 200 | break; 201 | case INVOKE_VIRTUAL_RANGE_QUICK: 202 | CHECK_GT(quicken_info_number_of_indices, 0); 203 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL_RANGE, true); 204 | break; 205 | default: 206 | hasCodeChange = false; 207 | break; 208 | } 209 | 210 | if (hasCodeChange) { 211 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, true); 212 | } 213 | codeIteratorAdvance(); 214 | } 215 | 216 | if (quicken_index != quicken_info_number_of_indices) { 217 | if (quicken_index == 0) { 218 | LOGMSG(l_ERROR, 219 | "Failed to use any value in quickening info, potentially due to duplicate methods."); 220 | } else { 221 | LOGMSG(l_ERROR, "Failed to use all values in quickening info, '%zx' items not processed", 222 | quicken_info_number_of_indices - quicken_index); 223 | return false; 224 | } 225 | } 226 | 227 | return true; 228 | } 229 | 230 | void vdex_decompiler_019_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { 231 | // We have different code items in Standard Dex and Compact Dex 232 | u2 *pCode = NULL; 233 | u4 codeSize = 0; 234 | if (dex_checkType(dexFileBuf) == kNormalDex) { 235 | dexCode *pDexCode = (dexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 236 | pCode = pDexCode->insns; 237 | codeSize = pDexCode->insnsSize; 238 | } else { 239 | cdexCode *pCdexCode = (cdexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 240 | pCode = pCdexCode->insns; 241 | dex_DecodeCDexFields(pCdexCode, &codeSize, NULL, NULL, NULL, NULL, true); 242 | } 243 | 244 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 245 | initCodeIterator(pCode, codeSize, startCodeOff); 246 | while (isCodeIteratorDone() == false) { 247 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 248 | codeIteratorAdvance(); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/vdex/vdex_decompiler_021.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_decompiler_021.h" 24 | 25 | #include "../utils.h" 26 | 27 | static const u1 *quicken_info_ptr; 28 | static size_t quicken_info_number_of_indices; 29 | static size_t quicken_index; 30 | 31 | static u2 GetData(size_t index) { 32 | return quicken_info_ptr[index * 2] | ((u2)(quicken_info_ptr[index * 2 + 1]) << 8); 33 | } 34 | 35 | static u4 NumberOfIndices(const u1 **data, u4 data_size) { 36 | return data_size != 0 ? dex_readULeb128(data) : 0u; 37 | } 38 | 39 | static u2 *code_ptr; 40 | static u2 *code_end; 41 | static u4 dex_pc; 42 | static u4 cur_code_off; 43 | 44 | static void initQuickenInfoTable(const vdex_data_array_t *quickenData) { 45 | quicken_info_ptr = quickenData->data; 46 | quicken_index = 0; 47 | quicken_info_number_of_indices = NumberOfIndices(&quicken_info_ptr, quickenData->size); 48 | } 49 | 50 | static void initCodeIterator(u2 *pCode, u4 codeSize, u4 startCodeOff) { 51 | code_ptr = pCode; 52 | code_end = pCode + codeSize; 53 | dex_pc = 0; 54 | cur_code_off = startCodeOff; 55 | } 56 | 57 | static bool isCodeIteratorDone() { return code_ptr >= code_end; } 58 | 59 | static void codeIteratorAdvance() { 60 | u4 instruction_size = dexInstr_SizeInCodeUnits(code_ptr); 61 | code_ptr += instruction_size; 62 | dex_pc += instruction_size; 63 | cur_code_off += instruction_size * sizeof(u2); 64 | } 65 | 66 | static u2 NextIndex() { 67 | CHECK_LT(quicken_index, quicken_info_number_of_indices); 68 | const u2 ret = GetData(quicken_index); 69 | quicken_index++; 70 | return ret; 71 | } 72 | 73 | static bool DecompileNop(u2 *insns) { 74 | const u2 reference_index = NextIndex(); 75 | if (reference_index == kDexNoIndex16) { 76 | // This means it was a normal nop and not a check-cast. 77 | return false; 78 | } 79 | const u2 type_index = NextIndex(); 80 | dexInstr_SetOpcode(insns, CHECK_CAST); 81 | dexInstr_SetVRegA_21c(insns, reference_index); 82 | dexInstr_SetVRegB_21c(insns, type_index); 83 | 84 | return true; 85 | } 86 | 87 | static void DecompileInstanceFieldAccess(u2 *insns, Code new_opcode) { 88 | u2 index = NextIndex(); 89 | dexInstr_SetOpcode(insns, new_opcode); 90 | dexInstr_SetVRegC_22c(insns, index); 91 | } 92 | 93 | static void DecompileInvokeVirtual(u2 *insns, Code new_opcode, bool is_range) { 94 | u2 index = NextIndex(); 95 | dexInstr_SetOpcode(insns, new_opcode); 96 | if (is_range) { 97 | dexInstr_SetVRegB_3rc(insns, index); 98 | } else { 99 | dexInstr_SetVRegB_35c(insns, index); 100 | } 101 | } 102 | 103 | bool vdex_decompiler_021_decompile(const u1 *dexFileBuf, 104 | dexMethod *pDexMethod, 105 | const vdex_data_array_t *quickenData, 106 | bool decompile_return_instruction) { 107 | if (quickenData->size == 0 && !decompile_return_instruction) { 108 | return true; 109 | } 110 | 111 | // Get method's CodeItem information 112 | u2 *pCode = NULL; 113 | u4 codeSize = 0; 114 | dex_getCodeItemInfo(dexFileBuf, pDexMethod, &pCode, &codeSize); 115 | 116 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 117 | 118 | // Initialize global data for every method that is decompiled 119 | initQuickenInfoTable(quickenData); 120 | initCodeIterator(pCode, codeSize, startCodeOff); 121 | 122 | log_dis(" quickening_size=%" PRIx32 " (%" PRIu32 ")\n", quickenData->size, quickenData->size); 123 | 124 | while (isCodeIteratorDone() == false) { 125 | bool hasCodeChange = true; 126 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 127 | switch (dexInstr_getOpcode(code_ptr)) { 128 | case RETURN_VOID_NO_BARRIER: 129 | if (decompile_return_instruction) { 130 | dexInstr_SetOpcode(code_ptr, RETURN_VOID); 131 | } 132 | break; 133 | case NOP: 134 | if (quicken_info_number_of_indices > 0) { 135 | // Only try to decompile NOP if there are more than 0 indices. Not having 136 | // any index happens when we unquicken a code item that only has 137 | // RETURN_VOID_NO_BARRIER as quickened instruction. 138 | hasCodeChange = DecompileNop(code_ptr); 139 | } 140 | break; 141 | case IGET_QUICK: 142 | CHECK_GT(quicken_info_number_of_indices, 0); 143 | DecompileInstanceFieldAccess(code_ptr, IGET); 144 | break; 145 | case IGET_WIDE_QUICK: 146 | CHECK_GT(quicken_info_number_of_indices, 0); 147 | DecompileInstanceFieldAccess(code_ptr, IGET_WIDE); 148 | break; 149 | case IGET_OBJECT_QUICK: 150 | CHECK_GT(quicken_info_number_of_indices, 0); 151 | DecompileInstanceFieldAccess(code_ptr, IGET_OBJECT); 152 | break; 153 | case IGET_BOOLEAN_QUICK: 154 | CHECK_GT(quicken_info_number_of_indices, 0); 155 | DecompileInstanceFieldAccess(code_ptr, IGET_BOOLEAN); 156 | break; 157 | case IGET_BYTE_QUICK: 158 | CHECK_GT(quicken_info_number_of_indices, 0); 159 | DecompileInstanceFieldAccess(code_ptr, IGET_BYTE); 160 | break; 161 | case IGET_CHAR_QUICK: 162 | CHECK_GT(quicken_info_number_of_indices, 0); 163 | DecompileInstanceFieldAccess(code_ptr, IGET_CHAR); 164 | break; 165 | case IGET_SHORT_QUICK: 166 | CHECK_GT(quicken_info_number_of_indices, 0); 167 | DecompileInstanceFieldAccess(code_ptr, IGET_SHORT); 168 | break; 169 | case IPUT_QUICK: 170 | CHECK_GT(quicken_info_number_of_indices, 0); 171 | DecompileInstanceFieldAccess(code_ptr, IPUT); 172 | break; 173 | case IPUT_BOOLEAN_QUICK: 174 | CHECK_GT(quicken_info_number_of_indices, 0); 175 | DecompileInstanceFieldAccess(code_ptr, IPUT_BOOLEAN); 176 | break; 177 | case IPUT_BYTE_QUICK: 178 | CHECK_GT(quicken_info_number_of_indices, 0); 179 | DecompileInstanceFieldAccess(code_ptr, IPUT_BYTE); 180 | break; 181 | case IPUT_CHAR_QUICK: 182 | CHECK_GT(quicken_info_number_of_indices, 0); 183 | DecompileInstanceFieldAccess(code_ptr, IPUT_CHAR); 184 | break; 185 | case IPUT_SHORT_QUICK: 186 | CHECK_GT(quicken_info_number_of_indices, 0); 187 | DecompileInstanceFieldAccess(code_ptr, IPUT_SHORT); 188 | break; 189 | case IPUT_WIDE_QUICK: 190 | CHECK_GT(quicken_info_number_of_indices, 0); 191 | DecompileInstanceFieldAccess(code_ptr, IPUT_WIDE); 192 | break; 193 | case IPUT_OBJECT_QUICK: 194 | CHECK_GT(quicken_info_number_of_indices, 0); 195 | DecompileInstanceFieldAccess(code_ptr, IPUT_OBJECT); 196 | break; 197 | case INVOKE_VIRTUAL_QUICK: 198 | CHECK_GT(quicken_info_number_of_indices, 0); 199 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL, false); 200 | break; 201 | case INVOKE_VIRTUAL_RANGE_QUICK: 202 | CHECK_GT(quicken_info_number_of_indices, 0); 203 | DecompileInvokeVirtual(code_ptr, INVOKE_VIRTUAL_RANGE, true); 204 | break; 205 | default: 206 | hasCodeChange = false; 207 | break; 208 | } 209 | 210 | if (hasCodeChange) { 211 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, true); 212 | } 213 | codeIteratorAdvance(); 214 | } 215 | 216 | if (quicken_index != quicken_info_number_of_indices) { 217 | if (quicken_index == 0) { 218 | LOGMSG(l_ERROR, 219 | "Failed to use any value in quickening info, potentially due to duplicate methods."); 220 | } else { 221 | LOGMSG(l_ERROR, "Failed to use all values in quickening info, '%zx' items not processed", 222 | quicken_info_number_of_indices - quicken_index); 223 | return false; 224 | } 225 | } 226 | 227 | return true; 228 | } 229 | 230 | void vdex_decompiler_021_walk(const u1 *dexFileBuf, dexMethod *pDexMethod) { 231 | // We have different code items in Standard Dex and Compact Dex 232 | u2 *pCode = NULL; 233 | u4 codeSize = 0; 234 | if (dex_checkType(dexFileBuf) == kNormalDex) { 235 | dexCode *pDexCode = (dexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 236 | pCode = pDexCode->insns; 237 | codeSize = pDexCode->insnsSize; 238 | } else { 239 | cdexCode *pCdexCode = (cdexCode *)(dex_getDataAddr(dexFileBuf) + pDexMethod->codeOff); 240 | pCode = pCdexCode->insns; 241 | dex_DecodeCDexFields(pCdexCode, &codeSize, NULL, NULL, NULL, NULL, true); 242 | } 243 | 244 | u4 startCodeOff = dex_getFirstInstrOff(dexFileBuf, pDexMethod); 245 | initCodeIterator(pCode, codeSize, startCodeOff); 246 | while (isCodeIteratorDone() == false) { 247 | dex_dumpInstruction(dexFileBuf, code_ptr, cur_code_off, dex_pc, false); 248 | codeIteratorAdvance(); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/vdex/vdex_027.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "vdex_027.h" 24 | 25 | #include "../out_writer.h" 26 | #include "../utils.h" 27 | #include "vdex_backend_027.h" 28 | 29 | bool vdex_027_isMagicValid(const u1 *cursor) { 30 | const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; 31 | return (memcmp(pVdexHeader->magic, kVdexMagic, sizeof(kVdexMagic)) == 0); 32 | } 33 | 34 | bool vdex_027_IsVdexVersionValid(const u1 *cursor) { 35 | const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; 36 | return (memcmp(pVdexHeader->vdexVersion, kVdexVersion_027, sizeof(kVdexVersion_027)) == 0); 37 | } 38 | 39 | bool vdex_027_isValidVdex(const u1 *cursor) { 40 | return vdex_027_isMagicValid(cursor) && vdex_027_IsVdexVersionValid(cursor); 41 | } 42 | 43 | const vdexSectionHeader_027 *vdex_027_GetSectionHeader(const u1 *cursor, u4 index) { 44 | const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; 45 | CHECK_LT(index, pVdexHeader->numberOfSections); 46 | return (vdexSectionHeader_027 *)(cursor + sizeof(vdexHeader_027) + index * 47 | sizeof(vdexSectionHeader_027)); 48 | } 49 | 50 | bool vdex_027_hasDexSection(const u1 *cursor) { 51 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); 52 | return pSectHeader->sectionSize != 0u; 53 | } 54 | 55 | u4 vdex_027_GetNumberOfDexFiles(const u1 *cursor) { 56 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kChecksumSection); 57 | return pSectHeader->sectionSize / sizeof(VdexChecksum); 58 | } 59 | 60 | const VdexChecksum *vdex_027_GetDexChecksumsArray(const u1 *cursor) { 61 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kChecksumSection); 62 | return (VdexChecksum *)(cursor + pSectHeader->sectionOffset); 63 | } 64 | 65 | const u1 *vdex_027_DexBegin(const u1 *cursor) { 66 | CHECK(vdex_027_hasDexSection(cursor)); 67 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); 68 | return cursor + pSectHeader->sectionOffset; 69 | } 70 | 71 | u4 vdex_027_DexBeginOffset(const u1 *cursor) { 72 | CHECK(vdex_027_hasDexSection(cursor)); 73 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); 74 | return pSectHeader->sectionOffset; 75 | } 76 | 77 | const u1 *vdex_027_DexEnd(const u1 *cursor) { 78 | CHECK(vdex_027_hasDexSection(cursor)); 79 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); 80 | return vdex_027_DexBegin(cursor) + pSectHeader->sectionSize; 81 | } 82 | 83 | u4 vdex_027_DexEndOffset(const u1 *cursor) { 84 | CHECK(vdex_027_hasDexSection(cursor)); 85 | const vdexSectionHeader_027 *pSectHeader = vdex_027_GetSectionHeader(cursor, kDexFileSection); 86 | return vdex_027_DexBeginOffset(cursor) + pSectHeader->sectionSize; 87 | } 88 | 89 | u4 vdex_027_GetLocationChecksum(const u1 *cursor, u4 fileIdx) { 90 | CHECK_LT(fileIdx, vdex_027_GetNumberOfDexFiles(cursor)); 91 | u4 *checksums = (u4 *)vdex_027_GetDexChecksumsArray(cursor); 92 | return checksums[fileIdx]; 93 | } 94 | 95 | void vdex_027_SetLocationChecksum(const u1 *cursor, u4 fileIdx, u4 value) { 96 | CHECK_LT(fileIdx, vdex_027_GetNumberOfDexFiles(cursor)); 97 | u4 *checksums = (u4 *)vdex_027_GetDexChecksumsArray(cursor); 98 | checksums[fileIdx] = value; 99 | } 100 | 101 | void vdex_027_dumpHeaderInfo(const u1 *cursor) { 102 | const vdexHeader_027 *pVdexHeader = (const vdexHeader_027 *)cursor; 103 | u4 numberOfDexFiles = vdex_027_GetNumberOfDexFiles(cursor); 104 | const vdexSectionHeader_027 *pDepsSectHeader = vdex_027_GetSectionHeader(cursor, 105 | kVerifierDepsSection); 106 | const vdexSectionHeader_027 *pTypeSectHeader = vdex_027_GetSectionHeader(cursor, 107 | kTypeLookupTableSection); 108 | const vdexSectionHeader_027 *pDexSectHeader = vdex_027_GetSectionHeader(cursor, 109 | kDexFileSection); 110 | 111 | LOGMSG_RAW(l_DEBUG, "------ Vdex Header Info -------\n"); 112 | LOGMSG_RAW(l_DEBUG, "magic header : %.4s\n", pVdexHeader->magic); 113 | LOGMSG_RAW(l_DEBUG, "vdex version : %.4s\n", pVdexHeader->vdexVersion); 114 | LOGMSG_RAW(l_DEBUG, "number of dex files : %" PRIx32 " (%" PRIu32 ")\n", 115 | numberOfDexFiles, numberOfDexFiles); 116 | LOGMSG_RAW(l_DEBUG, "dex file section size : %" PRIx32 " (%" PRIu32 ")\n", 117 | pDexSectHeader->sectionSize, pDexSectHeader->sectionSize); 118 | LOGMSG_RAW(l_DEBUG, "dex file section offset : %" PRIx32 " (%" PRIu32 ")\n", 119 | pDexSectHeader, pDexSectHeader->sectionOffset); 120 | LOGMSG_RAW(l_DEBUG, "verifier dependencies size : %" PRIx32 " (%" PRIu32 ")\n", 121 | pDepsSectHeader->sectionSize, pDepsSectHeader->sectionSize); 122 | LOGMSG_RAW(l_DEBUG, "verifier dependencies offset : %" PRIx32 " (%" PRIu32 ")\n", 123 | pDepsSectHeader->sectionOffset, pDepsSectHeader->sectionOffset); 124 | LOGMSG_RAW(l_DEBUG, "type lookup table size : %" PRIx32 " (%" PRIu32 ")\n", 125 | pTypeSectHeader->sectionSize, pTypeSectHeader->sectionSize); 126 | LOGMSG_RAW(l_DEBUG, "type lookup table offset : %" PRIx32 " (%" PRIu32 ")\n", 127 | pTypeSectHeader->sectionOffset, pTypeSectHeader->sectionOffset); 128 | if (vdex_027_hasDexSection(cursor)) { 129 | LOGMSG_RAW(l_DEBUG, "dex files info :\n"); 130 | for (u4 i = 0; i < numberOfDexFiles; ++i) { 131 | LOGMSG_RAW(l_DEBUG, " [%" PRIu32 "] location checksum : %" PRIx32 " (%" PRIu32 ")\n", i, 132 | vdex_027_GetLocationChecksum(cursor, i), vdex_027_GetLocationChecksum(cursor, i)); 133 | } 134 | } 135 | LOGMSG_RAW(l_DEBUG, "---- EOF Vdex Header Info ----\n"); 136 | } 137 | 138 | const u1 *vdex_027_GetNextDexFileData(const u1 *vdexCursor, u4 *curDexEndOff) { 139 | if (*curDexEndOff == 0) { 140 | if (vdex_027_hasDexSection(vdexCursor)) { 141 | // dex[0] 142 | const u1 *dexBuf = vdex_027_DexBegin(vdexCursor); 143 | LOGMSG(l_DEBUG, "Processing first Dex file at offset:0x%x", dexBuf - vdexCursor); 144 | 145 | // Adjust curDexEndOff to point at the end of the current Dex 146 | *curDexEndOff = dexBuf - vdexCursor + dex_getFileSize(dexBuf); 147 | 148 | return dexBuf; 149 | } else { 150 | LOGMSG(l_ERROR, "Vdex file has no Dex entries to process"); 151 | return NULL; 152 | } 153 | } else { 154 | // dex[i] 155 | const u1 *dexBuf = vdexCursor + *curDexEndOff; 156 | 157 | // Dex files are required to be 4 byte aligned 158 | // dexBuf = (u1*)utils_allignUp((uintptr_t)dexBuf, 4); // TODO: We shouldn't need to repair 159 | if ((uintptr_t)dexBuf & 0x3) { 160 | LOGMSG(l_ERROR, "Dex file in offset '0x%x' is not 4 byte aligned", *curDexEndOff); 161 | return NULL; 162 | } 163 | 164 | // Check boundaries 165 | const u1 *dexBufMax = dexBuf + dex_getFileSize(dexBuf); 166 | if (dexBufMax == vdex_027_DexEnd(vdexCursor)) { 167 | LOGMSG(l_DEBUG, "Processing last Dex file at offset:0x%x", *curDexEndOff); 168 | } else if (dexBufMax < vdex_027_DexEnd(vdexCursor)) { 169 | LOGMSG(l_DEBUG, "Processing Dex file at offset:0x%x", *curDexEndOff); 170 | } else { 171 | LOGMSG(l_ERROR, "Invalid cursor offset '0x%x'", *curDexEndOff); 172 | return NULL; 173 | } 174 | 175 | // Adjust curDexEndOff to point at the end of the current Dex 176 | *curDexEndOff += dex_getFileSize(dexBuf); 177 | 178 | return dexBuf; 179 | } 180 | } 181 | 182 | bool vdex_027_SanityCheck(const u1 *cursor, size_t bufSz) { 183 | // Check that verifier deps section doesn't point past the end of file. We expect at least one 184 | // byte (the number of entries) per struct. 185 | const vdexSectionHeader_027 *pDepsSectHeader = vdex_027_GetSectionHeader(cursor, 186 | kVerifierDepsSection); 187 | if (pDepsSectHeader->sectionOffset && pDepsSectHeader->sectionSize && 188 | ((pDepsSectHeader->sectionOffset + 7) > bufSz)) { 189 | LOGMSG(l_ERROR, 190 | "Verifier dependencies section points past the end of file (%" PRIx32 " + %" PRIx32 191 | " > %" PRIx32 ")", 192 | pDepsSectHeader->sectionOffset, pDepsSectHeader->sectionSize, bufSz); 193 | return false; 194 | } 195 | 196 | return true; 197 | } 198 | 199 | int vdex_027_process(const char *VdexFileName, 200 | const u1 *cursor, 201 | size_t bufSz, 202 | const runArgs_t *pRunArgs) { 203 | // Update Dex disassembler engine status 204 | dex_setDisassemblerStatus(pRunArgs->enableDisassembler); 205 | 206 | // Measure time spend to process all Dex files of a Vdex file 207 | struct timespec timer; 208 | utils_startTimer(&timer); 209 | 210 | // Process Vdex file 211 | int ret = vdex_backend_027_process(VdexFileName, cursor, bufSz, pRunArgs); 212 | 213 | // Get elapsed time in ns 214 | long timeSpend = utils_endTimer(&timer); 215 | LOGMSG(l_DEBUG, "Took %ld ms to process Vdex file", timeSpend / 1000000); 216 | 217 | return ret; 218 | } 219 | 220 | void vdex_027_dumpDepsInfo(const u1 *vdexFileBuf) { vdex_backend_027_dumpDepsInfo(vdexFileBuf); } 221 | -------------------------------------------------------------------------------- /src/vdexExtractor.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "common.h" 28 | #include "log.h" 29 | #include "utils.h" 30 | #include "vdex_api.h" 31 | 32 | // exit() wrapper 33 | void exitWrapper(int errCode) { 34 | log_closeLogFile(); 35 | exit(errCode); 36 | } 37 | 38 | // clang-format off 39 | static void usage(bool exit_success) { 40 | LOGMSG_RAW(l_INFO, " " PROG_NAME " ver. " PROG_VERSION "\n"); 41 | LOGMSG_RAW(l_INFO, PROG_AUTHORS "\n\n"); 42 | LOGMSG_RAW(l_INFO,"%s", 43 | " -i, --input= : input dir (search recursively) or single file\n" 44 | " -o, --output= : output path (default is same as input)\n" 45 | " -f, --file-override : allow output file override if already exists (default: false)\n" 46 | " --no-unquicken : disable unquicken bytecode decompiler (don't de-odex)\n" 47 | " --deps : dump verified dependencies information\n" 48 | " --dis : enable bytecode disassembler\n" 49 | " --ignore-crc-error : decompiled Dex CRC errors are ignored (see issue #3)\n" 50 | " --new-crc= : text file with extracted Apk or Dex file location checksum(s)\n" 51 | " --get-api : get Android API level based on Vdex version (expects single Vdex file)\n" 52 | " -v, --debug=LEVEL : log level (0 - FATAL ... 4 - DEBUG), default: '3' (INFO)\n" 53 | " -l, --log-file=: save disassembler and/or verified dependencies output to log " 54 | "file (default is STDOUT)\n" 55 | " -h, --help : this help\n"); 56 | 57 | if (exit_success) 58 | exitWrapper(EXIT_SUCCESS); 59 | else 60 | exitWrapper(EXIT_FAILURE); 61 | } 62 | // clang-format on 63 | 64 | int main(int argc, char **argv) { 65 | int c; 66 | int logLevel = l_INFO; 67 | const char *logFile = NULL; 68 | runArgs_t pRunArgs = { 69 | .outputDir = NULL, 70 | .fileOverride = false, 71 | .unquicken = true, 72 | .enableDisassembler = false, 73 | .ignoreCrc = false, 74 | .dumpDeps = false, 75 | .newCrcFile = NULL, 76 | .getApi = false, 77 | }; 78 | infiles_t pFiles = { 79 | .inputFile = NULL, 80 | .files = NULL, 81 | .fileCnt = 0, 82 | }; 83 | vdex_api_env_t vdex_api_env; 84 | vdex_api_env_t *pVdex = &vdex_api_env; 85 | 86 | if (argc < 1) usage(true); 87 | 88 | struct option longopts[] = { { "input", required_argument, 0, 'i' }, 89 | { "output", required_argument, 0, 'o' }, 90 | { "file-override", no_argument, 0, 'f' }, 91 | { "no-unquicken", no_argument, 0, 0x101 }, 92 | { "dis", no_argument, 0, 0x102 }, 93 | { "deps", no_argument, 0, 0x103 }, 94 | { "new-crc", required_argument, 0, 0x104 }, 95 | { "ignore-crc-error", no_argument, 0, 0x105 }, 96 | { "get-api", no_argument, 0, 0x106 }, 97 | { "debug", required_argument, 0, 'v' }, 98 | { "log-file", required_argument, 0, 'l' }, 99 | { "help", no_argument, 0, 'h' }, 100 | { 0, 0, 0, 0 } }; 101 | 102 | while ((c = getopt_long(argc, argv, "i:o:fv:l:h?", longopts, NULL)) != -1) { 103 | switch (c) { 104 | case 'i': 105 | pFiles.inputFile = optarg; 106 | break; 107 | case 'o': 108 | pRunArgs.outputDir = optarg; 109 | break; 110 | case 'f': 111 | pRunArgs.fileOverride = true; 112 | break; 113 | case 0x101: 114 | pRunArgs.unquicken = false; 115 | break; 116 | case 0x102: 117 | pRunArgs.enableDisassembler = true; 118 | break; 119 | case 0x103: 120 | pRunArgs.dumpDeps = true; 121 | break; 122 | case 0x104: 123 | pRunArgs.newCrcFile = optarg; 124 | break; 125 | case 0x105: 126 | pRunArgs.ignoreCrc = true; 127 | break; 128 | case 0x106: 129 | pRunArgs.getApi = true; 130 | break; 131 | case 'v': 132 | logLevel = atoi(optarg); 133 | break; 134 | case 'l': 135 | logFile = optarg; 136 | break; 137 | case '?': 138 | case 'h': 139 | usage(true); 140 | break; 141 | default: 142 | exitWrapper(EXIT_FAILURE); 143 | break; 144 | } 145 | } 146 | 147 | // Adjust log level 148 | if (logLevel < 0 || logLevel >= l_MAX_LEVEL) { 149 | LOGMSG(l_FATAL, "Invalid debug level '%d'", logLevel); 150 | } 151 | log_setMinLevel(logLevel); 152 | 153 | // Set log file 154 | if (log_initLogFile(logFile) == false) { 155 | LOGMSG(l_FATAL, "Failed to initialize log file"); 156 | exitWrapper(EXIT_FAILURE); 157 | } 158 | 159 | // Initialize input files 160 | if (!utils_init(&pFiles)) { 161 | LOGMSG(l_FATAL, "Couldn't load input files"); 162 | exitWrapper(EXIT_FAILURE); 163 | } 164 | 165 | // Check output directory 166 | if (pRunArgs.outputDir && !utils_isValidDir(pRunArgs.outputDir)) { 167 | LOGMSG(l_FATAL, "'%s' output directory is not valid", pRunArgs.outputDir); 168 | exitWrapper(EXIT_FAILURE); 169 | } 170 | 171 | int mainRet = EXIT_FAILURE; 172 | 173 | if (pRunArgs.getApi) { 174 | if (pFiles.fileCnt != 1) { 175 | LOGMSG(l_ERROR, "Exactly one input Vdex file is expected when querying API level"); 176 | goto complete; 177 | } 178 | 179 | if (!vdexApi_printApiLevel(pFiles.files[0])) { 180 | LOGMSG(l_ERROR, "Invalid or unsupported input Vdex file"); 181 | } else { 182 | mainRet = EXIT_SUCCESS; 183 | } 184 | 185 | // We're done 186 | goto complete; 187 | } 188 | 189 | // Parse input file with checksums (expects one per line) and update location checksum 190 | if (pRunArgs.newCrcFile) { 191 | if (pFiles.fileCnt != 1) { 192 | LOGMSG(l_ERROR, "Exactly one input Vdex file is expected when updating location checksums"); 193 | goto complete; 194 | } 195 | 196 | int nSums = -1; 197 | u4 *checksums = utils_processFileWithCsums(pRunArgs.newCrcFile, &nSums); 198 | if (checksums == NULL || nSums < 1) { 199 | LOGMSG(l_ERROR, "Failed to extract new location checksums from '%s'", pRunArgs.newCrcFile); 200 | goto complete; 201 | } 202 | 203 | if (!vdexApi_updateChecksums(pFiles.files[0], nSums, checksums, &pRunArgs)) { 204 | LOGMSG(l_ERROR, "Failed to update location checksums"); 205 | } else { 206 | mainRet = EXIT_SUCCESS; 207 | DISPLAY(l_INFO, "%d location checksums have been updated", nSums); 208 | DISPLAY(l_INFO, "Update Vdex file is available in '%s'", 209 | pRunArgs.outputDir ? pRunArgs.outputDir : dirname(pFiles.inputFile)); 210 | } 211 | 212 | free(checksums); 213 | goto complete; 214 | } 215 | 216 | size_t vdexCnt = 0, processedVdexCnt = 0, processedDexCnt = 0; 217 | DISPLAY(l_INFO, "Processing %zu file(s) from %s", pFiles.fileCnt, pFiles.inputFile); 218 | 219 | for (size_t f = 0; f < pFiles.fileCnt; f++) { 220 | off_t fileSz = 0; 221 | int srcfd = -1; 222 | u1 *buf = NULL; 223 | 224 | LOGMSG(l_DEBUG, "Processing '%s'", pFiles.files[f]); 225 | 226 | // mmap file 227 | buf = utils_mapFileToRead(pFiles.files[f], &fileSz, &srcfd); 228 | if (buf == NULL) { 229 | LOGMSG(l_ERROR, "Open & map failed - skipping '%s'", pFiles.files[f]); 230 | continue; 231 | } 232 | 233 | // Validate Vdex magic header and initialize matching version backend 234 | if (!vdexApi_initEnv(buf, pVdex)) { 235 | LOGMSG(l_WARN, "Invalid Vdex header - skipping '%s'", pFiles.files[f]); 236 | goto next_file; 237 | } 238 | 239 | pVdex->dumpHeaderInfo(buf); 240 | vdexCnt++; 241 | 242 | // Dump Vdex verified dependencies info 243 | if (pRunArgs.dumpDeps) { 244 | log_setDisStatus(true); // TODO: Remove 245 | // TODO: Migrate this to vdex_process to avoid iterating Dex files twice. For now it's not 246 | // a priority since the two flags offer different functionalities thus no point using them 247 | // at the same time. 248 | pVdex->dumpDepsInfo(buf); 249 | log_setDisStatus(false); 250 | } 251 | 252 | if (pRunArgs.enableDisassembler) { 253 | log_setDisStatus(true); 254 | } 255 | 256 | // Unquicken Dex bytecode or simply walk optimized Dex files 257 | int ret = pVdex->process(pFiles.files[f], buf, (size_t)fileSz, &pRunArgs); 258 | if (ret == -1) { 259 | LOGMSG(l_ERROR, "Failed to process Dex files - skipping '%s'", pFiles.files[f]); 260 | goto next_file; 261 | } 262 | 263 | processedDexCnt += ret; 264 | processedVdexCnt++; 265 | 266 | next_file: 267 | // Clean-up 268 | munmap(buf, fileSz); 269 | close(srcfd); 270 | } 271 | 272 | DISPLAY(l_INFO, "%zu out of %u Vdex files have been processed", processedVdexCnt, vdexCnt); 273 | DISPLAY(l_INFO, "%u Dex files have been extracted in total", processedDexCnt); 274 | DISPLAY(l_INFO, "Extracted Dex files are available in '%s'", 275 | pRunArgs.outputDir ? pRunArgs.outputDir 276 | : (utils_isValidDir(pFiles.inputFile) ? pFiles.inputFile 277 | : dirname(pFiles.inputFile))); 278 | mainRet = EXIT_SUCCESS; 279 | 280 | complete: 281 | if (pFiles.fileCnt > 1) { 282 | for (size_t i = 0; i < pFiles.fileCnt; i++) { 283 | free(pFiles.files[i]); 284 | } 285 | } 286 | free(pFiles.files); 287 | exitWrapper(mainRet); 288 | } 289 | -------------------------------------------------------------------------------- /src/dex_instruction.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #ifndef _DEX_INSTRUCTION_H_ 24 | #define _DEX_INSTRUCTION_H_ 25 | 26 | #include "common.h" 27 | #include "dex_instruction_list.h" 28 | 29 | typedef enum { 30 | kPackedSwitchSignature = 0x0100, 31 | kSparseSwitchSignature = 0x0200, 32 | kArrayDataSignature = 0x0300, 33 | } Signatures; 34 | 35 | // clang-format off 36 | 37 | typedef enum { 38 | #define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode), 39 | DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM) 40 | RSUB_INT_LIT16 = RSUB_INT, 41 | } Code; 42 | 43 | // clang-format on 44 | 45 | typedef enum { 46 | k10x, // op 47 | k12x, // op vA, vB 48 | k11n, // op vA, #+B 49 | k11x, // op vAA 50 | k10t, // op +AA 51 | k20t, // op +AAAA 52 | k22x, // op vAA, vBBBB 53 | k21t, // op vAA, +BBBB 54 | k21s, // op vAA, #+BBBB 55 | k21h, // op vAA, #+BBBB00000[00000000] 56 | k21c, // op vAA, thing@BBBB 57 | k23x, // op vAA, vBB, vCC 58 | k22b, // op vAA, vBB, #+CC 59 | k22t, // op vA, vB, +CCCC 60 | k22s, // op vA, vB, #+CCCC 61 | k22c, // op vA, vB, thing@CCCC 62 | k32x, // op vAAAA, vBBBB 63 | k30t, // op +AAAAAAAA 64 | k31t, // op vAA, +BBBBBBBB 65 | k31i, // op vAA, #+BBBBBBBB 66 | k31c, // op vAA, thing@BBBBBBBB 67 | k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG) 68 | k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB 69 | 70 | // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count) 71 | // format: AG op BBBB FEDC HHHH 72 | k45cc, 73 | 74 | // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) 75 | // format: AA op BBBB CCCC HHHH 76 | k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) 77 | 78 | k51l, // op vAA, #+BBBBBBBBBBBBBBBB 79 | } Format; 80 | 81 | typedef enum { 82 | kIndexUnknown = 0, 83 | kIndexNone, // has no index 84 | kIndexTypeRef, // type reference index 85 | kIndexStringRef, // string reference index 86 | kIndexMethodRef, // method reference index 87 | kIndexFieldRef, // field reference index 88 | kIndexFieldOffset, // field offset (for static linked fields) 89 | kIndexVtableOffset, // vtable offset (for static linked methods) 90 | kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic) 91 | kIndexCallSiteRef, // call site reference index 92 | kIndexMethodHandleRef, // constant method handle reference index 93 | kIndexProtoRef, // prototype reference index 94 | } IndexType; 95 | 96 | typedef enum { 97 | kBranch = 0x01, // conditional or unconditional branch 98 | kContinue = 0x02, // flow can continue to next statement 99 | kSwitch = 0x04, // switch statement 100 | kThrow = 0x08, // could cause an exception to be thrown 101 | kReturn = 0x10, // returns, no additional statements 102 | kInvoke = 0x20, // a flavor of invoke 103 | kUnconditional = 0x40, // unconditional branch 104 | kExperimental = 0x80, // is an experimental opcode 105 | } Flags; 106 | 107 | typedef enum { 108 | kAdd = 0x0000080, // addition 109 | kSubtract = 0x0000100, // subtract 110 | kMultiply = 0x0000200, // multiply 111 | kDivide = 0x0000400, // division 112 | kRemainder = 0x0000800, // remainder 113 | kAnd = 0x0001000, // and 114 | kOr = 0x0002000, // or 115 | kXor = 0x0004000, // xor 116 | kShl = 0x0008000, // shl 117 | kShr = 0x0010000, // shr 118 | kUshr = 0x0020000, // ushr 119 | kCast = 0x0040000, // cast 120 | kStore = 0x0080000, // store opcode 121 | kLoad = 0x0100000, // load opcode 122 | kClobber = 0x0200000, // clobbers memory in a big way (not just a write) 123 | kRegCFieldOrConstant = 124 | 0x0400000, // is the third virtual register a field or literal constant (vC) 125 | kRegBFieldOrConstant = 126 | 0x0800000, // is the second virtual register a field or literal constant (vB) 127 | } ExtendedFlags; 128 | 129 | typedef enum { 130 | kVerifyNone = 0x0000000, 131 | kVerifyRegA = 0x0000001, 132 | kVerifyRegAWide = 0x0000002, 133 | kVerifyRegB = 0x0000004, 134 | kVerifyRegBField = 0x0000008, 135 | kVerifyRegBMethod = 0x0000010, 136 | kVerifyRegBNewInstance = 0x0000020, 137 | kVerifyRegBString = 0x0000040, 138 | kVerifyRegBType = 0x0000080, 139 | kVerifyRegBWide = 0x0000100, 140 | kVerifyRegC = 0x0000200, 141 | kVerifyRegCField = 0x0000400, 142 | kVerifyRegCNewArray = 0x0000800, 143 | kVerifyRegCType = 0x0001000, 144 | kVerifyRegCWide = 0x0002000, 145 | kVerifyArrayData = 0x0004000, 146 | kVerifyBranchTarget = 0x0008000, 147 | kVerifySwitchTargets = 0x0010000, 148 | kVerifyVarArg = 0x0020000, 149 | kVerifyVarArgNonZero = 0x0040000, 150 | kVerifyVarArgRange = 0x0080000, 151 | kVerifyVarArgRangeNonZero = 0x0100000, 152 | kVerifyRuntimeOnly = 0x0200000, 153 | kVerifyError = 0x0400000, 154 | kVerifyRegHPrototype = 0x0800000, 155 | kVerifyRegBCallSite = 0x1000000, 156 | kVerifyRegBMethodHandle = 0x2000000, 157 | kVerifyRegBPrototype = 0x4000000, 158 | } VerifyFlag; 159 | 160 | typedef struct { 161 | u4 verify_flags; 162 | Format format; 163 | IndexType index_type; 164 | u1 flags; 165 | s1 size_in_code_units; 166 | } instrDesc_t; 167 | 168 | static const u4 kMaxVarArgRegs = 5; 169 | 170 | // clang-format off 171 | 172 | static const char *const kInstructionNames[] = { 173 | #define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname, 174 | DEX_INSTRUCTION_LIST(INSTRUCTION_NAME) 175 | }; 176 | 177 | #define INSTRUCTION_SIZE(opcode, format) \ 178 | (((opcode) == NOP) ? -1 : \ 179 | (((format) >= k10x) && ((format) <= k10t)) ? 1 : \ 180 | (((format) >= k20t) && ((format) <= k22c)) ? 2 : \ 181 | (((format) >= k32x) && ((format) <= k3rc)) ? 3 : \ 182 | (((format) >= k45cc) && ((format) <= k4rcc)) ? 4 : \ 183 | ((format) == k51l) ? 5 : -1) 184 | 185 | static instrDesc_t const kInstructionDescriptors[] = { 186 | #define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \ 187 | { vflags, \ 188 | format, \ 189 | index, \ 190 | flags, \ 191 | INSTRUCTION_SIZE((c), (format)), \ 192 | }, 193 | DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR) 194 | }; 195 | 196 | // clang-format on 197 | 198 | // Instruction opcode functions 199 | Code dexInstr_getOpcode(u2 *); 200 | const char *dexInst_getOpcodeStr(u2 *); 201 | void dexInstr_SetOpcode(u2 *, Code); 202 | 203 | // VRegA 204 | bool dexInstr_hasVRegA(u2 *); 205 | s4 dexInstr_getVRegA(u2 *); 206 | s1 dexInstr_getVRegA_10t(u2 *); 207 | u1 dexInstr_getVRegA_10x(u2 *); 208 | u1 dexInstr_getVRegA_11n(u2 *); 209 | u1 dexInstr_getVRegA_11x(u2 *); 210 | u1 dexInstr_getVRegA_12x(u2 *); 211 | s2 dexInstr_getVRegA_20t(u2 *); 212 | u1 dexInstr_getVRegA_21c(u2 *); 213 | u1 dexInstr_getVRegA_21h(u2 *); 214 | u1 dexInstr_getVRegA_21s(u2 *); 215 | u1 dexInstr_getVRegA_21t(u2 *); 216 | u1 dexInstr_getVRegA_22b(u2 *); 217 | u1 dexInstr_getVRegA_22c(u2 *); 218 | u1 dexInstr_getVRegA_22s(u2 *); 219 | u1 dexInstr_getVRegA_22t(u2 *); 220 | u1 dexInstr_getVRegA_22x(u2 *); 221 | u1 dexInstr_getVRegA_23x(u2 *); 222 | s4 dexInstr_getVRegA_30t(u2 *); 223 | u1 dexInstr_getVRegA_31c(u2 *); 224 | u1 dexInstr_getVRegA_31i(u2 *); 225 | u1 dexInstr_getVRegA_31t(u2 *); 226 | u2 dexInstr_getVRegA_32x(u2 *); 227 | u1 dexInstr_getVRegA_35c(u2 *); 228 | u1 dexInstr_getVRegA_3rc(u2 *); 229 | u1 dexInstr_getVRegA_51l(u2 *); 230 | u1 dexInstr_getVRegA_45cc(u2 *); 231 | u1 dexInstr_getVRegA_4rcc(u2 *); 232 | 233 | // VRegB 234 | bool dexInstr_hasVRegB(u2 *); 235 | s4 dexInstr_getVRegB(u2 *); 236 | u8 dexInstr_getWideVRegB(u2 *); 237 | s1 dexInstr_getVRegB_11n(u2 *); 238 | u1 dexInstr_getVRegB_12x(u2 *); 239 | u2 dexInstr_getVRegB_21c(u2 *); 240 | u2 dexInstr_getVRegB_21h(u2 *); 241 | s2 dexInstr_getVRegB_21s(u2 *); 242 | s2 dexInstr_getVRegB_21t(u2 *); 243 | u1 dexInstr_getVRegB_22b(u2 *); 244 | u1 dexInstr_getVRegB_22c(u2 *); 245 | u1 dexInstr_getVRegB_22s(u2 *); 246 | u1 dexInstr_getVRegB_22t(u2 *); 247 | u2 dexInstr_getVRegB_22x(u2 *); 248 | u1 dexInstr_getVRegB_23x(u2 *); 249 | u4 dexInstr_getVRegB_31c(u2 *); 250 | s4 dexInstr_getVRegB_31i(u2 *); 251 | s4 dexInstr_getVRegB_31t(u2 *); 252 | u2 dexInstr_getVRegB_32x(u2 *); 253 | u2 dexInstr_getVRegB_35c(u2 *); 254 | u2 dexInstr_getVRegB_3rc(u2 *); 255 | u2 dexInstr_getVRegB_45cc(u2 *); 256 | u2 dexInstr_getVRegB_4rcc(u2 *); 257 | u8 dexInstr_getVRegB_51l(u2 *); 258 | 259 | // VRegC 260 | bool dexInstr_hasVRegC(u2 *); 261 | s4 dexInstr_getVRegC(u2 *); 262 | s1 dexInstr_getVRegC_22b(u2 *); 263 | u2 dexInstr_getVRegC_22c(u2 *); 264 | s2 dexInstr_getVRegC_22s(u2 *); 265 | s2 dexInstr_getVRegC_22t(u2 *); 266 | u1 dexInstr_getVRegC_23x(u2 *); 267 | u1 dexInstr_getVRegC_35c(u2 *); 268 | u2 dexInstr_getVRegC_3rc(u2 *); 269 | u1 dexInstr_getVRegC_45cc(u2 *); 270 | u2 dexInstr_getVRegC_4rcc(u2 *); 271 | 272 | // VRegH 273 | bool dexInstr_hasVRegH(u2 *); 274 | s4 dexInstr_getVRegH(u2 *); 275 | u2 dexInstr_getVRegH_45cc(u2 *); 276 | u2 dexInstr_getVRegH_4rcc(u2 *); 277 | bool dexInstr_hasVarArgs(u2 *); 278 | void dexInstr_getVarArgs(u2 *, u4[]); 279 | 280 | // Set register functions 281 | void dexInstr_SetVRegA_10x(u2 *, u1); 282 | void dexInstr_SetVRegB_3rc(u2 *, u2); 283 | void dexInstr_SetVRegB_35c(u2 *, u2); 284 | void dexInstr_SetVRegC_22c(u2 *, u2); 285 | void dexInstr_SetVRegA_21c(u2 *, u1); 286 | void dexInstr_SetVRegB_21c(u2 *, u2); 287 | 288 | bool dexInstr_isBranch(u2 *); 289 | bool dexInstr_isUnconditional(u2 *); 290 | bool dexInstr_isQuickened(u2 *); 291 | bool dexInstr_isSwitch(u2 *); 292 | bool dexInstr_isThrow(u2 *); 293 | bool dexInstr_isReturn(u2 *); 294 | bool dexInstr_isBasicBlockEnd(u2 *); 295 | bool dexInstr_isInvoke(u2 *); 296 | 297 | // Returns the size (in 2 byte code units) of this instruction. 298 | u4 dexInstr_SizeInCodeUnits(u2 *); 299 | 300 | // Global exported arrays with constants 301 | extern const char *const kInstructionNames[]; 302 | extern instrDesc_t const kInstructionDescriptors[]; 303 | 304 | #endif 305 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Anestis Bechtsoudis 190 | Copyright 2017 - 2020 by CENSUS S.A. All Rights Reserved. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vdexExtractor 4 | ----------------------------------------- 5 | 6 | Anestis Bechtsoudis 7 | Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | */ 22 | 23 | #include "utils.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | static bool utils_readdir(infiles_t *pFiles, const char *basePath) { 30 | DIR *dir = opendir(basePath); 31 | if (!dir) { 32 | LOGMSG_P(l_ERROR, "Couldn't open dir '%s'", basePath); 33 | return false; 34 | } 35 | 36 | for (;;) { 37 | errno = 0; 38 | struct dirent *entry = readdir(dir); 39 | if (entry == NULL && errno == EINTR) { 40 | continue; 41 | } 42 | if (entry == NULL && errno != 0) { 43 | LOGMSG_P(l_ERROR, "readdir('%s')", basePath); 44 | return false; 45 | } 46 | if (entry == NULL) { 47 | break; 48 | } 49 | 50 | // Skip special files 51 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { 52 | continue; 53 | } 54 | 55 | char path[PATH_MAX + 2]; 56 | snprintf(path, sizeof(path), "%s/%s", basePath, entry->d_name); 57 | 58 | struct stat st; 59 | if (stat(path, &st) == -1) { 60 | LOGMSG(l_WARN, "Couldn't stat() the '%s' file", path); 61 | continue; 62 | } 63 | 64 | if (S_ISDIR(st.st_mode)) { 65 | if (!utils_readdir(pFiles, path)) { 66 | LOGMSG(l_ERROR, "Failed to process '%s' directory", path); 67 | continue; 68 | } 69 | } 70 | 71 | if (!S_ISREG(st.st_mode)) { 72 | LOGMSG(l_DEBUG, "'%s' is not a regular file, skipping", path); 73 | continue; 74 | } 75 | 76 | if (st.st_size == 0) { 77 | LOGMSG(l_DEBUG, "'%s' is empty", path); 78 | continue; 79 | } 80 | 81 | if (!(pFiles->files = realloc(pFiles->files, sizeof(char *) * (pFiles->fileCnt + 1)))) { 82 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 83 | closedir(dir); 84 | return false; 85 | } 86 | 87 | pFiles->files[pFiles->fileCnt] = strdup(path); 88 | if (!pFiles->files[pFiles->fileCnt]) { 89 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 90 | closedir(dir); 91 | return false; 92 | } 93 | pFiles->fileCnt++; 94 | 95 | LOGMSG(l_DEBUG, "Added '%s' to the list of input files", path); 96 | } 97 | 98 | closedir(dir); 99 | return true; 100 | } 101 | 102 | static bool isPowerOfTwo(uintptr_t x) { return (x & (x - 1)) == 0; } 103 | 104 | bool utils_init(infiles_t *pFiles) { 105 | pFiles->files = malloc(sizeof(char *)); 106 | if (!pFiles->files) { 107 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 108 | return false; 109 | } 110 | 111 | if (!pFiles->inputFile) { 112 | LOGMSG(l_ERROR, "No input file/dir specified"); 113 | return false; 114 | } 115 | 116 | struct stat st; 117 | if (stat(pFiles->inputFile, &st) == -1) { 118 | LOGMSG_P(l_ERROR, "Couldn't stat the input file/dir '%s'", pFiles->inputFile); 119 | return false; 120 | } 121 | 122 | // If a directory, recursively scan 123 | if (S_ISDIR(st.st_mode)) { 124 | if (!utils_readdir(pFiles, pFiles->inputFile)) { 125 | LOGMSG(l_ERROR, "Failed to recursively process '%s' directory", pFiles->inputFile); 126 | return false; 127 | } 128 | 129 | if (pFiles->fileCnt == 0) { 130 | LOGMSG(l_ERROR, "Directory '%s' doesn't contain any regular files", pFiles->inputFile); 131 | return false; 132 | } 133 | 134 | LOGMSG(l_INFO, "%u input files have been added to the list", pFiles->fileCnt); 135 | return true; 136 | } 137 | 138 | if (!S_ISREG(st.st_mode)) { 139 | LOGMSG(l_ERROR, "'%s' is not a regular file, nor a directory", pFiles->inputFile); 140 | return false; 141 | } 142 | 143 | // Single file case 144 | pFiles->files[0] = pFiles->inputFile; 145 | pFiles->fileCnt = 1; 146 | 147 | return true; 148 | } 149 | 150 | bool utils_writeToFd(int fd, const u1 *buf, off_t fileSz) { 151 | off_t written = 0; 152 | while (written < fileSz) { 153 | ssize_t sz = write(fd, &buf[written], fileSz - written); 154 | if (sz < 0 && errno == EINTR) continue; 155 | 156 | if (sz < 0) return false; 157 | 158 | written += sz; 159 | } 160 | 161 | return true; 162 | } 163 | 164 | u1 *utils_mapFileToRead(const char *fileName, off_t *fileSz, int *fd) { 165 | if ((*fd = open(fileName, O_RDONLY)) == -1) { 166 | LOGMSG_P(l_WARN, "Couldn't open() '%s' file in R/O mode", fileName); 167 | return NULL; 168 | } 169 | 170 | struct stat st; 171 | if (fstat(*fd, &st) == -1) { 172 | LOGMSG_P(l_WARN, "Couldn't stat() the '%s' file", fileName); 173 | close(*fd); 174 | return NULL; 175 | } 176 | 177 | u1 *buf; 178 | if ((buf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, *fd, 0)) == MAP_FAILED) { 179 | LOGMSG_P(l_WARN, "Couldn't mmap() the '%s' file", fileName); 180 | close(*fd); 181 | return NULL; 182 | } 183 | 184 | *fileSz = st.st_size; 185 | return buf; 186 | } 187 | 188 | void utils_hexDump(char *desc, const u1 *addr, int len) { 189 | int i; 190 | unsigned char buff[17]; 191 | unsigned char *pc = (unsigned char *)addr; 192 | 193 | // Output description if given. 194 | if (desc != NULL) LOGMSG_RAW(l_DEBUG, "%s:\n", desc); 195 | 196 | if (len == 0) { 197 | LOGMSG_RAW(l_DEBUG, " ZERO LENGTH\n"); 198 | return; 199 | } 200 | if (len < 0) { 201 | LOGMSG_RAW(l_DEBUG, " NEGATIVE LENGTH: %i\n", len); 202 | return; 203 | } 204 | 205 | // Process every byte in the data. 206 | for (i = 0; i < len; i++) { 207 | // Multiple of 16 means new line (with line offset). 208 | 209 | if ((i % 16) == 0) { 210 | // Just don't print ASCII for the zeroth line. 211 | if (i != 0) LOGMSG_RAW(l_DEBUG, " %s\n", buff); 212 | 213 | // Output the offset. 214 | LOGMSG_RAW(l_DEBUG, " %04x ", i); 215 | } 216 | 217 | // Now the hex code for the specific character. 218 | LOGMSG_RAW(l_DEBUG, " %02x", pc[i]); 219 | 220 | // And store a printable ASCII character for later. 221 | if ((pc[i] < 0x20) || (pc[i] > 0x7e)) 222 | buff[i % 16] = '.'; 223 | else 224 | buff[i % 16] = pc[i]; 225 | buff[(i % 16) + 1] = '\0'; 226 | } 227 | 228 | // Pad out last line if not exactly 16 characters. 229 | while ((i % 16) != 0) { 230 | LOGMSG_RAW(l_DEBUG, " "); 231 | i++; 232 | } 233 | 234 | // And print the final ASCII bit. 235 | LOGMSG_RAW(l_DEBUG, " %s\n", buff); 236 | } 237 | 238 | char *utils_bin2hex(const unsigned char *str, const size_t strLen) { 239 | char *result = (char *)malloc(strLen * 2 + 1); 240 | size_t i, j; 241 | int b = 0; 242 | 243 | for (i = j = 0; i < strLen; i++) { 244 | b = str[i] >> 4; 245 | result[j++] = (char)(87 + b + (((b - 10) >> 31) & -39)); 246 | b = str[i] & 0xf; 247 | result[j++] = (char)(87 + b + (((b - 10) >> 31) & -39)); 248 | } 249 | result[j] = '\0'; 250 | return result; 251 | } 252 | 253 | void *utils_malloc(size_t sz) { 254 | void *p = malloc(sz); 255 | if (p == NULL) { 256 | // This is expected to abort 257 | LOGMSG(l_FATAL, "malloc(size='%zu')", sz); 258 | } 259 | return p; 260 | } 261 | 262 | void *utils_calloc(size_t sz) { 263 | void *p = utils_malloc(sz); 264 | memset(p, '\0', sz); 265 | return p; 266 | } 267 | 268 | void *utils_realloc(void *ptr, size_t sz) { 269 | void *ret = realloc(ptr, sz); 270 | if (ret == NULL) { 271 | // This is expected to abort 272 | LOGMSG_P(l_FATAL, "realloc(%p, %zu)", ptr, sz); 273 | free(ptr); 274 | } 275 | return ret; 276 | } 277 | 278 | void *utils_crealloc(void *ptr, size_t old_sz, size_t new_sz) { 279 | // utils_realloc is expected to abort in case of error 280 | void *ret = utils_realloc(ptr, new_sz); 281 | memset(ret + old_sz, 0, new_sz - old_sz); 282 | return ret; 283 | } 284 | 285 | void utils_pseudoStrAppend(const char **charBuf, 286 | size_t *charBufSz, 287 | size_t *charBufOff, 288 | const char *strToAppend) { 289 | const char *buf = *charBuf; 290 | 291 | const size_t kResizeChunk = 512; 292 | if (*charBufSz == 1) { 293 | LOGMSG(l_FATAL, "Pseudo string buffer size must be > 1"); 294 | } 295 | 296 | // If charBuf is null, allocate a new buffer 297 | if (buf == NULL) { 298 | size_t alocSize = (*charBufSz == 0) ? kResizeChunk : *charBufSz; 299 | buf = utils_calloc(alocSize); 300 | *charBufSz = alocSize; 301 | *charBufOff = 0; 302 | } 303 | 304 | // Always ensure null termination 305 | size_t actualBufSz = *charBufSz - 1; 306 | 307 | // Verify valid offset is provided 308 | if (*charBufOff > actualBufSz) { 309 | LOGMSG(l_FATAL, "Invalid string buffer offset (%zu)", *charBufOff); 310 | } 311 | 312 | // Check if new string can fit 313 | if (strlen(strToAppend) + *charBufOff > actualBufSz) { 314 | // We need to resize. utils_crealloc is expected to abort in case of error 315 | size_t resizeSize = *charBufSz + kResizeChunk; 316 | while (resizeSize <= strlen(strToAppend) + *charBufOff) { 317 | resizeSize += kResizeChunk; 318 | } 319 | buf = utils_crealloc((void *)buf, *charBufSz, *charBufSz + resizeSize); 320 | *charBufSz += resizeSize; 321 | actualBufSz += resizeSize; 322 | } 323 | 324 | // Then append the actual string 325 | strncpy((void *)(buf + *charBufOff), strToAppend, strlen(strToAppend)); 326 | *charBufOff += strlen(strToAppend); 327 | 328 | // Update reference before returning 329 | *charBuf = buf; 330 | } 331 | 332 | void utils_startTimer(struct timespec *pTimeSpec) { 333 | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, pTimeSpec); 334 | } 335 | 336 | long utils_endTimer(struct timespec *pTimeSpec) { 337 | struct timespec endTime; 338 | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &endTime); 339 | long diffInNanos = endTime.tv_nsec - pTimeSpec->tv_nsec; 340 | return diffInNanos; 341 | } 342 | 343 | u4 *utils_processFileWithCsums(const char *filePath, int *nCsums) { 344 | u4 *ret = NULL; 345 | FILE *pFile = fopen(filePath, "rb"); 346 | if (pFile == NULL) { 347 | LOGMSG_P(l_WARN, "Couldn't open '%s' - R/O mode", filePath); 348 | return ret; 349 | } 350 | 351 | char *lineptr = NULL; 352 | size_t n = 0; 353 | size_t cnt = 0; 354 | u4 *checksums = NULL; 355 | for (;;) { 356 | if (getline(&lineptr, &n, pFile) == -1) { 357 | break; 358 | } 359 | 360 | if ((checksums = utils_realloc(checksums, (cnt + 1) * sizeof(u4))) == NULL) { 361 | LOGMSG_P(l_WARN, "realloc failed (sz=%zu)", (cnt + 1) * sizeof(u4)); 362 | goto fini; 363 | } 364 | 365 | checksums[cnt] = strtoull(lineptr, 0, 16); 366 | cnt += 1; 367 | } 368 | 369 | *nCsums = cnt; 370 | ret = checksums; 371 | 372 | fini: 373 | free(lineptr); 374 | fclose(pFile); 375 | return ret; 376 | } 377 | 378 | char *utils_fileBasename(char const *path) { 379 | char *s = strrchr(path, '/'); 380 | if (!s) { 381 | return strdup(path); 382 | } else { 383 | return strdup(s + 1); 384 | } 385 | } 386 | 387 | bool utils_isValidDir(const char *path) { 388 | struct stat buf; 389 | if (stat(path, &buf) != 0) { 390 | LOGMSG(l_ERROR, "stat() failed: %s", strerror(errno)); 391 | return false; 392 | } 393 | return S_ISDIR(buf.st_mode); 394 | } 395 | 396 | uintptr_t utils_roundDown(uintptr_t x, uintptr_t n) { 397 | CHECK(isPowerOfTwo(n)); 398 | return (x & -n); 399 | } 400 | 401 | uintptr_t utils_roundUp(uintptr_t x, uintptr_t n) { return utils_roundDown(x + n - 1, n); } 402 | 403 | uintptr_t utils_allignUp(uintptr_t x, uintptr_t n) { return utils_roundUp(x, n); } 404 | -------------------------------------------------------------------------------- /scripts/extract-apps-from-device.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # vdexExtractor 4 | # ----------------------------------------- 5 | # 6 | # Anestis Bechtsoudis 7 | # Copyright 2017 - 2018 by CENSUS S.A. All Rights Reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | set -e # fail on unhandled error 23 | set -u # fail on undefined variable 24 | #set -x # debug 25 | 26 | readonly TOOL_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 27 | readonly TMP_WORK_DIR=$(mktemp -d /tmp/vdex-extractor.XXXXXX) || exit 1 28 | 29 | declare -ar SYS_TOOLS=("mkdir" "dirname" "sed" "grep" "adb") 30 | declare -ar ART_FILE_FORMATS=("art" "dex" "vdex" "odex") 31 | 32 | info() { echo -e "[INFO]: $*" 1>&2; } 33 | warn() { echo -e "[WARN]: $*" 1>&2; } 34 | error() { echo -e "[ERR ]: $*" 1>&2; } 35 | debug() { echo -e "[DBG ]: $*" 1>&2; } 36 | log() { echo -e " $*" 1>&2; } 37 | userIn() { echo -en "[IN ]: $*" 1>&2; } 38 | 39 | abort() { 40 | rm -rf "$TMP_WORK_DIR" 41 | exit "$1" 42 | } 43 | 44 | usage() { 45 | cat <<_EOF 46 | Usage: $(basename "$0") [options] 47 | options: 48 | -o|--output : Output directory to save extracted data (default is '.') 49 | -d|--device : Device serial to use instead of default interactive selection 50 | --system-apps : Extract system apps too (default is user apps only) 51 | --apks : Extract apks (default is optimized files only) 52 | -h|--help : This help message 53 | _EOF 54 | abort 1 55 | } 56 | 57 | commandExists() { 58 | type "$1" &> /dev/null 59 | } 60 | 61 | arrayHasStr() { 62 | local element 63 | for element in "${@:2}"; do [[ "$element" == "$1" ]] && return 0; done 64 | return 1 65 | } 66 | 67 | isDevAuthorized() { 68 | local serial="$1" 69 | 70 | if "$ADB_BIN" -s "$serial" shell id 2>&1 | grep -iq 'device unauthorized'; then 71 | return 1 72 | else 73 | return 0 74 | fi 75 | } 76 | 77 | getDevFingerprint() { 78 | local serial="$1" 79 | 80 | "$ADB_BIN" -s "$serial" shell "getprop ro.build.fingerprint" | tr -d '\r' || { 81 | error "Failed to extract Android device fingerprint" 82 | abort 1 83 | } 84 | } 85 | 86 | getDevSupportedISAs() { 87 | local serial="$1" 88 | 89 | local -a abiList=() 90 | IFS=',' read -r -a abiList <<< "$(getDevCpuAbiList "$serial")" 91 | ( 92 | for abi in "${abiList[@]}"; do getFormattedISA "$abi"; done 93 | ) | uniq | tr '\n' ' ' 94 | } 95 | 96 | getFormattedISA() { 97 | case $1 in 98 | armeabi|armeabi-v7a) 99 | echo "arm" 100 | ;; 101 | arm64-v8a) 102 | echo "arm64" 103 | ;; 104 | x86) 105 | echo "x86" 106 | ;; 107 | x86-64) 108 | echo "x86-64" 109 | ;; 110 | *) 111 | error "Invalid CPU ABI '$1'" 112 | usage 113 | ;; 114 | esac 115 | } 116 | 117 | getDevCpuAbiList() { 118 | local serial="$1" 119 | 120 | "$ADB_BIN" -s "$serial" shell "getprop ro.product.cpu.abilist" | tr -d '\r' || { 121 | error "Failed to extract Android device fingerprint" 122 | abort 1 123 | } 124 | } 125 | 126 | getDevApi() { 127 | local serial="$1" 128 | 129 | "$ADB_BIN" -s "$serial" shell "getprop ro.build.version.sdk" | tr -d '\r' || { 130 | error "Failed to extract Android device fingerprint" 131 | abort 1 132 | } 133 | } 134 | 135 | getConnectedDevices() { 136 | # Ensure adb server is running 137 | local adbServerLog="$TMP_WORK_DIR/adb_start_server.log" 138 | "$ADB_BIN" start-server &> "$adbServerLog" || { 139 | error "Failed to start adb server" 140 | cat "$adbServerLog" 141 | abort 1 142 | } 143 | 144 | local devCount=0 145 | devCount=$("$ADB_BIN" devices -l | grep -v '^$' | grep -v "List of" -c || true) 146 | if [ "$devCount" == 0 ]; then 147 | warn "No connected Android devices found - verify devices are connected & discoverable" 148 | return 149 | fi 150 | 151 | # Change IFS to parse by line 152 | local oldIFS="$IFS" 153 | IFS=$'\n' 154 | 155 | DEVICES_ARRAY=( $("$ADB_BIN" devices -l | grep -v "List of" | cut -d ' ' -f1 || true) ) 156 | 157 | # Revert IFS 158 | IFS="$oldIFS" 159 | } 160 | 161 | processConnectedDevices() { 162 | info "Enumerating connected Android devices" 163 | getConnectedDevices 164 | 165 | # if no user selected device switch to interactive selection 166 | if [[ "$TARGET_DEVICE" == "" ]]; then 167 | if [[ ${#DEVICES_ARRAY[@]} -gt 0 ]]; then 168 | local devArrOff=0 169 | # Prompt user for choice if more than one connected devices 170 | if [[ ${#DEVICES_ARRAY[@]} -gt 1 ]]; then 171 | for (( i=0; i<${#DEVICES_ARRAY[@]}; i++ )) 172 | do 173 | local curDevFingerprint="" 174 | if isDevAuthorized "${DEVICES_ARRAY[i]}"; then 175 | curDevFingerprint=$(getDevFingerprint "${DEVICES_ARRAY[i]}") 176 | log "$i: ${DEVICES_ARRAY[i]} ($curDevFingerprint) [AUTH:true]" 177 | else 178 | log "$i: ${DEVICES_ARRAY[i]} [AUTH:false]" 179 | fi 180 | done 181 | if [[ ${#DEVICES_ARRAY[@]} -gt 1 ]]; then 182 | userIn "Enter device choice: " 183 | read devArrOff 184 | 185 | # Some error checking 186 | if [[ ! "$devArrOff" = *[[:digit:]]* ]]; then 187 | error "Invalid choice (not a number)" 188 | abort 1 189 | fi 190 | if [[ "$devArrOff" -ge ${#DEVICES_ARRAY[@]} || "$devArrOff" -lt 0 ]]; then 191 | error "Invalid choice (out of bounds)" 192 | abort 1 193 | fi 194 | fi 195 | fi 196 | TARGET_DEVICE=$(echo "${DEVICES_ARRAY[$devArrOff]}" | cut -d '"' -f2) 197 | fi 198 | else 199 | # Verify selected device is connected 200 | if ! arrayHasStr "$TARGET_DEVICE" "${DEVICES_ARRAY[@]}"; then 201 | error "Selected '$TARGET_DEVICE' device is not connected to host" 202 | abort 1 203 | fi 204 | fi 205 | 206 | if [[ "$TARGET_DEVICE" != "" ]]; then 207 | if ! isDevAuthorized "$TARGET_DEVICE"; then 208 | error "Cannot proceed with device '$TARGET_DEVICE' since it's not authorized" 209 | abort 1 210 | fi 211 | fi 212 | } 213 | 214 | remoteFileReadable() { 215 | local devSerial="$1" 216 | local remoteFile="$2" 217 | 218 | local adbFileCheckLog="$TMP_WORK_DIR/adb_file_check.log" 219 | 220 | "$ADB_BIN" -s "$targetDev" shell "if [ -r \"$remoteFile\" ]; then echo 'yes'; else echo 'no'; fi" | \ 221 | tr -d '\r' > "$adbFileCheckLog" || { 222 | error "Failed to check file presence" 223 | abort 1 224 | } 225 | 226 | if [ "$(cat "$adbFileCheckLog")" == "yes" ]; then 227 | return 0; 228 | else 229 | return 1; 230 | fi 231 | } 232 | 233 | downloadFileOverAdb() { 234 | local devSerial="$1" 235 | local remoteFile="$2" 236 | local dstFile="$3" 237 | 238 | local adbDownLog="$TMP_WORK_DIR/adb_pull.log" 239 | 240 | # adb internally tries to list directory which is disallowed from selinux 241 | # for dalvikcache_data_file. As such copy first and then download. 242 | "$ADB_BIN" -s "$devSerial" shell "cp \"$remoteFile\" /data/local/tmp/temp.file" &> "$adbDownLog" || { 243 | error "Failed to copy temp file" 244 | cat "$adbDownLog" 245 | abort 1 246 | } 247 | 248 | "$ADB_BIN" -s "$devSerial" pull "/data/local/tmp/temp.file" "$dstFile" &> "$adbDownLog" || { 249 | if grep -q "Permission denied" "$adbDownLog"; then 250 | return; 251 | fi 252 | error "Failed to download file from device" 253 | cat "$adbDownLog" 254 | abort 1 255 | } 256 | 257 | "$ADB_BIN" -s "$devSerial" shell "rm /data/local/tmp/temp.file" &> "$adbDownLog" || { 258 | error "Failed to copy temp file" 259 | cat "$adbDownLog" 260 | abort 1 261 | } 262 | } 263 | 264 | extractInstalledApps() { 265 | local targetDev="$1" 266 | 267 | declare -a installed_apps=() 268 | declare -a installed_app_paths=() 269 | 270 | local appsList="$TMP_WORK_DIR/android_apps_list.txt" 271 | local appsFilter="" appPath="" 272 | 273 | if [[ "$EXTRACT_SYSTEM_APPS" = false ]]; then 274 | appsFilter="-3 -f" 275 | else 276 | appsFilter="-f" 277 | fi 278 | 279 | "$ADB_BIN" -s "$targetDev" shell "pm list packages $appsFilter" | \ 280 | tr -d '\r' > "$appsList" || { 281 | error "Failed to extract installed applications list from device" 282 | abort 1 283 | } 284 | 285 | local package="" packageName="" packagePath="" 286 | while read -r package 287 | do 288 | packageName=$(echo "$package" | cut -d ':' -f2 | awk -F'.apk=' '{print $2}') 289 | packagePath=$(echo "$package" | cut -d ':' -f2 | awk -F'.apk=' '{print $1".apk"}') 290 | 291 | installed_apps+=("$packageName") 292 | installed_app_paths+=("$packagePath") 293 | done < <(cat "$appsList") 294 | 295 | if [ ${#installed_apps[@]} -eq 0 ]; then 296 | warn "No installed packages found" 297 | abort 0 298 | fi 299 | 300 | info "Trying to extract data from '${#installed_apps[@]}' packages" 301 | 302 | for (( i=0; i<${#installed_app_paths[@]}; i++ )) 303 | do 304 | local appPath="" remoteSrc="" localDst="" formatedBaseSuffix="" remoteSrcRoot="" 305 | appPath="${installed_app_paths[i]}" 306 | if [ "$EXTRACT_APKS" = true ]; then 307 | localDst="$OUTPUT_DIR/${installed_apps[i]}.apk" 308 | downloadFileOverAdb "$targetDev" "$appPath" "$localDst" 309 | fi 310 | 311 | if [[ "$appPath" == /data/app/* ]]; then 312 | formatedBaseSuffix="base" 313 | remoteSrcRoot="$(dirname "$appPath")/oat" 314 | else 315 | formatedBaseSuffix="$(echo "${appPath:1}" | tr / @)@classes" 316 | remoteSrcRoot="/data/dalvik-cache" 317 | fi 318 | for artFile in "${ART_FILE_FORMATS[@]}" 319 | do 320 | for abi in $(getDevSupportedISAs "$targetDev") 321 | do 322 | remoteSrc="$remoteSrcRoot/$abi/$formatedBaseSuffix.$artFile" 323 | if [[ "$artFile" == *dex ]]; then 324 | localDst="$OUTPUT_DIR/${installed_apps[i]}.$abi.oat" 325 | else 326 | localDst="$OUTPUT_DIR/${installed_apps[i]}.$abi.$artFile" 327 | fi 328 | if remoteFileReadable "$targetDev" "$remoteSrc"; then 329 | downloadFileOverAdb "$targetDev" "$remoteSrc" "$localDst" 330 | fi 331 | done 332 | done 333 | done 334 | } 335 | 336 | trap "abort 1" SIGHUP SIGINT SIGTERM 337 | 338 | OUTPUT_DIR="$(pwd)" 339 | EXTRACT_SYSTEM_APPS=false 340 | EXTRACT_APKS=false 341 | TARGET_DEVICE="" 342 | 343 | declare -a DEVICES_ARRAY=() 344 | 345 | for i in "${SYS_TOOLS[@]}" 346 | do 347 | if ! commandExists "$i"; then 348 | error "'$i' command not found" 349 | abort 1 350 | fi 351 | done 352 | 353 | ADB_BIN="$(which adb)" 354 | 355 | while [[ $# -gt 0 ]] 356 | do 357 | arg="$1" 358 | case $arg in 359 | -o|--output) 360 | OUTPUT_DIR="$2" 361 | shift 362 | ;; 363 | -d|--device) 364 | TARGET_DEVICE="$2" 365 | shift 366 | ;; 367 | --system-apps) 368 | EXTRACT_SYSTEM_APPS=true 369 | ;; 370 | --apks) 371 | EXTRACT_APKS=true 372 | ;; 373 | -h|--help) 374 | usage 375 | ;; 376 | *) 377 | error "Invalid argument '$1'" 378 | usage 379 | ;; 380 | esac 381 | shift 382 | done 383 | 384 | processConnectedDevices 385 | 386 | # Prepare output directory 387 | if [ ! -d "$OUTPUT_DIR" ]; then 388 | mkdir -p "$OUTPUT_DIR" || { 389 | error "Failed to create output directory" 390 | abort 1 391 | } 392 | fi 393 | 394 | # Only releases >= Nougat are supported 395 | apiVersion=$(getDevApi "$TARGET_DEVICE") 396 | if [ "$apiVersion" -lt 24 ]; then 397 | error "Unsupported old API-$apiVersion" 398 | abort 1 399 | fi 400 | 401 | extractInstalledApps "$TARGET_DEVICE" 402 | 403 | info "Extracted data stored under '$OUTPUT_DIR'" 404 | abort 0 405 | --------------------------------------------------------------------------------