1 /* 2 ** 3 ** Copyright 2016, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 #include "read_apk.h" 19 20 #include <errno.h> 21 #include <stdio.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <sys/types.h> 25 #include <unistd.h> 26 27 #include <memory> 28 29 #include <android-base/file.h> 30 #include <android-base/logging.h> 31 #include <android-base/strings.h> 32 #include <ziparchive/zip_archive.h> 33 #include "read_elf.h" 34 #include "utils.h" 35 36 std::unordered_map<std::string, ApkInspector::ApkNode> ApkInspector::embedded_elf_cache_; 37 38 EmbeddedElf* ApkInspector::FindElfInApkByOffset(const std::string& apk_path, uint64_t file_offset) { 39 // Already in cache? 40 ApkNode& node = embedded_elf_cache_[apk_path]; 41 auto it = node.offset_map.find(file_offset); 42 if (it != node.offset_map.end()) { 43 return it->second.get(); 44 } 45 std::unique_ptr<EmbeddedElf> elf = FindElfInApkByOffsetWithoutCache(apk_path, file_offset); 46 EmbeddedElf* result = elf.get(); 47 node.offset_map[file_offset] = std::move(elf); 48 if (result != nullptr) { 49 node.name_map[result->entry_name()] = result; 50 } 51 return result; 52 } 53 54 EmbeddedElf* ApkInspector::FindElfInApkByName(const std::string& apk_path, 55 const std::string& entry_name) { 56 ApkNode& node = embedded_elf_cache_[apk_path]; 57 auto it = node.name_map.find(entry_name); 58 if (it != node.name_map.end()) { 59 return it->second; 60 } 61 std::unique_ptr<EmbeddedElf> elf = FindElfInApkByNameWithoutCache(apk_path, entry_name); 62 EmbeddedElf* result = elf.get(); 63 node.name_map[entry_name] = result; 64 if (result != nullptr) { 65 node.offset_map[result->entry_offset()] = std::move(elf); 66 } 67 return result; 68 } 69 70 std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache( 71 const std::string& apk_path, uint64_t file_offset) { 72 std::unique_ptr<ArchiveHelper> ahelper = ArchiveHelper::CreateInstance(apk_path); 73 if (!ahelper) { 74 return nullptr; 75 } 76 77 // Iterate through the zip file. Look for a zip entry corresponding 78 // to an uncompressed blob whose range intersects with the mmap 79 // offset we're interested in. 80 bool found = false; 81 ZipEntry found_entry; 82 std::string found_entry_name; 83 bool result = ahelper->IterateEntries([&](ZipEntry& entry, const std::string& name) { 84 if (entry.method == kCompressStored && 85 file_offset >= static_cast<uint64_t>(entry.offset) && 86 file_offset < static_cast<uint64_t>(entry.offset) + entry.uncompressed_length) { 87 found = true; 88 found_entry = entry; 89 found_entry_name = name; 90 return false; 91 } 92 return true; 93 }); 94 if (!result || !found) { 95 return nullptr; 96 } 97 98 // We found something in the zip file at the right spot. Is it an ELF? 99 if (IsValidElfFile(ahelper->GetFd(), found_entry.offset) != ElfStatus::NO_ERROR) { 100 // Omit files that are not ELF files. 101 return nullptr; 102 } 103 return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, found_entry_name, 104 found_entry.offset, 105 found_entry.uncompressed_length)); 106 } 107 108 std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByNameWithoutCache( 109 const std::string& apk_path, const std::string& entry_name) { 110 std::unique_ptr<ArchiveHelper> ahelper = ArchiveHelper::CreateInstance(apk_path); 111 if (!ahelper) { 112 return nullptr; 113 } 114 ZipEntry zentry; 115 if (!ahelper->FindEntry(entry_name, &zentry)) { 116 return nullptr; 117 } 118 if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) { 119 return nullptr; 120 } 121 return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, entry_name, zentry.offset, 122 zentry.uncompressed_length)); 123 } 124 125 // Refer file in apk in compliance with http://developer.android.com/reference/java/net/JarURLConnection.html. 126 std::string GetUrlInApk(const std::string& apk_path, const std::string& elf_filename) { 127 return apk_path + "!/" + elf_filename; 128 } 129 130 std::tuple<bool, std::string, std::string> SplitUrlInApk(const std::string& path) { 131 size_t pos = path.find("!/"); 132 if (pos == std::string::npos) { 133 return std::make_tuple(false, "", ""); 134 } 135 return std::make_tuple(true, path.substr(0, pos), path.substr(pos + 2)); 136 } 137 138 // Parse path like "[anon:dalvik-classes.dex extracted in memory from /..base.apk] (deleted)", 139 // or "/dev/ashmem/dalvik-classes.dex extracted in memory from /..base.apk (deleted)" on Android P. 140 bool ParseExtractedInMemoryPath(const std::string& path, std::string* zip_path, 141 std::string* entry_name) { 142 const char* prefixes[2] = {"[anon:dalvik-", "/dev/ashmem/dalvik-"}; 143 const char* key = " extracted in memory from "; 144 size_t pos = path.find(key); 145 if (pos != std::string::npos) { 146 for (const char* prefix : prefixes) { 147 if (android::base::StartsWith(path, prefix)) { 148 size_t entry_name_start = strlen(prefix); 149 size_t entry_name_end = pos; 150 size_t zip_path_start = pos + strlen(key); 151 size_t zip_path_end = path.find_first_of(" ]", zip_path_start); 152 if (zip_path_end == std::string::npos) { 153 zip_path_end = path.size(); 154 } 155 if (entry_name_start < entry_name_end && zip_path_start < zip_path_end) { 156 *entry_name = path.substr(entry_name_start, entry_name_end - entry_name_start); 157 *zip_path = path.substr(zip_path_start, zip_path_end - zip_path_start); 158 size_t multidex_separator_pos = zip_path->find('!'); 159 if (multidex_separator_pos != std::string::npos) { 160 zip_path->resize(multidex_separator_pos); 161 } 162 return true; 163 } 164 } 165 } 166 } 167 return false; 168 } 169