我正在尝试做一个基本的 C 解析器,但显然遇到了 f{open,read,write,getc} 工作方式的问题。
下面是我做的最小问题重现器
#include <stdio.h>
#include <stdlib.h>
#include <urlmon.h>
#include <assert.h>
#include <winnt.h>
#define ___IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
typedef struct __IMAGE_DOS_HEADER {
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew; //offset in the file of the NT header structure
} ___IMAGE_DOS_HEADER, * ___PIMAGE_DOS_HEADER;
typedef struct __IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} ___IMAGE_DATA_DIRECTORY, * ___PIMAGE_DATA_DIRECTORY;
typedef struct __IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
___IMAGE_DATA_DIRECTORY DataDirectory[___IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} ___IMAGE_OPTIONAL_HEADER64, * ___PIMAGE_OPTIONAL_HEADER64;
___IMAGE_DOS_HEADER *parse_dos_header(FILE *fp){
___IMAGE_DOS_HEADER *dos_hdr = malloc(sizeof(___IMAGE_DOS_HEADER));
fread(dos_hdr,sizeof(___IMAGE_DOS_HEADER),1,fp);
// printf("offset optional header is %lu\n",dos_hdr->e_lfanew);
return dos_hdr;
}
___IMAGE_OPTIONAL_HEADER64* parse_optional_header(FILE *fp, ___IMAGE_DOS_HEADER* dos_hdr){
___IMAGE_OPTIONAL_HEADER64 *opt_hdr = malloc(sizeof(___IMAGE_OPTIONAL_HEADER64));
long offset = dos_hdr->e_lfanew + sizeof(long) + sizeof(IMAGE_FILE_HEADER);
fseek(fp,offset,SEEK_SET);
fread(opt_hdr,sizeof(___IMAGE_OPTIONAL_HEADER64),1,fp);
fseek(fp,offset,SEEK_SET);
for(int i=0;i<512;i++){
//debug loop to test char by char
char byte = fgetc(fp);
if (byte == EOF) break;
printf("ftell position %lx\n",ftell(fp));
printf("%02x\n",byte);
}
assert(opt_hdr->Magic == 0x20b);
long end = ftell(fp);
fseek(fp,0,SEEK_END);
long end_file = ftell(fp);
assert(end_file>end);
printf("Passed the check\n");
rewind(fp);
return opt_hdr;
}
int main(int argc, char* argv[]){
if(argc!=2){
printf("Specify the PE you want to parse through argv[1]\n usage : ./binary <path_to_pe>\n");
exit(-1);
}
FILE *fp;
fopen_s(&fp,argv[1],"rb");
fseek(fp, 0L, SEEK_END);
long long sz = ftell(fp);
rewind(fp);
if(sz<sizeof(___IMAGE_DOS_HEADER)){
printf("Not enough byte to parse the DOS header, exiting\n");
exit(-1);
}
___IMAGE_DOS_HEADER *dos_hdr = parse_dos_header(fp);
___IMAGE_OPTIONAL_HEADER64* opt_hdr = parse_optional_header(fp,dos_hdr);
}
基本上一切正常,但在某些文件上,文件指针位置似乎毫无原因地跳闸。 我检查了文件大小是否有问题,但没有。我使用的文件没有任何格式错误,pebear 正确加载信息,我用它自己的 PE 文件测试解析器,以便我确定它启动了 . 我无法理解的是,文件指针似乎随机前进(仅适用于某些文件,但因此破坏了某些文件的解析)。就像测试文件中从 0x118 到 0x308 一样。根据 pebear 的说法,该字节似乎是 0xda,因此不确定它会误解换行符,正如我在网上看到的那样,它可能会导致一些问题。 此外,跳转后,该字节与我可以在十六进制编辑器中的文件中看到的原始字节不对应(例如,如果我使用十六进制编辑器检查我的代码在文件中打印的字节 0x400 和偏移量 0x400 的内容,则该字节不匹配) 这里链接了针对 PE 文件启动的代码的输出 这里放了我用于测试的 PE 和相同的测试文件
由于我很难理解这个问题,它可能缺乏信息,我可以一路添加一些
我尝试与现有工具进行比较 我尝试在网上搜索但没有任何帮助。
最终目标是能够解析 PE,但最终大多数人最终明白为什么 fopen 显然无法处理该文件,没有真正的原因,并且有一种可靠的方法在 Windows 中处理文件
fseek 和 fread 似乎导致 PE 文件出现问题,可能是由于缓冲区问题造成的。尝试使用 fflush(fp) 清除缓冲区。字节 0xda 可能表示隐藏字符或编码问题 - 仔细检查原始字节。即使 PE Bear 工作正常,某些文件也可能表现不同,因此比较工作文件与非工作文件可能会有所帮助。确保您正在处理特定于 Windows 的文件异常,并跟踪文件指针位置以识别跳转发生的位置。