【问题标题】:How to find if a file is an exe?如何判断一个文件是否为exe?
【发布时间】:2010-05-20 18:30:05
【问题描述】:

如何确定传递给我的程序的文件是有效的 exe 文件?

实际上我的程序将一个文件作为输入并运行它,但用户可以输入任何文件,所以我必须确保输入是一个有效的 exe。

【问题讨论】:

  • 有几种有效的可执行文件格式可以相当容易地被怀疑(我会留给我的更好的人来列举)但我的问题是“你疯了吗?” ;-)
  • @Cipi:DLL 也包含该签名。我认为所有 PE 文件都可以。
  • 我当然希望这个程序不是一个网站!您的安全系统是否足够好,您是否足够信任您的用户以运行任意 EXE?
  • @quixoto:你在说什么“错误”?我在这里告诉他标准,它不同于新闻组或其他网络论坛中使用的标准。我没有投反对票或其他任何事情。
  • @Praveen:请看下面我的回答,它比检查文件扩展名或前导魔术字节更深入,但不需要您实际尝试执行文件。它使用 PE 格式的知识进行多次验证。

标签: c#


【解决方案1】:

如果您想要比“文件名是否以 '.exe' 结尾?”更深入的内容?但是您不想实际运行程序,您可以检查 PE 标头的存在和有效性。此外,检查前 2 个字节(PE 文件的“MZ”)对于 DLL 也将返回 true。如果你不想这样,你可以试试这个方法。

Matt Pietrek 写了几篇描述 PE 格式的精彩文章:

这里两个重要的数据结构是IMAGE_DOS_HEADERIMAGE_NT_HEADERS32/IMAGE_NT_HEADERS64。这些结构在 Windows SDK 的winnt.h 中定义。许多这些 PE 结构都被描述为here

