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