Hacking

Adding a section to PE binary

Krist Rash
January 20, 2017 by
Krist Rash

Let's take a look at expanding PE formatted binaries by hand. I was working on a project back in 2004 when we were required to add some interoperability for a program. Some of the problems that we ran into were that we needed space to make the modifications we needed and it was decided that we would make the inline changes via assembly. The method we used then is now called code caving. The idea is to create a space where you add your code, and when the program runs, it jumps to your section of code and executes then returns to the original section of code. The application for this is essentially limited only by your imagination as you could easily just create a space, add malicious code or any other modification, then jump to it execute to it and jump back.

There is much information to cover, so please feel free to re-read this a bit and don't feel bad If this doesn't immediately make sense. This is a very deep topic!

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

The executable used in the example will be on that I have pulled from The famous R4ndom on the backup of his site; As always don't do this in a production environment. Use a VM! I will be using WxHexEditor for making changes. You may use whatever you like.

URL for the binary provided by R4ndom:

http://octopuslabs.io/legend/files/tuts/R4ndom_tutorial_6.zip

So the process for doing this is quite simple.

  1. Find the IMAGEBASE; Many things will hinge off of this.
  2. Round off the VIRTUAL_SIZE of the last section header to the SECTION_ALIGNMENT found in the (IMAGEBASE+0x128)
  3. Add the stub in the PE Header
  4. Change the number of the stubs Usually at (IMAGEBASE+ 0xF6)
  5. Change the Image Size definition in the PE Header usually at (IMAGEBASE+0x140)
  6. Add the section at the new RAW Offset.

Here is what an example stub looks like, we reverse binary because of little endian.

I think dividing this like so, makes this easier to read and can provide understanding. We will be adding this in later.

2E 74 65 73 74 00 00 00 .test (ASCII)

00001000 Virtual Size

0000C000 Virtual Address

00001000 Size of Raw data

0000B000 Pointer to Raw data

00000000 Pointer to Relocations

00000000 Number of Relocations

0000 Pointer to Line Numbers

0000 Number of Line numbers

E0000060 Characteristics
  • Find the image base:

Somethings to keep in mind is that the Virtual sizes should be equal or greater to the raw size. This has to do with data that is initialized in memory at load time will need to be stored virtually.

Items that we will want to have: IMAGEBASE this will usually be 00400000; The PE header will have this defined at 0x124 bytes into the header. NumberOfSections usually located at 0xF6 of the IMAGEBASE Virtually. Otherwise, it will be 0xF6. Know the items Section header.

  • Round off the last section.

In this case, the last section is. RSRC If we look at the IMAGEBASE+128 to find the SECTION_ALIGNMENT Is the relocation of all the sections. For this scenario, it's important to know because it shows kind of what we can get away with in terms adding sections and what sections should be aligned to.

A simple way to do this is to open OllyDBG and open your binary and click the "M" for memory map and click the section of the binary that has the PE header. Once you double click in there, you'll be redirected to the PE HEADER signified with the "MZ" in hex.

As I said above the SECTION_ALIGNMENT is IMAGEBASE+128. Usually, and Usually, it is 0x1000 in size. The size value lets us know what we can what we can comfortably add regarding size as a section.

Since this is 0x1000, we can comfortably add 0x1000(4096) bytes to this. We can, of course, add more, but it becomes slightly more complicated when you have some bytes being padded and the image being rounded out. Also, this will mix up some analysis of the sections made by tools. Not to mention 0x1000(4096 bytes) isn't too much, but it's enough for getting a lot done.

LAST SECTION DEFINED:

.RSRC Dump - original:00400000..00400FFF

Name[8] = ".rare" 00400268 |. 20020000 DD 00000220;

VirtualSize = 544. 0040026C |. 00B00000 DD 0000B000;

VirtualAddress = 0B000 00400270 |. 00100000 DD 00001000;

SizeOfRawData = 4096. 00400274 |. 00A00000 DD 0000A000;

PointerToRawData = 0A000 00400278 |. 00000000 DD 00000000;

This is a visual hex representation of above last section defined.

Just change the highlighted part to 0010 for 0x1000

Now we need to go to the top and add to the NumberOfSections(0xF6) In this binary they're 4 sections. We will need to change this to 5 because we are adding a section. If we were adding two sections, we would make this a 6 and so forth. Let's jot down the last section header just because we'll need to take these numbers and add to them for the next section.

Take a look below...

  • Add the stub in the PE Header:

So this can feel a bit tricky to do in a hex editor because hex is not easily readable by humans. If you really want to know, the Stud Headers are 0x28 in size so; it's not too hard. Here is an image of a header (From Stud_PE).

Things to calculate for if you want to do this on your own.

  • Virtual size should be greater or equal to the raw size; This is space that gets allocated.
  • Virtual offset will be increased by the size of the virtual size
  • Raw Size, this is how much hard writable space you have.
  • The Raw Offset will increase based on the size of the raw offset.

Let's define the items that are needed in the section. These items are defined specified by Microsoft. If you want to read the specification, I will add a link at the end of this for you to read if you want.

EXAMPLE:

2E 74 65 73 74 00 00 Test; Name of Section (ASCII)

00001000 Virtual Size

0000C000 Virtual Offset

00001000 Raw Size

0000B000 Raw Offset

00 00 00 00 Pointer to Relocations

00 00 00 00 Pointer to Line Numbers

00 00 Number of Relocations

00 00 Number of Line numbers

E0000060 Characteristics

Here is a picture of how it should look:

  • Increase the number of sections defined:

This is a simple single byte change. Just go to IMAGEBASE+F6 or simple in the Hexeditor to Offset F6 and add to it.

In this case, just change it from 4 to 5, because we will then have 5 sections in our binary.

  • Change the IMAGE_SIZE:

Change the image size to reflect the added size. This is usually located at (IMAGEBASE+0x140). If you don't, the binary will be larger than the PE Header says it is and will fail to execute because it won't be a properly formatted PE file.

In this case, we are adding 0x1000(4096) bytes, change this from a C to D.

  • Add the bytes to the raw section:

Since we have specified that we are adding because of the image0x1000(4096) bytes thus far, we will add just that. This is simple using a Hex Editor (Link Below) WxHexEditer.

Just right click and click insert and insert 4096(0x1000) bytes.

 

Moreover, that is absolutely it!

Now, I understand this is very hard to do by hand, and since there are 5 steps to accomplish, this leaves at least 5 large places for mistakes. You can use a tool called Stud_Pe to add the section easily.

This isn't required for the tutorial, but If you want to see a binary diff of everything that we changed.

Become a Certified Ethical Hacker, guaranteed!

Become a Certified Ethical Hacker, guaranteed!

Get training from anywhere to earn your Certified Ethical Hacker (CEH) Certification — backed with an Exam Pass Guarantee.

Sources

Krist Rash
Krist Rash

Krist Rash is a security analyst and reverse engineer that is focused on malware analysis, application security and compliance. He loves writing in Python, C and Assembly and enjoys working with various types of malware including, Firmkits, Bootkits, and Rootkits. He is always trying to stay a head of the curve of the newest techniques , threats and exploits.