您可以使用托管代码处理 PE 标头(类似于 this approach)。以下代码为 32- (i386) 和 64 位 (IA64, AMD64) .exe PE 文件返回 true(例如,为 DLL 返回 false)。用法见底部(ExeChecker.IsValidExe)。如果需要,您可以添加额外的检查以支持更多架构或进行更多验证。有关更多常量,请参阅winnt.h

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace ExeChecker
{
    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_DOS_HEADER
    {
        public ushort e_magic;    // Magic number
        public ushort e_cblp;     // Bytes on last page of file
        public ushort e_cp;       // Pages in file
        public ushort e_crlc;     // Relocations
        public ushort e_cparhdr;  // Size of header in paragraphs
        public ushort e_minalloc; // Minimum extra paragraphs needed
        public ushort e_maxalloc; // Maximum extra paragraphs needed
        public ushort e_ss;       // Initial (relative) SS value
        public ushort e_sp;       // Initial SP value
        public ushort e_csum;     // Checksum
        public ushort e_ip;       // Initial IP value
        public ushort e_cs;       // Initial (relative) CS value
        public ushort e_lfarlc;   // File address of relocation table
        public ushort e_ovno;     // Overlay number
        public uint e_res1;       // Reserved
        public uint e_res2;       // Reserved
        public ushort e_oemid;    // OEM identifier (for e_oeminfo)
        public ushort e_oeminfo;  // OEM information; e_oemid specific
        public uint e_res3;       // Reserved
        public uint e_res4;       // Reserved
        public uint e_res5;       // Reserved
        public uint e_res6;       // Reserved
        public uint e_res7;       // Reserved
        public int e_lfanew;      // File address of new exe header
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_FILE_HEADER
    {
        public ushort Machine;
        public ushort NumberOfSections;
        public uint TimeDateStamp;
        public uint PointerToSymbolTable;
        public uint NumberOfSymbols;
        public ushort SizeOfOptionalHeader;
        public ushort Characteristics;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_NT_HEADERS_COMMON
    {
        public uint Signature;
        public IMAGE_FILE_HEADER FileHeader;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_NT_HEADERS32
    {
        public uint Signature;
        public IMAGE_FILE_HEADER FileHeader;
        public IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_NT_HEADERS64
    {
        public uint Signature;
        public IMAGE_FILE_HEADER FileHeader;
        public IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_OPTIONAL_HEADER32
    {
        public ushort Magic;
        public byte MajorLinkerVersion;
        public byte MinorLinkerVersion;
        public uint SizeOfCode;
        public uint SizeOfInitializedData;
        public uint SizeOfUninitializedData;
        public uint AddressOfEntryPoint;
        public uint BaseOfCode;
        public uint BaseOfData;
        public uint ImageBase;
        public uint SectionAlignment;
        public uint FileAlignment;
        public ushort MajorOperatingSystemVersion;
        public ushort MinorOperatingSystemVersion;
        public ushort MajorImageVersion;
        public ushort MinorImageVersion;
        public ushort MajorSubsystemVersion;
        public ushort MinorSubsystemVersion;
        public uint Win32VersionValue;
        public uint SizeOfImage;
        public uint SizeOfHeaders;
        public uint CheckSum;
        public ushort Subsystem;
        public ushort DllCharacteristics;
        public uint SizeOfStackReserve;
        public uint SizeOfStackCommit;
        public uint SizeOfHeapReserve;
        public uint SizeOfHeapCommit;
        public uint LoaderFlags;
        public uint NumberOfRvaAndSizes;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct IMAGE_OPTIONAL_HEADER64
    {
        public ushort Magic;
        public byte MajorLinkerVersion;
        public byte MinorLinkerVersion;
        public uint SizeOfCode;
        public uint SizeOfInitializedData;
        public uint SizeOfUninitializedData;
        public uint AddressOfEntryPoint;
        public uint BaseOfCode;
        public ulong ImageBase;
        public uint SectionAlignment;
        public uint FileAlignment;
        public ushort MajorOperatingSystemVersion;
        public ushort MinorOperatingSystemVersion;
        public ushort MajorImageVersion;
        public ushort MinorImageVersion;
        public ushort MajorSubsystemVersion;
        public ushort MinorSubsystemVersion;
        public uint Win32VersionValue;
        public uint SizeOfImage;
        public uint SizeOfHeaders;
        public uint CheckSum;
        public ushort Subsystem;
        public ushort DllCharacteristics;
        public ulong SizeOfStackReserve;
        public ulong SizeOfStackCommit;
        public ulong SizeOfHeapReserve;
        public ulong SizeOfHeapCommit;
        public uint LoaderFlags;
        public uint NumberOfRvaAndSizes;
    }

    static class ExeChecker
    {
        public static bool IsValidExe(string fileName)
        {
            if (!File.Exists(fileName))
                return false;

            try
            {
                using (var stream = File.OpenRead(fileName))
                {
                    IMAGE_DOS_HEADER dosHeader = GetDosHeader(stream);
                    if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE)
                        return false;

                    IMAGE_NT_HEADERS_COMMON ntHeader = GetCommonNtHeader(stream, dosHeader);
                    if (ntHeader.Signature != IMAGE_NT_SIGNATURE)
                        return false;

                    if ((ntHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0)
                        return false;

                    switch (ntHeader.FileHeader.Machine)
                    {
                        case IMAGE_FILE_MACHINE_I386:
                            return IsValidExe32(GetNtHeader32(stream, dosHeader));

                        case IMAGE_FILE_MACHINE_IA64:
                        case IMAGE_FILE_MACHINE_AMD64:
                            return IsValidExe64(GetNtHeader64(stream, dosHeader));
                    }
                }
            }
            catch (InvalidOperationException)
            {
                return false;
            }

            return true;
        }

        static bool IsValidExe32(IMAGE_NT_HEADERS32 ntHeader)
        {
            return ntHeader.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC;
        }

        static bool IsValidExe64(IMAGE_NT_HEADERS64 ntHeader)
        {
            return ntHeader.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
        }

        static IMAGE_DOS_HEADER GetDosHeader(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            return ReadStructFromStream<IMAGE_DOS_HEADER>(stream);
        }

        static IMAGE_NT_HEADERS_COMMON GetCommonNtHeader(Stream stream, IMAGE_DOS_HEADER dosHeader)
        {
            stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin);
            return ReadStructFromStream<IMAGE_NT_HEADERS_COMMON>(stream);
        }

        static IMAGE_NT_HEADERS32 GetNtHeader32(Stream stream, IMAGE_DOS_HEADER dosHeader)
        {
            stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin);
            return ReadStructFromStream<IMAGE_NT_HEADERS32>(stream);
        }

        static IMAGE_NT_HEADERS64 GetNtHeader64(Stream stream, IMAGE_DOS_HEADER dosHeader)
        {
            stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin);
            return ReadStructFromStream<IMAGE_NT_HEADERS64>(stream);
        }

        static T ReadStructFromStream<T>(Stream stream)
        {
            int structSize = Marshal.SizeOf(typeof(T));
            IntPtr memory = IntPtr.Zero;

            try
            {
                memory = Marshal.AllocCoTaskMem(structSize);
                if (memory == IntPtr.Zero)
                    throw new InvalidOperationException();

                byte[] buffer = new byte[structSize];
                int bytesRead = stream.Read(buffer, 0, structSize);
                if (bytesRead != structSize)
                    throw new InvalidOperationException();

                Marshal.Copy(buffer, 0, memory, structSize);

                return (T)Marshal.PtrToStructure(memory, typeof(T));
            }
            finally
            {
                if (memory != IntPtr.Zero)
                    Marshal.FreeCoTaskMem(memory);
            }
        }

        const ushort IMAGE_DOS_SIGNATURE = 0x5A4D;  // MZ
        const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00

        const ushort IMAGE_FILE_MACHINE_I386 = 0x014C;  // Intel 386
        const ushort IMAGE_FILE_MACHINE_IA64 = 0x0200;  // Intel 64
        const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664; // AMD64

        const ushort IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10B; // PE32
        const ushort IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20B; // PE32+

        const ushort IMAGE_FILE_DLL = 0x2000;
    }

    class Program
    {
        static int Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Please specify a file name to check.");
                return 1;
            }

            bool isValid = ExeChecker.IsValidExe(args[0]);
            Console.WriteLine(isValid);

            return 0;
        }
    }
}

【讨论】:

    【解决方案2】:

    这取决于你对“有效性”的定义。

    • 如果要验证用户是否传递了“EXE”文件,请检查文件扩展名。
    • 如果要验证用户是否传递了可运行 EXE 文件(无论扩展名如何),请检查文件的前两个字节。它们应包含值“MZ”。

    【讨论】:

    • 那么对于你来说,一个名为hello.exe 并以MZ 开头的文本文件是一个可执行文件吗?
    • 这是正确的答案,你能做到的最好的。
    • @Konrad:我认为真正知道的唯一方法是在偏移量 12h 处验证文件的校验和,但同样,总是有机会(尽管可能很小)值恰好是正确的。
    • Konrad 是完全正确的,此外,您的方法还将任何 dll 归类为可执行文件,但可执行文件还需要有一个入口点,即开始执行的主要方法。另一点是(尽管尚不清楚 OP 是否需要),可执行文件不一定需要 .exe 扩展名。 Windows 中的可执行文件还有其他有效的扩展名(例如 .com 等等,请参阅antivirus.about.com/od/securitytips/a/fileextview.htm)。
    • @Hosam Aly:不。唯一可靠的方法是检查文件中的每个字节,看它是否带有有效的操作码——或者至少检查 完成 EXE 标头和入口点定义。为什么你认为 WinAPI 没有提供一种简单的方法来检查文件是否是有效的可执行文件?因为没有简单的方法存在。
    【解决方案3】:

    一个非常原始的检查是检查文件扩展名:

    Path.GetExtension(filename).Equals(".exe", 
        StringComparison.InvariantCultureIgnoreCase);
    

    但是,Windows 支持多种可执行文件的扩展名(例如 .cmd、.com、.cpl、.scr 和 many more),因此上述检查不会涵盖所有可执行文件。

    正如其他人所提到的,您还可以检查文件头中的幻数是否存在,例如MZ(以及其他一些更罕见的签名)。除了检查扩展名之外,还可以使用第二次检查,尽管您永远无法确定该文件不是以相同文本开头的简单文本文件。

    如果您要启动要检查的可执行文件,那么简单地启动它并进行适当的异常处理可能是最安全的:

    const int ERROR_BAD_EXE_FORMAT = 193;
    try
    {
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.UseShellExecute = false;
        psi.FileName = @"C:\tmp\testOLElocal_4.docx";
        Process.Start(psi);
    }
    catch (Win32Exception ex)
    {
        if (ex.NativeErrorCode == ERROR_BAD_EXE_FORMAT)
        {
            // The exception message would be
            // "The specified executable is not a valid application for this OS platform."
            //
            Console.WriteLine("Not a valid executable.");
        }
        else
        {
            throw;
        }
    }
    

    注意:您没有提及有关您自己的应用程序的任何细节,但无论何时执行来自用户输入的代码,您都应确保您的用户是可信任的。

    【讨论】:

    • 把手伸进去测试它是否燃烧?你肯定会有答案,但我不确定这是否真的是 OP 的想法;o)
    • 如果我通过了 destoroyeverything.exe 会怎样?
    • @Vinzz,当然,您可以随时检查文件扩展名和幻数,btu 要确定文件实际执行,您必须尝试。如果应用程序无论如何都会启动文件,那么适当的异常处理将是可行的方法。
    • @Vinzz:这可能是可以给出的最可靠的答案。即使不是,它肯定是最可靠的答案,无需实现机器代码反汇编程序来检查“exe”中的每个操作码是否有效。
    • @bniwredyc:安全是一个完全不同的问题。但我不会认为传递用户无论如何都可以启动的应用程序过于关键,除非应用程序在不同的安全上下文中执行。
    【解决方案4】:
    bool IsExeFile(string path)
    {
        var twoBytes = new byte[2];
        using(var fileStream = File.Open(path, FileMode.Open))
        {
            fileStream.Read(twoBytes, 0, 2);
        }
    
        return Encoding.UTF8.GetString(twoBytes) == "MZ";
    }
    

    【讨论】:

    • @GSP:Windows 可执行文件以字母 MZ(即十六进制的 4D 5A)开头,这是设计文件格式的 Mark Zbikowski 的首字母。这是 EXE 文件的神奇数字。
    • 我明白了(M:77=0x4D,Z:90=0x5A)。谢谢!
    【解决方案5】:

    您可以使用PE Format DLL检查文件是否为有效的PE格式文件。

    PE 文件可以包含的不仅仅是可执行代码。可以包含资源和代码,或者只包含资源而不包含代码。 PE 文件也可以是本机的或托管的,或者是本机但链接到托管代码等。根据您想要执行的操作,能够检查这些内容会很有用。

    PE_EXE3 pef;
    int     ok = FALSE;
    
    if (pef.openFile(fileNameAndPath))
    {
        if (pef.IsValid())
        {
            ok = pef.GetHasExecutableCode();
        }
        pef.closeFile();
    }
    

    您可能还会发现以下有用的功能:

    pef.GetIsOnlyResource()
    pef.GetHasExecutableCode()
    pef.GetIsPureDotNetModule()
    

    【讨论】:

      【解决方案6】:

      如果您只想检查文件扩展名:

      if (String.Equals(Path.GetExtension(filePath), ".exe", StringComparison.InvariantCultureIgnoreCase))
      {
          ...
      }
      

      如果您需要验证文件是否真正可执行,您可以分析其标题。有.exe文件头信息:link.

      您也可以尝试按照0xA3 的建议运行文件

      【讨论】:

        猜你喜欢
        • 2011-09-12
        • 2011-09-05
        • 1970-01-01
        • 2019-03-20
        • 1970-01-01
        • 2011-04-28
        • 2022-11-22
        • 2014-02-21
        • 2023-03-12
        相关资源
        最近更新 更多