Windows可执行文件PE格式分析

小甲鱼PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02) 上一讲:小甲鱼PE详解之IMAGE_DOS_HEADER结构定义即各个属性的作用PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段。下边小甲鱼将为大家详细讲解哈~(视频教程:http://fishc.com 阅读详情

上面是 windows PE 可执行文件格式的结构图,分为 4 个部分:DOS 文件头、NT 文件头、Section 表以及 Directory 表格。

windows 的 executable image 文件使用的是这种 PE 格式,而 object 文件使用的是 COFF 文件格式。

 

这里仍然是延续我的风格,以实例看 image 文件格式,这次以 mircrosoft visual studio 2010 的 VC++ 9.0 编译出来经典 windows 程序为例:

这个例子是:

// helloworld.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "helloworld.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;        // current instance
TCHAR szTitle[MAX_LOADSTRING];     // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];   // the main window class name

// Forward declarations of functions included in this code module:
ATOM    MyRegisterClass(HINSTANCE hInstance);
BOOL    InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
 UNREFERENCED_PARAMETER(hPrevInstance);
 UNREFERENCED_PARAMETER(lpCmdLine);

  // TODO: Place code here.
 MSG msg;
 HACCEL hAccelTable;

 // Initialize global strings
 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
 LoadString(hInstance, IDC_HELLOWORLD, szWindowClass, MAX_LOADSTRING);
 MyRegisterClass(hInstance);

 // Perform application initialization:
 if (!InitInstance (hInstance, nCmdShow))
 {
  return FALSE;
 }

 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOWORLD));

 // Main message loop:
 while (GetMessage(&msg, NULL, 0, 0))
 {
  if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
 }

 return (int) msg.wParam;
}

 

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
 WNDCLASSEX wcex;

 wcex.cbSize = sizeof(WNDCLASSEX);

 wcex.style   = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc = WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOWORLD));
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_HELLOWORLD);
 wcex.lpszClassName = szWindowClass;
 wcex.hIconSm  = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

 return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND - process the application menu
