You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用C语言扫描二进制文件中的病毒签名?现有代码fgets调用处崩溃求修复

修复二进制文件病毒签名扫描的崩溃问题

咱们先来拆解你代码里的核心问题,这也是导致崩溃的根本原因:

为什么你的代码会崩溃?

  1. 病毒签名读取逻辑错误
    你用fseek(virus_file, 0L, SEEK_END)跳到文件末尾后,没有把文件指针重置回开头就直接调用fgets——这时候fgets从文件末尾开始读,根本读不到任何内容,virus指向的内存里全是未初始化的垃圾数据。后续用strstr搜索这种非法数据,很容易触发内存越界崩溃。
    而且fgets是文本模式专用的函数,它会把二进制文件里的\0字节当成字符串结束符,还会把换行符\n当作读取终止的标志,完全不适合读取二进制病毒签名。

  2. 扫描函数用错了工具

    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 17:07:49