1 /* 2 * Copyright (C) 2019 The Android Open Source Project 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 #define TRACE_TAG ADB 18 19 #include "apk_archive.h" 20 21 #include <inttypes.h> 22 23 #include "adb_trace.h" 24 #include "sysdeps.h" 25 26 #include <android-base/endian.h> 27 #include <android-base/mapped_file.h> 28 29 #include <openssl/md5.h> 30 31 constexpr uint16_t kCompressStored = 0; 32 33 // mask value that signifies that the entry has a DD 34 static const uint32_t kGPBDDFlagMask = 0x0008; 35 36 namespace { 37 struct FileRegion { 38 FileRegion(borrowed_fd fd, off64_t offset, size_t length) 39 : mapped_(android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), offset, length, 40 PROT_READ)) { 41 if (mapped_ != nullptr) { 42 return; 43 } 44 45 // Mapped file failed, falling back to pread. 46 buffer_.resize(length); 47 if (auto err = adb_pread(fd.get(), buffer_.data(), length, offset); size_t(err) != length) { 48 fprintf(stderr, "Unable to read %lld bytes at offset %" PRId64 " \n", 49 static_cast<long long>(length), offset); 50 buffer_.clear(); 51 return; 52 } 53 } 54 55 const char* data() const { return mapped_ ? mapped_->data() : buffer_.data(); } 56 size_t size() const { return mapped_ ? mapped_->size() : buffer_.size(); } 57 58 private: 59 FileRegion() = default; 60 DISALLOW_COPY_AND_ASSIGN(FileRegion); 61 62 std::unique_ptr<android::base::MappedFile> mapped_; 63 std::string buffer_; 64 }; 65 } // namespace 66 67 using com::android::fastdeploy::APKDump; 68 69 ApkArchive::ApkArchive(const std::string& path) : path_(path), size_(0) { 70 fd_.reset(adb_open(path_.c_str(), O_RDONLY)); 71 if (fd_ == -1) { 72 fprintf(stderr, "Unable to open file '%s'\n", path_.c_str()); 73 return; 74 } 75 76 struct stat st; 77 if (stat(path_.c_str(), &st) == -1) { 78 fprintf(stderr, "Unable to stat file '%s'\n", path_.c_str()); 79 return; 80 } 81 size_ = st.st_size; 82 } 83 84 ApkArchive::~ApkArchive() {} 85 86 APKDump ApkArchive::ExtractMetadata() { 87 D("ExtractMetadata"); 88 if (!ready()) { 89 return {}; 90 } 91 92 Location cdLoc = GetCDLocation(); 93 if (!cdLoc.valid) { 94 return {}; 95 } 96 97 APKDump dump; 98 dump.set_absolute_path(path_); 99 dump.set_cd(ReadMetadata(cdLoc)); 100 101 Location sigLoc = GetSignatureLocation(cdLoc.offset); 102 if (sigLoc.valid) { 103 dump.set_signature(ReadMetadata(sigLoc)); 104 } 105 return dump; 106 } 107 108 off_t ApkArchive::FindEndOfCDRecord() const { 109 constexpr int endOfCDSignature = 0x06054b50; 110 constexpr off_t endOfCDMinSize = 22; 111 constexpr off_t endOfCDMaxSize = 65535 + endOfCDMinSize; 112 113 auto sizeToRead = std::min(size_, endOfCDMaxSize); 114 auto readOffset = size_ - sizeToRead; 115 FileRegion mapped(fd_, readOffset, sizeToRead); 116 117 // Start scanning from the end 118 auto* start = mapped.data(); 119 auto* cursor = start + mapped.size() - sizeof(endOfCDSignature); 120 121 // Search for End of Central Directory record signature. 122 while (cursor >= start) { 123 if (*(int32_t*)cursor == endOfCDSignature) { 124 return readOffset + (cursor - start); 125 } 126 cursor--; 127 } 128 return -1; 129 } 130 131 ApkArchive::Location ApkArchive::FindCDRecord(const char* cursor) { 132 struct ecdr_t { 133 int32_t signature; 134 uint16_t diskNumber; 135 uint16_t numDisk; 136 uint16_t diskEntries; 137 uint16_t numEntries; 138 uint32_t crSize; 139 uint32_t offsetToCdHeader; 140 uint16_t commentSize; 141 uint8_t comment[0]; 142 } __attribute__((packed)); 143 ecdr_t* header = (ecdr_t*)cursor; 144 145 Location location; 146 location.offset = header->offsetToCdHeader; 147 location.size = header->crSize; 148 location.valid = true; 149 return location; 150 } 151 152 ApkArchive::Location ApkArchive::GetCDLocation() { 153 constexpr off_t cdEntryHeaderSizeBytes = 22; 154 Location location; 155 156 // Find End of Central Directory Record 157 off_t eocdRecord = FindEndOfCDRecord(); 158 if (eocdRecord < 0) { 159 fprintf(stderr, "Unable to find End of Central Directory record in file '%s'\n", 160 path_.c_str()); 161 return location; 162 } 163 164 // Find Central Directory Record 165 FileRegion mapped(fd_, eocdRecord, cdEntryHeaderSizeBytes); 166 location = FindCDRecord(mapped.data()); 167 if (!location.valid) { 168 fprintf(stderr, "Unable to find Central Directory File Header in file '%s'\n", 169 path_.c_str()); 170 return location; 171 } 172 173 return location; 174 } 175 176 ApkArchive::Location ApkArchive::GetSignatureLocation(off_t cdRecordOffset) { 177 Location location; 178 179 // Signature constants. 180 constexpr off_t endOfSignatureSize = 24; 181 off_t signatureOffset = cdRecordOffset - endOfSignatureSize; 182 if (signatureOffset < 0) { 183 fprintf(stderr, "Unable to find signature in file '%s'\n", path_.c_str()); 184 return location; 185 } 186 187 FileRegion mapped(fd_, signatureOffset, endOfSignatureSize); 188 189 uint64_t signatureSize = *(uint64_t*)mapped.data(); 190 auto* signature = mapped.data() + sizeof(signatureSize); 191 // Check if there is a v2/v3 Signature block here. 192 if (memcmp(signature, "APK Sig Block 42", 16)) { 193 return location; 194 } 195 196 // This is likely a signature block. 197 location.size = signatureSize; 198 location.offset = cdRecordOffset - location.size - 8; 199 location.valid = true; 200 201 return location; 202 } 203 204 std::string ApkArchive::ReadMetadata(Location loc) const { 205 FileRegion mapped(fd_, loc.offset, loc.size); 206 return {mapped.data(), mapped.size()}; 207 } 208 209 size_t ApkArchive::ParseCentralDirectoryRecord(const char* input, size_t size, std::string* md5Hash, 210 int64_t* localFileHeaderOffset, int64_t* dataSize) { 211 // A structure representing the fixed length fields for a single 212 // record in the central directory of the archive. In addition to 213 // the fixed length fields listed here, each central directory 214 // record contains a variable length "file_name" and "extra_field" 215 // whose lengths are given by |file_name_length| and |extra_field_length| 216 // respectively. 217 static constexpr int kCDFileHeaderMagic = 0x02014b50; 218 struct CentralDirectoryRecord { 219 // The start of record signature. Must be |kSignature|. 220 uint32_t record_signature; 221 // Source tool version. Top byte gives source OS. 222 uint16_t version_made_by; 223 // Tool version. Ignored by this implementation. 224 uint16_t version_needed; 225 // The "general purpose bit flags" for this entry. The only 226 // flag value that we currently check for is the "data descriptor" 227 // flag. 228 uint16_t gpb_flags; 229 // The compression method for this entry, one of |kCompressStored| 230 // and |kCompressDeflated|. 231 uint16_t compression_method; 232 // The file modification time and date for this entry. 233 uint16_t last_mod_time; 234 uint16_t last_mod_date; 235 // The CRC-32 checksum for this entry. 236 uint32_t crc32; 237 // The compressed size (in bytes) of this entry. 238 uint32_t compressed_size; 239 // The uncompressed size (in bytes) of this entry. 240 uint32_t uncompressed_size; 241 // The length of the entry file name in bytes. The file name 242 // will appear immediately after this record. 243 uint16_t file_name_length; 244 // The length of the extra field info (in bytes). This data 245 // will appear immediately after the entry file name. 246 uint16_t extra_field_length; 247 // The length of the entry comment (in bytes). This data will 248 // appear immediately after the extra field. 249 uint16_t comment_length; 250 // The start disk for this entry. Ignored by this implementation). 251 uint16_t file_start_disk; 252 // File attributes. Ignored by this implementation. 253 uint16_t internal_file_attributes; 254 // File attributes. For archives created on Unix, the top bits are the 255 // mode. 256 uint32_t external_file_attributes; 257 // The offset to the local file header for this entry, from the 258 // beginning of this archive. 259 uint32_t local_file_header_offset; 260 261 private: 262 CentralDirectoryRecord() = default; 263 DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord); 264 } __attribute__((packed)); 265 266 const CentralDirectoryRecord* cdr; 267 if (size < sizeof(*cdr)) { 268 return 0; 269 } 270 271 auto begin = input; 272 cdr = reinterpret_cast<const CentralDirectoryRecord*>(begin); 273 if (cdr->record_signature != kCDFileHeaderMagic) { 274 fprintf(stderr, "Invalid Central Directory Record signature\n"); 275 return 0; 276 } 277 auto end = begin + sizeof(*cdr) + cdr->file_name_length + cdr->extra_field_length + 278 cdr->comment_length; 279 280 uint8_t md5Digest[MD5_DIGEST_LENGTH]; 281 MD5((const unsigned char*)begin, end - begin, md5Digest); 282 md5Hash->assign((const char*)md5Digest, sizeof(md5Digest)); 283 284 *localFileHeaderOffset = cdr->local_file_header_offset; 285 *dataSize = (cdr->compression_method == kCompressStored) ? cdr->uncompressed_size 286 : cdr->compressed_size; 287 288 return end - begin; 289 } 290 291 size_t ApkArchive::CalculateLocalFileEntrySize(int64_t localFileHeaderOffset, 292 int64_t dataSize) const { 293 // The local file header for a given entry. This duplicates information 294 // present in the central directory of the archive. It is an error for 295 // the information here to be different from the central directory 296 // information for a given entry. 297 static constexpr int kLocalFileHeaderMagic = 0x04034b50; 298 struct LocalFileHeader { 299 // The local file header signature, must be |kSignature|. 300 uint32_t lfh_signature; 301 // Tool version. Ignored by this implementation. 302 uint16_t version_needed; 303 // The "general purpose bit flags" for this entry. The only 304 // flag value that we currently check for is the "data descriptor" 305 // flag. 306 uint16_t gpb_flags; 307 // The compression method for this entry, one of |kCompressStored| 308 // and |kCompressDeflated|. 309 uint16_t compression_method; 310 // The file modification time and date for this entry. 311 uint16_t last_mod_time; 312 uint16_t last_mod_date; 313 // The CRC-32 checksum for this entry. 314 uint32_t crc32; 315 // The compressed size (in bytes) of this entry. 316 uint32_t compressed_size; 317 // The uncompressed size (in bytes) of this entry. 318 uint32_t uncompressed_size; 319 // The length of the entry file name in bytes. The file name 320 // will appear immediately after this record. 321 uint16_t file_name_length; 322 // The length of the extra field info (in bytes). This data 323 // will appear immediately after the entry file name. 324 uint16_t extra_field_length; 325 326 private: 327 LocalFileHeader() = default; 328 DISALLOW_COPY_AND_ASSIGN(LocalFileHeader); 329 } __attribute__((packed)); 330 static constexpr int kLocalFileHeaderSize = sizeof(LocalFileHeader); 331 CHECK(ready()) << path_; 332 333 const LocalFileHeader* lfh; 334 if (localFileHeaderOffset + kLocalFileHeaderSize > size_) { 335 fprintf(stderr, 336 "Invalid Local File Header offset in file '%s' at offset %lld, file size %lld\n", 337 path_.c_str(), static_cast<long long>(localFileHeaderOffset), 338 static_cast<long long>(size_)); 339 return 0; 340 } 341 342 FileRegion lfhMapped(fd_, localFileHeaderOffset, sizeof(LocalFileHeader)); 343 lfh = reinterpret_cast<const LocalFileHeader*>(lfhMapped.data()); 344 if (lfh->lfh_signature != kLocalFileHeaderMagic) { 345 fprintf(stderr, "Invalid Local File Header signature in file '%s' at offset %lld\n", 346 path_.c_str(), static_cast<long long>(localFileHeaderOffset)); 347 return 0; 348 } 349 350 // The *optional* data descriptor start signature. 351 static constexpr int kOptionalDataDescriptorMagic = 0x08074b50; 352 struct DataDescriptor { 353 // CRC-32 checksum of the entry. 354 uint32_t crc32; 355 // Compressed size of the entry. 356 uint32_t compressed_size; 357 // Uncompressed size of the entry. 358 uint32_t uncompressed_size; 359 360 private: 361 DataDescriptor() = default; 362 DISALLOW_COPY_AND_ASSIGN(DataDescriptor); 363 } __attribute__((packed)); 364 static constexpr int kDataDescriptorSize = sizeof(DataDescriptor); 365 366 off_t ddOffset = localFileHeaderOffset + kLocalFileHeaderSize + lfh->file_name_length + 367 lfh->extra_field_length + dataSize; 368 int64_t ddSize = 0; 369 370 int64_t localDataSize; 371 if (lfh->gpb_flags & kGPBDDFlagMask) { 372 // There is trailing data descriptor. 373 const DataDescriptor* dd; 374 375 if (ddOffset + int(sizeof(uint32_t)) > size_) { 376 fprintf(stderr, 377 "Error reading trailing data descriptor signature in file '%s' at offset %lld, " 378 "file size %lld\n", 379 path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_)); 380 return 0; 381 } 382 383 FileRegion ddMapped(fd_, ddOffset, sizeof(uint32_t) + sizeof(DataDescriptor)); 384 385 off_t localDDOffset = 0; 386 if (kOptionalDataDescriptorMagic == *(uint32_t*)ddMapped.data()) { 387 ddOffset += sizeof(uint32_t); 388 localDDOffset += sizeof(uint32_t); 389 ddSize += sizeof(uint32_t); 390 } 391 if (ddOffset + kDataDescriptorSize > size_) { 392 fprintf(stderr, 393 "Error reading trailing data descriptor in file '%s' at offset %lld, file size " 394 "%lld\n", 395 path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_)); 396 return 0; 397 } 398 399 dd = reinterpret_cast<const DataDescriptor*>(ddMapped.data() + localDDOffset); 400 localDataSize = (lfh->compression_method == kCompressStored) ? dd->uncompressed_size 401 : dd->compressed_size; 402 ddSize += sizeof(*dd); 403 } else { 404 localDataSize = (lfh->compression_method == kCompressStored) ? lfh->uncompressed_size 405 : lfh->compressed_size; 406 } 407 if (localDataSize != dataSize) { 408 fprintf(stderr, 409 "Data sizes mismatch in file '%s' at offset %lld, CDr: %lld vs LHR/DD: %lld\n", 410 path_.c_str(), static_cast<long long>(localFileHeaderOffset), 411 static_cast<long long>(dataSize), static_cast<long long>(localDataSize)); 412 return 0; 413 } 414 415 return kLocalFileHeaderSize + lfh->file_name_length + lfh->extra_field_length + dataSize + 416 ddSize; 417 } 418