+#!/bin/awk -f
+#
+# Determine the subsystem of a PE32 executable.
+#
+# Copyright © 2022 Nick Bowler
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+BEGIN {
+ filename = ARGV[1];
+ subsys = "unknown";
+ endian = "unknown";
+
+ if (!filename) {
+ exit(1);
+ }
+ probe_dumper(filename);
+
+ od_read("0");
+ if ($2 == "055115") {
+ endian = "little";
+ } else if ($2 == "046532") {
+ endian = "big";
+ } else {
+ exit(1);
+ }
+
+ od_read("74");
+ val = 65536*od_decode($3) + od_decode($2);
+
+ pe_offset = sprintf("%o", val);
+ pe32_offset = sprintf("%o", val+24);
+ subsys_offset = sprintf("%o", val+24+68);
+
+ od_read(pe_offset);
+ if ($2 != "042520" || $3 != "000000") {
+ # bad PE header
+ exit(1);
+ }
+
+ od_read(pe32_offset);
+ if ($2 != "000413" && $2 != "001013") {
+ # bad PE32(+) header
+ exit(1);
+ }
+
+ od_read(subsys_offset);
+ subsys = od_decode($2);
+ if (subsys == 2)
+ subsys = "gui";
+ else if (subsys == 3)
+ subsys = "console";
+ else
+ subsys = "unknown (" subsys ")";
+
+ exit(0);
+}
+
+END {
+ print subsys
+}
+
+function probe_dumper(filename)
+{
+ (cmd = "od " filename " +10 2>/dev/null") | getline
+ close(cmd)
+
+ sub(/^0*/, "", $1);
+ if ($1 == 10) {
+ dumper_style = "od_traditional";
+ return;
+ }
+
+ (cmd = "od -j010 " filename " 2>/dev/null") | getline
+ close(cmd)
+
+ sub(/^0*/, "", $1);
+ if ($1 == 10) {
+ dumper_style = "od_posix";
+ return;
+ }
+
+ exit(1);
+}
+
+function od_read(offset, cmd)
+{
+ if (dumper_style == "od_traditional") {
+ cmd = "od " filename " +" offset;
+ } else if (dumper_style == "od_posix") {
+ cmd = "od -j0" offset " " filename;
+ }
+
+ cmd | getline
+ close(cmd);
+
+ if (endian == "big") {
+ $2 = od_bswap($2);
+ $3 = od_bswap($3);
+ }
+}
+
+# Byte-swap one of the 2-byte blocks of octal digits emitted by od.
+function od_bswap(word, i, digits, carry)
+{
+ for (i = 0; i < 6; i++) {
+ digits[i] = substr(word, i+1, 1);
+ }
+
+ # suss out the first byte by adjusting the first 4 digits left by 1 bit
+ if (carry = (digits[3] >= 4))
+ digits[3] -= 4;
+
+ for (i = 2; i >= 0; i--) {
+ if (carry = ( (digits[i] = digits[i]*2 + carry) >= 8 ))
+ digits[i] -= 8;
+ }
+
+ # now munge the second byte by adjusting the last 3 digits right 1 bit
+ carry = 0;
+ for (i = 3; i < 6; i++) {
+ carry = (digits[i] += 8*carry) % 2;
+ digits[i] = int(digits[i] / 2);
+ }
+
+ # and put the leftover bit in place
+ digits[0] += 4*carry;
+
+ return digits[3] digits[4] digits[5] digits[0] digits[1] digits[2];
+}
+
+# Parse a string of octal digits into a number.
+function od_decode(word, result)
+{
+ result = 0;
+
+ while (word) {
+ result = 8*result + substr(word, 1, 1);
+ word = substr(word, 2);
+ }
+
+ return result;
+}