//  WM_PAINT - Paint the main window
//  WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 int wmId, wmEvent;
 PAINTSTRUCT ps;
 HDC hDC;
 RECT rect;

 switch (message)
 {
 case WM_COMMAND:
  wmId    = LOWORD(wParam);
  wmEvent = HIWORD(wParam);
  // Parse the menu selections:
  switch (wmId)
  {
  case IDM_ABOUT:
   DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
   break;
  case IDM_EXIT:
   DestroyWindow(hWnd);
   break;
  default:
   return DefWindowProc(hWnd, message, wParam, lParam);
  }
  break;
 case WM_PAINT:
  hDC = BeginPaint(hWnd, &ps);
  // TODO: Add any drawing code here...
  GetClientRect(hWnd, &rect);
  DrawText(hDC, TEXT("hello, world"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
  EndPaint(hWnd, &ps);
  break;
 case WM_DESTROY:
  PostQuitMessage(0);
  break;
 default:
  return DefWindowProc(hWnd, message, wParam, lParam);
 }
 return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
 UNREFERENCED_PARAMETER(lParam);
 switch (message)
 {
 case WM_INITDIALOG:
  return (INT_PTR)TRUE;

 case WM_COMMAND:
  if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  {
   EndDialog(hDlg, LOWORD(wParam));
   return (INT_PTR)TRUE;
  }
  break;
 }
 return (INT_PTR)FALSE;
}

在窗口中间显示“hello, world”,使用的是 Win32 debug 版本编译,生成的 helloworld.exe 大小是 88,576 bytes

 

1. MS-DOS 文件头

在 image 文件的最开始处就是 DOS 文件头,DOS 文件头包含了 DOS stub 小程序。 在 WinNT.h 文件里定义了一个结构来描述 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, *PIMAGE_DOS_HEADER;

这个结构名叫 IMAGE_DOS_HEADER 共 64 bytes,以 IMAGE_DOS_HEADER 结构描述的 DOS 文件头结构从 image 的 0x00000000 - 0x0000003F(64 bytes)

结构的 e_magic 域是 DOS 头文件签名,它的值是:0x5A4D 代表字符 MZ,它在 WinNT.h 里定义为:

#define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

e_lfanew 域是一个 offset 值,它指出 NT 文件头的位置。

下面看看 helloworld.exe 的 DOS 文件头内容:

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00  MZ..........??..
00000010  B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ?.......@.......
00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000030  00 00 00 00 00 00 00 00 00 00 00 00 F0 00 00 00  ............e...

红色部分是 DOS 签名,蓝色部分是 PE header offset(NT 文件头)值,也就是 IMAGE_DOS_HEADER 里的 e_lfanew 值,表明 NT 文件头在 image 文件的0x000000F0 处。


1.1 DOS stub 程序

在 DOS 文件头下面紧跟着一小段 stub 程序,从 0x00000040 - 0x0000004D 共 14 bytes,这段 dos stub 程序是这样的:

00000040  0E                push cs
00000041  1F                pop ds
00000042  BA0E00            mov dx,0xe
00000045  B409              mov ah,0x9
00000047  CD21              int 0x21
00000049  B8014C            mov ax,0x4c01
0000004C  CD21              int 0x21

当 windows 的 PE 文件放在 DOS 上执行时,将会执行这一段 DOS stub 程序,作用是打印信息:This program cannot be run in DOS mode.... 然后调用 int 21 来终止执行返回到 DOS

看看它是怎样运行的:

00000014  00 00      // ip
00000016  00 00      // cs
00000018  40 00      // e_lfarlc

这个 DOS 执行环境中,CS 和 IP 被初始化为 0,e_lfarlc 是 DOS 环境的 relocate 表,它的值是 0x40 ,那么信息字符串的位置是:0x0040 + 0x000e = 0x4e,在 image 文件的 0x0000004e 正好这字符串的位置。

 

2. NT 文件头

NT 文件头是 PE 文件头的核心部分,由 IMAGE_DOS_HEADER 结构的 e_lfanew 域指出它的位置。

同样 NT 文件头部分由一个结构 IMAGE_NT_HEADER 来描述,在 WinNT.h 里定义如下:

typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

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

可见这个结构分为 32 和 64 位版本,IMAGE_NT_HEADER 结构分为三大部分:

  • PE 文件签名:Signature
  • IMAGE_FILE_HEADER 文件头:FileHeader
  • IMAGE_OPTINAL_HEADER(32/64) 可选头:OptionalHeader

IMAGE_NT_HEADERS32 和 IMAGE_NT_HEADERS64 的匹别在于 IMAGE_OPTIONAL_HEADER 结构,分别为:IMAGE_OPTIONAL_HEADERS32 和IMAGE_OPTIONAL_HEADERS64

在 Win32 下 IMAGE_NT_HEADERS32 是 248 bytes,在 Win64 下 IMAGE_NT_HEADERS64 是 264 bytes,因此 helloworld.exe 的 NT 文件头从 0x000000F0 - 0x000001E7 共 248 bytes


2.1 PE 签名

在 WinNT.h 文件里定义了 PE 文件的签名,它是:

#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

这个签名值是 32 位,值为:0x00004550 即:PE 的 ASCII 码,下面看看 helloworld.exe 中的 PE 签名:


2.2 IMAGE_FILE_HEADER 文件头结构

PE 签名接着是 IMAGE_FILE_HEADER 结构,它在 WinNT.h 中的定义为:

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

这个 IMAGE_FILE_HEADER 对 PE 文件大致的描述,这个结构共 20 bytes,它的域描述如下:

域
size
值
描述
Machine
WORD
IMAGE_FILE_MACHINE_xxx
表示目标平台 processor 类型,例:IMAGE_FILE_MACHINE_I386
NumberOfSections
WORD
节数量
表示映象中有多少个 section
TimeDataStamp
DWORD
从1970年1月1日0:00 以来的总秒数
表示文件创建的时间
PointerToSymbolTable
DWORD
COFF 符号表偏移量
在 image 文件中很少见,总是为 0
NumberOfSymbols
DWORD
COFF 符号表的个数
如果存在的话,表示符号表的个数
SizeOfOptionalHeader
WORD
IMAGE_OPTIONAL_HEADER 结构大小
该域表示 IMAGE_NT_HEADER 中的 IMAGE_OPTIONAL_HEADER 结构的大小
Characteristics
WORD
IMAGE_FILE_xxx
表示文件属性,例如:IMAGE_FILE_DLL 属性

WinNT.h 中定义了一些常量值用来描述 Machine,以 IMAGE_FILE_MACHINE_XXX 开头,下面是一些典型的常量值:

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)

WinNT.h 中还针对 Characteristics 域定义了一些常量值,以 IMAGE_FILE_XXX 开头,代表目标 image 文件的类型,下面是一些常见的值:

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

NumberOfSections 表示 image 有多个 section,另一个重要的域是:SizeOfOptionalHeader 它指出接下来 IMAGE_OPTIONAL_HEADER 的大小,它有两个 size:Win32 的 0xDC 和 Win64 的 0xF0

