栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

PE结构(DOS,NT,SectionHeader 不含 DataDirectory)一

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

PE结构(DOS,NT,SectionHeader 不含 DataDirectory)一

PE结构:所有的可执行文件都要满足PE文件建构

我这里就以内存图的形式展示一下pe结构

 上图为pe的简单内存结构图

1.DosHeader  结构  这里我仅说明我常用到的成员。

struct _IMAGE_DOS_HEADER {
0x00 WORD e_magic;                // 记录是否是可执行文件
0x02 WORD e_cblp;
0x04 WORD e_cp;
0x06 WORD e_crlc;
0x08 WORD e_cparhdr;
0x0a WORD e_minalloc;
0x0c WORD e_maxalloc;
0x0e WORD e_ss;
0x10 WORD e_sp;
0x12 WORD e_csum;
0x14 WORD e_ip;
0x16 WORD e_cs;
0x18 WORD e_lfarlc;
0x1a WORD e_ovno;
0x1c WORD e_res[4];
0x24 WORD e_oemid;
0x26 WORD e_oeminfo;
0x28 WORD e_res2[10];
0x3c DWORD e_lfanew;                // 记录PE标准头开始位置的偏移
};
2.NTHeader结构
struct _IMAGE_NT_HEADERS {
0x00 DWORD Signature;                              // pe签名
0x04 _IMAGE_FILE_HEADER FileHeader;                // 标准pe头
0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;        // 可选PE头
};
        2.1 FileHeader 标准PE头         
struct _IMAGE_FILE_HEADER {
0x00 WORD Machine;                   // 机器型号
0x02 WORD NumberOfSections;          // 节表数量
0x04 DWORD TimeDateStamp;            // 时间戳
0x08 DWORD PointerToSymbolTable;
0x0c DWORD NumberOfSymbols;
0x10 WORD SizeOfOptionalHeader;      // 可选pe头大小
0x12 WORD Characteristics;           // 特征码,每个位代表一个含义
};

        2.2 OptionalHeader 可选PE头

struct _IMAGE_OPTIONAL_HEADER {
0x00 WORD Magic;
0x02 BYTE MajorLinkerVersion;
0x03 BYTE MinorLinkerVersion;
0x04 DWORD SizeOfCode;                // 所有代码节的和 FileAlignment对齐后的大小
0x08 DWORD SizeOfInitializedData;     // 已经初始化数据大小的和 FileAlignment对齐后的大小
0x0c DWORD SizeOfUninitializedData;   // 未初始化数据大小的和  FileAlignment对齐后的大小
0x10 DWORD AddressOfEntryPoint;       // 程序入口地址偏移量
0x14 DWORD BaseOfCode;                // 代码开始的基址
0x18 DWORD BaseOfData;                // 数据开始的基址
0x1c DWORD ImageBase;                 // 内存镜像的基址
0x20 DWORD SectionAlignment;          // 内存对齐大小
0x24 DWORD FileAlignment;             // 文件对齐大小
0x28 WORD MajorOperatingSystemVersion;
0x2a WORD MinorOperatingSystemVersion;
0x2c WORD MajorImageVersion;
0x2e WORD MinorImageVersion;
0x30 WORD MajorSubsystemVersion;
0x32 WORD MinorSubsystemVersion;
0x34 DWORD Win32VersionValue;
0x38 DWORD SizeOfImage;                // 内存中整个pe的小小
0x3c DWORD SizeOfHeaders;              // 所有头+节表的大小, 按照文件对齐都的大小
0x40 DWORD CheckSum;
0x44 WORD Subsystem;
0x46 WORD DllCharacteristics;
0x48 DWORD SizeOfStackReserve;
0x4c DWORD SizeOfStackCommit;
0x50 DWORD SizeOfHeapReserve;
0x54 DWORD SizeOfHeapCommit;
0x58 DWORD LoaderFlags;
0x5c DWORD NumberOfRvaAndSizes;
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16];
};

3.节表 一个PE文件有多个节表每个节表都是内存连续的 节表结构如下

typedef struct _IMAGE_SECTION_HEADER {
0x00 BYTE Name[IMAGE_SIZEOF_SHORT_NAME];        // 节的名字
union {
0x08 DWORD PhysicalAddress;
0x08 DWORD VirtualSize;
} Misc;                                        // 联合 一般都是真实数据的大小
0x0c DWORD VirtualAddress;                     // 节在内存中的地址偏移
0x10 DWORD SizeOfRawData;                      // 节在文件中对齐后的大小
0x14 DWORD PointerToRawData;                   // 节在文件中的地址偏移
0x18 DWORD PointerToRelocations;
0x1c DWORD PointerToLinenumbers;
0x20 WORD NumberOfRelocations;
0x22 WORD NumberOfLinenumbers;
0x24 DWORD Characteristics;                    // 特征码  具体代表意思看下图
};

节表的特征码代表含义看下图

除了 DataDirectory 结构大概就是这样。然后下图是在 内存中 的关系图 

 下面代码实践。

1.一个从执行文件把二进制写入到内存,

