Windows PE权威指南-学习记录

关键词: Windows 状态: 进行中

运行环境

Windows
环境变量
path = e:\masm32\bin
include = e:\masm32\include
lib = e:\masm32\lib

第一个PE程序

.386

.model flat,stdcall
option casemap:none

;數據段 此处环境变量未配置成功所以全路径
include e:\masm32\include\windows.inc
include e:\masm32\include\user32.inc
includelib e:\masm32\lib\user32.lib
include e:\masm32\include\kernel32.inc
includelib e:\masm32\lib\kernel32.lib

;數據段
.data
szText db 'helloworld',0

;代碼段
.code
start:
invoke MessageBox,NULL,offset szText,NULL,MB_OK
invoke ExitProcess,NULL
end start
.386
;使用寄存器

.model flat,stdcall
option casemap:none

include e:\masm32\include\windows.inc
include e:\masm32\include\user32.inc
includelib e:\masm32\lib\user32.lib
include e:\masm32\include\kernel32.inc
includelib e:\masm32\lib\kernel32.lib

.data
szTitle db 'title',0
szText db 'helloworld',0

.code
start:
push 0
lea eax,szTitle
push eax
lea eax,szText
push eax
push 0
call MessageBox
push 0
call ExitProcess

end start
e:\masm32\bin\ml -c -coff new.asm
;编译生成obj文件 -c表示独立编译,不进行链接,-coff表示编译后生成标准coff目标文件
e:\masm32\bin\link -subsystem:windows new.obj
;subsystem表示允许运行的系统,无错误即生成可执行文件exe

/images/Windows_PE_Learning/Untitled.png

此处new为new.exe的进程缩写,0x00403000为虚拟内存地址

  • vscode二进制查看十六进制
ctrl+P
ext install slevesque.vscode-hexdump
右键文件Show Hexdump

十六进制文件说明

/images/Windows_PE_Learning/Untitled%201.png

PE头部 0x0000

/images/Windows_PE_Learning/Untitled%202.png

代码段 0x0400

/images/Windows_PE_Learning/Untitled%203.png

引入函数 0x0600

/images/Windows_PE_Learning/Untitled%204.png

数据段 0x0800

/images/Windows_PE_Learning/Untitled%205.png

PE 的地址

  • 虚拟内存地址 VA
    • 操作系统在程序运行时,假设让一个程序独立拥有4G内存,4G按照固定大小等成1M个页,页一部分与物理内存一一对应,无法对应的分配虚拟内存,对应关系记录在windows的pagefile.sys中,用于交换内存。
    • PE文件被加载进内存中后,分配了4G的独立虚拟空间,这个空间的定位地址为VA。进程本身的VA被解释为:进程的基地址+相对虚拟内存地址。
  • 相对虚拟内存地址 RVA
    • 相对于基地址的偏移,一般针对特定模块存在。
  • 文件偏移地址FOA

  • 特殊地址

数据结构

  • 数据目录(在PE头文件中)

    PE中有一个数据结构称为数据目录,其中记录了所有可能的数据类型。这些类型中,目前已定义的有15种,包括导出表、导入表、资源表、异常表、属性证书表、重定位表、调试数据、Architecture、Global Ptr、线程局部存储、加载配置表、绑定导入表、IAT、延迟导入表和CLR运行时头部。

  • 对齐

    32为系统4k对齐,64位8k对齐

尝试CCF Explorer 将shellcode打包至PE文件

https://www.ired.team/offensive-security/code-injection-process-injection/backdooring-portable-executables-pe-with-shellcode

CFF用法及相关功能介绍

/images/Windows_PE_Learning/Untitled%206.png

VituralSize映射到内存中的虚拟地址长度

Vitural Address 映射到内存中的虚拟地址

RAW address 在文件中的位置

RAW Size 在文件中的长度

Characteristics 属性(LordPE中叫做section Flags)

下图为LordPE工具,与CFF类似,叫法不同

/images/Windows_PE_Learning/Untitled%207.png

插入newdata区块后保存程序无法运行