下面是 helloworld.exe 的 IMAGE_FILE_HEADER 结构,从 0x000000F4 - 0x00000107 共 20 bytes:

将这些值分解为:

000000F4  4C 01                // Machine
000000F8  07 00                // NumberOfSections
000000FA  6C C6 26 4C          // TimeDateStamp
000000FE  00 00 00 00          // PointerToSymbolTable
00000100  00 00 00 00          // NumberOfSymbols
00000104  E0 00                // SizeOfOptionalHeader
00000106  02 01                // Characteristics

  • Machine 是 0x014C,它的值是 IMAGE_FILE_MACHINE_I386,说明这个 image 文件的目标平台是 i386,即:Win32 平台
  • NumberOfSections 是 0x07,说明 image 文件内含有 7 个 sections
  • SizeOfOptionalHeader 是 0xE0,说明接下来的 IMAGE_OPTIONAL_HEADERS32 将是 0xE0(224 bytes)

它的 Characteristics 是 0x102 = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE,说明这个 image 是 32 位可执行的映像。


2.3 IMAGE_OPTIONAL_HEADER32 结构

在 IMAGE_FILE_HEADER 结构里已经指明了 image 是 32 位,并且 IMAGE_OPTIONAL_HEADER 的大小是 224 bytes,那么这个结构就是IMAGE_OPTIONAL_HEADER32 结构。

可以根据 IMAGE_FILE_HEAER 结构的 Machine 来判断 image 是 Win32 还是 Win64 平台的。但是 Microsoft 官方推荐及认可的方法是从 IMAGE_OPTIONAL_HEADER 里的 magic 的值来判断目标平台 。

在 WinNT.h 里 IMAGE_OPTIONAL_HEADER32 的定义如下:

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, *PIMAGE_OPTIONAL_HEADER32;

64 位的 IMAGE_OPTIONAL_HEADER64 里没有 BaseOfData 域,其它的与 IMAGE_OPTIONAL_HEADER32 结构的域是一样的,只是一些域扩展为 64 位值,它们包括:

  • ImageBase
  • SizeOfStackReserve
  • SizeOfStackCommit
  • SizeOfHeapRerserve
  • SizeOfHeapCommit

这些域在 64 位结构里被定义为 ULONGLONG 类型。

从 IMAGE_OPTIONAL_HEADER32 的定义可以看出,这个结构分为 基本域 部分和 附加域 部分,它的基本域含义如下:

Offset

Size

Field

Description

0

2

Magic

The unsigned integer that identifies the state of the image file. The most common number is 0x10B, which identifies it as a normal executable file. 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable.

2

1

MajorLinkerVersion

The linker major version number.

3

1

MinorLinkerVersion

The linker minor version number.

4

4

SizeOfCode

The size of the code (text) section, or the sum of all code sections if there are multiple sections.

8

4

SizeOfInitializedData

The size of the initialized data section, or the sum of all such sections if there are multiple data sections.

12

4

SizeOfUninitializedData

The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections.

16

4

AddressOfEntryPoint

The address of the entry point relative to the image base when the executable file is loaded into memory. For program images, this is the starting address. For device drivers, this is the address of the initialization function. An entry point is optional for DLLs. When no entry point is present, this field must be zero.

20

4

BaseOfCode

The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.

Magic 域是一个幻数值,在 WinNT.h 里定义了一些常量值:

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x107

  • 值 0x10b 说明这个 image 是 32 位的,PE 文件格式是 PE32
  • 值 0x20b 说明这个 image 是 64 位的,PE 文件格式是 PE32+

PE32+ 代表的扩展的 PE 文件格式,扩展为 64 位。在 PE 文件规范中并没有 PE64 这种文件格式,Microsoft 官方的判断 image 文件是 32 位还是 64 位的方法就是通过Magic 的值来确定。

在这些基本的域里可以获得 linker 的版本,text 节,data 节以及 bss 节的大小,

下面看一看 helloworld.exe 的 IMAGE_OPTIONAL_HEADER32 结构的基本域,从 0x00000108 - 0x0000011F

00000108  0B 01             // Magic
0000010A  0A                // MajorLinkerVersion
0000010B  00                // MinorLinkerVersion
0000010C  00 3C 00 00       // SizeOfCode
00000110  00 20 01 00       // SizeOfInitializeData
00000114  00 00 00 00       // SizeOfUninitializeData
00000118  0D 12 01 00       // AddressOfEntryPoint
0000011C  00 10 00 00       // BaseOfCode

