文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

网络安全编程:PE编程实例之PE查看器

2024-12-03 05:31

关注

微信公众号:计算机与网络安全

ID:Computer-network

写PE查看器并不是件复杂的事情,只要按照PE结构一步一步地解析就可以了。下面简单地解析其中几个字段内容,显示一下节表的信息,其余的内容只要稍作修改即可。PE查看器的界面如图1所示。

 

 

 

图1 PE查看器解析记事本程序 

 

 

PE查看器的界面按照图1所示的设置,不过这个可以按照个人的偏好进行布局设置。编写该PE查看器的步骤为打开文件并创建文件内存映像,判断文件是否为PE文件并获得PE格式相关结构体的指针,解析基本的PE字段,枚举节表,最后关闭文件。需要在类中添加几个成员变量及成员函数,添加的内容如图2所示。

 

 

 

图2 在类中添加的成员变量及成员函数 

 

 

按照前面所说的顺序,依次实现添加的各个成员函数。

  1. BOOL CPeParseDlg::FileCreate(char *szFileName) 
  2.   BOOL bRet = FALSE
  3.   m_hFile = CreateFile(szFileName, 
  4.     GENERIC_READ | GENERIC_WRITE, 
  5.     FILE_SHARE_READ,NULL,OPEN_EXISTING, 
  6.     FILE_ATTRIBUTE_NORMAL,NULL); 
  7.   if ( m_hFile == INVALID_HANDLE_VALUE ) 
  8.   { 
  9.     return bRet; 
  10.   } 
  11.   m_hMap = CreateFileMapping(m_hFile, NULL
  12.     PAGE_READWRITE | SEC_IMAGE,0, 0, 0); 
  13.   if ( m_hMap == NULL ) 
  14.   { 
  15.     CloseHandle(m_hFile); 
  16.     return bRet; 
  17.   } 
  18.   m_lpBase = MapViewOfFile(m_hMap, 
  19.     FILE_MAP_READ | FILE_SHARE_WRITE, 
  20.     0, 0, 0); 
  21.   if ( m_lpBase == NULL ) 
  22.   { 
  23.     CloseHandle(m_hMap); 
  24.     CloseHandle(m_hFile); 
  25.     return bRet; 
  26.   } 
  27.   bRet = TRUE
  28.   return bRet; 

这个函数的主要功能是打开文件并创建内存文件映像。通常对文件进行连续读写时直接使用ReadFile()和WriteFile()两个函数。当不连续操作文件时,每次在ReadFile()或者WriteFile()后就要使用SetFilePointer()来调整文件指针的位置,这样的操作较为繁琐。内存文件映像的作用是把整个文件映射入进程的虚拟空间中,这样操作文件就像操作内存变量或内存数据一样方便。

创建内存文件映像所使用的函数有两个,分别是CreateFileMapping()和MapViewOfFile()。CreateFileMapping()函数的定义如下: 

  1. HANDLE CreateFileMapping( 
  2.  HANDLE hFile, 
  3.  LPSECURITY_ATTRIBUTES lpAttributes, 
  4.  DWORD flProtect, 
  5.  DWORD dwMaximumSizeHigh, 
  6.  DWORD dwMaximumSizeLow, 
  7.  LPCTSTR lpName 
  8. ); 

参数说明如下。

hFile:该参数是 CreateFile()函数返回的句柄。

lpAttributes:是安全属性,该值通常是 NULL。

flProtect:创建文件映射后的属性,通常设置为可读可写 PAGE_READWRITE。如果需要像装载可执行文件那样把文件映射入内存的话,那么需要使用 SEC_IMAGE。最后3个参数在这里为0。如果创建的映射需要在多进程中共享数据的话,那么最后一个参数设定为一个字符串,以便通过该名称找到该块共享内存。

该函数的返回值为一个内存映射的句柄。

MapViewOfFile()函数的定义如下: 

  1. LPVOID MapViewOfFile( 
  2.  HANDLE hFileMappingObject, 
  3.  DWORD dwDesiredAccess, 
  4.  DWORD dwFileOffsetHigh, 
  5.  DWORD dwFileOffsetLow, 
  6.  SIZE_T dwNumberOfBytesToMap 
  7. ); 

参数说明如下。

hFileMappingObject:该参数为 CreateFileMapping()返回的句柄。

dwDesiredAccess:想获得的访问权限,通常情况下也是可读可写 FILE_MAP_READ、FILE_MAP_WRITE。

最后3个参数一般给0值就可以了。

按照编程的规矩,打开要关闭,申请要释放。CreateFileMapping()的关闭需要使用CloseHandle()函数。MapViewOfFile()的关闭,要使用UnmapViewOfFile()函数,该函数的定义如下: 

  1. BOOL UnmapViewOfFile( 
  2.  LPCVOID lpBaseAddress 
  3. ); 