此时文件大小不对,

第一步,使用winhex等工具插入16进制的200字节,相当于十进制512字节

/images/Windows_PE_Learning/Untitled%208.png

第二步、在optional header中将sizeofimage+1000,此处1000为映射入内存的大小也就是设置中的vituralsize

/images/Windows_PE_Learning/Untitled%209.png

计算方式也可以看下图,最后一节的内存地址加上地址大小

/images/Windows_PE_Learning/Untitled%2010.png

修正后保存程序即可运行

OD分析修改过的文件

打开文件

/images/Windows_PE_Learning/Untitled%2011.png

此时00401000为程序的入口地址=00400000+AddressOfEntryPoint(入口点地址)

/images/Windows_PE_Learning/Untitled%2012.png

要想程序从自己的代码入口点地址,修改指针为.new的虚拟地址

/images/Windows_PE_Learning/Untitled%2013.png

修改后入口变为.new

/images/Windows_PE_Learning/Untitled%2014.png

OD调试入口点已修改

/images/Windows_PE_Learning/Untitled%2015.png

尝试插入汇编程序

选中地址中hex数据,改写ascii,此时会改变汇编代码块,此时代码是无法执行的假代码,真实有意义的内容是hex数据,假代码无意义

/images/Windows_PE_Learning/Untitled%2016.png

编写汇编代码

push 0
push 404030
push 404020
push 0
call MessageBoxA
jmp 401000跳转到原入口函数

/images/Windows_PE_Learning/Untitled%2017.png

F8单步执行

/images/Windows_PE_Learning/Untitled%2018.png

保存文件,选中所有修改过的代码

/images/Windows_PE_Learning/Untitled%2019.png

/images/Windows_PE_Learning/Untitled%2020.png

即可完成修改,程序即可按照修改运行

用Winhex对比可执行文件在文件和内存中的差异

.rdata区块内存和文件中有差异,.text.data中数据无差异

/images/Windows_PE_Learning/Untitled%2021.png

/images/Windows_PE_Learning/Untitled%2022.png

查看PE头

由于字节序,地址处数据在查看器中按字节倒序

/images/Windows_PE_Learning/Untitled%2023.png

/images/Windows_PE_Learning/Untitled%2024.png

DOS 头

typedef struct _IMAGE_DOS_HEADER {      // 
    WORD   e_magic;                     // 0000h MZ标识
    WORD   e_cblp;                      // 0002h 最后(部分)页的字节数
    WORD   e_cp;                        // 0004h 文件中的全部和部分页数
    WORD   e_crlc;                      // 0006h 重定位表中的指针数
    WORD   e_cparhdr;                   // 0008h 头部尺寸,以段落为单位
    WORD   e_minalloc;                  // 000Ah 所需的最小附加段
    WORD   e_maxalloc;                  // 000Ch 所需的最大附加段
    WORD   e_ss;                        // 000Eh 初始的SS值
    WORD   e_sp;                        // 0010h 初始的SP值
    WORD   e_csum;                      // 0012h 校验和
    WORD   e_ip;                        // 0014h 初始的IP值
    WORD   e_cs;                        // 0016h 初始的CS值
    WORD   e_lfarlc;                    // 0018h 重定位表的字节偏移量
    WORD   e_ovno;                      // 001Ah 覆盖号
    WORD   e_res[4];                    // 001Ch 保留字
    WORD   e_oemid;                     // 0024h OEM标识符
    WORD   e_oeminfo;                   // 0026h OEM信息
    WORD   e_res2[10];                  // 0028h 保留字
    LONG   e_lfanew;                    // 003Ch 对应PE头相对于文件的偏移地址
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

WORD 两字节 LONG 4字节

e_lfanew 对应PE头相对于文件的偏移地址

/images/Windows_PE_Learning/Untitled%2025.png

在windows中以下数据无关紧要

/images/Windows_PE_Learning/Untitled%2026.png

/images/Windows_PE_Learning/Untitled%2027.png

十进制64字节

#include <stdio.h>
#include <windows.h>

int main()
{
	printf("%d %x\r\n",sizeof(IMAGE_DOS_HEADER), sizeof(IMAGE_DOS_HEADER)); //%d十進制 %x十六進制
	return 0;
}

判断是否为PE文件

#include "framework.h"
#include "Project2.h"
#include <windows.h>
#include "resource.h"

INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT UMsg, _In_ WPARAM wParam, _In_ LPARAM IParam);
BOOL IsPE(LPVOID lpBase);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	DialogBox(hInstance, MAKEINTRESOURCE(MAIN_Window), NULL, &DialogProc); //啓動對話框
	return 0;
}

INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_  UINT UMsg, _In_  WPARAM wParam, _In_ LPARAM IParam) //對話框處理函數
{

	if (UMsg == WM_CLOSE) { //UMsg按鈕事件
		EndDialog(hwndDlg, NULL); //hwndDlg程序句柄
	}
	//所有界面上的按鈕事件都是走這個WM_COMMAND宏
	if (UMsg == WM_COMMAND) {

		if (wParam == OPEN_BUTTON) { //wParam 控件ID

			//從編輯框獲取文件路勁和文件名
			char FileLo[MAX_PATH] = {0};
			GetDlgItemText(hwndDlg,FILE_LO, FileLo, MAX_PATH);
			//MessageBox(NULL, FileLo, "Info", 0);

			//打開文件
			HANDLE FileHw = CreateFile(FileLo, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

			//創建一個内存映射内核對象
			HANDLE FileHwMap = CreateFileMapping(FileHw, NULL, PAGE_READONLY,0,0,NULL);

			//將文件映射機内内存
			LPVOID lpBase = MapViewOfFile(FileHwMap,FILE_MAP_READ,0,0,0);
			if (lpBase == NULL) {
				MessageBox(NULL, "文件打開/映射失敗", "Info", 0);
				return FALSE;
			}

			//判斷是否為PE文件
			if (IsPE(lpBase)) {
				MessageBox(NULL, "所選為PE文件", "Info", 0);
			}
			else {
				MessageBox(NULL, "所選文件非PE文件", "Info", 0);
			}

			//釋放内存映射
			UnmapViewOfFile(lpBase);
			//關閉内存映射内核對象
			CloseHandle(FileHwMap);
			//關閉文件
			CloseHandle(FileHw);
		}
	}
	return FALSE;
}

BOOL IsPE(LPVOID lpBase) {
	PIMAGE_DOS_HEADER pImgDosHeader = NULL;
	PIMAGE_NT_HEADERS pImgNtHeader = NULL;
	pImgDosHeader = (PIMAGE_DOS_HEADER)lpBase;
	pImgNtHeader = (PIMAGE_NT_HEADERS)((DWORD)lpBase+(DWORD)pImgDosHeader->e_lfanew);
	if (pImgDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
		return FALSE;
	}
	if (pImgNtHeader->Signature != IMAGE_NT_SIGNATURE) {
		return FALSE;
	}
	return TRUE;
}

NT_HEADERS

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

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;  //0004h 运行平台 i386(0x014c) amd64
    WORD    NumberOfSections; //0006h PE中节的数量 最大值96个节
    DWORD   TimeDateStamp; //0008h 文件创建时间,编译器创建文件的时间戳(无关)
    DWORD   PointerToSymbolTable; //000Ch 指向符号表(用于调试)
    DWORD   NumberOfSymbols; //0010h 符号表中符号数量(用于调试)
    WORD    SizeOfOptionalHeader; //0014h 可选头结构体长度 E0 F0
    WORD    Characteristics; //0016h 文件属性,由不同属性相加
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

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

    WORD    Magic; //0018h 魔术字 107h ROM Image 10Bh EXE 20Bh PE32+/EXE64
    BYTE    MajorLinkerVersion; //001Ah 连接器版本号
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode; //001Ch 所有代码节大小
    DWORD   SizeOfInitializedData; //0020h 初始化节大小
    DWORD   SizeOfUninitializedData; //0024h 未初始化数据节大小
    DWORD   AddressOfEntryPoint; //0028h 程序执行入口RVA,对于驱动程序为初始化地址
    DWORD   BaseOfCode; //002Ch 代码的节的起始RVA
    DWORD   BaseOfData; //0030h 数据节的起始RVA

    //
    // NT additional fields.
    //

    DWORD   ImageBase; //0034h 建议的存储空间
    DWORD   SectionAlignment;//0038h 内存中的节的对齐值 一般为4k
    DWORD   FileAlignment;//003Ch  文件中的节的对齐值 0x1000 0x200 512字节 刚好为1山区
    WORD    MajorOperatingSystemVersion; //0040h 操作系统版本号
    WORD    MinorOperatingSystemVersion; //0042h
    WORD    MajorImageVersion; //0044h PE版本号
    WORD    MinorImageVersion; //0046h
    WORD    MajorSubsystemVersion; //0048h 所需子系统的版本号
    WORD    MinorSubsystemVersion; //004Ah
    DWORD   Win32VersionValue; //004Ch 未使用,必须为0
    DWORD   SizeOfImage; //0050h 内存中整个PE文件的映像大小(按照内存对齐)
    DWORD   SizeOfHeaders; //0054h 所有头+节表大小
    DWORD   CheckSum; //0058h 校验和(EXE为0,DLL和SYS[内核驱动]必须为正确值)
    WORD    Subsystem; //005Ch 文件子系统 2为GUI,3为命令行
    WORD    DllCharacteristics; //005Eh dll文件特定
    DWORD   SizeOfStackReserve; //0060h 初始化时保留的栈区大小 (默认1M)
    DWORD   SizeOfStackCommit; //0064h 初始化时实际提交栈大小(默认4k)
    DWORD   SizeOfHeapReserve; //0068h 初始化时保留的堆大小(默认1M)
    DWORD   SizeOfHeapCommit; //006Ch 初始化实际提交的堆大小(默认4k)
    DWORD   LoaderFlags; //0070h 加载标志一般为0
    DWORD   NumberOfRvaAndSizes; //0074h 数据目录数量
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //0078h数据目录数组
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress; //虚拟地址
    DWORD   Size; //虚拟地址大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   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;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

Characteristics属性

/images/Windows_PE_Learning/Untitled%2028.png

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved external 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  // Aggressively 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.
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; //节名称
    union {
            DWORD   PhysicalAddress; //
            DWORD   VirtualSize; //节区尺寸
    } Misc;
    DWORD   VirtualAddress; //节起始RVA地址
    DWORD   SizeOfRawData; //在文件中对齐的尺寸
    DWORD   PointerToRawData; //该节在文件中的起始偏移
    DWORD   PointerToRelocations; //在obj文件中使用
    DWORD   PointerToLinenumbers; //行号表的位置(仅供调试)
    WORD    NumberOfRelocations; //在OBJ文件中使用
    WORD    NumberOfLinenumbers; //行号表中行号的数量
    DWORD   Characteristics; //节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SECTION_HEADER          40

PE导入表

在内存中情况

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // 指向的是INT表Import Name Table,保存的是所有导入函数名称的RVA RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;                           // 保存的是RVA,RVA指向的内容是DLL的文件名
    DWORD   FirstThunk;                     // 指向的是IAT表,Import Address Table,这个表保存的是所有导入函数的地址(VA)RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

在文件中OriginalFirstThunk FirstThunk相同

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // 指向的是INT表Import Name Table,保存的是所有导入函数名称的RVA RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;                           // 保存的是RVA,RVA指向的内容是DLL的文件名
    DWORD   FirstThunk;                     // 指向的是INT表Import Name Table,保存的是所有导入函数名称的RVA(if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

手动将shellcode打包至正常PE程序

新增区块reso

/images/Windows_PE_Learning/Untitled%2029.png

/images/Windows_PE_Learning/Untitled%2030.png

原程序入口

写shellcode

https://idafchev.github.io/exploit/2017/09/26/writing_windows_shellcode.html

comments powered by Disqus