// UefiHandler.cpp #include "StdAfx.h" // #define SHOW_DEBUG_INFO #ifdef SHOW_DEBUG_INFO #include #endif #include "../../../C/7zCrc.h" #include "../../../C/Alloc.h" #include "../../../C/CpuArch.h" #include "../../../C/LzmaDec.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Common/MyBuffer.h" #include "../../Common/StringConvert.h" #include "../../Windows/PropVariantUtils.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamObjects.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" #include "../Compress/LzhDecoder.h" #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) #define Get24(p) (Get32(p) & 0xFFFFFF) namespace NArchive { namespace NUefi { static const size_t kBufTotalSizeMax = (1 << 29); static const unsigned kNumFilesMax = (1 << 18); static const unsigned kLevelMax = 64; static const unsigned kFvHeaderSize = 0x38; static const unsigned kGuidSize = 16; #define CAPSULE_SIGNATURE \ { 0xBD,0x86,0x66,0x3B,0x76,0x0D,0x30,0x40,0xB7,0x0E,0xB5,0x51,0x9E,0x2F,0xC5,0xA0 } static const Byte kCapsuleSig[kGuidSize] = CAPSULE_SIGNATURE; static const unsigned kFfsGuidOffset = 16; #define FFS_SIGNATURE \ { 0xD9,0x54,0x93,0x7A,0x68,0x04,0x4A,0x44,0x81,0xCE,0x0B,0xF6,0x17,0xD8,0x90,0xDF } static const Byte k_FFS_Guid[kGuidSize] = FFS_SIGNATURE; static const Byte k_MacFS_Guid[kGuidSize] = { 0xAD,0xEE,0xAD,0x04,0xFF,0x61,0x31,0x4D,0xB6,0xBA,0x64,0xF8,0xBF,0x90,0x1F,0x5A }; static const UInt32 kFvSignature = 0x4856465F; // "_FVH" static const Byte kGuids[][kGuidSize] = { { 0xB0,0xCD,0x1B,0xFC,0x31,0x7D,0xAA,0x49,0x93,0x6A,0xA4,0x60,0x0D,0x9D,0xD0,0x83 }, { 0x2E,0x06,0xA0,0x1B,0x79,0xC7,0x82,0x45,0x85,0x66,0x33,0x6A,0xE8,0xF7,0x8F,0x09 }, { 0x25,0x4E,0x37,0x7E,0x01,0x8E,0xEE,0x4F,0x87,0xf2,0x39,0x0C,0x23,0xC6,0x06,0xCD }, { 0x97,0xE5,0x1B,0x16,0xC5,0xE9,0xDB,0x49,0xAE,0x50,0xC4,0x62,0xAB,0x54,0xEE,0xDA }, { 0xDB,0x7F,0xAD,0x77,0x2A,0xDF,0x02,0x43,0x88,0x98,0xC7,0x2E,0x4C,0xDB,0xD0,0xF4 }, { 0xAB,0x71,0xCF,0xF5,0x4B,0xB0,0x7E,0x4B,0x98,0x8A,0xD8,0xA0,0xD4,0x98,0xE6,0x92 }, { 0x91,0x45,0x53,0x7A,0xCE,0x37,0x81,0x48,0xB3,0xC9,0x71,0x38,0x14,0xF4,0x5D,0x6B }, { 0x84,0xE6,0x7A,0x36,0x5D,0x33,0x71,0x46,0xA1,0x6D,0x89,0x9D,0xBF,0xEA,0x6B,0x88 }, { 0x98,0x07,0x40,0x24,0x07,0x38,0x42,0x4A,0xB4,0x13,0xA1,0xEC,0xEE,0x20,0x5D,0xD8 }, { 0xEE,0xA2,0x3F,0x28,0x2C,0x53,0x4D,0x48,0x93,0x83,0x9F,0x93,0xB3,0x6F,0x0B,0x7E }, { 0x9B,0xD5,0xB8,0x98,0xBA,0xE8,0xEE,0x48,0x98,0xDD,0xC2,0x95,0x39,0x2F,0x1E,0xDB }, { 0x09,0x6D,0xE3,0xC3,0x94,0x82,0x97,0x4B,0xA8,0x57,0xD5,0x28,0x8F,0xE3,0x3E,0x28 }, { 0x18,0x88,0x53,0x4A,0xE0,0x5A,0xB2,0x4E,0xB2,0xEB,0x48,0x8B,0x23,0x65,0x70,0x22 } }; static const char * const kGuidNames[] = { "CRC" , "VolumeTopFile" , "ACPI" , "ACPI2" , "Main" , "Intel32" , "Intel64" , "Intel32c" , "Intel64c" , "MacVolume" , "MacUpdate.txt" , "MacName" , "Insyde" }; enum { kGuidIndex_CRC = 0 }; struct CSigExtPair { const char *ext; unsigned sigSize; Byte sig[16]; }; static const CSigExtPair g_Sigs[] = { { "bmp", 2, { 'B','M' } }, { "riff", 4, { 'R','I','F','F' } }, { "pe", 2, { 'M','Z'} }, { "gif", 6, { 'G','I','F','8','9', 'a' } }, { "png", 8, { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A } }, { "jpg", 10, { 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46 } }, { "rom", 2, { 0x55,0xAA } } }; enum { kSig_BMP, kSig_RIFF, kSig_PE }; static const char *FindExt(const Byte *p, size_t size) { unsigned i; for (i = 0; i < ARRAY_SIZE(g_Sigs); i++) { const CSigExtPair &pair = g_Sigs[i]; if (size >= pair.sigSize) if (memcmp(p, pair.sig, pair.sigSize) == 0) break; } if (i == ARRAY_SIZE(g_Sigs)) return NULL; switch (i) { case kSig_BMP: if (GetUi32(p + 2) > size || GetUi32(p + 0xA) > size) return NULL; break; case kSig_RIFF: if (GetUi32(p + 8) == 0x45564157 || GetUi32(p + 0xC) == 0x20746D66 ) return "wav"; break; case kSig_PE: { if (size < 512) return NULL; UInt32 peOffset = GetUi32(p + 0x3C); if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0) return NULL; if (GetUi32(p + peOffset) != 0x00004550) return NULL; break; } } return g_Sigs[i].ext; } static bool AreGuidsEq(const Byte *p1, const Byte *p2) { return memcmp(p1, p2, kGuidSize) == 0; } static int FindGuid(const Byte *p) { for (unsigned i = 0; i < ARRAY_SIZE(kGuids); i++) if (AreGuidsEq(p, kGuids[i])) return i; return -1; } static bool IsFfs(const Byte *p) { return (Get32(p + 0x28) == kFvSignature && AreGuidsEq(p + kFfsGuidOffset, k_FFS_Guid)); } #define FVB_ERASE_POLARITY (1 << 11) /* static const CUInt32PCharPair g_FV_Attribs[] = { { 0, "ReadDisabledCap" }, { 1, "ReadEnabledCap" }, { 2, "ReadEnabled" }, { 3, "WriteDisabledCap" }, { 4, "WriteEnabledCap" }, { 5, "WriteEnabled" }, { 6, "LockCap" }, { 7, "Locked" }, { 9, "StickyWrite" }, { 10, "MemoryMapped" }, { 11, "ErasePolarity" }, { 12, "ReadLockCap" }, { 13, "WriteLockCap" }, { 14, "WriteLockCap" } }; */ enum { FV_FILETYPE_ALL, FV_FILETYPE_RAW, FV_FILETYPE_FREEFORM, FV_FILETYPE_SECURITY_CORE, FV_FILETYPE_PEI_CORE, FV_FILETYPE_DXE_CORE, FV_FILETYPE_PEIM, FV_FILETYPE_DRIVER, FV_FILETYPE_COMBINED_PEIM_DRIVER, FV_FILETYPE_APPLICATION, // The value 0x0A is reserved and should not be used FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0x0B, // types 0xF0 - 0xFF are FFS file types FV_FILETYPE_FFS_PAD = 0xF0 }; static const char *g_FileTypes[] = { "ALL" , "RAW" , "FREEFORM" , "SECURITY_CORE" , "PEI_CORE" , "DXE_CORE" , "PEIM" , "DRIVER" , "COMBINED_PEIM_DRIVER" , "APPLICATION" , "0xA" , "VOLUME" }; // typedef Byte FFS_FILE_ATTRIBUTES; // FFS File Attributes #define FFS_ATTRIB_TAIL_PRESENT 0x01 // #define FFS_ATTRIB_RECOVERY 0x02 // #define FFS_ATTRIB_HEADER_EXTENSION 0x04 // #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 static const CUInt32PCharPair g_FFS_FILE_ATTRIBUTES[] = { { 0, "" /* "TAIL" */ }, { 1, "RECOVERY" }, // { 2, "HEADER_EXTENSION" }, // reserved for future { 6, "" /* "CHECKSUM" */ } }; // static const Byte g_Allignment[8] = { 3, 4, 7, 9, 10, 12, 15, 16 }; // typedef Byte FFS_FILE_STATE; // Look also FVB_ERASE_POLARITY. // Lower-order State bits are superceded by higher-order State bits. // #define FILE_HEADER_CONSTRUCTION 0x01 // #define FILE_HEADER_VALID 0x02 #define FILE_DATA_VALID 0x04 // #define FILE_MARKED_FOR_UPDATE 0x08 // #define FILE_DELETED 0x10 // #define FILE_HEADER_INVALID 0x20 // SECTION_TYPE #define SECTION_ALL 0x00 #define SECTION_COMPRESSION 0x01 #define SECTION_GUID_DEFINED 0x02 // Leaf section Type values #define SECTION_PE32 0x10 #define SECTION_PIC 0x11 #define SECTION_TE 0x12 #define SECTION_DXE_DEPEX 0x13 #define SECTION_VERSION 0x14 #define SECTION_USER_INTERFACE 0x15 #define SECTION_COMPATIBILITY16 0x16 #define SECTION_FIRMWARE_VOLUME_IMAGE 0x17 #define SECTION_FREEFORM_SUBTYPE_GUID 0x18 #define SECTION_RAW 0x19 #define SECTION_PEI_DEPEX 0x1B // #define GUIDED_SECTION_PROCESSING_REQUIRED 0x01 // #define GUIDED_SECTION_AUTH_STATUS_VALID 0x02 static const CUInt32PCharPair g_GUIDED_SECTION_ATTRIBUTES[] = { { 0, "PROCESSING_REQUIRED" }, { 1, "AUTH" } }; static const CUInt32PCharPair g_SECTION_TYPE[] = { { 0x01, "COMPRESSION" }, { 0x02, "GUID" }, { 0x10, "efi" }, { 0x11, "PIC" }, { 0x12, "te" }, { 0x13, "DXE_DEPEX" }, { 0x14, "VERSION" }, { 0x15, "USER_INTERFACE" }, { 0x16, "COMPATIBILITY16" }, { 0x17, "VOLUME" }, { 0x18, "FREEFORM_SUBTYPE_GUID" }, { 0x19, "raw" }, { 0x1B, "PEI_DEPEX" } }; #define COMPRESSION_TYPE_NONE 0 #define COMPRESSION_TYPE_LZH 1 #define COMPRESSION_TYPE_LZMA 2 static const char * const g_Methods[] = { "COPY" , "LZH" , "LZMA" }; static AString UInt32ToString(UInt32 val) { char sz[16]; ConvertUInt32ToString(val, sz); return sz; } static void ConvertByteToHex(unsigned value, char *s) { for (int i = 0; i < 2; i++) { unsigned t = value & 0xF; value >>= 4; s[1 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } } static AString GuidToString(const Byte *p, bool full) { char s[16 * 2 + 8]; int i; for (i = 0; i < 4; i++) ConvertByteToHex(p[3 - i], s + i * 2); s[8] = 0; if (full) { s[8] = '-'; for (i = 4; i < kGuidSize; i++) ConvertByteToHex(p[i], s + 1 + i * 2); s[32 + 1] = 0; } return s; } static const char * const kExpressionCommands[] = { "BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR" }; static bool ParseDepedencyExpression(const Byte *p, UInt32 size, AString &res) { res.Empty(); for (UInt32 i = 0; i < size;) { unsigned command = p[i++]; if (command > ARRAY_SIZE(kExpressionCommands)) return false; res += kExpressionCommands[command]; if (command < 3) { if (i + kGuidSize > size) return false; res.Add_Space(); res += GuidToString(p + i, false); i += kGuidSize; } res += "; "; } return true; } static bool ParseUtf16zString(const Byte *p, UInt32 size, UString &res) { if ((size & 1) != 0) return false; res.Empty(); UInt32 i; for (i = 0; i < size; i += 2) { wchar_t c = Get16(p + i); if (c == 0) break; res += c; } return (i == size - 2); } static bool ParseUtf16zString2(const Byte *p, UInt32 size, AString &res) { UString s; if (!ParseUtf16zString(p, size, s)) return false; res = UnicodeStringToMultiByte(s); return true; } #define FLAGS_TO_STRING(pairs, value) FlagsToString(pairs, ARRAY_SIZE(pairs), value) #define TYPE_TO_STRING(table, value) TypeToString(table, ARRAY_SIZE(table), value) #define TYPE_PAIR_TO_STRING(table, value) TypePairToString(table, ARRAY_SIZE(table), value) static const UInt32 kFileHeaderSize = 24; static void AddSpaceAndString(AString &res, const AString &newString) { if (!newString.IsEmpty()) { res.Add_Space_if_NotEmpty(); res += newString; } } class CFfsFileHeader { Byte CheckHeader; Byte CheckFile; Byte Attrib; Byte State; UInt16 GetTailReference() const { return (UInt16)(CheckHeader | ((UInt16)CheckFile << 8)); } UInt32 GetTailSize() const { return IsThereTail() ? 2 : 0; } bool IsThereFileChecksum() const { return (Attrib & FFS_ATTRIB_CHECKSUM) != 0; } bool IsThereTail() const { return (Attrib & FFS_ATTRIB_TAIL_PRESENT) != 0; } public: Byte GuidName[kGuidSize]; Byte Type; UInt32 Size; bool Parse(const Byte *p) { int i; for (i = 0; i < kFileHeaderSize; i++) if (p[i] != 0xFF) break; if (i == kFileHeaderSize) return false; memcpy(GuidName, p, kGuidSize); CheckHeader = p[0x10]; CheckFile = p[0x11]; Type = p[0x12]; Attrib = p[0x13]; Size = Get24(p + 0x14); State = p[0x17]; return true; } UInt32 GetDataSize() const { return Size - kFileHeaderSize - GetTailSize(); } UInt32 GetDataSize2(UInt32 rem) const { return rem - kFileHeaderSize - GetTailSize(); } bool Check(const Byte *p, UInt32 size) { if (Size > size) return false; UInt32 tailSize = GetTailSize(); if (Size < kFileHeaderSize + tailSize) return false; { unsigned checkSum = 0; for (UInt32 i = 0; i < kFileHeaderSize; i++) checkSum += p[i]; checkSum -= p[0x17]; checkSum -= p[0x11]; if ((Byte)checkSum != 0) return false; } if (IsThereFileChecksum()) { unsigned checkSum = 0; UInt32 checkSize = Size - tailSize; for (UInt32 i = 0; i < checkSize; i++) checkSum += p[i]; checkSum -= p[0x17]; if ((Byte)checkSum != 0) return false; } if (IsThereTail()) if (GetTailReference() != (UInt16)~Get16(p + Size - 2)) return false; int polarity = 0; int i; for (i = 5; i >= 0; i--) if (((State >> i) & 1) == polarity) { // AddSpaceAndString(s, g_FFS_FILE_STATE_Flags[i]); if ((1 << i) != FILE_DATA_VALID) return false; break; } if (i < 0) return false; return true; } AString GetCharacts() const { AString s; if (Type == FV_FILETYPE_FFS_PAD) s += "PAD"; else s += TYPE_TO_STRING(g_FileTypes, Type); AddSpaceAndString(s, FLAGS_TO_STRING(g_FFS_FILE_ATTRIBUTES, Attrib & 0xC7)); /* int align = (Attrib >> 3) & 7; if (align != 0) { s += " Align:"; s += UInt32ToString((UInt32)1 << g_Allignment[align]); } */ return s; } }; #define G32(_offs_, dest) dest = Get32(p + (_offs_)); struct CCapsuleHeader { UInt32 HeaderSize; UInt32 Flags; UInt32 CapsuleImageSize; UInt32 SequenceNumber; // Guid InstanceId; UInt32 OffsetToSplitInformation; UInt32 OffsetToCapsuleBody; UInt32 OffsetToOemDefinedHeader; UInt32 OffsetToAuthorInformation; UInt32 OffsetToRevisionInformation; UInt32 OffsetToShortDescription; UInt32 OffsetToLongDescription; UInt32 OffsetToApplicableDevices; void Clear() { memset(this, 0, sizeof(*this)); } void Parse(const Byte *p) { G32(0x10, HeaderSize); G32(0x14, Flags); G32(0x18, CapsuleImageSize); G32(0x1C, SequenceNumber); G32(0x30, OffsetToSplitInformation); G32(0x34, OffsetToCapsuleBody); G32(0x38, OffsetToOemDefinedHeader); G32(0x3C, OffsetToAuthorInformation); G32(0x40, OffsetToRevisionInformation); G32(0x44, OffsetToShortDescription); G32(0x48, OffsetToLongDescription); G32(0x4C, OffsetToApplicableDevices); } }; struct CItem { AString Name; AString Characts; int Parent; int Method; int NameIndex; int NumChilds; bool IsDir; bool Skip; bool ThereAreSubDirs; bool ThereIsUniqueName; bool KeepName; int BufIndex; UInt32 Offset; UInt32 Size; CItem(): Parent(-1), Method(-1), NameIndex(-1), NumChilds(0), IsDir(false), Skip(false), ThereAreSubDirs(false), ThereIsUniqueName(false), KeepName(true) {} void SetGuid(const Byte *guidName, bool full = false); AString GetName(int numChildsInParent) const; }; void CItem::SetGuid(const Byte *guidName, bool full) { ThereIsUniqueName = true; int index = FindGuid(guidName); if (index >= 0) Name = kGuidNames[(unsigned)index]; else Name = GuidToString(guidName, full); } AString CItem::GetName(int numChildsInParent) const { if (numChildsInParent <= 1 || NameIndex < 0) return Name; char sz[32]; char sz2[32]; ConvertUInt32ToString(NameIndex, sz); ConvertUInt32ToString(numChildsInParent - 1, sz2); unsigned numZeros = (unsigned)strlen(sz2) - (unsigned)strlen(sz); AString res; for (unsigned i = 0; i < numZeros; i++) res += '0'; return res + (AString)sz + '.' + Name; } struct CItem2 { AString Name; AString Characts; int MainIndex; int Parent; CItem2(): Parent(-1) {} }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { CObjectVector _items; CObjectVector _items2; CObjectVector _bufs; UString _comment; UInt32 _methodsMask; bool _capsuleMode; size_t _totalBufsSize; CCapsuleHeader _h; UInt64 _phySize; void AddCommentString(const wchar_t *name, UInt32 pos); int AddItem(const CItem &item); int AddFileItemWithIndex(CItem &item); int AddDirItem(CItem &item); unsigned AddBuf(size_t size); HRESULT ParseSections(int bufIndex, UInt32 pos, UInt32 size, int parent, int method, unsigned level); HRESULT ParseVolume(int bufIndex, UInt32 posBase, UInt32 exactSize, UInt32 limitSize, int parent, int method, int level); HRESULT OpenCapsule(IInStream *stream); HRESULT OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback); HRESULT Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback); public: CHandler(bool capsuleMode): _capsuleMode(capsuleMode) {} MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; static const Byte kProps[] = { kpidPath, kpidIsDir, kpidSize, kpidMethod, kpidCharacts }; static const Byte kArcProps[] = { kpidComment, kpidMethod, kpidCharacts }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; const CItem2 &item2 = _items2[index]; const CItem &item = _items[item2.MainIndex]; switch (propID) { case kpidPath: { AString path = item2.Name; int cur = item2.Parent; while (cur >= 0) { const CItem2 &item3 = _items2[cur]; path.InsertAtFront(CHAR_PATH_SEPARATOR); path.Insert(0, item3.Name); cur = item3.Parent; } prop = path; break; } case kpidIsDir: prop = item.IsDir; break; case kpidMethod: if (item.Method >= 0) prop = g_Methods[(unsigned)item.Method]; break; case kpidCharacts: if (!item2.Characts.IsEmpty()) prop = item2.Characts; break; case kpidSize: if (!item.IsDir) prop = (UInt64)item.Size; break; } prop.Detach(value); return S_OK; COM_TRY_END } void CHandler::AddCommentString(const wchar_t *name, UInt32 pos) { UString s; const Byte *buf = _bufs[0]; if (pos < _h.HeaderSize) return; for (UInt32 i = pos;; i += 2) { if (s.Len() > (1 << 16) || i >= _h.OffsetToCapsuleBody) return; wchar_t c = Get16(buf + i); if (c == 0) { i += 2; if (i >= _h.OffsetToCapsuleBody) return; c = Get16(buf + i); if (c == 0) break; s.Add_LF(); } s += c; } if (s.IsEmpty()) return; _comment.Add_LF(); _comment += name; _comment.AddAscii(": "); _comment += s; } STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; switch (propID) { case kpidMethod: { AString s; for (unsigned i = 0; i < 32; i++) if ((_methodsMask & ((UInt32)1 << i)) != 0) AddSpaceAndString(s, g_Methods[i]); if (!s.IsEmpty()) prop = s; break; } case kpidComment: if (!_comment.IsEmpty()) prop = _comment; break; case kpidPhySize: prop = (UInt64)_phySize; break; } prop.Detach(value); return S_OK; COM_TRY_END } #ifdef SHOW_DEBUG_INFO static void PrintLevel(int level) { PRF(printf("\n")); for (int i = 0; i < level; i++) PRF(printf(" ")); } static void MyPrint(UInt32 posBase, UInt32 size, int level, const char *name) { PrintLevel(level); PRF(printf("%s, pos = %6x, size = %6d", name, posBase, size)); } #else #define PrintLevel(level) #define MyPrint(posBase, size, level, name) #endif int CHandler::AddItem(const CItem &item) { if (_items.Size() >= kNumFilesMax) throw 2; return _items.Add(item); } int CHandler::AddFileItemWithIndex(CItem &item) { int nameIndex = _items.Size(); if (item.Parent >= 0) nameIndex = _items[item.Parent].NumChilds++; item.NameIndex = nameIndex; return AddItem(item); } int CHandler::AddDirItem(CItem &item) { if (item.Parent >= 0) _items[item.Parent].ThereAreSubDirs = true; item.IsDir = true; item.Size = 0; return AddItem(item); } unsigned CHandler::AddBuf(size_t size) { if (size > kBufTotalSizeMax - _totalBufsSize) throw 1; _totalBufsSize += size; unsigned index = _bufs.Size(); _bufs.AddNew().Alloc(size); return index; } HRESULT CHandler::ParseSections(int bufIndex, UInt32 posBase, UInt32 size, int parent, int method, unsigned level) { if (level > kLevelMax) return S_FALSE; MyPrint(posBase, size, level, "Sections"); level++; const Byte *bufData = _bufs[bufIndex]; UInt32 pos = 0; for (;;) { if (size == pos) return S_OK; PrintLevel(level); PRF(printf("%s, pos = %6x", "Sect", pos)); pos = (pos + 3) & ~(UInt32)3; if (pos > size) return S_FALSE; UInt32 rem = size - pos; if (rem == 0) return S_OK; if (rem < 4) return S_FALSE; const Byte *p = bufData + posBase + pos; UInt32 sectSize = Get24(p); if (sectSize > rem || sectSize < 4) return S_FALSE; Byte type = p[3]; PrintLevel(level); PRF(printf("%s, type = %2x, pos = %6x, size = %6d", "Sect", type, pos, sectSize)); CItem item; item.Method = method; item.BufIndex = bufIndex; item.Parent = parent; item.Offset = posBase + pos + 4; UInt32 sectDataSize = sectSize - 4; item.Size = sectDataSize; item.Name = TYPE_PAIR_TO_STRING(g_SECTION_TYPE, type); if (type == SECTION_COMPRESSION) { if (sectSize < 4 + 5) return S_FALSE; UInt32 uncompressedSize = Get32(p + 4); Byte compressionType = p[8]; UInt32 newSectSize = sectSize - 9; UInt32 newOffset = posBase + pos + 9; const Byte *pStart = p + 9; item.KeepName = false; if (compressionType > 2) { // AddFileItemWithIndex(item); return S_FALSE; } else { item.Name = g_Methods[compressionType]; // int parent = AddDirItem(item); if (compressionType == COMPRESSION_TYPE_NONE) { RINOK(ParseSections(bufIndex, newOffset, newSectSize, parent, method, level)); } else if (compressionType == COMPRESSION_TYPE_LZH) { unsigned newBufIndex = AddBuf(uncompressedSize); CByteBuffer &buf = _bufs[newBufIndex]; NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0; CMyComPtr lzhDecoder; lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder; lzhDecoder = lzhDecoderSpec; { const Byte *src = pStart; if (newSectSize < 8) return S_FALSE; UInt32 packSize = Get32(src); UInt32 unpackSize = Get32(src + 4); if (uncompressedSize != unpackSize || newSectSize - 8 != packSize) return S_FALSE; if (packSize < 1) return S_FALSE; packSize--; src += 8; if (src[packSize] != 0) return S_FALSE; CBufInStream *inStreamSpec = new CBufInStream; CMyComPtr inStream = inStreamSpec; inStreamSpec->Init(src, packSize); CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; CMyComPtr outStream = outStreamSpec; outStreamSpec->Init(buf, uncompressedSize); UInt64 uncompressedSize64 = uncompressedSize; lzhDecoderSpec->FinishMode = true; /* EFI 1.1 probably used small dictionary and (pbit = 4) in LZH. We don't support such archives. New version of compression code (named Tiano) uses LZH with (1 << 19) dictionary. But maybe LZH decoder in UEFI decoder supports larger than (1 << 19) dictionary. */ lzhDecoderSpec->SetDictSize(1 << 19); HRESULT res = lzhDecoder->Code(inStream, outStream, NULL, &uncompressedSize64, NULL); if (res != S_OK) return res; if (lzhDecoderSpec->GetInputProcessedSize() != packSize) return S_FALSE; } RINOK(ParseSections(newBufIndex, 0, uncompressedSize, parent, compressionType, level)); } else { if (newSectSize < 4 + 5 + 8) return S_FALSE; unsigned addSize = 4; if (pStart[0] == 0x5d && pStart[1] == 0 && pStart[2] == 0 && pStart[3] == 0x80 && pStart[4] == 0) { addSize = 0; // some archives have such header } else { // normal BIOS contains uncompressed size here // UInt32 uncompressedSize2 = Get24(pStart); // Byte firstSectType = p[9 + 3]; // firstSectType can be 0 in some archives } pStart += addSize; UInt64 lzmaUncompressedSize = Get64(pStart + 5); if (lzmaUncompressedSize > (1 << 30)) return S_FALSE; if (lzmaUncompressedSize < uncompressedSize) return S_FALSE; SizeT destLen = (SizeT)lzmaUncompressedSize; unsigned newBufIndex = AddBuf((size_t)lzmaUncompressedSize); CByteBuffer &buf = _bufs[newBufIndex]; ELzmaStatus status; SizeT srcLen = newSectSize - (addSize + 5 + 8); SizeT srcLen2 = srcLen; SRes res = LzmaDecode(buf, &destLen, pStart + 13, &srcLen, pStart, 5, LZMA_FINISH_END, &status, &g_Alloc); if (res != 0) return S_FALSE; if (srcLen != srcLen2 || destLen != lzmaUncompressedSize || ( status != LZMA_STATUS_FINISHED_WITH_MARK && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) return S_FALSE; RINOK(ParseSections(newBufIndex, 0, (UInt32)lzmaUncompressedSize, parent, compressionType, level)); } _methodsMask |= (1 << compressionType); } } else if (type == SECTION_GUID_DEFINED) { const unsigned kHeaderSize = 4 + kGuidSize + 4; if (sectSize < kHeaderSize) return S_FALSE; item.SetGuid(p + 4); UInt32 dataOffset = Get16(p + 4 + kGuidSize); UInt32 attrib = Get16(p + 4 + kGuidSize + 2); if (dataOffset > sectSize || dataOffset < kHeaderSize) return S_FALSE; UInt32 newSectSize = sectSize - dataOffset; item.Size = newSectSize; UInt32 newOffset = posBase + pos + dataOffset; item.Offset = newOffset; UInt32 propsSize = dataOffset - kHeaderSize; bool needDir = true; AddSpaceAndString(item.Characts, FLAGS_TO_STRING(g_GUIDED_SECTION_ATTRIBUTES, attrib)); if (AreGuidsEq(p + 0x4, kGuids[kGuidIndex_CRC]) && propsSize == 4) { needDir = false; item.KeepName = false; if (CrcCalc(bufData + newOffset, newSectSize) != Get32(p + kHeaderSize)) return S_FALSE; } else { if (propsSize != 0) { CItem item2 = item; item2.Name += ".prop"; item2.Size = propsSize; item2.Offset = posBase + pos + kHeaderSize; AddItem(item2); } } int newParent = parent; if (needDir) newParent = AddDirItem(item); RINOK(ParseSections(bufIndex, newOffset, newSectSize, newParent, method, level)); } else if (type == SECTION_FIRMWARE_VOLUME_IMAGE) { item.KeepName = false; int newParent = AddDirItem(item); RINOK(ParseVolume(bufIndex, posBase + pos + 4, sectSize - 4, sectSize - 4, newParent, method, level)); } else { bool needAdd = true; switch (type) { case SECTION_RAW: { const UInt32 kInsydeOffset = 12; if (sectDataSize >= kFvHeaderSize + kInsydeOffset) { if (IsFfs(p + 4 + kInsydeOffset) && sectDataSize - kInsydeOffset == Get64(p + 4 + kInsydeOffset + 0x20)) { needAdd = false; item.Name = "vol"; int newParent = AddDirItem(item); RINOK(ParseVolume(bufIndex, posBase + pos + 4 + kInsydeOffset, sectDataSize - kInsydeOffset, sectDataSize - kInsydeOffset, newParent, method, level)); } if (needAdd) { const char *ext = FindExt(p + 4, sectDataSize); if (ext) item.Name = ext; } } break; } case SECTION_DXE_DEPEX: case SECTION_PEI_DEPEX: { AString s; if (ParseDepedencyExpression(p + 4, sectDataSize, s)) { if (s.Len() < (1 << 9)) { s.InsertAtFront('['); s += ']'; AddSpaceAndString(_items[item.Parent].Characts, s); needAdd = false; } else { item.BufIndex = AddBuf(s.Len()); CByteBuffer &buf0 = _bufs[item.BufIndex]; if (s.Len() != 0) memcpy(buf0, s, s.Len()); item.Offset = 0; item.Size = s.Len(); } } break; } case SECTION_VERSION: { if (sectDataSize > 2) { AString s; if (ParseUtf16zString2(p + 6, sectDataSize - 2, s)) { AString s2 = "ver:"; s2 += UInt32ToString(Get16(p + 4)); s2.Add_Space(); s2 += s; AddSpaceAndString(_items[item.Parent].Characts, s2); needAdd = false; } } break; } case SECTION_USER_INTERFACE: { AString s; if (ParseUtf16zString2(p + 4, sectDataSize, s)) { _items[parent].Name = s; needAdd = false; } break; } case SECTION_FREEFORM_SUBTYPE_GUID: { if (sectDataSize >= kGuidSize) { item.SetGuid(p + 4); item.Size = sectDataSize - kGuidSize; item.Offset = posBase + pos + 4 + kGuidSize; } break; } } if (needAdd) AddFileItemWithIndex(item); } pos += sectSize; } } static UInt32 Count_FF_Bytes(const Byte *p, UInt32 size) { UInt32 i; for (i = 0; i < size && p[i] == 0xFF; i++); return i; } static bool Is_FF_Stream(const Byte *p, UInt32 size) { return (Count_FF_Bytes(p, size) == size); } struct CVolFfsHeader { UInt32 HeaderLen; UInt64 VolSize; bool Parse(const Byte *p); }; bool CVolFfsHeader::Parse(const Byte *p) { if (Get32(p + 0x28) != kFvSignature) return false; UInt32 attribs = Get32(p + 0x2C); if ((attribs & FVB_ERASE_POLARITY) == 0) return false; VolSize = Get64(p + 0x20); HeaderLen = Get16(p + 0x30); if (HeaderLen < kFvHeaderSize || (HeaderLen & 0x7) != 0 || VolSize < HeaderLen) return false; return true; }; HRESULT CHandler::ParseVolume( int bufIndex, UInt32 posBase, UInt32 exactSize, UInt32 limitSize, int parent, int method, int level) { if (level > kLevelMax) return S_FALSE; MyPrint(posBase, size, level, "Volume"); level++; if (exactSize < kFvHeaderSize) return S_FALSE; const Byte *p = _bufs[bufIndex] + posBase; // first 16 bytes must be zeros, but they are not zeros sometimes. if (!AreGuidsEq(p + kFfsGuidOffset, k_FFS_Guid) && !AreGuidsEq(p + kFfsGuidOffset, k_MacFS_Guid)) { CItem item; item.Method = method; item.BufIndex = bufIndex; item.Parent = parent; item.Offset = posBase; item.Size = exactSize; item.SetGuid(p + kFfsGuidOffset); item.Name += " [VOLUME]"; AddItem(item); return S_OK; } CVolFfsHeader ffsHeader; if (!ffsHeader.Parse(p)) return S_FALSE; // if (parent >= 0) AddSpaceAndString(_items[parent].Characts, FLAGS_TO_STRING(g_FV_Attribs, attribs)); // VolSize > exactSize (fh.Size) for some UEFI archives (is it correct UEFI?) // so we check VolSize for limitSize instead. if (ffsHeader.HeaderLen > limitSize || ffsHeader.VolSize > limitSize) return S_FALSE; { UInt32 checkCalc = 0; for (UInt32 i = 0; i < ffsHeader.HeaderLen; i += 2) checkCalc += Get16(p + i); if ((checkCalc & 0xFFFF) != 0) return S_FALSE; } // 3 reserved bytes are not zeros sometimes. // UInt16 ExtHeaderOffset; // in new SPECIFICATION? // Byte revision = p[0x37]; UInt32 pos = kFvHeaderSize; for (;;) { if (pos >= ffsHeader.HeaderLen) return S_FALSE; UInt32 numBlocks = Get32(p + pos); UInt32 length = Get32(p + pos + 4); pos += 8; if (numBlocks == 0 && length == 0) break; } if (pos != ffsHeader.HeaderLen) return S_FALSE; CRecordVector guidsVector; for (;;) { UInt32 rem = (UInt32)ffsHeader.VolSize - pos; if (rem < kFileHeaderSize) break; pos = (pos + 7) & ~7; rem = (UInt32)ffsHeader.VolSize - pos; if (rem < kFileHeaderSize) break; CItem item; item.Method = method; item.BufIndex = bufIndex; item.Parent = parent; const Byte *pFile = p + pos; CFfsFileHeader fh; if (!fh.Parse(pFile)) { UInt32 num_FF_bytes = Count_FF_Bytes(pFile, rem); if (num_FF_bytes != rem) { item.Name = "[junk]"; item.Offset = posBase + pos + num_FF_bytes; item.Size = rem - num_FF_bytes; AddItem(item); } break; } PrintLevel(level); PRF(printf("%s, pos = %6x, size = %6d", "FILE", posBase + pos, fh.Size)); if (!fh.Check(pFile, rem)) return S_FALSE; UInt32 offset = posBase + pos + kFileHeaderSize; UInt32 sectSize = fh.GetDataSize(); item.Offset = offset; item.Size = sectSize; pos += fh.Size; if (fh.Type == FV_FILETYPE_FFS_PAD) if (Is_FF_Stream(pFile + kFileHeaderSize, sectSize)) continue; UInt32 guid32 = Get32(fh.GuidName); bool full = true; if (guidsVector.FindInSorted(guid32) < 0) { guidsVector.AddToUniqueSorted(guid32); full = false; } item.SetGuid(fh.GuidName, full); item.Characts = fh.GetCharacts(); PrintLevel(level); PRF(printf("%s", item.Characts)); if (fh.Type == FV_FILETYPE_FFS_PAD || fh.Type == FV_FILETYPE_RAW) { bool isVolume = false; if (fh.Type == FV_FILETYPE_RAW) { if (sectSize >= kFvHeaderSize) if (IsFfs(pFile + kFileHeaderSize)) isVolume = true; } if (isVolume) { int newParent = AddDirItem(item); UInt32 limSize = fh.GetDataSize2(rem); // volume.VolSize > fh.Size for some UEFI archives (is it correct UEFI?) // so we will check VolSize for limitSize instead. RINOK(ParseVolume(bufIndex, offset, sectSize, limSize, newParent, method, level)); } else AddItem(item); } else { int newParent = AddDirItem(item); RINOK(ParseSections(bufIndex, offset, sectSize, newParent, method, level)); } } return S_OK; } HRESULT CHandler::OpenCapsule(IInStream *stream) { const unsigned kHeaderSize = 80; Byte buf[kHeaderSize]; RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); _h.Parse(buf); if (_h.HeaderSize != kHeaderSize || _h.CapsuleImageSize < kHeaderSize || _h.OffsetToCapsuleBody < kHeaderSize || _h.OffsetToCapsuleBody > _h.CapsuleImageSize) return S_FALSE; _phySize = _h.CapsuleImageSize; if (_h.SequenceNumber != 0 || _h.OffsetToSplitInformation != 0 ) return E_NOTIMPL; unsigned bufIndex = AddBuf(_h.CapsuleImageSize); CByteBuffer &buf0 = _bufs[bufIndex]; memcpy(buf0, buf, kHeaderSize); ReadStream_FALSE(stream, buf0 + kHeaderSize, _h.CapsuleImageSize - kHeaderSize); AddCommentString(L"Author", _h.OffsetToAuthorInformation); AddCommentString(L"Revision", _h.OffsetToRevisionInformation); AddCommentString(L"Short Description", _h.OffsetToShortDescription); AddCommentString(L"Long Description", _h.OffsetToLongDescription); return ParseVolume(bufIndex, _h.OffsetToCapsuleBody, _h.CapsuleImageSize - _h.OffsetToCapsuleBody, _h.CapsuleImageSize - _h.OffsetToCapsuleBody, -1, -1, 0); } HRESULT CHandler::OpenFv(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback * /* callback */) { Byte buf[kFvHeaderSize]; RINOK(ReadStream_FALSE(stream, buf, kFvHeaderSize)); if (!IsFfs(buf)) return S_FALSE; CVolFfsHeader ffsHeader; if (!ffsHeader.Parse(buf)) return S_FALSE; if (ffsHeader.VolSize > ((UInt32)1 << 30)) return S_FALSE; _phySize = ffsHeader.VolSize; RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); UInt32 fvSize32 = (UInt32)ffsHeader.VolSize; unsigned bufIndex = AddBuf(fvSize32); RINOK(ReadStream_FALSE(stream, _bufs[bufIndex], fvSize32)); return ParseVolume(bufIndex, 0, fvSize32, fvSize32, -1, -1, 0); } HRESULT CHandler::Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) { if (_capsuleMode) { RINOK(OpenCapsule(stream)); } else { RINOK(OpenFv(stream, maxCheckStartPosition, callback)); } unsigned num = _items.Size(); CIntArr numChilds(num); unsigned i; for (i = 0; i < num; i++) numChilds[i] = 0; for (i = 0; i < num; i++) { int parent = _items[i].Parent; if (parent >= 0) numChilds[(unsigned)parent]++; } for (i = 0; i < num; i++) { const CItem &item = _items[i]; int parent = item.Parent; if (parent >= 0) { CItem &parentItem = _items[(unsigned)parent]; if (numChilds[(unsigned)parent] == 1) if (!item.ThereIsUniqueName || !parentItem.ThereIsUniqueName || !parentItem.ThereAreSubDirs) parentItem.Skip = true; } } CUIntVector mainToReduced; for (i = 0; i < _items.Size(); i++) { mainToReduced.Add(_items2.Size()); const CItem &item = _items[i]; if (item.Skip) continue; AString name; int numItems = -1; int parent = item.Parent; if (parent >= 0) numItems = numChilds[(unsigned)parent]; AString name2 = item.GetName(numItems); AString characts2 = item.Characts; if (item.KeepName) name = name2; while (parent >= 0) { const CItem &item3 = _items[(unsigned)parent]; if (!item3.Skip) break; if (item3.KeepName) { AString name3 = item3.GetName(-1); if (name.IsEmpty()) name = name3; else name = name3 + '.' + name; } AddSpaceAndString(characts2, item3.Characts); parent = item3.Parent; } if (name.IsEmpty()) name = name2; CItem2 item2; item2.MainIndex = i; item2.Name = name; item2.Characts = characts2; if (parent >= 0) item2.Parent = mainToReduced[(unsigned)parent]; _items2.Add(item2); /* CItem2 item2; item2.MainIndex = i; item2.Name = item.Name; item2.Parent = item.Parent; _items2.Add(item2); */ } return S_OK; } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) { COM_TRY_BEGIN Close(); { HRESULT res = Open2(inStream, maxCheckStartPosition, callback); if (res == E_NOTIMPL) res = S_FALSE; return res; } COM_TRY_END } STDMETHODIMP CHandler::Close() { _phySize = 0; _totalBufsSize = 0; _methodsMask = 0; _items.Clear(); _items2.Clear(); _bufs.Clear(); _comment.Empty(); _h.Clear(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _items2.Size(); return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _items2.Size(); if (numItems == 0) return S_OK; UInt64 totalSize = 0; UInt32 i; for (i = 0; i < numItems; i++) totalSize += _items[_items2[allFilesMode ? i : indices[i]].MainIndex].Size; extractCallback->SetTotal(totalSize); UInt64 currentTotalSize = 0; NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); for (i = 0; i < numItems; i++) { lps->InSize = lps->OutSize = currentTotalSize; RINOK(lps->SetCur()); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; UInt32 index = allFilesMode ? i : indices[i]; const CItem &item = _items[_items2[index].MainIndex]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); currentTotalSize += item.Size; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); if (testMode || item.IsDir) { RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); continue; } int res = NExtract::NOperationResult::kDataError; CMyComPtr inStream; GetStream(index, &inStream); if (inStream) { RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); if (copyCoderSpec->TotalSize == item.Size) res = NExtract::NOperationResult::kOK; } realOutStream.Release(); RINOK(extractCallback->SetOperationResult(res)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN const CItem &item = _items[_items2[index].MainIndex]; if (item.IsDir) return S_FALSE; CBufInStream *streamSpec = new CBufInStream; CMyComPtr streamTemp = streamSpec; const CByteBuffer &buf = _bufs[item.BufIndex]; /* if (item.Offset + item.Size > buf.GetCapacity()) return S_FALSE; */ streamSpec->Init(buf + item.Offset, item.Size, (IInArchive *)this); *stream = streamTemp.Detach(); return S_OK; COM_TRY_END } namespace UEFIc { REGISTER_ARC_I_CLS( CHandler(true), "UEFIc", "scap", 0, 0xD0, kCapsuleSig, 0, NArcInfoFlags::kFindSignature, NULL) } namespace UEFIf { REGISTER_ARC_I_CLS( CHandler(false), "UEFIf", "uefif", 0, 0xD1, k_FFS_Guid, kFfsGuidOffset, NArcInfoFlags::kFindSignature, NULL) } }}