Magic 是 0x010B 表明这个 image 文件是 32 位的 PE32 格式,这里看出 linker 的版本是 10.00

.text 节的 size 是 0x00003C00 bytes,.data 节的 size 是 0x00012000 bytes,还有一个重要的信息,代码的 RVA 入口在 0x0001120D,它是基于 ImageBase 的RVA 值。代码的基址在 0x00001000,这个是 RVA(Relative Virtual Address)值。

下面再来看一看 IMAGE_OPTIONAL_HEADER32 结构的附加域:

Offset

Size

Field

Description

24

4

BaseOfData

The address that is relative to the image base of the beginning-of-data section when it is loaded into memory.

28/24

4/8

ImageBase

The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.

32/32

4

SectionAlignment

The alignment (in bytes) of sections when they are loaded into memory. It must be greater than or equal to FileAlignment. The default is the page size for the architecture.

36/36

4

FileAlignment

The alignment factor (in bytes) that is used to align the raw data of sections in the image file. The value should be a power of 2 between 512 and 64 K, inclusive. The default is 512. If the SectionAlignment is less than the architecture’s page size, then FileAlignment must match SectionAlignment.

40/40

2

MajorOperatingSystemVersion

The major version number of the required operating system.

42/42

2

MinorOperatingSystemVersion

The minor version number of the required operating system.

44/44

2

MajorImageVersion

The major version number of the image.

46/46

2

MinorImageVersion

The minor version number of the image.

48/48

2

MajorSubsystemVersion

The major version number of the subsystem.

50/50

2

MinorSubsystemVersion

The minor version number of the subsystem.

52/52

4

Win32VersionValue

Reserved, must be zero.

56/56

4

SizeOfImage

The size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of SectionAlignment.

60/60

4

SizeOfHeaders

The combined size of an MS?DOS stub, PE header, and section headers rounded up to a multiple of FileAlignment.

64/64

4

CheckSum

The image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process.

68/68

2

Subsystem

The subsystem that is required to run this image. For more information, see “Windows Subsystem” later in this specification.

70/70

2

DllCharacteristics

For more information, see “DLL Characteristics” later in this specification.

72/72

4/8

SizeOfStackReserve

The size of the stack to reserve. Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached.

76/80

4/8

SizeOfStackCommit

The size of the stack to commit.

80/88

4/8

SizeOfHeapReserve

The size of the local heap space to reserve. Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached.

84/96

4/8

SizeOfHeapCommit

The size of the local heap space to commit.

88/104

4

LoaderFlags

Reserved, must be zero.

92/108

4

NumberOfRvaAndSizes

The number of data-directory entries in the remainder of the optional header. Each describes a location and size.

上面表格中的 offset 值两个,前面的是 IMAGE_OPTIONAL_HEADER32 的 offset 值,后面的是 IMAGE_OPTIONAL_HEADER64,这是因为在 64 位版本中一些域被扩展为 64 位值,而 BaseOfData 域在 64 位版中是不存在的。

下面是 helloworld.exe 的 IMAGE_OPTIONAL_HEADER32 剩余部分,从 0x00000120 - 0x000001E7

00000120  00 10 00 00           // BaseOfData
00000124  00 00 40 00           // ImageBase
00000128  00 10 00 00           // SectionAlignment 
0000012C  00 02 00 00           // FileAlignment
00000130  05 00                 // MajorOperatingSystemVersion
00000132  01 00                 // MinorOperatingSystemVersion
00000134  00 00                 // MajorImageVersion
00000136  00 00                 // MinorImageVersion
00000138  05 00                 // MajorSubsystemVersion
0000013A  01 00                 // MinorSubsystemVersion
0000013C  00 00 00 00           // Win32VersionVAlue
00000140  00 90 02 00           // SizeOfImage 
00000144  00 04 00 00           // SizeOfHeaders
00000148  00 00 00 00           // CheckSum
0000014C  02 00                 // Subsystem
0000014E  40 81                 // DllCharacteristics
00000150  00 00 10 00           // SizeOfStackReserve
00000154  00 10 00 00           // SizeOfStackCommit
00000158  00 00 10 00           // SizeOfHeapReserve
0000015C  00 10 00 00           // SizeOfHeapCommit
00000160  00 00 00 00           // LoaderFlags
00000164  10 00 00 00           // NumberOfRvaAndSizes

00000168  00 00 00 00           // DataDirectory[0] 
0000016C  00 00 00 00
00000170  00 80 01 00           // DataDirectory[1] 
00000174  50 00 00 00 

