| 44 | } |
| 45 | |
| 46 | CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type) |
| 47 | { |
| 48 | auto file = FileAccessorCache::Global().Open(filePath).lock(); |
| 49 | |
| 50 | // TODO: Pull this out into another function so we can do IsValidDSCFile or something. |
| 51 | // We first want to make sure that the base file is dyld. |
| 52 | // All entries must start with "dyld". |
| 53 | DataBuffer sig = file->ReadBuffer(0, 4); |
| 54 | if (sig.GetLength() != 4) |
| 55 | throw std::runtime_error("File is empty!"); |
| 56 | const char* magic = static_cast<char*>(sig.GetData()); |
| 57 | if (strncmp(magic, "dyld", 4) != 0) |
| 58 | throw std::runtime_error("File does not start with `dyld`!"); |
| 59 | |
| 60 | // Read the header, this _should_ be compatible with all known DSC formats. |
| 61 | // Mason: the above is not true! https://github.com/Vector35/binaryninja-api/issues/6073 |
| 62 | // The mappingOffset should point right after the header. We use this to constrain the read size so unsupported fields are zeroed. |
| 63 | size_t headerSize = file->ReadUInt32(0x10); |
| 64 | dyld_cache_header header = {}; |
| 65 | // Truncate buffer length (headerSize) if larger than our `dyld_cache_header` for reading. |
| 66 | file->Read(&header, 0, std::min(headerSize, sizeof(dyld_cache_header))); |
| 67 | |
| 68 | // Read the mappings using the headers `mappingCount` and `mappingOffset`. |
| 69 | dyld_cache_mapping_info currentMapping = {}; |
| 70 | std::vector<dyld_cache_mapping_info> mappings; |
| 71 | for (size_t i = 0; i < header.mappingCount; i++) |
| 72 | { |
| 73 | file->Read(¤tMapping, header.mappingOffset + (i * sizeof(currentMapping)), sizeof(currentMapping)); |
| 74 | |
| 75 | // Cancel adding the entry if we have an invalid mapping. |
| 76 | if (currentMapping.fileOffset + currentMapping.size > file->Length()) |
| 77 | throw std::runtime_error("Invalid mapping in shared cache entry"); |
| 78 | |
| 79 | // TODO: Check initProt to make sure its in the range of expected values. |
| 80 | |
| 81 | mappings.push_back(currentMapping); |
| 82 | } |
| 83 | |
| 84 | // Handle special entry types. |
| 85 | if (fileName.find(".dylddata") != std::string::npos) |
| 86 | { |
| 87 | // We found a single dyld data cache entry file. Mark it as such! |
| 88 | type = CacheEntryType::DyldData; |
| 89 | } |
| 90 | else if (fileName.find(".symbols") != std::string::npos && mappings.size() == 1) |
| 91 | { |
| 92 | // We found a single symbols cache entry file. Mark it as such! |
| 93 | type = CacheEntryType::Symbols; |
| 94 | // Adjust the mapping for the symbol file, they seem to be only for the header. |
| 95 | // If we do not adjust the mapping than we will not be able to read the symbol table through the virtual memory. |
| 96 | mappings[0].fileOffset = 0; |
| 97 | mappings[0].size = file->Length(); |
| 98 | } |
| 99 | else if (mappings.size() == 1 && header.imagesCountOld == 0 && header.imagesCount == 0 |
| 100 | && header.imagesTextOffset == 0) |
| 101 | { |
| 102 | // Stub entry file, should only have a single mapping and no images. |
| 103 | // NOTE: If we end up identifying something incorrectly as a stub we need to restrict this further. |
nothing calls this directly
no test coverage detected