File Coverage

blib/lib/Image/ExifTool/EXE.pm
Criterion Covered Total %
statement 231 325 71.0
branch 97 204 47.5
condition 42 116 36.2
subroutine 11 12 91.6
pod 0 9 0.0
total 381 666 57.2


line stmt bran cond sub pod time code
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             # 2011/07/12 - P. Harvey Added CHM (ok, not EXE, but it fits here)
8             #
9             # References: 1) http://www.openwatcom.org/ftp/devel/docs/pecoff.pdf
10             # 2) http://support.microsoft.com/kb/65122
11             # 3) http://www.opensource.apple.com
12             # 4) http://www.skyfree.org/linux/references/ELF_Format.pdf
13             # 5) http://msdn.microsoft.com/en-us/library/ms809762.aspx
14             # 6) http://code.google.com/p/pefile/
15             # 7) http://www.codeproject.com/KB/DLL/showver.aspx
16             # 8) https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
17             #------------------------------------------------------------------------------
18              
19             package Image::ExifTool::EXE;
20              
21 1     1   5851 use strict;
  1         1  
  1         38  
22 1     1   5 use vars qw($VERSION);
  1         1  
  1         39  
23 1     1   3 use Image::ExifTool qw(:DataAccess :Utils);
  1         2  
  1         5589  
