| 101 | }; |
| 102 | |
| 103 | PeView parse_pe(std::vector<std::uint8_t> raw) { |
| 104 | PeView v; |
| 105 | v.bytes = std::move(raw); |
| 106 | if (v.bytes.size() < sizeof(IMAGE_DOS_HEADER)) throw Error("target PE too small for DOS header"); |
| 107 | |
| 108 | auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(v.bytes.data()); |
| 109 | if (dos->e_magic != kDosMagic) throw Error("target PE: bad DOS magic"); |
| 110 | |
| 111 | v.nt_off = static_cast<std::size_t>(dos->e_lfanew); |
| 112 | if (v.nt_off + sizeof(std::uint32_t) + sizeof(IMAGE_FILE_HEADER) > v.bytes.size()) throw Error("target PE: NT headers out of range"); |
| 113 | |
| 114 | const std::uint32_t sig = *reinterpret_cast<std::uint32_t*>(v.bytes.data() + v.nt_off); |
| 115 | if (sig != kPeMagic) throw Error("target PE: bad NT signature"); |
| 116 | |
| 117 | v.file_hdr_off = v.nt_off + 4; |
| 118 | auto* fh = reinterpret_cast<IMAGE_FILE_HEADER*>(v.bytes.data() + v.file_hdr_off); |
| 119 | |
| 120 | v.num_sections = fh->NumberOfSections; |
| 121 | v.opt_hdr_off = v.file_hdr_off + sizeof(IMAGE_FILE_HEADER); |
| 122 | |
| 123 | const std::uint16_t opt_magic = *reinterpret_cast<std::uint16_t*>(v.bytes.data() + v.opt_hdr_off); |
| 124 | if (opt_magic == kOpt64) { |
| 125 | v.is64 = true; |
| 126 | if (v.opt_hdr_off + sizeof(IMAGE_OPTIONAL_HEADER64) > v.bytes.size()) throw Error("target PE: PE32+ optional header out of range"); |
| 127 | auto* o = reinterpret_cast<IMAGE_OPTIONAL_HEADER64*>(v.bytes.data() + v.opt_hdr_off); |
| 128 | |
| 129 | v.file_alignment = o->FileAlignment; |
| 130 | v.section_alignment = o->SectionAlignment; |
| 131 | v.size_of_headers = o->SizeOfHeaders; |
| 132 | v.size_of_image = o->SizeOfImage; |
| 133 | v.dll_characteristics = o->DllCharacteristics; |
| 134 | v.image_base = o->ImageBase; |
| 135 | v.old_reloc_rva = o->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; |
| 136 | v.old_reloc_size = o->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; |
| 137 | } |
| 138 | else if (opt_magic == kOpt32) { |
| 139 | if (v.opt_hdr_off + sizeof(IMAGE_OPTIONAL_HEADER32) > v.bytes.size()) throw Error("target PE: PE32 optional header out of range"); |
| 140 | auto* o = reinterpret_cast<IMAGE_OPTIONAL_HEADER32*>( v.bytes.data() + v.opt_hdr_off); |
| 141 | |
| 142 | v.file_alignment = o->FileAlignment; |
| 143 | v.section_alignment = o->SectionAlignment; |
| 144 | v.size_of_headers = o->SizeOfHeaders; |
| 145 | v.size_of_image = o->SizeOfImage; |
| 146 | v.dll_characteristics = o->DllCharacteristics; |
| 147 | v.image_base = o->ImageBase; |
| 148 | v.old_reloc_rva = o->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; |
| 149 | v.old_reloc_size = o->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; |
| 150 | } |
| 151 | else { |
| 152 | throw Error(fmt::format("target PE: unknown OptionalHeader magic 0x{:04x}", opt_magic)); |
| 153 | } |
| 154 | |
| 155 | v.section_tbl_off = v.opt_hdr_off + fh->SizeOfOptionalHeader; |
| 156 | if (v.section_tbl_off + v.num_sections * sizeof(IMAGE_SECTION_HEADER) > v.bytes.size()) throw Error("target PE: section table out of range"); |
| 157 | if (v.file_alignment == 0 || v.section_alignment == 0) throw Error("target PE: zero alignment values"); |
| 158 | |
| 159 | return v; |
| 160 | } |
no test coverage detected