2.然后将读取到的信息拉伸后写入新的一块内存

3.将MessageBox的地址写入代码区

4.将拉伸后的内存 去掉拉伸后的内存形式写入 一块新内存

5.将新的内存写入一个新的 exe文件

PeStructPractise.h

#pragma once

DWORD ReadPEFile(const char* lpszFile, PVOID* pFileBuffer);

// 将filebuff 拉伸复制到  imagebuff
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer);

// 将imagebuff复制到   newbuff
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer, PVOID* pNewBuffer);

// 在.txt添加代码
BOOL WriteNewCodeInImageBuffCode(LPVOID* pImageBuff);

BOOL MemeryTOFile(PVOID pMemBuffer, DWORD size);

PeStructPractise.cpp

#include 
#include 
#include 

#include "PeStructPractise.h"


DWORD ReadPEFile(const char* lpszFile, PVOID* pFileBuffer)
{
  FILE* pFile = NULL;
  DWORD fileSize = 0;
  //打开文件	
  DWORD ret = fopen_s(&pFile, lpszFile, "rb");
  if (ret)
  {
    printf(" 无法打开 EXE 文件! ");
    return 0;
  }
  //读取文件大小		
  fseek(pFile, 0, SEEK_END);
  fileSize = ftell(pFile);
  fseek(pFile, 0, SEEK_SET);
  //分配缓冲区	
  *pFileBuffer = malloc(fileSize);

  if (!*pFileBuffer)
  {
    printf(" 分配空间失败! ");
    fclose(pFile);
    return 0;
  }
  //将文件数据读取到缓冲区	
  size_t n = fread(*pFileBuffer, fileSize, 1, pFile);
  if (!n)
  {
    printf(" 读取数据失败! ");
    free(*pFileBuffer);
    fclose(pFile);
    return 0;
  }
  //关闭文件	
  fclose(pFile);
  return fileSize;
}

DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
  PIMAGE_DOS_HEADER pDocHead = (PIMAGE_DOS_HEADER)pFileBuffer;
  PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((PBYTE)pFileBuffer + pDocHead->e_lfanew);
  PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((PBYTE)pNtHead + 4);
  PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)pFileHeader + sizeof(IMAGE_FILE_HEADER));

  // 复制  sizeofheader 到 imgebuff
  *pImageBuffer = malloc(pOptionalHeader->SizeOfImage);
  DWORD nSizeOfImage = pOptionalHeader->SizeOfImage;				// pe内存映射的总大小
  if (!*pImageBuffer)
  {
    printf(" 分配空间失败! ");
    return 0;
  }
  DWORD nSizeOfHeader = pOptionalHeader->SizeOfHeaders;			// pe头 + 所有节表  文件对齐后的大小
  memset(*pImageBuffer, 0, nSizeOfImage);
  memcpy_s(*pImageBuffer, nSizeOfHeader, pFileBuffer, nSizeOfHeader);

  // 复制节表
  DWORD nCopySize = 0;
  PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((PBYTE)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
  for (DWORD i = 0; i < pFileHeader->NumberOfSections; i++)
  {
    DWORD nPointToRawData = pSectionHeader->PointerToRawData;		// 节表在文件中的地址偏移
    DWORD nVirtualAddress = pSectionHeader->VirtualAddress;		// 节表在内存中的地址偏移
    DWORD nSizeOfRawData = pSectionHeader->SizeOfRawData;		// 节表对齐后的大小
    memcpy_s((PBYTE)*pImageBuffer + nVirtualAddress, nSizeOfRawData, (PBYTE)pDocHead + nPointToRawData, nSizeOfRawData);
    pSectionHeader += 1;
    nCopySize = nVirtualAddress + nSizeOfRawData;
  }
  return nCopySize;
}

DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer, PVOID* pNewBuffer)
{
  PIMAGE_DOS_HEADER pDocHead = (PIMAGE_DOS_HEADER)pImageBuffer;
  PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((PBYTE)pImageBuffer + pDocHead->e_lfanew);
  PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((PBYTE)pNtHead + 4);
  PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)pFileHeader + sizeof(IMAGE_FILE_HEADER));

  // 复制  sizeofheader 到 imgebuff
  DWORD nSizeOfHeader = pOptionalHeader->SizeOfHeaders;			// pe头 + 所有节表  文件对齐后的大小
  DWORD nSizeOfImage = pOptionalHeader->SizeOfImage;				// pe内存映射的总大小
  *pNewBuffer = malloc(nSizeOfImage);
  if (!*pNewBuffer)
  {
    printf(" 分配空间失败! ");
    return 0;
  }
  memset(*pNewBuffer, 0, nSizeOfImage);
  memcpy_s(*pNewBuffer, nSizeOfHeader, pImageBuffer, nSizeOfHeader);

  // 复制节表 节表开始的位置
  PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((PBYTE)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
  DWORD nCopySize = 0;
  for (DWORD i = 0; i < pFileHeader->NumberOfSections; i++)
  {
    DWORD nPointToRawData = pSectionHeader->PointerToRawData;		// 节表在文件中的地址偏移
    DWORD nVirtualAddress = pSectionHeader->VirtualAddress;		// 节表在内存中的地址偏移
    DWORD nSizeOfRawData = pSectionHeader->SizeOfRawData;	  // 节表对齐后的数据大小
    memcpy_s((PBYTE)*pNewBuffer + nPointToRawData, nSizeOfRawData, (PBYTE)pDocHead + nVirtualAddress, nSizeOfRawData);
    pSectionHeader += 1;
    nCopySize = nPointToRawData + nSizeOfRawData;
  }
  return nCopySize;
}

