gtxyzz

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

gtxyzz 安全防护 2022-12-10 324浏览 0

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

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

ID:Computer-network

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

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

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

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

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

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

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

BOOLCPeParseDlg::FileCreate(char*szFileName)
{
BOOLbRet=FALSE;
m_hFile=CreateFile(szFileName,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if(m_hFile==INVALID_HANDLE_VALUE)
{
returnbRet;
}
m_hMap=CreateFileMapping(m_hFile,NULL,
PAGE_READWRITE|SEC_IMAGE,0,0,0);
if(m_hMap==NULL)
{
CloseHandle(m_hFile);
returnbRet;
}
m_lpBase=MapViewOfFile(m_hMap,
FILE_MAP_READ|FILE_SHARE_WRITE,
0,0,0);
if(m_lpBase==NULL)
{
CloseHandle(m_hMap);
CloseHandle(m_hFile);
returnbRet;
}
bRet=TRUE;
returnbRet;
}

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

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

HANDLECreateFileMapping(
HANDLEhFile,
LPSECURITY_ATTRIBUTESlpAttributes,
DWORDflProtect,
DWORDdwMaximumSizeHigh,
DWORDdwMaximumSizeLow,
LPCTSTRlpName
);

参数说明如下。

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

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

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

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

MapViewOfFile()函数的定义如下:

LPVOIDMapViewOfFile(
HANDLEhFileMappingObject,
DWORDdwDesiredAccess,
DWORDdwFileOffsetHigh,
DWORDdwFileOffsetLow,
SIZE_TdwNumberOfBytesToMap
);

参数说明如下。

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

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

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

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

BOOLUnmapViewOfFile(
LPCVOIDlpBaseAddress
);

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

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

BOOLCPeParseDlg::IsPeFileAndGetPEPointer()
{
BOOLbRet=FALSE;
//判断是否为MZ头
m_pDosHdr=(PIMAGE_DOS_HEADER)m_lpBase;
if(m_pDosHdr->e_magic!=IMAGE_DOS_SIGNATURE)
{
returnbRet;
}
//根据IMAGE_DOS_HEADER的e_lfanew的值得到PE头的位置
m_pNtHdr=(PIMAGE_NT_HEADERS)((DWORD)m_lpBase+m_pDosHdr->e_lfanew);
//判断是否为PE\0\0
if(m_pNtHdr->Signature!=IMAGE_NT_SIGNATURE)
{
returnbRet;
}
//获得节表的位置
m_pSecHdr=(PIMAGE_SECTION_HEADER)((DWORD)&(m_pNtHdr->OptionalHeader)
+m_pNtHdr->FileHeader.SizeOfOptionalHeader);
bRet=TRUE;
returnbRet;
}

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

VOIDCPeParseDlg::ParseBasePe()
{
CStringStrTmp;
//入口地址
StrTmp.Format("%08X",m_pNtHdr->OptionalHeader.AddressOfEntryPoint);
SetDlgItemText(IDC_EDIT_EP,StrTmp);
//映像基地址
StrTmp.Format("%08X",m_pNtHdr->OptionalHeader.ImageBase);
SetDlgItemText(IDC_EDIT_IMAGEBASE,StrTmp);
//连接器版本号
StrTmp.Format("%d.%d",
m_pNtHdr->OptionalHeader.MajorLinkerVersion,
m_pNtHdr->OptionalHeader.MinorLinkerVersion);
SetDlgItemText(IDC_EDIT_LINKVERSION,StrTmp);
//节表数量
StrTmp.Format("%02X",m_pNtHdr->FileHeader.NumberOfSections);
SetDlgItemText(IDC_EDIT_SECTIONNUM,StrTmp);
//文件对齐值大小
StrTmp.Format("%08X",m_pNtHdr->OptionalHeader.FileAlignment);
SetDlgItemText(IDC_EDIT_FILEALIGN,StrTmp);
//内存对齐值大小
StrTmp.Format("%08X",m_pNtHdr->OptionalHeader.SectionAlignment);
SetDlgItemText(IDC_EDIT_SECALIGN,StrTmp);
}

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

VOIDCPeParseDlg::EnumSections()
{
intnSecNum=m_pNtHdr->FileHeader.NumberOfSections;
inti=0;
CStringStrTmp;
for(i=0;i<nSecNum;i++)
{
m_SectionLIst.InsertItem(i,(constchar*)m_pSecHdr[i].Name);
StrTmp.Format("%08X",m_pSecHdr[i].VirtualAddress);
m_SectionLIst.SetItemText(i,1,StrTmp);
StrTmp.Format("%08X",m_pSecHdr[i].Misc.VirtualSize);
m_SectionLIst.SetItemText(i,2,StrTmp);
StrTmp.Format("%08X",m_pSecHdr[i].PointerToRawData);
m_SectionLIst.SetItemText(i,3,StrTmp);
StrTmp.Format("%08X",m_pSecHdr[i].SizeOfRawData);
m_SectionLIst.SetItemText(i,4,StrTmp);
StrTmp.Format("%08X",m_pSecHdr[i].Characteristics);
m_SectionLIst.SetItemText(i,5,StrTmp);
}
}

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

继续浏览有关 安全 的文章
发表评论