设为首页 加入收藏

TOP

基于VC++实现PE的修改编程(一)
2013-07-22 18:12:48 来源: 作者: 【 】 浏览:321
Tags:基于 实现 修改 编程

  在Windows系统下的可执行文件的一种(还有NE、LE),是微软设计、TIS(Tool Interface Standard,工具接口标准)委员会批准的一种可执行文件格式。PE的意思是Portable Executable(可移植可执行)。所有Windows下的32位或64位可执行文件都是PE文件格式,其中包括DLL、EXE、FON、OCX、LIB和部分SYS文件。

  DOS-stub(DOS-头)

  DOS-根的概念很早从16位windows的可执行文件(当时是“NE”格式)时就广为人知了。根原来是用于OS/2系统的可执行文件的,也用于自解压档案文件和其它的应用程序。对于PE文件来说,它是一个总是由大约100个字节所组成的和MS-DOS 2.0兼容的可执行体,用来输出象“This program needs windows NT”之类的错误信息。

  你可以通过确认DOS-头部分是否为一个IMAGE_DOS_HEADER(DOS头)结构来认出DOS-根,它的前两个字节必须为连续的两个字母“MZ”(有一个#define IMAGE_DOS_SIGNATURE的定义是针对这个WORD单元的)。

  你可以通过跟在后面的签名来将一个PE二进制文件和其它含有根的二进制文件区分开来,跟在后面的签名可由头成员'e_lfanew'(它是从字节偏移地址60处开始的,有32字节长)所设定的偏移地址找到。对于OS/2系统和Windows系统的二进制文件来说,签名是一个16位的word单元;对于PE文件来说,它是一个按照8位字节边界对齐的32位的longword单元,并且IMAGE_NT_SIGNATURE(NT签名)的值已由#defined定义为0x00004550(即字母“PE/0/0”)。

  file-header(文件头)

  要到达IMAGE_FILE_HEADER(文件头)结构,请先确认DOS-头“MZ”(起始的2个字节),然后找出DOS-根的头部的成员“e_lfanew”,并从文件开始处跳过那么多的字节。在核实你在那里找到的签名后,IMAGE_FILE_HEADER(文件头)结构的文件头就紧跟其后开始了。

  optional header(可选头)

  紧跟在文件头后面的就是IMAGE_OPTIONAL_HEADER(尽管它名叫“可选头”,它却一直都在那里)。它包含有怎样去准确处理PE文件的信息。

  data directories(数据目录)

  IMAGE_NUMBEROF_DIRECTORY_ENTRIES (16)(映象文件目录项数目)个IMAGE_DATA_DIRECTORY(映象文件数据目录)数组。这些目录中的每一个目录都描述了一个特定的、位于目录项后面的某一节中的信息的位置(32位的RVA,叫“VirtualAddress(虚拟地址)”)和大小(也是32位,叫“Size(大小)”)。

  例如,安全目录能在索引4中给定的RVA处发现并具有索引4中给定的大小。

  section headers(节头)

  节由两个主要部分组成:首先,是一个节描述(IMAGE_SECTION_HEADER[意为“节头”]类型的),然后是原始的节数据。因此,我们会在数据目录后发现一“NumberOfSections”个节头组成的数组,它们按照各节的RVA排序。

  sections(节数据)

  所有的节在载入内存后都按“SectionAlignment”(节对齐)对齐,在文件中则以“FileAlignment”(文件对齐)对齐。节由节头中的相关项来描述:在文件中你可通过“PointerToRawData”(原始数据指针)来找到,在内存中你可通过“VirtualAddress”(虚拟地址)来找到;长度由“SizeOfRawData”(原始数据长度)决定。

  根据节中包含的内容,可分为好几种节。大多数(并非所有)情况下,节中至少由一个数据目录,并在可选头的数据目录数组中有一个指针指向它。

  下面我们实现编程(www.cppentry.com)修改PE

  [cpp]

  // Pe.cpp: 实现 CPe类.

  //

  #include "stdafx.h"

  #include "Pe.h"

  CPe::CPe()

  {

  }

  CPe::~CPe()

  {

  }

  void CPe::ModifyPe(CString strFileName,CString strMsg)

  {

  CString strErrMsg;

  HANDLE hFile, hMapping;

  void *basepointer;

  // 打开要修改的文件.

  if ((hFile = CreateFile(strFileName, GENERIC_READ|GENERIC_WRITE,

  FILE_SHARE_READ|FILE_SHARE_WRITE, 0,

  OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)

  {

  AfxMessageBox("Could not open file.");

  return;

  }

  // 创建一个映射文件.

  if (!(hMapping = CreateFileMapping(hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))

  {

  AfxMessageBox("Mapping failed.");

  CloseHandle(hFile);

  return;

  }

  // 把文件头映象存入baseointer.

  if (!(basepointer = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)))

  {

  AfxMessageBox("View failed.");

  CloseHandle(hMapping);

  CloseHandle(hFile);

  return;

  }

  CloseHandle(hMapping);

  CloseHandle(hFile);

  CalcAddress(basepointer); // 得到相关地址.

  UnmapViewOfFile(basepointer);

  if(dwSpace<50)

  {

  AfxMessageBox("No room to write the data!");

  }

  else

  {

  WriteFile(strFileName,strMsg); // 写文件.

  }

  if ((hFile = CreateFile(strFileName, GENERIC_READ|GENERIC_WRITE,

  FILE_SHARE_READ|FILE_SHARE_WRITE, 0,

  OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)

  {

  AfxMessageBox("Could not open file.");

  return;

  }

  CloseHandle(hFile);

  }

  void CPe::CalcAddress(const void *base)

  {

  IMAGE_DOS_HEADER * dos_head =(IMAGE_DOS_HEADER *)base;

  if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)

  {

  AfxMessageBox("Unknown type of file.");

  return;

  }

  peHeader * header;

  // 得到PE文件头.

  header = (peHeader *)((char *)dos_head + dos_head->e_lfanew);

  if(IsBadReadPtr(header, sizeof(*header)))

  {

  AfxMessageBox("No PE header, probably DOS executable.");

  return;

  }

  DWORD mods;

  char tmpstr ={0};

  if(strstr((const char *)header->section_header[0].Name,".text")!=NULL)

  {

  // 此段的真实长度.

  dwVirtSize=header->section_header[0].Misc.VirtualSize;

  // 此段的物理偏移.

  dwPhysAddress=header->section_header[0].PointerToRawData;

  // 此段的物理长度.

  dwPhysSize=header->section_header[0].SizeOfRawData;

  // 得到PE文件头的开始偏移.

  dwPeAddress=dos_head->e_lfanew;

  // 得到代码段的可用空间,用以判断可不可以写入我们的代码

  // 用此段的物理长度减去此段的真实长度就可以得到.

  dwSpace=dwPhysSize-dwVirtSize;

  // 得到程序的装载地址,一般为0x400000.

  dwProgRAV=header->opt_head.ImageBase;

  // 得到代码偏移,用代码段起始RVA减去此段的物理偏移

  // 应为程序的入口计算公式是一个相对的偏移地址,计算公式为:

  // 代码的写入地址+dwCodeOffset.

  dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;

  // 代码写入的物理偏移.

  dwEntryWrite=header->section_header[0].PointerToRawData+header->

  section_header[0].Misc.VirtualSize;

  //对齐边界.

  mods=dwEntryWrite%16;

  if(mods!=0)

  {

  dwEntryWrite+=(16-mods);

  }

  // 保存旧的程序入口地址.

  dwOldEntryAddress=header->opt_head.AddressOfEntryPoint;

  // 计算新的程序入口地址.

  dwNewEntryAddress=dwEntryWrite+dwCodeOffset;

  return;

  }

  }

  CString CPe::StrOfDWord(DWORD dwAddress)

  {

       

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇基于VC++实现APC注入 下一篇VC++锁屏程序遇到的问题

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: