问题排查错误 fopen/fgetc 文件指针似乎随机前进

问题描述 投票:0回答:1

我正在尝试做一个基本的 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 中处理文件

c file
1个回答
0
投票

fseek 和 fread 似乎导致 PE 文件出现问题,可能是由于缓冲区问题造成的。尝试使用 fflush(fp) 清除缓冲区。字节 0xda 可能表示隐藏字符或编码问题 - 仔细检查原始字节。即使 PE Bear 工作正常,某些文件也可能表现不同,因此比较工作文件与非工作文件可能会有所帮助。确保您正在处理特定于 Windows 的文件异常,并跟踪文件指针位置以识别跳转发生的位置。

© www.soinside.com 2019 - 2024. All rights reserved.