00000178  00 90 01 00           // DataDirectory[2] 
0000017C  1C E7 00 00

00000180  00 00 00 00           // DataDirectory[3] 
00000184  00 00 00 00 
00000188  00 00 00 00           // DataDirectory[4] 
0000018C  00 00 00 00
00000190  00 80 02 00           // DataDirectory[5] 
00000194  40 03 00 00 

00000198  20 57 01 00           // DataDirectory[6] 
0000019C  1C 00 00 00

000001A0  00 00 00 00           // DataDirectory[7] 
000001A4  00 00 00 00 
000001A8  00 00 00 00           // DataDirectory[8] 
000001AC  00 00 00 00
000001B0  00 00 00 00           // DataDirectory[9] 
000001B4  00 00 00 00 
000001B8  00 00 00 00           // DataDirectory[10] 
000001BC  00 00 00 00 
000001C0  00 00 00 00           // DataDirectory[11] 
000001C4  00 00 00 00 
000001C8  30 82 01 00           // DataDirectory[12] 
000001CC  E0 01 00 00 

000001D0  00 00 00 00           // DataDirectory[13] 
000001D4  00 00 00 00 
000001D8  00 00 00 00           // DataDirectory[14] 
000001DC  00 00 00 00
000001E0  00 00 00 00           // DataDirectory[15] 
000001E4  00 00 00 00

helloworld.exe 的 ImageBase 是 0x00400000,那么 helloworld.exe 映象的入口在:ImageBase + AddressOfEntryPoint = 0x00400000 + 0x0001120D =0x0041120D,这个地址是 _wWinMainCRTStartup() 的入口。

上面的 SectionAlinment 域值为 0x1000 是表示映象被加载到 virtual address 以是 0x1000(4K byte)为单位的倍数,也就是加载在 virtual address 的 4K 边界上。FileAlinment 域的值为 0x200 表示执行映象从 0x200 为边界开始加载到 virtual address 上。


2.3.1 Directory 表格

在 IMAGE_OPTIONAL_HEADER32 未端是一组 IMAGE_DATA_DIRECTORY 结构的数组,在 WinNT.h 里定义为:

//
// Directory format.
//

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

IMAGE_NUMBEROF_DIRECTORY_ENTRIES 的值为 16,因此有 16 个 Directory,Directory 结构中的 VirtualAddress 是一个 RVA 值。

这 16 个 Driectory 指引出 16 不同格式的 table,这 16 个 table 分别是:

表项
表格
0
export table
1
import table
2
resource table
3
exception table
4
certificate table
5
base relocation table
6
debug
7
architecute
8
global pointer
9
TLS table
10
load configuration table
11
bound import
12
import address table
13
delay import descriptor
14
CLR runtime header
15
reserved, must bo zero

在 WinNT.h 有对这些 table 的结构的全部定义。

在我们的实例 helloworld.exe 中使用了 5 个 Driectory,也就是使用了 5 个 Data table,它们是:

  • import table
  • resource table
  • base relocation table
  • debug table
  • import address table
域
import table
resource table
base relocation bale
debug table
import address table
VirtualAddress
0x00018000
0x00019000
0x00028000
0x00015720
0x00018230
size
0x50
0xE71C
0x340
0x1C
0x1E0

上面表格显示了 Directory 指示的 Data table 在 virtual address 上的位置

 

3. section 表

现在来看一看 helloworld.exe 的 section 表,从 0x000001E8 - 0x000002FF,共 280 bytes

这个节表结构在 WinNT.h 中定义为

//
// Section header format.
//

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SECTION_HEADER          40

每个 section 表的大小为 40 bytes,section 表的作用是指出 image 映象 section 所在

在 helloworld.exe 映象的 IMAGE_FILE_HEADER 结构的 NumberOfSections 域里已经指出 helloworld.exe 映象中包含有 7 个 sections,因此整个 section 表的大小为:280 bytes

helloworld.exe 的 section 表如下:

这 7 个 section 分别是:

  • .textbss 节
  • .text 节
  • .rdata 节
  • .data 节
  • .idata 节
  • .rsrc 节
  • .reloc 节
