Changes to PE executables

zhaozj2021-02-08  310

About the modification of the PE executable: Ilsy (Ilsy) Under Windows 9X, NT, 2000, all executable files are based on a new file format, which is based on Microsoft Design Portable Executable File Format (portable executive) That is, the PE format. There are some time, we need to modify these executables, the following text attempts to describe the format of the PE file and modifications to PE format files. 1, PE file frame composition DOS MZ Header DOS Stub PE Header Section Table Section 1 Section 2 Section ... section n The table is the overall hierarchy distribution of the PE file structure. All PE files (even 32-bit DLLs) must start with a simple DOS MZ Header, "MZ Signs" in DOS under DOS at offset 0, with it, once the program is executed under DOS, DOS It can identify this is an effective actuator and then runs closely with the DOS STUB after MZ HEADER. DOS Stub is actually a valid EXE. In an operating system that does not support PE file format, it will simply display an error prompt, similar to string "this program cannot run in dos mode" or programmer can implement according to your intent Complete DOS code. Usually DOS Stub is automatically generated by the assembler / compiler, not very large for our use, it simply calls the interrupt 21h service 9 to display the string "this Program Cannot Run in dos mode". The DOS Stub is PE Header. PE Header is an abbreviation for the PE-related structure image_nt_headers, which contains many important domains used in the PE loader. When executed in the operating system that supports the PE file structure, the PE loader finds the start offset from the DOS MZ HEADER offset 3ch. Therefore, the DOS STUB is jumped directly to the real file header PE Header. The true content of the PE file is divided into a block, called Sections (section). Each section is a data with common attributes, such as ".text" section, then, what is the content of each section? In fact, PE format files put the contents of the same properties into the same section, without having to care about ".text", ". Data" named, naming is just to facilitate identification, all, if we are formatted to PE format The file is modified. Theoretically, it can be written to any festival and adjust the properties of this section. PE Header Next Array Structure Section Table. Each structure contains the attributes, file offset, virtual offset, and the like of the corresponding section. If there are 5 festivals in the PE file, there are 5 members in this structure. The above is the physical distribution of the PE file format. He summarizes the main steps of the load of a PE file: 1. The PE file is executed, the PE loader checks the PE Header offset in the DOS MZ HEADER. If you find it, you jump to the pe header. 2, the PE loader checks the effectiveness of the Pe header. If it is effective, jump to the tail of Pe Header. 3, keeping with Pe Header is a knot. The PE loader reads the section information, and uses the file mapping method to map these sections to the memory, and pay the feature specified in the section table. 4. When the PE file is mapped into memory, the PE loader will process the Logical part similar to the Import Table in the PE file.

