1 // PeHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../../C/CpuArch.h"
8 
9 #include "../../Common/DynamicBuffer.h"
10 #include "../../Common/ComTry.h"
11 #include "../../Common/IntToString.h"
12 #include "../../Common/StringConvert.h"
13 
14 #include "../../Windows/PropVariantUtils.h"
15 #include "../../Windows/TimeUtils.h"
16 
17 #include "../Common/LimitedStreams.h"
18 #include "../Common/ProgressUtils.h"
19 #include "../Common/RegisterArc.h"
20 #include "../Common/StreamObjects.h"
21 #include "../Common/StreamUtils.h"
22 
23 #include "../Compress/CopyCoder.h"
24 
25 #define Get16(p) GetUi16(p)
26 #define Get32(p) GetUi32(p)
27 #define Get64(p) GetUi64(p)
28 
29 #define G16(offs, v) v = Get16(p + (offs))
30 #define G32(offs, v) v = Get32(p + (offs))
31 #define G64(offs, v) v = Get64(p + (offs))
32 
33 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
34 
35 using namespace NWindows;
36 
37 namespace NArchive {
38 namespace NPe {
39 
40 static const UInt32 k_Signature32 = 0x00004550;
41 
CalcCheckSum(ISequentialInStream * stream,UInt32 size,UInt32 excludePos,UInt32 & res)42 static HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res)
43 {
44   const UInt32 kBufSizeMax = (UInt32)1 << 15;
45   UInt32 bufSize = kBufSizeMax;
46   CByteBuffer buffer(bufSize);
47   Byte *buf = buffer;
48   UInt32 sum = 0;
49   UInt32 pos = 0;
50   for (;;)
51   {
52     UInt32 rem = size - pos;
53     if (rem > bufSize)
54       rem = bufSize;
55     if (rem == 0)
56       break;
57     size_t processed = rem;
58     RINOK(ReadStream(stream, buf, &processed));
59 
60     for (unsigned j = 0; j < 4; j++)
61     {
62       UInt32 e = excludePos + j;
63       if (pos <= e)
64       {
65         e -= pos;
66         if (e < processed)
67           buf[e] = 0;
68       }
69     }
70 
71     const unsigned kStep = (1 << 4);
72     {
73       for (size_t i = processed; (i & (kStep - 1)) != 0; i++)
74         buf[i] = 0;
75     }
76     {
77       const Byte *buf2 = buf;
78       const Byte *bufLimit = buf + processed;
79       UInt64 sum2 = 0;
80       for (; buf2 < bufLimit; buf2 += kStep)
81       {
82         UInt64 sum3 = (UInt64)Get32(buf2)
83             + Get32(buf2 + 4)
84             + Get32(buf2 + 8)
85             + Get32(buf2 + 12);
86         sum2 += sum3;
87       }
88       sum2 = (UInt32)(sum2) + (UInt64)(sum2 >> 32);
89       UInt32 sum3 = ((UInt32)sum2 + (UInt32)(sum2 >> 32));
90       sum += (sum3 & 0xFFFF) + (sum3 >> 16);
91       sum = (sum & 0xFFFF) + (sum >> 16);
92       sum = (sum & 0xFFFF) + (sum >> 16);
93     }
94 
95     pos += (UInt32)processed;
96     if (rem != processed)
97       break;
98   }
99   res = sum + pos;
100   return S_OK;
101 }
102 
103 
104 struct CVersion
105 {
106   UInt16 Major;
107   UInt16 Minor;
108 
ParseNArchive::NPe::CVersion109   void Parse(const Byte *p)
110   {
111     G16(0, Major);
112     G16(2, Minor);
113   }
114   void ToProp(NCOM::CPropVariant &prop);
115 };
116 
ToProp(NCOM::CPropVariant & prop)117 void CVersion::ToProp(NCOM::CPropVariant &prop)
118 {
119   char sz[32];
120   ConvertUInt32ToString(Major, sz);
121   unsigned len = MyStringLen(sz);
122   sz[len] = '.';
123   ConvertUInt32ToString(Minor, sz + len + 1);
124   prop = sz;
125 }
126 
127 static const unsigned kCoffHeaderSize = 20;
128 static const unsigned kPeHeaderSize = 4 + kCoffHeaderSize;
129 static const unsigned k_OptHeader32_Size_MIN = 96;
130 static const unsigned k_OptHeader64_Size_MIN = 112;
131 
132 static const UInt32 PE_IMAGE_FILE_DLL  = (1 << 13);
133 
134 struct CHeader
135 {
136   UInt16 Machine;
137   UInt16 NumSections;
138   UInt32 Time;
139   UInt32 PointerToSymbolTable;
140   UInt32 NumSymbols;
141   UInt16 OptHeaderSize;
142   UInt16 Flags;
143 
144   void ParseBase(const Byte *p);
145   bool ParseCoff(const Byte *p);
146   bool ParsePe(const Byte *p);
IsDllNArchive::NPe::CHeader147   bool IsDll() const { return (Flags & PE_IMAGE_FILE_DLL) != 0; }
148 };
149 
ParseBase(const Byte * p)150 void CHeader::ParseBase(const Byte *p)
151 {
152   G16( 0, Machine);
153   G16( 2, NumSections);
154   G32( 4, Time);
155   G32( 8, PointerToSymbolTable);
156   G32(12, NumSymbols);
157   G16(16, OptHeaderSize);
158   G16(18, Flags);
159 }
160 
ParsePe(const Byte * p)161 bool CHeader::ParsePe(const Byte *p)
162 {
163   if (Get32(p) != k_Signature32)
164     return false;
165   ParseBase(p + 4);
166   return OptHeaderSize >= k_OptHeader32_Size_MIN;
167 }
168 
169 struct CDirLink
170 {
171   UInt32 Va;
172   UInt32 Size;
173 
CDirLinkNArchive::NPe::CDirLink174   CDirLink(): Va(0), Size(0) {}
ParseNArchive::NPe::CDirLink175   void Parse(const Byte *p)
176   {
177     G32(0, Va);
178     G32(4, Size);
179   }
180 };
181 
182 enum
183 {
184   kDirLink_Certificate = 4,
185   kDirLink_Debug = 6
186 };
187 
188 static const UInt32 kNumDirItemsMax = 16;
189 
190 struct CDebugEntry
191 {
192   UInt32 Flags;
193   UInt32 Time;
194   CVersion Ver;
195   UInt32 Type;
196   UInt32 Size;
197   UInt32 Va;
198   UInt32 Pa;
199 
ParseNArchive::NPe::CDebugEntry200   void Parse(const Byte *p)
201   {
202     G32(0, Flags);
203     G32(4, Time);
204     Ver.Parse(p + 8);
205     G32(12, Type);
206     G32(16, Size);
207     G32(20, Va);
208     G32(24, Pa);
209   }
210 };
211 
212 static const UInt32 k_CheckSum_Field_Offset = 64;
213 
214 static const UInt32 PE_OptHeader_Magic_32 = 0x10B;
215 static const UInt32 PE_OptHeader_Magic_64 = 0x20B;
216 
217 static const UInt32 k_SubSystems_EFI_First = 10;
218 static const UInt32 k_SubSystems_EFI_Last = 13;
219 
220 struct COptHeader
221 {
222   UInt16 Magic;
223   Byte LinkerVerMajor;
224   Byte LinkerVerMinor;
225 
226   UInt32 CodeSize;
227   UInt32 InitDataSize;
228   UInt32 UninitDataSize;
229 
230   // UInt32 AddressOfEntryPoint;
231   // UInt32 BaseOfCode;
232   // UInt32 BaseOfData32;
233   UInt64 ImageBase;
234 
235   UInt32 SectAlign;
236   UInt32 FileAlign;
237 
238   CVersion OsVer;
239   CVersion ImageVer;
240   CVersion SubsysVer;
241 
242   UInt32 ImageSize;
243   UInt32 HeadersSize;
244   UInt32 CheckSum;
245   UInt16 SubSystem;
246   UInt16 DllCharacts;
247 
248   UInt64 StackReserve;
249   UInt64 StackCommit;
250   UInt64 HeapReserve;
251   UInt64 HeapCommit;
252 
253   UInt32 NumDirItems;
254   CDirLink DirItems[kNumDirItemsMax];
255 
Is64BitNArchive::NPe::COptHeader256   bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; }
257   bool Parse(const Byte *p, UInt32 size);
258 
GetNumFileAlignBitsNArchive::NPe::COptHeader259   int GetNumFileAlignBits() const
260   {
261     for (unsigned i = 0; i <= 31; i++)
262       if (((UInt32)1 << i) == FileAlign)
263         return i;
264     return -1;
265   }
266 
IsSybSystem_EFINArchive::NPe::COptHeader267   bool IsSybSystem_EFI() const
268   {
269     return
270         SubSystem >= k_SubSystems_EFI_First &&
271         SubSystem <= k_SubSystems_EFI_Last;
272   }
273 };
274 
Parse(const Byte * p,UInt32 size)275 bool COptHeader::Parse(const Byte *p, UInt32 size)
276 {
277   if (size < k_OptHeader32_Size_MIN)
278     return false;
279   Magic = Get16(p);
280   switch (Magic)
281   {
282     case PE_OptHeader_Magic_32:
283     case PE_OptHeader_Magic_64:
284       break;
285     default:
286       return false;
287   }
288   LinkerVerMajor = p[2];
289   LinkerVerMinor = p[3];
290 
291   G32( 4, CodeSize);
292   G32( 8, InitDataSize);
293   G32(12, UninitDataSize);
294   // G32(16, AddressOfEntryPoint);
295   // G32(20, BaseOfCode);
296 
297   G32(32, SectAlign);
298   G32(36, FileAlign);
299 
300   OsVer.Parse(p + 40);
301   ImageVer.Parse(p + 44);
302   SubsysVer.Parse(p + 48);
303 
304   // reserved = Get32(p + 52);
305 
306   G32(56, ImageSize);
307   G32(60, HeadersSize);
308   G32(64, CheckSum);
309   G16(68, SubSystem);
310   G16(70, DllCharacts);
311 
312   UInt32 pos;
313   if (Is64Bit())
314   {
315     if (size < k_OptHeader64_Size_MIN)
316       return false;
317     // BaseOfData32 = 0;
318     G64(24, ImageBase);
319     G64(72, StackReserve);
320     G64(80, StackCommit);
321     G64(88, HeapReserve);
322     G64(96, HeapCommit);
323     pos = 108;
324   }
325   else
326   {
327     // G32(24, BaseOfData32);
328     G32(28, ImageBase);
329     G32(72, StackReserve);
330     G32(76, StackCommit);
331     G32(80, HeapReserve);
332     G32(84, HeapCommit);
333     pos = 92;
334   }
335 
336   G32(pos, NumDirItems);
337   if (NumDirItems > (1 << 16))
338     return false;
339   pos += 4;
340   if (pos + 8 * NumDirItems > size)
341     return false;
342   for (UInt32 i = 0; i < NumDirItems && i < kNumDirItemsMax; i++)
343     DirItems[i].Parse(p + pos + i * 8);
344   return true;
345 }
346 
347 static const UInt32 kSectionSize = 40;
348 
349 struct CSection
350 {
351   AString Name;
352 
353   UInt32 VSize;
354   UInt32 Va;
355   UInt32 PSize;
356   UInt32 Pa;
357   UInt32 Flags;
358   UInt32 Time;
359   // UInt16 NumRelocs;
360   bool IsRealSect;
361   bool IsDebug;
362   bool IsAdditionalSection;
363 
CSectionNArchive::NPe::CSection364   CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {}
365 
GetSizeExtractNArchive::NPe::CSection366   UInt32 GetSizeExtract() const { return PSize; }
GetSizeMinNArchive::NPe::CSection367   UInt32 GetSizeMin() const { return MyMin(PSize, VSize); }
368 
UpdateTotalSizeNArchive::NPe::CSection369   void UpdateTotalSize(UInt32 &totalSize) const
370   {
371     UInt32 t = Pa + PSize;
372     if (totalSize < t)
373       totalSize = t;
374   }
375 
376   void Parse(const Byte *p);
377 
CompareNArchive::NPe::CSection378   int Compare(const CSection &s) const
379   {
380     RINOZ(MyCompare(Pa, s.Pa));
381     UInt32 size1 = GetSizeExtract();
382     UInt32 size2 = s.GetSizeExtract();
383     return MyCompare(size1, size2);
384   }
385 };
386 
387 static const unsigned kNameSize = 8;
388 
GetName(const Byte * name,AString & res)389 static void GetName(const Byte *name, AString &res)
390 {
391   res.SetFrom_CalcLen((const char *)name, kNameSize);
392 }
393 
Parse(const Byte * p)394 void CSection::Parse(const Byte *p)
395 {
396   GetName(p, Name);
397   G32( 8, VSize);
398   G32(12, Va);
399   G32(16, PSize);
400   G32(20, Pa);
401   // G16(32, NumRelocs);
402   G32(36, Flags);
403 }
404 
405 
406 
407 // IMAGE_FILE_*
408 
409 static const CUInt32PCharPair g_HeaderCharacts[] =
410 {
411   {  1, "Executable" },
412   { 13, "DLL" },
413   {  8, "32-bit" },
414   {  5, "LargeAddress" },
415   {  0, "NoRelocs" },
416   {  2, "NoLineNums" },
417   {  3, "NoLocalSyms" },
418   {  4, "AggressiveWsTrim" },
419   {  9, "NoDebugInfo" },
420   { 10, "RemovableRun" },
421   { 11, "NetRun" },
422   { 12, "System" },
423   { 14, "UniCPU" },
424   {  7, "Little-Endian" },
425   { 15, "Big-Endian" }
426 };
427 
428 // IMAGE_DLLCHARACTERISTICS_*
429 
430 static const char * const g_DllCharacts[] =
431 {
432     NULL
433   , NULL
434   , NULL
435   , NULL
436   , NULL
437   , "HighEntropyVA"
438   , "Relocated"
439   , "Integrity"
440   , "NX-Compatible"
441   , "NoIsolation"
442   , "NoSEH"
443   , "NoBind"
444   , "AppContainer"
445   , "WDM"
446   , "GuardCF"
447   , "TerminalServerAware"
448 };
449 
450 
451 // IMAGE_SCN_* constants:
452 
453 static const char * const g_SectFlags[] =
454 {
455     NULL
456   , NULL
457   , NULL
458   , "NoPad"
459   , NULL
460   , "Code"
461   , "InitializedData"
462   , "UninitializedData"
463   , "Other"
464   , "Comments"
465   , NULL // OVER
466   , "Remove"
467   , "COMDAT"
468   , NULL
469   , "NO_DEFER_SPEC_EXC"
470   , "GP" // MEM_FARDATA
471   , NULL // SYSHEAP
472   , "PURGEABLE" // 16BIT
473   , "LOCKED"
474   , "PRELOAD"
475   , NULL
476   , NULL
477   , NULL
478   , NULL
479   , "ExtendedRelocations"
480   , "Discardable"
481   , "NotCached"
482   , "NotPaged"
483   , "Shared"
484   , "Execute"
485   , "Read"
486   , "Write"
487 };
488 
489 static const CUInt32PCharPair g_MachinePairs[] =
490 {
491   { 0x014C, "x86" },
492   { 0x014D, "I860" },
493   { 0x0162, "MIPS-R3000" },
494   { 0x0166, "MIPS-R4000" },
495   { 0x0168, "MIPS-R10000" },
496   { 0x0169, "MIPS-V2" },
497   { 0x0184, "Alpha" },
498   { 0x01A2, "SH3" },
499   { 0x01A3, "SH3-DSP" },
500   { 0x01A4, "SH3E" },
501   { 0x01A6, "SH4" },
502   { 0x01A8, "SH5" },
503   { 0x01C0, "ARM" },
504   { 0x01C2, "ARM-Thumb" },
505   { 0x01C4, "ARM-NT" },
506   { 0x01D3, "AM33" },
507   { 0x01F0, "PPC" },
508   { 0x01F1, "PPC-FP" },
509   { 0x0200, "IA-64" },
510   { 0x0266, "MIPS-16" },
511   { 0x0284, "Alpha-64" },
512   { 0x0366, "MIPS-FPU" },
513   { 0x0466, "MIPS-FPU16" },
514   { 0x0520, "TriCore" },
515   { 0x0CEF, "CEF" },
516   { 0x0EBC, "EFI" },
517   { 0x8664, "x64" },
518   { 0x9041, "M32R" },
519   { 0xAA64, "ARM64" },
520   { 0xC0EE, "CEE" }
521 };
522 
523 static const char * const g_SubSystems[] =
524 {
525     "Unknown"
526   , "Native"
527   , "Windows GUI"
528   , "Windows CUI"
529   , NULL // "Old Windows CE"
530   , "OS2"
531   , NULL
532   , "Posix"
533   , "Win9x"
534   , "Windows CE"
535   , "EFI"
536   , "EFI Boot"
537   , "EFI Runtime"
538   , "EFI ROM"
539   , "XBOX"
540   , NULL
541   , "Windows Boot"
542   , "XBOX Catalog" // 17
543 };
544 
545 static const char * const g_ResTypes[] =
546 {
547     NULL
548   , "CURSOR"
549   , "BITMAP"
550   , "ICON"
551   , "MENU"
552   , "DIALOG"
553   , "STRING"
554   , "FONTDIR"
555   , "FONT"
556   , "ACCELERATOR"
557   , "RCDATA"
558   , "MESSAGETABLE"
559   , "GROUP_CURSOR"
560   , NULL
561   , "GROUP_ICON"
562   , NULL
563   , "VERSION"
564   , "DLGINCLUDE"
565   , NULL
566   , "PLUGPLAY"
567   , "VXD"
568   , "ANICURSOR"
569   , "ANIICON"
570   , "HTML"
571   , "MANIFEST"
572 };
573 
574 static const UInt32 kFlag = (UInt32)1 << 31;
575 static const UInt32 kMask = ~kFlag;
576 
577 struct CTableItem
578 {
579   UInt32 Offset;
580   UInt32 ID;
581 };
582 
583 
584 static const UInt32 kBmpHeaderSize = 14;
585 static const UInt32 kIconHeaderSize = 22;
586 
587 struct CResItem
588 {
589   UInt32 Type;
590   UInt32 ID;
591   UInt32 Lang;
592 
593   UInt32 Size;
594   UInt32 Offset;
595 
596   UInt32 HeaderSize;
597   Byte Header[kIconHeaderSize]; // it must be enough for max size header.
598   bool Enabled;
599 
IsNameEqualNArchive::NPe::CResItem600   bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; }
GetSizeNArchive::NPe::CResItem601   UInt32 GetSize() const { return Size + HeaderSize; }
IsBmpNArchive::NPe::CResItem602   bool IsBmp() const { return Type == 2; }
IsIconNArchive::NPe::CResItem603   bool IsIcon() const { return Type == 3; }
IsStringNArchive::NPe::CResItem604   bool IsString() const { return Type == 6; }
IsRcDataNArchive::NPe::CResItem605   bool IsRcData() const { return Type == 10; }
IsVersionNArchive::NPe::CResItem606   bool IsVersion() const { return Type == 16; }
IsRcDataOrUnknownNArchive::NPe::CResItem607   bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; }
608 };
609 
610 struct CTextFile
611 {
612   CByteDynamicBuffer Buf;
613 
FinalSizeNArchive::NPe::CTextFile614   size_t FinalSize() const { return Buf.GetPos(); }
615 
616   void AddChar(Byte c);
617   void AddWChar(UInt16 c);
618   void AddWChar_Smart(UInt16 c);
619   void NewLine();
620   void AddString(const char *s);
621   void AddSpaces(int num);
AddBytesNArchive::NPe::CTextFile622   void AddBytes(const Byte *p, size_t size)
623   {
624     Buf.AddData(p, size);
625   }
626 
OpenBlockNArchive::NPe::CTextFile627   void OpenBlock(int num)
628   {
629     AddSpaces(num);
630     AddChar('{');
631     NewLine();
632   }
CloseBlockNArchive::NPe::CTextFile633   void CloseBlock(int num)
634   {
635     AddSpaces(num);
636     AddChar('}');
637     NewLine();
638   }
639 };
640 
AddChar(Byte c)641 void CTextFile::AddChar(Byte c)
642 {
643   Byte *p = Buf.GetCurPtrAndGrow(2);
644   p[0] = c;
645   p[1] = 0;
646 }
647 
AddWChar(UInt16 c)648 void CTextFile::AddWChar(UInt16 c)
649 {
650   Byte *p = Buf.GetCurPtrAndGrow(2);
651   SetUi16(p, c);
652 }
653 
AddWChar_Smart(UInt16 c)654 void CTextFile::AddWChar_Smart(UInt16 c)
655 {
656   if (c == '\n')
657   {
658     AddChar('\\');
659     c = 'n';
660   }
661   AddWChar(c);
662 }
663 
NewLine()664 void CTextFile::NewLine()
665 {
666   AddChar(0x0D);
667   AddChar(0x0A);
668 }
669 
AddString(const char * s)670 void CTextFile::AddString(const char *s)
671 {
672   for (;; s++)
673   {
674     char c = *s;
675     if (c == 0)
676       return;
677     AddChar(c);
678   }
679 }
680 
AddSpaces(int num)681 void CTextFile::AddSpaces(int num)
682 {
683   for (int i = 0; i < num; i++)
684     AddChar(' ');
685 }
686 
687 struct CStringItem: public CTextFile
688 {
689   UInt32 Lang;
690 };
691 
692 struct CByteBuffer_WithLang: public CByteBuffer
693 {
694   UInt32 Lang;
695 };
696 
697 
698 struct CMixItem
699 {
700   int SectionIndex;
701   int ResourceIndex;
702   int StringIndex;
703   int VersionIndex;
704 
CMixItemNArchive::NPe::CMixItem705   CMixItem(): SectionIndex(-1), ResourceIndex(-1), StringIndex(-1), VersionIndex(-1) {}
IsSectionItemNArchive::NPe::CMixItem706   bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0 && VersionIndex < 0; }
707 };
708 
709 struct CUsedBitmap
710 {
711   CByteBuffer Buf;
712 public:
AllocNArchive::NPe::CUsedBitmap713   void Alloc(size_t size)
714   {
715     size = (size + 7) / 8;
716     Buf.Alloc(size);
717     memset(Buf, 0, size);
718   }
719 
FreeNArchive::NPe::CUsedBitmap720   void Free()
721   {
722     Buf.Free();
723   }
724 
SetRangeNArchive::NPe::CUsedBitmap725   bool SetRange(size_t from, unsigned size)
726   {
727     for (unsigned i = 0; i < size; i++)
728     {
729       size_t pos = (from + i) >> 3;
730       Byte mask = (Byte)(1 << ((from + i) & 7));
731       Byte b = Buf[pos];
732       if ((b & mask) != 0)
733         return false;
734       Buf[pos] = (Byte)(b | mask);
735     }
736     return true;
737   }
738 };
739 
740 struct CStringKeyValue
741 {
742   UString Key;
743   UString Value;
744 };
745 
746 class CHandler:
747   public IInArchive,
748   public IInArchiveGetStream,
749   public IArchiveAllowTail,
750   public CMyUnknownImp
751 {
752   CMyComPtr<IInStream> _stream;
753   CObjectVector<CSection> _sections;
754   CHeader _header;
755   UInt32 _totalSize;
756   Int32 _mainSubfile;
757 
758   CRecordVector<CMixItem> _mixItems;
759   CRecordVector<CResItem> _items;
760   CObjectVector<CStringItem> _strings;
761   CObjectVector<CByteBuffer_WithLang> _versionFiles;
762   UString _versionFullString;
763   UString _versionShortString;
764   UString _originalFilename;
765   CObjectVector<CStringKeyValue> _versionKeys;
766 
767   CByteBuffer _buf;
768   bool _oneLang;
769   UString _resourcesPrefix;
770   CUsedBitmap _usedRes;
771   // bool _parseResources;
772   bool _checksumError;
773 
IsOpt() const774   bool IsOpt() const { return _header.OptHeaderSize != 0; }
775 
776   COptHeader _optHeader;
777 
778   bool _coffMode;
779   bool _allowTail;
780 
781   HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection);
782   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
783 
784   void AddResNameToString(UString &s, UInt32 id) const;
785   void AddLangPrefix(UString &s, UInt32 lang) const;
786   HRESULT ReadString(UInt32 offset, UString &dest) const;
787   HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items);
788   bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size);
789   HRESULT OpenResources(unsigned sectIndex, IInStream *stream, IArchiveOpenCallback *callback);
790   void CloseResources();
791 
792 
CheckItem(const CSection & sect,const CResItem & item,size_t offset) const793   bool CheckItem(const CSection &sect, const CResItem &item, size_t offset) const
794   {
795     return item.Offset >= sect.Va && offset <= _buf.Size() && _buf.Size() - offset >= item.Size;
796   }
797 
798 public:
CHandler(bool coffMode=false)799   CHandler(bool coffMode = false):
800         _coffMode(coffMode),
801         _allowTail(coffMode)
802         {}
803 
804   MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IArchiveAllowTail)
805   INTERFACE_IInArchive(;)
806   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
807   STDMETHOD(AllowTail)(Int32 allowTail);
808 };
809 
810 
811 enum
812 {
813   kpidSectAlign = kpidUserDefined,
814   kpidFileAlign,
815   kpidLinkerVer,
816   kpidOsVer,
817   kpidImageVer,
818   kpidSubsysVer,
819   kpidCodeSize,
820   kpidImageSize,
821   kpidInitDataSize,
822   kpidUnInitDataSize,
823   kpidHeadersSizeUnInitDataSize,
824   kpidSubSystem,
825   kpidDllCharacts,
826   kpidStackReserve,
827   kpidStackCommit,
828   kpidHeapReserve,
829   kpidHeapCommit,
830   kpidImageBase
831   // kpidAddressOfEntryPoint,
832   // kpidBaseOfCode,
833   // kpidBaseOfData32,
834 };
835 
836 static const CStatProp kArcProps[] =
837 {
838   // { NULL, kpidWarning, VT_BSTR},
839   { NULL, kpidCpu, VT_BSTR},
840   { NULL, kpidBit64, VT_BOOL},
841   { NULL, kpidCharacts, VT_BSTR},
842   { NULL, kpidCTime, VT_FILETIME},
843   { NULL, kpidHeadersSize, VT_UI4},
844   { NULL, kpidChecksum, VT_UI4},
845   { NULL, kpidName, VT_BSTR},
846 
847   { "Image Size", kpidImageSize, VT_UI4},
848   { "Section Alignment", kpidSectAlign, VT_UI4},
849   { "File Alignment", kpidFileAlign, VT_UI4},
850   { "Code Size", kpidCodeSize, VT_UI4},
851   { "Initialized Data Size", kpidInitDataSize, VT_UI4},
852   { "Uninitialized Data Size", kpidUnInitDataSize, VT_UI4},
853   { "Linker Version", kpidLinkerVer, VT_BSTR},
854   { "OS Version", kpidOsVer, VT_BSTR},
855   { "Image Version", kpidImageVer, VT_BSTR},
856   { "Subsystem Version", kpidSubsysVer, VT_BSTR},
857   { "Subsystem", kpidSubSystem, VT_BSTR},
858   { "DLL Characteristics", kpidDllCharacts, VT_BSTR},
859   { "Stack Reserve", kpidStackReserve, VT_UI8},
860   { "Stack Commit", kpidStackCommit, VT_UI8},
861   { "Heap Reserve", kpidHeapReserve, VT_UI8},
862   { "Heap Commit", kpidHeapCommit, VT_UI8},
863   { "Image Base", kpidImageBase, VT_UI8},
864   { NULL, kpidComment, VT_BSTR},
865 
866   // { "Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8},
867   // { "Base Of Code", kpidBaseOfCode, VT_UI8},
868   // { "Base Of Data", kpidBaseOfData32, VT_UI8},
869 };
870 
871 static const Byte kProps[] =
872 {
873   kpidPath,
874   kpidSize,
875   kpidPackSize,
876   kpidVirtualSize,
877   kpidCharacts,
878   kpidOffset,
879   kpidVa,
880 };
881 
882 IMP_IInArchive_Props
883 IMP_IInArchive_ArcProps_WITH_NAME
884 
TimeToProp(UInt32 unixTime,NCOM::CPropVariant & prop)885 static void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop)
886 {
887   if (unixTime != 0)
888   {
889     FILETIME ft;
890     NTime::UnixTimeToFileTime(unixTime, ft);
891     prop = ft;
892   }
893 }
894 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)895 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
896 {
897   COM_TRY_BEGIN
898   NCOM::CPropVariant prop;
899   switch (propID)
900   {
901     case kpidPhySize: prop = _totalSize; break;
902     case kpidComment: if (!_versionFullString.IsEmpty()) prop = _versionFullString; break;
903     case kpidShortComment:
904       if (!_versionShortString.IsEmpty())
905         prop = _versionShortString;
906       else
907       {
908         PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop);
909       }
910       break;
911 
912     case kpidName: if (!_originalFilename.IsEmpty()) prop = _originalFilename; break;
913 
914     // case kpidIsSelfExe: prop = !_header.IsDll(); break;
915     // case kpidError:
916     case kpidWarning: if (_checksumError) prop = "Checksum error"; break;
917 
918     case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break;
919     case kpidMTime:
920     case kpidCTime: TimeToProp(_header.Time, prop); break;
921     case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break;
922     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;
923 
924     default:
925     if (IsOpt())
926     switch (propID)
927     {
928 
929     case kpidSectAlign: prop = _optHeader.SectAlign; break;
930     case kpidFileAlign: prop = _optHeader.FileAlign; break;
931     case kpidLinkerVer:
932     {
933       CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor };
934       v.ToProp(prop);
935       break;
936     }
937 
938     case kpidOsVer: _optHeader.OsVer.ToProp(prop); break;
939     case kpidImageVer: _optHeader.ImageVer.ToProp(prop); break;
940     case kpidSubsysVer: _optHeader.SubsysVer.ToProp(prop); break;
941     case kpidCodeSize: prop = _optHeader.CodeSize; break;
942     case kpidInitDataSize: prop = _optHeader.InitDataSize; break;
943     case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break;
944     case kpidImageSize: prop = _optHeader.ImageSize; break;
945     case kpidHeadersSize: prop = _optHeader.HeadersSize; break;
946     case kpidChecksum: prop = _optHeader.CheckSum; break;
947 
948     case kpidExtension:
949       if (_header.IsDll())
950         prop = "dll";
951       else if (_optHeader.IsSybSystem_EFI())
952         prop = "efi";
953       break;
954 
955     case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break;
956     case kpidSubSystem: TYPE_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break;
957 
958     case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break;
959     case kpidStackReserve: prop = _optHeader.StackReserve; break;
960     case kpidStackCommit: prop = _optHeader.StackCommit; break;
961     case kpidHeapReserve: prop = _optHeader.HeapReserve; break;
962     case kpidHeapCommit: prop = _optHeader.HeapCommit; break;
963 
964     case kpidImageBase: prop = _optHeader.ImageBase; break;
965     // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break;
966     // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break;
967     // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break;
968   }
969   }
970   prop.Detach(value);
971   return S_OK;
972   COM_TRY_END
973 }
974 
ReadString(UInt32 offset,UString & dest) const975 HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const
976 {
977   if ((offset & 1) != 0 || offset >= _buf.Size())
978     return S_FALSE;
979   size_t rem = _buf.Size() - offset;
980   if (rem < 2)
981     return S_FALSE;
982   unsigned len = Get16(_buf + offset);
983   if ((rem - 2) / 2 < len)
984     return S_FALSE;
985   dest.Empty();
986   wchar_t *destBuf = dest.GetBuf(len);
987   offset += 2;
988   const Byte *src = _buf + offset;
989   unsigned i;
990   for (i = 0; i < len; i++)
991   {
992     wchar_t c = (wchar_t)Get16(src + i * 2);
993     if (c == 0)
994       break;
995     destBuf[i] = c;
996   }
997   destBuf[i] = 0;
998   dest.ReleaseBuf_SetLen(i);
999   return S_OK;
1000 }
1001 
AddResNameToString(UString & s,UInt32 id) const1002 void CHandler::AddResNameToString(UString &s, UInt32 id) const
1003 {
1004   if ((id & kFlag) != 0)
1005   {
1006     UString name;
1007     if (ReadString(id & kMask, name) == S_OK)
1008     {
1009       const wchar_t *str = L"[]";
1010       if (name.Len() > 1 && name[0] == '"' && name.Back() == '"')
1011       {
1012         if (name.Len() != 2)
1013         {
1014           name.DeleteBack();
1015           str = name.Ptr(1);
1016         }
1017       }
1018       else if (!name.IsEmpty())
1019         str = name;
1020       s += str;
1021       return;
1022     }
1023   }
1024   s.Add_UInt32(id);
1025 }
1026 
AddLangPrefix(UString & s,UInt32 lang) const1027 void CHandler::AddLangPrefix(UString &s, UInt32 lang) const
1028 {
1029   if (!_oneLang)
1030   {
1031     AddResNameToString(s, lang);
1032     s.Add_PathSepar();
1033   }
1034 }
1035 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)1036 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
1037 {
1038   COM_TRY_BEGIN
1039   NCOM::CPropVariant prop;
1040   const CMixItem &mixItem = _mixItems[index];
1041   if (mixItem.StringIndex >= 0)
1042   {
1043     const CStringItem &item = _strings[mixItem.StringIndex];
1044     switch (propID)
1045     {
1046       case kpidPath:
1047       {
1048         UString s = _resourcesPrefix;
1049         AddLangPrefix(s, item.Lang);
1050         s += "string.txt";
1051         prop = s;
1052         break;
1053       }
1054       case kpidSize:
1055       case kpidPackSize:
1056         prop = (UInt64)item.FinalSize(); break;
1057     }
1058   }
1059   else if (mixItem.VersionIndex >= 0)
1060   {
1061     const CByteBuffer_WithLang &item = _versionFiles[mixItem.VersionIndex];
1062     switch (propID)
1063     {
1064       case kpidPath:
1065       {
1066         UString s = _resourcesPrefix;
1067         AddLangPrefix(s, item.Lang);
1068         s += "version.txt";
1069         prop = s;
1070         break;
1071       }
1072       case kpidSize:
1073       case kpidPackSize:
1074         prop = (UInt64)item.Size(); break;
1075     }
1076   }
1077   else if (mixItem.ResourceIndex >= 0)
1078   {
1079     const CResItem &item = _items[mixItem.ResourceIndex];
1080     switch (propID)
1081     {
1082       case kpidPath:
1083       {
1084         UString s = _resourcesPrefix;
1085         AddLangPrefix(s, item.Lang);
1086         {
1087           const char *p = NULL;
1088           if (item.Type < ARRAY_SIZE(g_ResTypes))
1089             p = g_ResTypes[item.Type];
1090           if (p)
1091             s += p;
1092           else
1093             AddResNameToString(s, item.Type);
1094         }
1095         s.Add_PathSepar();
1096         AddResNameToString(s, item.ID);
1097         if (item.HeaderSize != 0)
1098         {
1099           if (item.IsBmp())
1100             s += ".bmp";
1101           else if (item.IsIcon())
1102             s += ".ico";
1103         }
1104         prop = s;
1105         break;
1106       }
1107       case kpidSize: prop = (UInt64)item.GetSize(); break;
1108       case kpidPackSize: prop = (UInt64)item.Size; break;
1109     }
1110   }
1111   else
1112   {
1113     const CSection &item = _sections[mixItem.SectionIndex];
1114     switch (propID)
1115     {
1116       case kpidPath: prop = MultiByteToUnicodeString(item.Name); break;
1117       case kpidSize: prop = (UInt64)item.PSize; break;
1118       case kpidPackSize: prop = (UInt64)item.PSize; break;
1119       case kpidVirtualSize: prop = (UInt64)item.VSize; break;
1120       case kpidOffset: prop = item.Pa; break;
1121       case kpidVa: if (item.IsRealSect) prop = item.Va; break;
1122       case kpidMTime:
1123       case kpidCTime:
1124         TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break;
1125       case kpidCharacts:
1126        if (item.IsRealSect)
1127        {
1128          UInt32 flags = item.Flags;
1129          const UInt32 MY__IMAGE_SCN_ALIGN_MASK = 0x00F00000;
1130          AString s = FlagsToString(g_SectFlags, ARRAY_SIZE(g_SectFlags), item.Flags & ~MY__IMAGE_SCN_ALIGN_MASK);
1131          const UInt32 align = ((flags >> 20) & 0xF);
1132          if (align != 0)
1133          {
1134            char sz[32];
1135            ConvertUInt32ToString(1 << (align - 1), sz);
1136            s.Add_Space();
1137            s += "align_";
1138            s += sz;
1139          }
1140          prop = s;
1141        }
1142        break;
1143       case kpidZerosTailIsAllowed: if (!item.IsRealSect) prop = true; break;
1144     }
1145   }
1146   prop.Detach(value);
1147   return S_OK;
1148   COM_TRY_END
1149 }
1150 
LoadDebugSections(IInStream * stream,bool & thereIsSection)1151 HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection)
1152 {
1153   thereIsSection = false;
1154   const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug];
1155   if (debugLink.Size == 0)
1156     return S_OK;
1157   const unsigned kEntrySize = 28;
1158   UInt32 numItems = debugLink.Size / kEntrySize;
1159   if (numItems > 16)
1160     return S_FALSE;
1161 
1162   // MAC's EFI file: numItems can be incorrect. Only first CDebugEntry entry is correct.
1163   // debugLink.Size = kEntrySize + some_data, pointed by entry[0].
1164   if (numItems * kEntrySize != debugLink.Size)
1165   {
1166     // return S_FALSE;
1167     if (numItems > 1)
1168       numItems = 1;
1169   }
1170 
1171   UInt64 pa = 0;
1172   unsigned i;
1173   for (i = 0; i < _sections.Size(); i++)
1174   {
1175     const CSection &sect = _sections[i];
1176     if (sect.Va <= debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize)
1177     {
1178       pa = sect.Pa + (debugLink.Va - sect.Va);
1179       break;
1180     }
1181   }
1182   if (i == _sections.Size())
1183   {
1184     // Exe for ARM requires S_OK
1185     // return S_FALSE;
1186     return S_OK;
1187   }
1188 
1189   CByteBuffer buffer(debugLink.Size);
1190   Byte *buf = buffer;
1191 
1192   RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL));
1193   RINOK(ReadStream_FALSE(stream, buf, debugLink.Size));
1194 
1195   for (i = 0; i < numItems; i++)
1196   {
1197     CDebugEntry de;
1198     de.Parse(buf);
1199 
1200     if (de.Size == 0)
1201       continue;
1202 
1203     UInt32 totalSize = de.Pa + de.Size;
1204     if (totalSize > _totalSize)
1205     {
1206       _totalSize = totalSize;
1207       thereIsSection = true;
1208 
1209       CSection &sect = _sections.AddNew();
1210       sect.Name = ".debug";
1211       sect.Name.Add_UInt32(i);
1212       sect.IsDebug = true;
1213       sect.Time = de.Time;
1214       sect.Va = de.Va;
1215       sect.Pa = de.Pa;
1216       sect.PSize = sect.VSize = de.Size;
1217     }
1218     buf += kEntrySize;
1219   }
1220 
1221   return S_OK;
1222 }
1223 
ReadTable(UInt32 offset,CRecordVector<CTableItem> & items)1224 HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items)
1225 {
1226   if ((offset & 3) != 0 || offset >= _buf.Size())
1227     return S_FALSE;
1228   size_t rem = _buf.Size() - offset;
1229   if (rem < 16)
1230     return S_FALSE;
1231   unsigned numNameItems = Get16(_buf + offset + 12);
1232   unsigned numIdItems = Get16(_buf + offset + 14);
1233   unsigned numItems = numNameItems + numIdItems;
1234   if ((rem - 16) / 8 < numItems)
1235     return S_FALSE;
1236   if (!_usedRes.SetRange(offset, 16 + numItems * 8))
1237     return S_FALSE;
1238   offset += 16;
1239   items.ClearAndReserve(numItems);
1240   for (unsigned i = 0; i < numItems; i++, offset += 8)
1241   {
1242     const Byte *buf = _buf + offset;
1243     CTableItem item;
1244     item.ID = Get32(buf + 0);
1245     if ((bool)((item.ID & kFlag) != 0) != (bool)(i < numNameItems))
1246       return S_FALSE;
1247     item.Offset = Get32(buf + 4);
1248     items.AddInReserved(item);
1249   }
1250   return S_OK;
1251 }
1252 
1253 static const UInt32 kFileSizeMax = (UInt32)1 << 31;
1254 static const unsigned kNumResItemsMax = (unsigned)1 << 23;
1255 static const unsigned kNumStringLangsMax = 256;
1256 
1257 // BITMAPINFOHEADER
1258 struct CBitmapInfoHeader
1259 {
1260   // UInt32 HeaderSize;
1261   UInt32 XSize;
1262   Int32 YSize;
1263   UInt16 Planes;
1264   UInt16 BitCount;
1265   UInt32 Compression;
1266   UInt32 SizeImage;
1267 
1268   bool Parse(const Byte *p, size_t size);
1269 };
1270 
1271 static const UInt32 kBitmapInfoHeader_Size = 0x28;
1272 
Parse(const Byte * p,size_t size)1273 bool CBitmapInfoHeader::Parse(const Byte *p, size_t size)
1274 {
1275   if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size)
1276     return false;
1277   G32( 4, XSize);
1278   G32( 8, YSize);
1279   G16(12, Planes);
1280   G16(14, BitCount);
1281   G32(16, Compression);
1282   G32(20, SizeImage);
1283   return true;
1284 }
1285 
GetImageSize(UInt32 xSize,UInt32 ySize,UInt32 bitCount)1286 static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount)
1287 {
1288   return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize;
1289 }
1290 
SetBitmapHeader(Byte * dest,const Byte * src,UInt32 size)1291 static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size)
1292 {
1293   CBitmapInfoHeader h;
1294   if (!h.Parse(src, size))
1295     return 0;
1296   if (h.YSize < 0)
1297     h.YSize = -h.YSize;
1298   if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || h.BitCount > 32)
1299     return 0;
1300   if (h.SizeImage == 0)
1301   {
1302     if (h.Compression != 0) // BI_RGB
1303       return 0;
1304     h.SizeImage = GetImageSize(h.XSize, h.YSize, h.BitCount);
1305   }
1306   UInt32 totalSize = kBmpHeaderSize + size;
1307   UInt32 offBits = totalSize - h.SizeImage;
1308   // BITMAPFILEHEADER
1309   SetUi16(dest, 0x4D42);
1310   SetUi32(dest + 2, totalSize);
1311   SetUi32(dest + 6, 0);
1312   SetUi32(dest + 10, offBits);
1313   return kBmpHeaderSize;
1314 }
1315 
SetIconHeader(Byte * dest,const Byte * src,UInt32 size)1316 static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size)
1317 {
1318   CBitmapInfoHeader h;
1319   if (!h.Parse(src, size))
1320     return 0;
1321   if (h.YSize < 0)
1322     h.YSize = -h.YSize;
1323   if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 ||
1324       h.Compression != 0) // BI_RGB
1325     return 0;
1326 
1327   UInt32 numBitCount = h.BitCount;
1328   if (numBitCount != 1 &&
1329       numBitCount != 4 &&
1330       numBitCount != 8 &&
1331       numBitCount != 24 &&
1332       numBitCount != 32)
1333     return 0;
1334 
1335   if ((h.YSize & 1) != 0)
1336     return 0;
1337   h.YSize /= 2;
1338   if (h.XSize > 0x100 || h.YSize > 0x100)
1339     return 0;
1340 
1341   UInt32 imageSize;
1342   // imageSize is not correct if AND mask array contains zeros
1343   // in this case it is equal image1Size
1344 
1345   // UInt32 imageSize = h.SizeImage;
1346   // if (imageSize == 0)
1347   // {
1348     UInt32 image1Size = GetImageSize(h.XSize, h.YSize, h.BitCount);
1349     UInt32 image2Size = GetImageSize(h.XSize, h.YSize, 1);
1350     imageSize = image1Size + image2Size;
1351   // }
1352   UInt32 numColors = 0;
1353   if (numBitCount < 16)
1354     numColors = 1 << numBitCount;
1355 
1356   SetUi16(dest, 0); // Reserved
1357   SetUi16(dest + 2, 1); // RES_ICON
1358   SetUi16(dest + 4, 1); // ResCount
1359 
1360   dest[6] = (Byte)h.XSize; // Width
1361   dest[7] = (Byte)h.YSize; // Height
1362   dest[8] = (Byte)numColors; // ColorCount
1363   dest[9] = 0; // Reserved
1364 
1365   SetUi32(dest + 10, 0); // Reserved1 / Reserved2
1366 
1367   UInt32 numQuadsBytes = numColors * 4;
1368   UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize;
1369   SetUi32(dest + 14, BytesInRes);
1370   SetUi32(dest + 18, kIconHeaderSize);
1371 
1372   /*
1373   Description = DWORDToString(xSize) +
1374       kDelimiterChar + DWORDToString(ySize) +
1375       kDelimiterChar + DWORDToString(numBitCount);
1376   */
1377   return kIconHeaderSize;
1378 }
1379 
ParseStringRes(UInt32 id,UInt32 lang,const Byte * src,UInt32 size)1380 bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size)
1381 {
1382   if ((size & 1) != 0)
1383     return false;
1384 
1385   unsigned i;
1386   for (i = 0; i < _strings.Size(); i++)
1387     if (_strings[i].Lang == lang)
1388       break;
1389   if (i == _strings.Size())
1390   {
1391     if (_strings.Size() >= kNumStringLangsMax)
1392       return false;
1393     CStringItem &item = _strings.AddNew();
1394     item.Lang = lang;
1395   }
1396 
1397   CStringItem &item = _strings[i];
1398   id = (id - 1) << 4;
1399   UInt32 pos = 0;
1400   for (i = 0; i < 16; i++)
1401   {
1402     if (size - pos < 2)
1403       return false;
1404     UInt32 len = Get16(src + pos);
1405     pos += 2;
1406     if (len != 0)
1407     {
1408       if (size - pos < len * 2)
1409         return false;
1410       char temp[32];
1411       ConvertUInt32ToString(id + i, temp);
1412       size_t tempLen = strlen(temp);
1413       size_t j;
1414       for (j = 0; j < tempLen; j++)
1415         item.AddChar(temp[j]);
1416       item.AddChar('\t');
1417       for (j = 0; j < len; j++, pos += 2)
1418         item.AddWChar_Smart(Get16(src + pos));
1419       item.NewLine();
1420     }
1421   }
1422   if (size == pos)
1423     return true;
1424 
1425   // Some rare case files have additional ZERO.
1426   if (size == pos + 2 && Get16(src + pos) == 0)
1427     return true;
1428 
1429   return false;
1430 }
1431 
1432 
1433 // ---------- VERSION ----------
1434 
1435 static const UInt32 kMy_VS_FFI_SIGNATURE = 0xFEEF04BD;
1436 
1437 struct CMy_VS_FIXEDFILEINFO
1438 {
1439   // UInt32 Signature;
1440   // UInt32 StrucVersion;
1441   UInt32 VersionMS;
1442   UInt32 VersionLS;
1443   UInt32 ProductVersionMS;
1444   UInt32 ProductVersionLS;
1445   UInt32 FlagsMask;
1446   UInt32 Flags;
1447   UInt32 OS;
1448   UInt32 Type;
1449   UInt32 Subtype;
1450   UInt32 DateMS;
1451   UInt32 DateLS;
1452 
1453   bool Parse(const Byte *p);
1454   void PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys);
1455 };
1456 
Parse(const Byte * p)1457 bool CMy_VS_FIXEDFILEINFO::Parse(const Byte *p)
1458 {
1459   if (Get32(p) != kMy_VS_FFI_SIGNATURE) // signature;
1460     return false;
1461   // G32(0x04, StrucVersion);
1462   G32(0x08, VersionMS);
1463   G32(0x0C, VersionLS);
1464   G32(0x10, ProductVersionMS);
1465   G32(0x14, ProductVersionLS);
1466   G32(0x18, FlagsMask);
1467   G32(0x1C, Flags);
1468   G32(0x20, OS);
1469   G32(0x24, Type);
1470   G32(0x28, Subtype);
1471   G32(0x2C, DateMS);
1472   G32(0x40, DateLS);
1473   return true;
1474 }
1475 
PrintUInt32(CTextFile & f,UInt32 v)1476 static void PrintUInt32(CTextFile &f, UInt32 v)
1477 {
1478   char s[16];
1479   ConvertUInt32ToString(v, s);
1480   f.AddString(s);
1481 }
1482 
PrintUInt32(UString & dest,UInt32 v)1483 static inline void PrintUInt32(UString &dest, UInt32 v)
1484 {
1485   dest.Add_UInt32(v);
1486 }
1487 
PrintHex(CTextFile & f,UInt32 val)1488 static void PrintHex(CTextFile &f, UInt32 val)
1489 {
1490   char temp[16];
1491   temp[0] = '0';
1492   temp[1] = 'x';
1493   ConvertUInt32ToHex(val, temp + 2);
1494   f.AddString(temp);
1495 }
1496 
PrintVersion(CTextFile & f,UInt32 ms,UInt32 ls)1497 static void PrintVersion(CTextFile &f, UInt32 ms, UInt32 ls)
1498 {
1499   PrintUInt32(f, HIWORD(ms));  f.AddChar(',');
1500   PrintUInt32(f, LOWORD(ms));  f.AddChar(',');
1501   PrintUInt32(f, HIWORD(ls));  f.AddChar(',');
1502   PrintUInt32(f, LOWORD(ls));
1503 }
1504 
PrintVersion(UString & s,UInt32 ms,UInt32 ls)1505 static void PrintVersion(UString &s, UInt32 ms, UInt32 ls)
1506 {
1507   PrintUInt32(s, HIWORD(ms));  s += '.';
1508   PrintUInt32(s, LOWORD(ms));  s += '.';
1509   PrintUInt32(s, HIWORD(ls));  s += '.';
1510   PrintUInt32(s, LOWORD(ls));
1511 }
1512 
1513 static const char * const k_VS_FileFlags[] =
1514 {
1515     "DEBUG"
1516   , "PRERELEASE"
1517   , "PATCHED"
1518   , "PRIVATEBUILD"
1519   , "INFOINFERRED"
1520   , "SPECIALBUILD"
1521 };
1522 
1523 static const CUInt32PCharPair k_VS_FileOS[] =
1524 {
1525   {  0x10001, "VOS_DOS_WINDOWS16" },
1526   {  0x10004, "VOS_DOS_WINDOWS32" },
1527   {  0x20002, "VOS_OS216_PM16" },
1528   {  0x30003, "VOS_OS232_PM32" },
1529   {  0x40004, "VOS_NT_WINDOWS32" }
1530 };
1531 
1532 static const char * const k_VS_FileOS_High[] =
1533 {
1534     "VOS_UNKNOWN"
1535   , "VOS_DOS"
1536   , "VOS_OS216"
1537   , "VOS_OS232"
1538   , "VOS_NT"
1539   , "VOS_WINCE"
1540 };
1541 
1542 static const UInt32 kMY_VFT_DRV  = 3;
1543 static const UInt32 kMY_VFT_FONT = 4;
1544 
1545 static const char * const k_VS_FileOS_Low[] =
1546 {
1547     "VOS__BASE"
1548   , "VOS__WINDOWS16"
1549   , "VOS__PM16"
1550   , "VOS__PM32"
1551   , "VOS__WINDOWS32"
1552 };
1553 
1554 static const char * const k_VS_FileType[] =
1555 {
1556     "VFT_UNKNOWN"
1557   , "VFT_APP"
1558   , "VFT_DLL"
1559   , "VFT_DRV"
1560   , "VFT_FONT"
1561   , "VFT_VXD"
1562   , "0x6"
1563   , "VFT_STATIC_LIB"
1564 };
1565 
1566 // Subtype for VFT_DRV Type
1567 static const char * const k_VS_FileSubType_DRV[] =
1568 {
1569     "0"
1570   , "PRINTER"
1571   , "KEYBOARD"
1572   , "LANGUAGE"
1573   , "DISPLAY"
1574   , "MOUSE"
1575   , "NETWORK"
1576   , "SYSTEM"
1577   , "INSTALLABLE"
1578   , "SOUND"
1579   , "COMM"
1580   , "INPUTMETHOD"
1581   , "VERSIONED_PRINTER"
1582 };
1583 
1584 // Subtype for VFT_FONT Type
1585 static const char * const k_VS_FileSubType_FONT[] =
1586 {
1587     "0"
1588   , "VFT2_FONT_RASTER"
1589   , "VFT2_FONT_VECTOR"
1590   , "VFT2_FONT_TRUETYPE"
1591 };
1592 
FindKey(CObjectVector<CStringKeyValue> & v,const char * key)1593 static int FindKey(CObjectVector<CStringKeyValue> &v, const char *key)
1594 {
1595   FOR_VECTOR (i, v)
1596     if (v[i].Key.IsEqualTo(key))
1597       return i;
1598   return -1;
1599 }
1600 
AddToUniqueUStringVector(CObjectVector<CStringKeyValue> & v,const UString & key,const UString & value)1601 static void AddToUniqueUStringVector(CObjectVector<CStringKeyValue> &v, const UString &key, const UString &value)
1602 {
1603   bool needInsert = false;
1604   unsigned i;
1605   for (i = 0; i < v.Size(); i++)
1606   {
1607     if (v[i].Key == key)
1608     {
1609       if (v[i].Value == value)
1610         return;
1611       needInsert = true;
1612     }
1613     else if (needInsert)
1614       break;
1615   }
1616   CStringKeyValue &pair = v.InsertNew(i);
1617   pair.Key = key;
1618   pair.Value = value;
1619 }
1620 
PrintToTextFile(CTextFile & f,CObjectVector<CStringKeyValue> & keys)1621 void CMy_VS_FIXEDFILEINFO::PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys)
1622 {
1623   f.AddString("FILEVERSION    ");
1624   PrintVersion(f, VersionMS, VersionLS);
1625   f.NewLine();
1626 
1627   f.AddString("PRODUCTVERSION ");
1628   PrintVersion(f, ProductVersionMS, ProductVersionLS);
1629   f.NewLine();
1630 
1631   {
1632     UString s;
1633     PrintVersion(s, VersionMS, VersionLS);
1634     AddToUniqueUStringVector(keys, L"FileVersion", s);
1635   }
1636   {
1637     UString s;
1638     PrintVersion(s, ProductVersionMS, ProductVersionLS);
1639     AddToUniqueUStringVector(keys, L"ProductVersion", s);
1640   }
1641 
1642   f.AddString("FILEFLAGSMASK  ");
1643   PrintHex(f, FlagsMask);
1644   f.NewLine();
1645 
1646   f.AddString("FILEFLAGS      ");
1647   {
1648     bool wasPrinted = false;
1649     for (unsigned i = 0; i < ARRAY_SIZE(k_VS_FileFlags); i++)
1650     {
1651       if ((Flags & ((UInt32)1 << i)) != 0)
1652       {
1653         if (wasPrinted)
1654           f.AddString(" | ");
1655         f.AddString("VS_FF_");
1656         f.AddString(k_VS_FileFlags[i]);
1657         wasPrinted = true;
1658       }
1659     }
1660     UInt32 v = Flags & ~(((UInt32)1 << ARRAY_SIZE(k_VS_FileFlags)) - 1);
1661     if (v != 0 || !wasPrinted)
1662     {
1663       if (wasPrinted)
1664         f.AddString(" | ");
1665       PrintHex(f, v);
1666     }
1667   }
1668   f.NewLine();
1669 
1670   // OS = 0x111230;
1671   f.AddString("FILEOS         ");
1672   unsigned i;
1673   for (i = 0; i < ARRAY_SIZE(k_VS_FileOS); i++)
1674   {
1675     const CUInt32PCharPair &pair = k_VS_FileOS[i];
1676     if (OS == pair.Value)
1677     {
1678       // continue;
1679       // f.AddString("VOS_");
1680       f.AddString(pair.Name);
1681       break;
1682     }
1683   }
1684   if (i == ARRAY_SIZE(k_VS_FileOS))
1685   {
1686     UInt32 high = OS >> 16;
1687     if (high < ARRAY_SIZE(k_VS_FileOS_High))
1688       f.AddString(k_VS_FileOS_High[high]);
1689     else
1690       PrintHex(f, high << 16);
1691     UInt32 low = OS & 0xFFFF;
1692     if (low != 0)
1693     {
1694       f.AddString(" | ");
1695       if (low < ARRAY_SIZE(k_VS_FileOS_Low))
1696         f.AddString(k_VS_FileOS_Low[low]);
1697       else
1698         PrintHex(f, low);
1699     }
1700   }
1701   f.NewLine();
1702 
1703   f.AddString("FILETYPE       ");
1704   if (Type < ARRAY_SIZE(k_VS_FileType))
1705     f.AddString(k_VS_FileType[Type]);
1706   else
1707     PrintHex(f, Type);
1708   f.NewLine();
1709 
1710   f.AddString("FILESUBTYPE    ");
1711   bool needPrintSubType = true;
1712   if (Type == kMY_VFT_DRV)
1713   {
1714     if (Subtype != 0 && Subtype < ARRAY_SIZE(k_VS_FileSubType_DRV))
1715     {
1716       f.AddString("VFT2_DRV_");
1717       f.AddString(k_VS_FileSubType_DRV[Subtype]);
1718       needPrintSubType = false;
1719     }
1720   }
1721   else if (Type == kMY_VFT_FONT)
1722   {
1723     if (Subtype != 0 && Subtype < ARRAY_SIZE(k_VS_FileSubType_FONT))
1724     {
1725       f.AddString(k_VS_FileSubType_FONT[Subtype]);
1726       needPrintSubType = false;
1727     }
1728   }
1729   if (needPrintSubType)
1730     PrintHex(f, Subtype);
1731   f.NewLine();
1732 }
1733 
CopyToUString(const Byte * p,UString & s)1734 static void CopyToUString(const Byte *p, UString &s)
1735 {
1736   for (;;)
1737   {
1738     wchar_t c = (wchar_t)Get16(p);
1739     p += 2;
1740     if (c == 0)
1741       return;
1742     s += c;
1743   }
1744 }
1745 
CompareWStrStrings(const Byte * p,const char * s)1746 static bool CompareWStrStrings(const Byte *p, const char *s)
1747 {
1748   unsigned pos = 0;
1749   for (;;)
1750   {
1751     Byte c = *s++;
1752     if (Get16(p + pos) != c)
1753       return false;
1754     pos += 2;
1755     if (c == 0)
1756       return true;
1757   }
1758 }
1759 
1760 struct CVersionBlock
1761 {
1762   UInt32 TotalLen;
1763   UInt32 ValueLen;
1764   bool IsTextValue;
1765   unsigned StrSize;
1766 
1767   bool Parse(const Byte *p, UInt32 size);
1768 };
1769 
Get_Utf16Str_Len_InBytes(const Byte * p,size_t size)1770 static int Get_Utf16Str_Len_InBytes(const Byte *p, size_t size)
1771 {
1772   unsigned pos = 0;
1773   for (;;)
1774   {
1775     if (pos + 1 >= size)
1776       return -1;
1777     if (Get16(p + pos) == 0)
1778       return pos;
1779     pos += 2;
1780   }
1781 }
1782 
1783 static const unsigned k_ResoureBlockHeader_Size = 6;
1784 
Parse(const Byte * p,UInt32 size)1785 bool CVersionBlock::Parse(const Byte *p, UInt32 size)
1786 {
1787   if (size < k_ResoureBlockHeader_Size)
1788     return false;
1789   TotalLen = Get16(p);
1790   ValueLen = Get16(p + 2);
1791   if (TotalLen < k_ResoureBlockHeader_Size || TotalLen > size)
1792     return false;
1793   switch (Get16(p + 4))
1794   {
1795     case 0: IsTextValue = false; break;
1796     case 1: IsTextValue = true; break;
1797     default: return false;
1798   }
1799   StrSize = 0;
1800   int t = Get_Utf16Str_Len_InBytes(p + k_ResoureBlockHeader_Size, TotalLen - k_ResoureBlockHeader_Size);
1801   if (t < 0)
1802     return false;
1803   StrSize = t;
1804   return true;
1805 }
1806 
AddParamString(CTextFile & f,const Byte * p,size_t sLen)1807 static void AddParamString(CTextFile &f, const Byte *p, size_t sLen)
1808 {
1809   f.AddChar(' ');
1810   f.AddChar('\"');
1811   f.AddBytes(p, sLen);
1812   f.AddChar('\"');
1813 }
1814 
ParseVersion(const Byte * p,UInt32 size,CTextFile & f,CObjectVector<CStringKeyValue> & keys)1815 static bool ParseVersion(const Byte *p, UInt32 size, CTextFile &f, CObjectVector<CStringKeyValue> &keys)
1816 {
1817   UInt32 pos;
1818   {
1819     const unsigned k_sizeof_VS_FIXEDFILEINFO = 13 * 4;
1820 
1821     CVersionBlock vb;
1822     if (!vb.Parse(p, size))
1823       return false;
1824     if (vb.ValueLen != k_sizeof_VS_FIXEDFILEINFO) // maybe 0 is allowed here?
1825       return false;
1826     if (vb.IsTextValue)
1827       return false;
1828     pos = k_ResoureBlockHeader_Size;
1829     if (!CompareWStrStrings(p + pos, "VS_VERSION_INFO"))
1830       return false;
1831     pos += vb.StrSize + 2;
1832     pos += (4 - pos) & 3;
1833     if (pos + vb.ValueLen > vb.TotalLen)
1834       return false;
1835     /* sometimes resource contains zeros in remainder.
1836        So we don't check that size != vb.TotalLen
1837     // if (size != vb.TotalLen) return false;
1838     */
1839     if (size > vb.TotalLen)
1840       size = vb.TotalLen;
1841     CMy_VS_FIXEDFILEINFO FixedFileInfo;
1842     if (!FixedFileInfo.Parse(p + pos))
1843       return false;
1844     FixedFileInfo.PrintToTextFile(f, keys);
1845     pos += vb.ValueLen;
1846   }
1847 
1848   f.OpenBlock(0);
1849 
1850   for (;;)
1851   {
1852     pos += (4 - pos) & 3;
1853     if (pos >= size)
1854       break;
1855 
1856     CVersionBlock vb;
1857     if (!vb.Parse(p + pos, size - pos))
1858       return false;
1859     if (vb.ValueLen != 0)
1860       return false;
1861     UInt32 endPos = pos + vb.TotalLen;
1862     pos += k_ResoureBlockHeader_Size;
1863 
1864     f.AddSpaces(2);
1865     f.AddString("BLOCK");
1866     AddParamString(f, p + pos, vb.StrSize);
1867 
1868     f.NewLine();
1869     f.OpenBlock(2);
1870 
1871     if (CompareWStrStrings(p + pos, "VarFileInfo"))
1872     {
1873       pos += vb.StrSize + 2;
1874       for (;;)
1875       {
1876         pos += (4 - pos) & 3;
1877         if (pos >= endPos)
1878           break;
1879         CVersionBlock vb2;
1880         if (!vb2.Parse(p + pos, endPos - pos))
1881           return false;
1882         UInt32 endPos2 = pos + vb2.TotalLen;
1883         if (vb2.IsTextValue)
1884           return false;
1885         pos += k_ResoureBlockHeader_Size;
1886         f.AddSpaces(4);
1887         f.AddString("VALUE");
1888         AddParamString(f, p + pos, vb2.StrSize);
1889         if (!CompareWStrStrings(p + pos, "Translation"))
1890           return false;
1891         pos += vb2.StrSize + 2;
1892         pos += (4 - pos) & 3;
1893         if (pos + vb2.ValueLen != endPos2)
1894           return false;
1895         if ((vb2.ValueLen & 3) != 0)
1896           return false;
1897         UInt32 num = (vb2.ValueLen >> 2);
1898         for (; num != 0; num--, pos += 4)
1899         {
1900           UInt32 dw = Get32(p + pos);
1901           UInt32 lang = LOWORD(dw);
1902           UInt32 codePage = HIWORD(dw);
1903 
1904           f.AddString(", ");
1905           PrintHex(f, lang);
1906           f.AddString(", ");
1907           PrintUInt32(f, codePage);
1908         }
1909         f.NewLine();
1910       }
1911     }
1912     else
1913     {
1914       if (!CompareWStrStrings(p + pos, "StringFileInfo"))
1915         return false;
1916       pos += vb.StrSize + 2;
1917 
1918       for (;;)
1919       {
1920         pos += (4 - pos) & 3;
1921         if (pos >= endPos)
1922           break;
1923         CVersionBlock vb2;
1924         if (!vb2.Parse(p + pos, endPos - pos))
1925           return false;
1926         UInt32 endPos2 = pos + vb2.TotalLen;
1927         if (vb2.ValueLen != 0)
1928           return false;
1929         pos += k_ResoureBlockHeader_Size;
1930 
1931         f.AddSpaces(4);
1932         f.AddString("BLOCK");
1933         AddParamString(f, p + pos, vb2.StrSize);
1934         pos += vb2.StrSize + 2;
1935 
1936         f.NewLine();
1937         f.OpenBlock(4);
1938 
1939         for (;;)
1940         {
1941           pos += (4 - pos) & 3;
1942           if (pos >= endPos2)
1943             break;
1944 
1945           CVersionBlock vb3;
1946           if (!vb3.Parse(p + pos, endPos2 - pos))
1947             return false;
1948           // ValueLen sometimes is a number of characters (not bytes)?
1949           // So we don't use it.
1950           UInt32 endPos3 = pos + vb3.TotalLen;
1951           pos += k_ResoureBlockHeader_Size;
1952 
1953           // we don't write string if it's not text
1954           if (vb3.IsTextValue)
1955           {
1956             f.AddSpaces(6);
1957             f.AddString("VALUE");
1958             AddParamString(f, p + pos, vb3.StrSize);
1959             UString key;
1960             UString value;
1961             CopyToUString(p + pos, key);
1962             pos += vb3.StrSize + 2;
1963 
1964             pos += (4 - pos) & 3;
1965             if (vb3.ValueLen > 0 && pos + 2 <= endPos3)
1966             {
1967               f.AddChar(',');
1968               f.AddSpaces((34 - (int)vb3.StrSize) / 2);
1969               int sLen = Get_Utf16Str_Len_InBytes(p + pos, endPos3 - pos);
1970               if (sLen < 0)
1971                 return false;
1972               AddParamString(f, p + pos, (unsigned)sLen);
1973               CopyToUString(p + pos, value);
1974               pos += sLen + 2;
1975             }
1976             AddToUniqueUStringVector(keys, key, value);
1977           }
1978           pos = endPos3;
1979           f.NewLine();
1980         }
1981         f.CloseBlock(4);
1982       }
1983     }
1984     f.CloseBlock(2);
1985   }
1986 
1987   f.CloseBlock(0);
1988   return true;
1989 }
1990 
1991 
OpenResources(unsigned sectionIndex,IInStream * stream,IArchiveOpenCallback * callback)1992 HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchiveOpenCallback *callback)
1993 {
1994   const CSection &sect = _sections[sectionIndex];
1995   size_t fileSize = sect.PSize;
1996   {
1997     size_t fileSizeMin = sect.PSize;
1998 
1999     if (sect.VSize < sect.PSize)
2000     {
2001       fileSize = fileSizeMin = sect.VSize;
2002       const int numBits = _optHeader.GetNumFileAlignBits();
2003       if (numBits > 0)
2004       {
2005         const UInt32 mask = ((UInt32)1 << numBits) - 1;
2006         const size_t end = (size_t)((sect.VSize + mask) & (UInt32)~mask);
2007         if (end > sect.VSize)
2008         {
2009           if (end <= sect.PSize)
2010             fileSize = end;
2011           else
2012             fileSize = sect.PSize;
2013         }
2014       }
2015     }
2016 
2017     if (fileSize > kFileSizeMax)
2018       return S_FALSE;
2019 
2020     {
2021       const UInt64 fileSize64 = fileSize;
2022       if (callback)
2023         RINOK(callback->SetTotal(NULL, &fileSize64));
2024     }
2025 
2026     RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));
2027 
2028     _buf.Alloc(fileSize);
2029 
2030     size_t pos;
2031 
2032     for (pos = 0; pos < fileSize;)
2033     {
2034       {
2035         const UInt64 offset64 = pos;
2036         if (callback)
2037           RINOK(callback->SetCompleted(NULL, &offset64))
2038       }
2039       size_t rem = MyMin(fileSize - pos, (size_t)(1 << 22));
2040       RINOK(ReadStream(stream, _buf + pos, &rem));
2041       if (rem == 0)
2042       {
2043         if (pos < fileSizeMin)
2044           return S_FALSE;
2045         break;
2046       }
2047       pos += rem;
2048     }
2049 
2050     if (pos < fileSize)
2051       memset(_buf + pos, 0, fileSize - pos);
2052   }
2053 
2054   _usedRes.Alloc(fileSize);
2055   CRecordVector<CTableItem> specItems;
2056   RINOK(ReadTable(0, specItems));
2057 
2058   _oneLang = true;
2059   bool stringsOk = true;
2060   size_t maxOffset = 0;
2061 
2062   FOR_VECTOR (i, specItems)
2063   {
2064     const CTableItem &item1 = specItems[i];
2065     if ((item1.Offset & kFlag) == 0)
2066       return S_FALSE;
2067 
2068     CRecordVector<CTableItem> specItems2;
2069     RINOK(ReadTable(item1.Offset & kMask, specItems2));
2070 
2071     FOR_VECTOR (j, specItems2)
2072     {
2073       const CTableItem &item2 = specItems2[j];
2074       if ((item2.Offset & kFlag) == 0)
2075         return S_FALSE;
2076 
2077       CRecordVector<CTableItem> specItems3;
2078       RINOK(ReadTable(item2.Offset & kMask, specItems3));
2079 
2080       CResItem item;
2081       item.Type = item1.ID;
2082       item.ID = item2.ID;
2083 
2084       FOR_VECTOR (k, specItems3)
2085       {
2086         if (_items.Size() >= kNumResItemsMax)
2087           return S_FALSE;
2088         const CTableItem &item3 = specItems3[k];
2089         if ((item3.Offset & kFlag) != 0)
2090           return S_FALSE;
2091         if (item3.Offset >= _buf.Size() || _buf.Size() - item3.Offset < 16)
2092           return S_FALSE;
2093         const Byte *buf = _buf + item3.Offset;
2094         item.Lang = item3.ID;
2095         item.Offset = Get32(buf + 0);
2096         item.Size = Get32(buf + 4);
2097         // UInt32 codePage = Get32(buf + 8);
2098         if (Get32(buf + 12) != 0)
2099           return S_FALSE;
2100         if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back()))
2101           _oneLang = false;
2102 
2103         item.HeaderSize = 0;
2104 
2105         size_t offset = item.Offset - sect.Va;
2106         if (offset > maxOffset)
2107           maxOffset = offset;
2108         if (offset + item.Size > maxOffset)
2109           maxOffset = offset + item.Size;
2110 
2111         if (CheckItem(sect, item, offset))
2112         {
2113           const Byte *data = _buf + offset;
2114           if (item.IsBmp())
2115             item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size);
2116           else if (item.IsIcon())
2117             item.HeaderSize = SetIconHeader(item.Header, data, item.Size);
2118           else if (item.IsString())
2119           {
2120             if (stringsOk)
2121               stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size);
2122           }
2123         }
2124 
2125         if (item.IsVersion())
2126         {
2127           if (offset > _buf.Size() || _buf.Size() - offset < item.Size)
2128             continue;
2129           CTextFile f;
2130           if (ParseVersion((const Byte *)_buf + offset, item.Size, f, _versionKeys))
2131           {
2132             CMixItem mixItem;
2133             mixItem.VersionIndex = _versionFiles.Size();
2134             mixItem.SectionIndex = sectionIndex; // check it !!!!
2135             CByteBuffer_WithLang &vf = _versionFiles.AddNew();
2136             vf.Lang = item.Lang;
2137             vf.CopyFrom(f.Buf, f.Buf.GetPos());
2138             _mixItems.Add(mixItem);
2139             continue;
2140           }
2141           // PrintError("ver.Parse error");
2142         }
2143 
2144         item.Enabled = true;
2145         _items.Add(item);
2146       }
2147     }
2148   }
2149 
2150   if (stringsOk && !_strings.IsEmpty())
2151   {
2152     unsigned i;
2153     for (i = 0; i < _items.Size(); i++)
2154     {
2155       CResItem &item = _items[i];
2156       if (item.IsString())
2157         item.Enabled = false;
2158     }
2159     for (i = 0; i < _strings.Size(); i++)
2160     {
2161       if (_strings[i].FinalSize() == 0)
2162         continue;
2163       CMixItem mixItem;
2164       mixItem.StringIndex = i;
2165       mixItem.SectionIndex = sectionIndex;
2166       _mixItems.Add(mixItem);
2167     }
2168   }
2169 
2170   _usedRes.Free();
2171 
2172   {
2173     // PSize can be much larger than VSize in some exe installers.
2174     // it contains archive data after PE resources.
2175     // So we need to use PSize here!
2176     if (maxOffset < sect.PSize)
2177     {
2178       size_t end = fileSize;
2179 
2180       // we skip Zeros to start of aligned block
2181       size_t i;
2182       for (i = maxOffset; i < end; i++)
2183         if (_buf[i] != 0)
2184           break;
2185       if (i == end)
2186         maxOffset = end;
2187 
2188       CSection sect2;
2189       sect2.Flags = 0;
2190       sect2.Pa = sect.Pa + (UInt32)maxOffset;
2191       sect2.Va = sect.Va + (UInt32)maxOffset;
2192 
2193       // 9.29: we use sect.PSize instead of sect.VSize to support some CAB-SFX
2194       // the code for .rsrc_2 is commented.
2195       sect2.PSize = sect.PSize - (UInt32)maxOffset;
2196 
2197       if (sect2.PSize != 0)
2198       {
2199         sect2.VSize = sect2.PSize;
2200         sect2.Name = ".rsrc_1";
2201         sect2.Time = 0;
2202         sect2.IsAdditionalSection = true;
2203         _sections.Add(sect2);
2204       }
2205     }
2206   }
2207 
2208   return S_OK;
2209 }
2210 
2211 
ParseCoff(const Byte * p)2212 bool CHeader::ParseCoff(const Byte *p)
2213 {
2214   ParseBase(p);
2215   if (PointerToSymbolTable < kCoffHeaderSize)
2216     return false;
2217   if (NumSymbols >= (1 << 24))
2218     return false;
2219   if (OptHeaderSize != 0 && OptHeaderSize < k_OptHeader32_Size_MIN)
2220     return false;
2221 
2222   // 18.04: we reduce false detections
2223   if (NumSections == 0 && OptHeaderSize == 0)
2224     return false;
2225 
2226   for (unsigned i = 0; i < ARRAY_SIZE(g_MachinePairs); i++)
2227     if (Machine == g_MachinePairs[i].Value)
2228       return true;
2229   if (Machine == 0)
2230     return true;
2231 
2232   return false;
2233 }
2234 
2235 
CheckPeOffset(UInt32 pe)2236 static inline bool CheckPeOffset(UInt32 pe)
2237 {
2238   // ((pe & 7) == 0) is for most PE files. But there is unusual EFI-PE file that uses unaligned pe value.
2239   return pe >= 0x40 && pe <= 0x1000 /* && (pe & 7) == 0 */ ;
2240 }
2241 
2242 static const unsigned kStartSize = 0x40;
2243 
IsArc_Pe(const Byte * p,size_t size)2244 API_FUNC_static_IsArc IsArc_Pe(const Byte *p, size_t size)
2245 {
2246   if (size < 2)
2247     return k_IsArc_Res_NEED_MORE;
2248   if (p[0] != 'M' || p[1] != 'Z')
2249     return k_IsArc_Res_NO;
2250   if (size < kStartSize)
2251     return k_IsArc_Res_NEED_MORE;
2252   UInt32 pe = Get32(p + 0x3C);
2253   if (!CheckPeOffset(pe))
2254     return k_IsArc_Res_NO;
2255   if (pe + kPeHeaderSize > size)
2256     return k_IsArc_Res_NEED_MORE;
2257   CHeader header;
2258   if (!header.ParsePe(p + pe))
2259     return k_IsArc_Res_NO;
2260   return k_IsArc_Res_YES;
2261 }
2262 }
2263 
Open2(IInStream * stream,IArchiveOpenCallback * callback)2264 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
2265 {
2266   UInt32 coffOffset = 0;
2267   if (_coffMode)
2268   {
2269     Byte h[kCoffHeaderSize];
2270     RINOK(ReadStream_FALSE(stream, h, kCoffHeaderSize));
2271     if (!_header.ParseCoff(h))
2272       return S_FALSE;
2273   }
2274   else
2275   {
2276     UInt32 _peOffset;
2277     {
2278       Byte h[kStartSize];
2279       RINOK(ReadStream_FALSE(stream, h, kStartSize));
2280       if (h[0] != 'M' || h[1] != 'Z')
2281         return S_FALSE;
2282         /* most of PE files contain 0x0090 at offset 2.
2283         But some rare PE files contain another values. So we don't use that check.
2284       if (Get16(h + 2) != 0x90) return false; */
2285       _peOffset = Get32(h + 0x3C);
2286       if (!CheckPeOffset(_peOffset))
2287         return S_FALSE;
2288       coffOffset = _peOffset + 4;
2289     }
2290     {
2291       Byte h[kPeHeaderSize];
2292       RINOK(stream->Seek(_peOffset, STREAM_SEEK_SET, NULL));
2293       RINOK(ReadStream_FALSE(stream, h, kPeHeaderSize));
2294       if (!_header.ParsePe(h))
2295         return S_FALSE;
2296     }
2297   }
2298 
2299   const UInt32 optStart = coffOffset + kCoffHeaderSize;
2300   const UInt32 bufSize = _header.OptHeaderSize + (UInt32)_header.NumSections * kSectionSize;
2301   _totalSize = optStart + bufSize;
2302   CByteBuffer buffer(bufSize);
2303 
2304   RINOK(ReadStream_FALSE(stream, buffer, bufSize));
2305 
2306   if (_header.OptHeaderSize != 0)
2307   if (!_optHeader.Parse(buffer, _header.OptHeaderSize))
2308     return S_FALSE;
2309 
2310   UInt32 pos = _header.OptHeaderSize;
2311   unsigned i;
2312   for (i = 0; i < _header.NumSections; i++, pos += kSectionSize)
2313   {
2314     CSection &sect = _sections.AddNew();
2315     sect.Parse(buffer + pos);
2316     sect.IsRealSect = true;
2317 
2318     /* PE pre-file in .hxs file has errors:
2319        PSize of resource is larger tnan real size.
2320        So it overlaps next ".its" section.
2321        We correct it. */
2322 
2323     if (i > 0)
2324     {
2325       CSection &prev = _sections[i - 1];
2326       if (prev.Pa < sect.Pa &&
2327           prev.Pa + prev.PSize > sect.Pa &&
2328           sect.PSize > 0)
2329       {
2330         // printf("\n !!!! Section correction: %s\n ", prev.Name);
2331         // fflush(stdout);
2332         prev.PSize = sect.Pa - prev.Pa;
2333       }
2334     }
2335     /* last ".its" section in hxs file has incorrect sect.PSize.
2336        So we reduce it to real sect.VSize */
2337     if (sect.VSize == 24 && sect.PSize == 512 && i == (unsigned)_header.NumSections - 1)
2338       sect.PSize = sect.VSize;
2339   }
2340 
2341   for (i = 0; i < _sections.Size(); i++)
2342     _sections[i].UpdateTotalSize(_totalSize);
2343 
2344   bool thereISDebug = false;
2345   if (IsOpt())
2346   {
2347   RINOK(LoadDebugSections(stream, thereISDebug));
2348 
2349   const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate];
2350   if (certLink.Size != 0)
2351   {
2352     CSection &sect = _sections.AddNew();
2353     sect.Name = "CERTIFICATE";
2354     sect.Va = 0;
2355     sect.Pa = certLink.Va;
2356     sect.PSize = sect.VSize = certLink.Size;
2357     sect.UpdateTotalSize(_totalSize);
2358   }
2359 
2360   if (thereISDebug)
2361   {
2362     /* sometime there is some data after debug section.
2363        We don't see any reference in exe file to that data.
2364        But we suppose that it's part of EXE file */
2365 
2366     const UInt32 kAlign = 1 << 12;
2367     UInt32 alignPos = _totalSize & (kAlign - 1);
2368     if (alignPos != 0)
2369     {
2370       UInt32 size = kAlign - alignPos;
2371       RINOK(stream->Seek(_totalSize, STREAM_SEEK_SET, NULL));
2372       buffer.Alloc(kAlign);
2373       Byte *buf = buffer;
2374       size_t processed = size;
2375       RINOK(ReadStream(stream, buf, &processed));
2376 
2377       /*
2378       if (processed != 0)
2379       {
2380         printf("\ndata after debug %d, %d \n", (int)size, (int)processed);
2381         fflush(stdout);
2382       }
2383       */
2384 
2385       size_t k;
2386       for (k = 0; k < processed; k++)
2387         if (buf[k] != 0)
2388           break;
2389       if (processed < size && processed < 100)
2390         _totalSize += (UInt32)processed;
2391       else if (((_totalSize + k) & 0x1FF) == 0 || processed < size)
2392         _totalSize += (UInt32)k;
2393     }
2394   }
2395   }
2396 
2397   if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= optStart)
2398   {
2399     if (_header.NumSymbols >= (1 << 24))
2400       return S_FALSE;
2401     UInt32 size = _header.NumSymbols * 18;
2402     RINOK(stream->Seek((UInt64)_header.PointerToSymbolTable + size, STREAM_SEEK_SET, NULL));
2403     Byte buf[4];
2404     RINOK(ReadStream_FALSE(stream, buf, 4));
2405     UInt32 size2 = Get32(buf);
2406     if (size2 >= (1 << 28))
2407       return S_FALSE;
2408     size += size2;
2409 
2410     CSection &sect = _sections.AddNew();
2411     sect.Name = "COFF_SYMBOLS";
2412     sect.Va = 0;
2413     sect.Pa = _header.PointerToSymbolTable;
2414     sect.PSize = sect.VSize = size;
2415     sect.UpdateTotalSize(_totalSize);
2416   }
2417 
2418   {
2419     CObjectVector<CSection> sections = _sections;
2420     sections.Sort();
2421     UInt32 limit = (1 << 12);
2422     unsigned num = 0;
2423     FOR_VECTOR (k, sections)
2424     {
2425       const CSection &s = sections[k];
2426       if (s.Pa > limit)
2427       {
2428         CSection &s2 = _sections.AddNew();
2429         s2.Pa = s2.Va = limit;
2430         s2.PSize = s2.VSize = s.Pa - limit;
2431         s2.IsAdditionalSection = true;
2432         s2.Name = '[';
2433         s2.Name.Add_UInt32(num++);
2434         s2.Name += ']';
2435         limit = s.Pa;
2436       }
2437       UInt32 next = s.Pa + s.PSize;
2438       if (next < s.Pa)
2439         break;
2440       if (next >= limit)
2441         limit = next;
2442     }
2443   }
2444 
2445 
2446   if (IsOpt())
2447   if (_optHeader.CheckSum != 0)
2448   {
2449     RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
2450     UInt32 checkSum = 0;
2451     RINOK(CalcCheckSum(stream, _totalSize, optStart + k_CheckSum_Field_Offset, checkSum));
2452     _checksumError = (checkSum != _optHeader.CheckSum);
2453   }
2454 
2455 
2456   if (!_allowTail)
2457   {
2458     UInt64 fileSize;
2459     RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
2460     if (fileSize > _totalSize)
2461       return S_FALSE;
2462   }
2463 
2464   bool _parseResources = true;
2465   // _parseResources = false;
2466 
2467   UInt64 mainSize = 0, mainSize2 = 0;
2468 
2469   for (i = 0; i < _sections.Size(); i++)
2470   {
2471     const CSection &sect = _sections[i];
2472     if (IsOpt())
2473     if (_parseResources && sect.Name == ".rsrc")
2474     {
2475       // 20.01: we try to parse only first copy of .rsrc section.
2476       _parseResources = false;
2477       const unsigned numMixItems = _mixItems.Size();
2478       HRESULT res = OpenResources(i, stream, callback);
2479       if (res == S_OK)
2480       {
2481         _resourcesPrefix = sect.Name.Ptr();
2482         _resourcesPrefix.Add_PathSepar();
2483         FOR_VECTOR (j, _items)
2484         {
2485           const CResItem &item = _items[j];
2486           if (item.Enabled)
2487           {
2488             CMixItem mixItem;
2489             mixItem.SectionIndex = i;
2490             mixItem.ResourceIndex = j;
2491             if (item.IsRcDataOrUnknown())
2492             {
2493               if (item.Size >= mainSize)
2494               {
2495                 mainSize2 = mainSize;
2496                 mainSize = item.Size;
2497                 _mainSubfile = _mixItems.Size();
2498               }
2499               else if (item.Size >= mainSize2)
2500                 mainSize2 = item.Size;
2501             }
2502             _mixItems.Add(mixItem);
2503           }
2504         }
2505         // 9.29: .rsrc_2 code was commented.
2506         // .rsrc_1 now must include that .rsrc_2 block.
2507         /*
2508         if (sect.PSize > sect.VSize)
2509         {
2510           int numBits = _optHeader.GetNumFileAlignBits();
2511           if (numBits >= 0)
2512           {
2513             UInt32 mask = (1 << numBits) - 1;
2514             UInt32 end = ((sect.VSize + mask) & ~mask);
2515 
2516             if (sect.PSize > end)
2517             {
2518               CSection &sect2 = _sections.AddNew();
2519               sect2.Flags = 0;
2520               sect2.Pa = sect.Pa + end;
2521               sect2.Va = sect.Va + end;
2522               sect2.PSize = sect.PSize - end;
2523               sect2.VSize = sect2.PSize;
2524               sect2.Name = ".rsrc_2";
2525               sect2.Time = 0;
2526               sect2.IsAdditionalSection = true;
2527             }
2528           }
2529         }
2530         */
2531         continue;
2532       }
2533       if (res != S_FALSE)
2534         return res;
2535       _mixItems.DeleteFrom(numMixItems);
2536       CloseResources();
2537     }
2538 
2539     if (sect.IsAdditionalSection)
2540     {
2541       if (sect.PSize >= mainSize)
2542       {
2543         mainSize2 = mainSize;
2544         mainSize = sect.PSize;
2545         _mainSubfile = _mixItems.Size();
2546       }
2547       else if (sect.PSize >= mainSize2)
2548         mainSize2 = sect.PSize;
2549     }
2550 
2551     CMixItem mixItem;
2552     mixItem.SectionIndex = i;
2553     _mixItems.Add(mixItem);
2554   }
2555 
2556   if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2)
2557     _mainSubfile = -1;
2558 
2559   for (i = 0; i < _mixItems.Size(); i++)
2560   {
2561     const CMixItem &mixItem = _mixItems[i];
2562     if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_")
2563     {
2564       _mainSubfile = i;
2565       break;
2566     }
2567   }
2568 
2569   for (i = 0; i < _versionKeys.Size(); i++)
2570   {
2571     if (i != 0)
2572       _versionFullString.Add_LF();
2573     const CStringKeyValue &k = _versionKeys[i];
2574     _versionFullString += k.Key;
2575     _versionFullString += ": ";
2576     _versionFullString += k.Value;
2577   }
2578 
2579   {
2580     int keyIndex = FindKey(_versionKeys, "OriginalFilename");
2581     if (keyIndex >= 0)
2582       _originalFilename = _versionKeys[keyIndex].Value;
2583   }
2584   {
2585     int keyIndex = FindKey(_versionKeys, "FileDescription");
2586     if (keyIndex >= 0)
2587       _versionShortString = _versionKeys[keyIndex].Value;
2588   }
2589   {
2590     int keyIndex = FindKey(_versionKeys, "FileVersion");
2591     if (keyIndex >= 0)
2592     {
2593       _versionShortString.Add_Space();
2594       _versionShortString += _versionKeys[keyIndex].Value;
2595     }
2596   }
2597 
2598   return S_OK;
2599 }
2600 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)2601 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
2602 {
2603   COM_TRY_BEGIN
2604   Close();
2605   RINOK(Open2(inStream, callback));
2606   _stream = inStream;
2607   return S_OK;
2608   COM_TRY_END
2609 }
2610 
CloseResources()2611 void CHandler::CloseResources()
2612 {
2613   _usedRes.Free();
2614   _items.Clear();
2615   _strings.Clear();
2616   _versionFiles.Clear();
2617   _buf.Free();
2618   _versionFullString.Empty();
2619   _versionShortString.Empty();
2620   _originalFilename.Empty();
2621   _versionKeys.Clear();
2622 }
2623 
Close()2624 STDMETHODIMP CHandler::Close()
2625 {
2626   _totalSize = 0;
2627   _checksumError = false;
2628   _mainSubfile = -1;
2629 
2630   _stream.Release();
2631   _sections.Clear();
2632   _mixItems.Clear();
2633   CloseResources();
2634   return S_OK;
2635 }
2636 
GetNumberOfItems(UInt32 * numItems)2637 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
2638 {
2639   *numItems = _mixItems.Size();
2640   return S_OK;
2641 }
2642 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)2643 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2644     Int32 testMode, IArchiveExtractCallback *extractCallback)
2645 {
2646   COM_TRY_BEGIN
2647   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2648   if (allFilesMode)
2649     numItems = _mixItems.Size();
2650   if (numItems == 0)
2651     return S_OK;
2652   UInt64 totalSize = 0;
2653   UInt32 i;
2654   for (i = 0; i < numItems; i++)
2655   {
2656     const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]];
2657     UInt64 size;
2658     if (mixItem.StringIndex >= 0)
2659       size = _strings[mixItem.StringIndex].FinalSize();
2660     else if (mixItem.VersionIndex >= 0)
2661       size = _versionFiles[mixItem.VersionIndex].Size();
2662     else if (mixItem.ResourceIndex >= 0)
2663       size = _items[mixItem.ResourceIndex].GetSize();
2664     else
2665       size = _sections[mixItem.SectionIndex].GetSizeExtract();
2666     totalSize += size;
2667   }
2668   extractCallback->SetTotal(totalSize);
2669 
2670   UInt64 currentTotalSize = 0;
2671   UInt64 currentItemSize;
2672 
2673   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
2674   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
2675 
2676   CLocalProgress *lps = new CLocalProgress;
2677   CMyComPtr<ICompressProgressInfo> progress = lps;
2678   lps->Init(extractCallback, false);
2679 
2680   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
2681   CMyComPtr<ISequentialInStream> inStream(streamSpec);
2682   streamSpec->SetStream(_stream);
2683 
2684   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
2685   {
2686     lps->InSize = lps->OutSize = currentTotalSize;
2687     RINOK(lps->SetCur());
2688     Int32 askMode = testMode ?
2689         NExtract::NAskMode::kTest :
2690         NExtract::NAskMode::kExtract;
2691     UInt32 index = allFilesMode ? i : indices[i];
2692 
2693     CMyComPtr<ISequentialOutStream> outStream;
2694     RINOK(extractCallback->GetStream(index, &outStream, askMode));
2695     const CMixItem &mixItem = _mixItems[index];
2696 
2697     const CSection &sect = _sections[mixItem.SectionIndex];
2698     bool isOk = true;
2699     if (mixItem.StringIndex >= 0)
2700     {
2701       const CStringItem &item = _strings[mixItem.StringIndex];
2702       currentItemSize = item.FinalSize();
2703       if (!testMode && !outStream)
2704         continue;
2705 
2706       RINOK(extractCallback->PrepareOperation(askMode));
2707       if (outStream)
2708         RINOK(WriteStream(outStream, item.Buf, item.FinalSize()));
2709     }
2710     else if (mixItem.VersionIndex >= 0)
2711     {
2712       const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
2713       currentItemSize = item.Size();
2714       if (!testMode && !outStream)
2715         continue;
2716 
2717       RINOK(extractCallback->PrepareOperation(askMode));
2718       if (outStream)
2719         RINOK(WriteStream(outStream, item, item.Size()));
2720     }
2721     else if (mixItem.ResourceIndex >= 0)
2722     {
2723       const CResItem &item = _items[mixItem.ResourceIndex];
2724       currentItemSize = item.GetSize();
2725       if (!testMode && !outStream)
2726         continue;
2727 
2728       RINOK(extractCallback->PrepareOperation(askMode));
2729       size_t offset = item.Offset - sect.Va;
2730       if (!CheckItem(sect, item, offset))
2731         isOk = false;
2732       else if (outStream)
2733       {
2734         if (item.HeaderSize != 0)
2735           RINOK(WriteStream(outStream, item.Header, item.HeaderSize));
2736         RINOK(WriteStream(outStream, _buf + offset, item.Size));
2737       }
2738     }
2739     else
2740     {
2741       currentItemSize = sect.GetSizeExtract();
2742       if (!testMode && !outStream)
2743         continue;
2744 
2745       RINOK(extractCallback->PrepareOperation(askMode));
2746       RINOK(_stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));
2747       streamSpec->Init(currentItemSize);
2748       RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
2749       isOk = (copyCoderSpec->TotalSize == currentItemSize);
2750     }
2751 
2752     outStream.Release();
2753     RINOK(extractCallback->SetOperationResult(isOk ?
2754         NExtract::NOperationResult::kOK :
2755         NExtract::NOperationResult::kDataError));
2756   }
2757   return S_OK;
2758   COM_TRY_END
2759 }
2760 
GetStream(UInt32 index,ISequentialInStream ** stream)2761 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
2762 {
2763   COM_TRY_BEGIN
2764   *stream = 0;
2765 
2766   const CMixItem &mixItem = _mixItems[index];
2767   const CSection &sect = _sections[mixItem.SectionIndex];
2768   if (mixItem.IsSectionItem())
2769     return CreateLimitedInStream(_stream, sect.Pa, sect.PSize, stream);
2770 
2771   CBufInStream *inStreamSpec = new CBufInStream;
2772   CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec;
2773   CReferenceBuf *referenceBuf = new CReferenceBuf;
2774   CMyComPtr<IUnknown> ref = referenceBuf;
2775   if (mixItem.StringIndex >= 0)
2776   {
2777     const CStringItem &item = _strings[mixItem.StringIndex];
2778     referenceBuf->Buf.CopyFrom(item.Buf, item.FinalSize());
2779   }
2780   else if (mixItem.VersionIndex >= 0)
2781   {
2782     const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
2783     referenceBuf->Buf.CopyFrom(item, item.Size());
2784   }
2785   else
2786   {
2787     const CResItem &item = _items[mixItem.ResourceIndex];
2788     size_t offset = item.Offset - sect.Va;
2789     if (!CheckItem(sect, item, offset))
2790       return S_FALSE;
2791     if (item.HeaderSize == 0)
2792     {
2793       CBufInStream *streamSpec = new CBufInStream;
2794       CMyComPtr<IInStream> streamTemp2 = streamSpec;
2795       streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this);
2796       *stream = streamTemp2.Detach();
2797       return S_OK;
2798     }
2799     referenceBuf->Buf.Alloc(item.HeaderSize + item.Size);
2800     memcpy(referenceBuf->Buf, item.Header, item.HeaderSize);
2801     if (item.Size != 0)
2802       memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size);
2803   }
2804   inStreamSpec->Init(referenceBuf);
2805 
2806   *stream = streamTemp.Detach();
2807   return S_OK;
2808   COM_TRY_END
2809 }
2810 
AllowTail(Int32 allowTail)2811 STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
2812 {
2813   _allowTail = IntToBool(allowTail);
2814   return S_OK;
2815 }
2816 
2817 static const Byte k_Signature[] = { 'M', 'Z' };
2818 
2819 REGISTER_ARC_I(
2820   "PE", "exe dll sys", 0, 0xDD,
2821   k_Signature,
2822   0,
2823   NArcInfoFlags::kPreArc,
2824   IsArc_Pe)
2825 
2826 }
2827 
2828 namespace NCoff {
2829 
IsArc_Coff(const Byte * p,size_t size)2830 API_FUNC_static_IsArc IsArc_Coff(const Byte *p, size_t size)
2831 {
2832   if (size < NPe::kCoffHeaderSize)
2833     return k_IsArc_Res_NEED_MORE;
2834   NPe::CHeader header;
2835   if (!header.ParseCoff(p))
2836     return k_IsArc_Res_NO;
2837   return k_IsArc_Res_YES;
2838 }
2839 }
2840 
2841 /*
2842 static const Byte k_Signature[] =
2843 {
2844     2, 0x4C, 0x01, // x86
2845     2, 0x64, 0x86, // x64
2846     2, 0x64, 0xAA  // ARM64
2847 };
2848 REGISTER_ARC_I_CLS(
2849 */
2850 
2851 REGISTER_ARC_I_CLS_NO_SIG(
2852   NPe::CHandler(true),
2853   "COFF", "obj", 0, 0xC6,
2854   // k_Signature,
2855   0,
2856   // NArcInfoFlags::kMultiSignature |
2857   NArcInfoFlags::kStartOpen,
2858   IsArc_Coff)
2859 }
2860 
2861 
2862 namespace NTe {
2863 
2864 // Terse Executable (TE) image
2865 
2866 struct CDataDir
2867 {
2868   UInt32 Va;
2869   UInt32 Size;
2870 
2871   void Parse(const Byte *p)
2872   {
2873     G32(0, Va);
2874     G32(4, Size);
2875   }
2876 };
2877 
2878 static const UInt32 kHeaderSize = 40;
2879 
2880 static bool FindValue(const CUInt32PCharPair *pairs, unsigned num, UInt32 value)
2881 {
2882   for (unsigned i = 0; i < num; i++)
2883     if (pairs[i].Value == value)
2884       return true;
2885   return false;
2886 }
2887 
2888 #define MY_FIND_VALUE(pairs, val) FindValue(pairs, ARRAY_SIZE(pairs), val)
2889 #define MY_FIND_VALUE_2(strings, val) (val < ARRAY_SIZE(strings) && strings[val])
2890 
2891 static const UInt32 kNumSection_MAX = 32;
2892 
2893 struct CHeader
2894 {
2895   UInt16 Machine;
2896   Byte NumSections;
2897   Byte SubSystem;
2898   UInt16 StrippedSize;
2899   /*
2900   UInt32 AddressOfEntryPoint;
2901   UInt32 BaseOfCode;
2902   UInt64 ImageBase;
2903   */
2904   CDataDir DataDir[2]; // base relocation and debug directory
2905 
2906   bool ConvertPa(UInt32 &pa) const
2907   {
2908     if (pa < StrippedSize)
2909       return false;
2910     pa = pa - StrippedSize + kHeaderSize;
2911     return true;
2912   }
2913   bool Parse(const Byte *p);
2914 };
2915 
2916 bool CHeader::Parse(const Byte *p)
2917 {
2918   NumSections = p[4];
2919   if (NumSections > kNumSection_MAX)
2920     return false;
2921   SubSystem = p[5];
2922   G16(2, Machine);
2923   G16(6, StrippedSize);
2924   /*
2925   G32(8, AddressOfEntryPoint);
2926   G32(12, BaseOfCode);
2927   G64(16, ImageBase);
2928   */
2929   for (int i = 0; i < 2; i++)
2930   {
2931     CDataDir &dd = DataDir[i];
2932     dd.Parse(p + 24 + i * 8);
2933     if (dd.Size >= ((UInt32)1 << 28))
2934       return false;
2935   }
2936   return
2937       MY_FIND_VALUE(NPe::g_MachinePairs, Machine) &&
2938       MY_FIND_VALUE_2(NPe::g_SubSystems, SubSystem);
2939 }
2940 
2941 API_FUNC_static_IsArc IsArc_Te(const Byte *p, size_t size)
2942 {
2943   if (size < 2)
2944     return k_IsArc_Res_NEED_MORE;
2945   if (p[0] != 'V' || p[1] != 'Z')
2946     return k_IsArc_Res_NO;
2947   if (size < kHeaderSize)
2948     return k_IsArc_Res_NEED_MORE;
2949 
2950   CHeader h;
2951   if (!h.Parse(p))
2952     return k_IsArc_Res_NO;
2953   return k_IsArc_Res_YES;
2954 }
2955 }
2956 
2957 
2958 struct CSection
2959 {
2960   Byte Name[NPe::kNameSize];
2961 
2962   UInt32 VSize;
2963   UInt32 Va;
2964   UInt32 PSize;
2965   UInt32 Pa;
2966   UInt32 Flags;
2967   // UInt16 NumRelocs;
2968 
2969   void Parse(const Byte *p)
2970   {
2971     memcpy(Name, p, NPe::kNameSize);
2972     G32(8, VSize);
2973     G32(12, Va);
2974     G32(16, PSize);
2975     G32(20, Pa);
2976     // G32(p + 32, NumRelocs);
2977     G32(36, Flags);
2978   }
2979 
2980   bool Check() const
2981   {
2982     return
2983         Pa <= ((UInt32)1 << 30) &&
2984         PSize <= ((UInt32)1 << 30);
2985   }
2986 
2987   void UpdateTotalSize(UInt32 &totalSize)
2988   {
2989     UInt32 t = Pa + PSize;
2990     if (t > totalSize)
2991       totalSize = t;
2992   }
2993 };
2994 
2995 class CHandler:
2996   public IInArchive,
2997   public IInArchiveGetStream,
2998   public IArchiveAllowTail,
2999   public CMyUnknownImp
3000 {
3001   CRecordVector<CSection> _items;
3002   CMyComPtr<IInStream> _stream;
3003   UInt32 _totalSize;
3004   bool _allowTail;
3005   CHeader _h;
3006 
3007   HRESULT Open2(IInStream *stream);
3008 public:
3009   MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IArchiveAllowTail)
3010   INTERFACE_IInArchive(;)
3011   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
3012   STDMETHOD(AllowTail)(Int32 allowTail);
3013   CHandler(): _allowTail(false) {}
3014 };
3015 
3016 static const Byte kProps[] =
3017 {
3018   kpidPath,
3019   kpidSize,
3020   kpidVirtualSize,
3021   kpidCharacts,
3022   kpidOffset,
3023   kpidVa
3024 };
3025 
3026 enum
3027 {
3028   kpidSubSystem = kpidUserDefined
3029   // , kpidImageBase
3030 };
3031 
3032 static const CStatProp kArcProps[] =
3033 {
3034   // { NULL, kpidHeadersSize, VT_UI4 },
3035   { NULL, kpidCpu, VT_BSTR},
3036   { "Subsystem", kpidSubSystem, VT_BSTR },
3037   // { "Image Base", kpidImageBase, VT_UI8 }
3038 };
3039 
3040 IMP_IInArchive_Props
3041 IMP_IInArchive_ArcProps_WITH_NAME
3042 
3043 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
3044 {
3045   COM_TRY_BEGIN
3046   NCOM::CPropVariant prop;
3047   switch (propID)
3048   {
3049     case kpidPhySize: prop = _totalSize; break;
3050     case kpidCpu: PAIR_TO_PROP(NPe::g_MachinePairs, _h.Machine, prop); break;
3051     case kpidSubSystem: TYPE_TO_PROP(NPe::g_SubSystems, _h.SubSystem, prop); break;
3052     /*
3053     case kpidImageBase: prop = _h.ImageBase; break;
3054     case kpidAddressOfEntryPoint: prop = _h.AddressOfEntryPoint; break;
3055     case kpidBaseOfCode: prop = _h.BaseOfCode; break;
3056     */
3057   }
3058   prop.Detach(value);
3059   return S_OK;
3060   COM_TRY_END
3061 }
3062 
3063 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
3064 {
3065   COM_TRY_BEGIN
3066   NCOM::CPropVariant prop;
3067   {
3068     const CSection &item = _items[index];
3069     switch (propID)
3070     {
3071       case kpidPath:
3072       {
3073         AString name;
3074         NPe::GetName(item.Name, name);
3075         prop = MultiByteToUnicodeString(name);
3076         break;
3077       }
3078       case kpidSize:
3079       case kpidPackSize: prop = (UInt64)item.PSize; break;
3080       case kpidVirtualSize: prop = (UInt64)item.VSize; break;
3081       case kpidOffset: prop = item.Pa; break;
3082       case kpidVa: prop = item.Va; break;
3083       case kpidCharacts: FLAGS_TO_PROP(NPe::g_SectFlags, item.Flags, prop); break;
3084     }
3085   }
3086   prop.Detach(value);
3087   return S_OK;
3088   COM_TRY_END
3089 }
3090 
3091 HRESULT CHandler::Open2(IInStream *stream)
3092 {
3093   Byte h[kHeaderSize];
3094   RINOK(ReadStream_FALSE(stream, h, kHeaderSize));
3095   if (h[0] != 'V' || h[1] != 'Z')
3096     return S_FALSE;
3097   if (!_h.Parse(h))
3098     return S_FALSE;
3099 
3100   UInt32 headerSize = NPe::kSectionSize * (UInt32)_h.NumSections;
3101   CByteArr buf(headerSize);
3102   RINOK(ReadStream_FALSE(stream, buf, headerSize));
3103   headerSize += kHeaderSize;
3104 
3105   _totalSize = headerSize;
3106   _items.ClearAndReserve(_h.NumSections);
3107   for (UInt32 i = 0; i < _h.NumSections; i++)
3108   {
3109     CSection sect;
3110     sect.Parse(buf + i * NPe::kSectionSize);
3111     if (!_h.ConvertPa(sect.Pa))
3112       return S_FALSE;
3113     if (sect.Pa < headerSize)
3114       return S_FALSE;
3115     if (!sect.Check())
3116       return S_FALSE;
3117     _items.AddInReserved(sect);
3118     sect.UpdateTotalSize(_totalSize);
3119   }
3120 
3121   if (!_allowTail)
3122   {
3123     UInt64 fileSize;
3124     RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
3125     if (fileSize > _totalSize)
3126       return S_FALSE;
3127   }
3128 
3129   return S_OK;
3130 }
3131 
3132 STDMETHODIMP CHandler::Open(IInStream *inStream,
3133     const UInt64 * /* maxCheckStartPosition */,
3134     IArchiveOpenCallback * /* openArchiveCallback */)
3135 {
3136   COM_TRY_BEGIN
3137   Close();
3138   try
3139   {
3140     if (Open2(inStream) != S_OK)
3141       return S_FALSE;
3142     _stream = inStream;
3143   }
3144   catch(...) { return S_FALSE; }
3145   return S_OK;
3146   COM_TRY_END
3147 }
3148 
3149 STDMETHODIMP CHandler::Close()
3150 {
3151   _totalSize = 0;
3152   _stream.Release();
3153   _items.Clear();
3154   return S_OK;
3155 }
3156 
3157 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
3158 {
3159   *numItems = _items.Size();
3160   return S_OK;
3161 }
3162 
3163 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
3164     Int32 testMode, IArchiveExtractCallback *extractCallback)
3165 {
3166   COM_TRY_BEGIN
3167   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
3168   if (allFilesMode)
3169     numItems = _items.Size();
3170   if (numItems == 0)
3171     return S_OK;
3172   UInt64 totalSize = 0;
3173   UInt32 i;
3174   for (i = 0; i < numItems; i++)
3175     totalSize += _items[allFilesMode ? i : indices[i]].PSize;
3176   extractCallback->SetTotal(totalSize);
3177 
3178   UInt64 currentTotalSize = 0;
3179 
3180   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
3181   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
3182 
3183   CLocalProgress *lps = new CLocalProgress;
3184   CMyComPtr<ICompressProgressInfo> progress = lps;
3185   lps->Init(extractCallback, false);
3186 
3187   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
3188   CMyComPtr<ISequentialInStream> inStream(streamSpec);
3189   streamSpec->SetStream(_stream);
3190 
3191   for (i = 0; i < numItems; i++)
3192   {
3193     lps->InSize = lps->OutSize = currentTotalSize;
3194     RINOK(lps->SetCur());
3195     CMyComPtr<ISequentialOutStream> realOutStream;
3196     Int32 askMode = testMode ?
3197         NExtract::NAskMode::kTest :
3198         NExtract::NAskMode::kExtract;
3199     UInt32 index = allFilesMode ? i : indices[i];
3200     const CSection &item = _items[index];
3201     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
3202     currentTotalSize += item.PSize;
3203 
3204     if (!testMode && !realOutStream)
3205       continue;
3206     RINOK(extractCallback->PrepareOperation(askMode));
3207     int res = NExtract::NOperationResult::kDataError;
3208 
3209     RINOK(_stream->Seek(item.Pa, STREAM_SEEK_SET, NULL));
3210     streamSpec->Init(item.PSize);
3211     RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
3212     if (copyCoderSpec->TotalSize == item.PSize)
3213       res = NExtract::NOperationResult::kOK;
3214 
3215     realOutStream.Release();
3216     RINOK(extractCallback->SetOperationResult(res));
3217   }
3218   return S_OK;
3219   COM_TRY_END
3220 }
3221 
3222 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
3223 {
3224   COM_TRY_BEGIN
3225   const CSection &item = _items[index];
3226   return CreateLimitedInStream(_stream, item.Pa, item.PSize, stream);
3227   COM_TRY_END
3228 }
3229 
3230 STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
3231 {
3232   _allowTail = IntToBool(allowTail);
3233   return S_OK;
3234 }
3235 
3236 static const Byte k_Signature[] = { 'V', 'Z' };
3237 
3238 REGISTER_ARC_I(
3239   "TE", "te", 0, 0xCF,
3240   k_Signature,
3241   0,
3242   NArcInfoFlags::kPreArc,
3243   IsArc_Te)
3244 
3245 }
3246 }
3247