Files
TheDeclineOfWarriors/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs
T
2026-03-30 20:11:57 +07:00

812 lines
28 KiB
C#

//
// Author:
// Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2015 Jb Evain
// Copyright (c) 2008 - 2011 Novell, Inc.
//
// Licensed under the MIT/X11 license.
//
using MonoFN.Cecil.Cil;
using MonoFN.Cecil.Metadata;
using System;
using System.IO;
namespace MonoFN.Cecil.PE
{
internal sealed class ImageReader : BinaryStreamReader
{
private readonly Image image;
private DataDirectory cli;
private DataDirectory metadata;
private uint table_heap_offset;
public ImageReader(Disposable<Stream> stream, string file_name) : base(stream.value)
{
image = new();
image.Stream = stream;
image.FileName = file_name;
}
private void MoveTo(DataDirectory directory)
{
BaseStream.Position = image.ResolveVirtualAddress(directory.VirtualAddress);
}
private void ReadImage()
{
if (BaseStream.Length < 128)
throw new BadImageFormatException();
// - DOSHeader
// PE 2
// Start 58
// Lfanew 4
// End 64
if (ReadUInt16() != 0x5a4d)
throw new BadImageFormatException();
Advance(58);
MoveTo(ReadUInt32());
if (ReadUInt32() != 0x00004550)
throw new BadImageFormatException();
// - PEFileHeader
// Machine 2
image.Architecture = ReadArchitecture();
// NumberOfSections 2
ushort sections = ReadUInt16();
// TimeDateStamp 4
image.Timestamp = ReadUInt32();
// PointerToSymbolTable 4
// NumberOfSymbols 4
// OptionalHeaderSize 2
Advance(10);
// Characteristics 2
ushort characteristics = ReadUInt16();
ushort subsystem, dll_characteristics;
ReadOptionalHeaders(out subsystem, out dll_characteristics);
ReadSections(sections);
ReadCLIHeader();
ReadMetadata();
ReadDebugHeader();
image.Characteristics = characteristics;
image.Kind = GetModuleKind(characteristics, subsystem);
image.DllCharacteristics = (ModuleCharacteristics)dll_characteristics;
}
private TargetArchitecture ReadArchitecture()
{
return (TargetArchitecture)ReadUInt16();
}
private static ModuleKind GetModuleKind(ushort characteristics, ushort subsystem)
{
if ((characteristics & 0x2000) != 0) // ImageCharacteristics.Dll
return ModuleKind.Dll;
if (subsystem == 0x2 || subsystem == 0x9) // SubSystem.WindowsGui || SubSystem.WindowsCeGui
return ModuleKind.Windows;
return ModuleKind.Console;
}
private void ReadOptionalHeaders(out ushort subsystem, out ushort dll_characteristics)
{
// - PEOptionalHeader
// - StandardFieldsHeader
// Magic 2
bool pe64 = ReadUInt16() == 0x20b;
// pe32 || pe64
image.LinkerVersion = ReadUInt16();
// CodeSize 4
// InitializedDataSize 4
// UninitializedDataSize4
// EntryPointRVA 4
// BaseOfCode 4
// BaseOfData 4 || 0
// - NTSpecificFieldsHeader
// ImageBase 4 || 8
// SectionAlignment 4
// FileAlignement 4
// OSMajor 2
// OSMinor 2
// UserMajor 2
// UserMinor 2
// SubSysMajor 2
// SubSysMinor 2
Advance(44);
image.SubSystemMajor = ReadUInt16();
image.SubSystemMinor = ReadUInt16();
// Reserved 4
// ImageSize 4
// HeaderSize 4
// FileChecksum 4
Advance(16);
// SubSystem 2
subsystem = ReadUInt16();
// DLLFlags 2
dll_characteristics = ReadUInt16();
// StackReserveSize 4 || 8
// StackCommitSize 4 || 8
// HeapReserveSize 4 || 8
// HeapCommitSize 4 || 8
// LoaderFlags 4
// NumberOfDataDir 4
// - DataDirectoriesHeader
// ExportTable 8
// ImportTable 8
Advance(pe64 ? 56 : 40);
// ResourceTable 8
image.Win32Resources = ReadDataDirectory();
// ExceptionTable 8
// CertificateTable 8
// BaseRelocationTable 8
Advance(24);
// Debug 8
image.Debug = ReadDataDirectory();
// Copyright 8
// GlobalPtr 8
// TLSTable 8
// LoadConfigTable 8
// BoundImport 8
// IAT 8
// DelayImportDescriptor8
Advance(56);
// CLIHeader 8
cli = ReadDataDirectory();
if (cli.IsZero)
throw new BadImageFormatException();
// Reserved 8
Advance(8);
}
private string ReadAlignedString(int length)
{
int read = 0;
char[] buffer = new char [length];
while (read < length)
{
byte current = ReadByte();
if (current == 0)
break;
buffer[read++] = (char)current;
}
Advance(-1 + ((read + 4) & ~3) - read);
return new(buffer, 0, read);
}
private string ReadZeroTerminatedString(int length)
{
int read = 0;
char[] buffer = new char [length];
byte[] bytes = ReadBytes(length);
while (read < length)
{
byte current = bytes[read];
if (current == 0)
break;
buffer[read++] = (char)current;
}
return new(buffer, 0, read);
}
private void ReadSections(ushort count)
{
Section[] sections = new Section [count];
for (int i = 0; i < count; i++)
{
Section section = new();
// Name
section.Name = ReadZeroTerminatedString(8);
// VirtualSize 4
Advance(4);
// VirtualAddress 4
section.VirtualAddress = ReadUInt32();
// SizeOfRawData 4
section.SizeOfRawData = ReadUInt32();
// PointerToRawData 4
section.PointerToRawData = ReadUInt32();
// PointerToRelocations 4
// PointerToLineNumbers 4
// NumberOfRelocations 2
// NumberOfLineNumbers 2
// Characteristics 4
Advance(16);
sections[i] = section;
}
image.Sections = sections;
}
private void ReadCLIHeader()
{
MoveTo(cli);
// - CLIHeader
// Cb 4
// MajorRuntimeVersion 2
// MinorRuntimeVersion 2
Advance(8);
// Metadata 8
metadata = ReadDataDirectory();
// Flags 4
image.Attributes = (ModuleAttributes)ReadUInt32();
// EntryPointToken 4
image.EntryPointToken = ReadUInt32();
// Resources 8
image.Resources = ReadDataDirectory();
// StrongNameSignature 8
image.StrongName = ReadDataDirectory();
// CodeManagerTable 8
// VTableFixups 8
// ExportAddressTableJumps 8
// ManagedNativeHeader 8
}
private void ReadMetadata()
{
MoveTo(metadata);
if (ReadUInt32() != 0x424a5342)
throw new BadImageFormatException();
// MajorVersion 2
// MinorVersion 2
// Reserved 4
Advance(8);
image.RuntimeVersion = ReadZeroTerminatedString(ReadInt32());
// Flags 2
Advance(2);
ushort streams = ReadUInt16();
Section section = image.GetSectionAtVirtualAddress(metadata.VirtualAddress);
if (section == null)
throw new BadImageFormatException();
image.MetadataSection = section;
for (int i = 0; i < streams; i++)
ReadMetadataStream(section);
if (image.PdbHeap != null)
ReadPdbHeap();
if (image.TableHeap != null)
ReadTableHeap();
}
private void ReadDebugHeader()
{
if (image.Debug.IsZero)
{
image.DebugHeader = new(Empty<ImageDebugHeaderEntry>.Array);
return;
}
MoveTo(image.Debug);
ImageDebugHeaderEntry[] entries = new ImageDebugHeaderEntry [(int)image.Debug.Size / ImageDebugDirectory.Size];
for (int i = 0; i < entries.Length; i++)
{
ImageDebugDirectory directory = new()
{
Characteristics = ReadInt32(),
TimeDateStamp = ReadInt32(),
MajorVersion = ReadInt16(),
MinorVersion = ReadInt16(),
Type = (ImageDebugType)ReadInt32(),
SizeOfData = ReadInt32(),
AddressOfRawData = ReadInt32(),
PointerToRawData = ReadInt32()
};
if (directory.PointerToRawData == 0 || directory.SizeOfData < 0)
{
entries[i] = new(directory, Empty<byte>.Array);
continue;
}
int position = Position;
try
{
MoveTo((uint)directory.PointerToRawData);
byte[] data = ReadBytes(directory.SizeOfData);
entries[i] = new(directory, data);
}
finally
{
Position = position;
}
}
image.DebugHeader = new(entries);
}
private void ReadMetadataStream(Section section)
{
// Offset 4
uint offset = metadata.VirtualAddress - section.VirtualAddress + ReadUInt32(); // relative to the section start
// Size 4
uint size = ReadUInt32();
byte[] data = ReadHeapData(offset, size);
string name = ReadAlignedString(16);
switch (name)
{
case "#~":
case "#-":
image.TableHeap = new(data);
table_heap_offset = offset;
break;
case "#Strings":
image.StringHeap = new(data);
break;
case "#Blob":
image.BlobHeap = new(data);
break;
case "#GUID":
image.GuidHeap = new(data);
break;
case "#US":
image.UserStringHeap = new(data);
break;
case "#Pdb":
image.PdbHeap = new(data);
break;
}
}
private byte[] ReadHeapData(uint offset, uint size)
{
long position = BaseStream.Position;
MoveTo(offset + image.MetadataSection.PointerToRawData);
byte[] data = ReadBytes((int)size);
BaseStream.Position = position;
return data;
}
private void ReadTableHeap()
{
TableHeap heap = image.TableHeap;
MoveTo(table_heap_offset + image.MetadataSection.PointerToRawData);
// Reserved 4
// MajorVersion 1
// MinorVersion 1
Advance(6);
// HeapSizes 1
byte sizes = ReadByte();
// Reserved2 1
Advance(1);
// Valid 8
heap.Valid = ReadInt64();
// Sorted 8
heap.Sorted = ReadInt64();
if (image.PdbHeap != null)
{
for (int i = 0; i < Mixin.TableCount; i++)
{
if (!image.PdbHeap.HasTable((Table)i))
continue;
heap.Tables[i].Length = image.PdbHeap.TypeSystemTableRows[i];
}
}
for (int i = 0; i < Mixin.TableCount; i++)
{
if (!heap.HasTable((Table)i))
continue;
heap.Tables[i].Length = ReadUInt32();
}
SetIndexSize(image.StringHeap, sizes, 0x1);
SetIndexSize(image.GuidHeap, sizes, 0x2);
SetIndexSize(image.BlobHeap, sizes, 0x4);
ComputeTableInformations();
}
private static void SetIndexSize(Heap heap, uint sizes, byte flag)
{
if (heap == null)
return;
heap.IndexSize = (sizes & flag) > 0 ? 4 : 2;
}
private int GetTableIndexSize(Table table)
{
return image.GetTableIndexSize(table);
}
private int GetCodedIndexSize(CodedIndex index)
{
return image.GetCodedIndexSize(index);
}
private void ComputeTableInformations()
{
uint offset = (uint)BaseStream.Position - table_heap_offset - image.MetadataSection.PointerToRawData; // header
int stridx_size = image.StringHeap != null ? image.StringHeap.IndexSize : 2;
int guididx_size = image.GuidHeap != null ? image.GuidHeap.IndexSize : 2;
int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2;
TableHeap heap = image.TableHeap;
TableInformation[] tables = heap.Tables;
for (int i = 0; i < Mixin.TableCount; i++)
{
Table table = (Table)i;
if (!heap.HasTable(table))
continue;
int size;
switch (table)
{
case Table.Module:
size = 2 // Generation
+ stridx_size // Name
+ guididx_size * 3; // Mvid, EncId, EncBaseId
break;
case Table.TypeRef:
size = GetCodedIndexSize(CodedIndex.ResolutionScope) // ResolutionScope
+ stridx_size * 2; // Name, Namespace
break;
case Table.TypeDef:
size = 4 // Flags
+ stridx_size * 2 // Name, Namespace
+ GetCodedIndexSize(CodedIndex.TypeDefOrRef) // BaseType
+ GetTableIndexSize(Table.Field) // FieldList
+ GetTableIndexSize(Table.Method); // MethodList
break;
case Table.FieldPtr:
size = GetTableIndexSize(Table.Field); // Field
break;
case Table.Field:
size = 2 // Flags
+ stridx_size // Name
+ blobidx_size; // Signature
break;
case Table.MethodPtr:
size = GetTableIndexSize(Table.Method); // Method
break;
case Table.Method:
size = 8 // Rva 4, ImplFlags 2, Flags 2
+ stridx_size // Name
+ blobidx_size // Signature
+ GetTableIndexSize(Table.Param); // ParamList
break;
case Table.ParamPtr:
size = GetTableIndexSize(Table.Param); // Param
break;
case Table.Param:
size = 4 // Flags 2, Sequence 2
+ stridx_size; // Name
break;
case Table.InterfaceImpl:
size = GetTableIndexSize(Table.TypeDef) // Class
+ GetCodedIndexSize(CodedIndex.TypeDefOrRef); // Interface
break;
case Table.MemberRef:
size = GetCodedIndexSize(CodedIndex.MemberRefParent) // Class
+ stridx_size // Name
+ blobidx_size; // Signature
break;
case Table.Constant:
size = 2 // Type
+ GetCodedIndexSize(CodedIndex.HasConstant) // Parent
+ blobidx_size; // Value
break;
case Table.CustomAttribute:
size = GetCodedIndexSize(CodedIndex.HasCustomAttribute) // Parent
+ GetCodedIndexSize(CodedIndex.CustomAttributeType) // Type
+ blobidx_size; // Value
break;
case Table.FieldMarshal:
size = GetCodedIndexSize(CodedIndex.HasFieldMarshal) // Parent
+ blobidx_size; // NativeType
break;
case Table.DeclSecurity:
size = 2 // Action
+ GetCodedIndexSize(CodedIndex.HasDeclSecurity) // Parent
+ blobidx_size; // PermissionSet
break;
case Table.ClassLayout:
size = 6 // PackingSize 2, ClassSize 4
+ GetTableIndexSize(Table.TypeDef); // Parent
break;
case Table.FieldLayout:
size = 4 // Offset
+ GetTableIndexSize(Table.Field); // Field
break;
case Table.StandAloneSig:
size = blobidx_size; // Signature
break;
case Table.EventMap:
size = GetTableIndexSize(Table.TypeDef) // Parent
+ GetTableIndexSize(Table.Event); // EventList
break;
case Table.EventPtr:
size = GetTableIndexSize(Table.Event); // Event
break;
case Table.Event:
size = 2 // Flags
+ stridx_size // Name
+ GetCodedIndexSize(CodedIndex.TypeDefOrRef); // EventType
break;
case Table.PropertyMap:
size = GetTableIndexSize(Table.TypeDef) // Parent
+ GetTableIndexSize(Table.Property); // PropertyList
break;
case Table.PropertyPtr:
size = GetTableIndexSize(Table.Property); // Property
break;
case Table.Property:
size = 2 // Flags
+ stridx_size // Name
+ blobidx_size; // Type
break;
case Table.MethodSemantics:
size = 2 // Semantics
+ GetTableIndexSize(Table.Method) // Method
+ GetCodedIndexSize(CodedIndex.HasSemantics); // Association
break;
case Table.MethodImpl:
size = GetTableIndexSize(Table.TypeDef) // Class
+ GetCodedIndexSize(CodedIndex.MethodDefOrRef) // MethodBody
+ GetCodedIndexSize(CodedIndex.MethodDefOrRef); // MethodDeclaration
break;
case Table.ModuleRef:
size = stridx_size; // Name
break;
case Table.TypeSpec:
size = blobidx_size; // Signature
break;
case Table.ImplMap:
size = 2 // MappingFlags
+ GetCodedIndexSize(CodedIndex.MemberForwarded) // MemberForwarded
+ stridx_size // ImportName
+ GetTableIndexSize(Table.ModuleRef); // ImportScope
break;
case Table.FieldRVA:
size = 4 // RVA
+ GetTableIndexSize(Table.Field); // Field
break;
case Table.EncLog:
size = 8;
break;
case Table.EncMap:
size = 4;
break;
case Table.Assembly:
size = 16 // HashAlgId 4, Version 4 * 2, Flags 4
+ blobidx_size // PublicKey
+ stridx_size * 2; // Name, Culture
break;
case Table.AssemblyProcessor:
size = 4; // Processor
break;
case Table.AssemblyOS:
size = 12; // Platform 4, Version 2 * 4
break;
case Table.AssemblyRef:
size = 12 // Version 2 * 4 + Flags 4
+ blobidx_size * 2 // PublicKeyOrToken, HashValue
+ stridx_size * 2; // Name, Culture
break;
case Table.AssemblyRefProcessor:
size = 4 // Processor
+ GetTableIndexSize(Table.AssemblyRef); // AssemblyRef
break;
case Table.AssemblyRefOS:
size = 12 // Platform 4, Version 2 * 4
+ GetTableIndexSize(Table.AssemblyRef); // AssemblyRef
break;
case Table.File:
size = 4 // Flags
+ stridx_size // Name
+ blobidx_size; // HashValue
break;
case Table.ExportedType:
size = 8 // Flags 4, TypeDefId 4
+ stridx_size * 2 // Name, Namespace
+ GetCodedIndexSize(CodedIndex.Implementation); // Implementation
break;
case Table.ManifestResource:
size = 8 // Offset, Flags
+ stridx_size // Name
+ GetCodedIndexSize(CodedIndex.Implementation); // Implementation
break;
case Table.NestedClass:
size = GetTableIndexSize(Table.TypeDef) // NestedClass
+ GetTableIndexSize(Table.TypeDef); // EnclosingClass
break;
case Table.GenericParam:
size = 4 // Number, Flags
+ GetCodedIndexSize(CodedIndex.TypeOrMethodDef) // Owner
+ stridx_size; // Name
break;
case Table.MethodSpec:
size = GetCodedIndexSize(CodedIndex.MethodDefOrRef) // Method
+ blobidx_size; // Instantiation
break;
case Table.GenericParamConstraint:
size = GetTableIndexSize(Table.GenericParam) // Owner
+ GetCodedIndexSize(CodedIndex.TypeDefOrRef); // Constraint
break;
case Table.Document:
size = blobidx_size // Name
+ guididx_size // HashAlgorithm
+ blobidx_size // Hash
+ guididx_size; // Language
break;
case Table.MethodDebugInformation:
size = GetTableIndexSize(Table.Document) // Document
+ blobidx_size; // SequencePoints
break;
case Table.LocalScope:
size = GetTableIndexSize(Table.Method) // Method
+ GetTableIndexSize(Table.ImportScope) // ImportScope
+ GetTableIndexSize(Table.LocalVariable) // VariableList
+ GetTableIndexSize(Table.LocalConstant) // ConstantList
+ 4 * 2; // StartOffset, Length
break;
case Table.LocalVariable:
size = 2 // Attributes
+ 2 // Index
+ stridx_size; // Name
break;
case Table.LocalConstant:
size = stridx_size // Name
+ blobidx_size; // Signature
break;
case Table.ImportScope:
size = GetTableIndexSize(Table.ImportScope) // Parent
+ blobidx_size;
break;
case Table.StateMachineMethod:
size = GetTableIndexSize(Table.Method) // MoveNextMethod
+ GetTableIndexSize(Table.Method); // KickOffMethod
break;
case Table.CustomDebugInformation:
size = GetCodedIndexSize(CodedIndex.HasCustomDebugInformation) // Parent
+ guididx_size // Kind
+ blobidx_size; // Value
break;
default:
throw new NotSupportedException();
}
tables[i].RowSize = (uint)size;
tables[i].Offset = offset;
offset += (uint)size * tables[i].Length;
}
}
private void ReadPdbHeap()
{
PdbHeap heap = image.PdbHeap;
ByteBuffer buffer = new(heap.data);
heap.Id = buffer.ReadBytes(20);
heap.EntryPoint = buffer.ReadUInt32();
heap.TypeSystemTables = buffer.ReadInt64();
heap.TypeSystemTableRows = new uint [Mixin.TableCount];
for (int i = 0; i < Mixin.TableCount; i++)
{
Table table = (Table)i;
if (!heap.HasTable(table))
continue;
heap.TypeSystemTableRows[i] = buffer.ReadUInt32();
}
}
public static Image ReadImage(Disposable<Stream> stream, string file_name)
{
try
{
ImageReader reader = new(stream, file_name);
reader.ReadImage();
return reader.image;
}
catch (EndOfStreamException e)
{
throw new BadImageFormatException(stream.value.GetFileName(), e);
}
}
public static Image ReadPortablePdb(Disposable<Stream> stream, string file_name)
{
try
{
ImageReader reader = new(stream, file_name);
uint length = (uint)stream.value.Length;
reader.image.Sections = new[]
{
new Section
{
PointerToRawData = 0,
SizeOfRawData = length,
VirtualAddress = 0,
VirtualSize = length
}
};
reader.metadata = new(0, length);
reader.ReadMetadata();
return reader.image;
}
catch (EndOfStreamException e)
{
throw new BadImageFormatException(stream.value.GetFileName(), e);
}
}
}
}