This commit is contained in:
Igor Pavlov
2017-04-30 00:00:00 +00:00
committed by Kornel
parent 603abd5528
commit 2efa10565a
442 changed files with 15479 additions and 8525 deletions
+256 -77
View File
@@ -183,6 +183,30 @@ struct CExtraFile
};
#endif
struct CForkPair
{
UInt64 Offset;
UInt64 Len;
void Parse(const Byte *p)
{
Offset = Get64(p);
Len = Get64(p + 8);
}
bool UpdateTop(UInt64 limit, UInt64 &top)
{
if (Offset > limit || Len > limit - Offset)
return false;
UInt64 top2 = Offset + Len;
if (top <= top2)
top = top2;
return true;
}
};
class CHandler:
public IInArchive,
public IInArchiveGetStream,
@@ -191,14 +215,19 @@ class CHandler:
CMyComPtr<IInStream> _inStream;
CObjectVector<CFile> _files;
bool _masterCrcError;
bool _headersError;
UInt64 _startPos;
UInt64 _phySize;
AString _name;
#ifdef DMG_SHOW_RAW
CObjectVector<CExtraFile> _extras;
#endif
HRESULT ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf);
bool ParseBlob(const CByteBuffer &data);
HRESULT Open2(IInStream *stream);
HRESULT Extract(IInStream *stream);
public:
@@ -249,24 +278,20 @@ void CMethods::GetString(AString &res) const
case METHOD_BZIP2: s = "BZip2"; break;
default: ConvertUInt32ToString(type, buf); s = buf;
}
res.Add_Space_if_NotEmpty();
res += s;
res.Add_OptSpaced(s);
}
for (i = 0; i < ChecksumTypes.Size(); i++)
{
res.Add_Space_if_NotEmpty();
UInt32 type = ChecksumTypes[i];
char buf[32];
const char *s;
switch (type)
{
case kCheckSumType_CRC: s = "CRC"; break;
case kCheckSumType_CRC: res += "CRC"; break;
default:
ConvertUInt32ToString(type, MyStpCpy(buf, "Check"));
s = buf;
res += "Check";
res.Add_UInt32(type);
}
res.Add_Space_if_NotEmpty();
res += s;
}
}
@@ -308,7 +333,8 @@ IMP_IInArchive_Props
static const Byte kArcProps[] =
{
kpidMethod,
kpidNumBlocks
kpidNumBlocks,
kpidComment
};
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
@@ -372,9 +398,30 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
case kpidWarning:
if (_masterCrcError)
prop = "Master CRC error";
case kpidWarningFlags:
{
UInt32 v = 0;
if (_headersError) v |= kpv_ErrorFlags_HeadersError;
if (v != 0)
prop = v;
break;
}
case kpidOffset: prop = _startPos; break;
case kpidPhySize: prop = _phySize; break;
case kpidComment:
if (!_name.IsEmpty() && _name.Len() < 256)
prop = _name;
break;
case kpidName:
if (!_name.IsEmpty() && _name.Len() < 256)
{
prop = _name + ".dmg";
}
break;
}
prop.Detach(value);
return S_OK;
@@ -461,7 +508,7 @@ HRESULT CFile::Parse(const Byte *p, UInt32 size)
return S_OK;
}
static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag)
static int FindKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
{
for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++)
{
@@ -472,7 +519,7 @@ static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &
return -1;
}
static const AString *GetStringFromKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag)
static const AString *GetStringFromKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
{
int index = FindKeyPair(item, key, nextTag);
if (index >= 0)
@@ -498,6 +545,83 @@ static inline bool IsKoly(const Byte *p)
*/
}
HRESULT CHandler::ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf)
{
size_t size = (size_t)pair.Len;
if (size != pair.Len)
return E_OUTOFMEMORY;
buf.Alloc(size);
RINOK(stream->Seek(_startPos + pair.Offset, STREAM_SEEK_SET, NULL));
return ReadStream_FALSE(stream, buf, size);
}
bool CHandler::ParseBlob(const CByteBuffer &data)
{
if (data.Size() < 12)
return false;
const Byte *p = (const Byte *)data;
if (Get32(p) != 0xFADE0CC0)
return true;
const UInt32 size = Get32(p + 4);
if (size != data.Size())
return false;
const UInt32 num = Get32(p + 8);
if (num > (size - 12) / 8)
return false;
for (UInt32 i = 0; i < num; i++)
{
// UInt32 type = Get32(p + i * 8 + 12);
UInt32 offset = Get32(p + i * 8 + 12 + 4);
if (size - offset < 8)
return false;
const Byte *p2 = (const Byte *)data + offset;
const UInt32 magic = Get32(p2);
const UInt32 len = Get32(p2 + 4);
if (size - offset < len || len < 8)
return false;
#ifdef DMG_SHOW_RAW
CExtraFile &extra = _extras.AddNew();
extra.Name = "_blob_";
extra.Data.CopyFrom(p2, len);
#endif
if (magic == 0xFADE0C02)
{
#ifdef DMG_SHOW_RAW
extra.Name += "codedir";
#endif
if (len < 11 * 4)
return false;
UInt32 idOffset = Get32(p2 + 0x14);
if (idOffset >= len)
return false;
UInt32 len2 = len - idOffset;
if (len2 < (1 << 10))
_name.SetFrom_CalcLen((const char *)(p2 + idOffset), len2);
}
#ifdef DMG_SHOW_RAW
else if (magic == 0xFADE0C01)
extra.Name += "requirements";
else if (magic == 0xFADE0B01)
extra.Name += "signed";
else
{
char temp[16];
ConvertUInt32ToHex8Digits(magic, temp);
extra.Name += temp;
}
#endif
}
return true;
}
HRESULT CHandler::Open2(IInStream *stream)
{
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos));
@@ -522,35 +646,69 @@ HRESULT CHandler::Open2(IInStream *stream)
// UInt32 flags = Get32(buf + 12);
// UInt64 runningDataForkOffset = Get64(buf + 0x10);
UInt64 dataForkOffset = Get64(buf + 0x18);
UInt64 dataForkLen = Get64(buf + 0x20);
UInt64 rsrcOffset = Get64(buf + 0x28);
UInt64 rsrcLen = Get64(buf + 0x30);
CForkPair dataForkPair, rsrcPair, xmlPair, blobPair;
dataForkPair.Parse(buf + 0x18);
rsrcPair.Parse(buf + 0x28);
xmlPair.Parse(buf + 0xD8);
blobPair.Parse(buf + 0x128);
// UInt32 segmentNumber = Get32(buf + 0x38);
// UInt32 segmentCount = Get32(buf + 0x3C);
// Byte segmentGUID[16];
// CChecksum dataForkChecksum;
// dataForkChecksum.Parse(buf + 0x50);
UInt64 xmlOffset = Get64(buf + 0xD8);
UInt64 xmlLen = Get64(buf + 0xE0);
if ( headerPos < dataForkOffset
|| headerPos - dataForkOffset < dataForkLen
|| headerPos < rsrcOffset
|| headerPos - rsrcOffset < rsrcLen
|| headerPos < xmlOffset
|| headerPos - xmlOffset < xmlLen)
return S_FALSE;
_startPos = 0;
UInt64 totalLen = dataForkLen + rsrcLen + xmlLen;
if (totalLen > headerPos)
return S_FALSE;
_startPos = headerPos - totalLen;
_phySize = totalLen + HEADER_SIZE;
headerPos = totalLen;
UInt64 top = 0;
if (!dataForkPair.UpdateTop(headerPos, top)) return S_FALSE;
if (!xmlPair.UpdateTop(headerPos, top)) return S_FALSE;
if (!rsrcPair.UpdateTop(headerPos, top)) return S_FALSE;
/* Some old dmg files contain garbage data in blobPair field.
So we need to ignore such garbage case;
And we still need to detect offset of start of archive for "parser" mode. */
bool useBlob = blobPair.UpdateTop(headerPos, top);
_startPos = 0;
_phySize = headerPos + HEADER_SIZE;
if (top != headerPos)
{
CForkPair xmlPair2 = xmlPair;
const char *sz = "<?xml version";
const unsigned len = (unsigned)strlen(sz);
if (xmlPair2.Len > len)
xmlPair2.Len = len;
CByteBuffer buf2;
if (ReadData(stream, xmlPair2, buf2) != S_OK
|| memcmp(buf2, sz, len) != 0)
{
_startPos = headerPos - top;
_phySize = top + HEADER_SIZE;
}
}
// Byte reserved[0x78]
if (useBlob && blobPair.Len != 0)
{
#ifdef DMG_SHOW_RAW
CExtraFile &extra = _extras.AddNew();
extra.Name = "_blob.bin";
CByteBuffer &blobBuf = extra.Data;
#else
CByteBuffer blobBuf;
#endif
RINOK(ReadData(stream, blobPair, blobBuf));
if (!ParseBlob(blobBuf))
_headersError = true;
}
CChecksum masterChecksum;
masterChecksum.Parse(buf + 0x160);
@@ -562,7 +720,7 @@ HRESULT CHandler::Open2(IInStream *stream)
// We don't know the size of the field "offset" in rsrc.
// We suppose that it uses 24 bits. So we use Rsrc, only if the rsrcLen < (1 << 24).
bool useRsrc = (rsrcLen > RSRC_HEAD_SIZE && rsrcLen < ((UInt32)1 << 24));
bool useRsrc = (rsrcPair.Len > RSRC_HEAD_SIZE && rsrcPair.Len < ((UInt32)1 << 24));
// useRsrc = false;
if (useRsrc)
@@ -575,21 +733,18 @@ HRESULT CHandler::Open2(IInStream *stream)
CByteBuffer rsrcBuf;
#endif
size_t rsrcLenT = (size_t)rsrcLen;
rsrcBuf.Alloc(rsrcLenT);
RINOK(stream->Seek(_startPos + rsrcOffset, STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(stream, rsrcBuf, rsrcLenT));
RINOK(ReadData(stream, rsrcPair, rsrcBuf));
const Byte *p = rsrcBuf;
UInt32 headSize = Get32(p + 0);
UInt32 footerOffset = Get32(p + 4);
UInt32 mainDataSize = Get32(p + 8);
UInt32 footerSize = Get32(p + 12);
if (headSize != RSRC_HEAD_SIZE ||
footerOffset >= rsrcLenT ||
mainDataSize >= rsrcLenT ||
footerOffset + footerSize != rsrcLenT ||
footerOffset != headSize + mainDataSize)
if (headSize != RSRC_HEAD_SIZE
|| footerOffset >= rsrcPair.Len
|| mainDataSize >= rsrcPair.Len
|| footerOffset + footerSize != rsrcPair.Len
|| footerOffset != headSize + mainDataSize)
return S_FALSE;
if (footerSize < 16)
return S_FALSE;
@@ -600,7 +755,7 @@ HRESULT CHandler::Open2(IInStream *stream)
if ((UInt32)Get16(p + 0x18) != 0x1C)
return S_FALSE;
UInt32 namesOffset = Get16(p + 0x1A);
const UInt32 namesOffset = Get16(p + 0x1A);
if (namesOffset > footerSize)
return S_FALSE;
@@ -612,12 +767,15 @@ HRESULT CHandler::Open2(IInStream *stream)
{
const Byte *p2 = p + 0x1E + i * 8;
UInt32 typeId = Get32(p2);
const UInt32 typeId = Get32(p2);
#ifndef DMG_SHOW_RAW
if (typeId != 0x626C6B78) // blkx
continue;
#endif
UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
UInt32 offs = Get16(p2 + 6);
const UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
const UInt32 offs = Get16(p2 + 6);
if (0x1C + offs + 12 * numFiles > namesOffset)
return S_FALSE;
@@ -625,7 +783,7 @@ HRESULT CHandler::Open2(IInStream *stream)
{
const Byte *p3 = p + 0x1C + offs + k * 12;
// UInt32 id = Get16(p3);
UInt32 namePos = Get16(p3 + 2);
const UInt32 namePos = Get16(p3 + 2);
// Byte attributes = p3[4]; // = 0x50 for blkx
// we don't know how many bits we can use. So we use 24 bits only
UInt32 blockOffset = Get32(p3 + 4);
@@ -634,21 +792,12 @@ HRESULT CHandler::Open2(IInStream *stream)
if (blockOffset + 4 >= mainDataSize)
return S_FALSE;
const Byte *pBlock = rsrcBuf + headSize + blockOffset;
UInt32 blockSize = Get32(pBlock);
#ifdef DMG_SHOW_RAW
{
CExtraFile &extra = _extras.AddNew();
{
char extraName[16];
ConvertUInt32ToString(_files.Size(), extraName);
extra.Name = extraName;
}
extra.Data.CopyFrom(pBlock + 4, blockSize);
}
#endif
const UInt32 blockSize = Get32(pBlock);
if (mainDataSize - (blockOffset + 4) < blockSize)
return S_FALSE;
AString name;
CFile &file = _files.AddNew();
if (namePos != 0xFFFF)
{
UInt32 namesBlockSize = footerSize - namesOffset;
@@ -663,22 +812,56 @@ HRESULT CHandler::Open2(IInStream *stream)
Byte c = namePtr[r];
if (c < 0x20 || c >= 0x80)
break;
file.Name += (char)c;
name += (char)c;
}
}
RINOK(file.Parse(pBlock + 4, blockSize));
if (typeId == 0x626C6B78) // blkx
{
CFile &file = _files.AddNew();
file.Name = name;
RINOK(file.Parse(pBlock + 4, blockSize));
}
#ifdef DMG_SHOW_RAW
{
AString name2;
name2.Add_UInt32(i);
name2 += '_';
{
char temp[4 + 1] = { 0 };
memcpy(temp, p2, 4);
name2 += temp;
}
name2.Trim();
name2 += '_';
name2.Add_UInt32(k);
if (!name.IsEmpty())
{
name2 += '_';
name2 += name;
}
CExtraFile &extra = _extras.AddNew();
extra.Name = name2;
extra.Data.CopyFrom(pBlock + 4, blockSize);
}
#endif
}
}
}
else
{
if (xmlLen >= kXmlSizeMax || xmlLen == 0)
if (xmlPair.Len >= kXmlSizeMax || xmlPair.Len == 0)
return S_FALSE;
size_t size = (size_t)xmlLen;
if (size != xmlLen)
size_t size = (size_t)xmlPair.Len;
if (size != xmlPair.Len)
return S_FALSE;
RINOK(stream->Seek(_startPos + dataForkLen, STREAM_SEEK_SET, NULL));
RINOK(stream->Seek(_startPos + xmlPair.Offset, STREAM_SEEK_SET, NULL));
CXml xml;
{
@@ -733,16 +916,12 @@ HRESULT CHandler::Open2(IInStream *stream)
const Byte *endPtr = Base64ToBin(rawBuf, *dataString);
if (!endPtr)
return S_FALSE;
destLen = (unsigned)(endPtr - rawBuf);
destLen = (unsigned)(endPtr - (const Byte *)rawBuf);
}
#ifdef DMG_SHOW_RAW
CExtraFile &extra = _extras.AddNew();
{
char extraName[16];
ConvertUInt32ToString(_files.Size(), extraName);
extra.Name = extraName;
}
extra.Name.Add_UInt32(_files.Size());
extra.Data.CopyFrom(rawBuf, destLen);
#endif
}
@@ -800,6 +979,8 @@ STDMETHODIMP CHandler::Close()
_inStream.Release();
_files.Clear();
_masterCrcError = false;
_headersError = false;
_name.Empty();
#ifdef DMG_SHOW_RAW
_extras.Clear();
#endif
@@ -867,9 +1048,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
case kpidPath:
{
UString name;
wchar_t s[16];
ConvertUInt32ToString(index, s);
name = s;
name.Add_UInt32(index);
unsigned num = 10;
unsigned numDigits;
for (numDigits = 1; num < _files.Size(); numDigits++)
@@ -908,7 +1087,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
}
UString name2;
ConvertUTF8ToUnicode(subName, name2);
name += L'.';
name += '.';
name += name2;
}
else
@@ -916,7 +1095,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
UString name2;
ConvertUTF8ToUnicode(item.Name, name2);
if (!name2.IsEmpty())
name.AddAscii(" - ");
name += "_";
name += name2;
}
prop = name;