]> git.draconx.ca Git - dxcommon.git/blob - scripts/pe-subsys.awk
DX_C_ALIGNAS: Work around bash-5 parsing bug.
[dxcommon.git] / scripts / pe-subsys.awk
1 #!/bin/awk -f
2 #
3 # Determine the subsystem of a PE32 executable.
4 #
5 # Copyright © 2022 Nick Bowler
6 #
7 # License WTFPL2: Do What The Fuck You Want To Public License, version 2.
8 # This is free software: you are free to do what the fuck you want to.
9 # There is NO WARRANTY, to the extent permitted by law.
10
11 BEGIN {
12   filename = ARGV[1];
13   subsys = "unknown";
14   endian = "unknown";
15
16   if (!filename) {
17     exit(1);
18   }
19   probe_dumper(filename);
20
21   od_read("0");
22   if ($2 == "055115") {
23     endian = "little";
24   } else if ($2 == "046532") {
25     endian = "big";
26   } else {
27     exit(1);
28   }
29
30   od_read("74");
31   val = 65536*od_decode($3) + od_decode($2);
32
33   pe_offset = sprintf("%o", val);
34   pe32_offset = sprintf("%o", val+24);
35   subsys_offset = sprintf("%o", val+24+68);
36
37   od_read(pe_offset);
38   if ($2 != "042520" || $3 != "000000") {
39     # bad PE header
40     exit(1);
41   }
42
43   od_read(pe32_offset);
44   if ($2 != "000413" && $2 != "001013") {
45     # bad PE32(+) header
46     exit(1);
47   }
48
49   od_read(subsys_offset);
50   subsys = od_decode($2);
51   if (subsys == 2)
52     subsys = "gui";
53   else if (subsys == 3)
54     subsys = "console";
55   else
56     subsys = "unknown (" subsys ")";
57
58   exit(0);
59 }
60
61 END {
62   print subsys
63 }
64
65 function probe_dumper(filename)
66 {
67   (cmd = "od " filename " +10 2>/dev/null") | getline
68   close(cmd)
69
70   sub(/^0*/, "", $1);
71   if ($1 == 10) {
72     dumper_style = "od_traditional";
73     return;
74   }
75
76   (cmd = "od -j010 " filename " 2>/dev/null") | getline
77   close(cmd)
78
79   sub(/^0*/, "", $1);
80   if ($1 == 10) {
81     dumper_style = "od_posix";
82     return;
83   }
84
85   exit(1);
86 }
87
88 function od_read(offset, cmd)
89 {
90   if (dumper_style == "od_traditional") {
91     cmd = "od " filename " +" offset;
92   } else if (dumper_style == "od_posix") {
93     cmd = "od -j0" offset " " filename;
94   }
95
96   cmd | getline
97   close(cmd);
98
99   if (endian == "big") {
100     $2 = od_bswap($2);
101     $3 = od_bswap($3);
102   }
103 }
104
105 # Byte-swap one of the 2-byte blocks of octal digits emitted by od.
106 function od_bswap(word, i, digits, carry)
107 {
108   for (i = 0; i < 6; i++) {
109     digits[i] = substr(word, i+1, 1);
110   }
111
112   # suss out the first byte by adjusting the first 4 digits left by 1 bit
113   if (carry = (digits[3] >= 4))
114     digits[3] -= 4;
115
116   for (i = 2; i >= 0; i--) {
117     if (carry = ( (digits[i] = digits[i]*2 + carry) >= 8 ))
118       digits[i] -= 8;
119   }
120
121   # now munge the second byte by adjusting the last 3 digits right 1 bit
122   carry = 0;
123   for (i = 3; i < 6; i++) {
124     carry = (digits[i] += 8*carry) % 2;
125     digits[i] = int(digits[i] / 2);
126   }
127
128   # and put the leftover bit in place
129   digits[0] += 4*carry;
130
131   return digits[3] digits[4] digits[5] digits[0] digits[1] digits[2];
132 }
133
134 # Parse a string of octal digits into a number.
135 function od_decode(word, result)
136 {
137   result = 0;
138
139   while (word) {
140     result = 8*result + substr(word, 1, 1);
141     word = substr(word, 2);
142   }
143
144   return result;
145 }