The above steps are the results of some seniors analysis. 2, PE Full Overview We can find the definition of the PE file header in Winnt.h: type_Headers {DWORD SIGNATURE; // PE file header mark: "PE / 0/0". In the DOS header start address offset of the start point to the premises 3CH IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader // PE file information of physical distribution; // PE file information distribution logic} IMAGE_NT_HEADERS32, * PIMAGE_NT_HEADERS32; typedef struct _IMAGE_FILE_HEADER {WORD Machine; // This file runs the CPU required, for the Intel platform is the number of days DWORD TIMEDATAMP; // file creation date and time DWORD POINTOSYMBOLTABLE; / / The symbols are used to debug DWORD NUMBEROFSYMBOLS; // Symbol table number wORD SizeOfOptionalHeader; // OptionalHeader structure size wORD Characteristics; // file tag information, the file is distinguished exe or dll} IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER; typedef struct _IMAGE_OPTIONAL_HEADER {wORD Magic; // flag word (always 010bh) BYTE MajorLinkerVersion; / / connector version number BYTE MinorLinkerVersion; // DWORD SizeOfCode; // code segment size DWORD SizeOfInitializedData; // initialized data block size DWORD SizeOfUninitializedData; // uninitialized data block size DWORD AddressOfEntryPoint; // PE PE loader ready to run RVA of the first instruction of the file, to change the entire execution process, you can specify this value to the new RVA, so that the instructions at the new RVA are first performed. (Many articles have an introduction of RVA, please go to know) DWORD baseofcode; // code segment start RVA DWORD baseofData; // Data segment start RVA DWORD ImageBase; // PE file load address DWORD sectionalignment; // block Dword FileAlignment; // file block alignment WORD MajorOperatingSystemVersion; // required operating system version number WORD MinorOperatingSystemVersion; // WORD MajorImageVersion; // custom version WORD MinorImageVersion; // WORD MajorSubsystemVersion; // win32 subsystem-version.

If the PE file is a Word MinorsubsystemVersion designed for Win32; / / The subsystem version must be 4.0 otherwise the dialog does not have 3-dimensional sense DWORD WIN32VERSIONVALUE; // Reserved DWORD SIZEOFIMAGE; // The size of the entire PE image in the memory DWORD SIZEOFHERS; / / The size of the head segment table DWORD checksum; // check and Word subsystem; // NT is used to identify which subsystem of the PE file belongs to Word DllCharacteristics; // dword sizeofstackReServe; // DWORD SIZEOFSTACKCOMMIT; // DWORD SizeOfHeapReserve; // DWORD SizeOfHeapCommit; // DWORD LoaderFlags; // DWORD NumberOfRvaAndSizes; // IMAGE_DATA_DIRECTORY DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // IMAGE_DATA_DIRECTORY array of structures. Each structure is given a RVA important data structures, such as the introduction of the address tables, etc.} IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32; typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; // RVA table addresses DWORD Size; // size} IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY; PE file After the head is a knot, in Winnt.h, the TypedEf struct _image_section_Header {byte name [image_sizeof_short_name]; // session table name, such as ".text" union {dword physicaladdress; // physical address DWORD VIRTUALSIZE; // Real length } Misc; dword virtualaddress; // RVA DWORD SIZEOFRAWDATA; // Physical length DWORD POINTERTORAWDATA; // Subto Based on the file's offset DWORD POINTORELOCATIONS; // Relocated offset DWORD POINTERTOLINENUMBERS; // Line Table offset Word NumberofrelOcations; // Relocate Number Word Numberoflinenumbers; // The number of line numbers DWord Characteristics; // Section Attribute} Image_section_Header, * pimage_section_Header; The above structure is defined in Winnt.h About the PE file header, how to use C / C to perform PE executable operation, to use all of the above configuration, which describes the structure of the PE file header.

3, modify the PE executable Now let's write a code to any PE format executable, the code is as follows: - Test.asm - .386p .Model flat, stdcall option casemap: none incrude / masm32 / incrude /windows.inc include /masm32/include/user32.inc includelib /masm32/lib/user32.lib .code start: INVOKE MessageBoxA, 0,0,0, MB_ICONINFORMATION or MB_OK ret end start MessageBox above code shows only a frame, compiled The binary code is as follows: unsigned char WriteLine [18] = {0x6a, 0x40, 0x6a, 0x0, 0x6a, 0x0, 0x6a, 0x0, 0x0,0 x0, 0xe9, 0x0,0x0,0x0,0x0} Well, let's take a look at the code to write these code. Now use TDUMP.EXE to display a PE format to execute file information, you can find the following description: Object Table: # Name Virtsize RVA Physize Phys Off Flags - ---------------- -------------- -------- -------- 01 .Text 0000ccc0 00001000 0000CE00 000000600 60000020 [CER] 02 .DATA 00004628 0000E000 00002C00 0000D400 C0000040 [Irw] 03.RSRC 000003C8 0008000000000000000000000040400 00010000 40000040 [IR] Key to Section Flags: C - Contains Code E - EXECUTABLE I - Contains Initialized Data R - Readable W - Writeable The above describes 3 sections and each segment Get information, in fact, our code can write any piece, here I choose ".text" segment.

Get the header information of a PE format executable with the following code: //writepe.cpp #include #include #include #include #include < Time.h> #include unsigned char WriteLine [18] = {0x6a, 0x40, 0x6a, 0x0, 0x6a, 0x0,0x6a, 0x0,0x0,0x01,0x0,0 x0, 0x0,0xe9,0x0 , 0x0,0x0,0x0}; DWORD space; DWORD entryaddress; DWORD entrywrite; DWORD progRAV; DWORD oldentryaddress; DWORD newentryaddress; DWORD codeoffset; DWORD peaddress; DWORD flagaddress; DWORD flags; DWORD virtsize; DWORD physaddress; DWORD physsize; DWORD MessageBoxAadaddress; int main (int argc, char * * argv) {HANDLE hFile, hMapping; void * basepointer; FILETIME * Createtime; FILETIME * Accesstime; FILETIME * writetime; Createtime = new FILETIME; Accesstime = new FILETIME; writetime = new FILETIME; if ( (hFile = CreateFile (argv [1], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE) // open the modified files {puts ( "(could not open)"); Return EXIT_FAILURE;} if (! getFiletime) E, CreateTime, Accesstime, WriteTime)) {Printf ("/ Nerror getFileTime:% D / N", getLastError ());} // Get the time IF to modify the file, modify the time IF (! (hmapping = createfilemapping (hfile , 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)) {PUTS ("(Mapping Failed"); CloseHandle (HFILE); Return EXIT_FAILURE;} if (! (Basepointer = MapViewoffile (hmapping, file_map_read, 0, 0 , 0)) {PUTS ("View failed"); CloseHandle (HMApping); CloseHandle; Return EXIT_FAILURE;} // Put the file header image in BaseoInter CloseHandle (HMApping); CloseHandle (HFILE); Map_exe (basepointer); // Get relevant address unmapViewoffile (basepointer); printaddress (); Printf ("/ n / n"); if (Space <50) {printf ("

/ N gap is too small, data cannot be written ./N ");} else {writefile (); // write file} if ((hfile = createfile (argv [1], generic_read | generic_write, file_share_read | file_share_write, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE) {puts ( "(could not open)"); return EXIT_FAILURE;} if (SetFileTime (hFile, Createtime, Accesstime, writetime)) {printf ( "error settime:!% d / N ", getLastError ());} // Restore DELETE CREATETIME and other delete createtime; delete dispatch; delete dispi; retren 0;} void map_exe (const void * base) {image_dos_header * dos_head ; dos_head = (IMAGE_DOS_HEADER *) base; #include typedef struct PE_HEADER_MAP {DWORD signature; IMAGE_FILE_HEADER _head; IMAGE_OPTIONAL_HEADER opt_head; IMAGE_SECTION_HEADER section_header [];} peHeader; #include if (dos_head-> e_magic ! = Image_dos_signature) {PUTS ("Unknown Type of File"); return;} peheader * header; header = (PEHER *) ((char *) DOS_HEAD DOS_HEAD-> E_LFANEW); // Get PE file headers (IsbadReadptr (header, sizeof (* header)) {P UTS ("(" ("(NO PE Header, Probably DOS Executable); Return; DWORD MODS; CHAR TMPSTR [4] = {0}; DWORD TMPADDRESS; DWORD TMPADDRESS1; IF (strstr (const char *) header-> section_Header [0] .name, ". Text")! = Null) {virtsize = header-> section_Header [0] .misc.virtualsize; // This paragraph real length physaddress = header-> section_header [0] .pointertorawData; / / Physical Offset Physize = header-> section_Header [0] .sizeOfrawData; // This section peaddress = dos_head-> e_lfanew; // Get the start of the PE file header PEHEADER PEH; TMPADDRESS = (unsigned LONG) & PEH; // Gets the offset of the structure Tmpaddress1 = (unsigned long) & (Peh.SECTION_HEADER [0] .Characteristics); // Get the mutation of the variable flagaddress =

TMPAddress1-tmpaddress 2; // Get the relative offset of attributes FLAGS = 0x8000; // In general, ". Text" segment is unreadable, if we want to write data to this paragraph to change its properties, actually The program does not write the data into the ".text" segment, so do not need to be changed, but if you realize complex features, you must need data, there is definitely to change this value, space = physize-virtsize; // Get code segment The available space is used to determine whether you can write our code // to subtract the real length of this section to get the real length of this segment. You can get prograv = header-> opt_head.imagebase; // Get the loading address of the program, general For 400000 codeoffset = header-> opt_head.baseofcode-physaddress; // Get code offset, starting RVA with code segment, subtracting the physical offset of this segment / / should be a relative offset address Calculate the formula: // Code write address codeoffset entrywrite = header-> section_header [0] .pointertorawdata header-> section_header [0] .misc.virtualsize; // code write physical offset MODS = entrywrite% 16; // Align the boundary IF (MODS! = 0) {entrywrite = (16-mods);} OldEntryAddress = header-> opt_head.addressofentryPoint; // Save the old program entry address new refress = entrywrite codeoffset; // Calculate new the program entry address return;} void printaddress () {HINSTANCE gLibMsg = NULL; DWORD funaddress; gLibMsg = LoadLibrary ( "user32.dll"); funaddress = (DWORD) GetProcAddress (gLibMsg, "MessageBoxA"); MessageBoxAadaddress = funaddress; gLibAMsg = LoadLibrary ("kernel32.dll"); // gets the address in memory in memory so that we use} V Oid writefile () {int Ret; long rett; dword address; int Tmp; unsigned char Waddress [4] = {0}; RET = _Open (filename, _o_rdwr | _o_creat | _o_binary, _s_iread | _s_iwrite; if (! RET) {Printf ("Error Open / N"); return;} RETF = _LSeek (RET, (long) PEADDRESS 40, seek_set); // The entry address of the program starts at 40 at the PE file header (RETF == - 1) {Printf ("ERROR SEEK / N"); return; address = newntryaddress; TMP = address >> 24; Waddress [3] = TMP; TMP = address << 8; tmp = tmp >> 24; WADDRESS [ 2] = TMP; TMP =

Address << 16; TMP = TMP >> 24; WADDRESS [1] = TMP; TMP = address << 24; TMP = TMP >> 24; Waddress [0] = TMP; RETF = _Write (Ret, Waddress, 4) ; // Write the new entry address into the file IF (RETF == - 1) {Printf ("Error Write:% D / N", getLastError ()); return;} RETF = _LSeek (RET, (long) Entrywrite , Seek_set); if (RETF == - 1) {Printf ("Error SEEK / N"); return;} RETF = _Write (RET, WRITELINE, 18); if (RETF == - 1) {Printf ("Error Write:% D / N ", getLastError ()); return;} // writing WriteLine to our calculated space RETF = _LSeek (RET, (long) entrywrite 9, seek_set); // Change the MessageBox function address, Its binary code is in WriteLine [10] if (RETF == - 1) {Printf ("ERROR SEEK / N"); Return;} Address = MessageBoxaadaddress- (Program NewEntryAddress 9 4); // Recalculation The address of the Messagebox function, the original address of the MessageBox function minus the program load address plus the new entry address plus 9 (its binary code relative offset) plus 4 (address length) TMP = address >> 24; Waddress [3 ] = TMP; TMP = address << 8; TMP = TMP >> 24; Waddress [2] = TMP; TMP = address << 16; TMP = TMP >> 24; WADDRESS [1] = TMP; TMP = Address < <24; TMP = TMP >> 24; Waddress [0] = Tmp; RETF = _Write (RET, WADDRESS, 4); // Write Recalculated MessageBox address if (RETF == - 1) {Printf ("Error Write:% d / n ", getLastError ()); return;} RETF = _ LSeek (RET, (long) EntryWrite 14, seek_set); // Change the return address, return to the original program entry address with JPM, other binary code in WriteLine [15] if (RETF == - 1) {Printf (" Error seek / n "); return; address = 0- (NewEntryAddress-OldEntryAddress 4 15); // Return the address calculation method is the new entry address minus the old entrance address plus 4 (address length) plus 15 (Binary code relative offset) After taking the anti-TMP = Address >> 24; Waddress [3] = TMP; TMP = address << 8; TMP = TMP >> 24; WADDRESS [2] = TMP; TMP = address <<

16; TMP = TMP >> 24; WADDRESS [1] = TMP; TMP = address << 24; TMP = TMP >> 24; Waddress [0] = TMP; RETF = _Write (RET, WADDRESS, 4); // Write back address if (RETF == - 1) {Printf ("Error Write:% D / N", getLastError ()); return;} _close (re); printf ("/ nall done ... / n" );} // end Due to the use of the RVA address in the PE format file, some function calls and return addresses must be calculated, or the above is my experience in practice, if you Have a better way, I hope you can tell me. If there is an error, please let me not mislead the person who read this article. Write a chaotic, please forgive me. Ilsy@neetguard.com.cn Published: September 21, 2001 Reads: 202 Close window 2000 version of the version of the copyright belongs to the network security focus, all articles copyrights belong to their authors, such as reprint, you must indicate the author!

转载请注明原文地址:https://www.9cbs.com/read-1185.html

New Post(0)