/* ** ** Copyright 2016, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include "read_apk.h" #include #include #include #include #include #include #include #include #include #include #include #include "read_elf.h" #include "utils.h" std::unordered_map ApkInspector::embedded_elf_cache_; EmbeddedElf* ApkInspector::FindElfInApkByOffset(const std::string& apk_path, uint64_t file_offset) { // Already in cache? ApkNode& node = embedded_elf_cache_[apk_path]; auto it = node.offset_map.find(file_offset); if (it != node.offset_map.end()) { return it->second.get(); } std::unique_ptr elf = FindElfInApkByOffsetWithoutCache(apk_path, file_offset); EmbeddedElf* result = elf.get(); node.offset_map[file_offset] = std::move(elf); if (result != nullptr) { node.name_map[result->entry_name()] = result; } return result; } EmbeddedElf* ApkInspector::FindElfInApkByName(const std::string& apk_path, const std::string& entry_name) { ApkNode& node = embedded_elf_cache_[apk_path]; auto it = node.name_map.find(entry_name); if (it != node.name_map.end()) { return it->second; } std::unique_ptr elf = FindElfInApkByNameWithoutCache(apk_path, entry_name); EmbeddedElf* result = elf.get(); node.name_map[entry_name] = result; if (result != nullptr) { node.offset_map[result->entry_offset()] = std::move(elf); } return result; } std::unique_ptr ApkInspector::FindElfInApkByOffsetWithoutCache( const std::string& apk_path, uint64_t file_offset) { std::unique_ptr ahelper = ArchiveHelper::CreateInstance(apk_path); if (!ahelper) { return nullptr; } // Iterate through the zip file. Look for a zip entry corresponding // to an uncompressed blob whose range intersects with the mmap // offset we're interested in. bool found = false; ZipEntry found_entry; std::string found_entry_name; bool result = ahelper->IterateEntries([&](ZipEntry& entry, const std::string& name) { if (entry.method == kCompressStored && file_offset >= static_cast(entry.offset) && file_offset < static_cast(entry.offset) + entry.uncompressed_length) { found = true; found_entry = entry; found_entry_name = name; return false; } return true; }); if (!result || !found) { return nullptr; } // We found something in the zip file at the right spot. Is it an ELF? if (IsValidElfFile(ahelper->GetFd(), found_entry.offset) != ElfStatus::NO_ERROR) { // Omit files that are not ELF files. return nullptr; } return std::unique_ptr(new EmbeddedElf(apk_path, found_entry_name, found_entry.offset, found_entry.uncompressed_length)); } std::unique_ptr ApkInspector::FindElfInApkByNameWithoutCache( const std::string& apk_path, const std::string& entry_name) { std::unique_ptr ahelper = ArchiveHelper::CreateInstance(apk_path); if (!ahelper) { return nullptr; } ZipEntry zentry; if (!ahelper->FindEntry(entry_name, &zentry)) { return nullptr; } if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) { return nullptr; } return std::unique_ptr(new EmbeddedElf(apk_path, entry_name, zentry.offset, zentry.uncompressed_length)); } // Refer file in apk in compliance with http://developer.android.com/reference/java/net/JarURLConnection.html. std::string GetUrlInApk(const std::string& apk_path, const std::string& elf_filename) { return apk_path + "!/" + elf_filename; } std::tuple SplitUrlInApk(const std::string& path) { size_t pos = path.find("!/"); if (pos == std::string::npos) { return std::make_tuple(false, "", ""); } return std::make_tuple(true, path.substr(0, pos), path.substr(pos + 2)); } // Parse path like "[anon:dalvik-classes.dex extracted in memory from /..base.apk] (deleted)", // or "/dev/ashmem/dalvik-classes.dex extracted in memory from /..base.apk (deleted)" on Android P. bool ParseExtractedInMemoryPath(const std::string& path, std::string* zip_path, std::string* entry_name) { const char* prefixes[2] = {"[anon:dalvik-", "/dev/ashmem/dalvik-"}; const char* key = " extracted in memory from "; size_t pos = path.find(key); if (pos != std::string::npos) { for (const char* prefix : prefixes) { if (android::base::StartsWith(path, prefix)) { size_t entry_name_start = strlen(prefix); size_t entry_name_end = pos; size_t zip_path_start = pos + strlen(key); size_t zip_path_end = path.find_first_of(" ]", zip_path_start); if (zip_path_end == std::string::npos) { zip_path_end = path.size(); } if (entry_name_start < entry_name_end && zip_path_start < zip_path_end) { *entry_name = path.substr(entry_name_start, entry_name_end - entry_name_start); *zip_path = path.substr(zip_path_start, zip_path_end - zip_path_start); size_t multidex_separator_pos = zip_path->find('!'); if (multidex_separator_pos != std::string::npos) { zip_path->resize(multidex_separator_pos); } return true; } } } } return false; }