有时候会遇到某些程序把整个 PE 文件(就像整个 .exe, .dll, .sys, ...)嵌入到数据段中,在需要的时候才释放出来。
要提取这类程序中的文件,只有动态调试或者手工静态提取。
Exeinfo 本身带有提取功能,但是它的提取方法是找到一个有效的文件头之后就从文件头一直提取到整个文件的末尾。
这样虽然拨出了萝卜,但是带出了不少泥。
只好自己动手写一个程序了。
原理:
标准的 PE 文件结构是:Dos头|PE头|区段表|区段1,区段2,区段3,...
这样要提取出完整的文件,就是从Dos头开始,一直到最后一个区段的末尾。
思路:
先把目标文件映射到内存,获取文件在内存中的指针,遍历整个文件内容。
把所有疑似 PE 文件的内容都保存出来。
相关代码如下:
把文件映射到内存之后,通过内存指针来判断是否是一个 PE 文件,通过简单的判断 DOS 头和 PE 头来确定。
[font=Comic Sans MS][color=#008000]// 是否疑似 PE 文件?
[/color][/font][font=Fixedsys][color=#000000]BOOL IsPeLike[/color][color=#000080]([/color][color=#000000]LPVOID lpImageBase[/color][color=#000080])
{
[/color][color=#000000]PIMAGE_DOS_HEADER pDosHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_NT_HEADERS pNtHeader[/color][color=#000080];
[/color][color=#000000]pDosHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_DOS_HEADER[/color][color=#000080])[/color][color=#000000]lpImageBase[/color][color=#000080];
[/color][color=#0000FF]if [/color][color=#000080]([/color][color=#000000]IMAGE_DOS_SIGNATURE [/color][color=#000080]!= [/color][color=#000000]pDosHeader[/color][color=#000080]->[/color][color=#000000]e_magic[/color][color=#000080]) [/color][color=#0000FF]return [/color][color=#000000]FALSE[/color][color=#000080];
[/color][color=#000000]pNtHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_NT_HEADERS[/color][color=#000080])(([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]pDosHeader [/color][color=#000080]+ [/color][color=#000000]pDosHeader[/color][color=#000080]->[/color][color=#000000]e_lfanew[/color][color=#000080]);
[/color][/font][font=Comic Sans MS][color=#008000]// 如果偏移落在结构体范围之外,说明数据是错误的。跳过。
[/color][/font][font=Fixedsys][color=#0000FF]if[/color][color=#000080](([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]pDosHeader[/color][color=#000080]->[/color][color=#000000]e_lfanew [/color][color=#000080]> (([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]lpImageBase [/color][color=#000080]+ [/color][color=#0000FF]sizeof[/color][color=#000080]([/color][color=#000000]IMAGE_DOS_HEADER[/color][color=#000080]) + [/color][color=#0000FF]sizeof[/color][color=#000080]([/color][color=#000000]IMAGE_NT_HEADERS[/color][color=#000080]))) [/color][color=#0000FF]return [/color][color=#000000]FALSE[/color][color=#000080];
[/color][color=#0000FF]if [/color][color=#000080](([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]pNtHeader [/color][color=#000080]> (([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]lpImageBase [/color][color=#000080]+ [/color][color=#0000FF]sizeof[/color][color=#000080]([/color][color=#000000]IMAGE_DOS_HEADER[/color][color=#000080]) + [/color][color=#0000FF]sizeof[/color][color=#000080]([/color][color=#000000]IMAGE_NT_HEADERS[/color][color=#000080]))) [/color][color=#0000FF]return [/color][color=#000000]FALSE[/color][color=#000080];
[/color][color=#0000FF]if [/color][color=#000080]([/color][color=#000000]IMAGE_NT_SIGNATURE [/color][color=#000080]!= [/color][color=#000000]pNtHeader[/color][color=#000080]->[/color][color=#000000]Signature[/color][color=#000080]) [/color][color=#0000FF]return [/color][color=#000000]FALSE[/color][color=#000080];
[/color][color=#0000FF]return [/color][color=#000000]TRUE[/color][color=#000080];
}[/color][/font]
如果通过判断,那么说明找到的数据可能是一个 PE 文件,接下来获取这个文件的真实大小,真实大小是从 DOS 头开始,直到最后一个区段末尾的总大小,不包含附加数据。
[font=Comic Sans MS][color=#008000]// 获取 PE 文件真实大小
[/color][/font][font=Fixedsys][color=#000000]DWORD WINAPI GetRealSize[/color][color=#000080]([/color][color=#000000]LPVOID lpImageBase[/color][color=#000080])
{
[/color][color=#000000]PIMAGE_DOS_HEADER pDosHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_NT_HEADERS pNtHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_FILE_HEADER pFileHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_SECTION_HEADER pSectionHeader[/color][color=#000080];
[/color][color=#000000]pDosHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_DOS_HEADER[/color][color=#000080])[/color][color=#000000]lpImageBase[/color][color=#000080];
[/color][color=#000000]pNtHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_NT_HEADERS[/color][color=#000080])(([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]pDosHeader [/color][color=#000080]+ [/color][color=#000000]pDosHeader[/color][color=#000080]->[/color][color=#000000]e_lfanew[/color][color=#000080]);
[/color][color=#000000]pSectionHeader [/color][color=#000080]= [/color][color=#000000]IMAGE_FIRST_SECTION[/color][color=#000080]([/color][color=#000000]pNtHeader[/color][color=#000080]);
[/color][color=#000000]pFileHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_FILE_HEADER[/color][color=#000080])&[/color][color=#000000]pNtHeader[/color][color=#000080]->[/color][color=#000000]FileHeader[/color][color=#000080];
[/color][color=#000000]pSectionHeader [/color][color=#000080]+= ([/color][color=#000000]pFileHeader[/color][color=#000080]->[/color][color=#000000]NumberOfSections [/color][color=#000080]- [/color][color=#800080]1[/color][color=#000080]); [/color][/font][font=Comic Sans MS][color=#008000]//定位到最后一个区块表的开头
[/color][/font][font=Fixedsys][color=#0000FF]return [/color][color=#000080]([/color][color=#000000]pSectionHeader[/color][color=#000080]->[/color][color=#000000]PointerToRawData [/color][color=#000080]+ [/color][color=#000000]pSectionHeader[/color][color=#000080]->[/color][color=#000000]SizeOfRawData[/color][color=#000080]); [/color][/font][font=Comic Sans MS][color=#008000]// 文件实际大小 = 文件结尾偏移 = 最后一个区段的磁盘偏移 + 最后一个区段的磁盘大小
[/color][/font][font=Fixedsys][color=#000080]}[/color][/font]
在保存到磁盘之前先判断一下 PE 的类型,根据判断结果来决定文件的扩展名,如果不能确定类型,则默认为.bin。
[color=#000000]LPTSTR WINAPI GetRealType[/color][color=#000080]([/color][color=#000000]LPVOID lpImageBase[/color][color=#000080])
{
[/color][color=#000000]PIMAGE_DOS_HEADER pDosHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_NT_HEADERS pNtHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_FILE_HEADER pFileHeader[/color][color=#000080];
[/color][color=#000000]PIMAGE_OPTIONAL_HEADER pOptionalHeader[/color][color=#000080];
[/color][color=#000000]pDosHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_DOS_HEADER[/color][color=#000080])[/color][color=#000000]lpImageBase[/color][color=#000080];
[/color][color=#000000]pNtHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_NT_HEADERS[/color][color=#000080])(([/color][color=#000000]DWORD[/color][color=#000080])[/color][color=#000000]pDosHeader [/color][color=#000080]+ [/color][color=#000000]pDosHeader[/color][color=#000080]->[/color][color=#000000]e_lfanew[/color][color=#000080]);
[/color][color=#000000]pFileHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_FILE_HEADER[/color][color=#000080])&[/color][color=#000000]pNtHeader[/color][color=#000080]->[/color][color=#000000]FileHeader[/color][color=#000080];
[/color][color=#000000]pOptionalHeader [/color][color=#000080]= ([/color][color=#000000]PIMAGE_OPTIONAL_HEADER[/color][color=#000080])&[/color][color=#000000]pNtHeader[/color][color=#000080]->[/color][color=#000000]OptionalHeader[/color][color=#000080];
[/color][/font][font=Comic Sans MS][color=#008000]// 判断映像类型
[/color][/font][font=Fixedsys][color=#0000FF]if [/color][color=#000080]([/color][color=#000000]IMAGE_FILE_DLL [/color][color=#000080]& [/color][color=#000000]pFileHeader[/color][color=#000080]->[/color][color=#000000]Characteristics[/color][color=#000080]) [/color][color=#0000FF]return [/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]".dll"[/color][color=#000080]);
[/color][color=#0000FF]if [/color][color=#000080](([/color][color=#000000]IMAGE_FILE_EXECUTABLE_IMAGE [/color][color=#000080]& [/color][color=#000000]pFileHeader[/color][color=#000080]->[/color][color=#000000]Characteristics[/color][color=#000080]) && ([/color][color=#000000]IMAGE_SUBSYSTEM_NATIVE [/color][color=#000080]& [/color][color=#000000]pOptionalHeader[/color][color=#000080]->[/color][color=#000000]Subsystem[/color][color=#000080])) [/color][color=#0000FF]return [/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]".sys"[/color][color=#000080]);
[/color][color=#0000FF]if [/color][color=#000080]([/color][color=#000000]IMAGE_FILE_EXECUTABLE_IMAGE [/color][color=#000080]& [/color][color=#000000]pFileHeader[/color][color=#000080]->[/color][color=#000000]Characteristics[/color][color=#000080]) [/color][color=#0000FF]return [/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]".exe"[/color][color=#000080]);
[/color][color=#0000FF]return [/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]".bin"[/color][color=#000080]); [/color][/font][font=Comic Sans MS][color=#008000]//默认返回后缀 .bin
[/color][/font][font=Fixedsys][color=#000080]}[/color][/font]
剩下的工作就是遍历文件内容了,我使用的是效率最低的逐字节遍历,所以只能对付小文件。
[font=Fixedsys][color=#000000] [/color][color=#0000FF]for [/color][color=#000080]([/color][color=#000000]DWORD dwPos [/color][color=#000080]= [/color][color=#800080]0[/color][color=#000080]; [/color][color=#000000]dwPos [/color][color=#000080]< [/color][color=#000000]dwFileSize[/color][color=#000080]; [/color][color=#000000]dwPos[/color][color=#000080]++, [/color][color=#000000]lpByte[/color][color=#000080]++)
{
[/color][color=#0000FF]if [/color][color=#000080](![/color][color=#000000]IsPeLike[/color][color=#000080]([/color][color=#000000]lpByte[/color][color=#000080]) || ([/color][color=#000000]dwPos [/color][color=#000080]== [/color][color=#800080]0[/color][color=#000080])) [/color][color=#0000FF]continue[/color][color=#000080];
[/color][color=#000000]dwRealSize [/color][color=#000080]= [/color][color=#000000]GetRealSize[/color][color=#000080]([/color][color=#000000]lpByte[/color][color=#000080]);
[/color][color=#000000]csFileToSave[/color][color=#000080].[/color][color=#000000]Format[/color][color=#000080]([/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]"%s_~0x%08X%s"[/color][color=#000080]),[/color][color=#000000]m_csFile[/color][color=#000080], [/color][color=#000000]dwPos[/color][color=#000080], [/color][color=#000000]GetRealType[/color][color=#000080]([/color][color=#000000]lpByte[/color][color=#000080])); [/color][/font][font=Comic Sans MS][color=#008000]// 以在文件中的偏移地址为文件名称
[/color][/font][font=Fixedsys][color=#000000]hFile [/color][color=#000080]= [/color][color=#000000]CreateFile[/color][color=#000080]([/color][color=#000000]csFileToSave[/color][color=#000080].[/color][color=#000000]GetBuffer[/color][color=#000080]([/color][color=#000000]MAX_PATH[/color][color=#000080]), [/color][color=#000000]GENERIC_WRITE[/color][color=#000080], [/color][color=#000000]FILE_SHARE_READ[/color][color=#000080], [/color][color=#000000]NULL[/color][color=#000080], [/color][color=#000000]CREATE_ALWAYS[/color][color=#000080], [/color][color=#000000]FILE_ATTRIBUTE_NORMAL[/color][color=#000080], [/color][color=#000000]NULL[/color][color=#000080]);
[/color][color=#000000]csFileToSave[/color][color=#000080].[/color][color=#000000]ReleaseBuffer[/color][color=#000080]();
[/color][color=#0000FF]if [/color][color=#000080]([/color][color=#000000]INVALID_HANDLE_VALUE [/color][color=#000080]== [/color][color=#000000]hFile[/color][color=#000080]) [/color][color=#0000FF]continue[/color][color=#000080];
[/color][color=#000000]WriteFile[/color][color=#000080]([/color][color=#000000]hFile[/color][color=#000080], [/color][color=#000000]lpByte[/color][color=#000080], [/color][color=#000000]dwRealSize[/color][color=#000080], &[/color][color=#000000]dwWrited[/color][color=#000080], [/color][color=#000000]NULL[/color][color=#000080]);
[/color][color=#000000]CloseHandle[/color][color=#000080]([/color][color=#000000]hFile[/color][color=#000080]);
[/color][color=#000000]bFound [/color][color=#000080]= [/color][color=#000000]TRUE[/color][color=#000080];
++[/color][color=#000000]dwCounter[/color][color=#000080];
[/color][color=#000000]csTmp[/color][color=#000080].[/color][color=#000000]Format[/color][color=#000080]([/color][color=#000000]_T[/color][color=#000080]([/color][color=#808080]"文件名称:%s\r\n文件偏移:0x%08X (%d)\r\n文件大小:%d (字节)\r\n"[/color][color=#000080]), [/color][color=#000000]csFileToSave[/color][color=#000080], [/color][color=#000000]dwPos[/color][color=#000080], [/color][color=#000000]dwPos[/color][color=#000080], [/color][color=#000000]dwRealSize[/color][color=#000080]);
[/color][color=#000000]csLog [/color][color=#000080]+= [/color][color=#000000]csTmp[/color][color=#000080];
}[/color][/font]
附件是我写的演示程序,用于演示提取和写入文件。
附上效果图:
XueTr 0.37
SOD 0.35
*有些程序加密了PE头,或采用压缩的方法存储,对于这类程序是没办法的。
*添加拖放功能,方便测试。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法