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