mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 07:14:55 -06:00
1745 lines
43 KiB
C++
Executable File
1745 lines
43 KiB
C++
Executable File
// PeHandler.cpp
|
|
|
|
#include "StdAfx.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/Time.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)
|
|
|
|
using namespace NWindows;
|
|
|
|
namespace NArchive {
|
|
namespace NPe {
|
|
|
|
#define NUM_SCAN_SECTIONS_MAX (1 << 6)
|
|
|
|
#define PE_SIG 0x00004550
|
|
#define PE_OptHeader_Magic_32 0x10B
|
|
#define PE_OptHeader_Magic_64 0x20B
|
|
|
|
static AString GetDecString(UInt32 v)
|
|
{
|
|
char sz[32];
|
|
ConvertUInt64ToString(v, sz);
|
|
return sz;
|
|
}
|
|
|
|
struct CVersion
|
|
{
|
|
UInt16 Major;
|
|
UInt16 Minor;
|
|
|
|
void Parse(const Byte *buf);
|
|
AString GetString() const { return GetDecString(Major) + '.' + GetDecString(Minor); }
|
|
};
|
|
|
|
void CVersion::Parse(const Byte *p)
|
|
{
|
|
Major = Get16(p);
|
|
Minor = Get16(p + 2);
|
|
}
|
|
|
|
static const UInt32 kHeaderSize = 4 + 20;
|
|
|
|
struct CHeader
|
|
{
|
|
UInt16 NumSections;
|
|
UInt32 Time;
|
|
UInt32 PointerToSymbolTable;
|
|
UInt32 NumSymbols;
|
|
UInt16 OptHeaderSize;
|
|
UInt16 Flags;
|
|
UInt16 Machine;
|
|
|
|
bool Parse(const Byte *buf);
|
|
};
|
|
|
|
bool CHeader::Parse(const Byte *p)
|
|
{
|
|
if (Get32(p) != PE_SIG)
|
|
return false;
|
|
p += 4;
|
|
Machine = Get16(p + 0);
|
|
NumSections = Get16(p + 2);
|
|
Time = Get32(p + 4);
|
|
PointerToSymbolTable = Get32(p + 8);
|
|
NumSymbols = Get32(p + 12);
|
|
OptHeaderSize = Get16(p + 16);
|
|
Flags = Get16(p + 18);
|
|
return true;
|
|
}
|
|
|
|
struct CDirLink
|
|
{
|
|
UInt32 Va;
|
|
UInt32 Size;
|
|
void Parse(const Byte *p);
|
|
};
|
|
|
|
void CDirLink::Parse(const Byte *p)
|
|
{
|
|
Va = Get32(p);
|
|
Size = Get32(p + 4);
|
|
};
|
|
|
|
enum
|
|
{
|
|
kDirLink_Certificate = 4,
|
|
kDirLink_Debug = 6
|
|
};
|
|
|
|
struct CDebugEntry
|
|
{
|
|
UInt32 Flags;
|
|
UInt32 Time;
|
|
CVersion Ver;
|
|
UInt32 Type;
|
|
UInt32 Size;
|
|
UInt32 Va;
|
|
UInt32 Pa;
|
|
|
|
void Parse(const Byte *p);
|
|
};
|
|
|
|
void CDebugEntry::Parse(const Byte *p)
|
|
{
|
|
Flags = Get32(p);
|
|
Time = Get32(p + 4);
|
|
Ver.Parse(p + 8);
|
|
Type = Get32(p + 12);
|
|
Size = Get32(p + 16);
|
|
Va = Get32(p + 20);
|
|
Pa = Get32(p + 24);
|
|
}
|
|
|
|
static const UInt32 kNumDirItemsMax = 16;
|
|
|
|
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 (int i = 9; i <= 16; i++)
|
|
if (((UInt32)1 << i) == FileAlign)
|
|
return i;
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
bool COptHeader::Parse(const Byte *p, UInt32 size)
|
|
{
|
|
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];
|
|
|
|
bool hdr64 = Is64Bit();
|
|
|
|
CodeSize = Get32(p + 4);
|
|
InitDataSize = Get32(p + 8);
|
|
UninitDataSize = Get32(p + 12);
|
|
|
|
// AddressOfEntryPoint = Get32(p + 16);
|
|
// BaseOfCode = Get32(p + 20);
|
|
// BaseOfData32 = hdr64 ? 0: Get32(p + 24);
|
|
ImageBase = hdr64 ? GetUi64(p + 24) : Get32(p + 28);
|
|
|
|
SectAlign = Get32(p + 32);
|
|
FileAlign = Get32(p + 36);
|
|
|
|
OsVer.Parse(p + 40);
|
|
ImageVer.Parse(p + 44);
|
|
SubsysVer.Parse(p + 48);
|
|
|
|
// reserved = Get32(p + 52);
|
|
|
|
ImageSize = Get32(p + 56);
|
|
HeadersSize = Get32(p + 60);
|
|
CheckSum = Get32(p + 64);
|
|
SubSystem = Get16(p + 68);
|
|
DllCharacts = Get16(p + 70);
|
|
|
|
if (hdr64)
|
|
{
|
|
StackReserve = Get64(p + 72);
|
|
StackCommit = Get64(p + 80);
|
|
HeapReserve = Get64(p + 88);
|
|
HeapCommit = Get64(p + 96);
|
|
}
|
|
else
|
|
{
|
|
StackReserve = Get32(p + 72);
|
|
StackCommit = Get32(p + 76);
|
|
HeapReserve = Get32(p + 80);
|
|
HeapCommit = Get32(p + 84);
|
|
}
|
|
UInt32 pos = (hdr64 ? 108 : 92);
|
|
NumDirItems = Get32(p + pos);
|
|
pos += 4;
|
|
if (pos + 8 * NumDirItems != size)
|
|
return false;
|
|
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 IsDebug;
|
|
bool IsRealSect;
|
|
bool IsAdditionalSection;
|
|
|
|
CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {}
|
|
UInt64 GetPackSize() const { return PSize; }
|
|
|
|
void UpdateTotalSize(UInt32 &totalSize)
|
|
{
|
|
UInt32 t = Pa + PSize;
|
|
if (t > totalSize)
|
|
totalSize = t;
|
|
}
|
|
void Parse(const Byte *p);
|
|
};
|
|
|
|
static bool operator <(const CSection &a1, const CSection &a2) { return (a1.Pa < a2.Pa) || ((a1.Pa == a2.Pa) && (a1.PSize < a2.PSize)) ; }
|
|
static bool operator ==(const CSection &a1, const CSection &a2) { return (a1.Pa == a2.Pa) && (a1.PSize == a2.PSize); }
|
|
|
|
static AString GetName(const Byte *name)
|
|
{
|
|
const int kNameSize = 8;
|
|
AString res;
|
|
char *p = res.GetBuffer(kNameSize);
|
|
memcpy(p, name, kNameSize);
|
|
p[kNameSize] = 0;
|
|
res.ReleaseBuffer();
|
|
return res;
|
|
}
|
|
|
|
void CSection::Parse(const Byte *p)
|
|
{
|
|
Name = GetName(p);
|
|
VSize = Get32(p + 8);
|
|
Va = Get32(p + 12);
|
|
PSize = Get32(p + 16);
|
|
Pa = Get32(p + 20);
|
|
// NumRelocs = Get16(p + 32);
|
|
Flags = Get32(p + 36);
|
|
}
|
|
|
|
static const CUInt32PCharPair g_HeaderCharacts[] =
|
|
{
|
|
{ 1 << 1, "Executable" },
|
|
{ 1 << 13, "DLL" },
|
|
{ 1 << 8, "32-bit" },
|
|
{ 1 << 5, "LargeAddress" },
|
|
{ 1 << 0, "NoRelocs" },
|
|
{ 1 << 2, "NoLineNums" },
|
|
{ 1 << 3, "NoLocalSyms" },
|
|
{ 1 << 4, "AggressiveWsTrim" },
|
|
{ 1 << 9, "NoDebugInfo" },
|
|
{ 1 << 10, "RemovableRun" },
|
|
{ 1 << 11, "NetRun" },
|
|
{ 1 << 12, "System" },
|
|
{ 1 << 14, "UniCPU" },
|
|
{ 1 << 7, "Little-Endian" },
|
|
{ 1 << 15, "Big-Endian" }
|
|
};
|
|
|
|
static const CUInt32PCharPair g_DllCharacts[] =
|
|
{
|
|
{ 1 << 6, "Relocated" },
|
|
{ 1 << 7, "Integrity" },
|
|
{ 1 << 8, "NX-Compatible" },
|
|
{ 1 << 9, "NoIsolation" },
|
|
{ 1 << 10, "NoSEH" },
|
|
{ 1 << 11, "NoBind" },
|
|
{ 1 << 13, "WDM" },
|
|
{ 1 << 15, "TerminalServerAware" }
|
|
};
|
|
|
|
static const CUInt32PCharPair g_SectFlags[] =
|
|
{
|
|
{ 1 << 3, "NoPad" },
|
|
{ 1 << 5, "Code" },
|
|
{ 1 << 6, "InitializedData" },
|
|
{ 1 << 7, "UninitializedData" },
|
|
{ 1 << 9, "Comments" },
|
|
{ 1 << 11, "Remove" },
|
|
{ 1 << 12, "COMDAT" },
|
|
{ 1 << 15, "GP" },
|
|
{ 1 << 24, "ExtendedRelocations" },
|
|
{ 1 << 25, "Discardable" },
|
|
{ 1 << 26, "NotCached" },
|
|
{ 1 << 27, "NotPaged" },
|
|
{ 1 << 28, "Shared" },
|
|
{ 1 << 29, "Execute" },
|
|
{ 1 << 30, "Read" },
|
|
{ (UInt32)1 << 31, "Write" }
|
|
};
|
|
|
|
static const CUInt32PCharPair g_MachinePairs[] =
|
|
{
|
|
{ 0x014C, "x86" },
|
|
{ 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" },
|
|
{ 0x01F0, "PPC" },
|
|
{ 0x01F1, "PPC-FP" },
|
|
{ 0x0200, "IA-64" },
|
|
{ 0x0284, "Alpha-64" },
|
|
{ 0x0200, "IA-64" },
|
|
{ 0x0366, "MIPSFPU" },
|
|
{ 0x8664, "x64" },
|
|
{ 0x0EBC, "EFI" }
|
|
};
|
|
|
|
static const CUInt32PCharPair g_SubSystems[] =
|
|
{
|
|
{ 0, "Unknown" },
|
|
{ 1, "Native" },
|
|
{ 2, "Windows GUI" },
|
|
{ 3, "Windows CUI" },
|
|
{ 7, "Posix" },
|
|
{ 9, "Windows CE" },
|
|
{ 10, "EFI" },
|
|
{ 11, "EFI Boot" },
|
|
{ 12, "EFI Runtime" },
|
|
{ 13, "EFI ROM" },
|
|
{ 14, "XBOX" }
|
|
};
|
|
|
|
static const wchar_t *g_ResTypes[] =
|
|
{
|
|
NULL,
|
|
L"CURSOR",
|
|
L"BITMAP",
|
|
L"ICON",
|
|
L"MENU",
|
|
L"DIALOG",
|
|
L"STRING",
|
|
L"FONTDIR",
|
|
L"FONT",
|
|
L"ACCELERATOR",
|
|
L"RCDATA",
|
|
L"MESSAGETABLE",
|
|
L"GROUP_CURSOR",
|
|
NULL,
|
|
L"GROUP_ICON",
|
|
NULL,
|
|
L"VERSION",
|
|
L"DLGINCLUDE",
|
|
NULL,
|
|
L"PLUGPLAY",
|
|
L"VXD",
|
|
L"ANICURSOR",
|
|
L"ANIICON",
|
|
L"HTML",
|
|
L"MANIFEST"
|
|
};
|
|
|
|
const UInt32 kFlag = (UInt32)1 << 31;
|
|
const UInt32 kMask = ~kFlag;
|
|
|
|
struct CTableItem
|
|
{
|
|
UInt32 Offset;
|
|
UInt32 ID;
|
|
};
|
|
|
|
|
|
const UInt32 kBmpHeaderSize = 14;
|
|
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 IsRcDataOrUnknown() const { return IsRcData() || Type > 64; }
|
|
};
|
|
|
|
struct CStringItem
|
|
{
|
|
UInt32 Lang;
|
|
UInt32 Size;
|
|
CByteDynamicBuffer Buf;
|
|
|
|
void AddChar(Byte c);
|
|
void AddWChar(UInt16 c);
|
|
};
|
|
|
|
void CStringItem::AddChar(Byte c)
|
|
{
|
|
Buf.EnsureCapacity(Size + 2);
|
|
Buf[Size++] = c;
|
|
Buf[Size++] = 0;
|
|
}
|
|
|
|
void CStringItem::AddWChar(UInt16 c)
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
AddChar('\\');
|
|
c = 'n';
|
|
}
|
|
Buf.EnsureCapacity(Size + 2);
|
|
SetUi16(Buf + Size, c);
|
|
Size += 2;
|
|
}
|
|
|
|
struct CMixItem
|
|
{
|
|
int SectionIndex;
|
|
int ResourceIndex;
|
|
int StringIndex;
|
|
|
|
bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0; };
|
|
};
|
|
|
|
struct CUsedBitmap
|
|
{
|
|
CByteBuffer Buf;
|
|
public:
|
|
void Alloc(size_t size)
|
|
{
|
|
size = (size + 7) / 8;
|
|
Buf.SetCapacity(size);
|
|
memset(Buf, 0, size);
|
|
}
|
|
void Free()
|
|
{
|
|
Buf.SetCapacity(0);
|
|
}
|
|
bool SetRange(size_t from, int size)
|
|
{
|
|
for (int 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] = b | mask;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
class CHandler:
|
|
public IInArchive,
|
|
public IInArchiveGetStream,
|
|
public CMyUnknownImp
|
|
{
|
|
CMyComPtr<IInStream> _stream;
|
|
CObjectVector<CSection> _sections;
|
|
UInt32 _peOffset;
|
|
CHeader _header;
|
|
COptHeader _optHeader;
|
|
UInt32 _totalSize;
|
|
UInt32 _totalSizeLimited;
|
|
Int32 _mainSubfile;
|
|
|
|
CRecordVector<CResItem> _items;
|
|
CObjectVector<CStringItem> _strings;
|
|
|
|
CByteBuffer _buf;
|
|
bool _oneLang;
|
|
UString _resourceFileName;
|
|
CUsedBitmap _usedRes;
|
|
bool _parseResources;
|
|
|
|
CRecordVector<CMixItem> _mixItems;
|
|
|
|
HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection);
|
|
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
|
|
bool Parse(const Byte *buf, UInt32 size);
|
|
|
|
void AddResNameToString(UString &s, UInt32 id) const;
|
|
UString GetLangPrefix(UInt32 lang);
|
|
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(int 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.GetCapacity() && _buf.GetCapacity() - offset >= item.Size;
|
|
}
|
|
|
|
public:
|
|
MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
|
|
INTERFACE_IInArchive(;)
|
|
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
|
};
|
|
|
|
bool CHandler::Parse(const Byte *buf, UInt32 size)
|
|
{
|
|
UInt32 i;
|
|
if (size < 512)
|
|
return false;
|
|
_peOffset = Get32(buf + 0x3C);
|
|
if (_peOffset >= 0x1000 || _peOffset + 512 > size || (_peOffset & 7) != 0)
|
|
return false;
|
|
|
|
UInt32 pos = _peOffset;
|
|
if (!_header.Parse(buf + pos))
|
|
return false;
|
|
if (_header.OptHeaderSize > 512 || _header.NumSections > NUM_SCAN_SECTIONS_MAX)
|
|
return false;
|
|
pos += kHeaderSize;
|
|
|
|
if (!_optHeader.Parse(buf + pos, _header.OptHeaderSize))
|
|
return false;
|
|
|
|
pos += _header.OptHeaderSize;
|
|
_totalSize = pos;
|
|
|
|
for (i = 0; i < _header.NumSections; i++, pos += kSectionSize)
|
|
{
|
|
CSection sect;
|
|
if (pos + kSectionSize > size)
|
|
return false;
|
|
sect.Parse(buf + pos);
|
|
sect.IsRealSect = true;
|
|
sect.UpdateTotalSize(_totalSize);
|
|
_sections.Add(sect);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
enum
|
|
{
|
|
kpidSectAlign = kpidUserDefined,
|
|
kpidFileAlign,
|
|
kpidLinkerVer,
|
|
kpidOsVer,
|
|
kpidImageVer,
|
|
kpidSubsysVer,
|
|
kpidCodeSize,
|
|
kpidImageSize,
|
|
kpidInitDataSize,
|
|
kpidUnInitDataSize,
|
|
kpidHeadersSizeUnInitDataSize,
|
|
kpidSubSystem,
|
|
kpidDllCharacts,
|
|
kpidStackReserve,
|
|
kpidStackCommit,
|
|
kpidHeapReserve,
|
|
kpidHeapCommit,
|
|
kpidImageBase
|
|
// kpidAddressOfEntryPoint,
|
|
// kpidBaseOfCode,
|
|
// kpidBaseOfData32,
|
|
};
|
|
|
|
STATPROPSTG kArcProps[] =
|
|
{
|
|
{ NULL, kpidCpu, VT_BSTR},
|
|
{ NULL, kpidBit64, VT_BOOL},
|
|
{ NULL, kpidCharacts, VT_BSTR},
|
|
{ NULL, kpidCTime, VT_FILETIME},
|
|
{ NULL, kpidPhySize, VT_UI4},
|
|
{ NULL, kpidHeadersSize, VT_UI4},
|
|
{ NULL, kpidChecksum, VT_UI4},
|
|
{ L"Image Size", kpidImageSize, VT_UI4},
|
|
{ L"Section Alignment", kpidSectAlign, VT_UI4},
|
|
{ L"File Alignment", kpidFileAlign, VT_UI4},
|
|
{ L"Code Size", kpidCodeSize, VT_UI4},
|
|
{ L"Initialized Data Size", kpidInitDataSize, VT_UI4},
|
|
{ L"Uninitialized Data Size", kpidUnInitDataSize, VT_UI4},
|
|
{ L"Linker Version", kpidLinkerVer, VT_BSTR},
|
|
{ L"OS Version", kpidOsVer, VT_BSTR},
|
|
{ L"Image Version", kpidImageVer, VT_BSTR},
|
|
{ L"Subsystem Version", kpidSubsysVer, VT_BSTR},
|
|
{ L"Subsystem", kpidSubSystem, VT_BSTR},
|
|
{ L"DLL Characteristics", kpidDllCharacts, VT_BSTR},
|
|
{ L"Stack Reserve", kpidStackReserve, VT_UI8},
|
|
{ L"Stack Commit", kpidStackCommit, VT_UI8},
|
|
{ L"Heap Reserve", kpidHeapReserve, VT_UI8},
|
|
{ L"Heap Commit", kpidHeapCommit, VT_UI8},
|
|
{ L"Image Base", kpidImageBase, VT_UI8}
|
|
// { L"Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8},
|
|
// { L"Base Of Code", kpidBaseOfCode, VT_UI8},
|
|
// { L"Base Of Data", kpidBaseOfData32, VT_UI8},
|
|
};
|
|
|
|
STATPROPSTG kProps[] =
|
|
{
|
|
{ NULL, kpidPath, VT_BSTR},
|
|
{ NULL, kpidSize, VT_UI8},
|
|
{ NULL, kpidPackSize, VT_UI8},
|
|
{ NULL, kpidCharacts, VT_BSTR},
|
|
{ NULL, kpidOffset, VT_UI8},
|
|
{ NULL, kpidVa, VT_UI8}
|
|
};
|
|
|
|
IMP_IInArchive_Props
|
|
IMP_IInArchive_ArcProps_WITH_NAME
|
|
|
|
static void VerToProp(const CVersion &v, NCOM::CPropVariant &prop)
|
|
{
|
|
StringToProp(v.GetString(), prop);
|
|
}
|
|
|
|
void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop)
|
|
{
|
|
if (unixTime != 0)
|
|
{
|
|
FILETIME ft;
|
|
NTime::UnixTimeToFileTime(unixTime, ft);
|
|
prop = ft;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
switch(propID)
|
|
{
|
|
case kpidSectAlign: prop = _optHeader.SectAlign; break;
|
|
case kpidFileAlign: prop = _optHeader.FileAlign; break;
|
|
case kpidLinkerVer:
|
|
{
|
|
CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor };
|
|
VerToProp(v, prop);
|
|
break;
|
|
}
|
|
|
|
case kpidOsVer: VerToProp(_optHeader.OsVer, prop); break;
|
|
case kpidImageVer: VerToProp(_optHeader.ImageVer, prop); break;
|
|
case kpidSubsysVer: VerToProp(_optHeader.SubsysVer, 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 kpidPhySize: prop = _totalSize; break;
|
|
case kpidHeadersSize: prop = _optHeader.HeadersSize; break;
|
|
case kpidChecksum: prop = _optHeader.CheckSum; break;
|
|
|
|
case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break;
|
|
case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break;
|
|
case kpidSubSystem: PAIR_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break;
|
|
|
|
case kpidMTime:
|
|
case kpidCTime: TimeToProp(_header.Time, prop); break;
|
|
case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, 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;
|
|
|
|
case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
void CHandler::AddResNameToString(UString &s, UInt32 id) const
|
|
{
|
|
if ((id & kFlag) != 0)
|
|
{
|
|
UString name;
|
|
if (ReadString(id & kMask, name) == S_OK)
|
|
{
|
|
if (name.IsEmpty())
|
|
s += L"[]";
|
|
else
|
|
{
|
|
if (name.Length() > 1 && name[0] == '"' && name.Back() == '"')
|
|
name = name.Mid(1, name.Length() - 2);
|
|
s += name;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
wchar_t sz[32];
|
|
ConvertUInt32ToString(id, sz);
|
|
s += sz;
|
|
}
|
|
|
|
UString CHandler::GetLangPrefix(UInt32 lang)
|
|
{
|
|
UString s = _resourceFileName;
|
|
s += WCHAR_PATH_SEPARATOR;
|
|
if (!_oneLang)
|
|
{
|
|
AddResNameToString(s, lang);
|
|
s += WCHAR_PATH_SEPARATOR;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
STDMETHODIMP 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: prop = GetLangPrefix(item.Lang) + L"string.txt"; break;
|
|
case kpidSize:
|
|
case kpidPackSize:
|
|
prop = (UInt64)item.Size; break;
|
|
}
|
|
}
|
|
else if (mixItem.ResourceIndex < 0)
|
|
{
|
|
const CSection &item = _sections[mixItem.SectionIndex];
|
|
switch(propID)
|
|
{
|
|
case kpidPath: StringToProp(item.Name, prop); break;
|
|
case kpidSize: prop = (UInt64)item.VSize; break;
|
|
case kpidPackSize: prop = (UInt64)item.GetPackSize(); 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) FLAGS_TO_PROP(g_SectFlags, item.Flags, prop); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const CResItem &item = _items[mixItem.ResourceIndex];
|
|
switch(propID)
|
|
{
|
|
case kpidPath:
|
|
{
|
|
UString s = GetLangPrefix(item.Lang);
|
|
{
|
|
const wchar_t *p = NULL;
|
|
if (item.Type < sizeof(g_ResTypes) / sizeof(g_ResTypes[0]))
|
|
p = g_ResTypes[item.Type];
|
|
if (p != 0)
|
|
s += p;
|
|
else
|
|
AddResNameToString(s, item.Type);
|
|
}
|
|
s += WCHAR_PATH_SEPARATOR;
|
|
AddResNameToString(s, item.ID);
|
|
if (item.HeaderSize != 0)
|
|
{
|
|
if (item.IsBmp())
|
|
s += L".bmp";
|
|
else if (item.IsIcon())
|
|
s += L".ico";
|
|
}
|
|
prop = s;
|
|
break;
|
|
}
|
|
case kpidSize: prop = (UInt64)item.GetSize(); break;
|
|
case kpidPackSize: prop = (UInt64)item.Size; 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 * kEntrySize != debugLink.Size || numItems > 16)
|
|
return S_FALSE;
|
|
|
|
UInt64 pa = 0;
|
|
int 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())
|
|
{
|
|
return S_OK;
|
|
// Exe for ARM requires S_OK
|
|
// return S_FALSE;
|
|
}
|
|
|
|
CByteBuffer buffer;
|
|
buffer.SetCapacity(debugLink.Size);
|
|
Byte *buf = buffer;
|
|
|
|
RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL));
|
|
RINOK(ReadStream_FALSE(stream, buf, debugLink.Size));
|
|
|
|
for (i = 0; i < (int)numItems; i++)
|
|
{
|
|
CDebugEntry de;
|
|
de.Parse(buf);
|
|
|
|
if (de.Size == 0)
|
|
continue;
|
|
|
|
CSection sect;
|
|
sect.Name = ".debug" + GetDecString(i);
|
|
|
|
sect.IsDebug = true;
|
|
sect.Time = de.Time;
|
|
sect.Va = de.Va;
|
|
sect.Pa = de.Pa;
|
|
sect.PSize = sect.VSize = de.Size;
|
|
UInt32 totalSize = sect.Pa + sect.PSize;
|
|
if (totalSize > _totalSize)
|
|
{
|
|
_totalSize = totalSize;
|
|
_sections.Add(sect);
|
|
thereIsSection = true;
|
|
}
|
|
buf += kEntrySize;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const
|
|
{
|
|
if ((offset & 1) != 0 || offset >= _buf.GetCapacity())
|
|
return S_FALSE;
|
|
size_t rem = _buf.GetCapacity() - offset;
|
|
if (rem < 2)
|
|
return S_FALSE;
|
|
unsigned length = Get16(_buf + offset);
|
|
if ((rem - 2) / 2 < length)
|
|
return S_FALSE;
|
|
dest.Empty();
|
|
offset += 2;
|
|
for (unsigned i = 0; i < length; i++)
|
|
dest += (wchar_t)Get16(_buf + offset + i * 2);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items)
|
|
{
|
|
if ((offset & 3) != 0 || offset >= _buf.GetCapacity())
|
|
return S_FALSE;
|
|
size_t rem = _buf.GetCapacity() - offset;
|
|
if (rem < 16)
|
|
return S_FALSE;
|
|
items.Clear();
|
|
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;
|
|
_oneLang = true;
|
|
unsigned i;
|
|
for (i = 0; i < numItems; i++)
|
|
{
|
|
CTableItem item;
|
|
const Byte *buf = _buf + offset;
|
|
offset += 8;
|
|
item.ID = Get32(buf + 0);
|
|
if (((item.ID & kFlag) != 0) != (i < numNameItems))
|
|
return S_FALSE;
|
|
item.Offset = Get32(buf + 4);
|
|
items.Add(item);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static const UInt32 kFileSizeMax = (UInt32)1 << 30;
|
|
static const int kNumResItemsMax = (UInt32)1 << 23;
|
|
static const int kNumStringLangsMax = 128;
|
|
|
|
// 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;
|
|
XSize = Get32(p + 4);
|
|
YSize = (Int32)Get32(p + 8);
|
|
Planes = Get16(p + 12);
|
|
BitCount = Get16(p + 14);
|
|
Compression = Get32(p + 16);
|
|
SizeImage = Get32(p + 20);
|
|
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.Planes != 1 || h.BitCount > 32 ||
|
|
h.Compression != 0) // BI_RGB
|
|
return 0;
|
|
if (h.SizeImage == 0)
|
|
h.SizeImage = GetImageSize(h.XSize, 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.Planes != 1 ||
|
|
h.Compression != 0) // BI_RGB
|
|
return 0;
|
|
|
|
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)
|
|
// {
|
|
UInt32 image1Size = GetImageSize(h.XSize, h.YSize, h.BitCount);
|
|
UInt32 image2Size = GetImageSize(h.XSize, 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;
|
|
|
|
int 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;
|
|
item.Size = 0;
|
|
item.Lang = lang;
|
|
i = _strings.Add(item);
|
|
}
|
|
|
|
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(Get16(src + pos));
|
|
item.AddChar(0x0D);
|
|
item.AddChar(0x0A);
|
|
}
|
|
}
|
|
return (size == pos);
|
|
}
|
|
|
|
HRESULT CHandler::OpenResources(int sectionIndex, IInStream *stream, IArchiveOpenCallback *callback)
|
|
{
|
|
const CSection § = _sections[sectionIndex];
|
|
size_t fileSize = sect.PSize; // Maybe we need sect.VSize here !!!
|
|
if (fileSize > kFileSizeMax)
|
|
return S_FALSE;
|
|
{
|
|
UInt64 fileSize64 = fileSize;
|
|
if (callback)
|
|
RINOK(callback->SetTotal(NULL, &fileSize64));
|
|
RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));
|
|
_buf.SetCapacity(fileSize);
|
|
for (size_t pos = 0; pos < fileSize;)
|
|
{
|
|
UInt64 offset64 = pos;
|
|
if (callback)
|
|
RINOK(callback->SetCompleted(NULL, &offset64))
|
|
size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20));
|
|
RINOK(ReadStream_FALSE(stream, _buf + pos, rem));
|
|
pos += rem;
|
|
}
|
|
}
|
|
|
|
_usedRes.Alloc(fileSize);
|
|
CRecordVector<CTableItem> specItems;
|
|
RINOK(ReadTable(0, specItems));
|
|
|
|
_oneLang = true;
|
|
bool stringsOk = true;
|
|
size_t maxOffset = 0;
|
|
for (int i = 0; i < specItems.Size(); i++)
|
|
{
|
|
const CTableItem &item1 = specItems[i];
|
|
if ((item1.Offset & kFlag) == 0)
|
|
return S_FALSE;
|
|
|
|
CRecordVector<CTableItem> specItems2;
|
|
RINOK(ReadTable(item1.Offset & kMask, specItems2));
|
|
|
|
for (int j = 0; j < specItems2.Size(); j++)
|
|
{
|
|
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 (int k = 0; k < specItems3.Size(); k++)
|
|
{
|
|
if (_items.Size() >= kNumResItemsMax)
|
|
return S_FALSE;
|
|
const CTableItem &item3 = specItems3[k];
|
|
if ((item3.Offset & kFlag) != 0)
|
|
return S_FALSE;
|
|
if (item3.Offset >= _buf.GetCapacity() || _buf.GetCapacity() - 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);
|
|
}
|
|
}
|
|
|
|
item.Enabled = true;
|
|
_items.Add(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stringsOk && !_strings.IsEmpty())
|
|
{
|
|
int 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].Size == 0)
|
|
continue;
|
|
CMixItem mixItem;
|
|
mixItem.ResourceIndex = -1;
|
|
mixItem.StringIndex = i;
|
|
mixItem.SectionIndex = sectionIndex;
|
|
_mixItems.Add(mixItem);
|
|
}
|
|
}
|
|
|
|
_usedRes.Free();
|
|
|
|
int numBits = _optHeader.GetNumFileAlignBits();
|
|
if (numBits >= 0)
|
|
{
|
|
UInt32 mask = (1 << numBits) - 1;
|
|
size_t end = ((maxOffset + mask) & ~mask);
|
|
if (end < sect.VSize && end <= sect.PSize)
|
|
{
|
|
CSection sect2;
|
|
sect2.Flags = 0;
|
|
|
|
// 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;
|
|
|
|
sect2.Pa = sect.Pa + (UInt32)maxOffset;
|
|
sect2.Va = sect.Va + (UInt32)maxOffset;
|
|
sect2.PSize = sect.VSize - (UInt32)maxOffset;
|
|
sect2.VSize = sect2.PSize;
|
|
sect2.Name = ".rsrc_1";
|
|
sect2.Time = 0;
|
|
sect2.IsAdditionalSection = true;
|
|
_sections.Add(sect2);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
|
{
|
|
const UInt32 kBufSize = 1 << 18;
|
|
const UInt32 kSigSize = 2;
|
|
|
|
_mainSubfile = -1;
|
|
|
|
CByteBuffer buffer;
|
|
buffer.SetCapacity(kBufSize);
|
|
Byte *buf = buffer;
|
|
|
|
size_t processed = kSigSize;
|
|
RINOK(ReadStream_FALSE(stream, buf, processed));
|
|
if (buf[0] != 'M' || buf[1] != 'Z')
|
|
return S_FALSE;
|
|
processed = kBufSize - kSigSize;
|
|
RINOK(ReadStream(stream, buf + kSigSize, &processed));
|
|
processed += kSigSize;
|
|
if (!Parse(buf, (UInt32)processed))
|
|
return S_FALSE;
|
|
bool thereISDebug;
|
|
RINOK(LoadDebugSections(stream, thereISDebug));
|
|
|
|
const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate];
|
|
if (certLink.Size != 0)
|
|
{
|
|
CSection sect;
|
|
sect.Name = "CERTIFICATE";
|
|
sect.Va = 0;
|
|
sect.Pa = certLink.Va;
|
|
sect.PSize = sect.VSize = certLink.Size;
|
|
sect.UpdateTotalSize(_totalSize);
|
|
_sections.Add(sect);
|
|
}
|
|
|
|
if (thereISDebug)
|
|
{
|
|
const UInt32 kAlign = 1 << 12;
|
|
UInt32 alignPos = _totalSize & (kAlign - 1);
|
|
if (alignPos != 0)
|
|
{
|
|
UInt32 size = kAlign - alignPos;
|
|
RINOK(stream->Seek(_totalSize, STREAM_SEEK_SET, NULL));
|
|
buffer.Free();
|
|
buffer.SetCapacity(kAlign);
|
|
Byte *buf = buffer;
|
|
size_t processed = size;
|
|
RINOK(ReadStream(stream, buf, &processed));
|
|
size_t i;
|
|
for (i = 0; i < processed; i++)
|
|
{
|
|
if (buf[i] != 0)
|
|
break;
|
|
}
|
|
if (processed < size && processed < 100)
|
|
_totalSize += (UInt32)processed;
|
|
else if (((_totalSize + i) & 0x1FF) == 0 || processed < size)
|
|
_totalSize += (UInt32)i;
|
|
}
|
|
}
|
|
|
|
if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= 512)
|
|
{
|
|
if (_header.NumSymbols >= (1 << 24))
|
|
return S_FALSE;
|
|
CSection sect;
|
|
sect.Name = "COFF_SYMBOLS";
|
|
UInt32 size = _header.NumSymbols * 18;
|
|
RINOK(stream->Seek((UInt64)_header.PointerToSymbolTable + size, STREAM_SEEK_SET, NULL));
|
|
Byte buf[4];
|
|
RINOK(ReadStream_FALSE(stream, buf, 4));
|
|
UInt32 size2 = Get32(buf);
|
|
if (size2 >= (1 << 28))
|
|
return S_FALSE;
|
|
size += size2;
|
|
|
|
sect.Va = 0;
|
|
sect.Pa = _header.PointerToSymbolTable;
|
|
sect.PSize = sect.VSize = size;
|
|
sect.UpdateTotalSize(_totalSize);
|
|
_sections.Add(sect);
|
|
}
|
|
|
|
UInt64 fileSize;
|
|
RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
|
|
if (fileSize > _totalSize)
|
|
return S_FALSE;
|
|
_totalSizeLimited = (_totalSize < fileSize) ? _totalSize : (UInt32)fileSize;
|
|
|
|
{
|
|
CObjectVector<CSection> sections = _sections;
|
|
sections.Sort();
|
|
UInt32 limit = (1 << 12);
|
|
int num = 0;
|
|
int numSections = sections.Size();
|
|
for (int i = 0; i < numSections; i++)
|
|
{
|
|
const CSection &s = sections[i];
|
|
if (s.Pa > limit)
|
|
{
|
|
CSection s2;
|
|
s2.Pa = s2.Va = limit;
|
|
s2.PSize = s2.VSize = s.Pa - limit;
|
|
s2.IsAdditionalSection = true;
|
|
s2.Name = '[';
|
|
s2.Name += GetDecString(num++);
|
|
s2.Name += ']';
|
|
_sections.Add(s2);
|
|
limit = s.Pa;
|
|
}
|
|
UInt32 next = s.Pa + s.PSize;
|
|
if (next < s.Pa)
|
|
break;
|
|
if (next >= limit)
|
|
limit = next;
|
|
}
|
|
}
|
|
|
|
_parseResources = true;
|
|
|
|
UInt64 mainSize = 0, mainSize2 = 0;
|
|
int i;
|
|
for (i = 0; i < _sections.Size(); i++)
|
|
{
|
|
const CSection § = _sections[i];
|
|
CMixItem mixItem;
|
|
mixItem.SectionIndex = i;
|
|
if (_parseResources && sect.Name == ".rsrc" && _items.IsEmpty())
|
|
{
|
|
HRESULT res = OpenResources(i, stream, callback);
|
|
if (res == S_OK)
|
|
{
|
|
_resourceFileName = GetUnicodeString(sect.Name);
|
|
for (int j = 0; j < _items.Size(); j++)
|
|
{
|
|
const CResItem &item = _items[j];
|
|
if (item.Enabled)
|
|
{
|
|
mixItem.ResourceIndex = j;
|
|
mixItem.StringIndex = -1;
|
|
if (item.IsRcDataOrUnknown())
|
|
{
|
|
if (item.Size >= mainSize)
|
|
{
|
|
mainSize2 = mainSize;
|
|
mainSize = item.Size;
|
|
_mainSubfile = _mixItems.Size();
|
|
}
|
|
else if (item.Size >= mainSize2)
|
|
mainSize2 = item.Size;
|
|
}
|
|
_mixItems.Add(mixItem);
|
|
}
|
|
}
|
|
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 sect2;
|
|
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;
|
|
_sections.Add(sect2);
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if (res != S_FALSE)
|
|
return res;
|
|
CloseResources();
|
|
}
|
|
mixItem.StringIndex = -1;
|
|
mixItem.ResourceIndex = -1;
|
|
if (sect.IsAdditionalSection)
|
|
{
|
|
if (sect.PSize >= mainSize)
|
|
{
|
|
mainSize2 = mainSize;
|
|
mainSize = sect.PSize;
|
|
_mainSubfile = _mixItems.Size();
|
|
}
|
|
else
|
|
mainSize2 = sect.PSize;
|
|
}
|
|
_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 = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res)
|
|
{
|
|
// size &= ~1;
|
|
const UInt32 kBufSize = 1 << 23;
|
|
CByteBuffer buffer;
|
|
buffer.SetCapacity(kBufSize);
|
|
Byte *buf = buffer;
|
|
|
|
UInt32 sum = 0;
|
|
UInt32 pos = 0;
|
|
for (;;)
|
|
{
|
|
UInt32 rem = size - pos;
|
|
if (rem > kBufSize)
|
|
rem = kBufSize;
|
|
if (rem == 0)
|
|
break;
|
|
size_t processed = rem;
|
|
RINOK(ReadStream(stream, buf, &processed));
|
|
|
|
/*
|
|
for (; processed < rem; processed++)
|
|
buf[processed] = 0;
|
|
*/
|
|
|
|
if ((processed & 1) != 0)
|
|
buf[processed] = 0;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
UInt32 p = excludePos + j;
|
|
if (pos <= p && p < pos + processed)
|
|
buf[p - pos] = 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < processed; i += 2)
|
|
{
|
|
sum += Get16(buf + i);
|
|
sum = (sum + (sum >> 16)) & 0xFFFF;
|
|
}
|
|
pos += (UInt32)processed;
|
|
if (rem != processed)
|
|
break;
|
|
}
|
|
sum += pos;
|
|
res = sum;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP 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();
|
|
_buf.SetCapacity(0);
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_stream.Release();
|
|
_sections.Clear();
|
|
_mixItems.Clear();
|
|
CloseResources();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
|
{
|
|
*numItems = _mixItems.Size();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
|
Int32 testMode, IArchiveExtractCallback *extractCallback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
bool allFilesMode = (numItems == (UInt32)-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]];
|
|
if (mixItem.StringIndex >= 0)
|
|
totalSize += _strings[mixItem.StringIndex].Size;
|
|
else if (mixItem.ResourceIndex < 0)
|
|
totalSize += _sections[mixItem.SectionIndex].GetPackSize();
|
|
else
|
|
totalSize += _items[mixItem.ResourceIndex].GetSize();
|
|
}
|
|
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);
|
|
|
|
bool checkSumOK = true;
|
|
if (_optHeader.CheckSum != 0 && (int)numItems == _mixItems.Size())
|
|
{
|
|
UInt32 checkSum = 0;
|
|
RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
|
|
CalcCheckSum(_stream, _totalSizeLimited, _peOffset + kHeaderSize + 64, checkSum);
|
|
checkSumOK = (checkSum == _optHeader.CheckSum);
|
|
}
|
|
|
|
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());
|
|
Int32 askMode = testMode ?
|
|
NExtract::NAskMode::kTest :
|
|
NExtract::NAskMode::kExtract;
|
|
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.Size;
|
|
if (!testMode && !outStream)
|
|
continue;
|
|
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
if (outStream)
|
|
RINOK(WriteStream(outStream, item.Buf, item.Size));
|
|
}
|
|
else if (mixItem.ResourceIndex < 0)
|
|
{
|
|
currentItemSize = sect.GetPackSize();
|
|
if (!testMode && !outStream)
|
|
continue;
|
|
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
RINOK(_stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));
|
|
streamSpec->Init(currentItemSize);
|
|
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
|
|
isOk = (copyCoderSpec->TotalSize == currentItemSize);
|
|
}
|
|
else
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
|
|
outStream.Release();
|
|
RINOK(extractCallback->SetOperationResult(isOk ?
|
|
checkSumOK ?
|
|
NExtract::NOperationResult::kOK:
|
|
NExtract::NOperationResult::kCRCError:
|
|
NExtract::NOperationResult::kDataError));
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
|
|
{
|
|
COM_TRY_BEGIN
|
|
*stream = 0;
|
|
|
|
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.SetCapacity(item.Size);
|
|
memcpy(referenceBuf->Buf, item.Buf, item.Size);
|
|
}
|
|
else
|
|
{
|
|
const CResItem &item = _items[mixItem.ResourceIndex];
|
|
size_t offset = item.Offset - sect.Va;
|
|
if (!CheckItem(sect, item, offset))
|
|
return S_FALSE;
|
|
referenceBuf->Buf.SetCapacity(item.HeaderSize + item.Size);
|
|
memcpy(referenceBuf->Buf, item.Header, item.HeaderSize);
|
|
memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size);
|
|
}
|
|
inStreamSpec->Init(referenceBuf);
|
|
|
|
*stream = streamTemp.Detach();
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
static IInArchive *CreateArc() { return new CHandler; }
|
|
|
|
static CArcInfo g_ArcInfo =
|
|
{ L"PE", L"exe dll sys", 0, 0xDD, { 'P', 'E', 0, 0 }, 4, false, CreateArc, 0 };
|
|
|
|
REGISTER_ARC(Pe)
|
|
|
|
}}
|