24              
25             $VERSION = '1.25';
26              
27             sub ProcessPEResources($$);
28             sub ProcessPEVersion($$);
29              
30             # PE file resource types (ref 6)
31             my %resourceType = (
32             1 => 'Cursor',
33             2 => 'Bitmap',
34             3 => 'Icon',
35             4 => 'Menu',
36             5 => 'Dialog',
37             6 => 'String',
38             7 => 'Font Dir',
39             8 => 'Font',
40             9 => 'Accelerator',
41             10 => 'RC Data',
42             11 => 'Message Table',
43             12 => 'Group Cursor',
44             14 => 'Group Icon',
45             16 => 'Version',
46             17 => 'Dialog Include',
47             19 => 'Plug-n-Play',
48             20 => 'VxD',
49             21 => 'Animated Cursor',
50             22 => 'Animated Icon',
51             23 => 'HTML',
52             24 => 'Manifest',
53             );
54              
55             my %languageCode = (
56             Notes => q{
57             See L
58             for the full list of Microsoft language codes.
59             },
60             '0000' => 'Neutral',
61             '007F' => 'Invariant',
62             '0400' => 'Process default',
63             '0401' => 'Arabic',
64             '0402' => 'Bulgarian',
65             '0403' => 'Catalan',
66             '0404' => 'Chinese (Traditional)',
67             '0405' => 'Czech',
68             '0406' => 'Danish',
69             '0407' => 'German',
70             '0408' => 'Greek',
71             '0409' => 'English (U.S.)',
72             '040A' => 'Spanish (Castilian)',
73             '040B' => 'Finnish',
74             '040C' => 'French',
75             '040D' => 'Hebrew',
76             '040E' => 'Hungarian',
77             '040F' => 'Icelandic',
78             '0410' => 'Italian',
79             '0411' => 'Japanese',
80             '0412' => 'Korean',
81             '0413' => 'Dutch',
82             '0414' => 'Norwegian (Bokml)',
83             '0415' => 'Polish',
84             '0416' => 'Portuguese (Brazilian)',
85             '0417' => 'Rhaeto-Romanic',
86             '0418' => 'Romanian',
87             '0419' => 'Russian',
88             '041A' => 'Croato-Serbian (Latin)',
89             '041B' => 'Slovak',
90             '041C' => 'Albanian',
91             '041D' => 'Swedish',
92             '041E' => 'Thai',
93             '041F' => 'Turkish',
94             '0420' => 'Urdu',
95             # 0421-0493 ref 6
96             '0421' => 'Indonesian',
97             '0422' => 'Ukrainian',
98             '0423' => 'Belarusian',
99             '0424' => 'Slovenian',
100             '0425' => 'Estonian',
101             '0426' => 'Latvian',
102             '0427' => 'Lithuanian',
103             '0428' => 'Maori',
104             '0429' => 'Farsi',
105             '042a' => 'Vietnamese',
106             '042b' => 'Armenian',
107             '042c' => 'Azeri',
108             '042d' => 'Basque',
109             '042e' => 'Sorbian',
110             '042f' => 'Macedonian',
111             '0430' => 'Sutu',
112             '0431' => 'Tsonga',
113             '0432' => 'Tswana',
114             '0433' => 'Venda',
115             '0434' => 'Xhosa',
116             '0435' => 'Zulu',
117             '0436' => 'Afrikaans',
118             '0437' => 'Georgian',
119             '0438' => 'Faeroese',
120             '0439' => 'Hindi',
121             '043a' => 'Maltese',
122             '043b' => 'Saami',
123             '043c' => 'Gaelic',
124             '043e' => 'Malay',
125             '043f' => 'Kazak',
126             '0440' => 'Kyrgyz',
127             '0441' => 'Swahili',
128             '0443' => 'Uzbek',
129             '0444' => 'Tatar',
130             '0445' => 'Bengali',
131             '0446' => 'Punjabi',
132             '0447' => 'Gujarati',
133             '0448' => 'Oriya',
134             '0449' => 'Tamil',
135             '044a' => 'Telugu',
136             '044b' => 'Kannada',
137             '044c' => 'Malayalam',
138             '044d' => 'Assamese',
139             '044e' => 'Marathi',
140             '044f' => 'Sanskrit',
141             '0450' => 'Mongolian',
142             '0456' => 'Galician',
143             '0457' => 'Konkani',
144             '0458' => 'Manipuri',
145             '0459' => 'Sindhi',
146             '045a' => 'Syriac',
147             '0460' => 'Kashmiri',
148             '0461' => 'Nepali',
149             '0465' => 'Divehi',
150             '047f' => 'Invariant',
151             '048f' => 'Esperanto',
152             '0490' => 'Walon',
153             '0491' => 'Cornish',
154             '0492' => 'Welsh',
155             '0493' => 'Breton',
156             '0800' => 'Neutral 2',
157             '0804' => 'Chinese (Simplified)',
158             '0807' => 'German (Swiss)',
159             '0809' => 'English (British)',
160             '080A' => 'Spanish (Mexican)',
161             '080C' => 'French (Belgian)',
162             '0810' => 'Italian (Swiss)',
163             '0813' => 'Dutch (Belgian)',
164             '0814' => 'Norwegian (Nynorsk)',
165             '0816' => 'Portuguese',
166             '081A' => 'Serbo-Croatian (Cyrillic)',
167             '0C07' => 'German (Austrian)',
168             '0C09' => 'English (Australian)',
169             '0C0A' => 'Spanish (Modern)',
170             '0C0C' => 'French (Canadian)',
171             '1009' => 'English (Canadian)',
172             '100C' => 'French (Swiss)',
173             );
174              
175             my %int32uTime = (
176             Format => 'int32u',
177             Groups => { 0 => 'EXE', 1 => 'EXE', 2 => 'Time' },
178             ValueConv => 'ConvertUnixTime($val,1)',
179             PrintConv => '$self->ConvertDateTime($val)',
180             );
181              
182             # Information extracted from PE COFF (Windows EXE) file header
183             %Image::ExifTool::EXE::Main = (
184             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
185             GROUPS => { 2 => 'Other' },
186             FORMAT => 'int16u',
187             NOTES => q{
188             This module extracts information from various types of Windows, MacOS and
189             Unix executable and library files. The first table below lists information
190             extracted from the header of Windows PE (Portable Executable) EXE files and
191             DLL libraries.
192             },
193             0 => {
194             Name => 'MachineType',
195             PrintHex => 1,
196             PrintConv => {
197             0x0 => 'Unknown',
198             0x01 => 'Target host',
199             0x014c => 'Intel 386 or later, and compatibles',
200             0x014d => 'Intel i860', #5
201             0x0162 => 'MIPS R3000',
202             0x0166 => 'MIPS little endian (R4000)',
203             0x0168 => 'MIPS R10000',
204             0x0169 => 'MIPS little endian WCI v2',
205             0x0183 => 'Alpha AXP (old)', #5
206             0x0184 => 'Alpha AXP',
207             0x01a2 => 'Hitachi SH3',
208             0x01a3 => 'Hitachi SH3 DSP',
209             0x01a4 => 'Hitachi SH3E',
210             0x01a6 => 'Hitachi SH4',
211             0x01a8 => 'Hitachi SH5',
212             0x01c0 => 'ARM little endian',
213             0x01c2 => 'Thumb',
214             0x01c4 => 'Thumb 2 little endian',
215             0x01d3 => 'Matsushita AM33',
216             0x01f0 => 'PowerPC little endian',
217             0x01f1 => 'PowerPC with floating point support',
218             0x0200 => 'Intel IA64',
219             0x0266 => 'MIPS16',
220             0x0268 => 'Motorola 68000 series',
221             0x0284 => 'Alpha AXP 64-bit',
222             0x0366 => 'MIPS with FPU',
223             0x0466 => 'MIPS16 with FPU',
224             0x0520 => 'Infineon Tricore',
225             0x0ebc => 'EFI Byte Code',
226             0x3a64 => 'Compiled Hybrid PE',
227             0x5032 => 'RISC-V 32-bit',
228             0x5064 => 'RISC-V 64-bit',
229             0x5128 => 'RISC-V 128-bit',
230             0x6232 => 'LoongArch 32-bit',
231             0x6264 => 'LoongArch 64-bit',
232             0x8664 => 'AMD AMD64',
233             0x9041 => 'Mitsubishi M32R little endian',
234             0xaa64 => 'ARM64 little endian',
235             0xc0ee => 'clr pure MSIL',
236             0x0cef => 'CEF',
237             0xec20 => 'Dotnet 0xEC20'
238             },
239             },
240             2 => { Name => 'TimeStamp', %int32uTime },
241             9 => {
242             Name => 'ImageFileCharacteristics',
243             # ref https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-image_file_header
244             PrintConv => { BITMASK => {
245             0 => 'No relocs',
246             1 => 'Executable',
247             2 => 'No line numbers',
248             3 => 'No symbols',
249             4 => 'Aggressive working-set trim',
250             5 => 'Large address aware',
251             7 => 'Bytes reversed lo',
252             8 => '32-bit',
253             9 => 'No debug',
254             10 => 'Removable run from swap',
255             11 => 'Net run from swap',
256             12 => 'System file',
257             13 => 'DLL',
258             14 => 'Uniprocessor only',
259             15 => 'Bytes reversed hi',
260             }},
261             },
262             #
263             # optional header starts at index 10
264             # (note: all extracted tags are the same for 32 and 64-bit versions of the optional header)
265             #
266             10 => {
267             Name => 'PEType',
268             PrintHex => 1,
269             PrintConv => {
270             0x107 => 'ROM Image',
271             0x10b => 'PE32',
272             0x20b => 'PE32+',
273             },
274             },
275             11 => {
276             Name => 'LinkerVersion',
277             Format => 'int8u[2]',
278             ValueConv => '$val=~tr/ /./; $val',
279             },
280             12 => {
281             Name => 'CodeSize',
282             Format => 'int32u',
283             },
284             14 => {
285             Name => 'InitializedDataSize',
286             Format => 'int32u',
287             },
288             16 => {
289             Name => 'UninitializedDataSize',
290             Format => 'int32u',
291             },
292             18 => {
293             Name => 'EntryPoint',
294             Format => 'int32u',
295             PrintConv => 'sprintf("0x%.4x", $val)',
296             },
297             30 => {
298             Name => 'OSVersion',
299             Format => 'int16u[2]',
300             ValueConv => '$val=~tr/ /./; $val',
301             },
302             32 => {
303             Name => 'ImageVersion',
304             Format => 'int16u[2]',
305             ValueConv => '$val=~tr/ /./; $val',
306             },
307             34 => {
308             Name => 'SubsystemVersion',
309             Format => 'int16u[2]',
310             ValueConv => '$val=~tr/ /./; $val',
311             },
312             44 => {
313             Name => 'Subsystem',
314             PrintConv => {
315             0 => 'Unknown',
316             1 => 'Native',
317             2 => 'Windows GUI',
318             3 => 'Windows command line',
319             5 => 'OS/2 command line', #5
320             7 => 'POSIX command line',
321             9 => 'Windows CE GUI',
322             10 => 'EFI application',
323             11 => 'EFI boot service',
324             12 => 'EFI runtime driver',
325             13 => 'EFI ROM', #6
326             14 => 'XBOX', #6
327             },
328             },
329             );
330              
331             # information extracted from newer CodeView PDB70 ("RSDS") debug entry
332             %Image::ExifTool::EXE::DebugRSDS = (
333             GROUPS => { 2 => 'Other' },
334             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
335             NOTES => 'CodeView RSDS debug information found in some Windows EXE files.',
336             0 => {
337             Name => 'PDBModifyDate',
338             Notes => 'Taken from debug directory entry pointing to RSDS record.',
339             %int32uTime,
340             },
341             20 => { Name => 'PDBAge', Format => 'int32u' },
342             24 => { Name => 'PDBFileName', Format => 'string' },
343             );
344              
345             # information extracted from older CodeView PDB20 ("NB10") debug entry
346             %Image::ExifTool::EXE::DebugNB10 = (
347             GROUPS => { 2 => 'Other' },
348             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
349             NOTES => 'CodeView NB10 debug information found in some Windows EXE files.',
350             0 => {
351             Name => 'PDBModifyDate',
352             Notes => 'Taken from debug directory entry pointing to NB10 record.',
353             %int32uTime,
354             },
355             8 => { Name => 'PDBCreateDate',%int32uTime },
356             12 => { Name => 'PDBAge', Format => 'int32u' },
357             16 => { Name => 'PDBFileName', Format => 'string' },
358             );
359              
360             %Image::ExifTool::EXE::Misc = (
361             GROUPS => { 2 => 'Other' },
362             VARS => { ID_LABEL => 'Index1' },
363             NOTES => 'Miscellaneous CodeView debug information in Windows EXE files.',
364             12 => 'EXEFileName',
365             );
366              
367             # PE file version information (ref 6)
368             %Image::ExifTool::EXE::PEVersion = (
369             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
370             GROUPS => { 2 => 'Other' },
371             FORMAT => 'int32u',
372             NOTES => q{
373             Information extracted from the VS_VERSION_INFO structure of Windows PE
374             files.
375             },
376             # (boring -- always 0xfeef04bd)
377             #0 => {
378             # Name => 'Signature',
379             # PrintConv => 'sprintf("0x%.4x",$val)',
380             #},
381             # (boring -- always 1.0)
382             #1 => {
383             # Name => 'StructVersion',
384             # Format => 'int16u[2]',
385             # ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0]"',
386             #},
387             2 => {
388             Name => 'FileVersionNumber',
389             Format => 'int16u[4]',
390             ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0].$a[3].$a[2]"',
391             },
392             4 => {
393             Name => 'ProductVersionNumber',
394             Format => 'int16u[4]',
395             ValueConv => 'my @a=split(" ",$val); "$a[1].$a[0].$a[3].$a[2]"',
396             },
397             6 => {
398             Name => 'FileFlagsMask',
399             PrintConv => 'sprintf("0x%.4x",$val)',
400             },
401             7 => { # ref Cygwin /usr/include/w32api/winver.h
402             Name => 'FileFlags',
403             PrintConv => { BITMASK => {
404             0 => 'Debug',
405             1 => 'Pre-release',
406             2 => 'Patched',
407             3 => 'Private build',
408             4 => 'Info inferred',
409             5 => 'Special build',
410             }},
411             },
412             8 => {
413             Name => 'FileOS',
414             PrintHex => 1,
415             PrintConv => { # ref Cygwin /usr/include/w32api/winver.h
416             0x00001 => 'Win16',
417             0x00002 => 'PM-16',
418             0x00003 => 'PM-32',
419             0x00004 => 'Win32',
420             0x10000 => 'DOS',
421             0x20000 => 'OS/2 16-bit',
422             0x30000 => 'OS/2 32-bit',
423             0x40000 => 'Windows NT',
424             0x10001 => 'Windows 16-bit',
425             0x10004 => 'Windows 32-bit',
426             0x20002 => 'OS/2 16-bit PM-16',
427             0x30003 => 'OS/2 32-bit PM-32',
428             0x40004 => 'Windows NT 32-bit',
429             },
430             },
431             9 => { # ref Cygwin /usr/include/w32api/winver.h
432             Name => 'ObjectFileType',
433             PrintConv => {
434             0 => 'Unknown',
435             1 => 'Executable application',
436             2 => 'Dynamic link library',
437             3 => 'Driver',
438             4 => 'Font',
439             5 => 'VxD',
440             7 => 'Static library',
441             },
442             },
443             10 => 'FileSubtype',
444             # (these are usually zero, so ignore them)
445             # 11 => 'FileDateMS',
446             # 12 => 'FileDateLS',
447             );
448              
449             # Windows PE StringFileInfo resource strings
450             # (see http://msdn.microsoft.com/en-us/library/aa381049.aspx)
451             %Image::ExifTool::EXE::PEString = (
452             GROUPS => { 2 => 'Other' },
453             VARS => { ID_FMT => 'none' },
454             NOTES => q{
455             Resource strings found in Windows PE files. The B's are not shown
456             because they are the same as the B. ExifTool will extract any
457             existing StringFileInfo tags even if not listed in this table.
458             },
459             LanguageCode => {
460             Notes => 'Windows code page; extracted from the StringFileInfo value',
461             # ref http://techsupt.winbatch.com/TS/T000001050F49.html
462             # (also see http://support.bigfix.com/fixlet/documents/WinInspectors-2006-08-10.pdf)
463             # (also see ftp://ftp.dyu.edu.tw/pub/cpatch/faq/tech/tech_nlsnt.txt)
464             # (not a complete set)
465             PrintString => 1,
466             SeparateTable => 1,
467             PrintConv => \%languageCode,
468             },
469             CharacterSet => {
470             Notes => 'extracted from the StringFileInfo value',
471             # ref http://techsupt.winbatch.com/TS/T000001050F49.html
472             # (also see http://blog.chinaunix.net/u1/41189/showart_345768.html)
473             PrintString => 1,
474             PrintConv => {
475             '0000' => 'ASCII',
476             '03A4' => 'Windows, Japan (Shift - JIS X-0208)', # cp932
477             '03A8' => 'Windows, Chinese (Simplified)', # cp936
478             '03B5' => 'Windows, Korea (Shift - KSC 5601)', # cp949
479             '03B6' => 'Windows, Taiwan (Big5)', # cp950
480             '04B0' => 'Unicode', # UCS-2
481             '04E2' => 'Windows, Latin2 (Eastern European)',
482             '04E3' => 'Windows, Cyrillic',
483             '04E4' => 'Windows, Latin1',
484             '04E5' => 'Windows, Greek',
485             '04E6' => 'Windows, Turkish',
486             '04E7' => 'Windows, Hebrew',
487             '04E8' => 'Windows, Arabic',
488             },
489             },
490             BuildDate => { Groups => { 2 => 'Time' } }, # (non-standard)
491             BuildVersion => { }, # (non-standard)
492             Comments => { },
493             CompanyName => { },
494             Copyright => { }, # (non-standard)
495             FileDescription => { },
496             FileVersion => { },
497             InternalName => { },
498             LegalCopyright => { },
499             LegalTrademarks => { },
500             OriginalFilename=> { Name => 'OriginalFileName' },
501             PrivateBuild => { },
502             ProductName => { },
503             ProductVersion => { },
504             SpecialBuild => { },
505             );
506              
507             # Information extracted from Mach-O (Mac OS X) file header
508             %Image::ExifTool::EXE::MachO = (
509             GROUPS => { 2 => 'Other' },
510             VARS => { ID_LABEL => 'Index' },
511             NOTES => q{
512             Information extracted from Mach-O (Mac OS X) executable files and DYLIB
513             libraries.
514             },
515             # ref http://www.opensource.apple.com/darwinsource/DevToolsOct2007/cctools-622.9/include/mach/machine.h
516             0 => 'CPUArchitecture',
517             1 => 'CPUByteOrder',
518             2 => 'CPUCount',
519             # ref /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/mach/machine.h
520             3 => {
521             Name => 'CPUType',
522             List => 1,
523             PrintConv => {
524             # handle 64-bit flag (0x1000000)
525             OTHER => sub {
526             my ($val, $inv, $conv) = @_;
527             my $v = $val & 0xfeffffff;
528             return $$conv{$v} ? "$$conv{$v} 64-bit" : "Unknown ($val)";
529             },
530             -1 => 'Any',
531             1 => 'VAX',
532             2 => 'ROMP',
533             4 => 'NS32032',
534             5 => 'NS32332',
535             6 => 'MC680x0',
536             7 => 'x86',
537             8 => 'MIPS',
538             9 => 'NS32532',
539             10 => 'MC98000',
540             11 => 'HPPA',
541             12 => 'ARM',
542             13 => 'MC88000',
543             14 => 'SPARC',
544             15 => 'i860 big endian',
545             16 => 'i860 little endian',
546             17 => 'RS6000',
547             18 => 'PowerPC',
548             255 => 'VEO',
549             },
550             },
551             # ref /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/mach/machine.h
552             4 => {
553             Name => 'CPUSubtype',
554             List => 1,
555             PrintConv => {
556             # handle 64-bit flags on CPUType (0x1000000) and CPUSubtype (0x80000000)
557             OTHER => sub {
558             my ($val, $inv, $conv) = @_;
559             my @v = split ' ', $val;
560             my $v = ($v[0] & 0xfeffffff) . ' ' . ($v[1] & 0x7fffffff);
561             return $$conv{$v} ? "$$conv{$v} 64-bit" : "Unknown ($val)";
562             },
563             # in theory, subtype can be -1 for multiple CPU types,
564             # but in practice I'm not sure anyone uses this - PH
565             '1 0' => 'VAX (all)',
566             '1 1' => 'VAX780',
567             '1 2' => 'VAX785',
568             '1 3' => 'VAX750',
569             '1 4' => 'VAX730',
570             '1 5' => 'UVAXI',
571             '1 6' => 'UVAXII',
572             '1 7' => 'VAX8200',
573             '1 8' => 'VAX8500',
574             '1 9' => 'VAX8600',
575             '1 10' => 'VAX8650',
576             '1 11' => 'VAX8800',
577             '1 12' => 'UVAXIII',
578             '2 0' => 'RT (all)',
579             '2 1' => 'RT PC',
580             '2 2' => 'RT APC',
581             '2 3' => 'RT 135',
582             # 32032/32332/32532 subtypes.
583             '4 0' => 'NS32032 (all)',
584             '4 1' => 'NS32032 DPC (032 CPU)',
585             '4 2' => 'NS32032 SQT',
586             '4 3' => 'NS32032 APC FPU (32081)',
587             '4 4' => 'NS32032 APC FPA (Weitek)',
588             '4 5' => 'NS32032 XPC (532)',
589             '5 0' => 'NS32332 (all)',
590             '5 1' => 'NS32332 DPC (032 CPU)',
591             '5 2' => 'NS32332 SQT',
592             '5 3' => 'NS32332 APC FPU (32081)',
593             '5 4' => 'NS32332 APC FPA (Weitek)',
594             '5 5' => 'NS32332 XPC (532)',
595             '6 1' => 'MC680x0 (all)',
596             '6 2' => 'MC68040',
597             '6 3' => 'MC68030',
598             '7 3' => 'i386 (all)',
599             '7 4' => 'i486',
600             '7 132' => 'i486SX',
601             '7 5' => 'i586',
602             '7 22' => 'Pentium Pro',
603             '7 54' => 'Pentium II M3',
604             '7 86' => 'Pentium II M5',
605             '7 103' => 'Celeron',
606             '7 119' => 'Celeron Mobile',
607             '7 8' => 'Pentium III',
608             '7 24' => 'Pentium III M',
609             '7 40' => 'Pentium III Xeon',
610             '7 9' => 'Pentium M',
611             '7 10' => 'Pentium 4',
612             '7 26' => 'Pentium 4 M',
613             '7 11' => 'Itanium',
614             '7 27' => 'Itanium 2',
615             '7 12' => 'Xeon',
616             '7 28' => 'Xeon MP',
617             '8 0' => 'MIPS (all)',
618             '8 1' => 'MIPS R2300',
619             '8 2' => 'MIPS R2600',
620             '8 3' => 'MIPS R2800',
621             '8 4' => 'MIPS R2000a',
622             '8 5' => 'MIPS R2000',
623             '8 6' => 'MIPS R3000a',
624             '8 7' => 'MIPS R3000',
625             '10 0' => 'MC98000 (all)',
626             '10 1' => 'MC98601',
627             '11 0' => 'HPPA (all)',
628             '11 1' => 'HPPA 7100LC',
629             '12 0' => 'ARM (all)',
630             '12 1' => 'ARM A500 ARCH',
631             '12 2' => 'ARM A500',
632             '12 3' => 'ARM A440',
633             '12 4' => 'ARM M4',
634             '12 5' => 'ARM A680/V4T',
635             '12 6' => 'ARM V6',
636             '12 7' => 'ARM V5TEJ',
637             '12 8' => 'ARM XSCALE',
638             '12 9' => 'ARM V7',
639             '13 0' => 'MC88000 (all)',
640             '13 1' => 'MC88100',
641             '13 2' => 'MC88110',
642             '14 0' => 'SPARC (all)',
643             '14 1' => 'SUN 4/260',
644             '14 2' => 'SUN 4/110',
645             '15 0' => 'i860 (all)',
646             '15 1' => 'i860 860',
647             '16 0' => 'i860 little (all)',
648             '16 1' => 'i860 little',
649             '17 0' => 'RS6000 (all)',
650             '17 1' => 'RS6000',
651             '18 0' => 'PowerPC (all)',
652             '18 1' => 'PowerPC 601',
653             '18 2' => 'PowerPC 602',
654             '18 3' => 'PowerPC 603',
655             '18 4' => 'PowerPC 603e',
656             '18 5' => 'PowerPC 603ev',
657             '18 6' => 'PowerPC 604',
658             '18 7' => 'PowerPC 604e',
659             '18 8' => 'PowerPC 620',
660             '18 9' => 'PowerPC 750',
661             '18 10' => 'PowerPC 7400',
662             '18 11' => 'PowerPC 7450',
663             '18 100' => 'PowerPC 970',
664             '255 1' => 'VEO 1',
665             '255 2' => 'VEO 2',
666             },
667             },
668             5 => {
669             Name => 'ObjectFileType',
670             PrintHex => 1,
671             # ref https://svn.red-bean.com/pyobjc/branches/pyobjc-20x-branch/macholib/macholib/mach_o.py
672             PrintConv => {
673             -1 => 'Static library', #PH (internal use only)
674             1 => 'Relocatable object',
675             2 => 'Demand paged executable',
676             3 => 'Fixed VM shared library',
677             4 => 'Core',
678             5 => 'Preloaded executable',
679             6 => 'Dynamically bound shared library',
680             7 => 'Dynamic link editor',
681             8 => 'Dynamically bound bundle',
682             9 => 'Shared library stub for static linking',
683             # (the following from Apple loader.h header file)
684             10 => 'Debug information',
685             11 => 'x86_64 kexts',
686             },
687             },
688             6 => {
689             Name => 'ObjectFlags',
690             PrintHex => 1,
691             # ref Apple loader.h header file
692             PrintConv => { BITMASK => {
693             0 => 'No undefs',
694             1 => 'Incrementa link',
695             2 => 'Dyld link',
696             3 => 'Bind at load',
697             4 => 'Prebound',
698             5 => 'Split segs',
699             6 => 'Lazy init',
700             7 => 'Two level',
701             8 => 'Force flat',
702             9 => 'No multi defs',
703             10 => 'No fix prebinding',
704             11 => 'Prebindable',
705             12 => 'All mods bound',
706             13 => 'Subsections via symbols',
707             14 => 'Canonical',
708             15 => 'Weak defines',
709             16 => 'Binds to weak',
710             17 => 'Allow stack execution',
711             18 => 'Dead strippable dylib',
712             19 => 'Root safe',
713             20 => 'No reexported dylibs',
714             21 => 'Random address',
715             }},
716             },
717             );
718              
719             # Information extracted from PEF (Classic MacOS executable) file header
720             %Image::ExifTool::EXE::PEF = (
721             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
722             GROUPS => { 2 => 'Other' },
723             NOTES => q{
724             Information extracted from PEF (Classic MacOS) executable files and
725             libraries.
726             },
727             FORMAT => 'int32u',
728             2 => {
729             Name => 'CPUArchitecture',
730             Format => 'undef[4]',
731             PrintConv => {
732             pwpc => 'PowerPC',
733             m68k => '68000',
734             },
735             },
736             3 => 'PEFVersion',
737             4 => {
738             Name => 'TimeStamp',
739             Groups => { 2 => 'Time' },
740             # timestamp is relative to Jan 1, 1904
741             ValueConv => 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
742             PrintConv => '$self->ConvertDateTime($val)',
743             },
744             #5 => 'OldDefVersion',
745             #6 => 'OldImpVersion',
746             #7 => 'CurrentVersion',
747             );
748              
749             # Information extracted from ELF (Unix executable) file header
750             %Image::ExifTool::EXE::ELF = (
751             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
752             GROUPS => { 2 => 'Other' },
753             NOTES => q{
754             Information extracted from ELF (Unix) executable files and SO libraries.
755             },
756             4 => {
757             Name => 'CPUArchitecture',
758             PrintConv => {
759             1 => '32 bit',
760             2 => '64 bit',
761             },
762             },
763             5 => {
764             Name => 'CPUByteOrder',
765             PrintConv => {
766             1 => 'Little endian',
767             2 => 'Big endian',
768             },
769             },
770             16 => {
771             Name => 'ObjectFileType',
772             Format => 'int16u',
773             PrintConv => {
774             0 => 'None',
775             1 => 'Relocatable file',
776             2 => 'Executable file',
777             3 => 'Shared object file',
778             4 => 'Core file',
779             },
780             },
781             18 => {
782             Name => 'CPUType',
783             Format => 'int16u',
784             # ref /usr/include/linux/elf-em.h
785             # ref https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
786             PrintConv => {
787             0 => 'None',
788             1 => 'AT&T WE 32100',
789             2 => 'SPARC',
790             3 => 'i386',
791             4 => 'Motorola 68000',
792             5 => 'Motorola 88000',
793             6 => 'i486',
794             7 => 'i860',
795             8 => 'MIPS R3000',
796             9 => 'IBM System/370',
797             10 => 'MIPS R4000',
798             15 => 'HP PA-RISC',
799             18 => 'Sun v8plus',
800             19 => 'Intel 80960',
801             20 => 'PowerPC',
802             21 => 'PowerPC 64-bit',
803             22 => 'IBM S/390',
804             23 => 'Cell BE SPU',
805             36 => 'NEC V800',
806             37=> 'Fujitsu FR20',
807             38 => 'TRW RH-32',
808             39 => 'Motorola RCE',
809             40 => 'Arm (up to Armv7/AArch32)',
810             41 => 'Digital Alpha',
811             42 => 'SuperH',
812             43 => 'SPARC v9 64-bit',
813             44 => 'Siemens TriCore',
814             45 => 'Argonaut RISC Core',
815             46 => 'Renesas H8/300,300H,H8S',
816             47 => 'Hitachi H8/300H',
817             48 => 'Hitachi H8S',
818             49 => 'Hitachi H8/500',
819             50 => 'HP/Intel IA-64',
820             0x33 => 'Stanford MIPS-X',
821             0x34 => 'Motorola ColdFire',
822             0x35 => 'Motorola M68HC12',
823             0x36 => 'Fujitsu MMA Multimedia Accelerator',
824             0x37 => 'Siemens PCP',
825             0x38 => 'Sony nCPU embedded RISC processor',
826             0x39 => 'Denso NDR1 microprocessor',
827             0x3a => 'Motorola Star*Core processor',
828             0x3b => 'Toyota ME16 processor',
829             0x3c => 'STMicroelectronics ST100 processor',
830             0x3d => 'Advanced Logic Corp. TinyJ embedded processor family',
831             0x3e => 'AMD x86-64',
832             0x3f => 'Sony DSP Processor',
833             0x40 => 'Digital Equipment Corp. PDP-10',
834             0x41 => 'Digital Equipment Corp. PDP-11',
835             0x42 => 'Siemens FX66 microcontroller',
836             0x43 => 'STMicroelectronics ST9+ 8/16 bit microcontroller',
837             0x44 => 'STMicroelectronics ST7 8-bit microcontroller',
838             0x45 => 'Motorola MC68HC16 Microcontroller',
839             0x46 => 'Motorola MC68HC11 Microcontroller',
840             0x47 => 'Motorola MC68HC08 Microcontroller',
841             0x48 => 'Motorola MC68HC05 Microcontroller',
842             0x49 => 'Silicon Graphics SVx',
843             0x4a => 'STMicroelectronics ST19 8-bit microcontroller',
844             0x4b => 'Digital VAX',
845             0x4c => 'Axis Communications 32-bit embedded processor',
846             0x4d => 'Infineon Technologies 32-bit embedded processor',
847             0x4e => 'Element 14 64-bit DSP Processor',
848             0x4f => 'LSI Logic 16-bit DSP Processor',
849             0x57 => 'NEC v850',
850             0x58 => 'Renesas M32R',
851             0x8c => 'TMS320C6000 Family',
852             0xaf => 'MCST Elbrus e2k',
853             0xb7 => 'Arm 64-bits (Armv8/AArch64)',
854             0xdc => 'Zilog Z80',
855             0xf3 => 'RISC-V',
856             0xf7 => 'Berkeley Packet Filter',
857             0x101 => 'WDC 65C816',
858             0x5441 => 'Fujitsu FR-V',
859             0x9026 => 'Alpha', # (interim value)
860             0x9041 => 'm32r (old)',
861             0x9080 => 'v850 (old)',
862             0xa390 => 'S/390 (old)',
863             },
864             },
865             );
866              
867             # Information extracted from static library archives
868             # (ref http://opensource.apple.com//source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/ar.h)
869             %Image::ExifTool::EXE::AR = (
870             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
871             GROUPS => { 2 => 'Other' },
872             NOTES => q{
873             Information extracted from static libraries.
874             },
875             # 0 string[16] ar_name
876             16 => {
877             Name => 'CreateDate',
878             Groups => { 2 => 'Time' },
879             Format => 'string[12]',
880             ValueConv => 'ConvertUnixTime($val,1)',
881             PrintConv => '$self->ConvertDateTime($val)',
882             },
883             # 28 string[6] ar_uid
884             # 34 string[6] ar_gid
885             # 40 string[8] ar_mode
886             # 48 string[10] ar_size
887             # 58 string[2] terminator "`\n"
888             );
889              
890             # Microsoft compiled help format (ref http://www.russotto.net/chm/chmformat.html)
891             %Image::ExifTool::EXE::CHM = (
892             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
893             GROUPS => { 2 => 'Other' },
894             NOTES => 'Tags extracted from Microsoft Compiled HTML files.',
895             FORMAT => 'int32u',
896             1 => { Name => 'CHMVersion' },
897             # 2 - total header length
898             # 3 - 1
899             # 4 - low bits of date/time value plus 42 (ref http://www.nongnu.org/chmspec/latest/ITSF.html)
900             5 => {
901             Name => 'LanguageCode',
902             SeparateTable => 1,
903             ValueConv => 'sprintf("%.4X", $val)',
904             PrintConv => \%languageCode,
905             },
906             );
907              
908             #------------------------------------------------------------------------------
909             # Extract information from a CHM file
910             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
911             # Returns: 1 on success, 0 if this wasn't a valid CHM file
912             sub ProcessCHM($$)
913             {
914 0     0 0 0 my ($et, $dirInfo) = @_;
915 0         0 my $raf = $$dirInfo{RAF};
916 0         0 my $buff;
917              
918 0 0 0     0 return 0 unless $raf->Read($buff, 56) == 56 and
919             $buff =~ /^ITSF.{20}\x10\xfd\x01\x7c\xaa\x7b\xd0\x11\x9e\x0c\0\xa0\xc9\x22\xe6\xec/s;
920 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::CHM');
921 0         0 $et->SetFileType();
922 0         0 SetByteOrder('II');
923 0         0 $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
924 0         0 return 1;
925             }
926              
927             #------------------------------------------------------------------------------
928             # Read Unicode string (null terminated) from resource
929             # Inputs: 0) data ref, 1) start offset, 2) data end, 3) optional ExifTool object ref
930             # Returns: 0) Unicode string translated to UTF8, or current CharSet with ExifTool ref
931             # 1) end pos (rounded up to nearest 4 bytes)
932             sub ReadUnicodeStr($$$;$)
933             {
934 22     22 0 30 my ($dataPt, $pos, $end, $et) = @_;
935 22 50       31 $end = length $$dataPt if $end > length $$dataPt; # (be safe)
936 22         47 my $str = '';
937 22         31 while ($pos + 2 <= $end) {
938 309         314 my $ch = substr($$dataPt, $pos, 2);
939 309         291 $pos += 2;
940 309 100       340 last if $ch eq "\0\0";
941 287         330 $str .= $ch;
942             }
943 22 100       30 $pos += 2 if $pos & 0x03;
944 22 100       37 my $to = $et ? $et->Options('Charset') : 'UTF8';
945 22         42 return (Image::ExifTool::Decode(undef,$str,'UCS2','II',$to), $pos);
946             }
947              
948             #------------------------------------------------------------------------------
949             # Process Windows PE Version Resource
950             # Inputs: 0) ExifTool object ref, 1) dirInfo ref
951             # Returns: true on success
952             sub ProcessPEVersion($$)
953             {
954 1     1 0 2 my ($et, $dirInfo) = @_;
955 1         2 my $dataPt = $$dirInfo{DataPt};
956 1         2 my $pos = $$dirInfo{DirStart};
957 1         2 my $end = $pos + $$dirInfo{DirLen};
958 1         2 my ($index, $len, $valLen, $type, $string, $strEnd);
959              
960             # get VS_VERSION_INFO
961 1         2 for ($index = 0; ; ++$index) {
962 4         6 $pos = ($pos + 3) & 0xfffffffc; # align on a 4-byte boundary
963 4 100       15 last if $pos + 6 > $end;
964 3         5 $len = Get16u($dataPt, $pos);
965 3 50       5 return 0 if $pos + $len > $end;
966 3         6 $valLen = Get16u($dataPt, $pos + 2);
967 3         4 $type = Get16u($dataPt, $pos + 4);
968 3 50 33     15 return 0 unless $len or $valLen; # prevent possible infinite loop
969 3         8 ($string, $strEnd) = ReadUnicodeStr($dataPt, $pos + 6, $pos + $len);
970 3 50       7 return 0 if $strEnd + $valLen > $end;
971 3 50 66     8 unless ($index or $string eq 'VS_VERSION_INFO') {
972 0         0 $et->Warn('Invalid Version Info block');
973 0         0 return 0;
974             }
975 3 100 66     21 if ($string eq 'VS_VERSION_INFO') {
    100          
976             # parse the fixed version info
977 1         3 $$dirInfo{DirStart} = $strEnd;
978 1         1 $$dirInfo{DirLen} = $valLen;
979 1         3 my $subTablePtr = GetTagTable('Image::ExifTool::EXE::PEVersion');
980 1         4 $et->ProcessDirectory($dirInfo, $subTablePtr);
981 1         2 $pos = $strEnd + $valLen;
982             } elsif ($string eq 'StringFileInfo' and $valLen == 0) {
983 1         3 $pos += $len;
984 1         1 my $pt = $strEnd;
985             # parse string table
986 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::PEString');
987 1         4 for ($index = 0; $pt + 6 < $pos; ++$index) {
988 10         15 $len = Get16u($dataPt, $pt);
989 10 50       17 $len or $et->Warn('Invalid PEString entry'), last;
990 10         14 $valLen = Get16u($dataPt, $pt + 2);
991             # $type = Get16u($dataPt, $pt + 4);
992 10         11 my $entryEnd = $pt + $len;
993 10 50       11 return 0 if $entryEnd > $end;
994             # get tag ID (converted to UTF8)
995 10         17 ($string, $pt) = ReadUnicodeStr($dataPt, $pt + 6, $entryEnd);
996 10 100       18 unless ($index) {
997             # separate the language code and character set
998             # (not sure what the CharacterSet tag is for, but the string
999             # values stored here are UCS-2 in all my files even if the
1000             # CharacterSet is otherwise)
1001 1         1 my $char;
1002 1 50       4 if (length($string) > 4) {
1003 1         3 $char = substr($string, 4);
1004 1         2 $string = substr($string, 0, 4);
1005             }
1006 1         5 $et->HandleTag($tagTablePtr, 'LanguageCode', uc $string);
1007 1 50       9 $et->HandleTag($tagTablePtr, 'CharacterSet', uc $char) if $char;
1008 1         3 next;
1009             }
1010 9         11 my $tag = $string;
1011             # get tag value (converted to current Charset)
1012 9 50       12 if ($valLen) {
1013 9         12 ($string, $pt) = ReadUnicodeStr($dataPt, $pt, $entryEnd, $et);
1014             } else {
1015 0         0 $string = '';
1016             }
1017 9         20 $et->HandleTag($tagTablePtr, $tag, $string, MakeTagInfo => 1);
1018             # step to next entry (padded to an even word)
1019 9         22 $pt = ($entryEnd + 3) & 0xfffffffc;
1020             }
1021             } else {
1022 1         2 $pos += $len + $valLen;
1023             # ignore other information (for now)
1024             }
1025             }
1026 1         3 return 1;
1027             }
1028              
1029             #------------------------------------------------------------------------------
1030             # Get actual file offset given a virtual address in a PE file
1031             # Inputs: 0) virtual address, 1) section list ref
1032             # Returns: absolute file offset or undef on error
1033             sub GetFileOffset($$)
1034             {
1035 1     1 0 3 my ($addr, $sections) = @_;
1036 1         1 my $section;
1037 1         3 foreach $section (@$sections) {
1038             next unless $addr >= $$section{VirtualAddress} and
1039 5 100 66     14 $addr < $$section{VirtualAddress} + $$section{Size};
1040 1         4 return $addr + $$section{Base} - $$section{VirtualAddress};
1041             }
1042 0         0 return undef;
1043             }
1044              
1045             #------------------------------------------------------------------------------
1046             # Process Windows PE Resources
1047             # Inputs: 0) ExifTool object ref, 1) dirInfo ref
1048             # Returns: true on success
1049             sub ProcessPEResources($$)
1050             {
1051 3     3 0 4 my ($et, $dirInfo) = @_;
1052 3         5 my $raf = $$dirInfo{RAF};
1053 3         4 my $base = $$dirInfo{Base};
1054 3         4 my $dirStart = $$dirInfo{DirStart} + $base;
1055 3   100     7 my $level = $$dirInfo{Level} || 0;
1056 3         10 my $verbose = $et->Options('Verbose');
1057 3         4 my ($buff, $buf2, $item);
1058              
1059 3 50       5 return 0 if $level > 10; # protect against deep recursion
1060             # read the resource header
1061 3 50 33     8 $raf->Seek($dirStart, 0) and $raf->Read($buff, 16) == 16 or return 0;
1062 3         6 my $nameEntries = Get16u(\$buff, 12);
1063 3         4 my $idEntries = Get16u(\$buff, 14);
1064 3         4 my $count = $nameEntries + $idEntries;
1065 3 50       6 return 0 if $count > 10000;
1066 3 50       10 $raf->Read($buff, $count * 8) == $count * 8 or return 0;
1067             # loop through all resource entries
1068 3         7 for ($item=0; $item<$count; ++$item) {
1069 3         4 my $pos = $item * 8;
1070 3         7 my $name = Get32u(\$buff, $pos);
1071 3         6 my $entryPos = Get32u(\$buff, $pos + 4);
1072 3 100       8 unless ($level) {
1073             # set resource type if this is the 0th level directory
1074 1   33     4 my $resType = $resourceType{$name} || sprintf('Unknown (0x%x)', $name);
1075             # ignore everything but the Version resource unless verbose
1076 1 50       3 if ($verbose) {
1077 0         0 $et->VPrint(0, "$resType resource:\n");
1078             } else {
1079 1 50       12 next unless $resType eq 'Version';
1080             }
1081 1         4 $$dirInfo{ResType} = $resType;
1082             }
1083 3 100 33     37 if ($entryPos & 0x80000000) { # is this a directory?
    50 33        
1084             # descend into next directory level
1085 2         4 $$dirInfo{DirStart} = $entryPos & 0x7fffffff;
1086 2         3 $$dirInfo{Level} = $level + 1;
1087 2 50       10 ProcessPEResources($et, $dirInfo) or return 0;
1088 2         5 --$$dirInfo{Level};
1089             } elsif ($$dirInfo{ResType} eq 'Version' and $level == 2 and
1090             not $$dirInfo{GotVersion}) # (only process first Version resource)
1091             {
1092             # get position of this resource in the file
1093 1         2 my $buf2;
1094 1 50 33     3 $raf->Seek($entryPos + $base, 0) and $raf->Read($buf2, 16) == 16 or return 0;
1095 1         2 my $addr = Get32u(\$buf2, 0);
1096 1         3 my $len = Get32u(\$buf2, 4);
1097             # determine which section this is in so we can convert the virtual address
1098 1         4 my $fileOff = GetFileOffset($addr, $$dirInfo{Sections});
1099 1 50       2 return 0 unless $fileOff;
1100 1 50 33     3 $raf->Seek($fileOff, 0) and $raf->Read($buf2, $len) == $len or return 0;
1101 1 50       7 ProcessPEVersion($et, {
1102             DataPt => \$buf2,
1103             DataLen => $len,
1104             DirStart => 0,
1105             DirLen => $len,
1106             }) or $et->Warn('Possibly corrupt Version resource');
1107 1         7 $$dirInfo{GotVersion} = 1; # set flag so we don't do this again
1108             }
1109             }
1110 3         6 return 1;
1111             }
1112              
1113             #------------------------------------------------------------------------------
1114             # Process Windows PE file data dictionary
1115             # Inputs: 0) ExifTool object ref, 1) dirInfo ref
1116             # Returns: true on success or if the PE resources didn't exist, or false on error
1117             # processing the PE resources
1118             sub ProcessPEDict($$)
1119             {
1120 1     1 0 4 my ($et, $dirInfo) = @_;
1121 1         3 my $raf = $$dirInfo{RAF};
1122 1         3 my $dataPt = $$dirInfo{DataPt};
1123 1         2 my $dirLen = length($$dataPt);
1124 1         3 my ($pos, @sections, %dirInfo, $rsrcFound);
1125              
1126             # loop through all sections
1127 1         6 for ($pos=0; $pos+40<=$dirLen; $pos+=40) {
1128 5         8 my $name = substr($$dataPt, $pos, 8);
1129 5         12 my $va = Get32u($dataPt, $pos + 12);
1130 5         9 my $size = Get32u($dataPt, $pos + 16);
1131 5         9 my $offset = Get32u($dataPt, $pos + 20);
1132             # remember the section offsets for the VirtualAddress lookup later
1133 5         15 push @sections, { Base => $offset, Size => $size, VirtualAddress => $va };
1134             # save details of the first resource section (or .text if .rsrc not found, ref forum11465)
1135 5 100 66     28 next unless ($name eq ".rsrc\0\0\0" and not $rsrcFound and defined($rsrcFound = 1)) or
      66        
      66        
      100        
1136             ($name eq ".text\0\0\0" and not %dirInfo);
1137 2         12 %dirInfo = (
1138             RAF => $raf,
1139             Base => $offset,
1140             DirStart => 0, # (relative to Base)
1141             DirLen => $size,
1142             Sections => \@sections,
1143             );
1144             }
1145 1         2 $$dirInfo{Sections} = \@sections; # return section information
1146             # process the first resource section
1147 1 50 50     6 ProcessPEResources($et, \%dirInfo) or return 0 if %dirInfo;
1148 1         5 return 1;
1149             }
1150              
1151             #------------------------------------------------------------------------------
1152             # Override file type if necessary for Mach object files and libraries
1153             # Inputs: 0) ExifTool ref, 1) ObjectFileType number, 2) flag for fat binary
1154             my %machOverride = (
1155             1 => [ 'object file', 'O' ],
1156             6 => [ 'dynamic link library', 'DYLIB' ],
1157             8 => [ 'dynamic bound bundle', 'DYLIB' ],
1158             9 => [ 'dynamic link library stub', 'DYLIB' ],
1159             );
1160             sub MachOverride($$;$)
1161             {
1162 2     2 0 6 my ($et, $objType, $fat) = @_;
1163 2         6 my $override = $machOverride{$objType};
1164 2 100       7 if ($override) {
1165 1 50       7 my $desc = 'Mach-O ' . ($fat ? 'fat ' : '') . $$override[0];
1166 1         7 $et->OverrideFileType($desc, undef, $$override[1]);
1167             }
1168             }
1169              
1170             #------------------------------------------------------------------------------
1171             # Extract tags from Mach header
1172             # Inputs: 0) ExifTool ref, 1) data ref, 2) flag to extract object type
1173             # Returns: true if Mach header was found
1174             # Mach type based on magic number
1175             # [bit depth, byte order starting with "Little" or "Big"]
1176             my %machType = (
1177             "\xfe\xed\xfa\xce" => ['32 bit', 'Big endian'],
1178             "\xce\xfa\xed\xfe" => ['32 bit', 'Little endian'],
1179             "\xfe\xed\xfa\xcf" => ['64 bit', 'Big endian'],
1180             "\xcf\xfa\xed\xfe" => ['64 bit', 'Little endian'],
1181             );
1182             sub ExtractMachTags($$;$)
1183             {
1184 4     4 0 12 my ($et, $dataPt, $doObj) = @_;
1185             # get information about mach header based on the magic number (first 4 bytes)
1186 4         17 my $info = $machType{substr($$dataPt, 0, 4)};
1187 4 100       12 if ($info) {
1188             # Mach header structure:
1189             # 0 int32u magic
1190             # 4 int32u cputype
1191             # 8 int32u cpusubtype
1192             # 12 int32u filetype
1193             # 16 int32u ncmds
1194             # 20 int32u sizeofcmds
1195             # 24 int32u flags
1196 3         8 my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::MachO');
1197 3         14 SetByteOrder($$info[1]);
1198 3         11 my $cpuType = Get32s($dataPt, 4);
1199 3         7 my $subType = Get32s($dataPt, 8);
1200 3         16 $et->HandleTag($tagTablePtr, 0, $$info[0]);
1201 3         12 $et->HandleTag($tagTablePtr, 1, $$info[1]);
1202 3         9 $et->HandleTag($tagTablePtr, 3, $cpuType);
1203 3         16 $et->HandleTag($tagTablePtr, 4, "$cpuType $subType");
1204 3 100       7 if ($doObj) {
1205 2         8 my $objType = Get32u($dataPt, 12);
1206 2         5 my $flags = Get32u($dataPt, 24);
1207 2         7 $et->HandleTag($tagTablePtr, 5, $objType);
1208 2         7 $et->HandleTag($tagTablePtr, 6, $flags);
1209             # override file type if this is an object file or library
1210 2         7 MachOverride($et, $objType);
1211             } else { # otherwise this was a static library
1212 1         6 $et->OverrideFileType('Mach-O static library', undef, 'A');
1213             }
1214 3         12 return 1;
1215             }
1216 1         4 return 0;
1217             }
1218              
1219             #------------------------------------------------------------------------------
1220             # Extract information from an EXE file
1221             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1222             # Returns: 1 on success, 0 if this wasn't a valid EXE file
1223             sub ProcessEXE($$)
1224             {
1225 6     6 0 17 my ($et, $dirInfo) = @_;
1226 6         13 my $raf = $$dirInfo{RAF};
1227 6         13 my ($buff, $buf2, $type, $mime, $ext, $tagTablePtr, %dirInfo);
1228              
1229 6 50       18 my $size = $raf->Read($buff, 0x40) or return 0;
1230 6   33     41 my $fast3 = $$et{OPTIONS}{FastScan} && $$et{OPTIONS}{FastScan} == 3;
1231             #
1232             # DOS and Windows EXE
1233             #
1234 6 100 66     84 if ($buff =~ /^MZ/ and $size == 0x40) {
    100 66        
    50 33        
    100 66        
    50          
    0          
1235             # DOS/Windows executable
1236             # validate DOS header
1237             # (ref http://www.delphidabbler.com/articles?article=8&part=2)
1238             # 0 int16u magic - Magic number ("MZ")
1239             # 2 int16u cblp - Bytes on last page of file
1240             # 4 int16u cp - Pages in file
1241             # 6 int16u crlc - Relocations
1242             # 8 int16u cparhdr - Size of header in paragraphs
1243             # 10 int16u minalloc - Minimum extra paragraphs needed
1244             # 12 int16u maxalloc - Maximum extra paragraphs needed
1245             # 14 int16u ss - Initial (relative) SS value
1246             # 16 int16u sp - Initial SP value
1247             # 18 int16u csum - Checksum
1248             # 20 int16u ip - Initial IP value
1249             # 22 int16u cs - Initial (relative) CS value
1250             # 24 int16u lfarlc - Address of relocation table
1251             # 26 int16u ovno - Overlay number
1252             # 28 int16u[4] res - Reserved words
1253             # 36 int16u oemid - OEM identifier (for oeminfo)
1254             # 38 int16u oeminfo - OEM info; oemid specific
1255             # 40 int16u[10] res2 - Reserved words
1256             # 60 int32u; lfanew - File address of new exe header
1257 1         5 SetByteOrder('II');
1258 1         4 my ($cblp, $cp, $lfarlc, $lfanew) = unpack('x2v2x18vx34V', $buff);
1259 1 50       5 my $fileSize = ($cp - ($cblp ? 1 : 0)) * 512 + $cblp;
1260             #(patch to accommodate observed 64-bit files)
1261             #return 0 if $fileSize < 0x40 or $fileSize < $lfarlc;
1262             #return 0 if $fileSize < 0x40; (changed to warning in ExifTool 12.08)
1263 1 50       2 $et->Warn('Invalid file size in DOS header') if $fileSize < 0x40;
1264             # read the Windows NE, PE or LE (virtual device driver) header
1265             #if ($lfarlc == 0x40 and $fileSize > $lfanew + 2 and ...
1266 1 50 33     10 if ($raf->Seek($lfanew, 0) and $raf->Read($buff, 0x40) and $buff =~ /^(NE|PE|LE)/) {
      33        
1267 1 50       8 if ($1 eq 'NE') {
    50          
1268 0 0       0 if ($size >= 0x40) { # NE header is 64 bytes (ref 2)
1269             # check for DLL
1270 0         0 my $appFlags = Get16u(\$buff, 0x0c);
1271 0 0       0 $ext = $appFlags & 0x80 ? 'DLL' : 'EXE';
1272 0         0 $type = "Win16 $ext";
1273             # offset 0x02 is 2 bytes with linker version and revision numbers
1274             # offset 0x36 is executable type (2 = Windows)
1275             }
1276             } elsif ($1 eq 'PE') {
1277             # PE header comes at byte 4 in buff:
1278             # 4 int16u Machine
1279             # 6 int16u NumberOfSections
1280             # 8 int32u TimeDateStamp
1281             # 12 int32u PointerToSymbolTable
1282             # 16 int32u NumberOfSymbols
1283             # 20 int16u SizeOfOptionalHeader
1284             # 22 int16u Characteristics
1285 1 50       18 if ($size >= 24) { # PE header is 24 bytes (plus optional header)
1286 1         8 my $mach = Get16u(\$buff, 4); # MachineType
1287 1         4 my $flags = Get16u(\$buff, 22); # ImageFileCharacteristics
1288 1   50     8 my $machine = $Image::ExifTool::EXE::Main{0}{PrintConv}{$mach} || '';
1289 1 50       7 my $winType = $machine =~ /64/ ? 'Win64' : 'Win32';
1290 1 50       4 $ext = $flags & 0x2000 ? 'DLL' : 'EXE';
1291 1         9 $et->SetFileType("$winType $ext", undef, $ext);
1292 1 50       4 return 1 if $fast3;
1293             # read the rest of the optional header if necessary
1294 1         5 my $optSize = Get16u(\$buff, 20);
1295 1         4 my $more = $optSize + 24 - $size;
1296 1         3 my $magic = 0;
1297 1 50       5 if ($more > 0) {
1298 1 50       19 if ($raf->Read($buf2, $more) == $more) {
1299 1         3 $buff .= $buf2;
1300 1         2 $size += $more;
1301 1         4 $magic = Get16u(\$buff, 24);
1302             # verify PE magic number
1303 1 50 33     22 unless ($magic == 0x107 or $magic == 0x10b or $magic == 0x20b) {
      33        
1304 0         0 $et->Warn('Unknown PE magic number');
1305 0         0 return 1;
1306             }
1307             # --> 64-bit if $magic is 0x20b ????
1308             } else {
1309 0         0 $et->Warn('Error reading optional header');
1310             }
1311             }
1312             # process PE COFF file header
1313 1         6 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::Main');
1314 1         8 %dirInfo = (
1315             DataPt => \$buff,
1316             DataPos => $raf->Tell() - $size,
1317             DataLen => $size,
1318             DirStart => 4,
1319             DirLen => $size - 4,
1320             );
1321 1         10 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1322 1         4 my $num = Get16u(\$buff, 6); # NumberOfSections
1323             # image data directory entry for debug info is index 6,
1324             # so offset is 4 bytes + 20 byte header + 96 bytes into PE32 optional
1325             # header (or 112 bytes for PE32+) + 6 * 8 bytes into data directory
1326 1 50       6 my $dirEntry = $magic == 0x20b ? 184 : 168;
1327 1         3 my ($addr, $len, $pos, $buf2); # virtual address/size of debug section
1328 1 50       5 if (length($buff) >= $dirEntry + 8) {
1329 1         4 $addr = Get32u(\$buff, $dirEntry);
1330 1         4 $len = Get32u(\$buff, $dirEntry+4);
1331             }
1332             # process data dictionary
1333             # (ref https://www.debuginfo.com/articles/debuginfomatch.html)
1334 1 50       6 return 1 unless $raf->Read($buff, 40 * $num) == 40 * $num;
1335 1         7 %dirInfo = ( RAF => $raf, DataPt => \$buff );
1336 1 50       5 ProcessPEDict($et, \%dirInfo) or $et->Warn('Error processing PE resources');
1337             # dive into debug section to extract pdb info if available
1338             return 1 unless $addr and $len < 2800 and $dirInfo{Sections} and
1339 1 0 33     10 ($magic == 0x10b or $magic == 0x20b);
      33        
      0        
      0        
1340             # get file offset for debug section
1341 0         0 my $off = GetFileOffset($addr, $dirInfo{Sections});
1342 0 0 0     0 return 1 unless $off and $raf->Seek($off,0) and
      0        
1343             $raf->Read($buff,$len) == $len;
1344 0         0 for ($pos=0; $pos+28<=$len; $pos+=28) {
1345 0         0 my $type = Get32u(\$buff,$pos+12);
1346 0 0 0     0 next unless $type == 2 or $type == 4; # (CodeView debug data)
1347 0         0 my ($n, $of) = (Get32u(\$buff,$pos+16), Get32u(\$buff,$pos+24));
1348 0 0 0     0 next unless $n < 1e4 and $raf->Seek($of,0) and $raf->Read($buf2,$n) == $n;
      0        
1349 0 0       0 if ($type == 2) { # CodeView debug info
1350 0 0       0 next unless $buf2 =~ /^(RSDS|NB10)/;
1351 0         0 $tagTablePtr = GetTagTable("Image::ExifTool::EXE::Debug$1");
1352 0         0 substr($buf2,0,4) = substr($buff,$pos+4,4); # use timestamp from debug dir
1353 0         0 %dirInfo = ( DataPt => \$buf2, DataPos => $of );
1354 0         0 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1355             } else { # misc debug info
1356 0 0       0 next unless $n > 12;
1357 0         0 my $exe = substr($buf2,12);
1358 0 0       0 $exe = $et->Decode($exe, 'UCS2') if Get32u(\$buf2,8);
1359 0         0 $exe =~ s/\0.*//; # truncate at null
1360 0         0 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::Misc');
1361 0         0 $et->HandleTag($tagTablePtr, 12, $exe, DataPt => \$buf2, DataPos => $of);
1362             }
1363             }
1364 0         0 return 1;
1365             }
1366             } else {
1367 0         0 $type = 'Virtual Device Driver';
1368 0         0 $ext = '386';
1369             }
1370             } else {
1371 0         0 $type = 'DOS EXE';
1372 0         0 $ext = 'exe';
1373             }
1374             #
1375             # Mach-O (Mac OS X)
1376             #
1377             } elsif ($buff =~ /^(\xca\xfe\xba\xbe|\xfe\xed\xfa(\xce|\xcf)|(\xce|\xcf)\xfa\xed\xfe)/ and $size > 12) {
1378             # Mach-O executable
1379             # (ref http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html)
1380 2         10 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::MachO');
1381 2 50       13 if ($1 eq "\xca\xfe\xba\xbe") {
    50          
1382 0         0 SetByteOrder('MM');
1383 0         0 my $ver = Get32u(\$buff, 4);
1384             # Java bytecode .class files have the same magic number, so we need to look deeper
1385             # (ref https://github.com/file/file/blob/master/magic/Magdir/cafebabe#L6-L15)
1386 0 0       0 if ($ver > 30) {
1387             # this is Java bytecode
1388 0         0 $et->SetFileType('Java bytecode', 'application/java-byte-code', 'class');
1389 0         0 return 1;
1390             }
1391 0         0 $et->SetFileType('Mach-O fat binary executable', undef, '');
1392 0 0       0 return 1 if $fast3;
1393 0         0 my $count = Get32u(\$buff, 4); # get architecture count
1394 0         0 my $more = $count * 20 - ($size - 8);
1395 0 0       0 if ($more > 0) {
1396 0 0       0 unless ($raf->Read($buf2, $more) == $more) {
1397 0         0 $et->Warn('Error reading fat-arch headers');
1398 0         0 return 1;
1399             }
1400 0         0 $buff .= $buf2;
1401 0         0 $size += $more;
1402             }
1403 0         0 $et->HandleTag($tagTablePtr, 2, $count);
1404 0         0 my $i;
1405 0         0 for ($i=0; $i<$count; ++$i) {
1406 0         0 my $cpuType = Get32s(\$buff, 8 + $i * 20);
1407 0         0 my $subType = Get32s(\$buff, 12 + $i * 20);
1408 0         0 $et->HandleTag($tagTablePtr, 3, $cpuType);
1409 0         0 $et->HandleTag($tagTablePtr, 4, "$cpuType $subType");
1410             }
1411             # load first Mach-O header to get the object file type
1412 0         0 my $offset = Get32u(\$buff, 16);
1413 0 0 0     0 if ($raf->Seek($offset, 0) and $raf->Read($buf2, 16) == 16) {
1414 0 0       0 if ($buf2 =~ /^(\xfe\xed\xfa(\xce|\xcf)|(\xce|\xcf)\xfa\xed\xfe)/) {
    0          
1415 0 0       0 SetByteOrder($buf2 =~ /^\xfe\xed/ ? 'MM' : 'II');
1416 0         0 my $objType = Get32u(\$buf2, 12);
1417 0         0 $et->HandleTag($tagTablePtr, 5, $objType);
1418             # override file type if this is a library or object file
1419 0         0 MachOverride($et, $objType, 'fat');
1420             } elsif ($buf2 =~ /^!\x0a/) {
1421             # .a libraries use this magic number
1422 0         0 $et->HandleTag($tagTablePtr, 5, -1);
1423             # override file type since this is a library
1424 0         0 $et->OverrideFileType('Mach-O fat static library', undef, 'A');
1425             } else {
1426 0         0 $et->Warn('Unrecognized object file type');
1427             }
1428             } else {
1429 0         0 $et->Warn('Error reading file');
1430             }
1431             } elsif ($size >= 16) {
1432 2         12 $et->SetFileType('Mach-O executable', undef, '');
1433 2 50       5 return 1 if $fast3;
1434 2         10 ExtractMachTags($et, \$buff, 1);
1435             }
1436 2         6 return 1;
1437             #
1438             # PEF (classic MacOS)
1439             #
1440             } elsif ($buff =~ /^Joy!peff/ and $size > 12) {
1441             # ref http://developer.apple.com/documentation/mac/pdf/MacOS_RT_Architectures.pdf
1442 0         0 $et->SetFileType('Classic MacOS executable', undef, '');
1443 0 0       0 return 1 if $fast3;
1444 0         0 SetByteOrder('MM');
1445 0         0 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::PEF');
1446 0         0 %dirInfo = (
1447             DataPt => \$buff,
1448             DataPos => 0,
1449             DataLen => $size,
1450             DirStart => 0,
1451             DirLen => $size,
1452             );
1453 0         0 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1454 0         0 return 1;
1455             #
1456             # ELF (Unix)
1457             #
1458             } elsif ($buff =~ /^\x7fELF/ and $size >= 16) {
1459 2         12 $et->SetFileType('ELF executable', undef, '');
1460 2 50       6 return 1 if $fast3;
1461 2 50       10 SetByteOrder(Get8u(\$buff,5) == 1 ? 'II' : 'MM');
1462 2         7 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::ELF');
1463 2         12 %dirInfo = (
1464             DataPt => \$buff,
1465             DataPos => 0,
1466             DataLen => $size,
1467             DirLen => $size,
1468             );
1469 2         9 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1470             # override file type if this is a library or object file
1471             my $override = {
1472             1 => [ 'ELF object file', 'O' ],
1473             3 => [ 'ELF shared library', 'SO' ],
1474 2   50     18 }->{$$et{VALUE}{ObjectFileType} || 0};
1475 2 100       14 $et->OverrideFileType($$override[0], undef, $$override[1]) if $override;
1476 2         10 return 1;
1477             #
1478             # .a libraries
1479             #
1480             } elsif ($buff =~ /^!\x0a/) {
1481 1         5 $et->SetFileType('Static library', undef, 'A');
1482 1 50       2 return 1 if $fast3;
1483 1         2 my $pos = 8; # current file position
1484 1         2 my $max = 10; # maximum number of archive files to check
1485             # read into list of ar structures (each 60 bytes long):
1486 1         3 while ($max-- > 0) {
1487             # seek to start of the ar structure and read it
1488 2 50 33     6 $raf->Seek($pos, 0) and $raf->Read($buff, 60) == 60 or last;
1489 2 50       8 substr($buff, 58, 2) eq "`\n" or $et->Warn('Invalid archive header'), last;
1490 2 100       5 unless ($tagTablePtr) {
1491             # extract some information from first file in archive
1492 1         3 $tagTablePtr = GetTagTable('Image::ExifTool::EXE::AR');
1493 1         4 %dirInfo = (
1494             DataPt => \$buff,
1495             DataPos => $pos,
1496             );
1497 1         3 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1498             }
1499 2         8 my $name = substr($buff, 0, 16);
1500 2 50       15 if ($name =~ m{^#1/(\d+) *$}) { # check for extended archive (BSD variant)
1501 2         7 my $len = $1;
1502 2 50       8 $len > 256 and $et->Warn('Invalid extended archive name length'), last;
1503             # (we read the name here just to move the file pointer)
1504 2 50       8 $raf->Read($name, $len) == $len or $et->Warn('Error reading archive name'), last;
1505             }
1506 2         7 my $arSize = substr($buff, 48, 10);
1507 2 50       11 $arSize =~ s/^(\d+).*/$1/s or last; # make sure archive size is a number
1508 2 50       8 $raf->Read($buff, 28) == 28 or last; # read (possible) Mach header
1509 2 100       7 ExtractMachTags($et, \$buff) and last; # try to extract tags
1510 1         4 $pos += 60 + $arSize; # step to next entry
1511 1 50       5 ++$pos if $pos & 0x01; # padded to an even byte
1512             }
1513 1         6 return 1;
1514             #
1515             # various scripts (perl, sh, etc...)
1516             #
1517             } elsif ($buff =~ m{^#!\s*/\S*bin/(\w+)}) {
1518 0           my $prog = $1;
1519 0 0 0       $prog = $1 if $prog eq 'env' and $buff =~ /\b(perl|python|ruby|php)\b/;
1520 0           $type = "$prog script";
1521 0           $mime = "text/x-$prog";
1522             $ext = {
1523             perl => 'pl',
1524             python => 'py',
1525             ruby => 'rb',
1526             php => 'php',
1527 0           }->{$prog};
1528             # use '.sh' for extension of all shell scripts
1529 0 0         $ext = $prog =~ /sh$/ ? 'sh' : '' unless defined $ext;
    0          
1530             }
1531 0 0         return 0 unless $type;
1532 0           $et->SetFileType($type, $mime, $ext);
1533 0           return 1;
1534             }
1535              
1536             1; # end
1537              
1538             __END__