如何使用C语言扫描二进制文件中的病毒签名?现有代码fgets调用处崩溃求修复
修复二进制文件病毒签名扫描的崩溃问题
咱们先来拆解你代码里的核心问题,这也是导致崩溃的根本原因:
为什么你的代码会崩溃?
病毒签名读取逻辑错误
你用fseek(virus_file, 0L, SEEK_END)跳到文件末尾后,没有把文件指针重置回开头就直接调用fgets——这时候fgets从文件末尾开始读,根本读不到任何内容,virus指向的内存里全是未初始化的垃圾数据。后续用strstr搜索这种非法数据,很容易触发内存越界崩溃。
而且fgets是文本模式专用的函数,它会把二进制文件里的\0字节当成字符串结束符,还会把换行符\n当作读取终止的标志,完全不适合读取二进制病毒签名。扫描函数用错了工具
fgets同样不适合读取目标二进制文件:二进制文件里可能包含大量\0或者非打印字符,fgets遇到\0就会停止读取,导致你无法完整扫描文件内容;更糟的是,如果读取的缓冲区没有以\0结尾,printf("%s")和strstr都会越界访问内存,直接引发崩溃。strstr是字符串搜索函数,它依赖\0来判断字符串的边界,但病毒签名是二进制数据,大概率包含\0字节,用strstr会导致搜索提前终止,既找不到正确的签名,还可能因为内存越界崩溃。
修复后的完整实现方案
第一步:正确读取二进制病毒签名
我们改用fread读取签名,并且严格处理文件指针和内存分配:
#include <stdio.h> #include <stdlib.h> #include <string.h> // 读取病毒签名的函数 char* readVirusSignature(const char* filename, long* out_size) { FILE* virus_file = fopen(filename, "rb"); if (!virus_file) { perror("Failed to open virus signature file"); return NULL; } // 获取文件大小 if (fseek(virus_file, 0L, SEEK_END) != 0) { perror("Failed to seek to end of virus file"); fclose(virus_file); return NULL; } long virus_size = ftell(virus_file); if (virus_size <= 0) { fprintf(stderr, "Virus signature file is empty or invalid\n"); fclose(virus_file); return NULL; } // 重置文件指针到开头 if (fseek(virus_file, 0L, SEEK_SET) != 0) { perror("Failed to reset virus file pointer"); fclose(virus_file); return NULL; } // 分配内存(二进制数据不需要额外的\0) char* signature = malloc(virus_size); if (!signature) { perror("Failed to allocate memory for virus signature"); fclose(virus_file); return NULL; } // 完整读取签名 size_t bytes_read = fread(signature, 1, virus_size, virus_file); if (bytes_read != virus_size) { fprintf(stderr, "Failed to read complete virus signature (read %zu of %ld bytes)\n", bytes_read, virus_size); free(signature); fclose(virus_file); return NULL; } fclose(virus_file); *out_size = virus_size; return signature; }
第二步:实现正确的二进制扫描函数
我们用fread分块读取目标文件,用memcmp来比较二进制数据,同时处理跨块的病毒签名(比如签名的一部分在当前块末尾,另一部分在下一个块开头的情况):
int scanForVirus(FILE* fp, const char* virus_signature, long virus_size) { if (!fp || !virus_signature || virus_size <= 0) { return 0; } // 缓冲区大小:BUFSIZ + 签名长度-1,用来保存上一个块的末尾部分 size_t buffer_size = BUFSIZ + virus_size - 1; char* buffer = malloc(buffer_size); if (!buffer) { perror("Failed to allocate scan buffer"); return 0; } size_t prev_bytes = 0; // 上一个块保留的字节数 size_t bytes_read; while ((bytes_read = fread(buffer + prev_bytes, 1, BUFSIZ, fp)) > 0) { size_t total_bytes = prev_bytes + bytes_read; // 在当前缓冲区中搜索病毒签名 for (size_t i = 0; i <= total_bytes - virus_size; i++) { if (memcmp(buffer + i, virus_signature, virus_size) == 0) { free(buffer); return 1; // 找到签名,返回1 } } // 保留缓冲区最后(virus_size-1)个字节,处理跨块情况 if (total_bytes >= virus_size - 1) { memmove(buffer, buffer + total_bytes - (virus_size - 1), virus_size - 1); prev_bytes = virus_size - 1; } else { // 如果当前总字节数不足签名长度-1,全部保留 prev_bytes = total_bytes; } } // 检查文件读取是否出错 if (ferror(fp)) { perror("Error reading target file"); } free(buffer); return 0; // 未找到签名 }
第三步:调用示例
在主函数里这样使用这两个函数:
int main(int argc, char* argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s <target_file> <virus_signature_file>\n", argv[0]); return EXIT_FAILURE; } // 读取病毒签名 long virus_size; char* virus_signature = readVirusSignature(argv[2], &virus_size); if (!virus_signature) { return EXIT_FAILURE; } // 打开目标文件 FILE* fp = fopen(argv[1], "rb"); if (!fp) { perror("Failed to open target file"); free(virus_signature); return EXIT_FAILURE; } // 扫描病毒 int result = scanForVirus(fp, virus_signature, virus_size); if (result) { printf("Virus signature found in %s\n", argv[1]); } else { printf("No virus signature found in %s\n", argv[1]); } // 清理资源 fclose(fp); free(virus_signature); return EXIT_SUCCESS; }
关键修复要点总结
- 二进制文件必须用
fread/fwrite处理:避免fgets/fputs这类文本函数干扰二进制数据的读取。 - 用
memcmp代替strstr搜索二进制数据:memcmp会严格比较指定长度的字节,不受\0字符影响。 - 处理跨块扫描的边界问题:如果病毒签名跨两个读取块,只扫描当前块会漏掉,所以需要保留上一个块的末尾部分。
- 严格检查所有IO和内存操作的返回值:避免因文件打开失败、内存分配失败、读取不完整等问题导致的崩溃。
内容的提问来源于stack exchange,提问作者nigatoni
相关产品推荐
相关产品推荐

