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 §, 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 § = _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 § = _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 § = _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 § = _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 § = _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 § = _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 § = _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 §2 = _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 § = _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 § = _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