PE结构解析的代码

Posted go2sleep

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PE结构解析的代码相关的知识,希望对你有一定的参考价值。

0x00 32位程序的PE结构

DOS头

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER;

nt头 = PE标识 + 文件头 + 扩展头

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32,

文件头

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER;

扩展头,文件头中的SizeOfOptionalHeader指定了扩展头的大小。

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32;

0x01 一份用c实现的代码段起始地址获取

  • 读取dos头,获得nt头的偏移A
  • 文件头的偏移为A+sizeof(DWORD)
int get_text(FILE *file, unsigned int *start, unsigned int *end, unsigned int *vaddr)
{
	/*目标文件为32位*/
	/*获取代码段在二进制文件中的起始和终止地址*/

	FILE *pfile = file;
	unsigned int e_lfanew;
	unsigned int optional_header_size;
	unsigned int section_count;

	IMAGE_DOS_HEADER *dos_header;
	IMAGE_FILE_HEADER *file_header;
	IMAGE_SECTION_HEADER *section_header;

	/*读取dos头*/
	dos_header = (IMAGE_DOS_HEADER *)calloc(1, sizeof(IMAGE_DOS_HEADER));
	fread(dos_header, sizeof(IMAGE_DOS_HEADER), 1, file);
	e_lfanew = dos_header->e_lfanew;
	free(dos_header);

	/*读取文件头*/
	file_header = (IMAGE_FILE_HEADER *)calloc(1, sizeof(IMAGE_FILE_HEADER));
	//文件头的偏移为 e_lfanew + sizeof(DWORD)
	fseek(file, e_lfanew + sizeof(DWORD), SEEK_SET);
	fread(file_header, sizeof(IMAGE_FILE_HEADER), 1 , file);
	optional_header_size = file_header->SizeOfOptionalHeader;
	section_count = file_header->NumberOfSections;
	free(file_header);

	/*读取节表*/
	section_header = (IMAGE_SECTION_HEADER *)calloc(section_count, sizeof(IMAGE_SECTION_HEADER));
	//节表的偏移为 e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + optional_header_size
	fseek(file, e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + optional_header_size, SEEK_SET);
	fread(section_header, sizeof(IMAGE_SECTION_HEADER), section_count, file);

	for (int i = 0; i < section_count; i++) {
		if (strncmp((char *)(section_header + i)->Name, ".text", 5) == 0) {
			*start = (section_header + i)->PointerToRawData;
			*end = (section_header + i)->PointerToRawData + (section_header + i)->SizeOfRawData;
			*vaddr = (section_header + i)->VirtualAddress;
			break;
		}
	}

	free(section_header);
	return 0;
}

0x02 去除ASLR

dll加载时启用ALSR是由操作系统和二进制文件两方面决定的,Windows 10中的Windows安全中心提供了关闭ASLR的选项,但是启用后并不奏效,使用一些老的方法(修改注册表内存管理中的MoveImages值为零)也失效了,因此直接把二进制文件中的位进行修改。
这是由扩展头中的DllCharacteristics决定的,IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE这一位是关键,将其置零,那么将DllCharacteristics的值与上0xFFBF即可。

// DllCharacteristics Entries

//      IMAGE_LIBRARY_PROCESS_INIT            0x0001     // Reserved.
//      IMAGE_LIBRARY_PROCESS_TERM            0x0002     // Reserved.
//      IMAGE_LIBRARY_THREAD_INIT             0x0004     // Reserved.
//      IMAGE_LIBRARY_THREAD_TERM             0x0008     // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA    0x0020  // Image can handle a high entropy 64-bit virtual address space.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040     // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn‘t want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND      0x0800     // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000     // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   0x2000     // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF     0x4000     // Image supports Control Flow Guard.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     0x8000

一份python脚本

import sys
import pefile
import struct

class Patch(object):
    def __init__(self, filename):
        with open(filename, ‘rb‘) as f:
            self.data = f.read()

    def save_file(self, filename):
        with open(filename, ‘wb‘) as f:
            f.write(self.data)

    def patch_file(self, offset, data):
        if(offset >= 0) and (offset + len(data)) < len(self.data):
            self.data = self.data[0:offset] + data + self.data[offset+len(data):]
            return True
        else:
            return False

def get_dllcharacteristics(pe):
    offset = pe.OPTIONAL_HEADER.get_field_absolute_offset(‘DllCharacteristics‘)
    value = pe.OPTIONAL_HEADER.DllCharacteristics
    return offset, value


def disable_aslr(file):
    pe = pefile.PE(file)
    offset, value = get_dllcharacteristics(pe)
    # IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0
    value &= 0xFFBF

    patch = Patch(file)
    if patch.patch_file(offset, struct.pack(‘<H‘, value)):
        patch.save_file(file + ‘_patch‘)

if __name__ == ‘__main__‘:
    disable_aslr(sys.argv[1])

以上是关于PE结构解析的代码的主要内容,如果未能解决你的问题,请参考以下文章

手写ELF结构解析工具

pe工具04-获取数据目录

手写PE结构解析工具

解析PE文件的附加数据

PE文件解析 基础篇

逆向-PE头解析