域
.textbss 节
.text 节
.rdata 节
.data 节
.idata 节
.rsrc 节
.reloc 节
VirtualSize
0x00010000
0x00003BDB
0x00001CD1
0x00000764
0x00000AAE
0x0000E71C
0x00000564
VirtualAddress
0x00001000
0x00011000
0x00015000
0x00017000
0x00018000
0x00019000
0x00028000
SizeOfRawData
0
0x00003C00
0x00001E00
0x00000200
0x00000C00
0x0000E800
0x00000600
PointerToRawData
0
0x00000400
0x00004000
0x00005E00
0x00006000
0x00006C00
0x00015400
PointerToRelocations
0
0
0
0
0
0
0
PointerToLinenumbers
0
0
0
0
0
0
0
NumberOfRelocations
0
0
0
0
0
0
0
NumberOfLinenumbers
0
0
0
0
0
0
0
Characteristics
0xE00000A0
0x60000020
0x40000040
0xC0000040
0xC0000040
0x40000040
0x42000040

IMAGE_SECTION_HEADER 结构里的 name 指出 section 的名字,这个 section 名 8 个节字长。

VirtualAddress 是一个基于 ImageBase 的 RVA 值,它指出 section 的所在,VirtualSize 指出 section 的大小,SizeOfRawData 是在 image 文件里占有的空间,它是 FileAlignment 的倍数,即:0x200 的倍数,也就是说 0x200 的边界。PointerToRawData 是 section 在 image 文件的位置,同样也是 FileAligment 即:0x200 边界上。

转:http://www.mouseos.com/windows/PE_image1.html
Windows可执行文件分析:从PE结构到安全运行的完整指南 可执行文件是计算机程序的核心载体,在Windows平台下采用PE(Portable Executable)格式标准。PE文件通过特定的数据结构组织代码和数据,包含DOS头、文件头、节区表等关键组成部分,操作系统据此将程序加载到内存执行。理解PE格式有助于分析程序行为、排查运行异常,在软件开发和系统安全领域具有重要价值。通过静态分析工具查看导入表、资源信息,结合动态监控手段观察文件操作和网络活动,可以安全地评估未知可执行文件。本文以实际案例演示如何使用C、C#、Go等语言编译生成EXE文件,并介绍在虚拟机环境 阅读详情

相关推荐

PE之手动分析PE文件结构

这是解析导入表函数列表的关键入口,后续可通过该RVA继续定位具体的导入函数信息。计算模块名称RVA相对于 .idata 节的内部偏移公式:”节内偏移 = 模块名称 RVA - .idata 节起始 RVA代入数据:0002188A(模块名称 RVA) - 00021000(.idata 节起始 RVA) = 0000088A“第一步:000213B0(导入目录 RVA) - 00021000(.idata 节起始 RVA) = 000003B0(RVA 相对于 .idata 节内的偏移)

Strategic__qq3610256651的博客 1324

windows PE Image 文件分析

转自:http://www.mouseos.com/windows/PE_image1.html 上面是 windows PE 可执行文件格式的结构图,分为 4 个部分:DOS 文件头、NT 文件头、Section 表以及 Directory 表格。 windows 的 executable image 文件使用的是这种 PE 格式,而 object 文件使用的是 COFF 文件格式。

ymzhou117的专栏 3777

windowsPE文件格式详解

PE是的简称,它是Windows系统上主要的可执行文件格式。我们常看到的.exe.dll等程序,就是这种格式的。PE格式也有32位与64位的区别,但64位并没有引入新的结构,只是简单的将原本的32位字段扩展为了64位而已。一个PE文件的结构大致如下图:注意是从下往上看的。本文主要是简单过了一下windows平台下可执行文件的数据结构,以及如何通过代码编程来获取这些结构。如果你对此感兴趣,建议去看一看书籍,我这里只算是带你入个门,省略了很多分析过程。

余识- 440

各类文件头标志大全

各类文件的文件头标志  参见  http://www.garykessler.net/library/file_sigs.html  扩展名 文件头标识(HEX) 文件描述 123 00 00 1A 00 05 10 04 Lotus 1-2-3 spreadsheet (v9) file 3gg;

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 1万+

PE文件结构分析工具PEview应用实战

PE(Portable Executable)文件格式是Windows操作系统下可执行文件的格式,它包含了一系列结构化的数据,这些数据对程序的加载、执行以及系统资源的管理至关重要。了解PE文件格式可以帮助开发者和安全研究人员深入理解程序的内部工作机制,便于进行恶意软件分析、软件逆向工程和安全防护。加壳技术是计算机安全领域的一个重要分支,主要用于软件保护。在深入探讨之前,我们需要明确什么是“壳”。

weixin_35751412的博客 1719

深入剖析PE文件格式与PEViewer使用指南

