Instantly share code, notes, and snippets.

Embed
What would you like to do?
Display values of MS-DOS Portable Executable header
// Needs Guava to compile:
//
// compile group: 'com.google.guava', name: 'guava', version: '23.4-jre'
//
import com.google.common.io.LittleEndianDataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.joining;
public class Main {
// from: http://www.ntcore.com/files/inject2exe.htm#TheMSDOSdata2_1
private final static List<HeaderField> msdosHeaderFields = Arrays.asList(
new HeaderField("e_magic", "c2", "Magic number 'MZ'"),
new HeaderField("e_cblp", "s", "Bytes on last page of file"),
new HeaderField("e_cp", "s", "Pages in file"),
new HeaderField("e_crlc", "s", "Relocations"),
new HeaderField("e_cparhdr", "s", "Size of header in paragraphs"),
new HeaderField("e_minalloc", "s", "Minimum extra paragraphs needed"),
new HeaderField("e_maxalloc", "s", "Maximum extra paragraphs needed"),
new HeaderField("e_ss", "s", "Initial (relative) SS value"),
new HeaderField("e_sp", "s", "Initial SP value"),
new HeaderField("e_csum", "s", "Checksum"),
new HeaderField("e_ip", "s", "Initial IP value"),
new HeaderField("e_cs", "s", "Initial (relative) CS value"),
new HeaderField("e_lfarlc", "s", "File address of relocation table"),
new HeaderField("e_ovno", "s", "Overlay number"),
new HeaderField("e_res", "c8", "Reserved ss"),
new HeaderField("e_oemid", "s", "OEM identifier (for e_oeminfo)"),
new HeaderField("e_oeminfo", "s", "OEM information; e_oemid specific"),
new HeaderField("e_res2", "c20", "Reserved ss"),
new HeaderField("e_lfanew", "l", "File address of the new exe header")
);
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("usage: " + System.getProperty("sun.java.command") + " program.com");
System.exit(1);
}
String comProgramFilename = args[0];
// Data in COM and PE file headers is always stored using little-endian convention.
// This means that an int whose value is 0x11223344 will
// be represented with bytes 0x44 0x33 0x22 0x11 in exactly that order.
// In other words little-endian conventions tell us that we should store
// LSB first and MSB last.
// Here we use LittleEndianDataInputStream from Guava library.
try (LittleEndianDataInputStream dataInputStream =
new LittleEndianDataInputStream(new FileInputStream(comProgramFilename))) {
for (HeaderField field : msdosHeaderFields) {
switch (field.type) {
case "s":
print(field.name, String.format("0x%04x",
Short.toUnsignedInt(dataInputStream.readShort())),
field.comment);
break;
case "l":
print(field.name,
String.format("0x%08x", dataInputStream.readInt()),
field.comment);
break;
default: // cN - format
int numberOfBytes = Integer.parseInt(field.type.substring(1));
String hexString = IntStream.range(0, numberOfBytes)
.mapToObj(i -> {
try {
byte c = dataInputStream.readByte();
return String.format("%02x", Byte.toUnsignedInt(c));
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.collect(joining(" "));
print(field.name, hexString, field.comment);
break;
}
}
}
}
private static void print(String fieldName, String fieldValue, String comment) {
System.out.printf("%16s: %-16s // %s%n", fieldName, fieldValue, comment);
}
private static class HeaderField {
public final String name;
/** s - short, l - long, cN - N characters */
public final String type;
public final String comment;
public HeaderField(String fieldName, String fieldSize, String comment) {
this.name = fieldName;
this.type = fieldSize;
this.comment = comment;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment