day33.1 - new section, expanded section - add code

1, New section ideas

1. Meet the conditions of new section

  • Why add a new section to add code? Because sometimes the blank area of all sections may not be enough to store the code we want to add, we add enough sections to add code ourselves

  • First, judge whether there is enough space to add a section table (to add a new section, you need to add a section table to record the information of this section); Judgment method: SizeOfHeader - (DOS + garbage data + PE tag + standard PE header + Optional PE header + existing section table) > = the size of 2 section tables

  • Why two section table sizes are needed: after filling in the section table, the specified format should be maintained: 0 of the same byte as the width of the section table should be added after the last section table; Therefore, we must first judge whether we can save the size of two section tables, that is, 80 bytes. However, in fact, it is sometimes feasible not to add 0 or some other data after the last section table, but it is not easy to use because it does not meet the specified format requirements, so it is still in accordance with the standard

  • If the last section of the table is the byte corresponding to the last section of the table to be modified, then we can add all the bytes next to the last section of the table. If the last section of the table meets the condition of 40, then we can add all the bytes next to the last section of the table

  • Data to be modified:

    • Modify the NumberOfSections field in the standard PE header (number of sections)

    • Modify the size of SizeOfImage (the original value + the size of the new section, and finally align the memory)

    • At the end of the original data, add the data of the section to be added (when adding directly, it meets the integer multiple of memory alignment, and there is no need to worry about alignment later)

    • Modify the attributes of the new section table:

      • Name, which is randomly selected according to ASCII code, and the best length is limited to 8 bytes

      • Misc.VirtualSize. If we don't know the length of the added data at this time (before alignment), we can directly use the case after alignment. For example, if we want to add a new section size of 0x1000 (considering memory alignment), no matter how many bytes of data are used in it, the value of VirtualSize will be written as 0x1000

      • VirtualAddress is the starting offset address of memory. After the VirtualAddress + VirtualSize memory alignment of the previous section table, we get the end position of the memory alignment of the previous section, so the VirtualAddress of the new section table is this value.

        Never use SizeOfRawData to judge, because the VirtualSize of this section may be larger than SizeOfRawData, because it contains uninitialized data. At this time, it should be stored in memory according to the value obtained after virtual address + VirtualSize memory alignment

        • give an example:

          • We found that sometimes the size of the misaligned file in memory is indeed larger than that of the aligned file (because the section contains initialization data), such as Notepad The second section of the EXE file In data, the size in memory is 0x1BA8, but the aligned size in the file is 0x600. At this time, we can find that the third section is stored in the file according to the size of the previous section aligned in the file, that is, 0x7200 + 0x600 = 0x7800, so the pointtorawdata of the third section is 0x7800, The third section is stored in memory according to the aligned size of the previous section in memory (instead of being stored next to the aligned size in the file of the previous section, because the VirtualSize is larger than SizeOfRawData at this time), that is, 0x8000 + 0x1BA8 = 0x9BA8, which is 0xA000 after alignment, so the memory offset of the third section is 0xA000

      • SizeOfRawData: correct this value according to the file alignment granularity. For example, if the value of VirtualSize is set to 0x1000 above, it means that the 0x1000 bytes of this section are useful data, and this section should also have the 0x1000 bytes of data on the hard disk. Therefore, correct this value according to the file alignment granularity. If it is 0x200 or 0x1000, don't worry about 0x1000, because the conditions have been met

      • PointerToRawData is the file address after file alignment. We can calculate this value through PointerToRawData + SizeOfRawData in the previous section table

      • Characteristics can be modified according to the properties we want (readable, writable, executable, etc.)

  • If shown:

  • The value of SizeOfHeaders should not be changed casually, because if this value changes, the following sections will change later or forward, but the relevant values related to the calculation of addresses in these sections will change. For example, the values after E8 and E9 added yesterday are calculated through other addresses. If SizeOfHeaders changes and the section address changes, these values will also change, so the cost is very high, Modification is not recommended

2. If there is data added by the compiler after the section table

  • Some programs will add some information between the section table and the blank area of the section. Because the added new section table must be next to the original section table and cannot be interrupted, but we don't know whether the information of the program is useful and affects the normal operation of the program, so we can't easily modify and overwrite these contents at this time, If we want to add a new section table, we need to find a way: we know that there is an area DOS stub between the DOS header and the PE signature of the program, which does not affect the operation of the program, and the data is also some descriptive information of the program, which is garbage data for us, so we can move up the part from PE to the end of the section table as a whole, overwrite the data of DOS stub, and then modify the E in the DOS header_ If the value of lfanew field is the address signed by PE after moving up, then a part will be left below. We can modify all this part to 0x00, and then add a section table to this area

3. The overall space is not large enough after moving up

  • If the size of the newly added section table is not enough after overwriting DOS Stub up, we will expand the last section and add the added code to the expanded space of the last section, so as to ensure that all the addresses above will not be affected

4. Precautions

  • When the file is loaded into memory, the end of the last section aligned with VirtualAddress + VirtualSize must be the end position of a file in memory, that is, SizeOfImage; When the file is on the hard disk, the value of PointerToRawData + SizeOfRawData in the last section must be the end of the whole file!
  • Whether it is to add code to the blank area of a section, add a section or expand a section, if these operations are realized by programming, they should be better operated in ImageBuffer, that is, stretching later operations, which should be more convenient in the calculation of some values and the selection of positions
  • Loading into memory depends on how sections are arranged. It should be judged according to VirtualAddress, VirtualSize and memory alignment; On the hard disk, the arrangement of sections should be judged according to pointtorawdata, SizeOfRawData and file alignment

2, Add code manually

1. When all conditions are met

  • We use flying pigeon IPMsg Exe to add a new section, IPMsg Exe open with winhex

  • First judge whether there are enough 80 bytes between the end of the section table and the first section: the SizeOfHeaders value in the Optional PE header is 0x1000; In the standard PE header, the SizeOfOptionalHeader value is 0xE0, and the NumberOfSections value is 0x4; E in DOS header_ Lfanew value is 0xE0. Through 0x1000 - (0xE0 + 0x4 + 0x14 + 0xE0 + 0x4 * 0x28) = 0xD88, the new section table can be saved because 0xD88 > 0x50

  • We will Textcopy a copy of 40 bytes of the section table to the end of the section table address 0x278: selected text40 byte copy, then select 0x278 to the next 40 byte data, position the mouse to 0x278, and right click edit – clipboard data – write to copy Texta copy of the contents of the section table is added to the end of the section table as 40 bytes of the new section table

    In winhex, write is to overwrite the selected data; paste is insert

  • Then, change the 40 byte data behind the new section to 0. In order to meet the format requirements (there is no need to modify it if it is already)

  • Modify the NumberOfSections value in the PE standard header to 4 + 1 = 5

  • First, let's see that both file alignment and memory alignment are 0x1000, so the section size we added can be set to 0x1000 bytes for later modification, which is enough to store the code we want to add, and the alignment problem is also considered. (if the file alignment is different from the memory alignment, you can select the least common multiple of the two for later modification)

  • Then modify the SizeOfImage value in the Optional PE header: since the size of the new section we want to add is 0x1000 (considering memory alignment), we can just use SizeOfImage + 0x1000, 0x3D000 + 0x1000 = 0x3E000

  • Then modify the fields in the section table:

    • Name: change the name to tttt

    • Misc.VirtualSize: write 0x1000 directly

    • VirtualAddress: 0x30000 + 0xD000 = 0x3D000

    • SizeOfRawData: 0x1000

    • PointerToRawData: 0x2C000 + 0xD000 = 0x39000

    • The attribute value does not need to be changed because it meets the executable requirements

  • Now we will add a new section at the end of the file, select the last data of the file, right-click – edit – paste zero bytes – and enter the size of the new section 0x1000. Since only decimal numbers can be entered, we need to convert 0x1000 to 4096 and then enter!! (it's easy to make mistakes here)

  • After the new section is added, we will add the code to the new section. Here we use the shellcode of day32, that is, after running the flying pigeon, first pop a box and then run normally, but at this time, we also need to calculate the values after E8 and E9:

    • Value after E8: X = 0x77D36476 - (0x400000 + 0x3D000 + 0x8 + 0x5) = 0x778F9469

    • Value after E9: X = 0x400000 + 0x1D26F - (0x400000 + 0x3D000 + 0xD + 0x5) = 0xFFFE025D

    • So add the complete code (hard coding) to the new section:

  • Finally, change the OEP value to 0x3D000

  • After saving and running, it is found that the pop-up box is displayed first, and then the program is opened normally. It is successful

2. There is an overall upward movement of data at the end of the section table

  • We open Notepad copy_ test. Exe, find the end of the section table, and find that there are some data we don't know, and it is next to the section table, so we can't add a section table at the end of the section table at this time (the new section table must be connected with the previous section table and can't be disconnected)

  • At this time, let's take a look at the garbage data between the end of DOS header and PE signature. If all data from PE signature to the end of section table is next to the back of DOS header as a whole, then E_ If the value of lfanew field is changed to 0x40, there will be empty space between the section table and those data that do not know the meaning. 0xE8 - 0x40 = 0xA8 bytes of memory, which is enough to store 80 bytes, that is, the size of two section tables

  • Select all data from 0xE8 to 0x258, copy, write from 0x40, overwrite 0x258 - 0xE8 = 0x170 bytes of data, then change the data from the end of the section table to those unknown data to 0, and then modify E_ The value of lfanew is 0x40. At this time, 0xA8 bytes are empty between 0x1B0 and 0x258, and a new section can be added here

  • The process and conditions of the newly added section are exactly the same when they are met. It will not be demonstrated again

3. There is not enough space after moving up as a whole. Expand the last section

  • If the overall space after moving up and covering DOS Stub is not enough for the width of two sections of the table, the last section should be expanded. Do not expand the upper section, because if the last section is not expanded, the expansion of other sections will affect the offset of all the lower sections, and many data and addresses will have problems

  • SizeOfImage needs to be modified. For example, if I want to expand 500 bytes, it needs to meet the memory alignment, so SizeOfImage should be + 0x1000. Here, in order to facilitate subsequent modifications, it is directly expanded according to the memory alignment, so expand 0x1000 bytes

  • Next, modify the attributes of the last section table:

    • The values of VirtualAddress and VirtualAddress do not need to be changed
    • VirtualSize: SizeOfImage + Ex, and then align the memory
    • SizeOfRawData: because the file alignment is usually 0x200 or 0x1000, it can be directly equal to VirtualSize here
  • At this point, you can add code between the end of the last section and the end

3, Code implementation

1. Programming: add a new section and add code

The following is the simplest way to add a new section and add code to meet the conditions of adding a new section; Note: if we add or expand a section, it's better to operate in the ImageBuffer, and = = we must apply for a new memory NewImageBuffer to store the data of the newly added or expanded PE file = =, and we can't operate directly in the memory of the original ImageBuffer, because this memory is dynamically allocated and the size is the specified malloc(size), which can't be modified or added.

#include "stdafx.h"
#include "stdlib.h"
#define IMAGE_SIZEOF_SHORT_NAME 8
#define MessageBoxA_Address 0x77D36476

char shellCode[] = {  //Define the hard coded global variables to be added, write the determined ones, fill in the uncertain ones with 0x00 first, and then change them after calculation
    0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
    0xE8,0x00,0x00,0x00,0x00,
    0xE9,0x00,0x00,0x00,0x00
};

//*******************************************************************************
#pragma pack(1)
typedef struct DOS_HEADER{  //DOS header field
    short e_magic;
    short e_cblp;
    short e_cp;
    short e_crlc;
    short e_cparhdr;
    short e_minalloc;
    short e_maxalloc;
    short e_ss;
    short e_sp;
    short e_csum;
    short e_ip;
    short e_cs;
    short e_lfarlc;
    short e_ovno;
    short e_res[4];
    short e_oemid;
    short e_oeminfo;
    short e_res2[10];
    int e_lfanew;  
}Dos;
#pragma pack()

//Don't forget there's a PE signature in the middle

#pragma pack(1)
typedef struct FILE_HEADER{   //Standard PE header field
    short Machine;
    short NumberOfSections;
    int TimeDateStamp;
    int PointerToSymbolTable;
    int NumberOfSymbols;
    short SizeOfOptionalHeader;
    short Characteristics;
}File;
#pragma pack()
    
#pragma pack(1)
typedef struct OPTIONAL_HEADER{   //Optional PE header field
    short Magic;
    char MajorLinkerVersion;
    char MinorLinkerVersion;
    int SizeOfCode;
    int SizeOfInitializedData;
    int SizeOfUninitializedData;
    int AddressOfEntryPoint;
    int BaseOfCode;
    int BaseOfData;
    int ImageBase;
    int SectionAlignment;
    int FileAlignment;
    short MajorOperatingSystemVersion;
    short MinorOperatingSystemVersion;
    short MajorImageVersion;
    short MinorImageVersion;
    short MajorSubsystemVersion;
    short MinorSubsystemVersion;
    int Win32VersionValue;
    int SizeOfImage;
    int SizeOfHeaders;
    int CheckSum;
    short Subsystem;
    short DllCharacteristics;
    int SizeOfStackReserve;
    int SizeOfStackCommit;
    int SizeOfHeapReserve;
    int SizeOfHeapCommit;
    int LoaderFlags;
    int NumberOfRvaAndSizes;
    //There are several (16) structures, one of which is 8 bytes. We won't study it first, and we'll talk about it later
}Op;
#pragma pack()    
    
#pragma pack(1)
typedef struct _IMAGE_SECTION_HEADER {
    char Name[IMAGE_SIZEOF_SHORT_NAME];  //Macro definition usage
    union{
        int PhysicalAddress;
        int VirtualSize;
    }Misc;
    int VirtualAddress;
    int SizeOfRawData;
    int PointerToRawData;
    int PointerToRelocations;
    int PointerToLinenumbers;
    short NumberOfRelocations;
    short NumberOfLinenumbers;
    int Characteristics;
}Sec;
#pragma pack()
//*******************************************************************************

//New section
char* addSection(int size,char* imageBufferp){
    Dos* dosp = (Dos*)imageBufferp;
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);
    Op* opp = (Op*)((char*)filep + 20);
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);
    
    //First judge whether there is enough space, and add a section table (> = 80)
    int spareSpace = opp->SizeOfHeaders - (dosp->e_lfanew + 0x4 + 0x14 + filep->SizeOfOptionalHeader + filep->NumberOfSections * 0x28);
    if(spareSpace < 0x50){
        printf("Insufficient space to add section table");
        return NULL;
    }
    
	//Dynamically apply for a new space
	char* newImageBufferp = NULL;
	int wholeSize = (secp + filep->NumberOfSections - 1)->VirtualAddress + 
		(secp + filep->NumberOfSections - 1)->SizeOfRawData + ((size - 1) / opp->SectionAlignment + 1) * opp->SectionAlignment;
	newImageBufferp = (char*)malloc(wholeSize);
	if(NULL == newImageBufferp){
		printf("Dynamic memory allocation failed\n");
		return NULL;
	}
	for(int z = 0;z < size;z++){
		*(newImageBufferp + z) = 0x0;
	}
	
	for(int y = 0;y < (int)((secp + filep->NumberOfSections - 1)->VirtualAddress + (secp + filep->NumberOfSections - 1)->SizeOfRawData);y++){
		*(newImageBufferp + y) = *(imageBufferp + y);
	}
	dosp = (Dos*)newImageBufferp;
    filep = (File*)(newImageBufferp + dosp->e_lfanew + 4);
    opp = (Op*)((char*)filep + 20);
    secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);

    //Add a new section table (by copying). Note here that Notepad Exe program cannot be used because the section table is followed by something added by the compiler
    char* newSec = newImageBufferp + (dosp->e_lfanew + 0x4 + 0x14 + filep->SizeOfOptionalHeader + filep->NumberOfSections * 0x28);
    char* firstSec = newImageBufferp + (dosp->e_lfanew + 0x4 + 0x14 + filep->SizeOfOptionalHeader);
    for(int i = 0;i < 0x28;i++){
        *(newSec + i) = *(firstSec + i);
    }
    for(;i < 0x50;i++){
        *(newSec + i) = 0x0;
    }
    
    //Add a new section after the last section (after memory alignment)
    char* addSec = newImageBufferp + (int)((secp + filep->NumberOfSections - 1)->VirtualAddress + (secp + filep->NumberOfSections - 1)->SizeOfRawData);
    for(int j = 0;j < ((size - 1) / opp->SectionAlignment + 1) * opp->SectionAlignment;j++){
        *(addSec + j) = 0x0;
    }
    
	//Modify ofimage and ofsectionnumbers
    opp->SizeOfImage = wholeSize;
	filep->NumberOfSections++;

    //Fix new section table properties
    secp = (Sec*)newSec;
    char arr[] = ".tttt"; //Use our own default name
    for(int k = 0;k < 6;k++){
    	secp->Name[k] = arr[k]; 
    }
    secp->Misc.VirtualSize = ((size - 1) / opp->SectionAlignment + 1) * opp->SectionAlignment;
    secp->VirtualAddress = (int)(addSec - newImageBufferp);
   	secp->SizeOfRawData = ((size - 1) / opp->FileAlignment + 1) * opp->FileAlignment;
    secp->PointerToRawData = (secp - 1)->PointerToRawData + (secp - 1)->SizeOfRawData;
    
	printf("Section added successfully\n");
    return newImageBufferp; //Returns the first address of the new space
}


