/*
下文的代码既可在linux亦可在windows下运行。
为什么要保留这段代码呢?
1.包含了最近一段时间对pe文件格式的分析;
以后如果再需要用pe格式只要看看这段代码就行了,容易上手。
2.pe文件中rva到文件偏移量的转换比较头大;
这段代码中有较好的函数实现。
此程序的功能是判断一个文件是否为.cpl文件。cpl文件其实就是dll,但是包含了一个特殊接口。
*/
#include
#include
#include
#include
#include
using namespace std;
//下面的这些结构都是winnt.h中对pe文件头的定义
typedef struct _IMAGE_DATA_DIRECTORY {
unsigned long VirtualAddress;
unsigned long Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
typedef struct _IMAGE_FILE_HEADER {
unsigned short Machine;
unsigned short NumberOfSections;
unsigned long TimeDateStamp;
unsigned long PointerToSymbolTable;
unsigned long NumberOfSymbols;
unsigned short SizeOfOptionalHeader;
unsigned short Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
unsigned short Magic;
char MajorLinkerVersion;
char MinorLinkerVersion;
unsigned long SizeOfCode;
unsigned long SizeOfInitializedData;
unsigned long SizeOfUninitializedData;
unsigned long AddressOfEntryPoint;
unsigned long BaseOfCode;
unsigned long BaseOfData;
//
// NT additional fields.
//
unsigned long ImageBase;
unsigned long SectionAlignment;
unsigned long FileAlignment;
unsigned short MajorOperatingSystemVersion;
unsigned short MinorOperatingSystemVersion;
unsigned short MajorImageVersion;
unsigned short MinorImageVersion;
unsigned short MajorSubsystemVersion;
unsigned short MinorSubsystemVersion;
unsigned long Win32VersionValue;
unsigned long SizeOfImage;
unsigned long SizeOfHeaders;
unsigned long CheckSum;
unsigned short Subsystem;
unsigned short DllCharacteristics;
unsigned long SizeOfStackReserve;
unsigned long SizeOfStackCommit;
unsigned long SizeOfHeapReserve;
unsigned long SizeOfHeapCommit;
unsigned long LoaderFlags;
unsigned long NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_NT_HEADERS {
unsigned long Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
typedef struct _IMAGE_SECTION_HEADER {
char Name[8];
union {
unsigned long PhysicalAddress;
unsigned long VirtualSize;
} Misc;
unsigned long VirtualAddress;
unsigned long SizeOfRawData;
unsigned long PointerToRawData;
unsigned long PointerToRelocations;
unsigned long PointerToLinenumbers;
unsigned short NumberOfRelocations;
unsigned short NumberOfLinenumbers;
unsigned long Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
//这段代码判断rva在哪个section中
PIMAGE_SECTION_HEADER imagerva2section(PIMAGE_NT_HEADERS pimage_nt_headers,unsigned long dwRVA)
{
int i;
PIMAGE_SECTION_HEADER pimage_section_header=(PIMAGE_SECTION_HEADER)(((char *)(pimage_nt_headers)) + sizeof(IMAGE_NT_HEADERS));
for(i=0;iFileHeader.NumberOfSections;i++) {
//pimage_section_header->VirtualAddress 是section的首地址
//pimage_section_header->SizeOfRawData 是section的长度
if((pimage_section_header->VirtualAddress) && (dwRVA<=(pimage_section_header->VirtualAddress+pimage_section_header->SizeOfRawData)))
return ((PIMAGE_SECTION_HEADER)pimage_section_header);
pimage_section_header++;
}
return(NULL);
}
/*
计算偏移量的原理是什么呢?
先判断出rva在哪个section中,然后查section表找到与这个section对应的
文件偏移量,再用以下公式计算文件偏移量:
offset=rva-(section虚拟内存地址-section文件偏移量)
*/
unsigned long rva2offset(char * praw,unsigned long dwRVA)
{
unsigned long _offset;
PIMAGE_SECTION_HEADER section;
PIMAGE_NT_HEADERS pimage_nt_headers;
pimage_nt_headers = (PIMAGE_NT_HEADERS)(praw);
section=imagerva2section(pimage_nt_headers,dwRVA);
if(section==NULL)
return(-1);
//section->VirtualAddress 就是section的虚内存地址
//section->PointerToRawData 就是section的文件偏移量
_offset=dwRVA+section->PointerToRawData-section->VirtualAddress;
return(_offset);
}
// in fact the .cpl file is a .dll file, so we to check if it has "CPlApplet" symbol.
bool _iscpl(FILE * pf)
{
if (NULL==pf)
return false;
char symbolbuff[10];
unsigned long dwOffset = 0;
unsigned short magicnumber = 0;
fseek(pf,0,SEEK_SET);
fread(symbolbuff,1,2,pf);
if (symbolbuff[0]!='M' || symbolbuff[1]!='Z')
return false;
//skip dos stub.
//pe文件头是一个dos stub
fseek(pf,60,SEEK_SET);
//read pe offset.
//然后就是一个偏移量保存了真正的pe文件头
fread(&dwOffset,1,sizeof(dwOffset),pf);
//read nt header.
//在此读文件头并保存到一个结构内
fseek(pf,dwOffset,SEEK_SET);
char ntheader[10240];
fread(ntheader,1,sizeof(ntheader),pf);
fseek(pf,dwOffset,SEEK_SET);
//judge pe signature.
//pe文件头的首部是一个pe signature.
fread(symbolbuff,1,2,pf);
if (symbolbuff[0]!='P' || symbolbuff[1]!='E')
return false;
//read magic number to determine if the file is pe32 or pe32+.
fseek(pf,22,SEEK_CUR);
fread(&magicnumber,1,sizeof(magicnumber),pf);
//get the offset of optional header data directories
dwOffset = 0;
if (0x10b==magicnumber)
dwOffset = 96-2;
else
dwOffset = 112-2;
fseek(pf,dwOffset,SEEK_CUR);
fread(&dwOffset,1,sizeof(dwOffset),pf);
//seek to Optional Header Data Directories
dwOffset = rva2offset(ntheader,dwOffset);
if(dwOffset==-1)
return false;
fseek(pf,dwOffset,SEEK_SET);
int atenum = 0; //The number of entries in the export address table.
int nnpnum = 0; //The number of entries in the name pointer table. This is also the number of entries in the ordinal table.
fseek(pf,dwOffset+20,SEEK_SET);
fread(&atenum,1,sizeof(atenum),pf);
fseek(pf,dwOffset+24,SEEK_SET);
fread(&nnpnum,1,sizeof(nnpnum),pf);
//skip the export address table.
fseek(pf,dwOffset+40+4*atenum,SEEK_SET);
//now process name table.
vector vecstraddr;
for(int i=0;i
int nnpaddr = 0;
fread(&nnpaddr,1,sizeof(nnpaddr),pf);
nnpaddr = rva2offset(ntheader,nnpaddr);
if(nnpaddr==-1)
continue;
vecstraddr.push_back(nnpaddr);
}
//compare name string.
for(i=0;i
char buffstr[128];
int straddr = vecstraddr[i];
fseek(pf,straddr,SEEK_SET);
fread(buffstr,1,sizeof(buffstr),pf);
if(strcmp(buffstr,"CPlApplet")==0)
return true;
}
return false;
}
bool iscpl(const char * inputfile)
{
FILE * pf = fopen(inputfile,"rb");
bool res = _iscpl(pf);
fclose(pf);
return res;
}
int main(int argc, char* argv[])
{
bool res = iscpl("c:\\test.cpl");
return 0;
}
本文转载自:
http://tassardge.blog.163.com/blog/static/1723017082008102185122256/