1 // UefiHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #endif
10 
11 #include "../../../C/7zCrc.h"
12 #include "../../../C/Alloc.h"
13 #include "../../../C/CpuArch.h"
14 #include "../../../C/LzmaDec.h"
15 
16 #include "../../Common/ComTry.h"
17 #include "../../Common/IntToString.h"
18 #include "../../Common/MyBuffer.h"
19 #include "../../Common/StringConvert.h"
20 
21 #include "../../Windows/PropVariantUtils.h"
22 
23 #include "../Common/ProgressUtils.h"
24 #include "../Common/RegisterArc.h"
25 #include "../Common/StreamObjects.h"
26 #include "../Common/StreamUtils.h"
27 
28 #include "../Compress/CopyCoder.h"
29 #include "../Compress/LzhDecoder.h"
30 
31 #ifdef SHOW_DEBUG_INFO
32 #define PRF(x) x
33 #else
34 #define PRF(x)
35 #endif
36 
37 #define Get16(p) GetUi16(p)
38 #define Get32(p) GetUi32(p)
39 #define Get64(p) GetUi64(p)
40 #define Get24(p) (Get32(p) & 0xFFFFFF)
41 
42 namespace NArchive {
43 namespace NUefi {
44 
45 static const size_t kBufTotalSizeMax = (1 << 29);
46 static const unsigned kNumFilesMax = (1 << 18);
47 static const unsigned kLevelMax = 64;
48 
49 static const Byte k_IntelMeSignature[] =
50 {
51   0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
52   0x5A, 0xA5, 0xF0, 0x0F
53 };
54 
IsIntelMe(const Byte * p)55 bool IsIntelMe(const Byte *p)
56 {
57   return memcmp(p, k_IntelMeSignature, sizeof(k_IntelMeSignature)) == 0;
58 }
59 
60 static const unsigned kFvHeaderSize = 0x38;
61 
62 static const unsigned kGuidSize = 16;
63 
64 #define CAPSULE_SIGNATURE   0xBD,0x86,0x66,0x3B,0x76,0x0D,0x30,0x40,0xB7,0x0E,0xB5,0x51,0x9E,0x2F,0xC5,0xA0
65 #define CAPSULE2_SIGNATURE  0x8B,0xA6,0x3C,0x4A,0x23,0x77,0xFB,0x48,0x80,0x3D,0x57,0x8C,0xC1,0xFE,0xC4,0x4D
66 #define CAPSULE_UEFI_SIGNATURE  0xB9,0x82,0x91,0x53,0xB5,0xAB,0x91,0x43,0xB6,0x9A,0xE3,0xA9,0x43,0xF7,0x2F,0xCC
67 /*
68   6dcbd5ed-e82d-4c44-bda1-7194199ad92a : Firmware Management `
69 */
70 
71 static const Byte k_Guids_Capsules[][kGuidSize] =
72 {
73   { CAPSULE_SIGNATURE },
74   { CAPSULE2_SIGNATURE },
75   { CAPSULE_UEFI_SIGNATURE }
76 };
77 
78 
79 static const unsigned kFfsGuidOffset = 16;
80 
81 #define FFS1_SIGNATURE  0xD9,0x54,0x93,0x7A,0x68,0x04,0x4A,0x44,0x81,0xCE,0x0B,0xF6,0x17,0xD8,0x90,0xDF
82 #define FFS2_SIGNATURE  0x78,0xE5,0x8C,0x8C,0x3D,0x8A,0x1C,0x4F,0x99,0x35,0x89,0x61,0x85,0xC3,0x2D,0xD3
83 #define MACFS_SIGNATURE 0xAD,0xEE,0xAD,0x04,0xFF,0x61,0x31,0x4D,0xB6,0xBA,0x64,0xF8,0xBF,0x90,0x1F,0x5A
84 // APPLE_BOOT
85 /*
86   "FFS3":        "5473c07a-3dcb-4dca-bd6f-1e9689e7349a",
87   "NVRAM_EVSA":  "fff12b8d-7696-4c8b-a985-2747075b4f50",
88   "NVRAM_NVAR":  "cef5b9a3-476d-497f-9fdc-e98143e0422c",
89   "NVRAM_EVSA2": "00504624-8a59-4eeb-bd0f-6b36e96128e0",
90 static const Byte k_NVRAM_NVAR_Guid[kGuidSize] =
91   { 0xA3,0xB9,0xF5,0xCE,0x6D,0x47,0x7F,0x49,0x9F,0xDC,0xE9,0x81,0x43,0xE0,0x42,0x2C };
92 */
93 
94 static const Byte k_Guids_FS[][kGuidSize] =
95 {
96   { FFS1_SIGNATURE },
97   { FFS2_SIGNATURE },
98   { MACFS_SIGNATURE }
99 };
100 
101 
102 static const UInt32 kFvSignature = 0x4856465F; // "_FVH"
103 
104 static const Byte kGuids[][kGuidSize] =
105 {
106   { 0xB0,0xCD,0x1B,0xFC,0x31,0x7D,0xAA,0x49,0x93,0x6A,0xA4,0x60,0x0D,0x9D,0xD0,0x83 },
107   { 0x2E,0x06,0xA0,0x1B,0x79,0xC7,0x82,0x45,0x85,0x66,0x33,0x6A,0xE8,0xF7,0x8F,0x09 },
108   { 0x25,0x4E,0x37,0x7E,0x01,0x8E,0xEE,0x4F,0x87,0xf2,0x39,0x0C,0x23,0xC6,0x06,0xCD },
109   { 0x97,0xE5,0x1B,0x16,0xC5,0xE9,0xDB,0x49,0xAE,0x50,0xC4,0x62,0xAB,0x54,0xEE,0xDA },
110   { 0xDB,0x7F,0xAD,0x77,0x2A,0xDF,0x02,0x43,0x88,0x98,0xC7,0x2E,0x4C,0xDB,0xD0,0xF4 },
111   { 0xAB,0x71,0xCF,0xF5,0x4B,0xB0,0x7E,0x4B,0x98,0x8A,0xD8,0xA0,0xD4,0x98,0xE6,0x92 },
112   { 0x91,0x45,0x53,0x7A,0xCE,0x37,0x81,0x48,0xB3,0xC9,0x71,0x38,0x14,0xF4,0x5D,0x6B },
113   { 0x84,0xE6,0x7A,0x36,0x5D,0x33,0x71,0x46,0xA1,0x6D,0x89,0x9D,0xBF,0xEA,0x6B,0x88 },
114   { 0x98,0x07,0x40,0x24,0x07,0x38,0x42,0x4A,0xB4,0x13,0xA1,0xEC,0xEE,0x20,0x5D,0xD8 },
115   { 0xEE,0xA2,0x3F,0x28,0x2C,0x53,0x4D,0x48,0x93,0x83,0x9F,0x93,0xB3,0x6F,0x0B,0x7E },
116   { 0x9B,0xD5,0xB8,0x98,0xBA,0xE8,0xEE,0x48,0x98,0xDD,0xC2,0x95,0x39,0x2F,0x1E,0xDB },
117   { 0x09,0x6D,0xE3,0xC3,0x94,0x82,0x97,0x4B,0xA8,0x57,0xD5,0x28,0x8F,0xE3,0x3E,0x28 },
118   { 0x18,0x88,0x53,0x4A,0xE0,0x5A,0xB2,0x4E,0xB2,0xEB,0x48,0x8B,0x23,0x65,0x70,0x22 }
119 };
120 
121 static const Byte k_Guid_LZMA_COMPRESSED[kGuidSize] =
122   { 0x98,0x58,0x4E,0xEE,0x14,0x39,0x59,0x42,0x9D,0x6E,0xDC,0x7B,0xD7,0x94,0x03,0xCF };
123 
124 static const char * const kGuidNames[] =
125 {
126     "CRC"
127   , "VolumeTopFile"
128   , "ACPI"
129   , "ACPI2"
130   , "Main"
131   , "Intel32"
132   , "Intel64"
133   , "Intel32c"
134   , "Intel64c"
135   , "MacVolume"
136   , "MacUpdate.txt"
137   , "MacName"
138   , "Insyde"
139 };
140 
141 enum
142 {
143   kGuidIndex_CRC = 0
144 };
145 
146 struct CSigExtPair
147 {
148   const char *ext;
149   unsigned sigSize;
150   Byte sig[16];
151 };
152 
153 static const CSigExtPair g_Sigs[] =
154 {
155   { "bmp",  2, { 'B','M' } },
156   { "riff", 4, { 'R','I','F','F' } },
157   { "pe",   2, { 'M','Z'} },
158   { "gif",  6, { 'G','I','F','8','9', 'a' } },
159   { "png",  8, { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A } },
160   { "jpg", 10, { 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46 } },
161   { "rom",  2, { 0x55,0xAA } }
162 };
163 
164 enum
165 {
166   kSig_BMP,
167   kSig_RIFF,
168   kSig_PE
169 };
170 
FindExt(const Byte * p,size_t size)171 static const char *FindExt(const Byte *p, size_t size)
172 {
173   unsigned i;
174   for (i = 0; i < ARRAY_SIZE(g_Sigs); i++)
175   {
176     const CSigExtPair &pair = g_Sigs[i];
177     if (size >= pair.sigSize)
178       if (memcmp(p, pair.sig, pair.sigSize) == 0)
179         break;
180   }
181   if (i == ARRAY_SIZE(g_Sigs))
182     return NULL;
183   switch (i)
184   {
185     case kSig_BMP:
186       if (GetUi32(p + 2) > size || GetUi32(p + 0xA) > size)
187         return NULL;
188       break;
189     case kSig_RIFF:
190       if (GetUi32(p + 8) == 0x45564157 || GetUi32(p + 0xC) == 0x20746D66 )
191         return "wav";
192       break;
193     case kSig_PE:
194     {
195       if (size < 512)
196         return NULL;
197       UInt32 peOffset = GetUi32(p + 0x3C);
198       if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0)
199         return NULL;
200       if (GetUi32(p + peOffset) != 0x00004550)
201         return NULL;
202       break;
203     }
204   }
205   return g_Sigs[i].ext;
206 }
207 
AreGuidsEq(const Byte * p1,const Byte * p2)208 static bool AreGuidsEq(const Byte *p1, const Byte *p2)
209 {
210   return memcmp(p1, p2, kGuidSize) == 0;
211 }
212 
FindGuid(const Byte * p)213 static int FindGuid(const Byte *p)
214 {
215   for (unsigned i = 0; i < ARRAY_SIZE(kGuids); i++)
216     if (AreGuidsEq(p, kGuids[i]))
217       return i;
218   return -1;
219 }
220 
IsFfs(const Byte * p)221 static bool IsFfs(const Byte *p)
222 {
223   if (Get32(p + 0x28) != kFvSignature)
224     return false;
225   for (unsigned i = 0; i < ARRAY_SIZE(k_Guids_FS); i++)
226     if (AreGuidsEq(p + kFfsGuidOffset, k_Guids_FS[i]))
227       return true;
228   return false;
229 }
230 
231 #define FVB_ERASE_POLARITY  (1 << 11)
232 
233 /*
234 static const CUInt32PCharPair g_FV_Attribs[] =
235 {
236   {  0, "ReadDisabledCap" },
237   {  1, "ReadEnabledCap" },
238   {  2, "ReadEnabled" },
239   {  3, "WriteDisabledCap" },
240   {  4, "WriteEnabledCap" },
241   {  5, "WriteEnabled" },
242   {  6, "LockCap" },
243   {  7, "Locked" },
244 
245   {  9, "StickyWrite" },
246   { 10, "MemoryMapped" },
247   { 11, "ErasePolarity" },
248 
249   { 12, "ReadLockCap" },
250   { 13, "WriteLockCap" },
251   { 14, "WriteLockCap" }
252 };
253 */
254 
255 enum
256 {
257   FV_FILETYPE_ALL,
258   FV_FILETYPE_RAW,
259   FV_FILETYPE_FREEFORM,
260   FV_FILETYPE_SECURITY_CORE,
261   FV_FILETYPE_PEI_CORE,
262   FV_FILETYPE_DXE_CORE,
263   FV_FILETYPE_PEIM,
264   FV_FILETYPE_DRIVER,
265   FV_FILETYPE_COMBINED_PEIM_DRIVER,
266   FV_FILETYPE_APPLICATION,
267   // The value 0x0A is reserved and should not be used
268   FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0x0B,
269   // types 0xF0 - 0xFF are FFS file types
270   FV_FILETYPE_FFS_PAD = 0xF0
271 };
272 
273 static const char * const g_FileTypes[] =
274 {
275     "ALL"
276   , "RAW"
277   , "FREEFORM"
278   , "SECURITY_CORE"
279   , "PEI_CORE"
280   , "DXE_CORE"
281   , "PEIM"
282   , "DRIVER"
283   , "COMBINED_PEIM_DRIVER"
284   , "APPLICATION"
285   , "0xA"
286   , "VOLUME"
287 };
288 
289 // typedef Byte FFS_FILE_ATTRIBUTES;
290 // FFS File Attributes
291 #define FFS_ATTRIB_TAIL_PRESENT 0x01
292 // #define FFS_ATTRIB_RECOVERY 0x02
293 // #define FFS_ATTRIB_HEADER_EXTENSION 0x04
294 // #define FFS_ATTRIB_DATA_ALIGNMENT 0x38
295 #define FFS_ATTRIB_CHECKSUM 0x40
296 
297 static const CUInt32PCharPair g_FFS_FILE_ATTRIBUTES[] =
298 {
299   { 0, "" /* "TAIL" */ },
300   { 1, "RECOVERY" },
301   // { 2, "HEADER_EXTENSION" }, // reserved for future
302   { 6, "" /* "CHECKSUM" */ }
303 };
304 
305 // static const Byte g_Allignment[8] = { 3, 4, 7, 9, 10, 12, 15, 16 };
306 
307 // typedef Byte FFS_FILE_STATE;
308 
309 // Look also FVB_ERASE_POLARITY.
310 // Lower-order State bits are superceded by higher-order State bits.
311 
312 // #define FILE_HEADER_CONSTRUCTION  0x01
313 // #define FILE_HEADER_VALID         0x02
314 #define FILE_DATA_VALID           0x04
315 // #define FILE_MARKED_FOR_UPDATE    0x08
316 // #define FILE_DELETED              0x10
317 // #define FILE_HEADER_INVALID       0x20
318 
319 // SECTION_TYPE
320 
321 #define SECTION_ALL 0x00
322 
323 #define SECTION_COMPRESSION  0x01
324 #define SECTION_GUID_DEFINED 0x02
325 
326 // Leaf section Type values
327 #define SECTION_PE32      0x10
328 #define SECTION_PIC       0x11
329 #define SECTION_TE        0x12
330 #define SECTION_DXE_DEPEX 0x13
331 #define SECTION_VERSION   0x14
332 #define SECTION_USER_INTERFACE 0x15
333 #define SECTION_COMPATIBILITY16 0x16
334 #define SECTION_FIRMWARE_VOLUME_IMAGE 0x17
335 #define SECTION_FREEFORM_SUBTYPE_GUID 0x18
336 #define SECTION_RAW       0x19
337 #define SECTION_PEI_DEPEX 0x1B
338 
339 
340 // #define GUIDED_SECTION_PROCESSING_REQUIRED 0x01
341 // #define GUIDED_SECTION_AUTH_STATUS_VALID 0x02
342 
343 static const CUInt32PCharPair g_GUIDED_SECTION_ATTRIBUTES[] =
344 {
345   { 0, "PROCESSING_REQUIRED" },
346   { 1, "AUTH" }
347 };
348 
349 static const CUInt32PCharPair g_SECTION_TYPE[] =
350 {
351   { 0x01, "COMPRESSION" },
352   { 0x02, "GUID" },
353   { 0x10, "efi" },
354   { 0x11, "PIC" },
355   { 0x12, "te" },
356   { 0x13, "DXE_DEPEX" },
357   { 0x14, "VERSION" },
358   { 0x15, "USER_INTERFACE" },
359   { 0x16, "COMPATIBILITY16" },
360   { 0x17, "VOLUME" },
361   { 0x18, "FREEFORM_SUBTYPE_GUID" },
362   { 0x19, "raw" },
363   { 0x1B, "PEI_DEPEX" }
364 };
365 
366 #define COMPRESSION_TYPE_NONE 0
367 #define COMPRESSION_TYPE_LZH  1
368 #define COMPRESSION_TYPE_LZMA 2
369 
370 static const char * const g_Methods[] =
371 {
372     "COPY"
373   , "LZH"
374   , "LZMA"
375 };
376 
377 
AddGuid(AString & dest,const Byte * p,bool full)378 static void AddGuid(AString &dest, const Byte *p, bool full)
379 {
380   char s[64];
381   ::RawLeGuidToString(p, s);
382   // MyStringUpper_Ascii(s);
383   if (!full)
384     s[8] = 0;
385   dest += s;
386 }
387 
388 static const char * const kExpressionCommands[] =
389 {
390   "BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"
391 };
392 
ParseDepedencyExpression(const Byte * p,UInt32 size,AString & res)393 static bool ParseDepedencyExpression(const Byte *p, UInt32 size, AString &res)
394 {
395   res.Empty();
396   for (UInt32 i = 0; i < size;)
397   {
398     unsigned command = p[i++];
399     if (command > ARRAY_SIZE(kExpressionCommands))
400       return false;
401     res += kExpressionCommands[command];
402     if (command < 3)
403     {
404       if (i + kGuidSize > size)
405         return false;
406       res.Add_Space();
407       AddGuid(res, p + i, false);
408       i += kGuidSize;
409     }
410     res += "; ";
411   }
412   return true;
413 }
414 
ParseUtf16zString(const Byte * p,UInt32 size,UString & res)415 static bool ParseUtf16zString(const Byte *p, UInt32 size, UString &res)
416 {
417   if ((size & 1) != 0)
418     return false;
419   res.Empty();
420   UInt32 i;
421   for (i = 0; i < size; i += 2)
422   {
423     wchar_t c = Get16(p + i);
424     if (c == 0)
425       break;
426     res += c;
427   }
428   return (i == size - 2);
429 }
430 
ParseUtf16zString2(const Byte * p,UInt32 size,AString & res)431 static bool ParseUtf16zString2(const Byte *p, UInt32 size, AString &res)
432 {
433   UString s;
434   if (!ParseUtf16zString(p, size, s))
435     return false;
436   res = UnicodeStringToMultiByte(s);
437   return true;
438 }
439 
440 #define FLAGS_TO_STRING(pairs, value) FlagsToString(pairs, ARRAY_SIZE(pairs), value)
441 #define TYPE_TO_STRING(table, value) TypeToString(table, ARRAY_SIZE(table), value)
442 #define TYPE_PAIR_TO_STRING(table, value) TypePairToString(table, ARRAY_SIZE(table), value)
443 
444 static const UInt32 kFileHeaderSize = 24;
445 
AddSpaceAndString(AString & res,const AString & newString)446 static void AddSpaceAndString(AString &res, const AString &newString)
447 {
448   if (!newString.IsEmpty())
449   {
450     res.Add_Space_if_NotEmpty();
451     res += newString;
452   }
453 }
454 
455 class CFfsFileHeader
456 {
457 PRF(public:)
458   Byte CheckHeader;
459   Byte CheckFile;
460   Byte Attrib;
461   Byte State;
462 
GetTailReference() const463   UInt16 GetTailReference() const { return (UInt16)(CheckHeader | ((UInt16)CheckFile << 8)); }
GetTailSize() const464   UInt32 GetTailSize() const { return IsThereTail() ? 2 : 0; }
IsThereFileChecksum() const465   bool IsThereFileChecksum() const { return (Attrib & FFS_ATTRIB_CHECKSUM) != 0; }
IsThereTail() const466   bool IsThereTail() const { return (Attrib & FFS_ATTRIB_TAIL_PRESENT) != 0; }
467 public:
468   Byte GuidName[kGuidSize];
469   Byte Type;
470   UInt32 Size;
471 
Parse(const Byte * p)472   bool Parse(const Byte *p)
473   {
474     int i;
475     for (i = 0; i < kFileHeaderSize; i++)
476       if (p[i] != 0xFF)
477         break;
478     if (i == kFileHeaderSize)
479       return false;
480     memcpy(GuidName, p, kGuidSize);
481     CheckHeader = p[0x10];
482     CheckFile = p[0x11];
483     Type = p[0x12];
484     Attrib = p[0x13];
485     Size = Get24(p + 0x14);
486     State = p[0x17];
487     return true;
488   }
489 
GetDataSize() const490   UInt32 GetDataSize() const { return Size - kFileHeaderSize - GetTailSize(); }
GetDataSize2(UInt32 rem) const491   UInt32 GetDataSize2(UInt32 rem) const { return rem - kFileHeaderSize - GetTailSize(); }
492 
Check(const Byte * p,UInt32 size)493   bool Check(const Byte *p, UInt32 size)
494   {
495     if (Size > size)
496       return false;
497     UInt32 tailSize = GetTailSize();
498     if (Size < kFileHeaderSize + tailSize)
499       return false;
500 
501     {
502       unsigned checkSum = 0;
503       for (UInt32 i = 0; i < kFileHeaderSize; i++)
504         checkSum += p[i];
505       checkSum -= p[0x17];
506       checkSum -= p[0x11];
507       if ((Byte)checkSum != 0)
508         return false;
509     }
510 
511     if (IsThereFileChecksum())
512     {
513       unsigned checkSum = 0;
514       UInt32 checkSize = Size - tailSize;
515       for (UInt32 i = 0; i < checkSize; i++)
516         checkSum += p[i];
517       checkSum -= p[0x17];
518       if ((Byte)checkSum != 0)
519         return false;
520     }
521 
522     if (IsThereTail())
523       if (GetTailReference() != (UInt16)~Get16(p + Size - 2))
524         return false;
525 
526     int polarity = 0;
527     int i;
528     for (i = 5; i >= 0; i--)
529       if (((State >> i) & 1) == polarity)
530       {
531         // AddSpaceAndString(s, g_FFS_FILE_STATE_Flags[i]);
532         if ((1 << i) != FILE_DATA_VALID)
533           return false;
534         break;
535       }
536     if (i < 0)
537       return false;
538 
539     return true;
540   }
541 
GetCharacts() const542   AString GetCharacts() const
543   {
544     AString s;
545     if (Type == FV_FILETYPE_FFS_PAD)
546       s += "PAD";
547     else
548       s += TYPE_TO_STRING(g_FileTypes, Type);
549     AddSpaceAndString(s, FLAGS_TO_STRING(g_FFS_FILE_ATTRIBUTES, Attrib & 0xC7));
550     /*
551     int align = (Attrib >> 3) & 7;
552     if (align != 0)
553     {
554       s += " Align:";
555       s.Add_UInt32((UInt32)1 << g_Allignment[align]);
556     }
557     */
558     return s;
559   }
560 };
561 
562 #define G32(_offs_, dest) dest = Get32(p + (_offs_));
563 #define G16(_offs_, dest) dest = Get16(p + (_offs_));
564 
565 struct CCapsuleHeader
566 {
567   UInt32 HeaderSize;
568   UInt32 Flags;
569   UInt32 CapsuleImageSize;
570   UInt32 SequenceNumber;
571   // Guid InstanceId;
572   UInt32 OffsetToSplitInformation;
573   UInt32 OffsetToCapsuleBody;
574   UInt32 OffsetToOemDefinedHeader;
575   UInt32 OffsetToAuthorInformation;
576   UInt32 OffsetToRevisionInformation;
577   UInt32 OffsetToShortDescription;
578   UInt32 OffsetToLongDescription;
579   UInt32 OffsetToApplicableDevices;
580 
ClearNArchive::NUefi::CCapsuleHeader581   void Clear() { memset(this, 0, sizeof(*this)); }
582 
ParseNArchive::NUefi::CCapsuleHeader583   bool Parse(const Byte *p)
584   {
585     Clear();
586     G32(0x10, HeaderSize);
587     G32(0x14, Flags);
588     G32(0x18, CapsuleImageSize);
589     if (HeaderSize < 0x1C)
590       return false;
591     if (AreGuidsEq(p, k_Guids_Capsules[0]))
592     {
593       const unsigned kHeaderSize = 80;
594       if (HeaderSize != kHeaderSize)
595         return false;
596       G32(0x1C, SequenceNumber);
597       G32(0x30, OffsetToSplitInformation);
598       G32(0x34, OffsetToCapsuleBody);
599       G32(0x38, OffsetToOemDefinedHeader);
600       G32(0x3C, OffsetToAuthorInformation);
601       G32(0x40, OffsetToRevisionInformation);
602       G32(0x44, OffsetToShortDescription);
603       G32(0x48, OffsetToLongDescription);
604       G32(0x4C, OffsetToApplicableDevices);
605       return true;
606     }
607     else if (AreGuidsEq(p, k_Guids_Capsules[1]))
608     {
609       // capsule v2
610       G16(0x1C, OffsetToCapsuleBody);
611       G16(0x1E, OffsetToOemDefinedHeader);
612       return true;
613     }
614     else if (AreGuidsEq(p, k_Guids_Capsules[2]))
615     {
616       OffsetToCapsuleBody = HeaderSize;
617       return true;
618     }
619     else
620     {
621       // here we must check for another capsule types
622       return false;
623     }
624   }
625 };
626 
627 
628 struct CItem
629 {
630   AString Name;
631   AString Characts;
632   int Parent;
633   int Method;
634   int NameIndex;
635   int NumChilds;
636   bool IsDir;
637   bool Skip;
638   bool ThereAreSubDirs;
639   bool ThereIsUniqueName;
640   bool KeepName;
641 
642   int BufIndex;
643   UInt32 Offset;
644   UInt32 Size;
645 
CItemNArchive::NUefi::CItem646   CItem(): Parent(-1), Method(-1), NameIndex(-1), NumChilds(0),
647       IsDir(false), Skip(false), ThereAreSubDirs(false), ThereIsUniqueName(false), KeepName(true) {}
648   void SetGuid(const Byte *guidName, bool full = false);
649   AString GetName(int numChildsInParent) const;
650 };
651 
SetGuid(const Byte * guidName,bool full)652 void CItem::SetGuid(const Byte *guidName, bool full)
653 {
654   ThereIsUniqueName = true;
655   int index = FindGuid(guidName);
656   if (index >= 0)
657     Name = kGuidNames[(unsigned)index];
658   else
659   {
660     Name.Empty();
661     AddGuid(Name, guidName, full);
662   }
663 }
664 
GetName(int numChildsInParent) const665 AString CItem::GetName(int numChildsInParent) const
666 {
667   if (numChildsInParent <= 1 || NameIndex < 0)
668     return Name;
669   char sz[32];
670   char sz2[32];
671   ConvertUInt32ToString(NameIndex, sz);
672   ConvertUInt32ToString(numChildsInParent - 1, sz2);
673   int numZeros = (int)strlen(sz2) - (int)strlen(sz);
674   AString res;
675   for (int i = 0; i < numZeros; i++)
676     res += '0';
677   res += sz;
678   res += '.';
679   res += Name;
680   return res;
681 }
682 
683 struct CItem2
684 {
685   AString Name;
686   AString Characts;
687   int MainIndex;
688   int Parent;
689 
CItem2NArchive::NUefi::CItem2690   CItem2(): Parent(-1) {}
691 };
692 
693 class CHandler:
694   public IInArchive,
695   public IInArchiveGetStream,
696   public CMyUnknownImp
697 {
698   CObjectVector<CItem> _items;
699   CObjectVector<CItem2> _items2;
700   CObjectVector<CByteBuffer> _bufs;
701   UString _comment;
702   UInt32 _methodsMask;
703   bool _capsuleMode;
704   bool _headersError;
705 
706   size_t _totalBufsSize;
707   CCapsuleHeader _h;
708   UInt64 _phySize;
709 
710   void AddCommentString(const char *name, UInt32 pos);
711   int AddItem(const CItem &item);
712   int AddFileItemWithIndex(CItem &item);
713   int AddDirItem(CItem &item);
714   unsigned AddBuf(size_t size);
715 
716   HRESULT DecodeLzma(const Byte *data, size_t inputSize);
717 
718   HRESULT ParseSections(int bufIndex, UInt32 pos, UInt32 size, int parent, int method, unsigned level, bool &error);
719 
720   HRESULT ParseIntelMe(int bufIndex, UInt32 posBase,
721       UInt32 exactSize, UInt32 limitSize,
722       int parent, int method, int level);
723 
724   HRESULT ParseVolume(int bufIndex, UInt32 posBase,
725       UInt32 exactSize, UInt32 limitSize,
726       int parent, int method, int level);
727 
728   HRESULT OpenCapsule(IInStream *stream);
729   HRESULT OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
730   HRESULT Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
731 public:
CHandler(bool capsuleMode)732   CHandler(bool capsuleMode): _capsuleMode(capsuleMode) {}
733   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
734   INTERFACE_IInArchive(;)
735   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
736 };
737 
738 static const Byte kProps[] =
739 {
740   kpidPath,
741   kpidIsDir,
742   kpidSize,
743   // kpidOffset,
744   kpidMethod,
745   kpidCharacts
746 };
747 
748 static const Byte kArcProps[] =
749 {
750   kpidComment,
751   kpidMethod,
752   kpidCharacts
753 };
754 
755 IMP_IInArchive_Props
756 IMP_IInArchive_ArcProps
757 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)758 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
759 {
760   COM_TRY_BEGIN
761   NWindows::NCOM::CPropVariant prop;
762   const CItem2 &item2 = _items2[index];
763   const CItem &item = _items[item2.MainIndex];
764   switch (propID)
765   {
766     case kpidPath:
767     {
768       AString path (item2.Name);
769       int cur = item2.Parent;
770       while (cur >= 0)
771       {
772         const CItem2 &item3 = _items2[cur];
773         path.InsertAtFront(CHAR_PATH_SEPARATOR);
774         path.Insert(0, item3.Name);
775         cur = item3.Parent;
776       }
777       prop = path;
778       break;
779     }
780     case kpidIsDir: prop = item.IsDir; break;
781     case kpidMethod: if (item.Method >= 0) prop = g_Methods[(unsigned)item.Method]; break;
782     case kpidCharacts: if (!item2.Characts.IsEmpty()) prop = item2.Characts; break;
783     case kpidSize: if (!item.IsDir) prop = (UInt64)item.Size; break;
784     // case kpidOffset: if (!item.IsDir) prop = item.Offset; break;
785   }
786   prop.Detach(value);
787   return S_OK;
788   COM_TRY_END
789 }
790 
AddCommentString(const char * name,UInt32 pos)791 void CHandler::AddCommentString(const char *name, UInt32 pos)
792 {
793   UString s;
794   const Byte *buf = _bufs[0];
795   if (pos < _h.HeaderSize)
796     return;
797   for (UInt32 i = pos;; i += 2)
798   {
799     if (s.Len() > (1 << 16) || i >= _h.OffsetToCapsuleBody)
800       return;
801     wchar_t c = Get16(buf + i);
802     if (c == 0)
803     {
804       i += 2;
805       if (i >= _h.OffsetToCapsuleBody)
806         return;
807       c = Get16(buf + i);
808       if (c == 0)
809         break;
810       s.Add_LF();
811     }
812     s += c;
813   }
814   if (s.IsEmpty())
815     return;
816   _comment.Add_LF();
817   _comment += name;
818   _comment += ": ";
819   _comment += s;
820 }
821 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)822 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
823 {
824   COM_TRY_BEGIN
825   NWindows::NCOM::CPropVariant prop;
826   switch (propID)
827   {
828     case kpidMethod:
829     {
830       AString s;
831       for (unsigned i = 0; i < 32; i++)
832         if ((_methodsMask & ((UInt32)1 << i)) != 0)
833           AddSpaceAndString(s, (AString)g_Methods[i]);
834       if (!s.IsEmpty())
835         prop = s;
836       break;
837     }
838     case kpidComment: if (!_comment.IsEmpty()) prop = _comment; break;
839     case kpidPhySize: prop = (UInt64)_phySize; break;
840 
841     case kpidErrorFlags:
842     {
843       UInt32 v = 0;
844       if (!_headersError) v |= kpv_ErrorFlags_HeadersError;
845       if (v != 0)
846         prop = v;
847       break;
848     }
849   }
850   prop.Detach(value);
851   return S_OK;
852   COM_TRY_END
853 }
854 
855 #ifdef SHOW_DEBUG_INFO
PrintLevel(int level)856 static void PrintLevel(int level)
857 {
858   PRF(printf("\n"));
859   for (int i = 0; i < level; i++)
860     PRF(printf("  "));
861 }
MyPrint(UInt32 posBase,UInt32 size,int level,const char * name)862 static void MyPrint(UInt32 posBase, UInt32 size, int level, const char *name)
863 {
864   PrintLevel(level);
865   PRF(printf("%s, pos = %6x, size = %6x", name, posBase, size));
866 }
867 #else
868 #define PrintLevel(level)
869 #define MyPrint(posBase, size, level, name)
870 #endif
871 
872 
873 
AddItem(const CItem & item)874 int CHandler::AddItem(const CItem &item)
875 {
876   if (_items.Size() >= kNumFilesMax)
877     throw 2;
878   return _items.Add(item);
879 }
880 
AddFileItemWithIndex(CItem & item)881 int CHandler::AddFileItemWithIndex(CItem &item)
882 {
883   int nameIndex = _items.Size();
884   if (item.Parent >= 0)
885     nameIndex = _items[item.Parent].NumChilds++;
886   item.NameIndex = nameIndex;
887   return AddItem(item);
888 }
889 
AddDirItem(CItem & item)890 int CHandler::AddDirItem(CItem &item)
891 {
892   if (item.Parent >= 0)
893     _items[item.Parent].ThereAreSubDirs = true;
894   item.IsDir = true;
895   item.Size = 0;
896   return AddItem(item);
897 }
898 
AddBuf(size_t size)899 unsigned CHandler::AddBuf(size_t size)
900 {
901   if (size > kBufTotalSizeMax - _totalBufsSize)
902     throw 1;
903   _totalBufsSize += size;
904   unsigned index = _bufs.Size();
905   _bufs.AddNew().Alloc(size);
906   return index;
907 }
908 
909 
DecodeLzma(const Byte * data,size_t inputSize)910 HRESULT CHandler::DecodeLzma(const Byte *data, size_t inputSize)
911 {
912   if (inputSize < 5 + 8)
913     return S_FALSE;
914   const UInt64 unpackSize = Get64(data + 5);
915   if (unpackSize > ((UInt32)1 << 30))
916     return S_FALSE;
917   SizeT destLen = (SizeT)unpackSize;
918   const unsigned newBufIndex = AddBuf((size_t)unpackSize);
919   CByteBuffer &buf = _bufs[newBufIndex];
920   ELzmaStatus status;
921   SizeT srcLen = inputSize - (5 + 8);
922   const SizeT srcLen2 = srcLen;
923   SRes res = LzmaDecode(buf, &destLen, data + 13, &srcLen,
924       data, 5, LZMA_FINISH_END, &status, &g_Alloc);
925   if (res != 0)
926     return S_FALSE;
927   if (srcLen != srcLen2 || destLen != unpackSize || (
928       status != LZMA_STATUS_FINISHED_WITH_MARK &&
929       status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
930     return S_FALSE;
931   return S_OK;
932 }
933 
934 
ParseSections(int bufIndex,UInt32 posBase,UInt32 size,int parent,int method,unsigned level,bool & error)935 HRESULT CHandler::ParseSections(int bufIndex, UInt32 posBase, UInt32 size, int parent, int method, unsigned level, bool &error)
936 {
937   error = false;
938 
939   if (level > kLevelMax)
940     return S_FALSE;
941   MyPrint(posBase, size, level, "Sections");
942   level++;
943   const Byte *bufData = _bufs[bufIndex];
944   UInt32 pos = 0;
945   for (;;)
946   {
947     if (size == pos)
948       return S_OK;
949     PrintLevel(level);
950     PRF(printf("%s, abs = %6x, relat = %6x", "Sect", posBase + pos, pos));
951     pos = (pos + 3) & ~(UInt32)3;
952     if (pos > size)
953       return S_FALSE;
954     UInt32 rem = size - pos;
955     if (rem == 0)
956       return S_OK;
957     if (rem < 4)
958       return S_FALSE;
959 
960     const Byte *p = bufData + posBase + pos;
961 
962     const UInt32 sectSize = Get24(p);
963     const Byte type = p[3];
964 
965     // PrintLevel(level);
966     PRF(printf(" type = %2x, sectSize = %6x", type, sectSize));
967 
968     if (sectSize > rem || sectSize < 4)
969     {
970       _headersError = true;
971       error = true;
972       return S_OK;
973       // return S_FALSE;
974     }
975 
976     CItem item;
977     item.Method = method;
978     item.BufIndex = bufIndex;
979     item.Parent = parent;
980     item.Offset = posBase + pos + 4;
981     UInt32 sectDataSize = sectSize - 4;
982     item.Size = sectDataSize;
983     item.Name = TYPE_PAIR_TO_STRING(g_SECTION_TYPE, type);
984 
985     if (type == SECTION_COMPRESSION)
986     {
987       if (sectSize < 4 + 5)
988         return S_FALSE;
989       UInt32 uncompressedSize = Get32(p + 4);
990       Byte compressionType = p[8];
991 
992       UInt32 newSectSize = sectSize - 9;
993       UInt32 newOffset = posBase + pos + 9;
994       const Byte *pStart = p + 9;
995 
996       item.KeepName = false;
997       if (compressionType > 2)
998       {
999         // AddFileItemWithIndex(item);
1000         return S_FALSE;
1001       }
1002       else
1003       {
1004         item.Name = g_Methods[compressionType];
1005         // int parent = AddDirItem(item);
1006         if (compressionType == COMPRESSION_TYPE_NONE)
1007         {
1008           bool error2;
1009           RINOK(ParseSections(bufIndex, newOffset, newSectSize, parent, method, level, error2));
1010         }
1011         else if (compressionType == COMPRESSION_TYPE_LZH)
1012         {
1013           unsigned newBufIndex = AddBuf(uncompressedSize);
1014           CByteBuffer &buf = _bufs[newBufIndex];
1015 
1016           NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0;
1017           CMyComPtr<ICompressCoder> lzhDecoder;
1018 
1019           lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder;
1020           lzhDecoder = lzhDecoderSpec;
1021 
1022           {
1023             const Byte *src = pStart;
1024             if (newSectSize < 8)
1025               return S_FALSE;
1026             UInt32 packSize = Get32(src);
1027             UInt32 unpackSize = Get32(src + 4);
1028 
1029             PRF(printf(" LZH packSize = %6x, unpackSize = %6x", packSize, unpackSize));
1030 
1031             if (uncompressedSize != unpackSize || newSectSize - 8 != packSize)
1032               return S_FALSE;
1033             if (packSize < 1)
1034               return S_FALSE;
1035             packSize--;
1036             src += 8;
1037             if (src[packSize] != 0)
1038               return S_FALSE;
1039 
1040             CBufInStream *inStreamSpec = new CBufInStream;
1041             CMyComPtr<IInStream> inStream = inStreamSpec;
1042 
1043             CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream;
1044             CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
1045 
1046             UInt64 uncompressedSize64 = uncompressedSize;
1047             lzhDecoderSpec->FinishMode = true;
1048             /*
1049               EFI 1.1 probably used LZH with small dictionary and (pbit = 4). It was named "Efi compression".
1050               New version of compression code (named Tiano) uses LZH with (1 << 19) dictionary.
1051               But maybe LZH decoder in UEFI decoder supports larger than (1 << 19) dictionary.
1052               We check both LZH versions: Tiano and then Efi.
1053             */
1054             HRESULT res = S_FALSE;
1055 
1056             for (unsigned m = 0 ; m < 2; m++)
1057             {
1058               inStreamSpec->Init(src, packSize);
1059               outStreamSpec->Init(buf, uncompressedSize);
1060               lzhDecoderSpec->SetDictSize((m == 0) ? ((UInt32)1 << 19) : ((UInt32)1 << 14));
1061               res = lzhDecoder->Code(inStream, outStream, NULL, &uncompressedSize64, NULL);
1062               if (res == S_OK)
1063                 break;
1064             }
1065             RINOK(res);
1066           }
1067 
1068           bool error2;
1069           RINOK(ParseSections(newBufIndex, 0, uncompressedSize, parent, compressionType, level, error2));
1070         }
1071         else
1072         {
1073           if (newSectSize < 4 + 5 + 8)
1074             return S_FALSE;
1075           unsigned addSize = 4;
1076           if (pStart[0] == 0x5d && pStart[1] == 0 && pStart[2] == 0 && pStart[3] == 0x80 && pStart[4] == 0)
1077           {
1078             addSize = 0;
1079             // some archives have such header
1080           }
1081           else
1082           {
1083             // normal BIOS contains uncompressed size here
1084             // UInt32 uncompressedSize2 = Get24(pStart);
1085             // Byte firstSectType = p[9 + 3];
1086             // firstSectType can be 0 in some archives
1087           }
1088           pStart += addSize;
1089 
1090           RINOK(DecodeLzma(pStart, newSectSize - addSize));
1091           const size_t lzmaUncompressedSize = _bufs.Back().Size();
1092           // if (lzmaUncompressedSize != uncompressedSize)
1093           if (lzmaUncompressedSize < uncompressedSize)
1094             return S_FALSE;
1095           bool error2;
1096           RINOK(ParseSections(_bufs.Size() - 1, 0, (UInt32)lzmaUncompressedSize, parent, compressionType, level, error2));
1097         }
1098         _methodsMask |= (1 << compressionType);
1099       }
1100     }
1101     else if (type == SECTION_GUID_DEFINED)
1102     {
1103       const unsigned kHeaderSize = 4 + kGuidSize + 4;
1104       if (sectSize < kHeaderSize)
1105         return S_FALSE;
1106       item.SetGuid(p + 4);
1107       UInt32 dataOffset = Get16(p + 4 + kGuidSize);
1108       UInt32 attrib = Get16(p + 4 + kGuidSize + 2);
1109       if (dataOffset > sectSize || dataOffset < kHeaderSize)
1110         return S_FALSE;
1111       UInt32 newSectSize = sectSize - dataOffset;
1112       item.Size = newSectSize;
1113       UInt32 newOffset = posBase + pos + dataOffset;
1114       item.Offset = newOffset;
1115       UInt32 propsSize = dataOffset - kHeaderSize;
1116       AddSpaceAndString(item.Characts, FLAGS_TO_STRING(g_GUIDED_SECTION_ATTRIBUTES, attrib));
1117 
1118       bool needDir = true;
1119       unsigned newBufIndex = bufIndex;
1120       int newMethod = method;
1121 
1122       if (AreGuidsEq(p + 0x4, k_Guid_LZMA_COMPRESSED))
1123       {
1124         // item.Name = "guid.lzma";
1125         // AddItem(item);
1126         const Byte *pStart = bufData + newOffset;
1127         // do we need correct pStart here for lzma steram offset?
1128         RINOK(DecodeLzma(pStart, newSectSize));
1129         _methodsMask |= (1 << COMPRESSION_TYPE_LZMA);
1130         newBufIndex = _bufs.Size() - 1;
1131         newOffset = 0;
1132         newSectSize = (UInt32)_bufs.Back().Size();
1133         newMethod = COMPRESSION_TYPE_LZMA;
1134       }
1135       else if (AreGuidsEq(p + 0x4, kGuids[kGuidIndex_CRC]) && propsSize == 4)
1136       {
1137         needDir = false;
1138         item.KeepName = false;
1139         if (CrcCalc(bufData + newOffset, newSectSize) != Get32(p + kHeaderSize))
1140           return S_FALSE;
1141       }
1142       else
1143       {
1144         if (propsSize != 0)
1145         {
1146           CItem item2 = item;
1147           item2.Name += ".prop";
1148           item2.Size = propsSize;
1149           item2.Offset = posBase + pos + kHeaderSize;
1150           AddItem(item2);
1151         }
1152       }
1153 
1154       int newParent = parent;
1155       if (needDir)
1156         newParent = AddDirItem(item);
1157       bool error2;
1158       RINOK(ParseSections(newBufIndex, newOffset, newSectSize, newParent, newMethod, level, error2));
1159     }
1160     else if (type == SECTION_FIRMWARE_VOLUME_IMAGE)
1161     {
1162       item.KeepName = false;
1163       int newParent = AddDirItem(item);
1164       RINOK(ParseVolume(bufIndex, posBase + pos + 4,
1165           sectSize - 4,
1166           sectSize - 4,
1167           newParent, method, level));
1168     }
1169     else
1170     {
1171       bool needAdd = true;
1172       switch (type)
1173       {
1174         case SECTION_RAW:
1175         {
1176           const UInt32 kInsydeOffset = 12;
1177           if (sectDataSize >= kFvHeaderSize + kInsydeOffset)
1178           {
1179             if (IsFfs(p + 4 + kInsydeOffset) &&
1180                 sectDataSize - kInsydeOffset == Get64(p + 4 + kInsydeOffset + 0x20))
1181             {
1182               needAdd = false;
1183               item.Name = "vol";
1184               int newParent = AddDirItem(item);
1185               RINOK(ParseVolume(bufIndex, posBase + pos + 4 + kInsydeOffset,
1186                   sectDataSize - kInsydeOffset,
1187                   sectDataSize - kInsydeOffset,
1188                   newParent, method, level));
1189             }
1190 
1191             if (needAdd)
1192             {
1193               const char *ext = FindExt(p + 4, sectDataSize);
1194               if (ext)
1195                 item.Name = ext;
1196             }
1197           }
1198           break;
1199         }
1200         case SECTION_DXE_DEPEX:
1201         case SECTION_PEI_DEPEX:
1202         {
1203           AString s;
1204           if (ParseDepedencyExpression(p + 4, sectDataSize, s))
1205           {
1206             if (s.Len() < (1 << 9))
1207             {
1208               s.InsertAtFront('[');
1209               s += ']';
1210               AddSpaceAndString(_items[item.Parent].Characts, s);
1211               needAdd = false;
1212             }
1213             else
1214             {
1215               item.BufIndex = AddBuf(s.Len());
1216               CByteBuffer &buf0 = _bufs[item.BufIndex];
1217               if (s.Len() != 0)
1218                 memcpy(buf0, s, s.Len());
1219               item.Offset = 0;
1220               item.Size = s.Len();
1221             }
1222           }
1223           break;
1224         }
1225         case SECTION_VERSION:
1226         {
1227           if (sectDataSize > 2)
1228           {
1229             AString s;
1230             if (ParseUtf16zString2(p + 6, sectDataSize - 2, s))
1231             {
1232               AString s2 ("ver:");
1233               s2.Add_UInt32(Get16(p + 4));
1234               s2.Add_Space();
1235               s2 += s;
1236               AddSpaceAndString(_items[item.Parent].Characts, s2);
1237               needAdd = false;
1238             }
1239           }
1240           break;
1241         }
1242         case SECTION_USER_INTERFACE:
1243         {
1244           AString s;
1245           if (ParseUtf16zString2(p + 4, sectDataSize, s))
1246           {
1247             _items[parent].Name = s;
1248             needAdd = false;
1249           }
1250           break;
1251         }
1252         case SECTION_FREEFORM_SUBTYPE_GUID:
1253         {
1254           if (sectDataSize >= kGuidSize)
1255           {
1256             item.SetGuid(p + 4);
1257             item.Size = sectDataSize - kGuidSize;
1258             item.Offset = posBase + pos + 4 + kGuidSize;
1259           }
1260           break;
1261         }
1262       }
1263 
1264       if (needAdd)
1265         AddFileItemWithIndex(item);
1266     }
1267 
1268     pos += sectSize;
1269   }
1270 }
1271 
Count_FF_Bytes(const Byte * p,UInt32 size)1272 static UInt32 Count_FF_Bytes(const Byte *p, UInt32 size)
1273 {
1274   UInt32 i;
1275   for (i = 0; i < size && p[i] == 0xFF; i++);
1276   return i;
1277 }
1278 
Is_FF_Stream(const Byte * p,UInt32 size)1279 static bool Is_FF_Stream(const Byte *p, UInt32 size)
1280 {
1281   return (Count_FF_Bytes(p, size) == size);
1282 }
1283 
1284 struct CVolFfsHeader
1285 {
1286   UInt32 HeaderLen;
1287   UInt64 VolSize;
1288 
1289   bool Parse(const Byte *p);
1290 };
1291 
Parse(const Byte * p)1292 bool CVolFfsHeader::Parse(const Byte *p)
1293 {
1294   if (Get32(p + 0x28) != kFvSignature)
1295     return false;
1296 
1297   UInt32 attribs = Get32(p + 0x2C);
1298   if ((attribs & FVB_ERASE_POLARITY) == 0)
1299     return false;
1300   VolSize = Get64(p + 0x20);
1301   HeaderLen = Get16(p + 0x30);
1302   if (HeaderLen < kFvHeaderSize || (HeaderLen & 0x7) != 0 || VolSize < HeaderLen)
1303     return false;
1304   return true;
1305 };
1306 
1307 
ParseVolume(int bufIndex,UInt32 posBase,UInt32 exactSize,UInt32 limitSize,int parent,int method,int level)1308 HRESULT CHandler::ParseVolume(
1309     int bufIndex, UInt32 posBase,
1310     UInt32 exactSize, UInt32 limitSize,
1311     int parent, int method, int level)
1312 {
1313   if (level > kLevelMax)
1314     return S_FALSE;
1315   MyPrint(posBase, exactSize, level, "Volume");
1316   level++;
1317   if (exactSize < kFvHeaderSize)
1318     return S_FALSE;
1319   const Byte *p = _bufs[bufIndex] + posBase;
1320   // first 16 bytes must be zeros, but they are not zeros sometimes.
1321   if (!IsFfs(p))
1322   {
1323     CItem item;
1324     item.Method = method;
1325     item.BufIndex = bufIndex;
1326     item.Parent = parent;
1327     item.Offset = posBase;
1328     item.Size = exactSize;
1329     if (!Is_FF_Stream(p + kFfsGuidOffset, 16))
1330       item.SetGuid(p + kFfsGuidOffset);
1331     // if (item.Name.IsEmpty())
1332       item.Name += "[VOL]";
1333     AddItem(item);
1334     return S_OK;
1335   }
1336 
1337   CVolFfsHeader ffsHeader;
1338   if (!ffsHeader.Parse(p))
1339     return S_FALSE;
1340   // if (parent >= 0) AddSpaceAndString(_items[parent].Characts, FLAGS_TO_STRING(g_FV_Attribs, attribs));
1341 
1342   // VolSize > exactSize (fh.Size) for some UEFI archives (is it correct UEFI?)
1343   // so we check VolSize for limitSize instead.
1344 
1345   if (ffsHeader.HeaderLen > limitSize || ffsHeader.VolSize > limitSize)
1346     return S_FALSE;
1347 
1348   {
1349     UInt32 checkCalc = 0;
1350     for (UInt32 i = 0; i < ffsHeader.HeaderLen; i += 2)
1351       checkCalc += Get16(p + i);
1352     if ((checkCalc & 0xFFFF) != 0)
1353       return S_FALSE;
1354   }
1355 
1356   // 3 reserved bytes are not zeros sometimes.
1357   // UInt16 ExtHeaderOffset; // in new SPECIFICATION?
1358   // Byte revision = p[0x37];
1359 
1360   UInt32 pos = kFvHeaderSize;
1361   for (;;)
1362   {
1363     if (pos >= ffsHeader.HeaderLen)
1364       return S_FALSE;
1365     UInt32 numBlocks = Get32(p + pos);
1366     UInt32 length = Get32(p + pos + 4);
1367     pos += 8;
1368     if (numBlocks == 0 && length == 0)
1369       break;
1370   }
1371   if (pos != ffsHeader.HeaderLen)
1372     return S_FALSE;
1373 
1374   CRecordVector<UInt32> guidsVector;
1375 
1376   for (;;)
1377   {
1378     UInt32 rem = (UInt32)ffsHeader.VolSize - pos;
1379     if (rem < kFileHeaderSize)
1380       break;
1381     pos = (pos + 7) & ~7;
1382     rem = (UInt32)ffsHeader.VolSize - pos;
1383     if (rem < kFileHeaderSize)
1384       break;
1385 
1386     CItem item;
1387     item.Method = method;
1388     item.BufIndex = bufIndex;
1389     item.Parent = parent;
1390 
1391     const Byte *pFile = p + pos;
1392     CFfsFileHeader fh;
1393     if (!fh.Parse(pFile))
1394     {
1395       UInt32 num_FF_bytes = Count_FF_Bytes(pFile, rem);
1396       if (num_FF_bytes != rem)
1397       {
1398         item.Name = "[junk]";
1399         item.Offset = posBase + pos + num_FF_bytes;
1400         item.Size = rem - num_FF_bytes;
1401         AddItem(item);
1402       }
1403       PrintLevel(level); PRF(printf("== FF FF reminder"));
1404 
1405       break;
1406     }
1407 
1408     PrintLevel(level); PRF(printf("%s, type = %3d,  pos = %7x, size = %7x", "FILE", fh.Type, posBase + pos, fh.Size));
1409 
1410     if (!fh.Check(pFile, rem))
1411       return S_FALSE;
1412 
1413     UInt32 offset = posBase + pos + kFileHeaderSize;
1414     UInt32 sectSize = fh.GetDataSize();
1415     item.Offset = offset;
1416     item.Size = sectSize;
1417 
1418     pos += fh.Size;
1419 
1420     if (fh.Type == FV_FILETYPE_FFS_PAD)
1421       if (Is_FF_Stream(pFile + kFileHeaderSize, sectSize))
1422         continue;
1423 
1424     UInt32 guid32 = Get32(fh.GuidName);
1425     bool full = true;
1426     if (guidsVector.FindInSorted(guid32) < 0)
1427     {
1428       guidsVector.AddToUniqueSorted(guid32);
1429       full = false;
1430     }
1431     item.SetGuid(fh.GuidName, full);
1432 
1433     item.Characts = fh.GetCharacts();
1434     // PrintLevel(level);
1435     PRF(printf(" : %s", item.Characts));
1436 
1437     {
1438       PRF(printf(" attrib = %2d State = %3d ", (unsigned)fh.Attrib, (unsigned)fh.State));
1439       PRF(char s[64]);
1440       PRF(RawLeGuidToString(fh.GuidName, s));
1441       PRF(printf(" : %s ", s));
1442     }
1443 
1444     if (fh.Type == FV_FILETYPE_FFS_PAD ||
1445         fh.Type == FV_FILETYPE_RAW)
1446     {
1447       bool isVolume = false;
1448       if (fh.Type == FV_FILETYPE_RAW)
1449       {
1450         if (sectSize >= kFvHeaderSize)
1451           if (IsFfs(pFile + kFileHeaderSize))
1452             isVolume = true;
1453       }
1454       if (isVolume)
1455       {
1456         int newParent = AddDirItem(item);
1457         UInt32 limSize = fh.GetDataSize2(rem);
1458         // volume.VolSize > fh.Size for some UEFI archives (is it correct UEFI?)
1459         // so we will check VolSize for limitSize instead.
1460         RINOK(ParseVolume(bufIndex, offset, sectSize, limSize, newParent, method, level));
1461       }
1462       else
1463         AddItem(item);
1464     }
1465     else
1466     {
1467       /*
1468       if (fh.Type == FV_FILETYPE_FREEFORM)
1469       {
1470         // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1471         // AddItem(item);
1472       }
1473       else
1474       */
1475       {
1476         int newParent = AddDirItem(item);
1477         bool error2;
1478         RINOK(ParseSections(bufIndex, offset, sectSize, newParent, method, level + 1, error2));
1479         if (error2)
1480         {
1481           // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1482           item.IsDir = false;
1483           item.Size = sectSize;
1484           item.Name.Insert(0, "[ERROR]");
1485           AddItem(item);
1486         }
1487       }
1488     }
1489   }
1490 
1491   return S_OK;
1492 }
1493 
1494 
1495 static const char * const kRegionName[] =
1496 {
1497     "Descriptor"
1498   , "BIOS"
1499   , "ME"
1500   , "GbE"
1501   , "PDR"
1502   , "Region5"
1503   , "Region6"
1504   , "Region7"
1505 };
1506 
1507 
ParseIntelMe(int bufIndex,UInt32 posBase,UInt32 exactSize,UInt32 limitSize,int parent,int method,int level)1508 HRESULT CHandler::ParseIntelMe(
1509     int bufIndex, UInt32 posBase,
1510     UInt32 exactSize, UInt32 limitSize,
1511     int parent, int method, int level)
1512 {
1513   UNUSED_VAR(limitSize)
1514   level++;
1515   const Byte *p = _bufs[bufIndex] + posBase;
1516   if (exactSize < 16 + 16)
1517     return S_FALSE;
1518   if (!IsIntelMe(p))
1519     return S_FALSE;
1520 
1521   UInt32 v0 = GetUi32(p + 20);
1522   // UInt32 numRegions = (v0 >> 24) & 0x7;
1523   UInt32 regAddr = (v0 >> 12) & 0xFF0;
1524   // UInt32 numComps  = (v0 >> 8) & 0x3;
1525   // UInt32 fcba = (v0 <<  4) & 0xFF0;
1526 
1527   // (numRegions == 0) in header in some new images.
1528   // So we don't use the value from header
1529   UInt32 numRegions = 7;
1530 
1531   for (unsigned i = 0; i <= numRegions; i++)
1532   {
1533     UInt32 offset = regAddr + i * 4;
1534     if (offset + 4 > exactSize)
1535       break;
1536     UInt32 val = GetUi32(p + offset);
1537 
1538     // only 12 bits probably are OK.
1539     // How does it work for files larger than 16 MB?
1540     const UInt32 kMask = 0xFFF;
1541     // const UInt32 kMask = 0xFFFF; // 16-bit is more logical
1542     const UInt32 lim  = (val >> 16) & kMask;
1543     const UInt32 base = (val & kMask);
1544 
1545     /*
1546       strange combinations:
1547         PDR:   base = 0x1FFF lim = 0;
1548         empty: base = 0xFFFF lim = 0xFFFF;
1549 
1550         PDR:   base = 0x7FFF lim = 0;
1551         empty: base = 0x7FFF lim = 0;
1552     */
1553     if (base == kMask && lim == 0)
1554       continue; // unused
1555 
1556     if (lim < base)
1557       continue; // unused
1558 
1559     CItem item;
1560     item.Name = kRegionName[i];
1561     item.Method = method;
1562     item.BufIndex = bufIndex;
1563     item.Parent = parent;
1564     item.Offset = posBase + (base << 12);
1565     if (item.Offset > exactSize)
1566       continue;
1567     item.Size = (lim + 1 - base) << 12;
1568     // item.SetGuid(p + kFfsGuidOffset);
1569     // item.Name += " [VOLUME]";
1570     AddItem(item);
1571   }
1572   return S_OK;
1573 }
1574 
1575 
OpenCapsule(IInStream * stream)1576 HRESULT CHandler::OpenCapsule(IInStream *stream)
1577 {
1578   const unsigned kHeaderSize = 80;
1579   Byte buf[kHeaderSize];
1580   RINOK(ReadStream_FALSE(stream, buf, kHeaderSize));
1581   if (!_h.Parse(buf))
1582     return S_FALSE;
1583   if (_h.CapsuleImageSize < kHeaderSize
1584        || _h.CapsuleImageSize < _h.HeaderSize
1585        || _h.OffsetToCapsuleBody < _h.HeaderSize
1586        || _h.OffsetToCapsuleBody > _h.CapsuleImageSize
1587       )
1588     return S_FALSE;
1589   _phySize = _h.CapsuleImageSize;
1590 
1591   if (_h.SequenceNumber != 0 ||
1592       _h.OffsetToSplitInformation != 0 )
1593     return E_NOTIMPL;
1594 
1595   unsigned bufIndex = AddBuf(_h.CapsuleImageSize);
1596   CByteBuffer &buf0 = _bufs[bufIndex];
1597   memcpy(buf0, buf, kHeaderSize);
1598   ReadStream_FALSE(stream, buf0 + kHeaderSize, _h.CapsuleImageSize - kHeaderSize);
1599 
1600   AddCommentString("Author", _h.OffsetToAuthorInformation);
1601   AddCommentString("Revision", _h.OffsetToRevisionInformation);
1602   AddCommentString("Short Description", _h.OffsetToShortDescription);
1603   AddCommentString("Long Description", _h.OffsetToLongDescription);
1604 
1605 
1606   const UInt32 size = _h.CapsuleImageSize - _h.OffsetToCapsuleBody;
1607 
1608   if (size >= 32 && IsIntelMe(buf0 + _h.OffsetToCapsuleBody))
1609     return ParseIntelMe(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1610 
1611   return ParseVolume(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1612 }
1613 
1614 
OpenFv(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)1615 HRESULT CHandler::OpenFv(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback * /* callback */)
1616 {
1617   Byte buf[kFvHeaderSize];
1618   RINOK(ReadStream_FALSE(stream, buf, kFvHeaderSize));
1619   if (!IsFfs(buf))
1620     return S_FALSE;
1621   CVolFfsHeader ffsHeader;
1622   if (!ffsHeader.Parse(buf))
1623     return S_FALSE;
1624   if (ffsHeader.VolSize > ((UInt32)1 << 30))
1625     return S_FALSE;
1626   _phySize = ffsHeader.VolSize;
1627   RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
1628   UInt32 fvSize32 = (UInt32)ffsHeader.VolSize;
1629   unsigned bufIndex = AddBuf(fvSize32);
1630   RINOK(ReadStream_FALSE(stream, _bufs[bufIndex], fvSize32));
1631   return ParseVolume(bufIndex, 0, fvSize32, fvSize32, -1, -1, 0);
1632 }
1633 
1634 
Open2(IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * callback)1635 HRESULT CHandler::Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
1636 {
1637   if (_capsuleMode)
1638   {
1639     RINOK(OpenCapsule(stream));
1640   }
1641   else
1642   {
1643     RINOK(OpenFv(stream, maxCheckStartPosition, callback));
1644   }
1645 
1646   unsigned num = _items.Size();
1647   CIntArr numChilds(num);
1648 
1649   unsigned i;
1650 
1651   for (i = 0; i < num; i++)
1652     numChilds[i] = 0;
1653 
1654   for (i = 0; i < num; i++)
1655   {
1656     int parent = _items[i].Parent;
1657     if (parent >= 0)
1658       numChilds[(unsigned)parent]++;
1659   }
1660 
1661   for (i = 0; i < num; i++)
1662   {
1663     const CItem &item = _items[i];
1664     int parent = item.Parent;
1665     if (parent >= 0)
1666     {
1667       CItem &parentItem = _items[(unsigned)parent];
1668       if (numChilds[(unsigned)parent] == 1)
1669         if (!item.ThereIsUniqueName || !parentItem.ThereIsUniqueName || !parentItem.ThereAreSubDirs)
1670           parentItem.Skip = true;
1671     }
1672   }
1673 
1674   CUIntVector mainToReduced;
1675 
1676   for (i = 0; i < _items.Size(); i++)
1677   {
1678     mainToReduced.Add(_items2.Size());
1679     const CItem &item = _items[i];
1680     if (item.Skip)
1681       continue;
1682     AString name;
1683     int numItems = -1;
1684     int parent = item.Parent;
1685     if (parent >= 0)
1686       numItems = numChilds[(unsigned)parent];
1687     AString name2 (item.GetName(numItems));
1688     AString characts2 (item.Characts);
1689     if (item.KeepName)
1690       name = name2;
1691 
1692     while (parent >= 0)
1693     {
1694       const CItem &item3 = _items[(unsigned)parent];
1695       if (!item3.Skip)
1696         break;
1697       if (item3.KeepName)
1698       {
1699         AString name3 (item3.GetName(-1));
1700         if (name.IsEmpty())
1701           name = name3;
1702         else
1703           name = name3 + '.' + name;
1704       }
1705       AddSpaceAndString(characts2, item3.Characts);
1706       parent = item3.Parent;
1707     }
1708 
1709     if (name.IsEmpty())
1710       name = name2;
1711 
1712     CItem2 item2;
1713     item2.MainIndex = i;
1714     item2.Name = name;
1715     item2.Characts = characts2;
1716     if (parent >= 0)
1717       item2.Parent = mainToReduced[(unsigned)parent];
1718     _items2.Add(item2);
1719     /*
1720     CItem2 item2;
1721     item2.MainIndex = i;
1722     item2.Name = item.Name;
1723     item2.Parent = item.Parent;
1724     _items2.Add(item2);
1725     */
1726   }
1727 
1728   return S_OK;
1729 }
1730 
Open(IInStream * inStream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * callback)1731 STDMETHODIMP CHandler::Open(IInStream *inStream,
1732     const UInt64 *maxCheckStartPosition,
1733     IArchiveOpenCallback *callback)
1734 {
1735   COM_TRY_BEGIN
1736   Close();
1737   {
1738     HRESULT res = Open2(inStream, maxCheckStartPosition, callback);
1739     if (res == E_NOTIMPL)
1740       res = S_FALSE;
1741     return res;
1742   }
1743   COM_TRY_END
1744 }
1745 
Close()1746 STDMETHODIMP CHandler::Close()
1747 {
1748   _phySize = 0;
1749   _totalBufsSize = 0;
1750   _methodsMask = 0;
1751   _items.Clear();
1752   _items2.Clear();
1753   _bufs.Clear();
1754   _comment.Empty();
1755   _headersError = false;
1756   _h.Clear();
1757   return S_OK;
1758 }
1759 
GetNumberOfItems(UInt32 * numItems)1760 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1761 {
1762   *numItems = _items2.Size();
1763   return S_OK;
1764 }
1765 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)1766 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1767     Int32 testMode, IArchiveExtractCallback *extractCallback)
1768 {
1769   COM_TRY_BEGIN
1770   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1771   if (allFilesMode)
1772     numItems = _items2.Size();
1773   if (numItems == 0)
1774     return S_OK;
1775   UInt64 totalSize = 0;
1776   UInt32 i;
1777   for (i = 0; i < numItems; i++)
1778     totalSize += _items[_items2[allFilesMode ? i : indices[i]].MainIndex].Size;
1779   extractCallback->SetTotal(totalSize);
1780 
1781   UInt64 currentTotalSize = 0;
1782 
1783   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1784   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1785 
1786   CLocalProgress *lps = new CLocalProgress;
1787   CMyComPtr<ICompressProgressInfo> progress = lps;
1788   lps->Init(extractCallback, false);
1789 
1790   for (i = 0; i < numItems; i++)
1791   {
1792     lps->InSize = lps->OutSize = currentTotalSize;
1793     RINOK(lps->SetCur());
1794     CMyComPtr<ISequentialOutStream> realOutStream;
1795     Int32 askMode = testMode ?
1796         NExtract::NAskMode::kTest :
1797         NExtract::NAskMode::kExtract;
1798     UInt32 index = allFilesMode ? i : indices[i];
1799     const CItem &item = _items[_items2[index].MainIndex];
1800     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
1801     currentTotalSize += item.Size;
1802 
1803     if (!testMode && !realOutStream)
1804       continue;
1805     RINOK(extractCallback->PrepareOperation(askMode));
1806     if (testMode || item.IsDir)
1807     {
1808       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
1809       continue;
1810     }
1811     int res = NExtract::NOperationResult::kDataError;
1812     CMyComPtr<ISequentialInStream> inStream;
1813     GetStream(index, &inStream);
1814     if (inStream)
1815     {
1816       RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
1817       if (copyCoderSpec->TotalSize == item.Size)
1818         res = NExtract::NOperationResult::kOK;
1819     }
1820     realOutStream.Release();
1821     RINOK(extractCallback->SetOperationResult(res));
1822   }
1823   return S_OK;
1824   COM_TRY_END
1825 }
1826 
GetStream(UInt32 index,ISequentialInStream ** stream)1827 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
1828 {
1829   COM_TRY_BEGIN
1830   const CItem &item = _items[_items2[index].MainIndex];
1831   if (item.IsDir)
1832     return S_FALSE;
1833   CBufInStream *streamSpec = new CBufInStream;
1834   CMyComPtr<IInStream> streamTemp = streamSpec;
1835   const CByteBuffer &buf = _bufs[item.BufIndex];
1836   if (item.Offset > buf.Size())
1837     return S_FALSE;
1838   size_t size = buf.Size() - item.Offset;
1839   if (size > item.Size)
1840     size = item.Size;
1841   streamSpec->Init(buf + item.Offset, size, (IInArchive *)this);
1842   *stream = streamTemp.Detach();
1843   return S_OK;
1844   COM_TRY_END
1845 }
1846 
1847 
1848 namespace UEFIc {
1849 
1850 static const Byte k_Capsule_Signatures[] =
1851 {
1852    16, CAPSULE_SIGNATURE,
1853    16, CAPSULE2_SIGNATURE,
1854    16, CAPSULE_UEFI_SIGNATURE
1855 };
1856 
1857 REGISTER_ARC_I_CLS(
1858   CHandler(true),
1859   "UEFIc", "scap", 0, 0xD0,
1860   k_Capsule_Signatures,
1861   0,
1862   NArcInfoFlags::kMultiSignature |
1863   NArcInfoFlags::kFindSignature,
1864   NULL)
1865 
1866 }
1867 
1868 namespace UEFIf {
1869 
1870 static const Byte k_FFS_Signatures[] =
1871 {
1872    16, FFS1_SIGNATURE,
1873    16, FFS2_SIGNATURE
1874 };
1875 
1876 
1877 REGISTER_ARC_I_CLS(
1878   CHandler(false),
1879   "UEFIf", "uefif", 0, 0xD1,
1880   k_FFS_Signatures,
1881   kFfsGuidOffset,
1882   NArcInfoFlags::kMultiSignature |
1883   NArcInfoFlags::kFindSignature,
1884   NULL)
1885 
1886 }
1887 
1888 }}
1889