本文还有配套的精品资源,点击获取 简介:PEViewer是一款分析PE文件结构的工具,有助于深入理解Windows程序的工作原理。本简介探讨PE文件格式的核心组成部分,包括DOS头、NT头、节、导入表、导出表、资源表、异常处理和线程局部存储、重定位及调试信息。尽管PEViewer项目可能尚未完成,但它依然是学习PE格式的有价值资源。 1. PE文件格式概述 ...

weixin_35835030的博客 1875

1.PE文件之DOS头(IMAGE_DOS_HEADER)

IMAGE_DOS_HEADER结构是面对于16位程序的.现在大部分程序都是32或者64位的. 32或者64位的程序已经不使用IMAGE_DOS_HEADER结构里面的成员了,但是有两个成员还需使用. IMAGE_DOS_HEADER 结构及成员含义如下: //大小为: 0x40(64)字节 #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ typedef struct _IMAGE_DOS_HEADER { ..

kernelhack 9744

实战|验证码突破思路

漏洞成因:由于逻辑设计缺陷,可绕过验证,比如直接删除COOKIE或验证码参数可绕过、当验证不通过清空session时,验证码参数值为空时绕过等。测试方法:抓包,删除验证码字段,查看是否可以成功发送抓包,正常流程下,记录验证码后的数据包,替换目标包中内容,直接发送,查看是否可以直接绕过验证码案例:Step1.输入正确账户信息和错误验证码,登录时抓包Step2.删除COOKIEStep3.客户端登陆成功。

weixin_46239998的博客 8098

DOS头部IMAGE_DOS_HEADER

