推荐下这个帖子 希望能帮助你更好理解
手查PE导出表:http://bbs.pediy.com/showthread.php?p=1403106#post1403106
手查PE重定向:http://bbs.pediy.com/showthread.php?t=206072
代码500行 除了必要的计算文件偏移必须要封装外 其它都在main函数内 我不是故意的... 真的...
部分效果截图:
---------------------------------
进入正题:
目前已解析的有:
DWORD CalcOffset(DWORD Rva,PIMAGE_NT_HEADERS32 pNtH)
{
//PIMAGE_NT_HEADERS32 pnt=pNtH;
PIMAGE_SECTION_HEADER pSecHTemp=IMAGE_FIRST_SECTION(pNtH);//区段头
int index=0;
while (!(Rva>=pSecHTemp->VirtualAddress&&
Rva<pSecHTemp->VirtualAddress+pSecHTemp->SizeOfRawData))
{
//找完所有区段还没有找到
if (index>pNtH->FileHeader.NumberOfSections)
{
return Rva;
}
++index;
++pSecHTemp;
}
v=Rva-pSecHTemp->VirtualAddress+pSecHTemp->PointerToRawData;;
return v;
}
DWORD RelCalcOffset(DWORD Rva,PIMAGE_NT_HEADERS32 pNtH,char *Tempbuf,PCHAR pName=NULL,PCHAR pData=NULL,int Flag=NULL)
{
//PIMAGE_NT_HEADERS32 pnt=pNtH;
PIMAGE_SECTION_HEADER pSecHTemp=IMAGE_FIRST_SECTION(pNtH);//区段头
int index=0;
while (!(Rva>=pSecHTemp->VirtualAddress&&
Rva<pSecHTemp->VirtualAddress+pSecHTemp->SizeOfRawData))
{
//找完所有区段还没有找到
if (index>pNtH->FileHeader.NumberOfSections)
{
// m_Section=L"部首";
//// DWORD a[5];
// _memccpy(&Address,pSecHTemp,6,24);
// //Address=(int);
if (Flag==2)
{
return Rva-pNtH->OptionalHeader.ImageBase;
}
return Rva;
}
++index;
++pSecHTemp;
}
//获取区段名
if (pName!=NULL)
{
//return Rva-pNtH->OptionalHeader.ImageBase;
memcpy(pName,pSecHTemp->Name,8);
}
v=Rva-pSecHTemp->VirtualAddress+pSecHTemp->PointerToRawData;;
DWORD iiii=(long)Tempbuf+v;
//获取数据
if (pData!=NULL)
{
//if (Flag==NULL)
//{
// return Rva-pNtH->OptionalHeader.ImageBase;
//}
if (Flag==2)
{
return Rva-pNtH->OptionalHeader.ImageBase;
}
//flag 为1时
memcpy(pData,PCHAR((long)Tempbuf+v),10);
}
return v;
}
//TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\PEText.exe");C:\Users\Administrator\Desktop\MFCLibrary1Dll.dll
// TCHAR FileName[] = _T("D:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe");
//TCHAR FileName[] = _T("D:\\Program Files\\Tencent\\QQ\\Bin\\QQScLauncher.exe");
TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\Inpaint.exe");
//TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\MFCLibrary1Dll.dll");
char* buf = nullptr;
//得到文件句柄
HANDLE hFile = CreateFile(
FileName, GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//得到文件大小
DWORD dwFileSize =
GetFileSize(hFile, NULL);
DWORD ReadSize = 0;
buf = new char[dwFileSize];
//将文件读取到内存
ReadFile(hFile, buf, dwFileSize, &
ReadSize, NULL);
PIMAGE_DOS_HEADER pDosH=(PIMAGE_DOS_HEADER)buf;
if(IMAGE_DOS_SIGNATURE!=pDosH->e_magic)
{
printf_s("不是DOS头");
return 0;
}
printf_s("DOS头:0x%X\n",pDosH->e_magic);
printf_s("EXE文件的偏移头:0x%X\n",pDosH->e_lfanew);
//获取NT头
PIMAGE_NT_HEADERS32 pNtH=(PIMAGE_NT_HEADERS32)(buf+pDosH->e_lfanew);
//PIMAGE_NT_HEADERS32 pNtH=(PIMAGE_NT_HEADERS32)buf;
if (pNtH->Signature!=IMAGE_NT_SIGNATURE)
{
printf_s("不是NT头");
return 0;
}
printf_s("文件头:\n");
printf_s("文件的运行平台:0x%X\n",pNtH->FileHeader.Machine);
printf_s("区段的数量:0x%X\n",pNtH->FileHeader.NumberOfSections);
printf_s("文件的创建时间:0x%X\n",pNtH->FileHeader.TimeDateStamp);
printf_s("符号表偏移:0x%X\n",pNtH->FileHeader.PointerToSymbolTable);
printf_s("符号个数:0x%X\n",pNtH->FileHeader.NumberOfSymbols);
printf_s("扩展头大小:0x%X\n",pNtH->FileHeader.SizeOfOptionalHeader);
printf_s("PE文件的一些属性:0x%X\n",pNtH->FileHeader.Characteristics);
printf_s("---------------------------------------------------------\n");
printf_s("扩展头:\n");
printf_s("标志字:0x%X\n",pNtH->OptionalHeader.Magic);
printf_s("所有代码区段总大小:0x%X\n",pNtH->OptionalHeader.SizeOfCode);
printf_s("已初始化的数据总大小:0x%X\n",pNtH->OptionalHeader.SizeOfInitializedData);
printf_s("未初始化的数据总大小:0x%X\n",pNtH->OptionalHeader.SizeOfUninitializedData);
printf_s("入口点:0x%X\n",pNtH->OptionalHeader.AddressOfEntryPoint);
printf_s("代码基址:0x%X\n",pNtH->OptionalHeader.BaseOfCode);
printf_s("数据基址:0x%X\n",pNtH->OptionalHeader.BaseOfData);
printf_s("镜像基址:0x%X\n",pNtH->OptionalHeader.ImageBase);
printf_s("块对齐:0x%X\n",pNtH->OptionalHeader.SectionAlignment);
printf_s("文件对齐:0x%X\n",pNtH->OptionalHeader.FileAlignment);
printf_s("镜像大小:0x%X\n",pNtH->OptionalHeader.SizeOfImage);
printf_s("部首大小:0x%X\n",pNtH->OptionalHeader.SizeOfHeaders);
printf_s("校验和:0x%X\n",pNtH->OptionalHeader.CheckSum);
printf_s("子系统:0x%X\n",pNtH->OptionalHeader.Subsystem);
printf_s("DLL特征:0x%X\n",pNtH->OptionalHeader.DllCharacteristics);
printf_s("栈可增长最大值:0x%X\n",pNtH->OptionalHeader.SizeOfStackReserve);
printf_s("栈初始值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapCommit);
printf_s("堆可增长最大值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapReserve);
printf_s("堆初始值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapCommit);
printf_s("RVA数及大小:0x%X\n",pNtH->OptionalHeader.NumberOfRvaAndSizes);
printf_s("输出表 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[0].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[0].Size);
printf_s("输入表 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[1].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[1].Size);
printf_s("资源 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[2].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[2].Size);
printf_s("异常处理程序表 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[3].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[3].Size);
printf_s("安全 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[4].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[4].Size);
printf_s("重定位 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[5].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[5].Size);
printf_s("版权 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[6].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[6].Size);
printf_s("全局指针 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[7].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[7].Size);
printf_s("线程局部存储初始化节(TLS) RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[8].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[8].Size);
printf_s("载入配置 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[9].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[9].Size);
printf_s("载入范围 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[10].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[10].Size);
printf_s("导入地址表(IAT) RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[11].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[11].Size);
printf_s("延迟输入 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[12].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[12].Size);
printf_s("COM信息 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[13].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[13].Size);
printf_s("保留 RVA:0x%X SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[14].VirtualAddress,
pNtH->OptionalHeader.DataDirectory[14].Size);
PIMAGE_SECTION_HEADER pSecH=IMAGE_FIRST_SECTION(pNtH);
int i=0;
while (i<pNtH->FileHeader.NumberOfSections)
{
printf_s("名称 %s ",pSecH->Name);
printf_s("VOffset %08X ",pSecH->VirtualAddress);
printf_s("VSize %08X ",pSecH->Misc.VirtualSize);
printf_s("ROffset %08X ",pSecH->PointerToRawData);
printf_s("Rsize %08X ",pSecH->SizeOfRawData);
printf_s("标志 %08X \n",pSecH->Characteristics);
++pSecH;
i++;
}
PIMAGE_OPTIONAL_HEADER32 pOptH;//可选头
// PIMAGE_SECTION_HEADER pSecH;//区段头
PIMAGE_DATA_DIRECTORY pDatD;//数据目录
PIMAGE_EXPORT_DIRECTORY pExpD;// 导出表数据
//获取可选头数据
pOptH=&(pNtH->OptionalHeader);
//获取数据目录
pDatD=&(pOptH->DataDirectory[0]);
//获取导出表数据
pExpD=(PIMAGE_EXPORT_DIRECTORY)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
//判断是否有导出函数
if (pExpD->NumberOfFunctions==0)
{
printf_s("无导出函数");
}
//取出三个表的函数地址
PDWORD pFunAddr=(PDWORD)(buf+CalcOffset(pExpD->AddressOfFunctions,pNtH)); //函数地址
PDWORD pFunNameAddr=(PDWORD)(buf+CalcOffset(pExpD->AddressOfNames,pNtH)); //函数名地址
PWORD pOrdinalAddr=(PWORD)(buf+CalcOffset(pExpD->AddressOfNameOrdinals,pNtH));//函数序号地址
DWORD NumberOfFun=pExpD->NumberOfFunctions;
DWORD NumberOfName=pExpD->AddressOfNames;
printf_s("函数偏移表地址:%08X\n",CalcOffset(pDatD->VirtualAddress,pNtH));
printf_s("函数地址:%08X\n",pExpD->AddressOfFunctions);
printf_s("函数名序号地址:%08X\n",pExpD->AddressOfNameOrdinals);
printf_s("函数名称地址:%08X\n",pExpD->AddressOfNames);
printf_s("基址:%08X\n",pExpD->Base);
printf_s("特征值:%08X\n",pExpD->Characteristics);
printf_s("名称:%08X\n",pExpD->Name);
// printf_s("名称:%08X",pExpD->);
printf_s("函数数量:%08X\n",pExpD->NumberOfFunctions);
printf_s("函数名数量:%08X\n",pExpD->NumberOfNames);
for (DWORD i=0;i<NumberOfFun;i++)
{
//如果是无效函数 进行下一次
if (!pFunAddr[i])
{
continue;
}
//此时为有效函数 在序号表查找是否有这个序号 用以判断是函数名导出函数序号导出
DWORD j=0;
for (;j<NumberOfName;j++)
{
if (i==pOrdinalAddr[j])
{
break;
}
}
//size_t
//找到了 这是一个函数名导出的函数
if (j!=NumberOfName)
{
// (PCHAR)(buf+CalcOffset(pFunNameAddr[j],pNtH));
printf_s("序号:%d ",pOrdinalAddr[j]+pExpD->Base);
printf_s("RVA:%08X ",pFunAddr[i]);
printf_s("偏移:%08X ",CalcOffset(pFunAddr[i],pNtH));
printf_s("函数名:%s\n",(buf+CalcOffset(pFunNameAddr[j],pNtH)));
}
//没有找到 这是一个序号导出的函数 没有名字
else
{
printf_s("%d",i+pExpD->Base);
printf_s("%08X",pFunAddr[i]);
if (strcmp((buf+CalcOffset(pFunNameAddr[i],pNtH)),"")==0)
{
printf_s("没有");
}
//else
//{
// printf_s("%s\n",(buf+CalcOffset(pFunNameAddr[i],pNtH)));
//}
}
}
PIMAGE_IMPORT_DESCRIPTOR pImpD;
pDatD=&(pOptH->DataDirectory[1]);
pImpD=(PIMAGE_IMPORT_DESCRIPTOR)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
//第一层循环 每个导入的DLL依次解析
while(pImpD->Name)
{
//DLL名称相关
printf_s("DLL名称:%s ",(buf+CalcOffset(pImpD->Name,pNtH)));
printf_s("INT(输入名称表):%08X ",pImpD->OriginalFirstThunk);
printf_s("日期时间标志:%08X ",pImpD->TimeDateStamp);
printf_s("ForwarderChain:%08X ",pImpD->ForwarderChain);
printf_s("名称:%08X ",pImpD->Name);
printf_s("FirstThunk:%08X\n",pImpD->FirstThunk);
//从获取的DLL导入函数地址表 IAT 计算偏移
PIMAGE_THUNK_DATA32 pInt=(PIMAGE_THUNK_DATA32)(buf+CalcOffset(pImpD->FirstThunk,pNtH));
//循环解析导入地址表IAT
while(pInt->u1.Function)
{
DWORD ThunkOffest=CalcOffset(pImpD->OriginalFirstThunk,pNtH);
//判断最高位是否为1 不为1按名称导入
if (!IMAGE_SNAP_BY_ORDINAL32(pInt->u1.Ordinal))
{
//找到函数序号名地址 并将其取出
printf_s("ThunkRVA:%08X ",pImpD->OriginalFirstThunk);
printf_s("Thunk偏移:%08X ",ThunkOffest);
printf_s("Thunk值:%08X ",pInt->u1.AddressOfData);
PIMAGE_IMPORT_BY_NAME pFunName=(PIMAGE_IMPORT_BY_NAME)(buf+CalcOffset(pInt->u1.AddressOfData,pNtH));
printf_s("提示:%04X ",pFunName->Hint);
printf_s("API名称:%s\n",pFunName->Name);
//每次偏移四个字节
pImpD->OriginalFirstThunk+=4;
ThunkOffest+=4;
}
else
{
//找到函数序号名地址 并将其取出
// PIMAGE_IMPORT_BY_NAME pFunName=(PIMAGE_IMPORT_BY_NAME)(buf+CalcOffset(pInt->u1.AddressOfData,pNtH));
printf_s("ThunkRVA:%08X ",pImpD->OriginalFirstThunk);
printf_s("Thunk偏移:%08X ",ThunkOffest);
printf_s("Thunk值:%08X ",pInt->u1.AddressOfData);
printf_s("提示:- ");
printf_s("序号:%4xH %4dD\n",pInt->u1.Ordinal&0x7fffffff,pInt->u1.Ordinal&0x7fffffff);
}
pInt++;
}
system("pause");
pImpD++;
}
//获取资源表
pDatD=&(pOptH->DataDirectory[2]);
PIMAGE_RESOURCE_DIRECTORY pResD=(PIMAGE_RESOURCE_DIRECTORY)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
DWORD ReSize=pResD->NumberOfIdEntries+pResD->NumberOfNamedEntries;
PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD+sizeof(IMAGE_RESOURCE_DIRECTORY));
char ResourceName[15][16]={"鼠标指针","位图","图标","菜单","对话框",
"字符串列表","字体目录","字体","快捷键","非格式化资源",
"消息列表","鼠标指针组","图标组","版本信息"};
printf_s("第一层[名称条目]->:%04X ",pResDE->NameIsString);
printf_s("第一层[ID条目]->:%04X \n",ReSize);
for (DWORD FirstOrder=0;FirstOrder<ReSize;FirstOrder++)
{
//第一层 假如是字符串标识
if (pResDE->NameIsString==1)
{
PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU=(PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD+pResDE->NameOffset);
printf_s("资源类型名:%s",pREsDStrU->NameString);
}
//假如是一直类型 用序号作为标识
else
{
switch (pResDE->Name)
{
case 0x1:
printf_s("第一层:%s\n",ResourceName[0]);
break;
case 0x2:
printf_s("第一层:%s\n",ResourceName[1]);
break;
case 0x3:
printf_s("第一层:%s\n",ResourceName[2]);
break;
case 0x4:
printf_s("第一层:%s\n",ResourceName[3]);
break;
case 0x5:
printf_s("第一层:%s\n",ResourceName[4]);
break;
case 0x6:
printf_s("第一层:%s\n",ResourceName[5]);
break;
case 0x7:
printf_s("第一层:%s\n",ResourceName[6]);
break;
case 0x8:
printf_s("第一层:%s\n",ResourceName[7]);
break;
case 0x9:
printf_s("第一层:%s\n",ResourceName[8]);
break;
case 0xA:
printf_s("第一层:%s\n",ResourceName[9]);
break;
case 0xB:
printf_s("第一层:%s\n",ResourceName[10]);
break;
case 0xC:
printf_s("第一层:%s\n",ResourceName[11]);
break;
case 0xE:
printf_s("第一层:%s\n",ResourceName[12]);
break;
case 0x10:
printf_s("第一层:%s\n",ResourceName[13]);
break;
default:
printf_s("第一层:%d\n",pResDE->Name);
break;
}
}
//找第二层 注意OFFEST 是相对于pResD也就是资源开始位置的偏移
PIMAGE_RESOURCE_DIRECTORY pResD2=(PIMAGE_RESOURCE_DIRECTORY)((long)pResD+pResDE->OffsetToDirectory);
DWORD ReSize2=pResD2->NumberOfIdEntries+pResD2->NumberOfNamedEntries;
PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE2=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD2+sizeof(IMAGE_RESOURCE_DIRECTORY));
printf_s("第一层[名称条目]->:%04X ",pResD2->NumberOfNamedEntries);
printf_s("第一层[ID条目]->:%04X \n",pResD2->NumberOfIdEntries);
for (DWORD SecondOrder=0;SecondOrder<ReSize2;SecondOrder++)
{
if (pResDE2->DataIsDirectory==1)
{
//输出第二层资源的标识看是数字还是字符串
if (pResDE2->NameIsString==1)
{
PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU2=(PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD+pResDE2->NameOffset);
//输出资源类型名字
printf("第二层->资源类型名:%ls ",pREsDStrU2->NameString);
}
else
{
printf_s("第二层->资源类型名ID:%d ",pResDE2->Id);
}
//解析第三层
PIMAGE_RESOURCE_DIRECTORY pResD3=(PIMAGE_RESOURCE_DIRECTORY)((long)pResD+pResDE2->OffsetToDirectory);
PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE3=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD3+sizeof(IMAGE_RESOURCE_DIRECTORY));
PIMAGE_RESOURCE_DATA_ENTRY pResDataE=(PIMAGE_RESOURCE_DATA_ENTRY)((long)pResD+pResDE3->OffsetToData);
printf_s("第三层->RVA:%08X ",pResDataE->OffsetToData);
printf_s("第三层->偏移:%08X ",(CalcOffset(pResDataE->OffsetToData,pNtH)));
printf_s("第三层->大小:%08X\n",pResDataE->Size);
}
else
{
break;
}
pResDE2++;
}
pResDE++;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!