* Parses .NET metadata stream. * @param baseAddress Base address of .NET metadata header. * @param offset Offset of metadata stream. * @param size Size of stream. */
| 2339 | * @param size Size of stream. |
| 2340 | */ |
| 2341 | void PeFormat::parseMetadataStream(std::uint64_t baseAddress, std::uint64_t offset, std::uint64_t size) |
| 2342 | { |
| 2343 | const std::uint64_t metadataStreamHeaderSize = 24; |
| 2344 | if (size <= metadataStreamHeaderSize) |
| 2345 | { |
| 2346 | return; |
| 2347 | } |
| 2348 | |
| 2349 | metadataStream = std::make_unique<MetadataStream>(offset, size); |
| 2350 | auto address = baseAddress + offset; |
| 2351 | |
| 2352 | std::uint64_t majorVersion, minorVersion, heapOffsetSizes, valid, sorted; |
| 2353 | if (!get1Byte(address + 4, majorVersion) |
| 2354 | || !get1Byte(address + 5, minorVersion) |
| 2355 | || !get1Byte(address + 6, heapOffsetSizes) |
| 2356 | || !get8Byte(address + 8, valid) |
| 2357 | || !get8Byte(address + 16, sorted)) |
| 2358 | { |
| 2359 | return; |
| 2360 | } |
| 2361 | |
| 2362 | metadataStream->setMajorVersion(majorVersion); |
| 2363 | metadataStream->setMinorVersion(minorVersion); |
| 2364 | |
| 2365 | // 'heapOffsetSizes' define whether we should use std::uint16_t or dstd::uint16_t for indexes into different streams |
| 2366 | metadataStream->setStringStreamIndexSize(heapOffsetSizes & 0x01 ? 4 : 2); |
| 2367 | metadataStream->setGuidStreamIndexSize(heapOffsetSizes & 0x02 ? 4 : 2); |
| 2368 | metadataStream->setBlobStreamIndexSize(heapOffsetSizes & 0x04 ? 4 : 2); |
| 2369 | |
| 2370 | // Tables that are present in the stream are marked with bit 1 in 'valid' attribute |
| 2371 | // At first we need to create all tables with their sizes because we don't know how many of them is there, |
| 2372 | // so we don't know where to start to parse table content. |
| 2373 | std::uint64_t currentAddress = address + metadataStreamHeaderSize; |
| 2374 | for (std::size_t i = 0; i < 64; ++i) |
| 2375 | { |
| 2376 | if ((valid >> i) & 1) |
| 2377 | { |
| 2378 | std::uint64_t tableSize; |
| 2379 | if (!get4Byte(currentAddress, tableSize)) |
| 2380 | { |
| 2381 | return; |
| 2382 | } |
| 2383 | |
| 2384 | // If the size of the metadata table would be larger than there are data available, we just end. This must be corrupted. |
| 2385 | if (tableSize > getLoadedFileLength()) |
| 2386 | { |
| 2387 | return; |
| 2388 | } |
| 2389 | |
| 2390 | metadataStream->addMetadataTable(static_cast<MetadataTableType>(i), tableSize); |
| 2391 | currentAddress += 4; |
| 2392 | } |
| 2393 | } |
| 2394 | // ExtraData flags means there is extra 4 bytes at the end Rows array that contaisn the rows sizes |
| 2395 | // I don't see anything about in at ECMA-335, but I can see in real samples and in IlSpy source |
| 2396 | // that understands it and correctly decompiles, sample: 5b5817fe2d4f0989501802b0e2bb4451583ff27fd0723f40bb7f8b0417dd7c58 |
| 2397 | if (heapOffsetSizes & 0x40) |
| 2398 | { |
nothing calls this directly
no test coverage detected