该函数的参数就是MapViewOfFile()函数的返回值。

接着说PE查看器,文件已经打开,就要判断文件是否为有效的PE文件了。如果是有效的PE文件,就把解析PE格式的相关结构体的指针也得到。代码如下: 

  1. BOOL CPeParseDlg::IsPeFileAndGetPEPointer() 
  2.   BOOL bRet = FALSE
  3.   // 判断是否为 MZ 头 
  4.   m_pDosHdr = (PIMAGE_DOS_HEADER)m_lpBase; 
  5.   if ( m_pDosHdr->e_magic != IMAGE_DOS_SIGNATURE ) 
  6.   { 
  7.     return bRet; 
  8.   } 
  9.   // 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 PE 头的位置 
  10.   m_pNtHdr = (PIMAGE_NT_HEADERS)((DWORD)m_lpBase + m_pDosHdr->e_lfanew); 
  11.   // 判断是否为 PE\0\0 
  12.   if ( m_pNtHdr->Signature != IMAGE_NT_SIGNATURE ) 
  13.   { 
  14.     return bRet; 
  15.   } 
  16.   // 获得节表的位置 
  17.   m_pSecHdr = (PIMAGE_SECTION_HEADER)((DWORD)&(m_pNtHdr->OptionalHeader) 
  18.     + m_pNtHdr->FileHeader.SizeOfOptionalHeader); 
  19.   bRet = TRUE
  20.   return bRet; 

这段代码应该非常容易理解,继续看解析PE格式的部分。 

  1. VOID CPeParseDlg::ParseBasePe() 
  2.   CString StrTmp; 
  3.   // 入口地址 
  4.   StrTmp.Format("%08X", m_pNtHdr->OptionalHeader.AddressOfEntryPoint); 
  5.   SetDlgItemText(IDC_EDIT_EP, StrTmp); 
  6.   // 映像基地址 
  7.   StrTmp.Format("%08X", m_pNtHdr->OptionalHeader.ImageBase); 
  8.   SetDlgItemText(IDC_EDIT_IMAGEBASE, StrTmp); 
  9.   // 连接器版本号 
  10.   StrTmp.Format("%d.%d"
  11.     m_pNtHdr->OptionalHeader.MajorLinkerVersion, 
  12.     m_pNtHdr->OptionalHeader.MinorLinkerVersion); 
  13.   SetDlgItemText(IDC_EDIT_LINKVERSION, StrTmp); 
  14.   // 节表数量 
  15.   StrTmp.Format("%02X", m_pNtHdr->FileHeader.NumberOfSections); 
  16.   SetDlgItemText(IDC_EDIT_SECTIONNUM, StrTmp); 
  17.   // 文件对齐值大小 
  18.   StrTmp.Format("%08X", m_pNtHdr->OptionalHeader.FileAlignment); 
  19.   SetDlgItemText(IDC_EDIT_FILEALIGN, StrTmp); 
  20.   // 内存对齐值大小 
  21.   StrTmp.Format("%08X", m_pNtHdr->OptionalHeader.SectionAlignment); 
  22.   SetDlgItemText(IDC_EDIT_SECALIGN, StrTmp); 

PE格式的基础信息,就是简单地获取结构体的成员变量,没有过多复杂的内容。获取导入表、导出表比获取基础信息复杂。接下来进行节表的枚举,具体代码如下: 

  1. VOID CPeParseDlg::EnumSections() 
  2.   int nSecNum = m_pNtHdr->FileHeader.NumberOfSections; 
  3.   int i = 0; 
  4.   CString StrTmp; 
  5.   for ( i = 0; i < nSecNum; i ++ ) 
  6.   { 
  7.     m_SectionLIst.InsertItem(i, (const char *)m_pSecHdr[i].Name); 
  8.     StrTmp.Format("%08X", m_pSecHdr[i].VirtualAddress); 
  9.     m_SectionLIst.SetItemText(i, 1, StrTmp); 
  10.     StrTmp.Format("%08X", m_pSecHdr[i].Misc.VirtualSize); 
  11.     m_SectionLIst.SetItemText(i, 2, StrTmp); 
  12.     StrTmp.Format("%08X", m_pSecHdr[i].PointerToRawData); 
  13.     m_SectionLIst.SetItemText(i, 3, StrTmp); 
  14.     StrTmp.Format("%08X", m_pSecHdr[i].SizeOfRawData); 
  15.     m_SectionLIst.SetItemText(i, 4, StrTmp); 
  16.     StrTmp.Format("%08X", m_pSecHdr[i].Characteristics); 
  17.     m_SectionLIst.SetItemText(i, 5, StrTmp); 
  18.   } 

最后的动作是释放动作,因为很简单,这里就不给出代码了。将这些自定义函数通过界面上的“查看”按钮联系起来,整个PE查看器就算是写完了。  

 

来源:计算机与网络安全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