1 | #------------------------------------------------------------------------------
|
---|
2 | # File: EXE.pm
|
---|
3 | #
|
---|
4 | # Description: Read meta information of various executable file formats
|
---|
5 | #
|
---|
6 | # Revisions: 2008/08/28 - P. Harvey Created
|
---|
7 | #
|
---|
8 | # References: 1) http://www.openwatcom.org/ftp/devel/docs/pecoff.pdf
|
---|
9 | # 2) http://support.microsoft.com/kb/65122
|
---|
10 | # 3) http://www.opensource.apple.com
|
---|
11 | # 4) http://www.skyfree.org/linux/references/ELF_Format.pdf
|
---|
12 | # 5) http://msdn.microsoft.com/en-us/library/ms809762.aspx
|
---|
13 | # 6) http://code.google.com/p/pefile/
|
---|
14 | # 7) http://www.codeproject.com/KB/DLL/showver.aspx
|
---|
15 | #------------------------------------------------------------------------------
|
---|
16 |
|
---|
17 | package Image::ExifTool::EXE;
|
---|
18 |
|
---|
19 | use strict;
|
---|
20 | use vars qw($VERSION);
|
---|
21 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
22 |
|
---|
23 | $VERSION = '1.04';
|
---|
24 |
|
---|
25 | sub ProcessPEResources($$);
|
---|
26 | sub ProcessPEVersion($$);
|
---|
27 |
|
---|
28 | # PE file resource types (ref 6)
|
---|
29 | my %resourceType = (
|
---|
30 | 1 => 'Cursor',
|
---|
31 | 2 => 'Bitmap',
|
---|
32 | 3 => 'Icon',
|
---|
33 | 4 => 'Menu',
|
---|
34 | 5 => 'Dialog',
|
---|
35 | 6 => 'String',
|
---|
36 | 7 => 'Font Dir',
|
---|
37 | 8 => 'Font',
|
---|
38 | 9 => 'Accelerator',
|
---|
39 | 10 => 'RC Data',
|
---|
40 | 11 => 'Message Table',
|
---|
41 | 12 => 'Group Cursor',
|
---|
42 | 14 => 'Group Icon',
|
---|
43 | 16 => 'Version',
|
---|
44 | 17 => 'Dialog Include',
|
---|
45 | 19 => 'Plug-n-Play',
|
---|
46 | 20 => 'VxD',
|
---|
47 | 21 => 'Animated Cursor',
|
---|
48 | 22 => 'Animated Icon',
|
---|
49 | 23 => 'HTML',
|
---|
50 | 24 => 'Manifest',
|
---|
51 | );
|
---|
52 |
|
---|
53 | # Information extracted from PE COFF (Windows EXE) file header
|
---|
54 | %Image::ExifTool::EXE::Main = (
|
---|
55 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
56 | GROUPS => { 2 => 'Other' },
|
---|
57 | FORMAT => 'int16u',
|
---|
58 | NOTES => q{
|
---|
59 | This module extracts information from various types of Windows, MacOS and
|
---|
60 | Unix executable and library files. The first table below lists information
|
---|
61 | extracted from the header of Windows PE (Portable Executable) EXE files and
|
---|
62 | DLL libraries.
|
---|
63 | },
|
---|
64 | 0 => {
|
---|
65 | Name => 'MachineType',
|
---|
66 | PrintHex => 1,
|
---|
67 | PrintConv => {
|
---|
68 | 0x014c => 'Intel 386 or later, and compatibles',
|
---|
69 | 0x014d => 'Intel i860', #5
|
---|
70 | 0x0162 => 'MIPS R3000',
|
---|
71 | 0x0166 => 'MIPS little endian (R4000)',
|
---|
72 | 0x0168 => 'MIPS R10000',
|
---|
73 | 0x0169 => 'MIPS little endian WCI v2',
|
---|
74 | 0x0183 => 'Alpha AXP (old)', #5
|
---|
75 | 0x0184 => 'Alpha AXP',
|
---|
76 | 0x01a2 => 'Hitachi SH3',
|
---|
77 | 0x01a3 => 'Hitachi SH3 DSP',
|
---|
78 | 0x01a6 => 'Hitachi SH4',
|
---|
79 | 0x01a8 => 'Hitachi SH5',
|
---|
80 | 0x01c0 => 'ARM little endian',
|
---|
81 | 0x01c2 => 'Thumb',
|
---|
82 | 0x01d3 => 'Matsushita AM33',
|
---|
83 | 0x01f0 => 'PowerPC little endian',
|
---|
84 | 0x01f1 => 'PowerPC with floating point support',
|
---|
85 | 0x0200 => 'Intel IA64',
|
---|
86 | 0x0266 => 'MIPS16',
|
---|
87 | 0x0268 => 'Motorola 68000 series',
|
---|
88 | 0x0284 => 'Alpha AXP 64-bit',
|
---|
89 | 0x0366 => 'MIPS with FPU',
|
---|
90 | 0x0466 => 'MIPS16 with FPU',
|
---|
91 | 0x0ebc => 'EFI Byte Code',
|
---|
92 | 0x8664 => 'AMD AMD64',
|
---|
93 | 0x9041 => 'Mitsubishi M32R little endian',
|
---|
94 | 0xc0ee => 'clr pure MSIL',
|
---|
95 | },
|
---|
96 | },
|
---|
97 | 2 => {
|
---|
98 | Name => 'TimeStamp',
|
---|
99 | Format => 'int32u',
|
---|
100 | Groups => { 2 => 'Time' },
|
---|
101 | ValueConv => 'ConvertUnixTime($val,1)',
|
---|
102 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
103 | },
|
---|
104 | 10 => {
|
---|
105 | Name => 'PEType',
|
---|
106 | PrintHex => 1,
|
---|
107 | PrintConv => {
|
---|
108 | 0x10b => 'PE32',
|
---|
109 | 0x20b => 'PE32+',
|
---|
110 | },
|
---|
111 | },
|
---|
112 | 11 => {
|
---|
113 | Name => 'LinkerVersion',
|
---|
114 | Format => 'int8u[2]',
|
---|
115 | ValueConv => '$val=~tr/ /./; $val',
|
---|
116 | },
|
---|
117 | 12 => {
|
---|
118 | Name => 'CodeSize',
|
---|
119 | Format => 'int32u',
|
---|
120 | },
|
---|
121 | 14 => {
|
---|
122 | Name => 'InitializedDataSize',
|
---|
123 | Format => 'int32u',
|
---|
124 | },
|
---|
125 | 16 => {
|
---|
126 | Name => 'UninitializedDataSize',
|
---|
127 | Format => 'int32u',
|
---|
128 | },
|
---|
129 | 18 => {
|
---|
130 | Name => 'EntryPoint',
|
---|
131 | Format => 'int32u',
|
---|
132 | PrintConv => 'sprintf("0x%.4x", $val)',
|
---|
133 | },
|
---|
134 | 30 => {
|
---|
135 | Name => 'OSVersion',
|
---|
136 | Format => 'int16u[2]',
|
---|
137 | ValueConv => '$val=~tr/ /./; $val',
|
---|
138 | },
|
---|
139 | 32 => {
|
---|
140 | Name => 'ImageVersion',
|
---|
141 | Format => 'int16u[2]',
|
---|
142 | ValueConv => '$val=~tr/ /./; $val',
|
---|
143 | },
|
---|
144 | 34 => {
|
---|
145 | Name => 'SubsystemVersion',
|
---|
146 | Format => 'int16u[2]',
|
---|
147 | ValueConv => '$val=~tr/ /./; $val',
|
---|
148 | },
|
---|
149 | 44 => {
|
---|
150 | Name => 'Subsystem',
|
---|
151 | PrintConv => {
|
---|
152 | 0 => 'Unknown',
|
---|
153 | 1 => 'Native',
|
---|
154 | 2 => 'Windows GUI',
|
---|
155 | 3 => 'Windows command line',
|
---|
156 | 5 => 'OS/2 Command line', #5
|
---|
157 | 7 => 'POSIX command line',
|
---|
158 | 9 => 'Windows CE GUI',
|
---|
159 | 10 => 'EFI application',
|
---|
160 | 11 => 'EFI boot service',
|
---|
161 | 12 => 'EFI runtime driver',
|
---|
162 | 13 => 'EFI ROM', #6
|
---|
163 | 14 => 'XBOX', #6
|
---|
164 | },
|
---|
165 | },
|
---|
166 | );
|
---|
167 |
|
---|
168 | # PE file version information (ref 6)
|
---|
169 | %Image::ExifTool::EXE::PEVersion = (
|
---|
170 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
171 | GROUPS => { 2 => 'Other' },
|
---|
172 | FORMAT => 'int32u',
|
---|
173 | NOTES => q{
|
---|
174 | Information extracted from the VS_VERSION_INFO structure of Windows PE
|
---|
175 | files.
|
---|
176 | },
|
---|
177 | # (boring -- always 0xfeef04bd)
|
---|
178 | #0 => {
|
---|
179 | # Name => 'Signature',
|
---|
180 | # PrintConv => 'sprintf("0x%.4x",$val)',
|
---|
181 | #},
|
---|
182 | # (boring -- always 1.0)
|
---|
183 | #1 => {
|
---|
184 | # Name => 'StructVersion',
|
---|
185 | # Format => 'int16u[2]',
|
---|
186 | # ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0]"',
|
---|
187 | #},
|
---|
188 | 2 => {
|
---|
189 | Name => 'FileVersionNumber',
|
---|
190 | Format => 'int16u[4]',
|
---|
191 | ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0].$a[3].$a[2]"',
|
---|
192 | },
|
---|
193 | 4 => {
|
---|
194 | Name => 'ProductVersionNumber',
|
---|
195 | Format => 'int16u[4]',
|
---|
196 | ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0].$a[3].$a[2]"',
|
---|
197 | },
|
---|
198 | 6 => {
|
---|
199 | Name => 'FileFlagsMask',
|
---|
200 | PrintConv => 'sprintf("0x%.4x",$val)',
|
---|
201 | },
|
---|
202 | 7 => { # ref Cygwin /usr/include/w32api/winver.h
|
---|
203 | Name => 'FileFlags',
|
---|
204 | PrintConv => { BITMASK => {
|
---|
205 | 0 => 'Debug',
|
---|
206 | 1 => 'Pre-release',
|
---|
207 | 2 => 'Patched',
|
---|
208 | 3 => 'Private build',
|
---|
209 | 4 => 'Info inferred',
|
---|
210 | 5 => 'Special build',
|
---|
211 | }},
|
---|
212 | },
|
---|
213 | 8 => {
|
---|
214 | Name => 'FileOS',
|
---|
215 | PrintHex => 1,
|
---|
216 | PrintConv => { # ref Cygwin /usr/include/w32api/winver.h
|
---|
217 | 0x00001 => 'Win16',
|
---|
218 | 0x00002 => 'PM-16',
|
---|
219 | 0x00003 => 'PM-32',
|
---|
220 | 0x00004 => 'Win32',
|
---|
221 | 0x10000 => 'DOS',
|
---|
222 | 0x20000 => 'OS/2 16-bit',
|
---|
223 | 0x30000 => 'OS/2 32-bit',
|
---|
224 | 0x40000 => 'Windows NT',
|
---|
225 | 0x10001 => 'Windows 16-bit',
|
---|
226 | 0x10004 => 'Windows 32-bit',
|
---|
227 | 0x20002 => 'OS/2 16-bit PM-16',
|
---|
228 | 0x30003 => 'OS/2 32-bit PM-32',
|
---|
229 | 0x40004 => 'Windows NT 32-bit',
|
---|
230 | },
|
---|
231 | },
|
---|
232 | 9 => { # ref Cygwin /usr/include/w32api/winver.h
|
---|
233 | Name => 'ObjectFileType',
|
---|
234 | PrintConv => {
|
---|
235 | 0 => 'Unknown',
|
---|
236 | 1 => 'Executable application',
|
---|
237 | 2 => 'Dynamic link library',
|
---|
238 | 3 => 'Driver',
|
---|
239 | 4 => 'Font',
|
---|
240 | 5 => 'VxD',
|
---|
241 | 7 => 'Static library',
|
---|
242 | },
|
---|
243 | },
|
---|
244 | 10 => 'FileSubtype',
|
---|
245 | # (these are usually zero, so ignore them)
|
---|
246 | # 11 => 'FileDateMS',
|
---|
247 | # 12 => 'FileDateLS',
|
---|
248 | );
|
---|
249 |
|
---|
250 | # Windows PE StringFileInfo resource strings
|
---|
251 | # (see http://msdn.microsoft.com/en-us/library/aa381049.aspx)
|
---|
252 | %Image::ExifTool::EXE::PEString = (
|
---|
253 | GROUPS => { 2 => 'Other' },
|
---|
254 | VARS => { NO_ID => 1 },
|
---|
255 | NOTES => q{
|
---|
256 | Resource strings found in Windows PE files. The B<TagID>'s are not shown
|
---|
257 | because they are the same as the B<Tag Name>. ExifTool will extract any
|
---|
258 | existing StringFileInfo tags even if not listed in this table.
|
---|
259 | },
|
---|
260 | LanguageCode => {
|
---|
261 | Notes => 'extracted from the StringFileInfo value',
|
---|
262 | # ref http://techsupt.winbatch.com/TS/T000001050F49.html
|
---|
263 | # (also see http://support.bigfix.com/fixlet/documents/WinInspectors-2006-08-10.pdf)
|
---|
264 | # (also see ftp://ftp.dyu.edu.tw/pub/cpatch/faq/tech/tech_nlsnt.txt)
|
---|
265 | # (not a complete set)
|
---|
266 | PrintString => 1,
|
---|
267 | PrintConv => {
|
---|
268 | '0000' => 'Neutral',
|
---|
269 | '007F' => 'Invariant',
|
---|
270 | '0400' => 'Process default',
|
---|
271 | '0401' => 'Arabic',
|
---|
272 | '0402' => 'Bulgarian',
|
---|
273 | '0403' => 'Catalan',
|
---|
274 | '0404' => 'Chinese (Traditional)',
|
---|
275 | '0405' => 'Czech',
|
---|
276 | '0406' => 'Danish',
|
---|
277 | '0407' => 'German',
|
---|
278 | '0408' => 'Greek',
|
---|
279 | '0409' => 'English (U.S.)',
|
---|
280 | '040A' => 'Spanish (Castilian)',
|
---|
281 | '040B' => 'Finnish',
|
---|
282 | '040C' => 'French',
|
---|
283 | '040D' => 'Hebrew',
|
---|
284 | '040E' => 'Hungarian',
|
---|
285 | '040F' => 'Icelandic',
|
---|
286 | '0410' => 'Italian',
|
---|
287 | '0411' => 'Japanese',
|
---|
288 | '0412' => 'Korean',
|
---|
289 | '0413' => 'Dutch',
|
---|
290 | '0414' => 'Norwegian (Bokml)',
|
---|
291 | '0415' => 'Polish',
|
---|
292 | '0416' => 'Portuguese (Brazilian)',
|
---|
293 | '0417' => 'Rhaeto-Romanic',
|
---|
294 | '0418' => 'Romanian',
|
---|
295 | '0419' => 'Russian',
|
---|
296 | '041A' => 'Croato-Serbian (Latin)',
|
---|
297 | '041B' => 'Slovak',
|
---|
298 | '041C' => 'Albanian',
|
---|
299 | '041D' => 'Swedish',
|
---|
300 | '041E' => 'Thai',
|
---|
301 | '041F' => 'Turkish',
|
---|
302 | '0420' => 'Urdu',
|
---|
303 | # 0421-0493 ref 6
|
---|
304 | '0421' => 'Indonesian',
|
---|
305 | '0422' => 'Ukrainian',
|
---|
306 | '0423' => 'Belarusian',
|
---|
307 | '0424' => 'Slovenian',
|
---|
308 | '0425' => 'Estonian',
|
---|
309 | '0426' => 'Latvian',
|
---|
310 | '0427' => 'Lithuanian',
|
---|
311 | '0428' => 'Maori',
|
---|
312 | '0429' => 'Farsi',
|
---|
313 | '042a' => 'Vietnamese',
|
---|
314 | '042b' => 'Armenian',
|
---|
315 | '042c' => 'Azeri',
|
---|
316 | '042d' => 'Basque',
|
---|
317 | '042e' => 'Sorbian',
|
---|
318 | '042f' => 'Macedonian',
|
---|
319 | '0430' => 'Sutu',
|
---|
320 | '0431' => 'Tsonga',
|
---|
321 | '0432' => 'Tswana',
|
---|
322 | '0433' => 'Venda',
|
---|
323 | '0434' => 'Xhosa',
|
---|
324 | '0435' => 'Zulu',
|
---|
325 | '0436' => 'Afrikaans',
|
---|
326 | '0437' => 'Georgian',
|
---|
327 | '0438' => 'Faeroese',
|
---|
328 | '0439' => 'Hindi',
|
---|
329 | '043a' => 'Maltese',
|
---|
330 | '043b' => 'Saami',
|
---|
331 | '043c' => 'Gaelic',
|
---|
332 | '043e' => 'Malay',
|
---|
333 | '043f' => 'Kazak',
|
---|
334 | '0440' => 'Kyrgyz',
|
---|
335 | '0441' => 'Swahili',
|
---|
336 | '0443' => 'Uzbek',
|
---|
337 | '0444' => 'Tatar',
|
---|
338 | '0445' => 'Bengali',
|
---|
339 | '0446' => 'Punjabi',
|
---|
340 | '0447' => 'Gujarati',
|
---|
341 | '0448' => 'Oriya',
|
---|
342 | '0449' => 'Tamil',
|
---|
343 | '044a' => 'Telugu',
|
---|
344 | '044b' => 'Kannada',
|
---|
345 | '044c' => 'Malayalam',
|
---|
346 | '044d' => 'Assamese',
|
---|
347 | '044e' => 'Marathi',
|
---|
348 | '044f' => 'Sanskrit',
|
---|
349 | '0450' => 'Mongolian',
|
---|
350 | '0456' => 'Galician',
|
---|
351 | '0457' => 'Konkani',
|
---|
352 | '0458' => 'Manipuri',
|
---|
353 | '0459' => 'Sindhi',
|
---|
354 | '045a' => 'Syriac',
|
---|
355 | '0460' => 'Kashmiri',
|
---|
356 | '0461' => 'Nepali',
|
---|
357 | '0465' => 'Divehi',
|
---|
358 | '047f' => 'Invariant',
|
---|
359 | '048f' => 'Esperanto',
|
---|
360 | '0490' => 'Walon',
|
---|
361 | '0491' => 'Cornish',
|
---|
362 | '0492' => 'Welsh',
|
---|
363 | '0493' => 'Breton',
|
---|
364 | '0800' => 'Neutral 2',
|
---|
365 | '0804' => 'Chinese (Simplified)',
|
---|
366 | '0807' => 'German (Swiss)',
|
---|
367 | '0809' => 'English (British)',
|
---|
368 | '080A' => 'Spanish (Mexican)',
|
---|
369 | '080C' => 'French (Belgian)',
|
---|
370 | '0810' => 'Italian (Swiss)',
|
---|
371 | '0813' => 'Dutch (Belgian)',
|
---|
372 | '0814' => 'Norwegian (Nynorsk)',
|
---|
373 | '0816' => 'Portuguese',
|
---|
374 | '081A' => 'Serbo-Croatian (Cyrillic)',
|
---|
375 | '0C07' => 'German (Austrian)',
|
---|
376 | '0C09' => 'English (Australian)',
|
---|
377 | '0C0A' => 'Spanish (Modern)',
|
---|
378 | '0C0C' => 'French (Canadian)',
|
---|
379 | '1009' => 'English (Canadian)',
|
---|
380 | '100C' => 'French (Swiss)',
|
---|
381 | },
|
---|
382 | },
|
---|
383 | CharacterSet => {
|
---|
384 | Notes => 'extracted from the StringFileInfo value',
|
---|
385 | # ref http://techsupt.winbatch.com/TS/T000001050F49.html
|
---|
386 | # (also see http://blog.chinaunix.net/u1/41189/showart_345768.html)
|
---|
387 | PrintString => 1,
|
---|
388 | PrintConv => {
|
---|
389 | '0000' => 'ASCII',
|
---|
390 | '03A4' => 'Windows, Japan (Shift - JIS X-0208)', # cp932
|
---|
391 | '03A8' => 'Windows, Chinese (Simplified)', # cp936
|
---|
392 | '03B5' => 'Windows, Korea (Shift - KSC 5601)', # cp949
|
---|
393 | '03B6' => 'Windows, Taiwan (Big5)', # cp950
|
---|
394 | '04B0' => 'Unicode', # UCS-2
|
---|
395 | '04E2' => 'Windows, Latin2 (Eastern European)',
|
---|
396 | '04E3' => 'Windows, Cyrillic',
|
---|
397 | '04E4' => 'Windows, Latin1',
|
---|
398 | '04E5' => 'Windows, Greek',
|
---|
399 | '04E6' => 'Windows, Turkish',
|
---|
400 | '04E7' => 'Windows, Hebrew',
|
---|
401 | '04E8' => 'Windows, Arabic',
|
---|
402 | },
|
---|
403 | },
|
---|
404 | BuildDate => { Groups => { 2 => 'Time' } }, # (non-standard)
|
---|
405 | BuildVersion => { }, # (non-standard)
|
---|
406 | Comments => { },
|
---|
407 | CompanyName => { },
|
---|
408 | Copyright => { }, # (non-standard)
|
---|
409 | FileDescription => { },
|
---|
410 | FileVersion => { },
|
---|
411 | InternalName => { },
|
---|
412 | LegalCopyright => { },
|
---|
413 | LegalTrademarks => { },
|
---|
414 | OriginalFilename=> { },
|
---|
415 | PrivateBuild => { },
|
---|
416 | ProductName => { },
|
---|
417 | ProductVersion => { },
|
---|
418 | SpecialBuild => { },
|
---|
419 | );
|
---|
420 |
|
---|
421 | # Information extracted from Mach-O (Mac OS X) file header
|
---|
422 | %Image::ExifTool::EXE::MachO = (
|
---|
423 | GROUPS => { 2 => 'Other' },
|
---|
424 | VARS => { ID_LABEL => 'Index' },
|
---|
425 | NOTES => q{
|
---|
426 | Information extracted from Mach-O (Mac OS X) executable files and DYLIB
|
---|
427 | libraries.
|
---|
428 | },
|
---|
429 | # ref http://www.opensource.apple.com/darwinsource/DevToolsOct2007/cctools-622.9/include/mach/machine.h
|
---|
430 | 0 => 'CPUArchitecture',
|
---|
431 | 1 => 'CPUByteOrder',
|
---|
432 | 2 => 'CPUCount',
|
---|
433 | # ref /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/mach/machine.h
|
---|
434 | 3 => {
|
---|
435 | Name => 'CPUType',
|
---|
436 | List => 1,
|
---|
437 | PrintConv => {
|
---|
438 | # handle 64-bit flag (0x1000000)
|
---|
439 | OTHER => sub {
|
---|
440 | my ($val, $inv, $conv) = @_;
|
---|
441 | my $v = $val & 0xfeffffff;
|
---|
442 | return $$conv{$v} ? "$$conv{$v} 64-bit" : "Unknown ($val)";
|
---|
443 | },
|
---|
444 | -1 => 'Any',
|
---|
445 | 1 => 'VAX',
|
---|
446 | 2 => 'ROMP',
|
---|
447 | 4 => 'NS32032',
|
---|
448 | 5 => 'NS32332',
|
---|
449 | 6 => 'MC680x0',
|
---|
450 | 7 => 'x86',
|
---|
451 | 8 => 'MIPS',
|
---|
452 | 9 => 'NS32532',
|
---|
453 | 10 => 'MC98000',
|
---|
454 | 11 => 'HPPA',
|
---|
455 | 12 => 'ARM',
|
---|
456 | 13 => 'MC88000',
|
---|
457 | 14 => 'SPARC',
|
---|
458 | 15 => 'i860 big endian',
|
---|
459 | 16 => 'i860 little endian',
|
---|
460 | 17 => 'RS6000',
|
---|
461 | 18 => 'PowerPC',
|
---|
462 | 255 => 'VEO',
|
---|
463 | },
|
---|
464 | },
|
---|
465 | # ref /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/mach/machine.h
|
---|
466 | 4 => {
|
---|
467 | Name => 'CPUSubtype',
|
---|
468 | List => 1,
|
---|
469 | PrintConv => {
|
---|
470 | # handle 64-bit flags on CPUType (0x1000000) and CPUSubtype (0x80000000)
|
---|
471 | OTHER => sub {
|
---|
472 | my ($val, $inv, $conv) = @_;
|
---|
473 | my @v = split ' ', $val;
|
---|
474 | my $v = ($v[0] & 0xfeffffff) . ' ' . ($v[1] & 0x7fffffff);
|
---|
475 | return $$conv{$v} ? "$$conv{$v} 64-bit" : "Unknown ($val)";
|
---|
476 | },
|
---|
477 | # in theory, subtype can be -1 for multiple CPU types,
|
---|
478 | # but in practice I'm not sure anyone uses this - PH
|
---|
479 | '1 0' => 'VAX (all)',
|
---|
480 | '1 1' => 'VAX780',
|
---|
481 | '1 2' => 'VAX785',
|
---|
482 | '1 3' => 'VAX750',
|
---|
483 | '1 4' => 'VAX730',
|
---|
484 | '1 5' => 'UVAXI',
|
---|
485 | '1 6' => 'UVAXII',
|
---|
486 | '1 7' => 'VAX8200',
|
---|
487 | '1 8' => 'VAX8500',
|
---|
488 | '1 9' => 'VAX8600',
|
---|
489 | '1 10' => 'VAX8650',
|
---|
490 | '1 11' => 'VAX8800',
|
---|
491 | '1 12' => 'UVAXIII',
|
---|
492 | '2 0' => 'RT (all)',
|
---|
493 | '2 1' => 'RT PC',
|
---|
494 | '2 2' => 'RT APC',
|
---|
495 | '2 3' => 'RT 135',
|
---|
496 | # 32032/32332/32532 subtypes.
|
---|
497 | '4 0' => 'NS32032 (all)',
|
---|
498 | '4 1' => 'NS32032 DPC (032 CPU)',
|
---|
499 | '4 2' => 'NS32032 SQT',
|
---|
500 | '4 3' => 'NS32032 APC FPU (32081)',
|
---|
501 | '4 4' => 'NS32032 APC FPA (Weitek)',
|
---|
502 | '4 5' => 'NS32032 XPC (532)',
|
---|
503 | '5 0' => 'NS32332 (all)',
|
---|
504 | '5 1' => 'NS32332 DPC (032 CPU)',
|
---|
505 | '5 2' => 'NS32332 SQT',
|
---|
506 | '5 3' => 'NS32332 APC FPU (32081)',
|
---|
507 | '5 4' => 'NS32332 APC FPA (Weitek)',
|
---|
508 | '5 5' => 'NS32332 XPC (532)',
|
---|
509 | '6 1' => 'MC680x0 (all)',
|
---|
510 | '6 2' => 'MC68040',
|
---|
511 | '6 3' => 'MC68030',
|
---|
512 | '7 3' => 'i386 (all)',
|
---|
513 | '7 4' => 'i486',
|
---|
514 | '7 132' => 'i486SX',
|
---|
515 | '7 5' => 'i586',
|
---|
516 | '7 22' => 'Pentium Pro',
|
---|
517 | '7 54' => 'Pentium II M3',
|
---|
518 | '7 86' => 'Pentium II M5',
|
---|
519 | '7 103' => 'Celeron',
|
---|
520 | '7 119' => 'Celeron Mobile',
|
---|
521 | '7 8' => 'Pentium III',
|
---|
522 | '7 24' => 'Pentium III M',
|
---|
523 | '7 40' => 'Pentium III Xeon',
|
---|
524 | '7 9' => 'Pentium M',
|
---|
525 | '7 10' => 'Pentium 4',
|
---|
526 | '7 26' => 'Pentium 4 M',
|
---|
527 | '7 11' => 'Itanium',
|
---|
528 | '7 27' => 'Itanium 2',
|
---|
529 | '7 12' => 'Xeon',
|
---|
530 | '7 28' => 'Xeon MP',
|
---|
531 | '8 0' => 'MIPS (all)',
|
---|
532 | '8 1' => 'MIPS R2300',
|
---|
533 | '8 2' => 'MIPS R2600',
|
---|
534 | '8 3' => 'MIPS R2800',
|
---|
535 | '8 4' => 'MIPS R2000a',
|
---|
536 | '8 5' => 'MIPS R2000',
|
---|
537 | '8 6' => 'MIPS R3000a',
|
---|
538 | '8 7' => 'MIPS R3000',
|
---|
539 | '10 0' => 'MC98000 (all)',
|
---|
540 | '10 1' => 'MC98601',
|
---|
541 | '11 0' => 'HPPA (all)',
|
---|
542 | '11 1' => 'HPPA 7100LC',
|
---|
543 | '12 0' => 'ARM (all)',
|
---|
544 | '12 1' => 'ARM A500 ARCH',
|
---|
545 | '12 2' => 'ARM A500',
|
---|
546 | '12 3' => 'ARM A440',
|
---|
547 | '12 4' => 'ARM M4',
|
---|
548 | '12 5' => 'ARM A680/V4T',
|
---|
549 | '12 6' => 'ARM V6',
|
---|
550 | '12 7' => 'ARM V5TEJ',
|
---|
551 | '12 8' => 'ARM XSCALE',
|
---|
552 | '12 9' => 'ARM V7',
|
---|
553 | '13 0' => 'MC88000 (all)',
|
---|
554 | '13 1' => 'MC88100',
|
---|
555 | '13 2' => 'MC88110',
|
---|
556 | '14 0' => 'SPARC (all)',
|
---|
557 | '14 1' => 'SUN 4/260',
|
---|
558 | '14 2' => 'SUN 4/110',
|
---|
559 | '15 0' => 'i860 (all)',
|
---|
560 | '15 1' => 'i860 860',
|
---|
561 | '16 0' => 'i860 little (all)',
|
---|
562 | '16 1' => 'i860 little',
|
---|
563 | '17 0' => 'RS6000 (all)',
|
---|
564 | '17 1' => 'RS6000',
|
---|
565 | '18 0' => 'PowerPC (all)',
|
---|
566 | '18 1' => 'PowerPC 601',
|
---|
567 | '18 2' => 'PowerPC 602',
|
---|
568 | '18 3' => 'PowerPC 603',
|
---|
569 | '18 4' => 'PowerPC 603e',
|
---|
570 | '18 5' => 'PowerPC 603ev',
|
---|
571 | '18 6' => 'PowerPC 604',
|
---|
572 | '18 7' => 'PowerPC 604e',
|
---|
573 | '18 8' => 'PowerPC 620',
|
---|
574 | '18 9' => 'PowerPC 750',
|
---|
575 | '18 10' => 'PowerPC 7400',
|
---|
576 | '18 11' => 'PowerPC 7450',
|
---|
577 | '18 100' => 'PowerPC 970',
|
---|
578 | '255 1' => 'VEO 1',
|
---|
579 | '255 2' => 'VEO 2',
|
---|
580 | },
|
---|
581 | },
|
---|
582 | 5 => {
|
---|
583 | Name => 'ObjectFileType',
|
---|
584 | PrintHex => 1,
|
---|
585 | # ref https://svn.red-bean.com/pyobjc/branches/pyobjc-20x-branch/macholib/macholib/mach_o.py
|
---|
586 | PrintConv => {
|
---|
587 | -1 => 'Static library', #PH (internal use only)
|
---|
588 | 1 => 'Relocatable object',
|
---|
589 | 2 => 'Demand paged executable',
|
---|
590 | 3 => 'Fixed VM shared library',
|
---|
591 | 4 => 'Core',
|
---|
592 | 5 => 'Preloaded executable',
|
---|
593 | 6 => 'Dynamically bound shared library',
|
---|
594 | 7 => 'Dynamic link editor',
|
---|
595 | 8 => 'Dynamically bound bundle',
|
---|
596 | 9 => 'Shared library stub for static linking',
|
---|
597 | },
|
---|
598 | },
|
---|
599 | );
|
---|
600 |
|
---|
601 | # Information extracted from PEF (Classic MacOS executable) file header
|
---|
602 | %Image::ExifTool::EXE::PEF = (
|
---|
603 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
604 | GROUPS => { 2 => 'Other' },
|
---|
605 | NOTES => q{
|
---|
606 | Information extracted from PEF (Classic MacOS) executable files and
|
---|
607 | libraries.
|
---|
608 | },
|
---|
609 | FORMAT => 'int32u',
|
---|
610 | 2 => {
|
---|
611 | Name => 'CPUArchitecture',
|
---|
612 | Format => 'undef[4]',
|
---|
613 | PrintConv => {
|
---|
614 | pwpc => 'PowerPC',
|
---|
615 | m68k => '68000',
|
---|
616 | },
|
---|
617 | },
|
---|
618 | 3 => 'PEFVersion',
|
---|
619 | 4 => {
|
---|
620 | Name => 'TimeStamp',
|
---|
621 | Groups => { 2 => 'Time' },
|
---|
622 | # timestamp is relative to Jan 1, 1904
|
---|
623 | ValueConv => 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
|
---|
624 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
625 | },
|
---|
626 | #5 => 'OldDefVersion',
|
---|
627 | #6 => 'OldImpVersion',
|
---|
628 | #7 => 'CurrentVersion',
|
---|
629 | );
|
---|
630 |
|
---|
631 | # Information extracted from ELF (Unix executable) file header
|
---|
632 | %Image::ExifTool::EXE::ELF = (
|
---|
633 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
634 | GROUPS => { 2 => 'Other' },
|
---|
635 | NOTES => q{
|
---|
636 | Information extracted from ELF (Unix) executable files and SO libraries.
|
---|
637 | },
|
---|
638 | 4 => {
|
---|
639 | Name => 'CPUArchitecture',
|
---|
640 | PrintConv => {
|
---|
641 | 1 => '32 bit',
|
---|
642 | 2 => '64 bit',
|
---|
643 | },
|
---|
644 | },
|
---|
645 | 5 => {
|
---|
646 | Name => 'CPUByteOrder',
|
---|
647 | PrintConv => {
|
---|
648 | 1 => 'Little endian',
|
---|
649 | 2 => 'Big endian',
|
---|
650 | },
|
---|
651 | },
|
---|
652 | 16 => {
|
---|
653 | Name => 'ObjectFileType',
|
---|
654 | Format => 'int16u',
|
---|
655 | PrintConv => {
|
---|
656 | 0 => 'None',
|
---|
657 | 1 => 'Relocatable file',
|
---|
658 | 2 => 'Executable file',
|
---|
659 | 3 => 'Shared object file',
|
---|
660 | 4 => 'Core file',
|
---|
661 | },
|
---|
662 | },
|
---|
663 | 18 => {
|
---|
664 | Name => 'CPUType',
|
---|
665 | Format => 'int16u',
|
---|
666 | # ref /usr/include/linux/elf-em.h
|
---|
667 | PrintConv => {
|
---|
668 | 0 => 'None',
|
---|
669 | 1 => 'AT&T WE 32100',
|
---|
670 | 2 => 'SPARC',
|
---|
671 | 3 => 'i386',
|
---|
672 | 4 => 'Motorola 68000',
|
---|
673 | 5 => 'Motorola 88000',
|
---|
674 | 6 => 'i486',
|
---|
675 | 7 => 'i860',
|
---|
676 | 8 => 'MIPS R3000',
|
---|
677 | 10 => 'MIPS R4000',
|
---|
678 | 15 => 'HPPA',
|
---|
679 | 18 => 'Sun v8plus',
|
---|
680 | 20 => 'PowerPC',
|
---|
681 | 21 => 'PowerPC 64-bit',
|
---|
682 | 22 => 'IBM S/390',
|
---|
683 | 23 => 'Cell BE SPU',
|
---|
684 | 42 => 'SuperH',
|
---|
685 | 43 => 'SPARC v9 64-bit',
|
---|
686 | 46 => 'Renesas H8/300,300H,H8S',
|
---|
687 | 50 => 'HP/Intel IA-64',
|
---|
688 | 62 => 'AMD x86-64',
|
---|
689 | 76 => 'Axis Communications 32-bit embedded processor',
|
---|
690 | 87 => 'NEC v850',
|
---|
691 | 88 => 'Renesas M32R',
|
---|
692 | 0x5441 => 'Fujitsu FR-V',
|
---|
693 | 0x9026 => 'Alpha', # (interim value)
|
---|
694 | 0x9041 => 'm32r (old)',
|
---|
695 | 0x9080 => 'v850 (old)',
|
---|
696 | 0xa390 => 'S/390 (old)',
|
---|
697 | },
|
---|
698 | },
|
---|
699 | );
|
---|
700 |
|
---|
701 | #------------------------------------------------------------------------------
|
---|
702 | # Read Unicode string (null terminated) from resource
|
---|
703 | # Inputs: 0) data ref, 1) start offset, 2) optional ExifTool object ref
|
---|
704 | # Returns: 0) Unicode string translated to UTF8, or current CharSet with ExifTool ref
|
---|
705 | # 1) end pos (rounded up to nearest 4 bytes)
|
---|
706 | sub ReadUnicodeStr($$;$)
|
---|
707 | {
|
---|
708 | my ($dataPt, $pos, $exifTool) = @_;
|
---|
709 | my $len = length $$dataPt;
|
---|
710 | my $str = '';
|
---|
711 | while ($pos + 2 <= $len) {
|
---|
712 | my $ch = substr($$dataPt, $pos, 2);
|
---|
713 | $pos += 2;
|
---|
714 | last if $ch eq "\0\0";
|
---|
715 | $str .= $ch;
|
---|
716 | }
|
---|
717 | $pos += 2 if $pos & 0x03;
|
---|
718 | my $to = $exifTool ? $exifTool->Options('Charset') : 'UTF8';
|
---|
719 | return (Image::ExifTool::Decode(undef,$str,'UCS2','II',$to), $pos);
|
---|
720 | }
|
---|
721 |
|
---|
722 | #------------------------------------------------------------------------------
|
---|
723 | # Process Windows PE Version Resource
|
---|
724 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref
|
---|
725 | # Returns: true on success
|
---|
726 | sub ProcessPEVersion($$)
|
---|
727 | {
|
---|
728 | my ($exifTool, $dirInfo) = @_;
|
---|
729 | my $dataPt = $$dirInfo{DataPt};
|
---|
730 | my $pos = $$dirInfo{DirStart};
|
---|
731 | my $end = $pos + $$dirInfo{DirLen};
|
---|
732 | my ($index, $len, $valLen, $type, $string, $strEnd);
|
---|
733 |
|
---|
734 | # get VS_VERSION_INFO
|
---|
735 | for ($index = 0; ; ++$index) {
|
---|
736 | $pos = ($pos + 3) & 0xfffffffc; # align on a 4-byte boundary
|
---|
737 | last if $pos + 6 > $end;
|
---|
738 | $len = Get16u($dataPt, $pos);
|
---|
739 | $valLen = Get16u($dataPt, $pos + 2);
|
---|
740 | $type = Get16u($dataPt, $pos + 4);
|
---|
741 | return 0 unless $len or $valLen; # prevent possible infinite loop
|
---|
742 | ($string, $strEnd) = ReadUnicodeStr($dataPt, $pos + 6);
|
---|
743 | return 0 if $strEnd + $valLen > $end;
|
---|
744 | unless ($index or $string eq 'VS_VERSION_INFO') {
|
---|
745 | $exifTool->Warn('Invalid Version Info block');
|
---|
746 | return 0;
|
---|
747 | }
|
---|
748 | if ($string eq 'VS_VERSION_INFO') {
|
---|
749 | # parse the fixed version info
|
---|
750 | $$dirInfo{DirStart} = $strEnd;
|
---|
751 | $$dirInfo{DirLen} = $valLen;
|
---|
752 | my $subTablePtr = GetTagTable('Image::ExifTool::EXE::PEVersion');
|
---|
753 | $exifTool->ProcessDirectory($dirInfo, $subTablePtr);
|
---|
754 | $pos = $strEnd + $valLen;
|
---|
755 | } elsif ($string eq 'StringFileInfo' and $valLen == 0) {
|
---|
756 | $pos += $len;
|
---|
757 | my $pt = $strEnd;
|
---|
758 | # parse string table
|
---|
759 | my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::PEString');
|
---|
760 | for ($index = 0; $pt + 6 < $pos; ++$index) {
|
---|
761 | $len = Get16u($dataPt, $pt);
|
---|
762 | $valLen = Get16u($dataPt, $pt + 2);
|
---|
763 | # $type = Get16u($dataPt, $pt + 4);
|
---|
764 | # get tag ID (converted to UTF8)
|
---|
765 | ($string, $pt) = ReadUnicodeStr($dataPt, $pt + 6);
|
---|
766 | unless ($index) {
|
---|
767 | # separate the language code and character set
|
---|
768 | # (not sure what the CharacterSet tag is for, but the string
|
---|
769 | # values stored here are UCS-2 in all my files even if the
|
---|
770 | # CharacterSet is otherwise)
|
---|
771 | my $char;
|
---|
772 | if (length($string) > 4) {
|
---|
773 | $char = substr($string, 4);
|
---|
774 | $string = substr($string, 0, 4);
|
---|
775 | }
|
---|
776 | $exifTool->HandleTag($tagTablePtr, 'LanguageCode', uc $string);
|
---|
777 | $exifTool->HandleTag($tagTablePtr, 'CharacterSet', uc $char) if $char;
|
---|
778 | next;
|
---|
779 | }
|
---|
780 | my $tag = $string;
|
---|
781 | # create entry in tag table if it doesn't already exist
|
---|
782 | unless ($$tagTablePtr{$tag}) {
|
---|
783 | my $name = $tag;
|
---|
784 | $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
|
---|
785 | next unless length $name;
|
---|
786 | Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });
|
---|
787 | }
|
---|
788 | # get tag value (converted to current Charset)
|
---|
789 | if ($valLen) {
|
---|
790 | ($string, $pt) = ReadUnicodeStr($dataPt, $pt, $exifTool);
|
---|
791 | } else {
|
---|
792 | $string = '';
|
---|
793 | }
|
---|
794 | $exifTool->HandleTag($tagTablePtr, $tag, $string);
|
---|
795 | }
|
---|
796 | } else {
|
---|
797 | $pos += $len + $valLen;
|
---|
798 | # ignore other information (for now)
|
---|
799 | }
|
---|
800 | }
|
---|
801 | return 1;
|
---|
802 | }
|
---|
803 |
|
---|
804 | #------------------------------------------------------------------------------
|
---|
805 | # Process Windows PE Resources
|
---|
806 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref
|
---|
807 | # Returns: true on success
|
---|
808 | sub ProcessPEResources($$)
|
---|
809 | {
|
---|
810 | my ($exifTool, $dirInfo) = @_;
|
---|
811 | my $raf = $$dirInfo{RAF};
|
---|
812 | my $base = $$dirInfo{Base};
|
---|
813 | my $dirStart = $$dirInfo{DirStart} + $base;
|
---|
814 | my $level = $$dirInfo{Level} || 0;
|
---|
815 | my $verbose = $exifTool->Options('Verbose');
|
---|
816 | my ($buff, $buf2, $item);
|
---|
817 |
|
---|
818 | return 0 if $level > 10; # protect against deep recursion
|
---|
819 | # read the resource header
|
---|
820 | $raf->Seek($dirStart, 0) and $raf->Read($buff, 16) == 16 or return 0;
|
---|
821 | my $nameEntries = Get16u(\$buff, 12);
|
---|
822 | my $idEntries = Get16u(\$buff, 14);
|
---|
823 | my $count = $nameEntries + $idEntries;
|
---|
824 | $raf->Read($buff, $count * 8) == $count * 8 or return 0;
|
---|
825 | # loop through all resource entries
|
---|
826 | for ($item=0; $item<$count; ++$item) {
|
---|
827 | my $pos = $item * 8;
|
---|
828 | my $name = Get32u(\$buff, $pos);
|
---|
829 | my $entryPos = Get32u(\$buff, $pos + 4);
|
---|
830 | unless ($level) {
|
---|
831 | # set resource type if this is the 0th level directory
|
---|
832 | my $resType = $resourceType{$name} || sprintf('Unknown (0x%x)', $name);
|
---|
833 | # ignore everything but the Version resource unless verbose
|
---|
834 | if ($verbose) {
|
---|
835 | $exifTool->VPrint(0, "$resType resource:\n");
|
---|
836 | } else {
|
---|
837 | next unless $resType eq 'Version';
|
---|
838 | }
|
---|
839 | $$dirInfo{ResType} = $resType;
|
---|
840 | }
|
---|
841 | if ($entryPos & 0x80000000) { # is this a directory?
|
---|
842 | # descend into next directory level
|
---|
843 | $$dirInfo{DirStart} = $entryPos & 0x7fffffff;
|
---|
844 | $$dirInfo{Level} = $level + 1;
|
---|
845 | ProcessPEResources($exifTool, $dirInfo) or return 0;
|
---|
846 | --$$dirInfo{Level};
|
---|
847 | } elsif ($$dirInfo{ResType} eq 'Version' and $level == 2 and
|
---|
848 | not $$dirInfo{GotVersion}) # (only process first Version resource)
|
---|
849 | {
|
---|
850 | # get position of this resource in the file
|
---|
851 | my $buf2;
|
---|
852 | $raf->Seek($entryPos + $base, 0) and $raf->Read($buf2, 16) == 16 or return 0;
|
---|
853 | my $off = Get32u(\$buf2, 0);
|
---|
854 | my $len = Get32u(\$buf2, 4);
|
---|
855 | # determine which section this is in so we can convert the virtual address
|
---|
856 | my ($section, $filePos);
|
---|
857 | foreach $section (@{$$dirInfo{Sections}}) {
|
---|
858 | next unless $off >= $$section{VirtualAddress} and
|
---|
859 | $off < $$section{VirtualAddress} + $$section{Size};
|
---|
860 | $filePos = $off + $$section{Base} - $$section{VirtualAddress};
|
---|
861 | last;
|
---|
862 | }
|
---|
863 | return 0 unless $filePos;
|
---|
864 | $raf->Seek($filePos, 0) and $raf->Read($buf2, $len) == $len or return 0;
|
---|
865 | ProcessPEVersion($exifTool, {
|
---|
866 | DataPt => \$buf2,
|
---|
867 | DataLen => $len,
|
---|
868 | DirStart => 0,
|
---|
869 | DirLen => $len,
|
---|
870 | }) or $exifTool->Warn('Possibly corrupt Version resource');
|
---|
871 | $$dirInfo{GotVersion} = 1; # set flag so we don't do this again
|
---|
872 | }
|
---|
873 | }
|
---|
874 | return 1;
|
---|
875 | }
|
---|
876 |
|
---|
877 | #------------------------------------------------------------------------------
|
---|
878 | # Process Windows PE file data dictionary
|
---|
879 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref
|
---|
880 | # Returns: true on success
|
---|
881 | sub ProcessPEDict($$)
|
---|
882 | {
|
---|
883 | my ($exifTool, $dirInfo) = @_;
|
---|
884 | my $raf = $$dirInfo{RAF};
|
---|
885 | my $dataPt = $$dirInfo{DataPt};
|
---|
886 | my $dirLen = length($$dataPt);
|
---|
887 | my ($pos, @sections, %dirInfo);
|
---|
888 |
|
---|
889 | # loop through all sections
|
---|
890 | for ($pos=0; $pos+40<=$dirLen; $pos+=40) {
|
---|
891 | my $name = substr($$dataPt, $pos, 8);
|
---|
892 | my $va = Get32u($dataPt, $pos + 12);
|
---|
893 | my $size = Get32u($dataPt, $pos + 16);
|
---|
894 | my $offset = Get32u($dataPt, $pos + 20);
|
---|
895 | # remember the section offsets for the VirtualAddress lookup later
|
---|
896 | push @sections, { Base => $offset, Size => $size, VirtualAddress => $va };
|
---|
897 | # save details of the first resource section
|
---|
898 | %dirInfo = (
|
---|
899 | RAF => $raf,
|
---|
900 | Base => $offset,
|
---|
901 | DirStart => 0, # (relative to Base)
|
---|
902 | DirLen => $size,
|
---|
903 | Sections => \@sections,
|
---|
904 | ) if $name eq ".rsrc\0\0\0" and not %dirInfo;
|
---|
905 | }
|
---|
906 | # process the first resource section
|
---|
907 | ProcessPEResources($exifTool, \%dirInfo) or return 0 if %dirInfo;
|
---|
908 | return 1;
|
---|
909 | }
|
---|
910 |
|
---|
911 | #------------------------------------------------------------------------------
|
---|
912 | # Extract information from an EXE file
|
---|
913 | # Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
---|
914 | # Returns: 1 on success, 0 if this wasn't a valid EXE file
|
---|
915 | sub ProcessEXE($$)
|
---|
916 | {
|
---|
917 | my ($exifTool, $dirInfo) = @_;
|
---|
918 | my $raf = $$dirInfo{RAF};
|
---|
919 | my ($buff, $buf2, $type, $tagTablePtr, %dirInfo);
|
---|
920 |
|
---|
921 | my $size = $raf->Read($buff, 0x40) or return 0;
|
---|
922 | #
|
---|
923 | # DOS and Windows EXE
|
---|
924 | #
|
---|
925 | if ($buff =~ /^MZ/ and $size == 0x40) {
|
---|
926 | # DOS/Windows executable
|
---|
927 | # validate DOS header
|
---|
928 | # (ref http://www.delphidabbler.com/articles?article=8&part=2)
|
---|
929 | # 0 magic : int16u # Magic number ("MZ")
|
---|
930 | # 2 cblp : int16u # Bytes on last page of file
|
---|
931 | # 4 cp : int16u # Pages in file
|
---|
932 | # 6 crlc : int16u # Relocations
|
---|
933 | # 8 cparhdr : int16u # Size of header in paragraphs
|
---|
934 | # 10 minalloc: int16u # Minimum extra paragraphs needed
|
---|
935 | # 12 maxalloc: int16u # Maximum extra paragraphs needed
|
---|
936 | # 14 ss : int16u # Initial (relative) SS value
|
---|
937 | # 16 sp : int16u # Initial SP value
|
---|
938 | # 18 csum : int16u # Checksum
|
---|
939 | # 20 ip : int16u # Initial IP value
|
---|
940 | # 22 cs : int16u # Initial (relative) CS value
|
---|
941 | # 24 lfarlc : int16u # Address of relocation table
|
---|
942 | # 26 ovno : int16u # Overlay number
|
---|
943 | # 28 res : int16u[4] # Reserved words
|
---|
944 | # 36 oemid : int16u # OEM identifier (for oeminfo)
|
---|
945 | # 38 oeminfo : int16u # OEM info; oemid specific
|
---|
946 | # 40 res2 : int16u[10]# Reserved words
|
---|
947 | # 60 lfanew : int32u; # File address of new exe header
|
---|
948 | SetByteOrder('II');
|
---|
949 | my ($cblp, $cp, $lfarlc, $lfanew) = unpack('x2v2x18vx34V', $buff);
|
---|
950 | my $fileSize = ($cp - ($cblp ? 1 : 0)) * 512 + $cblp;
|
---|
951 | return 0 if $fileSize < 0x40 or $fileSize < $lfarlc;
|
---|
952 | # read the Windows PE header
|
---|
953 | if ($lfarlc == 0x40 and $fileSize > $lfanew + 2 and
|
---|
954 | # read the Windows NE, PE or LE (virtual device driver) header
|
---|
955 | $raf->Seek($lfanew, 0) and $raf->Read($buff, 0x40) and
|
---|
956 | $buff =~ /^(NE|PE|LE)/)
|
---|
957 | {
|
---|
958 | if ($1 eq 'NE') {
|
---|
959 | if ($size >= 0x40) { # NE header is 64 bytes (ref 2)
|
---|
960 | # check for DLL
|
---|
961 | my $appFlags = Get16u(\$buff, 0x0c);
|
---|
962 | $type = 'Win16 ' . ($appFlags & 0x80 ? 'DLL' : 'EXE');
|
---|
963 | # offset 0x02 is 2 bytes with linker version and revision numbers
|
---|
964 | # offset 0x36 is executable type (2 = Windows)
|
---|
965 | }
|
---|
966 | } elsif ($1 eq 'PE') {
|
---|
967 | # PE header comes at byte 4 in buff:
|
---|
968 | # 4 int16u Machine
|
---|
969 | # 6 int16u NumberOfSections
|
---|
970 | # 8 int32u TimeDateStamp
|
---|
971 | # 12 int32u PointerToSymbolTable
|
---|
972 | # 16 int32u NumberOfSymbols
|
---|
973 | # 20 int16u SizeOfOptionalHeader
|
---|
974 | # 22 int16u Characteristics
|
---|
975 | if ($size >= 24) { # PE header is 24 bytes (plus optional header)
|
---|
976 | my $flags = Get16u(\$buff, 22);
|
---|
977 | $exifTool->SetFileType('Win32 ' . ($flags & 0x2000 ? 'DLL' : 'EXE'));
|
---|
978 | # read the rest of the optional header if necessary
|
---|
979 | my $optSize = Get16u(\$buff, 20);
|
---|
980 | my $more = $optSize + 24 - $size;
|
---|
981 | if ($more > 0) {
|
---|
982 | if ($raf->Read($buf2, $more) == $more) {
|
---|
983 | $buff .= $buf2;
|
---|
984 | $size += $more;
|
---|
985 | my $magic = Get16u(\$buff, 24);
|
---|
986 | # verify PE32/PE32+ magic number
|
---|
987 | unless ($magic == 0x10b or $magic == 0x20b) {
|
---|
988 | $exifTool->Warn('Unknown PE magic number');
|
---|
989 | return 1;
|
---|
990 | }
|
---|
991 | } else {
|
---|
992 | $exifTool->Warn('Error reading optional header');
|
---|
993 | }
|
---|
994 | }
|
---|
995 | # process PE COFF file header
|
---|
996 | $tagTablePtr = GetTagTable('Image::ExifTool::EXE::Main');
|
---|
997 | %dirInfo = (
|
---|
998 | DataPt => \$buff,
|
---|
999 | DataPos => $raf->Tell() - $size,
|
---|
1000 | DataLen => $size,
|
---|
1001 | DirStart => 4,
|
---|
1002 | DirLen => $size,
|
---|
1003 | );
|
---|
1004 | $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
---|
1005 | # process data dictionary
|
---|
1006 | my $num = Get16u(\$buff, 6); # NumberOfSections
|
---|
1007 | if ($raf->Read($buff, 40 * $num) == 40 * $num) {
|
---|
1008 | %dirInfo = (
|
---|
1009 | RAF => $raf,
|
---|
1010 | DataPt => \$buff,
|
---|
1011 | );
|
---|
1012 | ProcessPEDict($exifTool, \%dirInfo) or $exifTool->Warn('Error processing PE data dictionary');
|
---|
1013 | }
|
---|
1014 | return 1;
|
---|
1015 | }
|
---|
1016 | } else {
|
---|
1017 | $type = 'Virtual Device Driver';
|
---|
1018 | }
|
---|
1019 | } else {
|
---|
1020 | $type = 'DOS EXE';
|
---|
1021 | }
|
---|
1022 | #
|
---|
1023 | # Mach-O (Mac OS X)
|
---|
1024 | #
|
---|
1025 | } elsif ($buff =~ /^(\xca\xfe\xba\xbe|\xfe\xed\xfa(\xce|\xcf)|(\xce|\xcf)\xfa\xed\xfe)/ and $size > 12) {
|
---|
1026 | # Mach-O executable
|
---|
1027 | # (ref http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html)
|
---|
1028 | $tagTablePtr = GetTagTable('Image::ExifTool::EXE::MachO');
|
---|
1029 | if ($1 eq "\xca\xfe\xba\xbe") {
|
---|
1030 | SetByteOrder('MM');
|
---|
1031 | $exifTool->SetFileType('Mach-O fat binary executable');
|
---|
1032 | my $count = Get32u(\$buff, 4); # get architecture count
|
---|
1033 | my $more = $count * 20 - ($size - 8);
|
---|
1034 | if ($more > 0) {
|
---|
1035 | unless ($raf->Read($buf2, $more) == $more) {
|
---|
1036 | $exifTool->Warn('Error reading fat-arch headers');
|
---|
1037 | return 1;
|
---|
1038 | }
|
---|
1039 | $buff .= $buf2;
|
---|
1040 | $size += $more;
|
---|
1041 | }
|
---|
1042 | $exifTool->HandleTag($tagTablePtr, 2, $count);
|
---|
1043 | my $i;
|
---|
1044 | for ($i=0; $i<$count; ++$i) {
|
---|
1045 | my $cpuType = Get32s(\$buff, 8 + $i * 20);
|
---|
1046 | my $cpuSubtype = Get32u(\$buff, 12 + $i * 20);
|
---|
1047 | $exifTool->HandleTag($tagTablePtr, 3, $cpuType);
|
---|
1048 | $exifTool->HandleTag($tagTablePtr, 4, "$cpuType $cpuSubtype");
|
---|
1049 | }
|
---|
1050 | # load first Mach-O header to get the object file type
|
---|
1051 | my $offset = Get32u(\$buff, 16);
|
---|
1052 | if ($raf->Seek($offset, 0) and $raf->Read($buf2, 16) == 16) {
|
---|
1053 | if ($buf2 =~ /^(\xfe\xed\xfa(\xce|\xcf)|(\xce|\xcf)\xfa\xed\xfe)/) {
|
---|
1054 | SetByteOrder($buf2 =~ /^\xfe\xed/ ? 'MM' : 'II');
|
---|
1055 | my $objType = Get32s(\$buf2, 12);
|
---|
1056 | $exifTool->HandleTag($tagTablePtr, 5, $objType);
|
---|
1057 | } elsif ($buf2 =~ /^!<arch>\x0a/) {
|
---|
1058 | # .a libraries use this magic number
|
---|
1059 | $exifTool->HandleTag($tagTablePtr, 5, -1);
|
---|
1060 | } else {
|
---|
1061 | $exifTool->Warn('Unrecognized object file type');
|
---|
1062 | }
|
---|
1063 | } else {
|
---|
1064 | $exifTool->Warn('Error reading file');
|
---|
1065 | }
|
---|
1066 | } elsif ($size >= 16) {
|
---|
1067 | $exifTool->SetFileType('Mach-O executable');
|
---|
1068 | my $info = {
|
---|
1069 | "\xfe\xed\xfa\xce" => ['32 bit', 'Big endian'],
|
---|
1070 | "\xce\xfa\xed\xfe" => ['32 bit', 'Little endian'],
|
---|
1071 | "\xfe\xed\xfa\xcf" => ['64 bit', 'Big endian'],
|
---|
1072 | "\xcf\xfa\xed\xfe" => ['64 bit', 'Little endian'],
|
---|
1073 | }->{substr($buff, 0, 4)};
|
---|
1074 | my $byteOrder = ($buff =~ /^\xfe/) ? 'MM' : 'II';
|
---|
1075 | SetByteOrder($byteOrder);
|
---|
1076 | my $cpuType = Get32s(\$buff, 4);
|
---|
1077 | my $cpuSubtype = Get32s(\$buff, 8);
|
---|
1078 | my $objType = Get32s(\$buff, 12);
|
---|
1079 | $exifTool->HandleTag($tagTablePtr, 0, $$info[0]);
|
---|
1080 | $exifTool->HandleTag($tagTablePtr, 1, $$info[1]);
|
---|
1081 | $exifTool->HandleTag($tagTablePtr, 3, $cpuType);
|
---|
1082 | $exifTool->HandleTag($tagTablePtr, 4, "$cpuType $cpuSubtype");
|
---|
1083 | $exifTool->HandleTag($tagTablePtr, 5, $objType);
|
---|
1084 | }
|
---|
1085 | return 1;
|
---|
1086 | #
|
---|
1087 | # PEF (classic MacOS)
|
---|
1088 | #
|
---|
1089 | } elsif ($buff =~ /^Joy!peff/ and $size > 12) {
|
---|
1090 | # ref http://developer.apple.com/documentation/mac/pdf/MacOS_RT_Architectures.pdf
|
---|
1091 | $exifTool->SetFileType('Classic MacOS executable');
|
---|
1092 | SetByteOrder('MM');
|
---|
1093 | $tagTablePtr = GetTagTable('Image::ExifTool::EXE::PEF');
|
---|
1094 | %dirInfo = (
|
---|
1095 | DataPt => \$buff,
|
---|
1096 | DataPos => 0,
|
---|
1097 | DataLen => $size,
|
---|
1098 | DirStart => 0,
|
---|
1099 | DirLen => $size,
|
---|
1100 | );
|
---|
1101 | $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
---|
1102 | return 1;
|
---|
1103 | #
|
---|
1104 | # ELF (Unix)
|
---|
1105 | #
|
---|
1106 | } elsif ($buff =~ /^\x7fELF/ and $size >= 16) {
|
---|
1107 | $exifTool->SetFileType("ELF executable");
|
---|
1108 | SetByteOrder(Get8u(\$buff,5) == 1 ? 'II' : 'MM');
|
---|
1109 | $tagTablePtr = GetTagTable('Image::ExifTool::EXE::ELF');
|
---|
1110 | %dirInfo = (
|
---|
1111 | DataPt => \$buff,
|
---|
1112 | DataPos => 0,
|
---|
1113 | DataLen => $size,
|
---|
1114 | DirStart => 0,
|
---|
1115 | DirLen => $size,
|
---|
1116 | );
|
---|
1117 | $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
---|
1118 | return 1;
|
---|
1119 | #
|
---|
1120 | # various scripts (perl, sh, etc...)
|
---|
1121 | #
|
---|
1122 | } elsif ($buff =~ m{^#!\s*/\S*bin/(\w+)}) {
|
---|
1123 | $type = "$1 script";
|
---|
1124 | #
|
---|
1125 | # .a libraries
|
---|
1126 | #
|
---|
1127 | } elsif ($buff =~ /^!<arch>\x0a/) {
|
---|
1128 | $type = 'Static library',
|
---|
1129 | }
|
---|
1130 | return 0 unless $type;
|
---|
1131 | $exifTool->SetFileType($type);
|
---|
1132 | return 1;
|
---|
1133 | }
|
---|
1134 |
|
---|
1135 | 1; # end
|
---|
1136 |
|
---|
1137 | __END__
|
---|
1138 |
|
---|
1139 | =head1 NAME
|
---|
1140 |
|
---|
1141 | Image::ExifTool::EXE - Read executable file meta information
|
---|
1142 |
|
---|
1143 | =head1 SYNOPSIS
|
---|
1144 |
|
---|
1145 | This module is used by Image::ExifTool
|
---|
1146 |
|
---|
1147 | =head1 DESCRIPTION
|
---|
1148 |
|
---|
1149 | This module contains definitions required by Image::ExifTool to extract meta
|
---|
1150 | information from various types of Windows, MacOS and Unix executable and
|
---|
1151 | library files.
|
---|
1152 |
|
---|
1153 | =head1 AUTHOR
|
---|
1154 |
|
---|
1155 | Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
1156 |
|
---|
1157 | This library is free software; you can redistribute it and/or modify it
|
---|
1158 | under the same terms as Perl itself.
|
---|
1159 |
|
---|
1160 | =head1 REFERENCES
|
---|
1161 |
|
---|
1162 | =over 4
|
---|
1163 |
|
---|
1164 | =item L<http://www.openwatcom.org/ftp/devel/docs/pecoff.pdf>
|
---|
1165 |
|
---|
1166 | =item L<http://support.microsoft.com/kb/65122>
|
---|
1167 |
|
---|
1168 | =item L<http://www.opensource.apple.com>
|
---|
1169 |
|
---|
1170 | =item L<http://www.skyfree.org/linux/references/ELF_Format.pdf>
|
---|
1171 |
|
---|
1172 | =item L<http://msdn.microsoft.com/en-us/library/ms809762.aspx>
|
---|
1173 |
|
---|
1174 | =item L<http://code.google.com/p/pefile/>
|
---|
1175 |
|
---|
1176 | =item L<http://www.codeproject.com/KB/DLL/showver.aspx>
|
---|
1177 |
|
---|
1178 | =back
|
---|
1179 |
|
---|
1180 | =head1 SEE ALSO
|
---|
1181 |
|
---|
1182 | L<Image::ExifTool::TagNames/EXE Tags>,
|
---|
1183 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
1184 |
|
---|
1185 | =cut
|
---|
1186 |
|
---|