对于一个PE文件来说,最开始的位置就是一个DOS程序。DOS程序包含一个DOS头和一个DOS程序体。 DOS头部是由IMAGE_DOS_HEADER结构体来定义的。 该结构体定义如下: [cpp] view plain copy typedef struct _IMAE_DOS_HEADER {       //DOS .EXE he

482

数据埋点系列 3|数据质量保证:验证、清洗与管理

数据质量管理是一个持续的过程,需要技术和组织文化的共同支持。通过本文,我们深入探讨了数据质量保证的各个方面,从基本的验证和清洗技术,到高级的监控和自动化策略。主动的而非被动的自动化的而非手动的持续的而非一次性的全面的而非局部的随着数据在决策中的作用越来越重要,确保数据质量将成为每个数据驱动型组织的核心竞争力。通过实施本文中讨论的策略和技术,你将能够建立一个强大的数据质量保证体系,为你的组织带来更可靠、更有价值的数据洞察。

一个8年大数据开发工程师的碎碎念 1463

实战分享:如何用Jetson Nano+HM30图传搭建低空YOLOv5检测系统(附VNC避坑指南)

本文详细介绍了如何利用Jetson Nano和HM30图传搭建低空YOLOv5检测系统,重点解决了无人机应用中的硬件连接和VNC延迟优化问题。通过对比WiFi直连与图传方案的优劣,提供了网络配置、VNC优化及YOLOv5模型加速的实战技巧,帮助开发者实现高效稳定的低空目标检测系统。

weixin_29174385的博客 110

JBuilder2006打包的PE文件

 今天想浅谈一下JB打包的程序是如何运行的。众人皆知,在windows系统上一个程序要想运行就必须是PE格式的,我们又知道JAVA的类要想运行就必须要虚拟机的支持,(在JRE中就是将这个作为参数传递给java.exe).这两个要求是必须同时满足的,那么我们现在就来看看JBuilder2006中的打包功能是如何做到这点的。 首先启动JB2006,由于我的系统只有256M的内存,所以启动那

一花一世界 1521

COMSOL电磁仿真-网格剖分-边界层

介绍COMSOL在低频电磁场中边界层网格的剖分方法。

奇妙水果的技术博客,欢迎交流技术~ 4475

PE文件格式详解:深入理解Windows可执行文件结构

PE文件格式详解

weixin_62391735的博客 1221

vscode 配置 codeql

vscode 配置 codeql

SHELLCODE_8BIT的博客 1439

【零基础入门unity游戏开发——unity3D篇】unity CharacterController 3D角色控制器最详细的使用介绍,并实现俯视角、第三人称角色控制(附项目源码)

【unity小技巧】unity最完美的CharacterController 3d角色控制器,实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果,复制粘贴即用但是我发现很多人还是不理解,都跑来私信问我,所以决定重新做一个更加详细且简单控制器,主要是实现俯视角和第三人称角色控制。添加一个胶囊体,添加Character Controller组件,记得删除Capsule Collider组件,因为Character Controller自带了碰撞,所以不需要Character Controller参数介绍。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 5020

深入解析 PE 头结构:Windows 可执行文件的关键脉络

PE 头结构是 Windows 可执行文件的核心组成部分,它承载了大量关于文件的关键信息。通过本文的介绍和代码示例,相信大家对 PE 头结构有了更深入的理解。无论是对于系统开发、逆向工程,还是安全研究人员,掌握 PE 头结构的解析方法都是非常重要的。希望大家在今后的学习和实践中,能够继续深入探索 PE 文件的奥秘,挖掘更多有价值的信息。

m0_57836225的博客 738

Qt c++ llama.cpp 运行 LLaMA-3-8B 中文对话机器人(多模态输入)

助手:

寒寒豆豆软件公司的博客 219

可执行文件结构:PE文件结构讲解

我们使用电脑时肯定会接触各种各样的应用程序,有应用程序就会有对应的可执行文件,比如最常见的.exe文件。事实上,Windows平台下,所有的可执行文件(包括EXE、DLL、SYS、OCX、COM......)使用的都是同一种文件结构,也就是PE(Portable Executable)文件结构。所以说所有可执行文件究其本质都可以叫它PE文件。这里要注意一点:事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。 下面就来结合事例详细介绍一下PE文件到底是个什么东西。

qq_46145027的博客 3092

全网最全-QCC51xx-QCC30xx(TWS)系列从入门到精通开发教程汇总(持续更新中)

QCC3040开发笔记之基本介绍(0) QCC3040开发笔记之开发环境搭建(1) QCC3040开发笔记之debug调试(trb调试,usbt调试,adb调试,WIFI调试)(2) QCC3040开发笔记之按键配置(3) QCC3040开发笔记之按键切换(4) QCC3040开发笔记之按键逻辑(5) QCC3040开发笔记之tone(6) QCC3040开发笔记之提示音(7) QCC3040开发笔记之bitserial接口(8) QCC3040开发笔记之双mic+AEC+ANC(9...

心跳包的博客 3万+

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Win32汇编PE结构剖析(6)

title: Win32汇编PE结构剖析(6) date: 2019-06-25 09:34:16 tags: 汇编语言 categories: 汇编语言 copyright: true --- 汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言.在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用...

weixin_30568715的博客 114

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【《程序员的自我修养---链接装载于库》读书笔记】windows PE/COFF

介绍了windows下的可执行文件和目标文件格式PE/COFF。

qq_41531474的博客 1116

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

PE结构 ---> 1. 什么是PE 什么是PE结构 什么是windows可执行程序结构

Windows PE文件结构解析 PE(Portable Executable)是Windows平台可执行文件的标准格式,包括.exe、.dll等文件类型。PE文件由DOS头、PE签名、COFF头、可选头、节表和实际数据区组成,每个部分都有严格定义的结构。关键结构包括:DOS头中的e_lfanew指向PE签名;COFF头定义文件基本属性;可选头包含入口点、基址等关键信息;节表描述各节区属性。PE文件通过这种层次化结构,使Windows加载器能准确识别代码、数据和所需资源的位置,确保程序正确加载执行。相比Li

盾悟 1万+

PE Tools深度解析:逆向工程师的Windows可执行文件瑞士军刀

在Windows逆向工程和安全分析领域,PE Tools犹如一把经过二十年淬炼的瑞士军刀,集成了从基础PE文件分析到高级进程调试的全套功能。这款自2002年诞生至今的经典工具,见证了Windows安全生态的演变,成为无数安全研究人员和逆向工程师的必备利器。 ## 技术架构解析:如何构建一个完整的PE分析平台 ### PE文件格式的深度理解 PE Tools的核心价值在于对PE(Portabl

gitblog_00705的博客 384

PeNet:轻松解析Windows可移植可执行文件的利器

PeNet:轻松解析Windows可移植可执行文件的利器 项目介绍 PeNet是一款专为Windows可移植可执行文件(PE文件)设计的解析工具,完全使用C#编写,无需依赖任何Windows原生API。它不仅能够解析PE文件的头部信息,还支持生成导入哈希(ImpHash),这是恶意软件分析中常用的功能。此外,PeNet还能提取证书吊销列表、计算多种哈希值,以及其他与PE文件相关的实用功能。 项目技...

gitblog_00832的博客 818
上一篇: Visual Studio, Microsoft Portable Executable and Common Object File Format Specification
下一篇: Mach-O可执行文件格式
scribbler
博客等级 码龄19年 20粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值