/*************************************************************************/ /* Copyright (c) 2015 dx, http://kaimi.ru */ /* */ /* Permission is hereby granted, free of charge, to any person */ /* obtaining a copy of this software and associated documentation */ /* files (the "Software"), to deal in the Software without */ /* restriction, including without limitation the rights to use, */ /* copy, modify, merge, publish, distribute, sublicense, and/or */ /* sell copies of the Software, and to permit persons to whom the */ /* Software is furnished to do so, subject to the following conditions: */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "pe_rebuilder.h" #include "pe_base.h" #include "pe_structures.h" #include "pe_exception.h" namespace pe_bliss { using namespace pe_win; //Rebuilds PE image headers //If strip_dos_header is true, DOS headers partially will be used for PE headers //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically //If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) { dos_header = pe.get_dos_header(); if(strip_dos_header) { //Strip stub overlay pe.strip_stub_overlay(); //BaseOfCode NT Headers field now overlaps //e_lfanew field, so we're acrually setting //e_lfanew with this call pe.set_base_of_code(8 * sizeof(uint16_t)); } else { //Set start of PE headers dos_header.e_lfanew = sizeof(image_dos_header) + pe_utils::align_up(static_cast(pe.get_stub_overlay().size()), sizeof(uint32_t)); } section_list& sections = pe.get_image_sections(); //Calculate pointer to section data size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + sections.size() * sizeof(image_section_header); if(save_bound_import && pe.has_bound_import()) { //It will be aligned to DWORD, because we're aligning to DWORD everything above it pe.set_directory_rva(image_directory_entry_bound_import, static_cast(ptr_to_section_data)); ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); } ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); //Set size of headers and size of optional header if(change_size_of_headers) { if(!pe.get_image_sections().empty()) { if(static_cast(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); } pe.set_size_of_headers(static_cast(ptr_to_section_data)); } //Set number of sections in PE header pe.update_number_of_sections(); pe.update_image_size(); pe.set_size_of_optional_header(static_cast(pe.get_sizeof_opt_headers() - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); //Recalculate pointer to raw data according to section list for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) { //Save section headers PointerToRawData (*it).set_pointer_to_raw_data(static_cast(ptr_to_section_data)); ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); } } //Rebuild PE image and write it to "out" ostream //If strip_dos_header is true, DOS headers partially will be used for PE headers //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically //If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) { if(out.bad()) throw pe_exception("Stream is bad", pe_exception::stream_is_bad); if(save_bound_import && pe.has_bound_import()) { if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) < pe.get_directory_size(image_directory_entry_bound_import)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); } //Change ostream state out.exceptions(std::ios::goodbit); out.clear(); uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) { //No need to do anything with bound import directory //if it is placed inside of any section, not headers original_bound_import_rva = 0; save_bound_import = false; } { image_dos_header dos_header; //Rebuild PE image headers rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); //Write DOS header out.write(reinterpret_cast(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); } //If we have stub overlay, write it too { const std::string& stub = pe.get_stub_overlay(); if(stub.size()) { out.write(stub.data(), stub.size()); size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); //Align PE header, which is right after rich overlay while(aligned_size > stub.size()) { out.put('\0'); --aligned_size; } } } //Write NT headers out.write(static_cast(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); //Write section headers const section_list& sections = pe.get_image_sections(); for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { if(it == sections.end() - 1) //If last section encountered { image_section_header header((*it).get_raw_header()); header.SizeOfRawData = static_cast((*it).get_raw_data().length()); //Set non-aligned actual data length for it out.write(reinterpret_cast(&header), sizeof(image_section_header)); } else { out.write(reinterpret_cast(&(*it).get_raw_header()), sizeof(image_section_header)); } } //Write bound import data if requested if(save_bound_import && pe.has_bound_import()) { out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), pe.get_directory_size(image_directory_entry_bound_import)); } //Write section data finally for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { const section& s = *it; std::streamoff wpos = out.tellp(); //Fill unused overlay data between sections with null bytes for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) out.put(0); //Write raw section data out.write(s.get_raw_data().data(), s.get_raw_data().length()); } } //Rebuild PE image and write it to "out" file //If strip_dos_header is true, DOS headers partially will be used for PE headers //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically //If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) { std::ofstream pe_file(out, std::ios::out | std::ios::binary | std::ios::trunc); if(!pe_file) { throw pe_exception("Error in open file.", pe_exception::stream_is_bad); } rebuild_pe(pe, pe_file, strip_dos_header, change_size_of_headers, save_bound_import); } }