#!perl -w
use strict;
-unless (@ARGV == 2) {
- print "Usage: $0 exefile [CONSOLE|WINDOWS]\n";
+
+# All the IMAGE_* structures are defined in the WINNT.H file
+# of the Microsoft Platform SDK.
+
+my %subsys = (NATIVE => 1,
+ WINDOWS => 2,
+ CONSOLE => 3,
+ POSIX => 7,
+ WINDOWSCE => 9);
+
+unless (0 < @ARGV && @ARGV < 3) {
+ printf "Usage: $0 exefile [%s]\n", join '|', sort keys %subsys;
exit;
}
-unless ($ARGV[1] =~ /^(console|windows)$/i) {
- print "Invalid subsystem $ARGV[1], please use CONSOLE or WINDOWS\n";
+
+$ARGV[1] = uc $ARGV[1] if $ARGV[1];
+unless (@ARGV == 1 || defined $subsys{$ARGV[1]}) {
+ (my $subsys = join(', ', sort keys %subsys)) =~ s/, (\w+)$/ or $1/;
+ print "Invalid subsystem $ARGV[1], please use $subsys\n";
exit;
}
-my ($record,$magic,$offset,$size);
-open EXE, "+< $ARGV[0]" or die "Cannot open $ARGV[0]: $!";
+
+my ($record,$magic,$signature,$offset,$size);
+open EXE, "+< $ARGV[0]" or die "Cannot open $ARGV[0]: $!\n";
binmode EXE;
-read EXE, $record, 32*4;
+
+# read IMAGE_DOS_HEADER structure
+read EXE, $record, 64;
($magic,$offset) = unpack "Sx58L", $record;
-die "Not an MSDOS executable file" unless $magic == 0x5a4d;
+
+die "$ARGV[0] is not an MSDOS executable file.\n"
+ unless $magic == 0x5a4d; # "MZ"
+
+# read signature, IMAGE_FILE_HEADER and first WORD of IMAGE_OPTIONAL_HEADER
seek EXE, $offset, 0;
-read EXE, $record, 24;
-($magic,$size) = unpack "Lx16S", $record;
-die "PE header not found" unless $magic == 0x4550;
-die "Optional header not in NT32 format" unless $size == 224;
-seek EXE, $offset+24+68, 0;
-print EXE pack "S", uc($ARGV[1]) eq 'CONSOLE' ? 3 : 2;
+read EXE, $record, 4+20+2;
+($signature,$size,$magic) = unpack "Lx16Sx2S", $record;
+
+die "PE header not found" unless $signature == 0x4550; # "PE\0\0"
+
+die "Optional header is neither in NT32 nor in NT64 format"
+ unless ($size == 224 && $magic == 0x10b) || # IMAGE_NT_OPTIONAL_HDR32_MAGIC
+ ($size == 240 && $magic == 0x20b); # IMAGE_NT_OPTIONAL_HDR64_MAGIC
+
+# Offset 68 in the IMAGE_OPTIONAL_HEADER(32|64) is the 16 bit subsystem code
+seek EXE, $offset+4+20+68, 0;
+if (@ARGV == 1) {
+ read EXE, $record, 2;
+ my ($subsys) = unpack "S", $record;
+ $subsys = {reverse %subsys}->{$subsys} || "UNKNOWN($subsys)";
+ print "$ARGV[0] uses the $subsys subsystem.\n";
+}
+else {
+ print EXE pack "S", $subsys{$ARGV[1]};
+}
close EXE;
__END__
The WINDOWS subsystem handles an application that does not require a
console and creates its own windows, if required.
+=item NATIVE
+
+The NATIVE subsystem handles a Windows NT device driver.
+
+=item WINDOWSCE
+
+The WINDOWSCE subsystem handles Windows CE consumer electronics
+applications.
+
+=item POSIX
+
+The POSIX subsystem handles a POSIX application in Windows NT.
+
=back
=head1 AUTHOR