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

C语言可执行文件检测程序优化及代码问题排查咨询

检测可执行文件类型:Shell脚本与二进制文件的准确区分及代码问题修正

现有代码存在的问题

  • 竞态条件漏洞:使用access()检查文件存在性和可执行权限存在TOCTOU(检查时间与使用时间不一致)问题。在access()调用后到后续操作文件的间隙,文件的权限或存在状态可能被篡改,导致判断结果失效。应该用stat()直接获取文件元数据,避免这个问题。
  • 函数命名与功能不符:checkIfFileExists函数实际不仅检查文件存在,还判断可执行权限和计划检测文件类型,命名改为checkExecutableType更准确。
  • 冗余的返回语句:err_quit函数(来自《UNIX环境高级编程》)会直接终止程序,所以在err_quit之后的return 0完全多余,不会被执行。
  • 字符串格式错误:代码中err_quit的参数字符串直接换行,这会导致编译错误,应该用\n来表示换行。
  • 缺少核心类型判断逻辑:仅保留了注释,没有实现区分Shell脚本和二进制文件的代码。

准确区分Shell脚本与二进制文件的高效实现

判断逻辑

  1. Shell脚本:几乎所有可执行脚本都以shebang(#!)开头,这是脚本的标识,读取文件前2字节即可判断。
  2. 二进制可执行文件:主流的Linux/Unix二进制文件是ELF格式,其开头4字节为固定魔数:0x7f 0x45 0x4c 0x46(对应ASCII的\x7fELF),读取前4字节即可识别。
  3. 前置检查:先用stat()判断文件是否为普通文件(S_ISREG),排除目录、设备文件等有执行权限但不是常规可执行文件的情况。

实现步骤

  1. 通过stat()获取文件状态,验证文件存在且为普通文件。
  2. 检查文件是否具有可执行权限(通过stat()返回的st_mode判断,无需access())。
  3. 打开文件,读取前几个字节进行类型判断:
    • 先读取2字节,判断是否为#!,若是则判定为Shell/解释型脚本。
    • 若不是,读取前4字节判断是否为ELF魔数,若是则判定为二进制可执行文件。
    • 其余情况标记为未知类型的可执行文件。

修正后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <apue.h>
#include <sys/stat.h>
#include <fcntl.h>

void checkExecutableType(const char *fileName);

int main(int argc, char *argv[]) 
{
    if (argc < 2)
    {
        err_quit("File to check not specified\n");
    }
    if (argc > 2)
    {
        err_quit("Too many arguments\n");
    }
    checkExecutableType(argv[1]);
    return 0;
}

void checkExecutableType(const char *fileName)
{
    struct stat st;
    // 用stat获取文件信息,同时检查文件存在性
    if (stat(fileName, &st) == -1)
    {
        err_sys("Failed to access file %s", fileName);
    }

    // 检查是否为普通文件
    if (!S_ISREG(st.st_mode))
    {
        printf("The file %s is not a regular file\n", fileName);
        return;
    }

    // 检查当前用户是否有执行权限
    if ((st.st_mode & S_IXUSR) == 0)
    {
        printf("The file %s is not an executable (no user execute permission)\n", fileName);
        return;
    }

    printf("The file %s is an executable\n", fileName);

    // 打开文件检测类型
    int fd = open(fileName, O_RDONLY);
    if (fd == -1)
    {
        err_sys("Failed to open file %s", fileName);
    }

    unsigned char buf[4];
    ssize_t read_len = read(fd, buf, sizeof(buf));
    if (read_len >= 2)
    {
        if (buf[0] == '#' && buf[1] == '!')
        {
            printf("Type: Shell/Interpreted Script\n");
            close(fd);
            return;
        }
    }
    if (read_len >= 4)
    {
        if (buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F')
        {
            printf("Type: ELF Binary Executable\n");
            close(fd);
            return;
        }
    }

    printf("Type: Unknown executable type\n");
    close(fd);
}

补充说明

  • 使用stat()替代access()避免了竞态条件,同时一次性获取文件的类型和权限信息,效率更高。
  • 读取文件头部字节时,只需读取必要的几个字节,无需加载整个文件,保证了检测的高效性。
  • 加入了普通文件的判断,避免误判目录、设备等特殊文件。

内容的提问来源于stack exchange,提问作者Mirel Vlaseanu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 05:54:23