views:

1111

answers:

5

I have a need to work with Windows executables which are made for x86, x64, and IA64. I'd like to programmatically figure out the platform by examining the files themselves.

My target language is PowerShell but a C# example will do. Failing either of those, if you know the logic required that would be great.

A: 

Unix OS have a utility called "file" which identifies files. The rules for identifying are kept in a description file called "magic". You could try file to see if it is able to identify your files correctly and grab the appropriate rules out of the magic file.

Sec
+2  A: 
Assembly assembly = Assembly.LoadFile(Path.GetFullPath("ConsoleApplication1.exe"));
Module manifestModule = assembly.ManifestModule;
PortableExecutableKinds peKind;
ImageFileMachine machine;
manifestModule.GetPEKind(out peKind, out machine);

The target machine should then be in machine.

That'll only work with .NET assemblies though.

ICR
+7  A: 

You need the GetBinaryType win32 function. This will return the relevant parts of the PE-format executable.

Typically, you'll get either SCS_32BIT_BINARY or SCS_64BIT_BINARY in the BinaryType field,

Alternativaly you can check the PE format itself to see what architecture the executable is compiled for.

The IMAGE_FILE_HEADER.Machine field will have "IMAGE_FILE_MACHINE_IA64" set for IA64 binaries, IMAGE_FILE_MACHINE_I386 for 32-bit and IMAGE_FILE_MACHINE_AMD64 for 64-bit (ie x86_64).

There's a MSDN magazine article to help you get going.

Addendum: This may help you a little more. You read the binary as a file: check the first 2 bytes say "MZ", then skip the next 58 bytes and read the magic 32-bit value at 60 bytes into the image (which equals 0x00004550 for PE executables). The following bytes are this header, the first 2 bytes of which tell you which machine the binary is designed for (0x8664 = x86_64, 0x0200 = IA64, 0x014c = i386).

(executive summary: read bytes 65 and 66 of the file to get the image type)

gbjbaanb
That's more informative than helpful in my case. My fault, not yours. :) I need something which gets me closer.
halr9000
sorry old chap, I'm not au-fait with powershell, but I hope I set you on the right path. see my edit.
gbjbaanb
I'll have a go at this next week and may end up marking yours as "the answer".
halr9000
A: 

I can offer a link to some C# code for accessing the IMAGE_FILE_HEADER, which I think could be (easily) compiled into a PowerShell cmdlet. I'm reasonably sure you can't use that method in PowerShell script directly, since it lacks pointers and PInvoke capability.

However, you should be able to use your by now extensive knowledge of the PE header format ;-) to just go "straight" to the right bytes and figure it out. This will work in PowerShell script, and you should be able to just convert this C# code from Tasos' blog to script. I won't bother repeating the code here since it's not mine.

Jaykul
+2  A: 

(from another Q, since removed)

Machine type: This is a quick little bit of code I based on some that gets the linker timestamp. This is in the same header, and it seems to work - it returns I386 when compiled -any cpu-, and x64 when compiled with that as the target platform.

The Exploring PE Headers (K. Stanton,MSDN) blog entry that showed me the offset, as another response noted.

public enum MachineType {
    Native = 0, I386 = 0x014c, Itanium = 0x0200, x64 = 0x8664
}

public static MachineType GetMachineType(string fileName)
{
    const int PE_POINTER_OFFSET = 60;            
    const int MACHINE_OFFSET = 4;
    byte[] data = new byte[4096];
    using (Stream s = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
        s.Read(data, 0, 4096);
    }
    // dos header is 64 bytes, last element, long (4 bytes) is the address of the PE header
    int PE_HEADER_ADDR = BitConverter.ToInt32(data, PE_POINTER_OFFSET);
    int machineUint = BitConverter.ToUInt16(data, PE_HEADER_ADDR + MACHINE_OFFSET);
    return (MachineType)machineUint;
}
Andrew Backer