skipped 12 lines 13 13 * See the License for the specific language governing permissions and 14 14 * limitations under the License. 15 15 */ 16 + #include <map> 16 17 namespace LIEF { 17 18 namespace PE { 18 19 19 - template<typename PE_T> 20 - std::vector<uint8_t> Builder::build_jmp(uint64_t from, uint64_t address) { 21 - std::vector<uint8_t> instruction; 20 + /********************************* 22 21 23 - // call $+5 24 - instruction.push_back(0xe8); 25 - instruction.push_back(0x00); 26 - instruction.push_back(0x00); 27 - instruction.push_back(0x00); 28 - instruction.push_back(0x00); 22 + PE Import 29 23 30 - // pop eax/pop rax 31 - instruction.push_back(0x58); // eax/rax holds the current PC 24 + +-----------------------+ 25 + | ILT RVA | --+ 26 + +-----------------------+ | 27 + | Name RVA | | 28 + +-----------------------+ | 29 + ---| IAT RVA | | 30 + | +-----------------------+ | 31 + | | | | 32 + | | | | 33 + | | | | 34 + | +-----------------------+ | 35 + | | 000000000000000000000 | | 36 + | +-----------------------+ | 37 + | | 38 + | +---- Ordinal Flag | 39 + | v | 40 + | +-+---------------------+ <-+-------- ILT 41 + | | | Hint/Name RVA |---+ 42 + | +-+---------------------+ | 43 + | | | .... | | 44 + | +-+---------------------+ | 45 + | | 46 + | IAT | 47 + +->+-+---------------------+ | 48 + | | Same Value as ILT[0]| | 49 + +-+---------------------+ | 50 + | | | | 51 + +-+---------------------+ | 52 + | 53 + | 54 + +---+---------------+---+ | 55 + | 9 | LoadLibrary | 0 | <-+ 56 + +---+---------------+---+ 57 + ^ ^ ^ 58 + | | | 59 + Hint Name Padding 32 60 33 - // add rax/eax (signed) 34 - if (std::is_same<PE_T, PE64>::value) { 35 - instruction.push_back(0x48); //x64 36 - } 37 - instruction.push_back(0x05); 61 + *********************************/ 38 62 39 - uint64_t diff = address - (from + 5); 40 63 41 - for (size_t i = 0; i < sizeof(uint32_t); ++i) { 42 - instruction.push_back(static_cast<uint8_t>((diff >> (8 * i)) & 0xFF)); 43 - } 44 - // jmp [rax/eax] 45 - instruction.push_back(0xff); 46 - instruction.push_back(0x20); 47 - 48 - return instruction; 49 - } 50 64 51 65 52 66 template<typename PE_T> 53 - std::vector<uint8_t> Builder::build_jmp_hook(uint64_t from, uint64_t address) { 54 - std::vector<uint8_t> instruction; 55 - instruction.push_back(0xe9); // jmp xxxx 56 - uint64_t disp = address - from - 5; 57 - 58 - for (size_t i = 0; i < sizeof(uint32_t); ++i) { 59 - instruction.push_back(static_cast<uint8_t>((disp >> (8 * i)) & 0xFF)); 60 - } 61 - 62 - return instruction; 63 - } 64 - 65 - 66 - /* 67 - Original IAT New IAT 68 - +------------------+ +------------------+ 69 - |Trampoline 1 addr |------+ | new address 1 |-+ 70 - +------------------+ | +------------------+ | 71 - |Trampoline 2 addr | | | new address 1 | | 72 - +------------------+ | +------------------+ | 73 - |Trampoline 3 addr | | | new address 1 | | 74 - +------------------+ | +------------------+ | 75 - | | 76 - | Trampoline 1 +--+ 77 - | +-----------------v-----+ Kernel32.dll 78 - +----->| mov rax, [new addr1] | +--------------+ 79 - | jmp rax |---------->| GetLocalTime | 80 - +-----------------------+ +--------------+ 81 - +--->| LocalSize | 82 - Trampoline 2 | +--------------+ 83 - +-----------------------+ | | WriteFile | 84 - | mov rax, [new addr2] | | +--------------+ 85 - | jmp rax |------+ 86 - +-----------------------+ 87 - 88 - */ 89 - 90 - template<typename PE_T> 91 67 void Builder::build_import_table(void) { 92 - using uint__ = typename PE_T::uint; 68 + using uint__ = typename PE_T::uint; 69 + using patches_t = std::map<uint__, uint__>; 93 70 94 - // Compute size of the the diffrent (sub)sections 95 - // inside the future import section 96 - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 + /************************************** 97 72 98 - // Size of pe_import + 1 for the null entry 99 - uint32_t import_table_size = static_cast<uint32_t>((this->binary_->imports().size() + 1) * sizeof(pe_import)); // +1 for the null entry 73 + +----------------------------------+ 74 + | pe_import[0] | 75 + +----------------------------------+ 76 + | pe_import[1] | 77 + +----------------------------------+ 78 + | | 79 + | Import Lookup Tables | 80 + | | 81 + +----------------------------------+ 82 + | Library Names | 83 + +----------------------------------+ 84 + | | 85 + | Hint/Names table | 86 + | | 87 + +----------------------------------+ 88 + | | 89 + | | 90 + | New IAT (For new imports) | 91 + | | 92 + | | 93 + +----------------------------------+ 100 94 101 - // Size of import entries 102 - uint32_t entries_size = 0; 95 + **************************************/ 103 96 104 - // Size of the section which will holds imported functions names 105 - uint32_t functions_name_size = 0; 97 + it_imports imports = this->binary_->imports(); 98 + patches_t iat_patches; 106 99 107 - // Size of the section which will holds library name (e.g. kernel32.dll) 108 - uint32_t libraries_name_size = 0; 100 + uint32_t import_table_size = static_cast<uint32_t>((imports.size() + 1) * sizeof(pe_import)); // +1 for the null entry 101 + uint32_t ilt_size = 0; 102 + uint32_t library_names_size = 0; 103 + uint32_t hint_name_sizes = 0; 104 + uint32_t new_iat_size = 0; 105 + for (const Import& import : imports) { 109 106 110 - // Size of the trampoline section 111 - uint32_t trampolines_size = 0; 107 + library_names_size += import.name().size() + 1; 108 + ilt_size += (import.entries().size() + 1) * sizeof(uint__); 112 109 113 - // Size of the instructions in the trampoline 114 - uint32_t trampoline_size = build_jmp<PE_T>(0, 0).size(); 110 + // Added by the user 111 + if (import.import_address_table_rva() == 0) { 112 + new_iat_size += (import.entries().size() + 1) * sizeof(uint__); 113 + } 115 114 116 - // Compute size of each imports's sections 117 - for (const Import& import : this->binary_->imports()) { 118 115 for (const ImportEntry& entry : import.entries()) { 116 + if (not entry.is_ordinal()) { 117 + hint_name_sizes += sizeof(uint16_t) + entry.name().size() + 1; 118 + hint_name_sizes += hint_name_sizes % 2; 119 + } 119 120 120 - functions_name_size += 2 + entry.name().size() + 1; // [Hint] [Name\0] 121 - functions_name_size += functions_name_size % 2; // [padding] 122 121 } 123 - 124 - libraries_name_size += import.name().size() + 1; // [Name\0] 125 - entries_size += 2 * (import.entries().size() + 1) * sizeof(uint__); // Once for Lookup table and the other for Import Address Table (IAT). +1 for the null entry 126 - trampolines_size += import.entries().size() * trampoline_size; 127 122 } 128 - 129 - // Offset of the diffrents sections inside *import section* 130 - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 131 - 132 - // Offset to the import table (i.e list of pe_import) 133 - uint32_t import_table_offset = 0; 123 + uint32_t import_table_offset = 0; 124 + uint32_t ilt_offset = import_table_offset + import_table_size; 125 + uint32_t library_names_offset = ilt_offset + ilt_size; 126 + uint32_t hint_names_offset = library_names_offset + library_names_size; 127 + uint32_t new_iat_offset = align(hint_names_offset + hint_name_sizes, 4); 128 + uint32_t end_off = new_iat_offset + new_iat_size; 134 129 135 - // Offset to the lookup table: After import table 136 - uint32_t lookuptable_offset = import_table_offset + import_table_size; 137 130 138 - // Address table (IAT). Identical to the lookup table until the library is bound 139 - uint32_t iat_offset = lookuptable_offset + entries_size / 2; 140 - 141 - // Offset to the section which will contains hints/names of the imported functions name 142 - uint32_t functions_name_offset = iat_offset + entries_size / 2; 143 - 144 - // Offset of the section which will holds libraries name 145 - uint32_t libraries_name_offset = functions_name_offset + functions_name_size; 146 - 147 - // Offset of the section where trampolines will be written 148 - uint32_t trampolines_offset = libraries_name_offset + libraries_name_size; 149 - 150 - // Create empty content of the required size and align it 151 - std::vector<uint8_t> content(trampolines_offset + trampolines_size, 0); 152 - size_t content_size_aligned = align(content.size(), this->binary_->optional_header().file_alignment()); 153 - content.insert(std::end(content), content_size_aligned - content.size(), 0); 131 + std::vector<uint8_t> new_imports(end_off, 0); 132 + size_t content_size_aligned = align(new_imports.size(), this->binary_->optional_header().file_alignment()); 133 + new_imports.insert(std::end(new_imports), content_size_aligned - new_imports.size(), 0); 154 134 155 135 // Create a new section to handle imports 156 136 Section new_import_section{".l" + std::to_string(static_cast<uint32_t>(DATA_DIRECTORY::IMPORT_TABLE))}; 157 - new_import_section.content(content ); 137 + new_import_section.content(new_imports ); 158 138 159 - new_import_section.add_characteristic(SECTION_CHARACTERISTICS::IMAGE_SCN_CNT_CODE); 139 + / / new_import_section.add_characteristic(SECTION_CHARACTERISTICS::IMAGE_SCN_CNT_CODE); 160 140 161 141 auto&& it_import_section = std::find_if( 162 142 std::begin(this->binary_->sections_), skipped 7 lines 170 150 (*it_import_section)->remove_type(PE_SECTION_TYPES::IMPORT); 171 151 } 172 152 173 - // As add_section will change DATA_DIRECTORY::IMPORT_TABLE we have to save it before 174 - uint32_t offset_imports = this->binary_->rva_to_offset(this->binary_->data_directory(DATA_DIRECTORY::IMPORT_TABLE).RVA()); 175 153 Section& import_section = this->binary_->add_section(new_import_section, PE_SECTION_TYPES::IMPORT); 176 154 177 155 skipped 30 lines 208 186 } 209 187 210 188 // Process libraries 211 - for (const Import& import : this - > binary_ - > imports( ) ) { 189 + for (const Import& import : imports) { 190 + uint32_t iat_rva = import.import_address_table_rva(); 191 + 192 + // If IAT is 0 it means that it's a user import 193 + if (import.import_address_table_rva() == 0) { 194 + iat_rva = import_section.virtual_address() + new_iat_offset; 195 + } 212 196 // Header 213 197 pe_import header; 214 - header.ImportLookupTableRVA = static_cast<uint__ >(import_section.virtual_address() + lookuptable_offset ); 198 + header.ImportLookupTableRVA = static_cast<uint32_t >(import_section.virtual_address() + ilt_offset ); 215 199 header.TimeDateStamp = static_cast<uint32_t>(import.timedatestamp()); 216 200 header.ForwarderChain = static_cast<uint32_t>(import.forwarder_chain()); 217 - header.NameRVA = static_cast<uint__ >(import_section.virtual_address() + libraries_name_offset ); 218 - header.ImportAddressTableRVA = static_cast<uint__ >(import_section . virtual_address ( ) + iat_offset ); 201 + header.NameRVA = static_cast<uint32_t >(import_section.virtual_address() + library_names_offset ); 202 + header.ImportAddressTableRVA = static_cast<uint32_t >(iat_rva ); 219 203 220 204 // Copy the header in the "header section" 221 205 std::copy( 222 206 reinterpret_cast<uint8_t*>(&header), 223 207 reinterpret_cast<uint8_t*>(&header) + sizeof(pe_import), 224 - content .data() + import_table_offset); 208 + new_imports .data() + import_table_offset); 225 209 226 210 import_table_offset += sizeof(pe_import); 227 211 228 212 // Copy the name in the "string section" 229 213 const std::string& import_name = import.name(); 214 + std::cout << import_name << std::endl; 230 215 std::copy( 231 216 std::begin(import_name), 232 217 std::end(import_name), 233 - content.data() + libraries_name_offset); 218 + new_imports.data() + library_names_offset); 234 219 235 - libraries_name_offset += import_name.size() + 1; // +1 for '\0' 220 + library_names_offset += import_name.size() + 1; // +1 for '\0' 221 + uint__ ilt_value = 0; 236 222 237 223 // Process imported functions 238 224 for (const ImportEntry& entry : import.entries()) { 239 - 240 - // If patch is enabled, we have to create a trampoline for this function 241 - if (this->patch_imports_) { 242 - std::vector<uint8_t> instructions; 243 - uint64_t address = this->binary_->optional_header().imagebase() + import_section.virtual_address() + iat_offset; 244 - if (this->binary_->hooks_.count(import_name) > 0 and this->binary_->hooks_[import_name].count(entry.name())) { 245 - address = this->binary_->hooks_[import_name][entry.name()]; 246 - instructions = Builder::build_jmp_hook<PE_T>(this->binary_->optional_header().imagebase() + import_section.virtual_address() + trampolines_offset, address); 247 - } else { 248 - instructions = Builder::build_jmp<PE_T>(this->binary_->optional_header().imagebase() + import_section.virtual_address() + trampolines_offset, address); 249 - } 250 - std::copy( 251 - std::begin(instructions), 252 - std::end(instructions), 253 - content.data() + trampolines_offset); 254 - 255 - trampolines_offset += trampoline_size; 256 - } 257 - 258 225 // Default: ordinal case 259 - uint__ lookup_table_value = entry.data(); 226 + ilt_value = entry.data(); 260 227 261 228 if (not entry.is_ordinal()) { 262 - 263 - lookup_table_value = import_section.virtual_address() + functions_name_offset; 229 + ilt_value = import_section.virtual_address() + hint_names_offset; 264 230 265 231 // Insert entry in hint/name table 266 232 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 267 233 268 234 // First: hint 269 235 const uint16_t hint = entry.hint(); 236 + 270 237 std::copy( 271 238 reinterpret_cast<const uint8_t*>(&hint), 272 239 reinterpret_cast<const uint8_t*>(&hint) + sizeof(uint16_t), 273 - content.data() + functions_name_offset); //hintIdx 240 + new_imports.data() + hint_names_offset); //hintIdx 274 241 275 - functions_name_offset += sizeof(uint16_t); 242 + hint_names_offset += sizeof(uint16_t); 276 243 277 244 // Then: name 278 245 const std::string& name = entry.name(); 246 + std::cout << name << std::endl; 279 247 std::copy( 280 248 std::begin(name), 281 249 std::end(name), 282 - content.data() + functions_name_offset); 250 + new_imports.data() + hint_names_offset); 251 + 252 + hint_names_offset += name.size() + 1; // +1 for \0 253 + hint_names_offset += hint_names_offset % 2; //Require to be even 254 + } // is_ordinal 283 255 284 - functions_name_offset += name.size() + 1; // +1 for \0 256 + // Write ILT Value 257 + std::copy( 258 + reinterpret_cast<const uint8_t*>(&ilt_value), 259 + reinterpret_cast<const uint8_t*>(&ilt_value) + sizeof(uint__), 260 + new_imports.data() + ilt_offset); 285 261 286 - functions_name_offset += functions_name_offset % 2; //Require to be even 262 + // Patch IAT value 263 + if (import.import_address_table_rva() == 0) { 264 + uint32_t off = iat_rva - import_section.virtual_address(); 265 + 266 + std::copy( 267 + reinterpret_cast<const uint8_t*>(&ilt_value), 268 + reinterpret_cast<const uint8_t*>(&ilt_value) + sizeof(uint__), 269 + new_imports.data() + off); 270 + new_iat_offset += sizeof(uint__); 271 + } else { 272 + 273 + iat_patches.emplace(iat_rva, ilt_value); 287 274 } 288 275 289 - uint__ iat_value = 0; 276 + ilt_offset += sizeof(uint__); 277 + iat_rva += sizeof(uint__); 278 + } // </end> ImportEntry iterator 279 + 280 + // Null value at the end 281 + ilt_value = 0; 290 282 291 - // Check if manually set 292 - if (entry.data() != entry.iat_value() and entry.iat_value() > 0) { 293 - iat_value = entry.iat_value(); 294 - } else { // default value same that in the lookup table 295 - iat_value = lookup_table_value; 296 - } 283 + std::copy( 284 + reinterpret_cast<const uint8_t*>(&ilt_value), 285 + reinterpret_cast<const uint8_t*>(&ilt_value) + sizeof(uint__), 286 + new_imports.data() + ilt_offset); 287 + 288 + // Patch IAT value 289 + if (import.import_address_table_rva() == 0) { 290 + uint32_t off = iat_rva - import_section.virtual_address(); 297 291 298 - // Insert entry in lookup table and address table 299 292 std::copy( 300 - reinterpret_cast<const uint8_t*>(&lookup_table_value ), 301 - reinterpret_cast<const uint8_t*>(&lookup_table_value ) + sizeof(uint__), 302 - content.data() + lookuptable_offset); 293 + reinterpret_cast<const uint8_t*>(&ilt_value ), 294 + reinterpret_cast<const uint8_t*>(&ilt_value ) + sizeof(uint__), 295 + new_imports.data() + off); 296 + new_iat_offset += sizeof(uint__); 297 + } else { 298 + iat_patches.emplace(iat_rva, ilt_value); 299 + } 300 + ilt_offset += sizeof(uint__); 301 + iat_rva += sizeof(uint__); 303 302 304 - std::copy( 305 - reinterpret_cast<const uint8_t*>(&iat_value), 306 - reinterpret_cast<const uint8_t*>(&iat_value) + sizeof(uint__), 307 - content.data() + iat_offset); 303 + } // </end> Import Iterator 308 304 309 - lookuptable_offset += sizeof(uint__); 310 - iat_offset += sizeof(uint__); 305 + // Insert null entry at the end 306 + std::fill( 307 + new_imports.data() + import_table_offset, 308 + new_imports.data() + import_table_offset + sizeof(pe_import), 309 + 0); 311 310 312 - } 311 + import_table_offset += sizeof(pe_import); 312 + import_section.content(std::move(new_imports)); 313 313 314 - // Insert null entry at the end 315 - std::fill( 316 - content.data() + lookuptable_offset, 317 - content.data() + lookuptable_offset + sizeof(uint__), 318 - 0); 314 + for (auto&& p : iat_patches) { 315 + this->binary_->patch_address(p.first, p.second, sizeof(uint__), LIEF::Binary::VA_TYPES::RVA); 316 + } 319 317 320 - std::fill( 321 - content.data() + iat_offset, 322 - content.data() + iat_offset + sizeof(uint__), 323 - 0); 318 + } 324 319 325 - lookuptable_offset += sizeof(uint__); 326 - iat_offset += sizeof(uint__); 320 + template<typename PE_T> 321 + std::vector<uint8_t> Builder::build_jmp(uint64_t from, uint64_t address) { 322 + std::vector<uint8_t> instruction; 327 323 324 + // call $+5 325 + instruction.push_back(0xe8); 326 + instruction.push_back(0x00); 327 + instruction.push_back(0x00); 328 + instruction.push_back(0x00); 329 + instruction.push_back(0x00); 330 + 331 + // pop eax/pop rax 332 + instruction.push_back(0x58); // eax/rax holds the current PC 333 + 334 + // add rax/eax (signed) 335 + if (std::is_same<PE_T, PE64>::value) { 336 + instruction.push_back(0x48); //x64 328 337 } 338 + instruction.push_back(0x05); 329 339 330 - // Insert null entry at the end 331 - std::fill( 332 - content.data() + import_table_offset, 333 - content.data() + import_table_offset + sizeof(pe_import), 334 - 0); 340 + uint64_t diff = address - (from + 5); 335 341 336 - import_table_offset += sizeof(pe_import); 342 + for (size_t i = 0; i < sizeof(uint32_t); ++i) { 343 + instruction.push_back(static_cast<uint8_t>((diff >> (8 * i)) & 0xFF)); 344 + } 345 + // jmp [rax/eax] 346 + instruction.push_back(0xff); 347 + instruction.push_back(0x20); 348 + 349 + return instruction; 350 + } 351 + 352 + 353 + template<typename PE_T> 354 + std::vector<uint8_t> Builder::build_jmp_hook(uint64_t from, uint64_t address) { 355 + std::vector<uint8_t> instruction; 356 + instruction.push_back(0xe9); // jmp xxxx 357 + uint64_t disp = address - from - 5; 337 358 338 - // Fill the section 339 - import_section.content(content); 359 + for (size_t i = 0; i < sizeof(uint32_t); ++i) { 360 + instruction.push_back(static_cast<uint8_t>((disp >> (8 * i)) & 0xFF)); 361 + } 340 362 341 - // Update IAT data directory 342 - const uint32_t rva = static_cast<uint32_t>(import_section.virtual_address() + iat_offset); 343 - this->binary_->data_directory(DATA_DIRECTORY::IAT).RVA(rva); 344 - this->binary_->data_directory(DATA_DIRECTORY::IAT).size(functions_name_offset - iat_offset + 1); 363 + return instruction; 345 364 } 365 + 366 + 346 367 347 368 template<typename PE_T> 348 369 void Builder::build_optional_header(const OptionalHeader& optional_header) { skipped 212 lines