2010-05-19 9 views
16

¿Cómo puedo estar seguro de que un archivo pasado a mi programa es un archivo exe válido?¿Cómo encontrar si un archivo es un exe?

en realidad mi programa toma un archivo como entrada y lo ejecuta, pero el usuario puede ingresar cualquier archivo, así que tengo que asegurarme de que la entrada sea un exe válido.

+0

Para cualquier exe, o simplemente exe .NET? – leppie

+2

hay varios formatos de archivo ejecutables válidos que se pueden suscribir bastante fácilmente (dejaré que mis superiores los enumeren) pero la pregunta que tengo es '¿has perdido la cabeza?' ;-) –

+0

"Exe file" es un término amplio ... Si lee los primeros dos bytes en el archivo, puede saber que es EXE si comienza con 'MZ' (DOS 32bit EXE, o Windows PE EXE), pero nunca puedes estar seguro hasta que intentes ejecutarlo. – Cipi

Respuesta

9

Depende de su definición de "validez".

  • Si desea validar que el usuario pasa un archivo "EXE", verifique la extensión del archivo.
  • Si desea validar que el usuario pasa un archivo EXE ejecutable (independientemente de la extensión), verifique los primeros dos bytes del archivo. Deben contener el valor "MZ".
+5

¿Entonces para usted un archivo de texto que se llama 'hello.exe' y comienza con' MZ' es un archivo ejecutable? –

+0

Esta es la respuesta correcta, lo mejor que podrá hacer. – NibblyPig

+0

MZ o ZM http://faydoc.tripod.com/structures/15/1594.htm – bniwredyc

7

Un registro muy primitiva sería para comprobar la extensión de archivo:

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

Sin embargo, Windows es compatible con una variedad de extensiones para archivos ejecutables (por ejemplo, .cmd, .com, .cpl, .scr y many more), por lo que la comprobación anterior no cubriría todos los archivos ejecutables.

Según lo mencionado por otros, también puede verificar los números mágicos en el encabezado del archivo para ver la existencia de, p. MZ (y algunas otras firmas más raras). Esta segunda verificación se podría usar además de verificar la (s) extensión (es), aunque nunca se puede estar seguro de que el archivo no sea un simple archivo de texto que empiece accidentalmente con el mismo texto.

Si va a iniciar el ejecutable que comprobar todos modos, entonces es probable que sea más seguro para simplemente empezar con adecuado manejo de excepciones:

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; 
    } 
} 

Nota: Usted no ha mencionado ningún detalle acerca de su propia aplicación, pero cada vez que ejecute código que proviene de la entrada del usuario, debe asegurarse de que se pueda confiar en su usuario.

+3

¿Prueba si quema poniendo la mano dentro? seguro tendrá una respuesta, pero no estoy seguro de que sea realmente lo que el OP tenía en mente; o) – Vinzz

+0

¿Qué pasa si paso el destoroyeverything.exe? – bniwredyc

+1

@Vinzz, por supuesto, siempre puedes comprobar las extensiones de archivos y los números mágicos, pero para estar absolutamente seguro de que el archivo realmente se ejecuta, deberías intentarlo. Y en caso de que la aplicación inicie el archivo de todos modos, el manejo adecuado de las excepciones sería el camino a seguir. –

0

Si desea simplemente marque la extensión de archivo:

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

Si es necesario verificar que el archivo está realmente ejecutable que puede analizar su encabezado. Hay información sobre el encabezado del archivo .exe: link.

También puede intentar ejecutar el archivo como 0xA3 sugirió

16

Si quieres algo más profundo que "hace el extremo nombre de archivo en '.exe'?" pero no desea tener que ejecutar realmente el programa, puede verificar la existencia y validez de los encabezados PE. Además, comprobar los 2 bytes iniciales ("MZ" para los archivos PE) también devolverá verdadero para los archivos DLL. Si no quieres eso, puedes probar este enfoque.

Matt Pietrek ha escrito un par de grandes artículos que describen el formato PE:

Las dos estructuras de datos importantes aquí son IMAGE_DOS_HEADER y IMAGE_NT_HEADERS32/IMAGE_NT_HEADERS64.Estas estructuras se definen en winnt.h en el SDK de Windows. Muchas de estas estructuras PE se describen here.

Puede trabajar con los encabezados PE utilizando el código administrado (similar a this approach). El siguiente código devuelve verdadero para archivos PE 32- (i386) y 64 bits (IA64, AMD64) .exe (por ejemplo, devuelve falso para DLL). Ver la parte inferior para el uso (ExeChecker.IsValidExe). Si lo desea, puede agregar comprobaciones adicionales para admitir más arquitecturas o para hacer más validación. Consulte winnt.h para obtener más constantes.

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; 
     } 
    } 
} 
+6

Gracias. Esto debería haber sido marcado como la respuesta correcta. – ahmd0

3
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"; 
} 
+0

¿Cuál es su número mágico? – GSP

+0

@GSP: los ejecutables de Windows comienzan con las letras MZ (4D 5A en hexadecimal) que son las iniciales de Mark Zbikowski que diseñó el formato de archivo. Este es el número mágico para los archivos EXE. –

+0

Veo (M: 77 = 0x4D, Z: 90 = 0x5A). ¡Gracias! – GSP

2

Puede comprobar si un archivo es un archivo de formato PE válida utilizando el PE Format DLL.

Los archivos PE pueden contener más que solo código ejecutable. Puede contener recursos, así como código o solo recursos y ningún código. Los archivos PE también pueden ser nativos o administrados, o nativos pero vinculados al código administrado, etc. Dependiendo de lo que desee hacer, sería útil verificar estos elementos.

PE_EXE3 pef; 
int  ok = FALSE; 

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

Usted también puede encontrar las siguientes funciones útiles:

pef.GetIsOnlyResource() 
pef.GetHasExecutableCode() 
pef.GetIsPureDotNetModule() 
Cuestiones relacionadas