//Read in FileBuffer
char* readInFileBuffer(char* filePath){
    FILE* fp = NULL;
    char* p = NULL;
    fp = fopen(filePath,"rb");
    if(NULL == fp){
        printf("File open failed\n");
        fclose(fp);
        return NULL;
    }
    
    //Calculate file length
    fseek(fp,0,2);
    int len = ftell(fp);
    fseek(fp,0,0);
    
    //Dynamically request FileBuffer memory space
    p = (char*)malloc(len);
    if(NULL == p){
        printf("FileBuffer Memory space allocation failed\n");
        fclose(fp);
        return NULL;
    }
    
    //Write data
    int isSuccessed = fread(p,len,1,fp);
    if(!isSuccessed){
        printf("write in FileBuffer fail\n");
        fclose(fp);
        free(p);
        return NULL;
    }
    fclose(fp);
    return p;  //Return the first address of FileBuffer
}


//FileBuffer to ImageBuffer
char* fileBufferToImageBuffer(char* fileBufferp){
    Dos* dosp = (Dos*)fileBufferp; //Define DOS structure type pointer
    File* filep = (File*)(fileBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Dynamically request the memory space of ImageBuffer
    char* ip = NULL;
    ip = (char*)malloc(opp->SizeOfImage);
    if(NULL == ip){
        printf("Dynamic application ImageBuffer Memory failure\n");
        return NULL;
    }
    for(int i = 0;i < opp->SizeOfImage;i++){
        *(ip + i) = 0x00;
    }
    
    //Copy all headers
    for(int j = 0;j < opp->SizeOfHeaders;j++){
        *(ip + j) = *(fileBufferp + j);
    }
    
    //Copy all sections
    for(int k = 0;k < filep->NumberOfSections;k++){
        for(int x = 0;x < secp->SizeOfRawData;x++){
            *(ip + secp->VirtualAddress + x) = *(fileBufferp + secp->PointerToRawData + x);
        }
        secp++;
    }
    
    return ip; //Returns the starting address of the ImageBuffer
}


//ImageBuffer to newBuffer
char* imageBufferToNewBuffer(char* imageBufferp){
    Dos* dosp = (Dos*)imageBufferp; //Define DOS structure type pointer
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Calculate the size required for newBuffer (the problem not solved by day30 is solved here)
    //Use the file offset address of the last section + the aligned size of the last section
    Sec* temp = secp;
    secp = secp + filep->NumberOfSections - 1;
    int len = secp->PointerToRawData + secp->SizeOfRawData;
    secp = temp;
    
    //Dynamically allocate NewBuffer memory
    char* np = (char*)malloc(len);
    if(NULL == np){
        printf("NewBuffer memory allocation failed\n");
        return NULL;
    }
    for(int i = 0;i < len;i++){
        *(np + i) = 0x00;
    }
    
    //Copy all headers
    for(int j = 0;j < opp->SizeOfHeaders;j++){
        *(np + j) = *(imageBufferp + j);
    }
    
    //Copy all sections
    for(int k = 0;k < filep->NumberOfSections;k++){
        for(int x = 0;x < secp->SizeOfRawData;x++){
            *(np + secp->PointerToRawData + x) = *(imageBufferp + secp->VirtualAddress + x);
        }
        secp++;
    }
    
    return np; //Return the first address of NewBuffer
}


//save
int save(char* savePath,char* newBufferp){
    Dos* dosp = (Dos*)newBufferp; //Define DOS structure type pointer
    File* filep = (File*)(newBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    FILE* fp = fopen(savePath,"wb");
    if(NULL == fp){
        printf("File open failed\n");
        fclose(fp);
        return 0;
    }
    
    secp = secp + filep->NumberOfSections - 1;
    int len = secp->PointerToRawData + secp->SizeOfRawData;  //Get the size of newBuffer
    int isSuccessed = fwrite(newBufferp,len,1,fp);
    if(!isSuccessed){
        printf("Save failed\n");
        fclose(fp);
        return 0;
    }
    fclose(fp);
	printf("Save succeeded\n");
    return 1;
}


//Add code to new section
//We don't need to consider the case of virtualsize > sizeofrawdata here, because the properties of the new section are modified by us, so we're sure it won't happen
int addCodeToNewSec(char* code,int num,char* imageBufferp){ //Add the address directly in the ImageBuffer, which is more convenient for calculation
    Dos* dosp = (Dos*)imageBufferp; //Define DOS structure type pointer
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Judge whether the section is legal
	int len = 0;
    if(num < 1 || num > filep->NumberOfSections){
        printf("This section cannot be found\n");
        return 0;
    }

	//First judge whether the hard code to be added can be saved in the blank area of this area
    secp = secp + num - 1;
    len = opp->SizeOfImage - secp->VirtualAddress;
    if(len < 18){
        printf("Insufficient blank space\n");
        return 0;
    }

    //First, add the determined shellcode to the blank area
	char* spacepianyi = (char*)secp->VirtualAddress;//Blank area offset start address
    char* spacep = imageBufferp + secp->VirtualAddress;//Blank area start memory address
	
    for(int i = 0;i < 18;i++){  //Suppose we add the code we learned earlier
        *(spacep + i) = *(code + i);
    }
    
    //Calculate the value after E8 and add it
    int E8Result = MessageBoxA_Address - (int)(opp->ImageBase + spacepianyi + 8 + 5);
    *(int*)(spacep + 9) = E8Result;//Although the value of E8p is 0x76D2E6F7, it is stored reversely in memory

    
    //Calculate the value after E9 and add it
    int E9Result = (opp->AddressOfEntryPoint + opp->ImageBase) - (opp->ImageBase + (int)spacepianyi + 0xD + 5);
    *(int*)(spacep + 0xE) = E9Result;
   
    //Modify the value of OEP
    opp->AddressOfEntryPoint = (int)spacepianyi;//Blank area offset address
    
    //Because this section table is copied text section table, so the value of the Characteristics field does not need to be modified
    
	printf("Added successfully\n");
    return 1;
}

int main(int argc,char* argv[]){
    //Read in FileBuffer
    char filePath[] = "D:\\ipmsg.exe";
    char* fileBuffer = readInFileBuffer(filePath);
    if(NULL == fileBuffer){
        return 0;
    }
    
    //FileBuffer to ImageBuffer
    char* imageBuffer = fileBufferToImageBuffer(fileBuffer); 
    if(NULL == imageBuffer){
        free(fileBuffer);
        fileBuffer = NULL;
        return 0;
    }
    
    //New section (imagebuffer - > newimagebuffer)
    char* newImageBuffer = addSection(200,imageBuffer);
    if(NULL == newImageBuffer){
        free(fileBuffer);
    	free(imageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
        return 0;
    }

	//Add code to new section
	int isSuc = addCodeToNewSec(shellCode,5,newImageBuffer);//There is still a small problem here. If the virtualsize > sizeofrawdata of a section is added, it is wrong and cannot be opened. I don't know how to solve it
	if(!isSuc){
		free(fileBuffer);
        free(imageBuffer);
		free(newImageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
		newImageBuffer = NULL;
		return 0;
	}
    
    //newImageBuffer to NewBuffer
    char* newBuffer = imageBufferToNewBuffer(newImageBuffer);
    if(NULL == newBuffer){
        free(fileBuffer);
        free(imageBuffer);
		free(newImageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
		newImageBuffer = NULL;
        return 0;
    }
    
    //save
    char destFilePath[] = "D:\\ipmsg_new.exe";
    int isSuccessed2 = save(destFilePath,newBuffer);
    if(!isSuccessed2){
        free(fileBuffer);
		free(imageBuffer);
		free(newBuffer);
		free(newImageBuffer);
		fileBuffer = NULL;
		newImageBuffer = NULL;
		imageBuffer = NULL;
		newBuffer = NULL;
		return 0;
    }
    
    free(fileBuffer);
    free(imageBuffer);
    free(newBuffer);
	free(newImageBuffer);
    fileBuffer = NULL;
	newImageBuffer = NULL;
    imageBuffer = NULL;
    newBuffer = NULL;
    return 0;
}

2. Programming: expand the last section and add code

A new memory expandImageBuffer must be applied to store the expanded PE file, which cannot be expanded on the original ImageBuffer

#include "stdafx.h"
#include "stdlib.h"
#define IMAGE_SIZEOF_SHORT_NAME 8

#define MessageBoxA_Address 0x77D36476 / / since we haven't learned to import and export tables, we can't get the address of messageboxa function of different machines, so we can only check the address of messageboxa function and macro definition first

char shellCode[] = {  //Define the hard coded global variables to be added, write the determined ones, fill in the uncertain ones with 0x00 first, and then change them after calculation
    0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
    0xE8,0x00,0x00,0x00,0x00,
    0xE9,0x00,0x00,0x00,0x00
};
int shellLen = sizeof(shellCode);

//*******************************************************************************
#pragma pack(1)
typedef struct DOS_HEADER{  //DOS Header field
    short e_magic;
    short e_cblp;
    short e_cp;
    short e_crlc;
    short e_cparhdr;
    short e_minalloc;
    short e_maxalloc;
    short e_ss;
    short e_sp;
    short e_csum;
    short e_ip;
    short e_cs;
    short e_lfarlc;
    short e_ovno;
    short e_res[4];
    short e_oemid;
    short e_oeminfo;
    short e_res2[10];
    int e_lfanew;  
}Dos;
#pragma pack()

//Don't forget there's a PE signature in the middle

#pragma pack(1)
typedef struct FILE_HEADER{   //Standard PE header field
    short Machine;
    short NumberOfSections;
    int TimeDateStamp;
    int PointerToSymbolTable;
    int NumberOfSymbols;
    short SizeOfOptionalHeader;
    short Characteristics;
}File;
#pragma pack()
    
#pragma pack(1)
typedef struct OPTIONAL_HEADER{   //Optional PE header field
    short Magic;
    char MajorLinkerVersion;
    char MinorLinkerVersion;
    int SizeOfCode;
    int SizeOfInitializedData;
    int SizeOfUninitializedData;
    int AddressOfEntryPoint;
    int BaseOfCode;
    int BaseOfData;
    int ImageBase;
    int SectionAlignment;
    int FileAlignment;
    short MajorOperatingSystemVersion;
    short MinorOperatingSystemVersion;
    short MajorImageVersion;
    short MinorImageVersion;
    short MajorSubsystemVersion;
    short MinorSubsystemVersion;
    int Win32VersionValue;
    int SizeOfImage;
    int SizeOfHeaders;
    int CheckSum;
    short Subsystem;
    short DllCharacteristics;
    int SizeOfStackReserve;
    int SizeOfStackCommit;
    int SizeOfHeapReserve;
    int SizeOfHeapCommit;
    int LoaderFlags;
    int NumberOfRvaAndSizes;
    //There are several (16) structures, one of which is 8 bytes. We won't study it first, and we'll talk about it later
}Op;
#pragma pack()    
    
#pragma pack(1)
typedef struct _IMAGE_SECTION_HEADER {
    char Name[IMAGE_SIZEOF_SHORT_NAME];  //Macro definition usage
    union{
        int PhysicalAddress;
        int VirtualSize;
    }Misc;
    int VirtualAddress;
    int SizeOfRawData;
    int PointerToRawData;
    int PointerToRelocations;
    int PointerToLinenumbers;
    short NumberOfRelocations;
    short NumberOfLinenumbers;
    int Characteristics;
}Sec;
#pragma pack()
//*******************************************************************************

//Read in FileBuffer
char* readInFileBuffer(char* filePath){
    FILE* fp = NULL;
    char* p = NULL;
    fp = fopen(filePath,"rb");
    if(NULL == fp){
        printf("File open failed \ n "");
        fclose(fp);
        return NULL;
    }
    
    //Calculate file length
    fseek(fp,0,2);
    int len = ftell(fp);
    fseek(fp,0,0);
    
    //Dynamically request FileBuffer memory space
    p = (char*)malloc(len);
    if(NULL == p){
        printf("FileBuffer Memory space allocation failed. \ n "");
        fclose(fp);
        return NULL;
    }
    
    //Write data
    int isSuccessed = fread(p,len,1,fp);
    if(!isSuccessed){
        printf("Failed to write to FileBuffer \ n "");
        fclose(fp);
        free(p);
        return NULL;
    }
    fclose(fp);
    return p;  //Return the first address of FileBuffer
}

//FileBuffer To ImageBuffer
char* fileBufferToImageBuffer(char* fileBufferp){
    Dos* dosp = (Dos*)fileBufferp; //Define DOS structure type pointer
    File* filep = (File*)(fileBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Dynamically request the memory space of ImageBuffer
    char* ip = NULL;
    ip = (char*)malloc(opp->SizeOfImage);
    if(NULL == ip){
        printf("Dynamic request for ImageBuffer memory failed. \ n "");
        return NULL;
    }
    for(int i = 0;i < opp->SizeOfImage;i++){
        *(ip + i) = 0x00;
    }
    
    //Copy all headers
    for(int j = 0;j < opp->SizeOfHeaders;j++){
        *(ip + j) = *(fileBufferp + j);
    }
    
    //Copy all sections
    for(int k = 0;k < filep->NumberOfSections;k++){
        for(int x = 0;x < secp->SizeOfRawData;x++){
            *(ip + secp->VirtualAddress + x) = *(fileBufferp + secp->PointerToRawData + x);
        }
        secp++;
    }
    
    return ip; //Returns the starting address of the ImageBuffer
}

//ImageBuffer To newBuffer
char* imageBufferToNewBuffer(char* imageBufferp){
    Dos* dosp = (Dos*)imageBufferp; //Define DOS structure type pointer
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Calculate the size required for newBuffer (the problem not solved by day30 is solved here)
    //Use the file offset address of the last section + the aligned size of the last section
    Sec* temp = secp;
    secp = secp + filep->NumberOfSections - 1;
    int len = secp->PointerToRawData + secp->SizeOfRawData;
    secp = temp;
    
    //Dynamically allocate NewBuffer memory
    char* np = (char*)malloc(len);
    if(NULL == np){
        printf("NewBuffer Memory allocation failed. \ n "");
        return NULL;
    }
    for(int i = 0;i < len;i++){
        *(np + i) = 0x00;
    }
    
    //Copy all headers
    for(int j = 0;j < opp->SizeOfHeaders;j++){
        *(np + j) = *(imageBufferp + j);
    }
    
    //Copy all sections
    for(int k = 0;k < filep->NumberOfSections;k++){
        for(int x = 0;x < secp->SizeOfRawData;x++){
            *(np + secp->PointerToRawData + x) = *(imageBufferp + secp->VirtualAddress + x);
        }
        secp++;
    }
    
    return np; //Return the first address of NewBuffer
}

//save
int save(char* savePath,char* newBufferp){
    Dos* dosp = (Dos*)newBufferp; //Define DOS structure type pointer
    File* filep = (File*)(newBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    FILE* fp = fopen(savePath,"wb");
    if(NULL == fp){
        printf("File open failed \ n "");
        fclose(fp);
        return 0;
    }
    
    secp = secp + filep->NumberOfSections - 1;
    int len = secp->PointerToRawData + secp->SizeOfRawData;  //Get the size of newBuffer
    int isSuccessed = fwrite(newBufferp,len,1,fp);
    if(!isSuccessed){
        printf("Save failed. \ n "");
        fclose(fp);
        return 0;
    }
    fclose(fp);
	printf("Save succeeded. \ n "");
    return 1;
}

//Expand the last section separately
char* expandSection(int size,char* imageBufferp){
    Dos* dosp = (Dos*)imageBufferp; //Define DOS structure type pointer
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    //Re apply for a new memory to store the expanded expandImageBuffer
    int SizeOfExpandImageBuffer = ((opp->SizeOfImage + size - 1) / opp->SectionAlignment + 1) * opp->SectionAlignment;
	char* expandImageBufferp = (char*)malloc(SizeOfExpandImageBuffer);
	if(expandImageBufferp == NULL){
		printf("Failed to request memory \ n "");
		return NULL;
	}
	for(int i = 0;i < SizeOfExpandImageBuffer;i++){
		*(expandImageBufferp + i) = 0x0;//Fill all with 0x0
	}
	for(int k = 0;k < opp->SizeOfImage;k++){
		*(expandImageBufferp + k) = *(imageBufferp + k);
	}
    
    //Modify the properties of the last section table
    dosp = (Dos*)expandImageBufferp; //Define DOS structure type pointer
    filep = (File*)(expandImageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
   	Sec* lastSec = (Sec*)(expandImageBufferp + (dosp->e_lfanew + 0x4 + 0x14 + filep->SizeOfOptionalHeader + (filep->NumberOfSections - 1) * 0x28));
	lastSec->Misc.VirtualSize = SizeOfExpandImageBuffer - lastSec->VirtualAddress;
    lastSec->SizeOfRawData = lastSec->Misc.VirtualSize; //It doesn't matter if the memory size is equal to the file size

    //Modify SizeOfImage
    opp->SizeOfImage = SizeOfExpandImageBuffer;

	printf("Expanded section (successful) ";
	return expandImageBufferp;
}

//Expand section and add code
char* expandAndAddCode(char* code,int size,char* imageBufferp){ //Add the address directly in the ImageBuffer, which is more convenient for calculation
    Dos* dosp = (Dos*)imageBufferp; //Define DOS structure type pointer
    File* filep = (File*)(imageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    Op* opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    Sec* secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
    
	//Expanded section
	//Re apply for a new memory to store the expanded expandImageBuffer
    int SizeOfExpandImageBuffer = ((opp->SizeOfImage + size - 1) / opp->SectionAlignment + 1) * opp->SectionAlignment;
	char* expandImageBufferp = (char*)malloc(SizeOfExpandImageBuffer);
	if(expandImageBufferp == NULL){
		printf("Failed to request memory \ n "");
		return NULL;
	}
	for(int i = 0;i < SizeOfExpandImageBuffer;i++){
		*(expandImageBufferp + i) = 0x0;//Fill all with 0x0
	}
	for(int k = 0;k < opp->SizeOfImage;k++){
		*(expandImageBufferp + k) = *(imageBufferp + k);
	}
    
    //Modify the properties of the last section table
    dosp = (Dos*)expandImageBufferp; //Define DOS structure type pointer
    filep = (File*)(expandImageBufferp + dosp->e_lfanew + 4);//Defines the File structure type pointer
    opp = (Op*)((char*)filep + 20);//Define Op structure type pointer
    secp = (Sec*)((char*)opp + filep->SizeOfOptionalHeader);//Define Sec structure type pointer
    
   	Sec* lastSec = (Sec*)(expandImageBufferp + (dosp->e_lfanew + 0x4 + 0x14 + filep->SizeOfOptionalHeader + (filep->NumberOfSections - 1) * 0x28));
	lastSec->Misc.VirtualSize = SizeOfExpandImageBuffer - lastSec->VirtualAddress;
    lastSec->SizeOfRawData = lastSec->Misc.VirtualSize; //The file alignment size doesn't matter. It can be directly equal to the memory size

    //Modify SizeOfImage
    opp->SizeOfImage = SizeOfExpandImageBuffer;
	
	
    //**************************Add code section*******************************
	//First judge whether the hard code to be added can be saved in the blank area of this area
    if(size < 18){
        printf("There is not enough space in the blank area \ n "");
        return 0;
    }

    //First, add the determined shellcode to the blank area
	char* spacepianyi = (char*)opp->SizeOfImage - size;//Blank area offset start address
    char* spacep = expandImageBufferp + (opp->SizeOfImage - size);//Blank area start memory address
	
    for(int z = 0;z < 18;z++){  //Suppose we add the code we learned earlier
        *(spacep + z) = *(code + z);
    }
    
    //Calculate the value after E8 and add it
    int E8Result = MessageBoxA_Address - (int)(opp->ImageBase + spacepianyi + 8 + 5);
    *(int*)(spacep + 9) = E8Result;//Although the value of E8p is 0x76D2E6F7, it is stored reversely in memory

    
    //Calculate the value after E9 and add it
    int E9Result = (opp->AddressOfEntryPoint + opp->ImageBase) - (opp->ImageBase + (int)spacepianyi + 0xD + 5);
    *(int*)(spacep + 0xE) = E9Result;
   
    //Modify the value of OEP
    opp->AddressOfEntryPoint = (int)spacepianyi;//Blank area offset address
    
    //Also modify the value of the Characteristics field of this section
    secp->Characteristics = secp->Characteristics | 0x60000020;
    
	printf("Successfully added \ n "");
    return expandImageBufferp;
}


int main(int argc,char* argv[]){
    //Read in FileBuffer
    char filePath[] = "D:\\ipmsg.exe";
    char* fileBuffer = readInFileBuffer(filePath);
    if(NULL == fileBuffer){
        return 0;
    }
    
    //FileBuffer To ImageBuffer
    char* imageBuffer = fileBufferToImageBuffer(fileBuffer); 
    if(NULL == imageBuffer){
        free(fileBuffer);
        fileBuffer = NULL;
        return 0;
    }

	/*
	//Expand the last section separately
	char* expandImageBuffer = expandSection(200,imageBuffer);
	if(NULL == expandImageBuffer){
		free(fileBuffer);
    	free(imageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
		return 0;
	}
	*/
    
	//Expand the last section and add code
	char* expandImageBuffer = expandAndAddCode(shellCode,200,imageBuffer);
	if(NULL == expandImageBuffer){
		free(fileBuffer);
        free(imageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
        return 0;
	}

    //newImageBuffer to NewBuffer
    char* newBuffer = imageBufferToNewBuffer(expandImageBuffer);
    if(NULL == newBuffer){
        free(fileBuffer);
        free(imageBuffer);
		free(expandImageBuffer);
        fileBuffer = NULL;
    	imageBuffer = NULL;
		expandImageBuffer = NULL;
        return 0;
    }
    
    //save
    char destFilePath[] = "D:\\ipmsg_new.exe";
    int isSuccessed2 = save(destFilePath,newBuffer);
    if(!isSuccessed2){
        free(fileBuffer);
		free(imageBuffer);
		free(newBuffer);
		free(expandImageBuffer);
		fileBuffer = NULL;
		expandImageBuffer = NULL;
		imageBuffer = NULL;
		newBuffer = NULL;
		return 0;
    }
    
    free(fileBuffer);
    free(imageBuffer);
    free(newBuffer);
	free(expandImageBuffer);
    fileBuffer = NULL;
	expandImageBuffer = NULL;
    imageBuffer = NULL;
    newBuffer = NULL;
    return 0;
}

Tags: C++ security PE

Posted by andy1398 on Mon, 02 May 2022 18:30:54 +0300