5 // Jb Evain (jbevain@gmail.com)
7 // (C) 2005 - 2007 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.
29 namespace Mono
.Cecil
.Binary
{
35 using Mono
.Cecil
.Metadata
;
37 sealed class ImageReader
: BaseImageVisitor
{
39 MetadataReader m_mdReader
;
40 BinaryReader m_binaryReader
;
43 public MetadataReader MetadataReader
{
44 get { return m_mdReader; }
48 get { return m_image; }
51 ImageReader (Image img
, BinaryReader reader
)
54 m_binaryReader
= reader
;
57 static ImageReader
Read (Image img
, Stream stream
)
59 ImageReader reader
= new ImageReader (img
, new BinaryReader (stream
));
64 public static ImageReader
Read (string file
)
67 throw new ArgumentNullException ("file");
69 FileInfo fi
= new FileInfo (file
);
70 if (!File
.Exists (fi
.FullName
))
72 throw new FileNotFoundException (fi
.FullName
);
74 throw new FileNotFoundException (string.Format ("File '{0}' not found.", fi
.FullName
), fi
.FullName
);
77 FileStream stream
= null;
79 stream
= new FileStream (fi
.FullName
, FileMode
.Open
, FileAccess
.Read
, FileShare
.Read
);
80 return Read (new Image (fi
), stream
);
81 } catch (Exception e
) {
85 throw new BadImageFormatException ("Invalid PE file: " + file
, e
);
87 throw new BadImageFormatException ("Invalid PE file", file
, e
);
92 public static ImageReader
Read (byte [] image
)
95 throw new ArgumentNullException ("image");
97 if (image
.Length
== 0)
98 throw new ArgumentException ("Empty image array");
100 return Read (new Image (), new MemoryStream (image
));
103 public static ImageReader
Read (Stream stream
)
106 throw new ArgumentNullException ("stream");
109 throw new ArgumentException ("Can not read from stream");
111 return Read (new Image (), stream
);
114 public BinaryReader
GetReader ()
116 return m_binaryReader
;
119 public override void VisitImage (Image img
)
121 m_mdReader
= new MetadataReader (this);
124 void SetPositionToAddress (RVA address
)
126 m_binaryReader
.BaseStream
.Position
= m_image
.ResolveVirtualAddress (address
);
129 public override void VisitDOSHeader (DOSHeader header
)
131 header
.Start
= m_binaryReader
.ReadBytes (60);
132 header
.Lfanew
= m_binaryReader
.ReadUInt32 ();
133 header
.End
= m_binaryReader
.ReadBytes (64);
135 m_binaryReader
.BaseStream
.Position
= header
.Lfanew
;
137 if (m_binaryReader
.ReadUInt16 () != 0x4550 ||
138 m_binaryReader
.ReadUInt16 () != 0)
140 throw new ImageFormatException ("Invalid PE File Signature");
143 public override void VisitPEFileHeader (PEFileHeader header
)
145 header
.Machine
= m_binaryReader
.ReadUInt16 ();
146 header
.NumberOfSections
= m_binaryReader
.ReadUInt16 ();
147 header
.TimeDateStamp
= m_binaryReader
.ReadUInt32 ();
148 header
.PointerToSymbolTable
= m_binaryReader
.ReadUInt32 ();
149 header
.NumberOfSymbols
= m_binaryReader
.ReadUInt32 ();
150 header
.OptionalHeaderSize
= m_binaryReader
.ReadUInt16 ();
151 header
.Characteristics
= (ImageCharacteristics
) m_binaryReader
.ReadUInt16 ();
154 ulong ReadIntOrLong ()
156 return m_image
.PEOptionalHeader
.StandardFields
.IsPE64
?
157 m_binaryReader
.ReadUInt64 () :
158 m_binaryReader
.ReadUInt32 ();
163 return m_binaryReader
.ReadUInt32 ();
166 DataDirectory
ReadDataDirectory ()
168 return new DataDirectory (ReadRVA (), m_binaryReader
.ReadUInt32 ());
171 public override void VisitNTSpecificFieldsHeader (PEOptionalHeader
.NTSpecificFieldsHeader header
)
173 header
.ImageBase
= ReadIntOrLong ();
174 header
.SectionAlignment
= m_binaryReader
.ReadUInt32 ();
175 header
.FileAlignment
= m_binaryReader
.ReadUInt32 ();
176 header
.OSMajor
= m_binaryReader
.ReadUInt16 ();
177 header
.OSMinor
= m_binaryReader
.ReadUInt16 ();
178 header
.UserMajor
= m_binaryReader
.ReadUInt16 ();
179 header
.UserMinor
= m_binaryReader
.ReadUInt16 ();
180 header
.SubSysMajor
= m_binaryReader
.ReadUInt16 ();
181 header
.SubSysMinor
= m_binaryReader
.ReadUInt16 ();
182 header
.Reserved
= m_binaryReader
.ReadUInt32 ();
183 header
.ImageSize
= m_binaryReader
.ReadUInt32 ();
184 header
.HeaderSize
= m_binaryReader
.ReadUInt32 ();
185 header
.FileChecksum
= m_binaryReader
.ReadUInt32 ();
186 header
.SubSystem
= (SubSystem
) m_binaryReader
.ReadUInt16 ();
187 header
.DLLFlags
= m_binaryReader
.ReadUInt16 ();
188 header
.StackReserveSize
= ReadIntOrLong ();
189 header
.StackCommitSize
= ReadIntOrLong ();
190 header
.HeapReserveSize
= ReadIntOrLong ();
191 header
.HeapCommitSize
= ReadIntOrLong ();
192 header
.LoaderFlags
= m_binaryReader
.ReadUInt32 ();
193 header
.NumberOfDataDir
= m_binaryReader
.ReadUInt32 ();
196 public override void VisitStandardFieldsHeader (PEOptionalHeader
.StandardFieldsHeader header
)
198 header
.Magic
= m_binaryReader
.ReadUInt16 ();
199 header
.LMajor
= m_binaryReader
.ReadByte ();
200 header
.LMinor
= m_binaryReader
.ReadByte ();
201 header
.CodeSize
= m_binaryReader
.ReadUInt32 ();
202 header
.InitializedDataSize
= m_binaryReader
.ReadUInt32 ();
203 header
.UninitializedDataSize
= m_binaryReader
.ReadUInt32 ();
204 header
.EntryPointRVA
= ReadRVA ();
205 header
.BaseOfCode
= ReadRVA ();
207 header
.BaseOfData
= ReadRVA ();
210 public override void VisitDataDirectoriesHeader (PEOptionalHeader
.DataDirectoriesHeader header
)
212 header
.ExportTable
= ReadDataDirectory ();
213 header
.ImportTable
= ReadDataDirectory ();
214 header
.ResourceTable
= ReadDataDirectory ();
215 header
.ExceptionTable
= ReadDataDirectory ();
216 header
.CertificateTable
= ReadDataDirectory ();
217 header
.BaseRelocationTable
= ReadDataDirectory ();
218 header
.Debug
= ReadDataDirectory ();
219 header
.Copyright
= ReadDataDirectory ();
220 header
.GlobalPtr
= ReadDataDirectory ();
221 header
.TLSTable
= ReadDataDirectory ();
222 header
.LoadConfigTable
= ReadDataDirectory ();
223 header
.BoundImport
= ReadDataDirectory ();
224 header
.IAT
= ReadDataDirectory ();
225 header
.DelayImportDescriptor
= ReadDataDirectory ();
226 header
.CLIHeader
= ReadDataDirectory ();
227 header
.Reserved
= ReadDataDirectory ();
229 if (header
.CLIHeader
!= DataDirectory
.Zero
)
230 m_image
.CLIHeader
= new CLIHeader ();
231 if (header
.ExportTable
!= DataDirectory
.Zero
)
232 m_image
.ExportTable
= new ExportTable ();
235 public override void VisitSectionCollection (SectionCollection coll
)
237 for (int i
= 0; i
< m_image
.PEFileHeader
.NumberOfSections
; i
++)
238 coll
.Add (new Section ());
241 public override void VisitSection (Section sect
)
243 char [] buffer
= new char [8];
246 char cur
= (char) m_binaryReader
.ReadSByte ();
248 m_binaryReader
.BaseStream
.Position
+= 8 - read
- 1;
251 buffer
[read
++] = cur
;
253 sect
.Name
= read
== 0 ? string.Empty
: new string (buffer
, 0, read
);
254 if (sect
.Name
== Section
.Text
)
255 m_image
.TextSection
= sect
;
257 sect
.VirtualSize
= m_binaryReader
.ReadUInt32 ();
258 sect
.VirtualAddress
= ReadRVA ();
259 sect
.SizeOfRawData
= m_binaryReader
.ReadUInt32 ();
260 sect
.PointerToRawData
= ReadRVA ();
261 sect
.PointerToRelocations
= ReadRVA ();
262 sect
.PointerToLineNumbers
= ReadRVA ();
263 sect
.NumberOfRelocations
= m_binaryReader
.ReadUInt16 ();
264 sect
.NumberOfLineNumbers
= m_binaryReader
.ReadUInt16 ();
265 sect
.Characteristics
= (SectionCharacteristics
) m_binaryReader
.ReadUInt32 ();
267 long pos
= m_binaryReader
.BaseStream
.Position
;
268 m_binaryReader
.BaseStream
.Position
= sect
.PointerToRawData
;
269 sect
.Data
= m_binaryReader
.ReadBytes ((int) sect
.SizeOfRawData
);
270 m_binaryReader
.BaseStream
.Position
= pos
;
273 public override void VisitImportAddressTable (ImportAddressTable iat
)
275 if (m_image
.PEOptionalHeader
.DataDirectories
.IAT
.VirtualAddress
== RVA
.Zero
)
278 SetPositionToAddress (m_image
.PEOptionalHeader
.DataDirectories
.IAT
.VirtualAddress
);
280 iat
.HintNameTableRVA
= ReadRVA ();
283 public override void VisitCLIHeader (CLIHeader header
)
285 if (m_image
.PEOptionalHeader
.DataDirectories
.Debug
!= DataDirectory
.Zero
) {
286 m_image
.DebugHeader
= new DebugHeader ();
287 VisitDebugHeader (m_image
.DebugHeader
);
290 SetPositionToAddress (m_image
.PEOptionalHeader
.DataDirectories
.CLIHeader
.VirtualAddress
);
291 header
.Cb
= m_binaryReader
.ReadUInt32 ();
292 header
.MajorRuntimeVersion
= m_binaryReader
.ReadUInt16 ();
293 header
.MinorRuntimeVersion
= m_binaryReader
.ReadUInt16 ();
294 header
.Metadata
= ReadDataDirectory ();
295 header
.Flags
= (RuntimeImage
) m_binaryReader
.ReadUInt32 ();
296 header
.EntryPointToken
= m_binaryReader
.ReadUInt32 ();
297 header
.Resources
= ReadDataDirectory ();
298 header
.StrongNameSignature
= ReadDataDirectory ();
299 header
.CodeManagerTable
= ReadDataDirectory ();
300 header
.VTableFixups
= ReadDataDirectory ();
301 header
.ExportAddressTableJumps
= ReadDataDirectory ();
302 header
.ManagedNativeHeader
= ReadDataDirectory ();
304 if (header
.StrongNameSignature
!= DataDirectory
.Zero
) {
305 SetPositionToAddress (header
.StrongNameSignature
.VirtualAddress
);
306 header
.ImageHash
= m_binaryReader
.ReadBytes ((int) header
.StrongNameSignature
.Size
);
308 header
.ImageHash
= new byte [0];
310 SetPositionToAddress (m_image
.CLIHeader
.Metadata
.VirtualAddress
);
311 m_image
.MetadataRoot
.Accept (m_mdReader
);
314 public override void VisitDebugHeader (DebugHeader header
)
316 if (m_image
.PEOptionalHeader
.DataDirectories
.Debug
== DataDirectory
.Zero
)
319 long pos
= m_binaryReader
.BaseStream
.Position
;
321 SetPositionToAddress (m_image
.PEOptionalHeader
.DataDirectories
.Debug
.VirtualAddress
);
322 header
.Characteristics
= m_binaryReader
.ReadUInt32 ();
323 header
.TimeDateStamp
= m_binaryReader
.ReadUInt32 ();
324 header
.MajorVersion
= m_binaryReader
.ReadUInt16 ();
325 header
.MinorVersion
= m_binaryReader
.ReadUInt16 ();
326 header
.Type
= (DebugStoreType
) m_binaryReader
.ReadUInt32 ();
327 header
.SizeOfData
= m_binaryReader
.ReadUInt32 ();
328 header
.AddressOfRawData
= ReadRVA ();
329 header
.PointerToRawData
= m_binaryReader
.ReadUInt32 ();
331 m_binaryReader
.BaseStream
.Position
= header
.PointerToRawData
;
333 header
.Magic
= m_binaryReader
.ReadUInt32 ();
334 header
.Signature
= new Guid (m_binaryReader
.ReadBytes (16));
335 header
.Age
= m_binaryReader
.ReadUInt32 ();
336 header
.FileName
= ReadZeroTerminatedString ();
338 m_binaryReader
.BaseStream
.Position
= pos
;
341 string ReadZeroTerminatedString ()
343 StringBuilder sb
= new StringBuilder ();
345 byte chr
= m_binaryReader
.ReadByte ();
348 sb
.Append ((char) chr
);
350 return sb
.ToString ();
353 public override void VisitImportTable (ImportTable it
)
355 if (m_image
.PEOptionalHeader
.DataDirectories
.ImportTable
.VirtualAddress
== RVA
.Zero
)
358 SetPositionToAddress (m_image
.PEOptionalHeader
.DataDirectories
.ImportTable
.VirtualAddress
);
360 it
.ImportLookupTable
= ReadRVA ();
361 it
.DateTimeStamp
= m_binaryReader
.ReadUInt32 ();
362 it
.ForwardChain
= m_binaryReader
.ReadUInt32 ();
363 it
.Name
= ReadRVA ();
364 it
.ImportAddressTable
= ReadRVA ();
367 public override void VisitImportLookupTable (ImportLookupTable ilt
)
369 if (m_image
.ImportTable
.ImportLookupTable
== RVA
.Zero
)
372 SetPositionToAddress (m_image
.ImportTable
.ImportLookupTable
);
374 ilt
.HintNameRVA
= ReadRVA ();
377 public override void VisitHintNameTable (HintNameTable hnt
)
379 if (m_image
.ImportAddressTable
.HintNameTableRVA
== RVA
.Zero
)
382 if ((m_image
.ImportAddressTable
.HintNameTableRVA
& 0x80000000) != 0)
385 SetPositionToAddress (m_image
.ImportAddressTable
.HintNameTableRVA
);
387 hnt
.Hint
= m_binaryReader
.ReadUInt16 ();
389 byte [] bytes
= m_binaryReader
.ReadBytes (11);
390 hnt
.RuntimeMain
= Encoding
.ASCII
.GetString (bytes
, 0, bytes
.Length
);
392 SetPositionToAddress (m_image
.ImportTable
.Name
);
394 bytes
= m_binaryReader
.ReadBytes (11);
395 hnt
.RuntimeLibrary
= Encoding
.ASCII
.GetString (bytes
, 0, bytes
.Length
);
397 SetPositionToAddress (m_image
.PEOptionalHeader
.StandardFields
.EntryPointRVA
);
398 hnt
.EntryPoint
= m_binaryReader
.ReadUInt16 ();
399 hnt
.RVA
= ReadRVA ();
402 public override void VisitExportTable (ExportTable et
)
404 SetPositionToAddress (m_image
.PEOptionalHeader
.DataDirectories
.ExportTable
.VirtualAddress
);
406 et
.Characteristics
= m_binaryReader
.ReadUInt32 ();
407 et
.TimeDateStamp
= m_binaryReader
.ReadUInt32 ();
408 et
.MajorVersion
= m_binaryReader
.ReadUInt16 ();
409 et
.MinorVersion
= m_binaryReader
.ReadUInt16 ();
412 m_binaryReader
.ReadUInt32 ();
414 et
.Base
= m_binaryReader
.ReadUInt32 ();
415 et
.NumberOfFunctions
= m_binaryReader
.ReadUInt32 ();
416 et
.NumberOfNames
= m_binaryReader
.ReadUInt32 ();
417 et
.AddressOfFunctions
= m_binaryReader
.ReadUInt32 ();
418 et
.AddressOfNames
= m_binaryReader
.ReadUInt32 ();
419 et
.AddressOfNameOrdinals
= m_binaryReader
.ReadUInt32 ();
421 et
.AddressesOfFunctions
= ReadArrayOfRVA (et
.AddressOfFunctions
, et
.NumberOfFunctions
);
422 et
.AddressesOfNames
= ReadArrayOfRVA (et
.AddressOfNames
, et
.NumberOfNames
);
423 et
.NameOrdinals
= ReadArrayOfUInt16 (et
.AddressOfNameOrdinals
, et
.NumberOfNames
);
424 et
.Names
= new string [et
.NumberOfFunctions
];
426 for (int i
= 0; i
< et
.NumberOfFunctions
; i
++) {
427 if (et
.AddressesOfFunctions
[i
] == 0)
430 et
.Names
[i
] = ReadFunctionName (et
, i
);
434 string ReadFunctionName (ExportTable et
, int index
)
436 for (int i
= 0; i
< et
.NumberOfNames
; i
++) {
437 if (et
.NameOrdinals
[i
] != index
)
440 SetPositionToAddress (et
.AddressesOfNames
[i
]);
441 return ReadZeroTerminatedString ();
447 ushort [] ReadArrayOfUInt16 (RVA position
, uint length
)
449 if (position
== RVA
.Zero
)
450 return new ushort [0];
452 SetPositionToAddress (position
);
453 ushort [] array
= new ushort [length
];
454 for (int i
= 0; i
< length
; i
++)
455 array
[i
] = m_binaryReader
.ReadUInt16 ();
460 RVA
[] ReadArrayOfRVA (RVA position
, uint length
)
462 if (position
== RVA
.Zero
)
465 SetPositionToAddress (position
);
466 RVA
[] addresses
= new RVA
[length
];
467 for (int i
= 0; i
< length
; i
++)
468 addresses
[i
] = m_binaryReader
.ReadUInt32 ();
473 public override void TerminateImage(Image img
)
475 m_binaryReader
.Close ();
478 ResourceReader resReader
= new ResourceReader (img
);
479 img
.ResourceDirectoryRoot
= resReader
.Read ();
481 img
.ResourceDirectoryRoot
= null;