| // PeHandler.cpp |
| |
| #include "StdAfx.h" |
| |
| // #include <stdio.h> |
| |
| #include "../../../C/CpuArch.h" |
| |
| #include "../../Common/DynamicBuffer.h" |
| #include "../../Common/ComTry.h" |
| #include "../../Common/IntToString.h" |
| #include "../../Common/StringConvert.h" |
| |
| #include "../../Windows/PropVariantUtils.h" |
| #include "../../Windows/TimeUtils.h" |
| |
| #include "../Common/LimitedStreams.h" |
| #include "../Common/ProgressUtils.h" |
| #include "../Common/RegisterArc.h" |
| #include "../Common/StreamObjects.h" |
| #include "../Common/StreamUtils.h" |
| |
| #include "../Compress/CopyCoder.h" |
| |
| #define Get16(p) GetUi16(p) |
| #define Get32(p) GetUi32(p) |
| #define Get64(p) GetUi64(p) |
| |
| #define G16(offs, v) v = Get16(p + (offs)) |
| #define G32(offs, v) v = Get32(p + (offs)) |
| #define G32_signed(offs, v) v = (Int32)Get32(p + (offs)) |
| #define G64(offs, v) v = Get64(p + (offs)) |
| |
| #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; } |
| |
| using namespace NWindows; |
| |
| namespace NArchive { |
| namespace NPe { |
| |
| static const UInt32 k_Signature32 = 0x00004550; |
| |
| static HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res) |
| { |
| const UInt32 kBufSizeMax = (UInt32)1 << 15; |
| UInt32 bufSize = kBufSizeMax; |
| CByteBuffer buffer(bufSize); |
| Byte *buf = buffer; |
| UInt32 sum = 0; |
| UInt32 pos = 0; |
| for (;;) |
| { |
| UInt32 rem = size - pos; |
| if (rem > bufSize) |
| rem = bufSize; |
| if (rem == 0) |
| break; |
| size_t processed = rem; |
| RINOK(ReadStream(stream, buf, &processed)) |
| |
| for (unsigned j = 0; j < 4; j++) |
| { |
| UInt32 e = excludePos + j; |
| if (pos <= e) |
| { |
| e -= pos; |
| if (e < processed) |
| buf[e] = 0; |
| } |
| } |
| |
| const unsigned kStep = (1 << 4); |
| { |
| for (size_t i = processed; (i & (kStep - 1)) != 0; i++) |
| buf[i] = 0; |
| } |
| { |
| const Byte *buf2 = buf; |
| const Byte *bufLimit = buf + processed; |
| UInt64 sum2 = 0; |
| for (; buf2 < bufLimit; buf2 += kStep) |
| { |
| UInt64 sum3 = (UInt64)Get32(buf2) |
| + Get32(buf2 + 4) |
| + Get32(buf2 + 8) |
| + Get32(buf2 + 12); |
| sum2 += sum3; |
| } |
| sum2 = (UInt32)(sum2) + (UInt64)(sum2 >> 32); |
| UInt32 sum3 = ((UInt32)sum2 + (UInt32)(sum2 >> 32)); |
| sum += (sum3 & 0xFFFF) + (sum3 >> 16); |
| sum = (sum & 0xFFFF) + (sum >> 16); |
| sum = (sum & 0xFFFF) + (sum >> 16); |
| } |
| |
| pos += (UInt32)processed; |
| if (rem != processed) |
| break; |
| } |
| res = sum + pos; |
| return S_OK; |
| } |
| |
| |
| struct CVersion |
| { |
| UInt16 Major; |
| UInt16 Minor; |
| |
| void Parse(const Byte *p) |
| { |
| G16(0, Major); |
| G16(2, Minor); |
| } |
| void ToProp(NCOM::CPropVariant &prop); |
| }; |
| |
| void CVersion::ToProp(NCOM::CPropVariant &prop) |
| { |
| char sz[32]; |
| ConvertUInt32ToString(Major, sz); |
| unsigned len = MyStringLen(sz); |
| sz[len] = '.'; |
| ConvertUInt32ToString(Minor, sz + len + 1); |
| prop = sz; |
| } |
| |
| static const unsigned kCoffHeaderSize = 20; |
| static const unsigned kPeHeaderSize = 4 + kCoffHeaderSize; |
| static const unsigned k_OptHeader32_Size_MIN = 96; |
| static const unsigned k_OptHeader64_Size_MIN = 112; |
| |
| static const UInt32 PE_IMAGE_FILE_DLL = (1 << 13); |
| |
| struct CHeader |
| { |
| UInt16 Machine; |
| UInt16 NumSections; |
| UInt32 Time; |
| UInt32 PointerToSymbolTable; |
| UInt32 NumSymbols; |
| UInt16 OptHeaderSize; |
| UInt16 Flags; |
| |
| void ParseBase(const Byte *p); |
| bool ParseCoff(const Byte *p); |
| bool ParsePe(const Byte *p); |
| bool IsDll() const { return (Flags & PE_IMAGE_FILE_DLL) != 0; } |
| }; |
| |
| void CHeader::ParseBase(const Byte *p) |
| { |
| G16( 0, Machine); |
| G16( 2, NumSections); |
| G32( 4, Time); |
| G32( 8, PointerToSymbolTable); |
| G32(12, NumSymbols); |
| G16(16, OptHeaderSize); |
| G16(18, Flags); |
| } |
| |
| bool CHeader::ParsePe(const Byte *p) |
| { |
| if (Get32(p) != k_Signature32) |
| return false; |
| ParseBase(p + 4); |
| return OptHeaderSize >= k_OptHeader32_Size_MIN; |
| } |
| |
| struct CDirLink |
| { |
| UInt32 Va; |
| UInt32 Size; |
| |
| CDirLink(): Va(0), Size(0) {} |
| void Parse(const Byte *p) |
| { |
| G32(0, Va); |
| G32(4, Size); |
| } |
| }; |
| |
| enum |
| { |
| kDirLink_Certificate = 4, |
| kDirLink_Debug = 6 |
| }; |
| |
| static const UInt32 kNumDirItemsMax = 16; |
| |
| struct CDebugEntry |
| { |
| UInt32 Flags; |
| UInt32 Time; |
| CVersion Ver; |
| UInt32 Type; |
| UInt32 Size; |
| UInt32 Va; |
| UInt32 Pa; |
| |
| void Parse(const Byte *p) |
| { |
| G32(0, Flags); |
| G32(4, Time); |
| Ver.Parse(p + 8); |
| G32(12, Type); |
| G32(16, Size); |
| G32(20, Va); |
| G32(24, Pa); |
| } |
| }; |
| |
| static const UInt32 k_CheckSum_Field_Offset = 64; |
| |
| static const UInt32 PE_OptHeader_Magic_32 = 0x10B; |
| static const UInt32 PE_OptHeader_Magic_64 = 0x20B; |
| |
| static const UInt32 k_SubSystems_EFI_First = 10; |
| static const UInt32 k_SubSystems_EFI_Last = 13; |
| |
| struct COptHeader |
| { |
| UInt16 Magic; |
| Byte LinkerVerMajor; |
| Byte LinkerVerMinor; |
| |
| UInt32 CodeSize; |
| UInt32 InitDataSize; |
| UInt32 UninitDataSize; |
| |
| // UInt32 AddressOfEntryPoint; |
| // UInt32 BaseOfCode; |
| // UInt32 BaseOfData32; |
| UInt64 ImageBase; |
| |
| UInt32 SectAlign; |
| UInt32 FileAlign; |
| |
| CVersion OsVer; |
| CVersion ImageVer; |
| CVersion SubsysVer; |
| |
| UInt32 ImageSize; |
| UInt32 HeadersSize; |
| UInt32 CheckSum; |
| UInt16 SubSystem; |
| UInt16 DllCharacts; |
| |
| UInt64 StackReserve; |
| UInt64 StackCommit; |
| UInt64 HeapReserve; |
| UInt64 HeapCommit; |
| |
| UInt32 NumDirItems; |
| CDirLink DirItems[kNumDirItemsMax]; |
| |
| bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; } |
| bool Parse(const Byte *p, UInt32 size); |
| |
| int GetNumFileAlignBits() const |
| { |
| for (unsigned i = 0; i < 32; i++) |
| if (((UInt32)1 << i) == FileAlign) |
| return (int)i; |
| return -1; |
| } |
| |
| bool IsSybSystem_EFI() const |
| { |
| return |
| SubSystem >= k_SubSystems_EFI_First && |
| SubSystem <= k_SubSystems_EFI_Last; |
| } |
| }; |
| |
| bool COptHeader::Parse(const Byte *p, UInt32 size) |
| { |
| if (size < k_OptHeader32_Size_MIN) |
| return false; |
| Magic = Get16(p); |
| switch (Magic) |
| { |
| case PE_OptHeader_Magic_32: |
| case PE_OptHeader_Magic_64: |
| break; |
| default: |
| return false; |
| } |
| LinkerVerMajor = p[2]; |
| LinkerVerMinor = p[3]; |
| |
| G32( 4, CodeSize); |
| G32( 8, InitDataSize); |
| G32(12, UninitDataSize); |
| // G32(16, AddressOfEntryPoint); |
| // G32(20, BaseOfCode); |
| |
| G32(32, SectAlign); |
| G32(36, FileAlign); |
| |
| OsVer.Parse(p + 40); |
| ImageVer.Parse(p + 44); |
| SubsysVer.Parse(p + 48); |
| |
| // reserved = Get32(p + 52); |
| |
| G32(56, ImageSize); |
| G32(60, HeadersSize); |
| G32(64, CheckSum); |
| G16(68, SubSystem); |
| G16(70, DllCharacts); |
| |
| UInt32 pos; |
| if (Is64Bit()) |
| { |
| if (size < k_OptHeader64_Size_MIN) |
| return false; |
| // BaseOfData32 = 0; |
| G64(24, ImageBase); |
| G64(72, StackReserve); |
| G64(80, StackCommit); |
| G64(88, HeapReserve); |
| G64(96, HeapCommit); |
| pos = 108; |
| } |
| else |
| { |
| // G32(24, BaseOfData32); |
| G32(28, ImageBase); |
| G32(72, StackReserve); |
| G32(76, StackCommit); |
| G32(80, HeapReserve); |
| G32(84, HeapCommit); |
| pos = 92; |
| } |
| |
| G32(pos, NumDirItems); |
| if (NumDirItems > (1 << 16)) |
| return false; |
| pos += 4; |
| if (pos + 8 * NumDirItems > size) |
| return false; |
| memset((void *)DirItems, 0, sizeof(DirItems)); |
| for (UInt32 i = 0; i < NumDirItems && i < kNumDirItemsMax; i++) |
| DirItems[i].Parse(p + pos + i * 8); |
| return true; |
| } |
| |
| static const UInt32 kSectionSize = 40; |
| |
| struct CSection |
| { |
| AString Name; |
| |
| UInt32 VSize; |
| UInt32 Va; |
| UInt32 PSize; |
| UInt32 Pa; |
| UInt32 Flags; |
| UInt32 Time; |
| // UInt16 NumRelocs; |
| bool IsRealSect; |
| bool IsDebug; |
| bool IsAdditionalSection; |
| |
| CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {} |
| |
| UInt32 GetSizeExtract() const { return PSize; } |
| UInt32 GetSizeMin() const { return MyMin(PSize, VSize); } |
| |
| void UpdateTotalSize(UInt32 &totalSize) const |
| { |
| UInt32 t = Pa + PSize; |
| if (totalSize < t) |
| totalSize = t; |
| } |
| |
| void Parse(const Byte *p); |
| |
| int Compare(const CSection &s) const |
| { |
| RINOZ(MyCompare(Pa, s.Pa)) |
| UInt32 size1 = GetSizeExtract(); |
| UInt32 size2 = s.GetSizeExtract(); |
| return MyCompare(size1, size2); |
| } |
| }; |
| |
| static const unsigned kNameSize = 8; |
| |
| static void GetName(const Byte *name, AString &res) |
| { |
| res.SetFrom_CalcLen((const char *)name, kNameSize); |
| } |
| |
| void CSection::Parse(const Byte *p) |
| { |
| GetName(p, Name); |
| G32( 8, VSize); |
| G32(12, Va); |
| G32(16, PSize); |
| G32(20, Pa); |
| // G16(32, NumRelocs); |
| G32(36, Flags); |
| } |
| |
| |
| |
| // IMAGE_FILE_* |
| |
| static const CUInt32PCharPair g_HeaderCharacts[] = |
| { |
| { 1, "Executable" }, |
| { 13, "DLL" }, |
| { 8, "32-bit" }, |
| { 5, "LargeAddress" }, |
| { 0, "NoRelocs" }, |
| { 2, "NoLineNums" }, |
| { 3, "NoLocalSyms" }, |
| { 4, "AggressiveWsTrim" }, |
| { 9, "NoDebugInfo" }, |
| { 10, "RemovableRun" }, |
| { 11, "NetRun" }, |
| { 12, "System" }, |
| { 14, "UniCPU" }, |
| { 7, "Little-Endian" }, |
| { 15, "Big-Endian" } |
| }; |
| |
| // IMAGE_DLLCHARACTERISTICS_* |
| |
| static const char * const g_DllCharacts[] = |
| { |
| NULL |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| , "HighEntropyVA" |
| , "Relocated" |
| , "Integrity" |
| , "NX-Compatible" |
| , "NoIsolation" |
| , "NoSEH" |
| , "NoBind" |
| , "AppContainer" |
| , "WDM" |
| , "GuardCF" |
| , "TerminalServerAware" |
| }; |
| |
| |
| // IMAGE_SCN_* constants: |
| |
| static const char * const g_SectFlags[] = |
| { |
| NULL |
| , NULL |
| , NULL |
| , "NoPad" |
| , NULL |
| , "Code" |
| , "InitializedData" |
| , "UninitializedData" |
| , "Other" |
| , "Comments" |
| , NULL // OVER |
| , "Remove" |
| , "COMDAT" |
| , NULL |
| , "NO_DEFER_SPEC_EXC" |
| , "GP" // MEM_FARDATA |
| , NULL // SYSHEAP |
| , "PURGEABLE" // 16BIT |
| , "LOCKED" |
| , "PRELOAD" |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| , "ExtendedRelocations" |
| , "Discardable" |
| , "NotCached" |
| , "NotPaged" |
| , "Shared" |
| , "Execute" |
| , "Read" |
| , "Write" |
| }; |
| |
| static const CUInt32PCharPair g_MachinePairs[] = |
| { |
| { 0x014C, "x86" }, |
| { 0x014D, "I860" }, |
| { 0x0162, "MIPS-R3000" }, |
| { 0x0166, "MIPS-R4000" }, |
| { 0x0168, "MIPS-R10000" }, |
| { 0x0169, "MIPS-V2" }, |
| { 0x0184, "Alpha" }, |
| { 0x01A2, "SH3" }, |
| { 0x01A3, "SH3-DSP" }, |
| { 0x01A4, "SH3E" }, |
| { 0x01A6, "SH4" }, |
| { 0x01A8, "SH5" }, |
| { 0x01C0, "ARM" }, |
| { 0x01C2, "ARM-Thumb" }, |
| { 0x01C4, "ARM-NT" }, |
| { 0x01D3, "AM33" }, |
| { 0x01F0, "PPC" }, |
| { 0x01F1, "PPC-FP" }, |
| { 0x0200, "IA-64" }, |
| { 0x0266, "MIPS-16" }, |
| { 0x0284, "Alpha-64" }, |
| { 0x0366, "MIPS-FPU" }, |
| { 0x0466, "MIPS-FPU16" }, |
| { 0x0520, "TriCore" }, |
| { 0x0CEF, "CEF" }, |
| { 0x0EBC, "EFI" }, |
| { 0x8664, "x64" }, |
| { 0x9041, "M32R" }, |
| { 0xAA64, "ARM64" }, |
| { 0xC0EE, "CEE" } |
| }; |
| |
| static const char * const g_SubSystems[] = |
| { |
| "Unknown" |
| , "Native" |
| , "Windows GUI" |
| , "Windows CUI" |
| , NULL // "Old Windows CE" |
| , "OS2" |
| , NULL |
| , "Posix" |
| , "Win9x" |
| , "Windows CE" |
| , "EFI" |
| , "EFI Boot" |
| , "EFI Runtime" |
| , "EFI ROM" |
| , "XBOX" |
| , NULL |
| , "Windows Boot" |
| , "XBOX Catalog" // 17 |
| }; |
| |
| static const char * const g_ResTypes[] = |
| { |
| NULL |
| , "CURSOR" |
| , "BITMAP" |
| , "ICON" |
| , "MENU" |
| , "DIALOG" |
| , "STRING" |
| , "FONTDIR" |
| , "FONT" |
| , "ACCELERATOR" |
| , "RCDATA" |
| , "MESSAGETABLE" |
| , "GROUP_CURSOR" |
| , NULL |
| , "GROUP_ICON" |
| , NULL |
| , "VERSION" |
| , "DLGINCLUDE" |
| , NULL |
| , "PLUGPLAY" |
| , "VXD" |
| , "ANICURSOR" |
| , "ANIICON" |
| , "HTML" |
| , "MANIFEST" |
| }; |
| |
| static const UInt32 kFlag = (UInt32)1 << 31; |
| static const UInt32 kMask = ~kFlag; |
| |
| struct CTableItem |
| { |
| UInt32 Offset; |
| UInt32 ID; |
| }; |
| |
| |
| static const UInt32 kBmpHeaderSize = 14; |
| static const UInt32 kIconHeaderSize = 22; |
| |
| struct CResItem |
| { |
| UInt32 Type; |
| UInt32 ID; |
| UInt32 Lang; |
| |
| UInt32 Size; |
| UInt32 Offset; |
| |
| UInt32 HeaderSize; |
| Byte Header[kIconHeaderSize]; // it must be enough for max size header. |
| bool Enabled; |
| |
| bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; } |
| UInt32 GetSize() const { return Size + HeaderSize; } |
| bool IsBmp() const { return Type == 2; } |
| bool IsIcon() const { return Type == 3; } |
| bool IsString() const { return Type == 6; } |
| bool IsRcData() const { return Type == 10; } |
| bool IsVersion() const { return Type == 16; } |
| bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; } |
| }; |
| |
| struct CTextFile |
| { |
| CByteDynamicBuffer Buf; |
| |
| size_t FinalSize() const { return Buf.GetPos(); } |
| |
| void AddChar(char c); |
| void AddWChar(UInt16 c); |
| void AddWChar_Smart(UInt16 c); |
| void NewLine(); |
| void AddString(const char *s); |
| void AddSpaces(int num); |
| void AddBytes(const Byte *p, size_t size) |
| { |
| Buf.AddData(p, size); |
| } |
| |
| void OpenBlock(int num) |
| { |
| AddSpaces(num); |
| AddChar('{'); |
| NewLine(); |
| } |
| void CloseBlock(int num) |
| { |
| AddSpaces(num); |
| AddChar('}'); |
| NewLine(); |
| } |
| }; |
| |
| void CTextFile::AddChar(char c) |
| { |
| Byte *p = Buf.GetCurPtrAndGrow(2); |
| p[0] = (Byte)c; |
| p[1] = 0; |
| } |
| |
| void CTextFile::AddWChar(UInt16 c) |
| { |
| Byte *p = Buf.GetCurPtrAndGrow(2); |
| SetUi16(p, c) |
| } |
| |
| void CTextFile::AddWChar_Smart(UInt16 c) |
| { |
| if (c == '\n') |
| { |
| AddChar('\\'); |
| c = 'n'; |
| } |
| AddWChar(c); |
| } |
| |
| void CTextFile::NewLine() |
| { |
| AddChar(0x0D); |
| AddChar(0x0A); |
| } |
| |
| void CTextFile::AddString(const char *s) |
| { |
| for (;; s++) |
| { |
| char c = *s; |
| if (c == 0) |
| return; |
| AddChar(c); |
| } |
| } |
| |
| void CTextFile::AddSpaces(int num) |
| { |
| for (int i = 0; i < num; i++) |
| AddChar(' '); |
| } |
| |
| struct CStringItem: public CTextFile |
| { |
| UInt32 Lang; |
| }; |
| |
| struct CByteBuffer_WithLang: public CByteBuffer |
| { |
| UInt32 Lang; |
| }; |
| |
| |
| struct CMixItem |
| { |
| int SectionIndex; |
| int ResourceIndex; |
| int StringIndex; |
| int VersionIndex; |
| |
| CMixItem(): SectionIndex(-1), ResourceIndex(-1), StringIndex(-1), VersionIndex(-1) {} |
| bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0 && VersionIndex < 0; } |
| }; |
| |
| struct CUsedBitmap |
| { |
| CByteBuffer Buf; |
| public: |
| void Alloc(size_t size) |
| { |
| size = (size + 7) / 8; |
| Buf.Alloc(size); |
| memset(Buf, 0, size); |
| } |
| |
| void Free() |
| { |
| Buf.Free(); |
| } |
| |
| bool SetRange(size_t from, unsigned size) |
| { |
| for (unsigned i = 0; i < size; i++) |
| { |
| size_t pos = (from + i) >> 3; |
| Byte mask = (Byte)(1 << ((from + i) & 7)); |
| Byte b = Buf[pos]; |
| if ((b & mask) != 0) |
| return false; |
| Buf[pos] = (Byte)(b | mask); |
| } |
| return true; |
| } |
| }; |
| |
| struct CStringKeyValue |
| { |
| UString Key; |
| UString Value; |
| }; |
| |
| |
| Z7_CLASS_IMP_CHandler_IInArchive_2( |
| IInArchiveGetStream, |
| IArchiveAllowTail |
| ) |
| CMyComPtr<IInStream> _stream; |
| CObjectVector<CSection> _sections; |
| CHeader _header; |
| UInt32 _totalSize; |
| Int32 _mainSubfile; |
| |
| CRecordVector<CMixItem> _mixItems; |
| CRecordVector<CResItem> _items; |
| CObjectVector<CStringItem> _strings; |
| CObjectVector<CByteBuffer_WithLang> _versionFiles; |
| UString _versionFullString; |
| UString _versionShortString; |
| UString _originalFilename; |
| CObjectVector<CStringKeyValue> _versionKeys; |
| |
| CByteBuffer _buf; |
| bool _oneLang; |
| UString _resourcesPrefix; |
| CUsedBitmap _usedRes; |
| // bool _parseResources; |
| bool _checksumError; |
| bool _sectionsError; |
| |
| bool IsOpt() const { return _header.OptHeaderSize != 0; } |
| |
| COptHeader _optHeader; |
| |
| bool _coffMode; |
| bool _allowTail; |
| |
| HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection); |
| HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); |
| |
| void AddResNameToString(UString &s, UInt32 id) const; |
| void AddLangPrefix(UString &s, UInt32 lang) const; |
| HRESULT ReadString(UInt32 offset, UString &dest) const; |
| HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items); |
| bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size); |
| HRESULT OpenResources(unsigned sectIndex, IInStream *stream, IArchiveOpenCallback *callback); |
| void CloseResources(); |
| |
| |
| bool CheckItem(const CSection §, const CResItem &item, size_t offset) const |
| { |
| return item.Offset >= sect.Va && offset <= _buf.Size() && _buf.Size() - offset >= item.Size; |
| } |
| |
| public: |
| CHandler(bool coffMode = false): |
| _coffMode(coffMode), |
| _allowTail(coffMode) |
| {} |
| }; |
| |
| |
| enum |
| { |
| kpidSectAlign = kpidUserDefined, |
| kpidFileAlign, |
| kpidLinkerVer, |
| kpidOsVer, |
| kpidImageVer, |
| kpidSubsysVer, |
| kpidCodeSize, |
| kpidImageSize, |
| kpidInitDataSize, |
| kpidUnInitDataSize, |
| kpidHeadersSizeUnInitDataSize, |
| kpidSubSystem, |
| kpidDllCharacts, |
| kpidStackReserve, |
| kpidStackCommit, |
| kpidHeapReserve, |
| kpidHeapCommit, |
| kpidImageBase |
| // kpidAddressOfEntryPoint, |
| // kpidBaseOfCode, |
| // kpidBaseOfData32, |
| }; |
| |
| static const CStatProp kArcProps[] = |
| { |
| // { NULL, kpidWarning, VT_BSTR}, |
| { NULL, kpidCpu, VT_BSTR}, |
| { NULL, kpidBit64, VT_BOOL}, |
| { NULL, kpidCharacts, VT_BSTR}, |
| { NULL, kpidCTime, VT_FILETIME}, |
| { NULL, kpidHeadersSize, VT_UI4}, |
| { NULL, kpidChecksum, VT_UI4}, |
| { NULL, kpidName, VT_BSTR}, |
| |
| { "Image Size", kpidImageSize, VT_UI4}, |
| { "Section Alignment", kpidSectAlign, VT_UI4}, |
| { "File Alignment", kpidFileAlign, VT_UI4}, |
| { "Code Size", kpidCodeSize, VT_UI4}, |
| { "Initialized Data Size", kpidInitDataSize, VT_UI4}, |
| { "Uninitialized Data Size", kpidUnInitDataSize, VT_UI4}, |
| { "Linker Version", kpidLinkerVer, VT_BSTR}, |
| { "OS Version", kpidOsVer, VT_BSTR}, |
| { "Image Version", kpidImageVer, VT_BSTR}, |
| { "Subsystem Version", kpidSubsysVer, VT_BSTR}, |
| { "Subsystem", kpidSubSystem, VT_BSTR}, |
| { "DLL Characteristics", kpidDllCharacts, VT_BSTR}, |
| { "Stack Reserve", kpidStackReserve, VT_UI8}, |
| { "Stack Commit", kpidStackCommit, VT_UI8}, |
| { "Heap Reserve", kpidHeapReserve, VT_UI8}, |
| { "Heap Commit", kpidHeapCommit, VT_UI8}, |
| { "Image Base", kpidImageBase, VT_UI8}, |
| { NULL, kpidComment, VT_BSTR}, |
| |
| // { "Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8}, |
| // { "Base Of Code", kpidBaseOfCode, VT_UI8}, |
| // { "Base Of Data", kpidBaseOfData32, VT_UI8}, |
| }; |
| |
| static const Byte kProps[] = |
| { |
| kpidPath, |
| kpidSize, |
| kpidPackSize, |
| kpidVirtualSize, |
| kpidCharacts, |
| kpidOffset, |
| kpidVa, |
| }; |
| |
| IMP_IInArchive_Props |
| IMP_IInArchive_ArcProps_WITH_NAME |
| |
| static void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop) |
| { |
| if (unixTime != 0) |
| PropVariant_SetFrom_UnixTime(prop, unixTime); |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| NCOM::CPropVariant prop; |
| switch (propID) |
| { |
| case kpidPhySize: prop = _totalSize; break; |
| case kpidComment: if (!_versionFullString.IsEmpty()) prop = _versionFullString; break; |
| case kpidShortComment: |
| if (!_versionShortString.IsEmpty()) |
| prop = _versionShortString; |
| else |
| { |
| PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); |
| } |
| break; |
| |
| case kpidName: if (!_originalFilename.IsEmpty()) prop = _originalFilename; break; |
| |
| // case kpidIsSelfExe: prop = !_header.IsDll(); break; |
| // case kpidError: |
| case kpidWarning: if (_checksumError) prop = "Checksum error"; break; |
| |
| case kpidWarningFlags: |
| { |
| UInt32 v = 0; |
| if (_sectionsError) v |= kpv_ErrorFlags_HeadersError; |
| if (v != 0) |
| prop = v; |
| break; |
| } |
| |
| case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break; |
| case kpidMTime: |
| case kpidCTime: TimeToProp(_header.Time, prop); break; |
| case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break; |
| case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; |
| |
| default: |
| if (IsOpt()) |
| switch (propID) |
| { |
| |
| case kpidSectAlign: prop = _optHeader.SectAlign; break; |
| case kpidFileAlign: prop = _optHeader.FileAlign; break; |
| case kpidLinkerVer: |
| { |
| CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor }; |
| v.ToProp(prop); |
| break; |
| } |
| |
| case kpidOsVer: _optHeader.OsVer.ToProp(prop); break; |
| case kpidImageVer: _optHeader.ImageVer.ToProp(prop); break; |
| case kpidSubsysVer: _optHeader.SubsysVer.ToProp(prop); break; |
| case kpidCodeSize: prop = _optHeader.CodeSize; break; |
| case kpidInitDataSize: prop = _optHeader.InitDataSize; break; |
| case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break; |
| case kpidImageSize: prop = _optHeader.ImageSize; break; |
| case kpidHeadersSize: prop = _optHeader.HeadersSize; break; |
| case kpidChecksum: prop = _optHeader.CheckSum; break; |
| |
| case kpidExtension: |
| if (_header.IsDll()) |
| prop = "dll"; |
| else if (_optHeader.IsSybSystem_EFI()) |
| prop = "efi"; |
| break; |
| |
| case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break; |
| case kpidSubSystem: TYPE_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break; |
| |
| case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break; |
| case kpidStackReserve: prop = _optHeader.StackReserve; break; |
| case kpidStackCommit: prop = _optHeader.StackCommit; break; |
| case kpidHeapReserve: prop = _optHeader.HeapReserve; break; |
| case kpidHeapCommit: prop = _optHeader.HeapCommit; break; |
| |
| case kpidImageBase: prop = _optHeader.ImageBase; break; |
| // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break; |
| // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break; |
| // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break; |
| } |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const |
| { |
| if ((offset & 1) != 0 || offset >= _buf.Size()) |
| return S_FALSE; |
| size_t rem = _buf.Size() - offset; |
| if (rem < 2) |
| return S_FALSE; |
| unsigned len = Get16(_buf + offset); |
| if ((rem - 2) / 2 < len) |
| return S_FALSE; |
| dest.Empty(); |
| wchar_t *destBuf = dest.GetBuf(len); |
| offset += 2; |
| const Byte *src = _buf + offset; |
| unsigned i; |
| for (i = 0; i < len; i++) |
| { |
| wchar_t c = (wchar_t)Get16(src + i * 2); |
| if (c == 0) |
| break; |
| destBuf[i] = c; |
| } |
| destBuf[i] = 0; |
| dest.ReleaseBuf_SetLen(i); |
| return S_OK; |
| } |
| |
| void CHandler::AddResNameToString(UString &s, UInt32 id) const |
| { |
| if ((id & kFlag) != 0) |
| { |
| UString name; |
| if (ReadString(id & kMask, name) == S_OK) |
| { |
| const wchar_t *str = L"[]"; |
| if (name.Len() > 1 && name[0] == '"' && name.Back() == '"') |
| { |
| if (name.Len() != 2) |
| { |
| name.DeleteBack(); |
| str = name.Ptr(1); |
| } |
| } |
| else if (!name.IsEmpty()) |
| str = name; |
| s += str; |
| return; |
| } |
| } |
| s.Add_UInt32(id); |
| } |
| |
| void CHandler::AddLangPrefix(UString &s, UInt32 lang) const |
| { |
| if (!_oneLang) |
| { |
| AddResNameToString(s, lang); |
| s.Add_PathSepar(); |
| } |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| NCOM::CPropVariant prop; |
| const CMixItem &mixItem = _mixItems[index]; |
| if (mixItem.StringIndex >= 0) |
| { |
| const CStringItem &item = _strings[mixItem.StringIndex]; |
| switch (propID) |
| { |
| case kpidPath: |
| { |
| UString s = _resourcesPrefix; |
| AddLangPrefix(s, item.Lang); |
| s += "string.txt"; |
| prop = s; |
| break; |
| } |
| case kpidSize: |
| case kpidPackSize: |
| prop = (UInt64)item.FinalSize(); break; |
| } |
| } |
| else if (mixItem.VersionIndex >= 0) |
| { |
| const CByteBuffer_WithLang &item = _versionFiles[mixItem.VersionIndex]; |
| switch (propID) |
| { |
| case kpidPath: |
| { |
| UString s = _resourcesPrefix; |
| AddLangPrefix(s, item.Lang); |
| s += "version.txt"; |
| prop = s; |
| break; |
| } |
| case kpidSize: |
| case kpidPackSize: |
| prop = (UInt64)item.Size(); break; |
| } |
| } |
| else if (mixItem.ResourceIndex >= 0) |
| { |
| const CResItem &item = _items[mixItem.ResourceIndex]; |
| switch (propID) |
| { |
| case kpidPath: |
| { |
| UString s = _resourcesPrefix; |
| AddLangPrefix(s, item.Lang); |
| { |
| const char *p = NULL; |
| if (item.Type < Z7_ARRAY_SIZE(g_ResTypes)) |
| p = g_ResTypes[item.Type]; |
| if (p) |
| s += p; |
| else |
| AddResNameToString(s, item.Type); |
| } |
| s.Add_PathSepar(); |
| AddResNameToString(s, item.ID); |
| if (item.HeaderSize != 0) |
| { |
| if (item.IsBmp()) |
| s += ".bmp"; |
| else if (item.IsIcon()) |
| s += ".ico"; |
| } |
| prop = s; |
| break; |
| } |
| case kpidSize: prop = (UInt64)item.GetSize(); break; |
| case kpidPackSize: prop = (UInt64)item.Size; break; |
| } |
| } |
| else |
| { |
| const CSection &item = _sections[mixItem.SectionIndex]; |
| switch (propID) |
| { |
| case kpidPath: |
| { |
| AString s = item.Name; |
| s.Replace('/', '_'); |
| s.Replace('\\', '_'); |
| prop = MultiByteToUnicodeString(s); |
| break; |
| } |
| case kpidSize: prop = (UInt64)item.PSize; break; |
| case kpidPackSize: prop = (UInt64)item.PSize; break; |
| case kpidVirtualSize: prop = (UInt64)item.VSize; break; |
| case kpidOffset: prop = item.Pa; break; |
| case kpidVa: if (item.IsRealSect) prop = item.Va; break; |
| case kpidMTime: |
| case kpidCTime: |
| TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break; |
| case kpidCharacts: |
| if (item.IsRealSect) |
| { |
| UInt32 flags = item.Flags; |
| const UInt32 MY_IMAGE_SCN_ALIGN_MASK = 0x00F00000; |
| AString s = FlagsToString(g_SectFlags, Z7_ARRAY_SIZE(g_SectFlags), item.Flags & ~MY_IMAGE_SCN_ALIGN_MASK); |
| const UInt32 align = ((flags >> 20) & 0xF); |
| if (align != 0) |
| { |
| char sz[32]; |
| ConvertUInt32ToString(1 << (align - 1), sz); |
| s.Add_Space(); |
| s += "align_"; |
| s += sz; |
| } |
| prop = s; |
| } |
| break; |
| case kpidZerosTailIsAllowed: if (!item.IsRealSect) prop = true; break; |
| } |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection) |
| { |
| thereIsSection = false; |
| const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug]; |
| if (debugLink.Size == 0) |
| return S_OK; |
| const unsigned kEntrySize = 28; |
| UInt32 numItems = debugLink.Size / kEntrySize; |
| if (numItems > 16) |
| return S_FALSE; |
| |
| // MAC's EFI file: numItems can be incorrect. Only first CDebugEntry entry is correct. |
| // debugLink.Size = kEntrySize + some_data, pointed by entry[0]. |
| if (numItems * kEntrySize != debugLink.Size) |
| { |
| // return S_FALSE; |
| if (numItems > 1) |
| numItems = 1; |
| } |
| |
| UInt64 pa = 0; |
| unsigned i; |
| for (i = 0; i < _sections.Size(); i++) |
| { |
| const CSection § = _sections[i]; |
| if (sect.Va <= debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize) |
| { |
| pa = sect.Pa + (debugLink.Va - sect.Va); |
| break; |
| } |
| } |
| if (i == _sections.Size()) |
| { |
| // Exe for ARM requires S_OK |
| // return S_FALSE; |
| return S_OK; |
| } |
| |
| CByteBuffer buffer(debugLink.Size); |
| Byte *buf = buffer; |
| |
| RINOK(InStream_SeekSet(stream, pa)) |
| RINOK(ReadStream_FALSE(stream, buf, debugLink.Size)) |
| |
| for (i = 0; i < numItems; i++) |
| { |
| CDebugEntry de; |
| de.Parse(buf); |
| |
| if (de.Size == 0) |
| continue; |
| |
| UInt32 totalSize = de.Pa + de.Size; |
| if (totalSize > _totalSize) |
| { |
| _totalSize = totalSize; |
| thereIsSection = true; |
| |
| CSection § = _sections.AddNew(); |
| sect.Name = ".debug"; |
| sect.Name.Add_UInt32(i); |
| sect.IsDebug = true; |
| sect.Time = de.Time; |
| sect.Va = de.Va; |
| sect.Pa = de.Pa; |
| sect.PSize = sect.VSize = de.Size; |
| } |
| buf += kEntrySize; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items) |
| { |
| if ((offset & 3) != 0 || offset >= _buf.Size()) |
| return S_FALSE; |
| size_t rem = _buf.Size() - offset; |
| if (rem < 16) |
| return S_FALSE; |
| unsigned numNameItems = Get16(_buf + offset + 12); |
| unsigned numIdItems = Get16(_buf + offset + 14); |
| unsigned numItems = numNameItems + numIdItems; |
| if ((rem - 16) / 8 < numItems) |
| return S_FALSE; |
| if (!_usedRes.SetRange(offset, 16 + numItems * 8)) |
| return S_FALSE; |
| offset += 16; |
| items.ClearAndReserve(numItems); |
| for (unsigned i = 0; i < numItems; i++, offset += 8) |
| { |
| const Byte *buf = _buf + offset; |
| CTableItem item; |
| item.ID = Get32(buf + 0); |
| if ((bool)((item.ID & kFlag) != 0) != (bool)(i < numNameItems)) |
| return S_FALSE; |
| item.Offset = Get32(buf + 4); |
| items.AddInReserved(item); |
| } |
| return S_OK; |
| } |
| |
| static const UInt32 kFileSizeMax = (UInt32)1 << 31; |
| static const unsigned kNumResItemsMax = (unsigned)1 << 23; |
| static const unsigned kNumStringLangsMax = 256; |
| |
| // BITMAPINFOHEADER |
| struct CBitmapInfoHeader |
| { |
| // UInt32 HeaderSize; |
| UInt32 XSize; |
| Int32 YSize; |
| UInt16 Planes; |
| UInt16 BitCount; |
| UInt32 Compression; |
| UInt32 SizeImage; |
| |
| bool Parse(const Byte *p, size_t size); |
| }; |
| |
| static const UInt32 kBitmapInfoHeader_Size = 0x28; |
| |
| bool CBitmapInfoHeader::Parse(const Byte *p, size_t size) |
| { |
| if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size) |
| return false; |
| G32( 4, XSize); |
| G32_signed( 8, YSize); |
| G16(12, Planes); |
| G16(14, BitCount); |
| G32(16, Compression); |
| G32(20, SizeImage); |
| return true; |
| } |
| |
| static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount) |
| { |
| return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize; |
| } |
| |
| static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size) |
| { |
| CBitmapInfoHeader h; |
| if (!h.Parse(src, size)) |
| return 0; |
| if (h.YSize < 0) |
| h.YSize = -h.YSize; |
| if (h.XSize > (1 << 26) |
| || h.YSize > (1 << 26) |
| || h.YSize < 0 |
| || h.Planes != 1 || h.BitCount > 32) |
| return 0; |
| if (h.SizeImage == 0) |
| { |
| if (h.Compression != 0) // BI_RGB |
| return 0; |
| h.SizeImage = GetImageSize(h.XSize, (UInt32)h.YSize, h.BitCount); |
| } |
| UInt32 totalSize = kBmpHeaderSize + size; |
| UInt32 offBits = totalSize - h.SizeImage; |
| // BITMAPFILEHEADER |
| SetUi16(dest, 0x4D42) |
| SetUi32(dest + 2, totalSize) |
| SetUi32(dest + 6, 0) |
| SetUi32(dest + 10, offBits) |
| return kBmpHeaderSize; |
| } |
| |
| static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size) |
| { |
| CBitmapInfoHeader h; |
| if (!h.Parse(src, size)) |
| return 0; |
| if (h.YSize < 0) |
| h.YSize = -h.YSize; |
| if (h.XSize > (1 << 26) |
| || h.YSize > (1 << 26) |
| || h.YSize < 0 |
| || h.Planes != 1 |
| || h.Compression != 0) // BI_RGB |
| return 0; |
| |
| const UInt32 numBitCount = h.BitCount; |
| if (numBitCount != 1 && |
| numBitCount != 4 && |
| numBitCount != 8 && |
| numBitCount != 24 && |
| numBitCount != 32) |
| return 0; |
| |
| if ((h.YSize & 1) != 0) |
| return 0; |
| h.YSize /= 2; |
| if (h.XSize > 0x100 || h.YSize > 0x100) |
| return 0; |
| |
| UInt32 imageSize; |
| // imageSize is not correct if AND mask array contains zeros |
| // in this case it is equal image1Size |
| |
| // UInt32 imageSize = h.SizeImage; |
| // if (imageSize == 0) |
| // { |
| const UInt32 image1Size = GetImageSize(h.XSize, (UInt32)h.YSize, h.BitCount); |
| const UInt32 image2Size = GetImageSize(h.XSize, (UInt32)h.YSize, 1); |
| imageSize = image1Size + image2Size; |
| // } |
| UInt32 numColors = 0; |
| if (numBitCount < 16) |
| numColors = 1 << numBitCount; |
| |
| SetUi16(dest, 0) // Reserved |
| SetUi16(dest + 2, 1) // RES_ICON |
| SetUi16(dest + 4, 1) // ResCount |
| |
| dest[6] = (Byte)h.XSize; // Width |
| dest[7] = (Byte)h.YSize; // Height |
| dest[8] = (Byte)numColors; // ColorCount |
| dest[9] = 0; // Reserved |
| |
| SetUi32(dest + 10, 0) // Reserved1 / Reserved2 |
| |
| UInt32 numQuadsBytes = numColors * 4; |
| UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize; |
| SetUi32(dest + 14, BytesInRes) |
| SetUi32(dest + 18, kIconHeaderSize) |
| |
| /* |
| Description = DWORDToString(xSize) + |
| kDelimiterChar + DWORDToString(ySize) + |
| kDelimiterChar + DWORDToString(numBitCount); |
| */ |
| return kIconHeaderSize; |
| } |
| |
| bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size) |
| { |
| if ((size & 1) != 0) |
| return false; |
| |
| unsigned i; |
| for (i = 0; i < _strings.Size(); i++) |
| if (_strings[i].Lang == lang) |
| break; |
| if (i == _strings.Size()) |
| { |
| if (_strings.Size() >= kNumStringLangsMax) |
| return false; |
| CStringItem &item = _strings.AddNew(); |
| item.Lang = lang; |
| } |
| |
| CStringItem &item = _strings[i]; |
| id = (id - 1) << 4; |
| UInt32 pos = 0; |
| for (i = 0; i < 16; i++) |
| { |
| if (size - pos < 2) |
| return false; |
| UInt32 len = Get16(src + pos); |
| pos += 2; |
| if (len != 0) |
| { |
| if (size - pos < len * 2) |
| return false; |
| char temp[32]; |
| ConvertUInt32ToString(id + i, temp); |
| size_t tempLen = strlen(temp); |
| size_t j; |
| for (j = 0; j < tempLen; j++) |
| item.AddChar(temp[j]); |
| item.AddChar('\t'); |
| for (j = 0; j < len; j++, pos += 2) |
| item.AddWChar_Smart(Get16(src + pos)); |
| item.NewLine(); |
| } |
| } |
| if (size == pos) |
| return true; |
| |
| // Some rare case files have additional ZERO. |
| if (size == pos + 2 && Get16(src + pos) == 0) |
| return true; |
| |
| return false; |
| } |
| |
| |
| // ---------- VERSION ---------- |
| |
| static const UInt32 kMy_VS_FFI_SIGNATURE = 0xFEEF04BD; |
| |
| struct CMy_VS_FIXEDFILEINFO |
| { |
| // UInt32 Signature; |
| // UInt32 StrucVersion; |
| UInt32 VersionMS; |
| UInt32 VersionLS; |
| UInt32 ProductVersionMS; |
| UInt32 ProductVersionLS; |
| UInt32 FlagsMask; |
| UInt32 Flags; |
| UInt32 OS; |
| UInt32 Type; |
| UInt32 Subtype; |
| UInt32 DateMS; |
| UInt32 DateLS; |
| |
| bool Parse(const Byte *p); |
| void PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys); |
| }; |
| |
| bool CMy_VS_FIXEDFILEINFO::Parse(const Byte *p) |
| { |
| if (Get32(p) != kMy_VS_FFI_SIGNATURE) // signature; |
| return false; |
| // G32(0x04, StrucVersion); |
| G32(0x08, VersionMS); |
| G32(0x0C, VersionLS); |
| G32(0x10, ProductVersionMS); |
| G32(0x14, ProductVersionLS); |
| G32(0x18, FlagsMask); |
| G32(0x1C, Flags); |
| G32(0x20, OS); |
| G32(0x24, Type); |
| G32(0x28, Subtype); |
| G32(0x2C, DateMS); |
| G32(0x40, DateLS); |
| return true; |
| } |
| |
| static void PrintUInt32(CTextFile &f, UInt32 v) |
| { |
| char s[16]; |
| ConvertUInt32ToString(v, s); |
| f.AddString(s); |
| } |
| |
| static inline void PrintUInt32(UString &dest, UInt32 v) |
| { |
| dest.Add_UInt32(v); |
| } |
| |
| static void PrintHex(CTextFile &f, UInt32 val) |
| { |
| char temp[16]; |
| temp[0] = '0'; |
| temp[1] = 'x'; |
| ConvertUInt32ToHex(val, temp + 2); |
| f.AddString(temp); |
| } |
| |
| static void PrintVersion(CTextFile &f, UInt32 ms, UInt32 ls) |
| { |
| PrintUInt32(f, HIWORD(ms)); f.AddChar(','); |
| PrintUInt32(f, LOWORD(ms)); f.AddChar(','); |
| PrintUInt32(f, HIWORD(ls)); f.AddChar(','); |
| PrintUInt32(f, LOWORD(ls)); |
| } |
| |
| static void PrintVersion(UString &s, UInt32 ms, UInt32 ls) |
| { |
| PrintUInt32(s, HIWORD(ms)); s.Add_Dot(); |
| PrintUInt32(s, LOWORD(ms)); s.Add_Dot(); |
| PrintUInt32(s, HIWORD(ls)); s.Add_Dot(); |
| PrintUInt32(s, LOWORD(ls)); |
| } |
| |
| static const char * const k_VS_FileFlags[] = |
| { |
| "DEBUG" |
| , "PRERELEASE" |
| , "PATCHED" |
| , "PRIVATEBUILD" |
| , "INFOINFERRED" |
| , "SPECIALBUILD" |
| }; |
| |
| static const CUInt32PCharPair k_VS_FileOS[] = |
| { |
| { 0x10001, "VOS_DOS_WINDOWS16" }, |
| { 0x10004, "VOS_DOS_WINDOWS32" }, |
| { 0x20002, "VOS_OS216_PM16" }, |
| { 0x30003, "VOS_OS232_PM32" }, |
| { 0x40004, "VOS_NT_WINDOWS32" } |
| }; |
| |
| static const char * const k_VS_FileOS_High[] = |
| { |
| "VOS_UNKNOWN" |
| , "VOS_DOS" |
| , "VOS_OS216" |
| , "VOS_OS232" |
| , "VOS_NT" |
| , "VOS_WINCE" |
| }; |
| |
| static const UInt32 kMY_VFT_DRV = 3; |
| static const UInt32 kMY_VFT_FONT = 4; |
| |
| static const char * const k_VS_FileOS_Low[] = |
| { |
| "VOS__BASE" |
| , "VOS__WINDOWS16" |
| , "VOS__PM16" |
| , "VOS__PM32" |
| , "VOS__WINDOWS32" |
| }; |
| |
| static const char * const k_VS_FileType[] = |
| { |
| "VFT_UNKNOWN" |
| , "VFT_APP" |
| , "VFT_DLL" |
| , "VFT_DRV" |
| , "VFT_FONT" |
| , "VFT_VXD" |
| , "0x6" |
| , "VFT_STATIC_LIB" |
| }; |
| |
| // Subtype for VFT_DRV Type |
| static const char * const k_VS_FileSubType_DRV[] = |
| { |
| "0" |
| , "PRINTER" |
| , "KEYBOARD" |
| , "LANGUAGE" |
| , "DISPLAY" |
| , "MOUSE" |
| , "NETWORK" |
| , "SYSTEM" |
| , "INSTALLABLE" |
| , "SOUND" |
| , "COMM" |
| , "INPUTMETHOD" |
| , "VERSIONED_PRINTER" |
| }; |
| |
| // Subtype for VFT_FONT Type |
| static const char * const k_VS_FileSubType_FONT[] = |
| { |
| "0" |
| , "VFT2_FONT_RASTER" |
| , "VFT2_FONT_VECTOR" |
| , "VFT2_FONT_TRUETYPE" |
| }; |
| |
| static int FindKey(CObjectVector<CStringKeyValue> &v, const char *key) |
| { |
| FOR_VECTOR (i, v) |
| if (v[i].Key.IsEqualTo(key)) |
| return (int)i; |
| return -1; |
| } |
| |
| static void AddToUniqueUStringVector(CObjectVector<CStringKeyValue> &v, const UString &key, const UString &value) |
| { |
| bool needInsert = false; |
| unsigned i; |
| for (i = 0; i < v.Size(); i++) |
| { |
| if (v[i].Key == key) |
| { |
| if (v[i].Value == value) |
| return; |
| needInsert = true; |
| } |
| else if (needInsert) |
| break; |
| } |
| CStringKeyValue &pair = v.InsertNew(i); |
| pair.Key = key; |
| pair.Value = value; |
| } |
| |
| void CMy_VS_FIXEDFILEINFO::PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys) |
| { |
| f.AddString("FILEVERSION "); |
| PrintVersion(f, VersionMS, VersionLS); |
| f.NewLine(); |
| |
| f.AddString("PRODUCTVERSION "); |
| PrintVersion(f, ProductVersionMS, ProductVersionLS); |
| f.NewLine(); |
| |
| { |
| UString s; |
| PrintVersion(s, VersionMS, VersionLS); |
| AddToUniqueUStringVector(keys, L"FileVersion", s); |
| } |
| { |
| UString s; |
| PrintVersion(s, ProductVersionMS, ProductVersionLS); |
| AddToUniqueUStringVector(keys, L"ProductVersion", s); |
| } |
| |
| f.AddString("FILEFLAGSMASK "); |
| PrintHex(f, FlagsMask); |
| f.NewLine(); |
| |
| f.AddString("FILEFLAGS "); |
| { |
| bool wasPrinted = false; |
| for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_VS_FileFlags); i++) |
| { |
| if ((Flags & ((UInt32)1 << i)) != 0) |
| { |
| if (wasPrinted) |
| f.AddString(" | "); |
| f.AddString("VS_FF_"); |
| f.AddString(k_VS_FileFlags[i]); |
| wasPrinted = true; |
| } |
| } |
| UInt32 v = Flags & ~(((UInt32)1 << Z7_ARRAY_SIZE(k_VS_FileFlags)) - 1); |
| if (v != 0 || !wasPrinted) |
| { |
| if (wasPrinted) |
| f.AddString(" | "); |
| PrintHex(f, v); |
| } |
| } |
| f.NewLine(); |
| |
| // OS = 0x111230; |
| f.AddString("FILEOS "); |
| unsigned i; |
| for (i = 0; i < Z7_ARRAY_SIZE(k_VS_FileOS); i++) |
| { |
| const CUInt32PCharPair &pair = k_VS_FileOS[i]; |
| if (OS == pair.Value) |
| { |
| // continue; |
| // f.AddString("VOS_"); |
| f.AddString(pair.Name); |
| break; |
| } |
| } |
| if (i == Z7_ARRAY_SIZE(k_VS_FileOS)) |
| { |
| UInt32 high = OS >> 16; |
| if (high < Z7_ARRAY_SIZE(k_VS_FileOS_High)) |
| f.AddString(k_VS_FileOS_High[high]); |
| else |
| PrintHex(f, high << 16); |
| UInt32 low = OS & 0xFFFF; |
| if (low != 0) |
| { |
| f.AddString(" | "); |
| if (low < Z7_ARRAY_SIZE(k_VS_FileOS_Low)) |
| f.AddString(k_VS_FileOS_Low[low]); |
| else |
| PrintHex(f, low); |
| } |
| } |
| f.NewLine(); |
| |
| f.AddString("FILETYPE "); |
| if (Type < Z7_ARRAY_SIZE(k_VS_FileType)) |
| f.AddString(k_VS_FileType[Type]); |
| else |
| PrintHex(f, Type); |
| f.NewLine(); |
| |
| f.AddString("FILESUBTYPE "); |
| bool needPrintSubType = true; |
| if (Type == kMY_VFT_DRV) |
| { |
| if (Subtype != 0 && Subtype < Z7_ARRAY_SIZE(k_VS_FileSubType_DRV)) |
| { |
| f.AddString("VFT2_DRV_"); |
| f.AddString(k_VS_FileSubType_DRV[Subtype]); |
| needPrintSubType = false; |
| } |
| } |
| else if (Type == kMY_VFT_FONT) |
| { |
| if (Subtype != 0 && Subtype < Z7_ARRAY_SIZE(k_VS_FileSubType_FONT)) |
| { |
| f.AddString(k_VS_FileSubType_FONT[Subtype]); |
| needPrintSubType = false; |
| } |
| } |
| if (needPrintSubType) |
| PrintHex(f, Subtype); |
| f.NewLine(); |
| } |
| |
| static void CopyToUString(const Byte *p, UString &s) |
| { |
| for (;;) |
| { |
| wchar_t c = (wchar_t)Get16(p); |
| p += 2; |
| if (c == 0) |
| return; |
| s += c; |
| } |
| } |
| |
| static bool CompareWStrStrings(const Byte *p, const char *s) |
| { |
| unsigned pos = 0; |
| for (;;) |
| { |
| const Byte c = (Byte)*s++; |
| if (Get16(p + pos) != c) |
| return false; |
| pos += 2; |
| if (c == 0) |
| return true; |
| } |
| } |
| |
| struct CVersionBlock |
| { |
| UInt32 TotalLen; |
| UInt32 ValueLen; |
| bool IsTextValue; |
| unsigned StrSize; |
| |
| bool Parse(const Byte *p, UInt32 size); |
| }; |
| |
| static int Get_Utf16Str_Len_InBytes(const Byte *p, size_t size) |
| { |
| unsigned pos = 0; |
| for (;;) |
| { |
| if (pos + 1 >= size) |
| return -1; |
| if (Get16(p + pos) == 0) |
| return (int)pos; |
| pos += 2; |
| } |
| } |
| |
| static const unsigned k_ResoureBlockHeader_Size = 6; |
| |
| bool CVersionBlock::Parse(const Byte *p, UInt32 size) |
| { |
| if (size < k_ResoureBlockHeader_Size) |
| return false; |
| TotalLen = Get16(p); |
| ValueLen = Get16(p + 2); |
| if (TotalLen < k_ResoureBlockHeader_Size || TotalLen > size) |
| return false; |
| switch (Get16(p + 4)) |
| { |
| case 0: IsTextValue = false; break; |
| case 1: IsTextValue = true; break; |
| default: return false; |
| } |
| StrSize = 0; |
| const int t = Get_Utf16Str_Len_InBytes(p + k_ResoureBlockHeader_Size, TotalLen - k_ResoureBlockHeader_Size); |
| if (t < 0) |
| return false; |
| StrSize = (unsigned)t; |
| return true; |
| } |
| |
| static void AddParamString(CTextFile &f, const Byte *p, size_t sLen) |
| { |
| f.AddChar(' '); |
| f.AddChar('\"'); |
| f.AddBytes(p, sLen); |
| f.AddChar('\"'); |
| } |
| |
| static bool ParseVersion(const Byte *p, UInt32 size, CTextFile &f, CObjectVector<CStringKeyValue> &keys) |
| { |
| UInt32 pos; |
| { |
| const unsigned k_sizeof_VS_FIXEDFILEINFO = 13 * 4; |
| |
| CVersionBlock vb; |
| if (!vb.Parse(p, size)) |
| return false; |
| if (vb.ValueLen != k_sizeof_VS_FIXEDFILEINFO) // maybe 0 is allowed here? |
| return false; |
| if (vb.IsTextValue) |
| return false; |
| pos = k_ResoureBlockHeader_Size; |
| if (!CompareWStrStrings(p + pos, "VS_VERSION_INFO")) |
| return false; |
| pos += vb.StrSize + 2; |
| pos += (4 - pos) & 3; |
| if (pos + vb.ValueLen > vb.TotalLen) |
| return false; |
| /* sometimes resource contains zeros in remainder. |
| So we don't check that size != vb.TotalLen |
| // if (size != vb.TotalLen) return false; |
| */ |
| if (size > vb.TotalLen) |
| size = vb.TotalLen; |
| CMy_VS_FIXEDFILEINFO FixedFileInfo; |
| if (!FixedFileInfo.Parse(p + pos)) |
| return false; |
| FixedFileInfo.PrintToTextFile(f, keys); |
| pos += vb.ValueLen; |
| } |
| |
| f.OpenBlock(0); |
| |
| for (;;) |
| { |
| pos += (4 - pos) & 3; |
| if (pos >= size) |
| break; |
| |
| CVersionBlock vb; |
| if (!vb.Parse(p + pos, size - pos)) |
| return false; |
| if (vb.ValueLen != 0) |
| return false; |
| UInt32 endPos = pos + vb.TotalLen; |
| pos += k_ResoureBlockHeader_Size; |
| |
| f.AddSpaces(2); |
| f.AddString("BLOCK"); |
| AddParamString(f, p + pos, vb.StrSize); |
| |
| f.NewLine(); |
| f.OpenBlock(2); |
| |
| if (CompareWStrStrings(p + pos, "VarFileInfo")) |
| { |
| pos += vb.StrSize + 2; |
| for (;;) |
| { |
| pos += (4 - pos) & 3; |
| if (pos >= endPos) |
| break; |
| CVersionBlock vb2; |
| if (!vb2.Parse(p + pos, endPos - pos)) |
| return false; |
| UInt32 endPos2 = pos + vb2.TotalLen; |
| if (vb2.IsTextValue) |
| return false; |
| pos += k_ResoureBlockHeader_Size; |
| f.AddSpaces(4); |
| f.AddString("VALUE"); |
| AddParamString(f, p + pos, vb2.StrSize); |
| if (!CompareWStrStrings(p + pos, "Translation")) |
| return false; |
| pos += vb2.StrSize + 2; |
| pos += (4 - pos) & 3; |
| if (pos + vb2.ValueLen != endPos2) |
| return false; |
| if ((vb2.ValueLen & 3) != 0) |
| return false; |
| UInt32 num = (vb2.ValueLen >> 2); |
| for (; num != 0; num--, pos += 4) |
| { |
| UInt32 dw = Get32(p + pos); |
| UInt32 lang = LOWORD(dw); |
| UInt32 codePage = HIWORD(dw); |
| |
| f.AddString(", "); |
| PrintHex(f, lang); |
| f.AddString(", "); |
| PrintUInt32(f, codePage); |
| } |
| f.NewLine(); |
| } |
| } |
| else |
| { |
| if (!CompareWStrStrings(p + pos, "StringFileInfo")) |
| return false; |
| pos += vb.StrSize + 2; |
| |
| for (;;) |
| { |
| pos += (4 - pos) & 3; |
| if (pos >= endPos) |
| break; |
| CVersionBlock vb2; |
| if (!vb2.Parse(p + pos, endPos - pos)) |
| return false; |
| UInt32 endPos2 = pos + vb2.TotalLen; |
| if (vb2.ValueLen != 0) |
| return false; |
| pos += k_ResoureBlockHeader_Size; |
| |
| f.AddSpaces(4); |
| f.AddString("BLOCK"); |
| AddParamString(f, p + pos, vb2.StrSize); |
| pos += vb2.StrSize + 2; |
| |
| f.NewLine(); |
| f.OpenBlock(4); |
| |
| for (;;) |
| { |
| pos += (4 - pos) & 3; |
| if (pos >= endPos2) |
| break; |
| |
| CVersionBlock vb3; |
| if (!vb3.Parse(p + pos, endPos2 - pos)) |
| return false; |
| // ValueLen sometimes is a number of characters (not bytes)? |
| // So we don't use it. |
| UInt32 endPos3 = pos + vb3.TotalLen; |
| pos += k_ResoureBlockHeader_Size; |
| |
| // we don't write string if it's not text |
| if (vb3.IsTextValue) |
| { |
| f.AddSpaces(6); |
| f.AddString("VALUE"); |
| AddParamString(f, p + pos, vb3.StrSize); |
| UString key; |
| UString value; |
| CopyToUString(p + pos, key); |
| pos += vb3.StrSize + 2; |
| |
| pos += (4 - pos) & 3; |
| if (vb3.ValueLen > 0 && pos + 2 <= endPos3) |
| { |
| f.AddChar(','); |
| f.AddSpaces((34 - (int)vb3.StrSize) / 2); |
| const int sLen = Get_Utf16Str_Len_InBytes(p + pos, endPos3 - pos); |
| if (sLen < 0) |
| return false; |
| AddParamString(f, p + pos, (unsigned)sLen); |
| CopyToUString(p + pos, value); |
| pos += (unsigned)sLen + 2; |
| } |
| AddToUniqueUStringVector(keys, key, value); |
| } |
| pos = endPos3; |
| f.NewLine(); |
| } |
| f.CloseBlock(4); |
| } |
| } |
| f.CloseBlock(2); |
| } |
| |
| f.CloseBlock(0); |
| return true; |
| } |
| |
| |
| HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchiveOpenCallback *callback) |
| { |
| const CSection § = _sections[sectionIndex]; |
| size_t fileSize = sect.PSize; |
| { |
| size_t fileSizeMin = sect.PSize; |
| |
| if (sect.VSize < sect.PSize) |
| { |
| fileSize = fileSizeMin = sect.VSize; |
| const int numBits = _optHeader.GetNumFileAlignBits(); |
| if (numBits > 0) |
| { |
| const UInt32 mask = ((UInt32)1 << numBits) - 1; |
| const size_t end = (size_t)((sect.VSize + mask) & (UInt32)~mask); |
| if (end > sect.VSize) |
| { |
| if (end <= sect.PSize) |
| fileSize = end; |
| else |
| fileSize = sect.PSize; |
| } |
| } |
| } |
| |
| if (fileSize > kFileSizeMax) |
| return S_FALSE; |
| |
| { |
| const UInt64 fileSize64 = fileSize; |
| if (callback) |
| RINOK(callback->SetTotal(NULL, &fileSize64)) |
| } |
| |
| RINOK(InStream_SeekSet(stream, sect.Pa)) |
| |
| _buf.Alloc(fileSize); |
| |
| size_t pos; |
| |
| for (pos = 0; pos < fileSize;) |
| { |
| { |
| const UInt64 offset64 = pos; |
| if (callback) |
| RINOK(callback->SetCompleted(NULL, &offset64)) |
| } |
| size_t rem = MyMin(fileSize - pos, (size_t)(1 << 22)); |
| RINOK(ReadStream(stream, _buf + pos, &rem)) |
| if (rem == 0) |
| { |
| if (pos < fileSizeMin) |
| return S_FALSE; |
| break; |
| } |
| pos += rem; |
| } |
| |
| if (pos < fileSize) |
| memset(_buf + pos, 0, fileSize - pos); |
| } |
| |
| _usedRes.Alloc(fileSize); |
| CRecordVector<CTableItem> specItems; |
| RINOK(ReadTable(0, specItems)) |
| |
| _oneLang = true; |
| bool stringsOk = true; |
| size_t maxOffset = 0; |
| |
| FOR_VECTOR (i, specItems) |
| { |
| const CTableItem &item1 = specItems[i]; |
| if ((item1.Offset & kFlag) == 0) |
| return S_FALSE; |
| |
| CRecordVector<CTableItem> specItems2; |
| RINOK(ReadTable(item1.Offset & kMask, specItems2)) |
| |
| FOR_VECTOR (j, specItems2) |
| { |
| const CTableItem &item2 = specItems2[j]; |
| if ((item2.Offset & kFlag) == 0) |
| return S_FALSE; |
| |
| CRecordVector<CTableItem> specItems3; |
| RINOK(ReadTable(item2.Offset & kMask, specItems3)) |
| |
| CResItem item; |
| item.Type = item1.ID; |
| item.ID = item2.ID; |
| |
| FOR_VECTOR (k, specItems3) |
| { |
| if (_items.Size() >= kNumResItemsMax) |
| return S_FALSE; |
| const CTableItem &item3 = specItems3[k]; |
| if ((item3.Offset & kFlag) != 0) |
| return S_FALSE; |
| if (item3.Offset >= _buf.Size() || _buf.Size() - item3.Offset < 16) |
| return S_FALSE; |
| const Byte *buf = _buf + item3.Offset; |
| item.Lang = item3.ID; |
| item.Offset = Get32(buf + 0); |
| item.Size = Get32(buf + 4); |
| // UInt32 codePage = Get32(buf + 8); |
| if (Get32(buf + 12) != 0) |
| return S_FALSE; |
| if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back())) |
| _oneLang = false; |
| |
| item.HeaderSize = 0; |
| |
| size_t offset = item.Offset - sect.Va; |
| if (offset > maxOffset) |
| maxOffset = offset; |
| if (offset + item.Size > maxOffset) |
| maxOffset = offset + item.Size; |
| |
| if (CheckItem(sect, item, offset)) |
| { |
| const Byte *data = _buf + offset; |
| if (item.IsBmp()) |
| item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size); |
| else if (item.IsIcon()) |
| item.HeaderSize = SetIconHeader(item.Header, data, item.Size); |
| else if (item.IsString()) |
| { |
| if (stringsOk) |
| stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size); |
| } |
| } |
| |
| if (item.IsVersion()) |
| { |
| if (offset > _buf.Size() || _buf.Size() - offset < item.Size) |
| continue; |
| CTextFile f; |
| if (ParseVersion((const Byte *)_buf + offset, item.Size, f, _versionKeys)) |
| { |
| CMixItem mixItem; |
| mixItem.VersionIndex = (int)_versionFiles.Size(); |
| mixItem.SectionIndex = (int)sectionIndex; // check it !!!! |
| CByteBuffer_WithLang &vf = _versionFiles.AddNew(); |
| vf.Lang = item.Lang; |
| vf.CopyFrom(f.Buf, f.Buf.GetPos()); |
| _mixItems.Add(mixItem); |
| continue; |
| } |
| // PrintError("ver.Parse error"); |
| } |
| |
| item.Enabled = true; |
| _items.Add(item); |
| } |
| } |
| } |
| |
| if (stringsOk && !_strings.IsEmpty()) |
| { |
| unsigned i; |
| for (i = 0; i < _items.Size(); i++) |
| { |
| CResItem &item = _items[i]; |
| if (item.IsString()) |
| item.Enabled = false; |
| } |
| for (i = 0; i < _strings.Size(); i++) |
| { |
| if (_strings[i].FinalSize() == 0) |
| continue; |
| CMixItem mixItem; |
| mixItem.StringIndex = (int)i; |
| mixItem.SectionIndex = (int)sectionIndex; |
| _mixItems.Add(mixItem); |
| } |
| } |
| |
| _usedRes.Free(); |
| |
| { |
| // PSize can be much larger than VSize in some exe installers. |
| // it contains archive data after PE resources. |
| // So we need to use PSize here! |
| if (maxOffset < sect.PSize) |
| { |
| size_t end = fileSize; |
| |
| // we skip Zeros to start of aligned block |
| size_t i; |
| for (i = maxOffset; i < end; i++) |
| if (_buf[i] != 0) |
| break; |
| if (i == end) |
| maxOffset = end; |
| |
| CSection sect2; |
| sect2.Flags = 0; |
| sect2.Pa = sect.Pa + (UInt32)maxOffset; |
| sect2.Va = sect.Va + (UInt32)maxOffset; |
| |
| // 9.29: we use sect.PSize instead of sect.VSize to support some CAB-SFX |
| // the code for .rsrc_2 is commented. |
| sect2.PSize = sect.PSize - (UInt32)maxOffset; |
| |
| if (sect2.PSize != 0) |
| { |
| sect2.VSize = sect2.PSize; |
| sect2.Name = ".rsrc_1"; |
| sect2.Time = 0; |
| sect2.IsAdditionalSection = true; |
| _sections.Add(sect2); |
| } |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| |
| bool CHeader::ParseCoff(const Byte *p) |
| { |
| ParseBase(p); |
| if (PointerToSymbolTable < kCoffHeaderSize) |
| return false; |
| if (NumSymbols >= (1 << 24)) |
| return false; |
| if (OptHeaderSize != 0 && OptHeaderSize < k_OptHeader32_Size_MIN) |
| return false; |
| |
| // 18.04: we reduce false detections |
| if (NumSections == 0 && OptHeaderSize == 0) |
| return false; |
| |
| for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_MachinePairs); i++) |
| if (Machine == g_MachinePairs[i].Value) |
| return true; |
| if (Machine == 0) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static inline bool CheckPeOffset(UInt32 pe) |
| { |
| // ((pe & 7) == 0) is for most PE files. But there is unusual EFI-PE file that uses unaligned pe value. |
| return pe >= 0x40 && pe <= 0x1000 /* && (pe & 7) == 0 */ ; |
| } |
| |
| static const unsigned kStartSize = 0x40; |
| |
| API_FUNC_static_IsArc IsArc_Pe(const Byte *p, size_t size) |
| { |
| if (size < 2) |
| return k_IsArc_Res_NEED_MORE; |
| if (p[0] != 'M' || p[1] != 'Z') |
| return k_IsArc_Res_NO; |
| if (size < kStartSize) |
| return k_IsArc_Res_NEED_MORE; |
| UInt32 pe = Get32(p + 0x3C); |
| if (!CheckPeOffset(pe)) |
| return k_IsArc_Res_NO; |
| if (pe + kPeHeaderSize > size) |
| return k_IsArc_Res_NEED_MORE; |
| CHeader header; |
| if (!header.ParsePe(p + pe)) |
| return k_IsArc_Res_NO; |
| return k_IsArc_Res_YES; |
| } |
| } |
| |
| HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) |
| { |
| UInt32 coffOffset = 0; |
| if (_coffMode) |
| { |
| Byte h[kCoffHeaderSize]; |
| RINOK(ReadStream_FALSE(stream, h, kCoffHeaderSize)) |
| if (!_header.ParseCoff(h)) |
| return S_FALSE; |
| } |
| else |
| { |
| UInt32 _peOffset; |
| { |
| Byte h[kStartSize]; |
| RINOK(ReadStream_FALSE(stream, h, kStartSize)) |
| if (h[0] != 'M' || h[1] != 'Z') |
| return S_FALSE; |
| /* most of PE files contain 0x0090 at offset 2. |
| But some rare PE files contain another values. So we don't use that check. |
| if (Get16(h + 2) != 0x90) return false; */ |
| _peOffset = Get32(h + 0x3C); |
| if (!CheckPeOffset(_peOffset)) |
| return S_FALSE; |
| coffOffset = _peOffset + 4; |
| } |
| { |
| Byte h[kPeHeaderSize]; |
| RINOK(InStream_SeekSet(stream, _peOffset)) |
| RINOK(ReadStream_FALSE(stream, h, kPeHeaderSize)) |
| if (!_header.ParsePe(h)) |
| return S_FALSE; |
| } |
| } |
| |
| const UInt32 optStart = coffOffset + kCoffHeaderSize; |
| const UInt32 bufSize = _header.OptHeaderSize + (UInt32)_header.NumSections * kSectionSize; |
| _totalSize = optStart + bufSize; |
| CByteBuffer buffer(bufSize); |
| |
| RINOK(ReadStream_FALSE(stream, buffer, bufSize)) |
| |
| // memset((void *)&_optHeader, 0, sizeof(_optHeader)); |
| if (_header.OptHeaderSize != 0) |
| if (!_optHeader.Parse(buffer, _header.OptHeaderSize)) |
| return S_FALSE; |
| |
| UInt32 pos = _header.OptHeaderSize; |
| unsigned i; |
| for (i = 0; i < _header.NumSections; i++, pos += kSectionSize) |
| { |
| CSection § = _sections.AddNew(); |
| sect.Parse(buffer + pos); |
| sect.IsRealSect = true; |
| |
| /* PE pre-file in .hxs file has errors: |
| PSize of resource is larger than real size. |
| So it overlaps next ".its" section. |
| 7-zip before 22.02: we corrected it. |
| |
| 22.02: another bad case is possible in incorrect pe (exe) file: |
| PSize in .rsrc section is correct, |
| but next .reloc section has incorrect (Pa) that overlaps with .rsrc. |
| */ |
| |
| if (i != 0) |
| { |
| const CSection &prev = _sections[i - 1]; |
| if (prev.Pa < sect.Pa |
| && prev.Pa + prev.PSize > sect.Pa |
| && sect.PSize != 0 |
| && prev.PSize != 0) |
| { |
| _sectionsError = true; |
| // PRF(printf("\n !!!! Section correction: %s\n ", prev.Name)); |
| |
| /* we corrected that case in 7-zip before 22.02: */ |
| // prev.PSize = sect.Pa - prev.Pa; |
| |
| /* 22.02: here we can try to change bad section position to expected postion. |
| but original Windows code probably will not do same things. */ |
| // if (prev.PSize <= sect.Va - prev.Va) sect.Pa = prev.Pa + prev.PSize; |
| } |
| } |
| /* last ".its" section in hxs file has incorrect sect.PSize. |
| 7-zip before 22.02: we reduced section to real sect.VSize */ |
| /* |
| if (sect.VSize == 24 && sect.PSize == 512 && i == (unsigned)_header.NumSections - 1) |
| sect.PSize = sect.VSize; |
| */ |
| } |
| |
| for (i = 0; i < _sections.Size(); i++) |
| _sections[i].UpdateTotalSize(_totalSize); |
| |
| bool thereISDebug = false; |
| if (IsOpt()) |
| { |
| RINOK(LoadDebugSections(stream, thereISDebug)) |
| |
| const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate]; |
| if (certLink.Size != 0) |
| { |
| CSection § = _sections.AddNew(); |
| sect.Name = "CERTIFICATE"; |
| sect.Va = 0; |
| sect.Pa = certLink.Va; |
| sect.PSize = sect.VSize = certLink.Size; |
| sect.UpdateTotalSize(_totalSize); |
| } |
| |
| if (thereISDebug) |
| { |
| /* sometime there is some data after debug section. |
| We don't see any reference in exe file to that data. |
| But we suppose that it's part of EXE file */ |
| |
| const UInt32 kAlign = 1 << 12; |
| UInt32 alignPos = _totalSize & (kAlign - 1); |
| if (alignPos != 0) |
| { |
| UInt32 size = kAlign - alignPos; |
| RINOK(InStream_SeekSet(stream, _totalSize)) |
| buffer.Alloc(kAlign); |
| Byte *buf = buffer; |
| size_t processed = size; |
| RINOK(ReadStream(stream, buf, &processed)) |
| |
| /* |
| if (processed != 0) |
| { |
| printf("\ndata after debug %d, %d \n", (int)size, (int)processed); |
| fflush(stdout); |
| } |
| */ |
| |
| size_t k; |
| for (k = 0; k < processed; k++) |
| if (buf[k] != 0) |
| break; |
| if (processed < size && processed < 100) |
| _totalSize += (UInt32)processed; |
| else if (((_totalSize + k) & 0x1FF) == 0 || processed < size) |
| _totalSize += (UInt32)k; |
| } |
| } |
| } |
| |
| if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= optStart) |
| { |
| if (_header.NumSymbols >= (1 << 24)) |
| return S_FALSE; |
| UInt32 size = _header.NumSymbols * 18; |
| RINOK(InStream_SeekSet(stream, (UInt64)_header.PointerToSymbolTable + size)) |
| Byte buf[4]; |
| RINOK(ReadStream_FALSE(stream, buf, 4)) |
| UInt32 size2 = Get32(buf); |
| if (size2 >= (1 << 28)) |
| return S_FALSE; |
| size += size2; |
| |
| CSection § = _sections.AddNew(); |
| sect.Name = "COFF_SYMBOLS"; |
| sect.Va = 0; |
| sect.Pa = _header.PointerToSymbolTable; |
| sect.PSize = sect.VSize = size; |
| sect.UpdateTotalSize(_totalSize); |
| } |
| |
| { |
| CObjectVector<CSection> sections = _sections; |
| sections.Sort(); |
| UInt32 limit = (1 << 12); |
| unsigned num = 0; |
| FOR_VECTOR (k, sections) |
| { |
| const CSection &s = sections[k]; |
| if (s.Pa > limit) |
| { |
| CSection &s2 = _sections.AddNew(); |
| s2.Pa = s2.Va = limit; |
| s2.PSize = s2.VSize = s.Pa - limit; |
| s2.IsAdditionalSection = true; |
| s2.Name = '['; |
| s2.Name.Add_UInt32(num++); |
| s2.Name += ']'; |
| limit = s.Pa; |
| } |
| UInt32 next = s.Pa + s.PSize; |
| if (next < s.Pa) |
| break; |
| if (next >= limit) |
| limit = next; |
| } |
| } |
| |
| |
| if (IsOpt()) |
| if (_optHeader.CheckSum != 0) |
| { |
| RINOK(InStream_SeekToBegin(stream)) |
| UInt32 checkSum = 0; |
| RINOK(CalcCheckSum(stream, _totalSize, optStart + k_CheckSum_Field_Offset, checkSum)) |
| _checksumError = (checkSum != _optHeader.CheckSum); |
| } |
| |
| |
| if (!_allowTail) |
| { |
| UInt64 fileSize; |
| RINOK(InStream_GetSize_SeekToEnd(stream, fileSize)) |
| if (fileSize > _totalSize) |
| return S_FALSE; |
| } |
| |
| bool _parseResources = true; |
| // _parseResources = false; // for debug |
| |
| UInt64 mainSize = 0, mainSize2 = 0; |
| |
| for (i = 0; i < _sections.Size(); i++) |
| { |
| const CSection § = _sections[i]; |
| if (IsOpt()) |
| if (_parseResources && sect.Name == ".rsrc") |
| { |
| // 20.01: we try to parse only first copy of .rsrc section. |
| _parseResources = false; |
| const unsigned numMixItems = _mixItems.Size(); |
| HRESULT res = OpenResources(i, stream, callback); |
| if (res == S_OK) |
| { |
| _resourcesPrefix = sect.Name.Ptr(); |
| _resourcesPrefix.Add_PathSepar(); |
| FOR_VECTOR (j, _items) |
| { |
| const CResItem &item = _items[j]; |
| if (item.Enabled) |
| { |
| CMixItem mixItem; |
| mixItem.SectionIndex = (int)i; |
| mixItem.ResourceIndex = (int)j; |
| if (item.IsRcDataOrUnknown()) |
| { |
| if (item.Size >= mainSize) |
| { |
| mainSize2 = mainSize; |
| mainSize = item.Size; |
| _mainSubfile = (Int32)(int)_mixItems.Size(); |
| } |
| else if (item.Size >= mainSize2) |
| mainSize2 = item.Size; |
| } |
| _mixItems.Add(mixItem); |
| } |
| } |
| // 9.29: .rsrc_2 code was commented. |
| // .rsrc_1 now must include that .rsrc_2 block. |
| /* |
| if (sect.PSize > sect.VSize) |
| { |
| int numBits = _optHeader.GetNumFileAlignBits(); |
| if (numBits >= 0) |
| { |
| UInt32 mask = (1 << numBits) - 1; |
| UInt32 end = ((sect.VSize + mask) & ~mask); |
| |
| if (sect.PSize > end) |
| { |
| CSection §2 = _sections.AddNew(); |
| sect2.Flags = 0; |
| sect2.Pa = sect.Pa + end; |
| sect2.Va = sect.Va + end; |
| sect2.PSize = sect.PSize - end; |
| sect2.VSize = sect2.PSize; |
| sect2.Name = ".rsrc_2"; |
| sect2.Time = 0; |
| sect2.IsAdditionalSection = true; |
| } |
| } |
| } |
| */ |
| continue; |
| } |
| if (res != S_FALSE) |
| return res; |
| _mixItems.DeleteFrom(numMixItems); |
| CloseResources(); |
| } |
| |
| if (sect.IsAdditionalSection) |
| { |
| if (sect.PSize >= mainSize) |
| { |
| mainSize2 = mainSize; |
| mainSize = sect.PSize; |
| _mainSubfile = (Int32)(int)_mixItems.Size(); |
| } |
| else if (sect.PSize >= mainSize2) |
| mainSize2 = sect.PSize; |
| } |
| |
| CMixItem mixItem; |
| mixItem.SectionIndex = (int)i; |
| _mixItems.Add(mixItem); |
| } |
| |
| if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2) |
| _mainSubfile = -1; |
| |
| for (i = 0; i < _mixItems.Size(); i++) |
| { |
| const CMixItem &mixItem = _mixItems[i]; |
| if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_") |
| { |
| _mainSubfile = (Int32)(int)i; |
| break; |
| } |
| } |
| |
| for (i = 0; i < _versionKeys.Size(); i++) |
| { |
| if (i != 0) |
| _versionFullString.Add_LF(); |
| const CStringKeyValue &k = _versionKeys[i]; |
| _versionFullString += k.Key; |
| _versionFullString += ": "; |
| _versionFullString += k.Value; |
| } |
| |
| { |
| int keyIndex = FindKey(_versionKeys, "OriginalFilename"); |
| if (keyIndex >= 0) |
| _originalFilename = _versionKeys[keyIndex].Value; |
| } |
| { |
| int keyIndex = FindKey(_versionKeys, "FileDescription"); |
| if (keyIndex >= 0) |
| _versionShortString = _versionKeys[keyIndex].Value; |
| } |
| { |
| int keyIndex = FindKey(_versionKeys, "FileVersion"); |
| if (keyIndex >= 0) |
| { |
| _versionShortString.Add_Space(); |
| _versionShortString += _versionKeys[keyIndex].Value; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)) |
| { |
| COM_TRY_BEGIN |
| Close(); |
| RINOK(Open2(inStream, callback)) |
| _stream = inStream; |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| void CHandler::CloseResources() |
| { |
| _usedRes.Free(); |
| _items.Clear(); |
| _strings.Clear(); |
| _versionFiles.Clear(); |
| _buf.Free(); |
| _versionFullString.Empty(); |
| _versionShortString.Empty(); |
| _originalFilename.Empty(); |
| _versionKeys.Clear(); |
| } |
| |
| Z7_COM7F_IMF(CHandler::Close()) |
| { |
| _totalSize = 0; |
| _checksumError = false; |
| _sectionsError = false; |
| _mainSubfile = -1; |
| |
| _stream.Release(); |
| _sections.Clear(); |
| _mixItems.Clear(); |
| CloseResources(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) |
| { |
| *numItems = _mixItems.Size(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, |
| Int32 testMode, IArchiveExtractCallback *extractCallback)) |
| { |
| COM_TRY_BEGIN |
| const bool allFilesMode = (numItems == (UInt32)(Int32)-1); |
| if (allFilesMode) |
| numItems = _mixItems.Size(); |
| if (numItems == 0) |
| return S_OK; |
| UInt64 totalSize = 0; |
| UInt32 i; |
| for (i = 0; i < numItems; i++) |
| { |
| const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]]; |
| UInt64 size; |
| if (mixItem.StringIndex >= 0) |
| size = _strings[mixItem.StringIndex].FinalSize(); |
| else if (mixItem.VersionIndex >= 0) |
| size = _versionFiles[mixItem.VersionIndex].Size(); |
| else if (mixItem.ResourceIndex >= 0) |
| size = _items[mixItem.ResourceIndex].GetSize(); |
| else |
| size = _sections[mixItem.SectionIndex].GetSizeExtract(); |
| totalSize += size; |
| } |
| extractCallback->SetTotal(totalSize); |
| |
| UInt64 currentTotalSize = 0; |
| UInt64 currentItemSize; |
| |
| NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); |
| CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; |
| |
| CLocalProgress *lps = new CLocalProgress; |
| CMyComPtr<ICompressProgressInfo> progress = lps; |
| lps->Init(extractCallback, false); |
| |
| CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; |
| CMyComPtr<ISequentialInStream> inStream(streamSpec); |
| streamSpec->SetStream(_stream); |
| |
| for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) |
| { |
| lps->InSize = lps->OutSize = currentTotalSize; |
| RINOK(lps->SetCur()) |
| const Int32 askMode = testMode ? |
| NExtract::NAskMode::kTest : |
| NExtract::NAskMode::kExtract; |
| const UInt32 index = allFilesMode ? i : indices[i]; |
| |
| CMyComPtr<ISequentialOutStream> outStream; |
| RINOK(extractCallback->GetStream(index, &outStream, askMode)) |
| const CMixItem &mixItem = _mixItems[index]; |
| |
| const CSection § = _sections[mixItem.SectionIndex]; |
| bool isOk = true; |
| if (mixItem.StringIndex >= 0) |
| { |
| const CStringItem &item = _strings[mixItem.StringIndex]; |
| currentItemSize = item.FinalSize(); |
| if (!testMode && !outStream) |
| continue; |
| |
| RINOK(extractCallback->PrepareOperation(askMode)) |
| if (outStream) |
| RINOK(WriteStream(outStream, item.Buf, item.FinalSize())) |
| } |
| else if (mixItem.VersionIndex >= 0) |
| { |
| const CByteBuffer &item = _versionFiles[mixItem.VersionIndex]; |
| currentItemSize = item.Size(); |
| if (!testMode && !outStream) |
| continue; |
| |
| RINOK(extractCallback->PrepareOperation(askMode)) |
| if (outStream) |
| RINOK(WriteStream(outStream, item, item.Size())) |
| } |
| else if (mixItem.ResourceIndex >= 0) |
| { |
| const CResItem &item = _items[mixItem.ResourceIndex]; |
| currentItemSize = item.GetSize(); |
| if (!testMode && !outStream) |
| continue; |
| |
| RINOK(extractCallback->PrepareOperation(askMode)) |
| size_t offset = item.Offset - sect.Va; |
| if (!CheckItem(sect, item, offset)) |
| isOk = false; |
| else if (outStream) |
| { |
| if (item.HeaderSize != 0) |
| RINOK(WriteStream(outStream, item.Header, item.HeaderSize)) |
| RINOK(WriteStream(outStream, _buf + offset, item.Size)) |
| } |
| } |
| else |
| { |
| currentItemSize = sect.GetSizeExtract(); |
| if (!testMode && !outStream) |
| continue; |
| |
| RINOK(extractCallback->PrepareOperation(askMode)) |
| RINOK(InStream_SeekSet(_stream, sect.Pa)) |
| streamSpec->Init(currentItemSize); |
| RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)) |
| isOk = (copyCoderSpec->TotalSize == currentItemSize); |
| } |
| |
| outStream.Release(); |
| RINOK(extractCallback->SetOperationResult(isOk ? |
| NExtract::NOperationResult::kOK : |
| NExtract::NOperationResult::kDataError)) |
| } |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) |
| { |
| COM_TRY_BEGIN |
| *stream = NULL; |
| |
| const CMixItem &mixItem = _mixItems[index]; |
| const CSection § = _sections[mixItem.SectionIndex]; |
| if (mixItem.IsSectionItem()) |
| return CreateLimitedInStream(_stream, sect.Pa, sect.PSize, stream); |
| |
| CBufInStream *inStreamSpec = new CBufInStream; |
| CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec; |
| CReferenceBuf *referenceBuf = new CReferenceBuf; |
| CMyComPtr<IUnknown> ref = referenceBuf; |
| if (mixItem.StringIndex >= 0) |
| { |
| const CStringItem &item = _strings[mixItem.StringIndex]; |
| referenceBuf->Buf.CopyFrom(item.Buf, item.FinalSize()); |
| } |
| else if (mixItem.VersionIndex >= 0) |
| { |
| const CByteBuffer &item = _versionFiles[mixItem.VersionIndex]; |
| referenceBuf->Buf.CopyFrom(item, item.Size()); |
| } |
| else |
| { |
| const CResItem &item = _items[mixItem.ResourceIndex]; |
| size_t offset = item.Offset - sect.Va; |
| if (!CheckItem(sect, item, offset)) |
| return S_FALSE; |
| if (item.HeaderSize == 0) |
| { |
| CBufInStream *streamSpec = new CBufInStream; |
| CMyComPtr<IInStream> streamTemp2 = streamSpec; |
| streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this); |
| *stream = streamTemp2.Detach(); |
| return S_OK; |
| } |
| referenceBuf->Buf.Alloc(item.HeaderSize + item.Size); |
| memcpy(referenceBuf->Buf, item.Header, item.HeaderSize); |
| if (item.Size != 0) |
| memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size); |
| } |
| inStreamSpec->Init(referenceBuf); |
| |
| *stream = streamTemp.Detach(); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::AllowTail(Int32 allowTail)) |
| { |
| _allowTail = IntToBool(allowTail); |
| return S_OK; |
| } |
| |
| static const Byte k_Signature[] = { 'M', 'Z' }; |
| |
| REGISTER_ARC_I( |
| "PE", "exe dll sys", NULL, 0xDD, |
| k_Signature, |
| 0, |
| NArcInfoFlags::kPreArc, |
| IsArc_Pe) |
| |
| } |
| |
| namespace NCoff { |
| |
| API_FUNC_static_IsArc IsArc_Coff(const Byte *p, size_t size) |
| { |
| if (size < NPe::kCoffHeaderSize) |
| return k_IsArc_Res_NEED_MORE; |
| NPe::CHeader header; |
| if (!header.ParseCoff(p)) |
| return k_IsArc_Res_NO; |
| return k_IsArc_Res_YES; |
| } |
| } |
| |
| /* |
| static const Byte k_Signature[] = |
| { |
| 2, 0x4C, 0x01, // x86 |
| 2, 0x64, 0x86, // x64 |
| 2, 0x64, 0xAA // ARM64 |
| }; |
| REGISTER_ARC_I_CLS( |
| */ |
| |
| REGISTER_ARC_I_CLS_NO_SIG( |
| NPe::CHandler(true), |
| "COFF", "obj", NULL, 0xC6, |
| // k_Signature, |
| 0, |
| // NArcInfoFlags::kMultiSignature | |
| NArcInfoFlags::kStartOpen, |
| IsArc_Coff) |
| } |
| |
| |
| namespace NTe { |
| |
| // Terse Executable (TE) image |
| |
| struct CDataDir |
| { |
| UInt32 Va; |
| UInt32 Size; |
| |
| void Parse(const Byte *p) |
| { |
| G32(0, Va); |
| G32(4, Size); |
| } |
| }; |
| |
| static const UInt32 kHeaderSize = 40; |
| |
| static bool FindValue(const CUInt32PCharPair *pairs, unsigned num, UInt32 value) |
| { |
| for (unsigned i = 0; i < num; i++) |
| if (pairs[i].Value == value) |
| return true; |
| return false; |
| } |
| |
| #define MY_FIND_VALUE(pairs, val) FindValue(pairs, Z7_ARRAY_SIZE(pairs), val) |
| #define MY_FIND_VALUE_2(strings, val) (val < Z7_ARRAY_SIZE(strings) && strings[val]) |
| |
| static const UInt32 kNumSection_MAX = 32; |
| |
| struct CHeader |
| { |
| UInt16 Machine; |
| Byte NumSections; |
| Byte SubSystem; |
| UInt16 StrippedSize; |
| /* |
| UInt32 AddressOfEntryPoint; |
| UInt32 BaseOfCode; |
| UInt64 ImageBase; |
| */ |
| CDataDir DataDir[2]; // base relocation and debug directory |
| |
| bool ConvertPa(UInt32 &pa) const |
| { |
| if (pa < StrippedSize) |
| return false; |
| pa = pa - StrippedSize + kHeaderSize; |
| return true; |
| } |
| bool Parse(const Byte *p); |
| }; |
| |
| bool CHeader::Parse(const Byte *p) |
| { |
| NumSections = p[4]; |
| if (NumSections > kNumSection_MAX) |
| return false; |
| SubSystem = p[5]; |
| G16(2, Machine); |
| G16(6, StrippedSize); |
| /* |
| G32(8, AddressOfEntryPoint); |
| G32(12, BaseOfCode); |
| G64(16, ImageBase); |
| */ |
| for (int i = 0; i < 2; i++) |
| { |
| CDataDir &dd = DataDir[i]; |
| dd.Parse(p + 24 + i * 8); |
| if (dd.Size >= ((UInt32)1 << 28)) |
| return false; |
| } |
| return |
| MY_FIND_VALUE(NPe::g_MachinePairs, Machine) && |
| MY_FIND_VALUE_2(NPe::g_SubSystems, SubSystem); |
| } |
| |
| API_FUNC_static_IsArc IsArc_Te(const Byte *p, size_t size) |
| { |
| if (size < 2) |
| return k_IsArc_Res_NEED_MORE; |
| if (p[0] != 'V' || p[1] != 'Z') |
| return k_IsArc_Res_NO; |
| if (size < kHeaderSize) |
| return k_IsArc_Res_NEED_MORE; |
| |
| CHeader h; |
| if (!h.Parse(p)) |
| return k_IsArc_Res_NO; |
| return k_IsArc_Res_YES; |
| } |
| } |
| |
| |
| struct CSection |
| { |
| Byte Name[NPe::kNameSize]; |
| |
| UInt32 VSize; |
| UInt32 Va; |
| UInt32 PSize; |
| UInt32 Pa; |
| UInt32 Flags; |
| // UInt16 NumRelocs; |
| |
| void Parse(const Byte *p) |
| { |
| memcpy(Name, p, NPe::kNameSize); |
| G32(8, VSize); |
| G32(12, Va); |
| G32(16, PSize); |
| G32(20, Pa); |
| // G32(p + 32, NumRelocs); |
| G32(36, Flags); |
| } |
| |
| bool Check() const |
| { |
| return |
| Pa <= ((UInt32)1 << 30) && |
| PSize <= ((UInt32)1 << 30); |
| } |
| |
| void UpdateTotalSize(UInt32 &totalSize) |
| { |
| UInt32 t = Pa + PSize; |
| if (t > totalSize) |
| totalSize = t; |
| } |
| }; |
| |
| |
| Z7_CLASS_IMP_CHandler_IInArchive_2( |
| IInArchiveGetStream, |
| IArchiveAllowTail |
| ) |
| CRecordVector<CSection> _items; |
| CMyComPtr<IInStream> _stream; |
| UInt32 _totalSize; |
| bool _allowTail; |
| CHeader _h; |
| |
| HRESULT Open2(IInStream *stream); |
| public: |
| CHandler(): _allowTail(false) {} |
| }; |
| |
| static const Byte kProps[] = |
| { |
| kpidPath, |
| kpidSize, |
| kpidVirtualSize, |
| kpidCharacts, |
| kpidOffset, |
| kpidVa |
| }; |
| |
| enum |
| { |
| kpidSubSystem = kpidUserDefined |
| // , kpidImageBase |
| }; |
| |
| static const CStatProp kArcProps[] = |
| { |
| // { NULL, kpidHeadersSize, VT_UI4 }, |
| { NULL, kpidCpu, VT_BSTR}, |
| { "Subsystem", kpidSubSystem, VT_BSTR }, |
| // { "Image Base", kpidImageBase, VT_UI8 } |
| }; |
| |
| IMP_IInArchive_Props |
| IMP_IInArchive_ArcProps_WITH_NAME |
| |
| Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| NCOM::CPropVariant prop; |
| switch (propID) |
| { |
| case kpidPhySize: prop = _totalSize; break; |
| case kpidCpu: PAIR_TO_PROP(NPe::g_MachinePairs, _h.Machine, prop); break; |
| case kpidSubSystem: TYPE_TO_PROP(NPe::g_SubSystems, _h.SubSystem, prop); break; |
| /* |
| case kpidImageBase: prop = _h.ImageBase; break; |
| case kpidAddressOfEntryPoint: prop = _h.AddressOfEntryPoint; break; |
| case kpidBaseOfCode: prop = _h.BaseOfCode; break; |
| */ |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| NCOM::CPropVariant prop; |
| { |
| const CSection &item = _items[index]; |
| switch (propID) |
| { |
| case kpidPath: |
| { |
| AString name; |
| NPe::GetName(item.Name, name); |
| prop = MultiByteToUnicodeString(name); |
| break; |
| } |
| case kpidSize: |
| case kpidPackSize: prop = (UInt64)item.PSize; break; |
| case kpidVirtualSize: prop = (UInt64)item.VSize; break; |
| case kpidOffset: prop = item.Pa; break; |
| case kpidVa: prop = item.Va; break; |
| case kpidCharacts: FLAGS_TO_PROP(NPe::g_SectFlags, item.Flags, prop); break; |
| } |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| HRESULT CHandler::Open2(IInStream *stream) |
| { |
| Byte h[kHeaderSize]; |
| RINOK(ReadStream_FALSE(stream, h, kHeaderSize)) |
| if (h[0] != 'V' || h[1] != 'Z') |
| return S_FALSE; |
| if (!_h.Parse(h)) |
| return S_FALSE; |
| |
| UInt32 headerSize = NPe::kSectionSize * (UInt32)_h.NumSections; |
| CByteArr buf(headerSize); |
| RINOK(ReadStream_FALSE(stream, buf, headerSize)) |
| headerSize += kHeaderSize; |
| |
| _totalSize = headerSize; |
| _items.ClearAndReserve(_h.NumSections); |
| for (UInt32 i = 0; i < _h.NumSections; i++) |
| { |
| CSection sect; |
| sect.Parse(buf + i * NPe::kSectionSize); |
| if (!_h.ConvertPa(sect.Pa)) |
| return S_FALSE; |
| if (sect.Pa < headerSize) |
| return S_FALSE; |
| if (!sect.Check()) |
| return S_FALSE; |
| _items.AddInReserved(sect); |
| sect.UpdateTotalSize(_totalSize); |
| } |
| |
| if (!_allowTail) |
| { |
| UInt64 fileSize; |
| RINOK(InStream_GetSize_SeekToEnd(stream, fileSize)) |
| if (fileSize > _totalSize) |
| return S_FALSE; |
| } |
| |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, |
| const UInt64 * /* maxCheckStartPosition */, |
| IArchiveOpenCallback * /* openArchiveCallback */)) |
| { |
| COM_TRY_BEGIN |
| Close(); |
| try |
| { |
| if (Open2(inStream) != S_OK) |
| return S_FALSE; |
| _stream = inStream; |
| } |
| catch(...) { return S_FALSE; } |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::Close()) |
| { |
| _totalSize = 0; |
| _stream.Release(); |
| _items.Clear(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) |
| { |
| *numItems = _items.Size(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, |
| Int32 testMode, IArchiveExtractCallback *extractCallback)) |
| { |
| COM_TRY_BEGIN |
| const bool allFilesMode = (numItems == (UInt32)(Int32)-1); |
| if (allFilesMode) |
| numItems = _items.Size(); |
| if (numItems == 0) |
| return S_OK; |
| UInt64 totalSize = 0; |
| UInt32 i; |
| for (i = 0; i < numItems; i++) |
| totalSize += _items[allFilesMode ? i : indices[i]].PSize; |
| extractCallback->SetTotal(totalSize); |
| |
| UInt64 currentTotalSize = 0; |
| |
| NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); |
| CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; |
| |
| CLocalProgress *lps = new CLocalProgress; |
| CMyComPtr<ICompressProgressInfo> progress = lps; |
| lps->Init(extractCallback, false); |
| |
| CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; |
| CMyComPtr<ISequentialInStream> inStream(streamSpec); |
| streamSpec->SetStream(_stream); |
| |
| for (i = 0; i < numItems; i++) |
| { |
| lps->InSize = lps->OutSize = currentTotalSize; |
| RINOK(lps->SetCur()) |
| CMyComPtr<ISequentialOutStream> realOutStream; |
| const Int32 askMode = testMode ? |
| NExtract::NAskMode::kTest : |
| NExtract::NAskMode::kExtract; |
| const UInt32 index = allFilesMode ? i : indices[i]; |
| const CSection &item = _items[index]; |
| RINOK(extractCallback->GetStream(index, &realOutStream, askMode)) |
| currentTotalSize += item.PSize; |
| |
| if (!testMode && !realOutStream) |
| continue; |
| RINOK(extractCallback->PrepareOperation(askMode)) |
| int res = NExtract::NOperationResult::kDataError; |
| |
| RINOK(InStream_SeekSet(_stream, item.Pa)) |
| streamSpec->Init(item.PSize); |
| RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)) |
| if (copyCoderSpec->TotalSize == item.PSize) |
| res = NExtract::NOperationResult::kOK; |
| |
| realOutStream.Release(); |
| RINOK(extractCallback->SetOperationResult(res)) |
| } |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) |
| { |
| COM_TRY_BEGIN |
| const CSection &item = _items[index]; |
| return CreateLimitedInStream(_stream, item.Pa, item.PSize, stream); |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::AllowTail(Int32 allowTail)) |
| { |
| _allowTail = IntToBool(allowTail); |
| return S_OK; |
| } |
| |
| static const Byte k_Signature[] = { 'V', 'Z' }; |
| |
| REGISTER_ARC_I( |
| "TE", "te", NULL, 0xCF, |
| k_Signature, |
| 0, |
| NArcInfoFlags::kPreArc, |
| IsArc_Te) |
| |
| } |
| } |