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])