5 // Jb Evain (jbevain@gmail.com)
7 // Copyright (c) 2008 - 2011 Jb Evain
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using Mono.Cecil.Metadata;
34 using RVA = System.UInt32;
36 namespace Mono.Cecil.PE {
38 sealed class ImageReader : BinaryStreamReader {
43 DataDirectory metadata;
45 public ImageReader (Stream stream)
50 image.FileName = stream.GetFullyQualifiedName ();
53 void MoveTo (DataDirectory directory)
55 BaseStream.Position = image.ResolveVirtualAddress (directory.VirtualAddress);
58 void MoveTo (uint position)
60 BaseStream.Position = position;
65 if (BaseStream.Length < 128)
66 throw new BadImageFormatException ();
75 if (ReadUInt16 () != 0x5a4d)
76 throw new BadImageFormatException ();
80 MoveTo (ReadUInt32 ());
82 if (ReadUInt32 () != 0x00004550)
83 throw new BadImageFormatException ();
88 image.Architecture = ReadArchitecture ();
91 ushort sections = ReadUInt16 ();
94 // PointerToSymbolTable 4
96 // OptionalHeaderSize 2
100 ushort characteristics = ReadUInt16 ();
103 ReadOptionalHeaders (out subsystem);
104 ReadSections (sections);
108 image.Kind = GetModuleKind (characteristics, subsystem);
111 TargetArchitecture ReadArchitecture ()
113 var machine = ReadUInt16 ();
116 return TargetArchitecture.I386;
118 return TargetArchitecture.AMD64;
120 return TargetArchitecture.IA64;
122 return TargetArchitecture.ARMv7;
125 throw new NotSupportedException ();
128 static ModuleKind GetModuleKind (ushort characteristics, ushort subsystem)
130 if ((characteristics & 0x2000) != 0) // ImageCharacteristics.Dll
131 return ModuleKind.Dll;
133 if (subsystem == 0x2 || subsystem == 0x9) // SubSystem.WindowsGui || SubSystem.WindowsCeGui
134 return ModuleKind.Windows;
136 return ModuleKind.Console;
139 void ReadOptionalHeaders (out ushort subsystem)
141 // - PEOptionalHeader
142 // - StandardFieldsHeader
145 bool pe64 = ReadUInt16 () == 0x20b;
152 // InitializedDataSize 4
153 // UninitializedDataSize4
158 // - NTSpecificFieldsHeader
161 // SectionAlignment 4
176 subsystem = ReadUInt16 ();
179 // StackReserveSize 4 || 8
180 // StackCommitSize 4 || 8
181 // HeapReserveSize 4 || 8
182 // HeapCommitSize 4 || 8
186 // - DataDirectoriesHeader
192 // CertificateTable 8
193 // BaseRelocationTable 8
195 Advance (pe64 ? 90 : 74);
198 image.Debug = ReadDataDirectory ();
206 // DelayImportDescriptor8
210 cli = ReadDataDirectory ();
213 throw new BadImageFormatException ();
219 string ReadAlignedString (int length)
222 var buffer = new char [length];
223 while (read < length) {
224 var current = ReadByte ();
228 buffer [read++] = (char) current;
231 Advance (-1 + ((read + 4) & ~3) - read);
233 return new string (buffer, 0, read);
236 string ReadZeroTerminatedString (int length)
239 var buffer = new char [length];
240 var bytes = ReadBytes (length);
241 while (read < length) {
242 var current = bytes [read];
246 buffer [read++] = (char) current;
249 return new string (buffer, 0, read);
252 void ReadSections (ushort count)
254 var sections = new Section [count];
256 for (int i = 0; i < count; i++) {
257 var section = new Section ();
260 section.Name = ReadZeroTerminatedString (8);
266 section.VirtualAddress = ReadUInt32 ();
268 section.SizeOfRawData = ReadUInt32 ();
269 // PointerToRawData 4
270 section.PointerToRawData = ReadUInt32 ();
272 // PointerToRelocations 4
273 // PointerToLineNumbers 4
274 // NumberOfRelocations 2
275 // NumberOfLineNumbers 2
279 sections [i] = section;
281 ReadSectionData (section);
284 image.Sections = sections;
287 void ReadSectionData (Section section)
289 var position = BaseStream.Position;
291 MoveTo (section.PointerToRawData);
293 var length = (int) section.SizeOfRawData;
294 var data = new byte [length];
295 int offset = 0, read;
297 while ((read = Read (data, offset, length - offset)) > 0)
302 BaseStream.Position = position;
305 void ReadCLIHeader ()
312 // MajorRuntimeVersion 2
313 // MinorRuntimeVersion 2
317 metadata = ReadDataDirectory ();
319 image.Attributes = (ModuleAttributes) ReadUInt32 ();
321 image.EntryPointToken = ReadUInt32 ();
323 image.Resources = ReadDataDirectory ();
324 // StrongNameSignature 8
325 // CodeManagerTable 8
327 // ExportAddressTableJumps 8
328 // ManagedNativeHeader 8
335 if (ReadUInt32 () != 0x424a5342)
336 throw new BadImageFormatException ();
343 var version = ReadZeroTerminatedString (ReadInt32 ());
344 image.Runtime = version.ParseRuntime ();
349 var streams = ReadUInt16 ();
351 var section = image.GetSectionAtVirtualAddress (metadata.VirtualAddress);
353 throw new BadImageFormatException ();
355 image.MetadataSection = section;
357 for (int i = 0; i < streams; i++)
358 ReadMetadataStream (section);
360 if (image.TableHeap != null)
364 void ReadMetadataStream (Section section)
367 uint start = metadata.VirtualAddress - section.VirtualAddress + ReadUInt32 (); // relative to the section start
370 uint size = ReadUInt32 ();
372 var name = ReadAlignedString (16);
376 image.TableHeap = new TableHeap (section, start, size);
379 image.StringHeap = new StringHeap (section, start, size);
382 image.BlobHeap = new BlobHeap (section, start, size);
385 image.GuidHeap = new GuidHeap (section, start, size);
388 image.UserStringHeap = new UserStringHeap (section, start, size);
393 void ReadTableHeap ()
395 var heap = image.TableHeap;
397 uint start = heap.Section.PointerToRawData;
399 MoveTo (heap.Offset + start);
407 var sizes = ReadByte ();
413 heap.Valid = ReadInt64 ();
416 heap.Sorted = ReadInt64 ();
418 for (int i = 0; i < TableHeap.TableCount; i++) {
419 if (!heap.HasTable ((Table) i))
422 heap.Tables [i].Length = ReadUInt32 ();
425 SetIndexSize (image.StringHeap, sizes, 0x1);
426 SetIndexSize (image.GuidHeap, sizes, 0x2);
427 SetIndexSize (image.BlobHeap, sizes, 0x4);
429 ComputeTableInformations ();
432 static void SetIndexSize (Heap heap, uint sizes, byte flag)
437 heap.IndexSize = (sizes & flag) > 0 ? 4 : 2;
440 int GetTableIndexSize (Table table)
442 return image.GetTableIndexSize (table);
445 int GetCodedIndexSize (CodedIndex index)
447 return image.GetCodedIndexSize (index);
450 void ComputeTableInformations ()
452 uint offset = (uint) BaseStream.Position - image.MetadataSection.PointerToRawData; // header
454 int stridx_size = image.StringHeap.IndexSize;
455 int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2;
457 var heap = image.TableHeap;
458 var tables = heap.Tables;
460 for (int i = 0; i < TableHeap.TableCount; i++) {
461 var table = (Table) i;
462 if (!heap.HasTable (table))
468 size = 2 // Generation
469 + stridx_size // Name
470 + (image.GuidHeap.IndexSize * 3); // Mvid, EncId, EncBaseId
473 size = GetCodedIndexSize (CodedIndex.ResolutionScope) // ResolutionScope
474 + (stridx_size * 2); // Name, Namespace
478 + (stridx_size * 2) // Name, Namespace
479 + GetCodedIndexSize (CodedIndex.TypeDefOrRef) // BaseType
480 + GetTableIndexSize (Table.Field) // FieldList
481 + GetTableIndexSize (Table.Method); // MethodList
484 size = GetTableIndexSize (Table.Field); // Field
488 + stridx_size // Name
489 + blobidx_size; // Signature
491 case Table.MethodPtr:
492 size = GetTableIndexSize (Table.Method); // Method
495 size = 8 // Rva 4, ImplFlags 2, Flags 2
496 + stridx_size // Name
497 + blobidx_size // Signature
498 + GetTableIndexSize (Table.Param); // ParamList
501 size = GetTableIndexSize (Table.Param); // Param
504 size = 4 // Flags 2, Sequence 2
505 + stridx_size; // Name
507 case Table.InterfaceImpl:
508 size = GetTableIndexSize (Table.TypeDef) // Class
509 + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // Interface
511 case Table.MemberRef:
512 size = GetCodedIndexSize (CodedIndex.MemberRefParent) // Class
513 + stridx_size // Name
514 + blobidx_size; // Signature
518 + GetCodedIndexSize (CodedIndex.HasConstant) // Parent
519 + blobidx_size; // Value
521 case Table.CustomAttribute:
522 size = GetCodedIndexSize (CodedIndex.HasCustomAttribute) // Parent
523 + GetCodedIndexSize (CodedIndex.CustomAttributeType) // Type
524 + blobidx_size; // Value
526 case Table.FieldMarshal:
527 size = GetCodedIndexSize (CodedIndex.HasFieldMarshal) // Parent
528 + blobidx_size; // NativeType
530 case Table.DeclSecurity:
532 + GetCodedIndexSize (CodedIndex.HasDeclSecurity) // Parent
533 + blobidx_size; // PermissionSet
535 case Table.ClassLayout:
536 size = 6 // PackingSize 2, ClassSize 4
537 + GetTableIndexSize (Table.TypeDef); // Parent
539 case Table.FieldLayout:
541 + GetTableIndexSize (Table.Field); // Field
543 case Table.StandAloneSig:
544 size = blobidx_size; // Signature
547 size = GetTableIndexSize (Table.TypeDef) // Parent
548 + GetTableIndexSize (Table.Event); // EventList
551 size = GetTableIndexSize (Table.Event); // Event
555 + stridx_size // Name
556 + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // EventType
558 case Table.PropertyMap:
559 size = GetTableIndexSize (Table.TypeDef) // Parent
560 + GetTableIndexSize (Table.Property); // PropertyList
562 case Table.PropertyPtr:
563 size = GetTableIndexSize (Table.Property); // Property
567 + stridx_size // Name
568 + blobidx_size; // Type
570 case Table.MethodSemantics:
571 size = 2 // Semantics
572 + GetTableIndexSize (Table.Method) // Method
573 + GetCodedIndexSize (CodedIndex.HasSemantics); // Association
575 case Table.MethodImpl:
576 size = GetTableIndexSize (Table.TypeDef) // Class
577 + GetCodedIndexSize (CodedIndex.MethodDefOrRef) // MethodBody
578 + GetCodedIndexSize (CodedIndex.MethodDefOrRef); // MethodDeclaration
580 case Table.ModuleRef:
581 size = stridx_size; // Name
584 size = blobidx_size; // Signature
587 size = 2 // MappingFlags
588 + GetCodedIndexSize (CodedIndex.MemberForwarded) // MemberForwarded
589 + stridx_size // ImportName
590 + GetTableIndexSize (Table.ModuleRef); // ImportScope
594 + GetTableIndexSize (Table.Field); // Field
601 size = 16 // HashAlgId 4, Version 4 * 2, Flags 4
602 + blobidx_size // PublicKey
603 + (stridx_size * 2); // Name, Culture
605 case Table.AssemblyProcessor:
606 size = 4; // Processor
608 case Table.AssemblyOS:
609 size = 12; // Platform 4, Version 2 * 4
611 case Table.AssemblyRef:
612 size = 12 // Version 2 * 4 + Flags 4
613 + (blobidx_size * 2) // PublicKeyOrToken, HashValue
614 + (stridx_size * 2); // Name, Culture
616 case Table.AssemblyRefProcessor:
617 size = 4 // Processor
618 + GetTableIndexSize (Table.AssemblyRef); // AssemblyRef
620 case Table.AssemblyRefOS:
621 size = 12 // Platform 4, Version 2 * 4
622 + GetTableIndexSize (Table.AssemblyRef); // AssemblyRef
626 + stridx_size // Name
627 + blobidx_size; // HashValue
629 case Table.ExportedType:
630 size = 8 // Flags 4, TypeDefId 4
631 + (stridx_size * 2) // Name, Namespace
632 + GetCodedIndexSize (CodedIndex.Implementation); // Implementation
634 case Table.ManifestResource:
635 size = 8 // Offset, Flags
636 + stridx_size // Name
637 + GetCodedIndexSize (CodedIndex.Implementation); // Implementation
639 case Table.NestedClass:
640 size = GetTableIndexSize (Table.TypeDef) // NestedClass
641 + GetTableIndexSize (Table.TypeDef); // EnclosingClass
643 case Table.GenericParam:
644 size = 4 // Number, Flags
645 + GetCodedIndexSize (CodedIndex.TypeOrMethodDef) // Owner
646 + stridx_size; // Name
648 case Table.MethodSpec:
649 size = GetCodedIndexSize (CodedIndex.MethodDefOrRef) // Method
650 + blobidx_size; // Instantiation
652 case Table.GenericParamConstraint:
653 size = GetTableIndexSize (Table.GenericParam) // Owner
654 + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // Constraint
657 throw new NotSupportedException ();
660 tables [i].RowSize = (uint) size;
661 tables [i].Offset = offset;
663 offset += (uint) size * tables [i].Length;
667 public static Image ReadImageFrom (Stream stream)
670 var reader = new ImageReader (stream);
673 } catch (EndOfStreamException e) {
674 throw new BadImageFormatException (stream.GetFullyQualifiedName (), e);