char CodeMessageBox[] = {
  0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00,
  0xE8, 0x00, 0x00, 0x00, 0x00,
  0xE9, 0x00, 0x00, 0x00, 0x00
};


// 加入代码
BOOL WriteNewCodeInImageBuffCode(LPVOID* pImageBuff)
{
  PIMAGE_DOS_HEADER pDocHead = (PIMAGE_DOS_HEADER)*pImageBuff;
  PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((PBYTE)*pImageBuff + pDocHead->e_lfanew);
  PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((PBYTE)pNtHead + 4);
  PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)pFileHeader + sizeof(IMAGE_FILE_HEADER));
  PIMAGE_SECTION_HEADER pSectionHead = (PIMAGE_SECTION_HEADER)((PBYTE)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);			// 节表开始的位置
  WORD nNumberOfSections = pFileHeader->NumberOfSections;

  DWORD nImageBase = pOptionalHeader->ImageBase;
  DWORD nPointToRawData = pSectionHead->PointerToRawData;		// 节表在文件中的地址偏移
  DWORD nVirtualAddress = pSectionHead->VirtualAddress;		// 节表在内存中的地址偏移
  DWORD nMisc = pSectionHead->Misc.VirtualSize;							// 节表中真实数据的大小
  DWORD nSizeOfRawData = pSectionHead->SizeOfRawData;	  // 节表对齐后的数据大小
  DWORD nEntryOfPoint = pOptionalHeader->AddressOfEntryPoint;	// 入口偏移
  
  DWORD nNewCodeSize = sizeof(CodeMessageBox);
  if ((nSizeOfRawData - nMisc) > nNewCodeSize)
  {
    DWORD nBeginAddress = nVirtualAddress + nMisc;
    //0x75E90D60 是MessageBoxA的地址,电脑重启和其他电脑都会变,可用x64dlg查找。使用方法看后面的
    DWORD nDealNewCodeAdress = 0x75E90D60 - (nImageBase + nBeginAddress + 8 + 5);
    memcpy_s(CodeMessageBox + 9, 4, &nDealNewCodeAdress, 4);
    DWORD nMainCodeAddress = (nImageBase + nEntryOfPoint) - (nImageBase + nBeginAddress + 13 + 5);
    memcpy_s(CodeMessageBox + 14, 4, &nMainCodeAddress, 4);
    memcpy_s((PBYTE)pDocHead + nBeginAddress, nNewCodeSize, CodeMessageBox, nNewCodeSize);
    pOptionalHeader->AddressOfEntryPoint = nBeginAddress;
    return true;
  }
  return false;
}

BOOL MemeryTOFile(PVOID pMemBuffer, DWORD size)
{
  FILE* stream;
  fopen_s(&stream, ".\test.exe", "wb+");
  fwrite(pMemBuffer, size, 1, stream);
  fclose(stream);
  return true;
}

main.cpp

#include 
#include 
#include "PeStructPractise.h"
int main()
{
  std::string szExePath = "D:\PETool 1.0.0.5.exe";     // 找一个只有exe就可以正常执行的文件
  PVOID pFileBuff = nullptr;
  int ret = ReadPEFile(szExePath.c_str(), &pFileBuff);
  if (ret == 0)
  {
    printf("ReadPEFile failed, szExePath:%sn", szExePath.c_str());
    return 0;
  }
  else
  {
    printf("ReadPEFile sucess, szExePath:%s, nLen:%dn", szExePath.c_str(), ret);
  }

  PVOID pImageBuff = nullptr;
  ret = CopyFileBufferToImageBuffer(pFileBuff, &pImageBuff);
  free(pFileBuff);
  pFileBuff = nullptr;

  // 写入.text节
  ret = WriteNewCodeInImageBuffCode(&pImageBuff);

  PVOID pNewBuff = nullptr;
  ret = CopyImageBufferToNewBuffer(pImageBuff, &pNewBuff);
  MemeryTOFile(pNewBuff, ret);

  free(pImageBuff);
  free(pNewBuff);
  pNewBuff = nullptr;
  pImageBuff = nullptr;
  return 0;
}

注意: 

1.exe你要弄一个可单独执行的exe

2.添加进去的代码 是要你自己电脑上MessageBoxA的地址加上去。  可通过Ob 或者x64dlg查看如下图

写入代码前效果打开后直接是这样

写入代码后效果打开后先弹窗  再打开 petool如下图

 

 

 

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1008816.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号