mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 15:14:59 -06:00
488 lines
12 KiB
C++
Executable File
488 lines
12 KiB
C++
Executable File
// MachoHandler.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "Common/Buffer.h"
|
|
#include "Common/ComTry.h"
|
|
|
|
#include "Windows/PropVariantUtils.h"
|
|
|
|
#include "../Common/LimitedStreams.h"
|
|
#include "../Common/ProgressUtils.h"
|
|
#include "../Common/RegisterArc.h"
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "../Compress/CopyCoder.h"
|
|
|
|
#include "Common/DummyOutStream.h"
|
|
|
|
static UInt32 Get32(const Byte *p, int be) { if (be) return GetBe32(p); return GetUi32(p); }
|
|
static UInt64 Get64(const Byte *p, int be) { if (be) return GetBe64(p); return GetUi64(p); }
|
|
|
|
using namespace NWindows;
|
|
|
|
namespace NArchive {
|
|
namespace NMacho {
|
|
|
|
#define MACH_ARCH_ABI64 (1 << 24)
|
|
#define MACH_MACHINE_386 7
|
|
#define MACH_MACHINE_ARM 12
|
|
#define MACH_MACHINE_SPARC 14
|
|
#define MACH_MACHINE_PPC 18
|
|
|
|
#define MACH_MACHINE_PPC64 (MACH_ARCH_ABI64 | MACH_MACHINE_PPC)
|
|
#define MACH_MACHINE_AMD64 (MACH_ARCH_ABI64 | MACH_MACHINE_386)
|
|
|
|
#define MACH_CMD_SEGMENT_32 1
|
|
#define MACH_CMD_SEGMENT_64 0x19
|
|
|
|
#define MACH_SECT_TYPE_MASK 0x000000FF
|
|
#define MACH_SECT_ATTR_MASK 0xFFFFFF00
|
|
|
|
#define MACH_SECT_ATTR_ZEROFILL 1
|
|
|
|
const char *g_SectTypes[] =
|
|
{
|
|
"REGULAR",
|
|
"ZEROFILL",
|
|
"CSTRINGS",
|
|
"4BYTE_LITERALS",
|
|
"8BYTE_LITERALS",
|
|
"LITERAL_POINTERS",
|
|
"NON_LAZY_SYMBOL_POINTERS",
|
|
"LAZY_SYMBOL_POINTERS",
|
|
"SYMBOL_STUBS",
|
|
"MOD_INIT_FUNC_POINTERS",
|
|
"MOD_TERM_FUNC_POINTERS",
|
|
"COALESCED",
|
|
"GB_ZEROFILL",
|
|
"INTERPOSING",
|
|
"16BYTE_LITERALS"
|
|
};
|
|
|
|
const char *g_FileTypes[] =
|
|
{
|
|
"0",
|
|
"OBJECT",
|
|
"EXECUTE",
|
|
"FVMLIB",
|
|
"CORE",
|
|
"PRELOAD",
|
|
"DYLIB",
|
|
"DYLINKER",
|
|
"BUNDLE",
|
|
"DYLIB_STUB",
|
|
"DSYM"
|
|
};
|
|
|
|
static const CUInt32PCharPair g_Flags[] =
|
|
{
|
|
{ (UInt32)1 << 31, "PURE_INSTRUCTIONS" },
|
|
{ 1 << 30, "NO_TOC" },
|
|
{ 1 << 29, "STRIP_STATIC_SYMS" },
|
|
{ 1 << 28, "NO_DEAD_STRIP" },
|
|
{ 1 << 27, "LIVE_SUPPORT" },
|
|
{ 1 << 26, "SELF_MODIFYING_CODE" },
|
|
{ 1 << 25, "DEBUG" },
|
|
{ 1 << 10, "SOME_INSTRUCTIONS" },
|
|
{ 1 << 9, "EXT_RELOC" },
|
|
{ 1 << 8, "LOC_RELOC" }
|
|
};
|
|
|
|
static const CUInt32PCharPair g_MachinePairs[] =
|
|
{
|
|
{ MACH_MACHINE_386, "x86" },
|
|
{ MACH_MACHINE_ARM, "ARM" },
|
|
{ MACH_MACHINE_SPARC, "SPARC" },
|
|
{ MACH_MACHINE_PPC, "PowerPC" },
|
|
{ MACH_MACHINE_PPC64, "PowerPC 64-bit" },
|
|
{ MACH_MACHINE_AMD64, "x64" }
|
|
};
|
|
|
|
static const int kNameSize = 16;
|
|
|
|
struct CSegment
|
|
{
|
|
char Name[kNameSize];
|
|
};
|
|
|
|
struct CSection
|
|
{
|
|
char Name[kNameSize];
|
|
char SegName[kNameSize];
|
|
UInt64 Va;
|
|
UInt64 Size;
|
|
UInt32 Pa;
|
|
UInt32 Flags;
|
|
int SegmentIndex;
|
|
UInt64 GetPackSize() const { return Flags == MACH_SECT_ATTR_ZEROFILL ? 0 : Size; }
|
|
};
|
|
|
|
|
|
class CHandler:
|
|
public IInArchive,
|
|
public CMyUnknownImp
|
|
{
|
|
CMyComPtr<IInStream> _inStream;
|
|
CObjectVector<CSegment> _segments;
|
|
CObjectVector<CSection> _sections;
|
|
bool _mode64;
|
|
bool _be;
|
|
UInt32 _machine;
|
|
UInt32 _type;
|
|
UInt32 _headersSize;
|
|
UInt64 _totalSize;
|
|
HRESULT Open2(ISequentialInStream *stream);
|
|
bool Parse(const Byte *buf, UInt32 size);
|
|
public:
|
|
MY_UNKNOWN_IMP1(IInArchive)
|
|
INTERFACE_IInArchive(;)
|
|
};
|
|
|
|
bool CHandler::Parse(const Byte *buf, UInt32 size)
|
|
{
|
|
bool mode64 = _mode64;
|
|
bool be = _be;
|
|
|
|
const Byte *bufStart = buf;
|
|
bool reduceCommands = false;
|
|
if (size < 512)
|
|
return false;
|
|
|
|
_machine = Get32(buf + 4, be);
|
|
_type = Get32(buf + 0xC, be);
|
|
|
|
UInt32 numCommands = Get32(buf + 0x10, be);
|
|
UInt32 commandsSize = Get32(buf + 0x14, be);
|
|
if (commandsSize > size)
|
|
return false;
|
|
|
|
if (commandsSize > (1 << 24) || numCommands > (1 << 18))
|
|
return false;
|
|
|
|
if (numCommands > 16)
|
|
{
|
|
reduceCommands = true;
|
|
numCommands = 16;
|
|
}
|
|
|
|
_headersSize = 0;
|
|
|
|
buf += 0x1C;
|
|
size -= 0x1C;
|
|
|
|
if (mode64)
|
|
{
|
|
buf += 4;
|
|
size -= 4;
|
|
}
|
|
|
|
_totalSize = (UInt32)(buf - bufStart);
|
|
if (commandsSize < size)
|
|
size = commandsSize;
|
|
|
|
for (UInt32 cmdIndex = 0; cmdIndex < numCommands; cmdIndex++)
|
|
{
|
|
if (size < 8)
|
|
return false;
|
|
UInt32 cmd = Get32(buf, be);
|
|
UInt32 cmdSize = Get32(buf + 4, be);
|
|
if (size < cmdSize)
|
|
return false;
|
|
if (cmd == MACH_CMD_SEGMENT_32 || cmd == MACH_CMD_SEGMENT_64)
|
|
{
|
|
UInt32 offs = (cmd == MACH_CMD_SEGMENT_64) ? 0x48 : 0x38;
|
|
if (cmdSize < offs)
|
|
break;
|
|
|
|
{
|
|
UInt64 vmAddr, vmSize, phAddr, phSize;
|
|
if (cmd == MACH_CMD_SEGMENT_64)
|
|
{
|
|
vmAddr = Get64(buf + 0x18, be);
|
|
vmSize = Get64(buf + 0x20, be);
|
|
phAddr = Get64(buf + 0x28, be);
|
|
phSize = Get64(buf + 0x30, be);
|
|
}
|
|
else
|
|
{
|
|
vmAddr = Get32(buf + 0x18, be);
|
|
vmSize = Get32(buf + 0x1C, be);
|
|
phAddr = Get32(buf + 0x20, be);
|
|
phSize = Get32(buf + 0x24, be);
|
|
}
|
|
{
|
|
UInt64 totalSize = phAddr + phSize;
|
|
if (totalSize > _totalSize)
|
|
_totalSize = totalSize;
|
|
}
|
|
}
|
|
|
|
CSegment seg;
|
|
memcpy(seg.Name, buf + 8, kNameSize);
|
|
_segments.Add(seg);
|
|
|
|
UInt32 numSections = Get32(buf + offs - 8, be);
|
|
if (numSections > (1 << 8))
|
|
return false;
|
|
|
|
while (numSections-- != 0)
|
|
{
|
|
CSection section;
|
|
UInt32 headerSize = (cmd == MACH_CMD_SEGMENT_64) ? 0x50 : 0x44;
|
|
const Byte *p = buf + offs;
|
|
if (cmdSize - offs < headerSize)
|
|
break;
|
|
if (cmd == MACH_CMD_SEGMENT_64)
|
|
{
|
|
section.Va = Get64(p + 0x20, be);
|
|
section.Size = Get64(p + 0x28, be);
|
|
section.Pa = Get32(p + 0x30, be);
|
|
section.Flags = Get32(p + 0x40, be);
|
|
}
|
|
else
|
|
{
|
|
section.Va = Get32(p + 0x20, be);
|
|
section.Size = Get32(p + 0x24, be);
|
|
section.Pa = Get32(p + 0x28, be);
|
|
section.Flags = Get32(p + 0x38, be);
|
|
}
|
|
memcpy(section.Name, p, kNameSize);
|
|
memcpy(section.SegName, p + kNameSize, kNameSize);
|
|
section.SegmentIndex = _segments.Size() - 1;
|
|
_sections.Add(section);
|
|
offs += headerSize;
|
|
}
|
|
if (offs != cmdSize)
|
|
return false;
|
|
}
|
|
buf += cmdSize;
|
|
size -= cmdSize;
|
|
}
|
|
_headersSize = (UInt32)(buf - bufStart);
|
|
return reduceCommands || (size == 0);
|
|
}
|
|
|
|
STATPROPSTG kArcProps[] =
|
|
{
|
|
{ NULL, kpidCpu, VT_BSTR},
|
|
{ NULL, kpidBit64, VT_BOOL},
|
|
{ NULL, kpidBigEndian, VT_BOOL},
|
|
{ NULL, kpidCharacts, VT_BSTR},
|
|
{ NULL, kpidPhySize, VT_UI8},
|
|
{ NULL, kpidHeadersSize, VT_UI4}
|
|
};
|
|
|
|
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
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
switch(propID)
|
|
{
|
|
case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _machine, prop); break;
|
|
case kpidCharacts: TYPE_TO_PROP(g_FileTypes, _type, prop); break;
|
|
case kpidPhySize: prop = _totalSize; break;
|
|
case kpidHeadersSize: prop = _headersSize; break;
|
|
case kpidBit64: if (_mode64) prop = _mode64; break;
|
|
case kpidBigEndian: if (_be) prop = _be; break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
static AString GetName(const char *name)
|
|
{
|
|
char res[kNameSize + 1];
|
|
memcpy(res, name, kNameSize);
|
|
res[kNameSize] = 0;
|
|
return res;
|
|
}
|
|
|
|
static AString SectFlagsToString(UInt32 flags)
|
|
{
|
|
AString res = TypeToString(g_SectTypes, sizeof(g_SectTypes) / sizeof(g_SectTypes[0]),
|
|
flags & MACH_SECT_TYPE_MASK);
|
|
AString s = FlagsToString(g_Flags, sizeof(g_Flags) / sizeof(g_Flags[0]),
|
|
flags & MACH_SECT_ATTR_MASK);
|
|
if (!s.IsEmpty())
|
|
{
|
|
res += ' ';
|
|
res += s;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
const CSection &item = _sections[index];
|
|
switch(propID)
|
|
{
|
|
case kpidPath: StringToProp(GetName(_segments[item.SegmentIndex].Name) + GetName(item.Name), prop); break;
|
|
case kpidSize: prop = (UInt64)item.Size; break;
|
|
case kpidPackSize: prop = (UInt64)item.GetPackSize(); break;
|
|
case kpidCharacts: StringToProp(SectFlagsToString(item.Flags), prop); break;
|
|
case kpidOffset: prop = item.Pa; break;
|
|
case kpidVa: prop = item.Va; break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
HRESULT CHandler::Open2(ISequentialInStream *stream)
|
|
{
|
|
const UInt32 kBufSize = 1 << 18;
|
|
const UInt32 kSigSize = 4;
|
|
|
|
CByteBuffer buffer;
|
|
buffer.SetCapacity(kBufSize);
|
|
Byte *buf = buffer;
|
|
|
|
size_t processed = kSigSize;
|
|
RINOK(ReadStream_FALSE(stream, buf, processed));
|
|
UInt32 sig = GetUi32(buf);
|
|
bool be, mode64;
|
|
switch(sig)
|
|
{
|
|
case 0xCEFAEDFE: be = true; mode64 = false; break;
|
|
case 0xCFFAEDFE: be = true; mode64 = true; break;
|
|
case 0xFEEDFACE: be = false; mode64 = false; break;
|
|
case 0xFEEDFACF: be = false; mode64 = true; break;
|
|
default: return S_FALSE;
|
|
}
|
|
processed = kBufSize - kSigSize;
|
|
RINOK(ReadStream(stream, buf + kSigSize, &processed));
|
|
_mode64 = mode64;
|
|
_be = be;
|
|
return Parse(buf, (UInt32)processed + kSigSize) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Open(IInStream *inStream,
|
|
const UInt64 * /* maxCheckStartPosition */,
|
|
IArchiveOpenCallback * /* openArchiveCallback */)
|
|
{
|
|
COM_TRY_BEGIN
|
|
Close();
|
|
RINOK(Open2(inStream));
|
|
_inStream = inStream;
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_inStream.Release();
|
|
_sections.Clear();
|
|
_segments.Clear();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
|
{
|
|
*numItems = _sections.Size();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
|
Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
bool testMode = (_aTestMode != 0);
|
|
bool allFilesMode = (numItems == UInt32(-1));
|
|
if (allFilesMode)
|
|
numItems = _sections.Size();
|
|
if (numItems == 0)
|
|
return S_OK;
|
|
UInt64 totalSize = 0;
|
|
UInt32 i;
|
|
for (i = 0; i < numItems; i++)
|
|
totalSize += _sections[allFilesMode ? i : indices[i]].GetPackSize();
|
|
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(_inStream);
|
|
|
|
CDummyOutStream *outStreamSpec = new CDummyOutStream;
|
|
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
|
|
|
|
for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
|
|
{
|
|
lps->InSize = lps->OutSize = currentTotalSize;
|
|
RINOK(lps->SetCur());
|
|
Int32 askMode = testMode ?
|
|
NArchive::NExtract::NAskMode::kTest :
|
|
NArchive::NExtract::NAskMode::kExtract;
|
|
UInt32 index = allFilesMode ? i : indices[i];
|
|
const CSection &item = _sections[index];
|
|
currentItemSize = item.GetPackSize();
|
|
{
|
|
CMyComPtr<ISequentialOutStream> realOutStream;
|
|
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
|
if (!testMode && (!realOutStream))
|
|
continue;
|
|
outStreamSpec->SetStream(realOutStream);
|
|
outStreamSpec->Init();
|
|
}
|
|
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
RINOK(_inStream->Seek(item.Pa, STREAM_SEEK_SET, NULL));
|
|
streamSpec->Init(currentItemSize);
|
|
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
|
|
outStreamSpec->ReleaseStream();
|
|
RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == currentItemSize) ?
|
|
|
|
NArchive::NExtract::NOperationResult::kOK:
|
|
|
|
NArchive::NExtract::NOperationResult::kDataError));
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
static IInArchive *CreateArc() { return new CHandler; }
|
|
|
|
static CArcInfo g_ArcInfo =
|
|
{ L"MachO", L"", 0, 0xDF, { 0 }, 0, false, CreateArc, 0 };
|
|
|
|
REGISTER_ARC(Macho)
|
|
|
|
}}
|
|
|