遍历目录检测文件签名:file_sign函数异常排查及实现疑问
问题分析与解决方案
核心错误原因
你当前的实现存在一个关键问题:把十六进制字符串直接当作二进制字节流来比较。
- 你定义的
given_signature是ASCII字符串,比如"98"对应的是两个字节的0x39('9'的ASCII码)和0x38('8'的ASCII码),但实际要匹配的是二进制的0x98单字节。 memcmp是直接逐字节比较内存内容,所以用它去对比文件二进制数据和ASCII格式的签名字符串,永远不可能匹配成功。
修正方案:将十六进制签名转为二进制数组
正确的做法是把十六进制签名转换成对应的二进制字节数组,再和文件内容对比。以下是两种可行的实现方式:
方式一:直接定义二进制数组(最简洁高效)
直接把十六进制签名转成二进制字面量,省去解析步骤:
// 每个十六进制对对应一个字节,共16字节 unsigned char given_signature[] = {0x98, 0x1d, 0x00, 0x00, 0xec, 0x33, 0xff, 0xff, 0xfb, 0x06, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10}; #define SIGNATURE_LEN 16
修改后的file_sign函数:
int file_sign(char* path){ FILE* file = fopen(path, "rb"); if(!file){ printf("Failed to open file: %s\n", path); return -1; } fseek(file, 0, SEEK_END); long filelen = ftell(file); fseek(file, 0, SEEK_SET); // 提前判断文件长度是否小于签名长度,避免后续越界 if(filelen < SIGNATURE_LEN){ fclose(file); return 0; } unsigned char* buffer = malloc(filelen); if(!buffer){ printf("Memory allocation failed\n"); fclose(file); return -1; } size_t read_bytes = fread(buffer, 1, filelen, file); // 检查文件是否完整读取 if(read_bytes != filelen){ printf("Incomplete read for file: %s\n", path); free(buffer); fclose(file); return -1; } fclose(file); // 遍历文件内容,逐位置对比二进制签名 for(long i = 0; i <= filelen - SIGNATURE_LEN; i++){ if(memcmp(buffer + i, given_signature, SIGNATURE_LEN) == 0){ printf("Signature found in %s\n", path); // 若只需找第一个匹配,可在此处添加break退出循环 } } free(buffer); return 0; }
方式二:从十六进制字符串数组解析二进制(对应你的思路)
如果你坚持用十六进制字符串数组的形式,可以写一个辅助函数把每个十六进制字符串转成单字节,再进行对比:
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <ctype.h> // 把两位十六进制字符串转成字节 unsigned char hex_str_to_byte(const char* hex_str){ unsigned char byte = 0; // 处理第一个字符 char c = toupper(hex_str[0]); byte |= (c >= 'A' ? (c - 'A' + 10) : (c - '0')) << 4; // 处理第二个字符 c = toupper(hex_str[1]); byte |= (c >= 'A' ? (c - 'A' + 10) : (c - '0')); return byte; } // 定义十六进制字符串数组 char* given_signature_hex[] = {"98", "1d", "00", "00", "ec", "33", "ff", "ff", "fb", "06", "00", "00", "00", "46", "0e", "10"}; #define SIGNATURE_HEX_COUNT 16 int file_sign(char* path){ FILE* file = fopen(path, "rb"); if(!file){ printf("Failed to open file: %s\n", path); return -1; } fseek(file, 0, SEEK_END); long filelen = ftell(file); fseek(file, 0, SEEK_SET); if(filelen < SIGNATURE_HEX_COUNT){ fclose(file); return 0; } unsigned char* buffer = malloc(filelen); if(!buffer){ printf("Memory allocation failed\n"); fclose(file); return -1; } size_t read_bytes = fread(buffer, 1, filelen, file); if(read_bytes != filelen){ printf("Incomplete read for file: %s\n", path); free(buffer); fclose(file); return -1; } fclose(file); // 先把十六进制数组转成二进制数组 unsigned char signature_bin[SIGNATURE_HEX_COUNT]; for(int i=0; i<SIGNATURE_HEX_COUNT; i++){ signature_bin[i] = hex_str_to_byte(given_signature_hex[i]); } // 对比逻辑与方式一一致 for(long i = 0; i <= filelen - SIGNATURE_HEX_COUNT; i++){ if(memcmp(buffer + i, signature_bin, SIGNATURE_HEX_COUNT) == 0){ printf("Signature found in %s\n", path); } } free(buffer); return 0; }
额外优化建议
- 内存优化:不需要把整个文件读入内存,可以每次读入与签名长度相当的内容滑动对比,避免大文件占用过多内存。
- 遍历边界:原代码中
i < filelen -16会漏掉最后一个可能的匹配位置,应改为i <= filelen - SIGNATURE_LEN(当文件长度刚好等于签名长度时,i=0是唯一的匹配位置)。
内容的提问来源于stack exchange,提问作者Jukeland
相关产品推荐
相关产品推荐

