Skip to content

Instantly share code, notes, and snippets.

@phansson
Last active August 27, 2017 20:16
Show Gist options
  • Save phansson/3160a3f607fd887a2cdf031896115f5c to your computer and use it in GitHub Desktop.
Save phansson/3160a3f607fd887a2cdf031896115f5c to your computer and use it in GitHub Desktop.
Windows EXE utils for Java

Windows .exe utilities

Reads the machine type field from a Windows .exe file. From this we can determine 32-bit vs 64-bit as well as a couple of other things.

/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.phansson.pefilereader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Utilities for getting information about Windows executable files (.exe).
* A Windows executable file adheres to the Windows PE (portable executable)
* file format, the specification of which is fully documented by
* Microsoft.
*
* <p>
* As of Aug 2017 the PE/COFF specification from Microsoft can be found
* at <a href="https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx">
* this location</a>. (download the file called {@code pecoff.docx}).
*
* @author Peter Hansson
*/
public class WinExeUtils {
// In an .exe file the offset location of the PE signature is always
// found in a short val at position 60.
private static int OFFSET_FOR_PE_SIGNATURE = 0x3c; // decimal 60
// The PE signature is always 4 bytes. It consist of the values
// PE\0\0 (the letters 'P' and 'E' followed by two nul bytes).
// We know that what we want - the machine type - is the next two
// bytes immediately following the PE signature.
private static int LENGTH_OF_PE_SIGNATURE = 4;
/**
* Gets the Windows machine type for which the file argument was compiled.
* The file argument is expected to be a Windows executable file (.exe).
*
* <p>
* The method is designed to be fast. It reads sufficient bytes from the
* file to be able to determine the machine type, but not more.
*
* <p>
* The method never returns {@code null}.
*
* @param exeFile
* @return the machine type. This may be {@link WinPEMachineType#IMAGE_FILE_MACHINE_UNKNOWN}
* if the file is not really an executable file, but some other file.
*
* @throws IOException if the file cannot be read or doesn't exist
*/
public static WinPEMachineType getMachineType(File exeFile) throws IOException {
try (InputStream inputStream = new FileInputStream(exeFile)) {
// Read position of PE\0\0 from the byte value at position 3C (=60).
byte[] buffer = new byte[1024];
int noOfBytesRead = inputStream.read(buffer);
if (noOfBytesRead < OFFSET_FOR_PE_SIGNATURE +1) {
// err : not enough bytes in file, probably not an .exe file
return WinPEMachineType.IMAGE_FILE_MACHINE_UNKNOWN;
}
byte peOffsetB1 = buffer[OFFSET_FOR_PE_SIGNATURE];
byte peOffsetB2 = buffer[OFFSET_FOR_PE_SIGNATURE+1];
int peOffset = getShort(peOffsetB1, peOffsetB2);
if (noOfBytesRead < peOffset + LENGTH_OF_PE_SIGNATURE + 2) {
// err : not enough bytes in file, probably not an .exe file
return WinPEMachineType.IMAGE_FILE_MACHINE_UNKNOWN;
}
byte machineTypeB1 = buffer[peOffset + LENGTH_OF_PE_SIGNATURE];
byte machineTypeB2 = buffer[peOffset + LENGTH_OF_PE_SIGNATURE +1];
short machineType = getShort(machineTypeB1, machineTypeB2);
WinPEMachineType mType = WinPEMachineType.valueOf(machineType);
if (mType == null) {
return WinPEMachineType.IMAGE_FILE_MACHINE_UNKNOWN;
} else {
return mType;
}
}
}
/**
* Returns {@code true} if the executable file is 64 bit.
*
* @see #getMachineType(java.io.File)
* @param exeFile Windows .exe file
* @return
* @throws IOException if the file cannot be read or doesn't exist
*/
public static boolean is64Bit(File exeFile) throws IOException {
return (getMachineType(exeFile).getMachineTypeBitness() == 64);
}
/**
* Returns {@code true} if the executable file is 32 bit.
*
* @see #getMachineType(java.io.File)
* @param exeFile Windows .exe file
* @return
* @throws IOException if the file cannot be read or doesn't exist
*/
public static boolean is32Bit(File exeFile) throws IOException {
return (getMachineType(exeFile).getMachineTypeBitness() == 32);
}
// converts two bytes to a short (little endian)
private static short getShort(byte b1, byte b2) {
return (short)((b2 << 8) | (b1 & 0xff));
}
public static void main(String[] args) throws IOException {
WinPEMachineType machineType = getMachineType(new File("C:\\Program Files\\Java\\jdk1.8.0_92\\jre\\bin\\java.exe"));
System.out.println("Machine type : " + machineType);
machineType = getMachineType(new File("C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroRd32.exe"));
System.out.println("Machine type : " + machineType);
}
}
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.phansson.pefilereader;
/**
* Windows Machine Type as they are specified in the COFF File header.
* The COFF file header can be found in Windows executables (.exe), dynamic
* link libraries (.dll) and object files (.obj).
*
* <p>
* See "Microsoft Portable Executable and Common Object File Format Specification"
* for more information (can be downloaded from Microsoft).
*
* @author Peter Hansson
*/
public enum WinPEMachineType {
IMAGE_FILE_MACHINE_UNKNOWN( (short) 0x0, -1),
IMAGE_FILE_MACHINE_AM33( (short) 0x1d3, 32), // Matsushita AM33
IMAGE_FILE_MACHINE_AMD64( (short) 0x8664, 64), // x64
IMAGE_FILE_MACHINE_ARM( (short) 0x1c0, 32), // ARM little endian
IMAGE_FILE_MACHINE_ARM64( (short) 0xaa64, 64), // ARM64 little endian
IMAGE_FILE_MACHINE_ARMNT( (short) 0x1c4, 32), // ARM Thumb-2 little endian
IMAGE_FILE_MACHINE_EBC( (short) 0xebc, -1), // EFI byte code
IMAGE_FILE_MACHINE_I386( (short) 0x14c, 32), // Intel 386 or later processors and compatible processors
IMAGE_FILE_MACHINE_IA64( (short) 0x200, 64), // Intel Itanium processor family
IMAGE_FILE_MACHINE_M32R( (short) 0x9041, 32), // Mitsubishi M32R little endian
IMAGE_FILE_MACHINE_MIPS16( (short) 0x266, 16), // MIPS16
IMAGE_FILE_MACHINE_MIPSFPU( (short) 0x366, 32), // MIPS with FPU
IMAGE_FILE_MACHINE_MIPSFPU16( (short) 0x466, 16), // MIPS16 with FPU
IMAGE_FILE_MACHINE_POWERPC( (short) 0x1f0, 64), // Power PC little endian
IMAGE_FILE_MACHINE_POWERPCFP( (short) 0x1f1, 64), // Power PC with floating point support
IMAGE_FILE_MACHINE_R4000( (short) 0x166, 64), // MIPS little endian
IMAGE_FILE_MACHINE_RISCV32( (short) 0x5032, 32), // RISC-V 32-bit address space
IMAGE_FILE_MACHINE_RISCV64( (short) 0x5064, 64), // RISC-V 64-bit address space
IMAGE_FILE_MACHINE_RISCV128( (short) 0x5128, 128), // RISC-V 128-bit address space
IMAGE_FILE_MACHINE_SH3( (short) 0x1a2, 32), // Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP( (short) 0x1a3, 32), // Hitachi SH3 DSP
IMAGE_FILE_MACHINE_SH4( (short) 0x1a6, 32), // Hitachi SH4
IMAGE_FILE_MACHINE_SH5( (short) 0x1a8, 64), // Hitachi SH5
IMAGE_FILE_MACHINE_THUMB( (short) 0x1c2, 32), // ARM Thumb/Thumb-2 Little-Endian
IMAGE_FILE_MACHINE_WCEMIPSV2( (short) 0x169, 32); // MIPS little-endian WCE v2
private final short shortVal;
private final int bitness;
WinPEMachineType(short shortVal, int bitness) {
this.shortVal = shortVal;
this.bitness = bitness;
}
/**
* Gets type safe enum from short value
* @param shortVal
* @return
*/
public static WinPEMachineType valueOf(short shortVal) {
for (WinPEMachineType p : WinPEMachineType.values()) {
if (p.getShortValue() == shortVal) {
return p;
}
}
return null;
}
/**
* Gets corresponding short value
* @return
*/
public short getShortValue() {
return shortVal;
}
/**
* Gets the 'bitness' of the platform, e.g. 32-bit, 64-bit, etc.
*
* @return 32 for a 32-bit platform, and so on.
*/
public int getMachineTypeBitness() {
return bitness;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment