I am working with PE binaries using Python and the pefile library. Its good for reading information out of a binary and rewriting certain bytes. If I wanted to add a section to the file then i am clueless on how to do it. I would be thankful if someone can provide me any previous works in this topic or help regarding this.
3 Answers
This isn't a language specific concept. The values you'll need to modify as they are (or are meant to be) referenced:
!IMAGE_FILE_HEADER: - NumberOfSections
!IMAGE_OPTIONAL_HEADER: - SizeOfImage - SizeOfHeaders (optional depending on which sections you're modifying. Relevant for .text, .data, and .idata but afaik are unused) - SizeOfCode, SizeOfInitializedData, or SizeOfUninitializedData - BaseOfCode, BaseOfData
Then you'll have to construct a new IMAGE_SECTION_HEADER, some values are dependant on the image. - VirtualAddress and VirtualSize must be aligned to !IMAGE_OPTIONAL_HEADER.SectionAlignment - PointerToRawData and SizeOfRawData must be aligned to !IMAGE_OPTIONAL_HEADER.FileAlignment - PointerToRelocations is a little tricky. If there is not enough space at the end of !IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC], which can be calculated by finding the size of the section it is contained in, subtracting the ::.Size and (::.VirtualAddress - Reloc Section Virtual Address), you must relocate the table. After that, you'll just need to adjust the size and append.
Example
Appending an data section: - Check if enough room in headers for another section - Add
(IMAGE_SECTION_HEADER){
.VirtualSize = align_up(%SIZE%, !IMAGE_OPTIONAL_HEADER.SectionAlignment,
.SizeOfRawData = align_up(%SIZE%, !IMAGE_OPTIONAL_HEADER.FileAlignment),
.VirtualAddress = find_max(!IMAGE_SECTION_HEADER[].VirtualAddress + !IMAGE_SECTION_HEADER[].VirtualSize),
.PointerToRawData = find_max(!IMAGE_SECTION_HEADER[].PointerToRawData + !IMAGE_SECTION_HEADER[].SizeOfRawData),
.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
}
Then resize the file, copy your data into the .PointeToRawData
Adding a new PE section is the main approach I take when I significantly modify a PE file. There are position and RVA and VA-specific dependencies in existing binaries. Changing those things is challenging, especially if the base relocation table is missing or corrupted for whatever reason.
Let's take a look at the general approach I take in Windows PE Tools for PHP (it's PHP but a similar implementation/approach for Python should be possible):
https://github.com/cubiclesoft/php-winpefile/blob/master/support/win_pe_file.php#L2222
The CreateNewPESection()
function first calls PrepareForNewPESection()
. PrepareForNewPESection()
checks to see if there is enough space in the header as-is for a new section. If there is enough space, then it just returns. Note that there can be all kinds of weird stuff in the header though such as Bound Imports, Debug Directory data, Relocations, etc. PrepareForNewPESection()
calculates the actual amount of available space by reducing the value if it encounters such data in the header space.
If there isn't enough space, PrepareForNewPESection()
will resize the headers and shift data around up to the PE Section Alignment by minimizing the DOS stub and wiping unneeded information from the headers to attempt to make space. A new section only needs 40 bytes. Resizing the headers is an optional and complex exercise just to gain a few bytes of storage.
Assuming there is enough space, CreateNewPESection()
then calculates the next available RVA by walking the sections and calculating the section's maximum RVA value. Note that the RVA size in a section can actually be smaller than the raw data size. The Windows loader in those cases appears to assume that the RVA size is equal to the section-aligned raw data size. I consider those to be broken PE files that just happen to work. The new section is then file aligned (usually a no-op) and the new data for the section (also file aligned) is placed at the end of the file.
After adding the PE section, don't forget to update various values: The number of sections in the PE header as well as the code size, initialized data size, uninitialized data size, and, most importantly, the image size in the PE optional header (and, of course, the PE checksum when all is said and done).
Instead of adding another section, appending to the last section may also be an option worth considering. First, look at the flags of the section. If the flags match what you are looking for and the last section is also the last RVA and located at the end of the physical file, then that section may be a candidate for reuse by just resizing the existing section instead of adding a new section. Doing this helps reduce the possibility of running out of space in the header for new sections.
See this: Adding/removing sections with Python http://www.reddit.com/r/ReverseEngineering/comments/1jpghd/addingremoving_sections_with_python/
-
I can tell you why I downvoted. The reason is that this amounts to a link-only answer. The link may go stale and the other link in the comment has gone stale, unless you edit it. At a minimum you should include the steps and tools in this answer. Not a mere link ...– 0xC0000022L ♦Nov 21, 2019 at 20:47