ImageReader.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. //
  2. // ImageReader.cs
  3. //
  4. // Author:
  5. // Jb Evain (jbevain@gmail.com)
  6. //
  7. // Copyright (c) 2008 - 2011 Jb Evain
  8. //
  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:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  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.
  27. //
  28. using System;
  29. using System.IO;
  30. using Mono.Cecil.Metadata;
  31. using RVA = System.UInt32;
  32. namespace Mono.Cecil.PE
  33. {
  34. sealed class ImageReader : BinaryStreamReader
  35. {
  36. readonly Image image;
  37. DataDirectory cli;
  38. DataDirectory metadata;
  39. public ImageReader(Stream stream)
  40. : base(stream)
  41. {
  42. image = new Image();
  43. image.FileName = Mixin.GetFullyQualifiedName(stream);
  44. }
  45. void MoveTo(DataDirectory directory)
  46. {
  47. BaseStream.Position = image.ResolveVirtualAddress(directory.VirtualAddress);
  48. }
  49. void MoveTo(uint position)
  50. {
  51. BaseStream.Position = position;
  52. }
  53. void ReadImage()
  54. {
  55. if (BaseStream.Length < 128)
  56. throw new BadImageFormatException();
  57. // - DOSHeader
  58. // PE 2
  59. // Start 58
  60. // Lfanew 4
  61. // End 64
  62. if (ReadUInt16() != 0x5a4d)
  63. throw new BadImageFormatException();
  64. Advance(58);
  65. MoveTo(ReadUInt32());
  66. if (ReadUInt32() != 0x00004550)
  67. throw new BadImageFormatException();
  68. // - PEFileHeader
  69. // Machine 2
  70. image.Architecture = ReadArchitecture();
  71. // NumberOfSections 2
  72. ushort sections = ReadUInt16();
  73. // TimeDateStamp 4
  74. // PointerToSymbolTable 4
  75. // NumberOfSymbols 4
  76. // OptionalHeaderSize 2
  77. Advance(14);
  78. // Characteristics 2
  79. ushort characteristics = ReadUInt16();
  80. ushort subsystem, dll_characteristics;
  81. ReadOptionalHeaders(out subsystem, out dll_characteristics);
  82. ReadSections(sections);
  83. ReadCLIHeader();
  84. ReadMetadata();
  85. image.Kind = GetModuleKind(characteristics, subsystem);
  86. image.Characteristics = (ModuleCharacteristics)dll_characteristics;
  87. }
  88. TargetArchitecture ReadArchitecture()
  89. {
  90. var machine = ReadUInt16();
  91. switch (machine)
  92. {
  93. case 0x014c:
  94. return TargetArchitecture.I386;
  95. case 0x8664:
  96. return TargetArchitecture.AMD64;
  97. case 0x0200:
  98. return TargetArchitecture.IA64;
  99. case 0x01c4:
  100. return TargetArchitecture.ARMv7;
  101. }
  102. throw new NotSupportedException();
  103. }
  104. static ModuleKind GetModuleKind(ushort characteristics, ushort subsystem)
  105. {
  106. if ((characteristics & 0x2000) != 0) // ImageCharacteristics.Dll
  107. return ModuleKind.Dll;
  108. if (subsystem == 0x2 || subsystem == 0x9) // SubSystem.WindowsGui || SubSystem.WindowsCeGui
  109. return ModuleKind.Windows;
  110. return ModuleKind.Console;
  111. }
  112. void ReadOptionalHeaders(out ushort subsystem, out ushort dll_characteristics)
  113. {
  114. // - PEOptionalHeader
  115. // - StandardFieldsHeader
  116. // Magic 2
  117. bool pe64 = ReadUInt16() == 0x20b;
  118. // pe32 || pe64
  119. // LMajor 1
  120. // LMinor 1
  121. // CodeSize 4
  122. // InitializedDataSize 4
  123. // UninitializedDataSize4
  124. // EntryPointRVA 4
  125. // BaseOfCode 4
  126. // BaseOfData 4 || 0
  127. // - NTSpecificFieldsHeader
  128. // ImageBase 4 || 8
  129. // SectionAlignment 4
  130. // FileAlignement 4
  131. // OSMajor 2
  132. // OSMinor 2
  133. // UserMajor 2
  134. // UserMinor 2
  135. // SubSysMajor 2
  136. // SubSysMinor 2
  137. // Reserved 4
  138. // ImageSize 4
  139. // HeaderSize 4
  140. // FileChecksum 4
  141. Advance(66);
  142. // SubSystem 2
  143. subsystem = ReadUInt16();
  144. // DLLFlags 2
  145. dll_characteristics = ReadUInt16();
  146. // StackReserveSize 4 || 8
  147. // StackCommitSize 4 || 8
  148. // HeapReserveSize 4 || 8
  149. // HeapCommitSize 4 || 8
  150. // LoaderFlags 4
  151. // NumberOfDataDir 4
  152. // - DataDirectoriesHeader
  153. // ExportTable 8
  154. // ImportTable 8
  155. // ResourceTable 8
  156. // ExceptionTable 8
  157. // CertificateTable 8
  158. // BaseRelocationTable 8
  159. Advance(pe64 ? 88 : 72);
  160. // Debug 8
  161. image.Debug = ReadDataDirectory();
  162. // Copyright 8
  163. // GlobalPtr 8
  164. // TLSTable 8
  165. // LoadConfigTable 8
  166. // BoundImport 8
  167. // IAT 8
  168. // DelayImportDescriptor8
  169. Advance(56);
  170. // CLIHeader 8
  171. cli = ReadDataDirectory();
  172. if (cli.IsZero)
  173. throw new BadImageFormatException();
  174. // Reserved 8
  175. Advance(8);
  176. }
  177. string ReadAlignedString(int length)
  178. {
  179. int read = 0;
  180. var buffer = new char[length];
  181. while (read < length)
  182. {
  183. var current = ReadByte();
  184. if (current == 0)
  185. break;
  186. buffer[read++] = (char)current;
  187. }
  188. Advance(-1 + ((read + 4) & ~3) - read);
  189. return new string(buffer, 0, read);
  190. }
  191. string ReadZeroTerminatedString(int length)
  192. {
  193. int read = 0;
  194. var buffer = new char[length];
  195. var bytes = ReadBytes(length);
  196. while (read < length)
  197. {
  198. var current = bytes[read];
  199. if (current == 0)
  200. break;
  201. buffer[read++] = (char)current;
  202. }
  203. return new string(buffer, 0, read);
  204. }
  205. void ReadSections(ushort count)
  206. {
  207. var sections = new Section[count];
  208. for (int i = 0; i < count; i++)
  209. {
  210. var section = new Section();
  211. // Name
  212. section.Name = ReadZeroTerminatedString(8);
  213. // VirtualSize 4
  214. Advance(4);
  215. // VirtualAddress 4
  216. section.VirtualAddress = ReadUInt32();
  217. // SizeOfRawData 4
  218. section.SizeOfRawData = ReadUInt32();
  219. // PointerToRawData 4
  220. section.PointerToRawData = ReadUInt32();
  221. // PointerToRelocations 4
  222. // PointerToLineNumbers 4
  223. // NumberOfRelocations 2
  224. // NumberOfLineNumbers 2
  225. // Characteristics 4
  226. Advance(16);
  227. sections[i] = section;
  228. ReadSectionData(section);
  229. }
  230. image.Sections = sections;
  231. }
  232. void ReadSectionData(Section section)
  233. {
  234. var position = BaseStream.Position;
  235. MoveTo(section.PointerToRawData);
  236. var length = (int)section.SizeOfRawData;
  237. var data = new byte[length];
  238. int offset = 0, read;
  239. while ((read = Read(data, offset, length - offset)) > 0)
  240. offset += read;
  241. section.Data = data;
  242. BaseStream.Position = position;
  243. }
  244. void ReadCLIHeader()
  245. {
  246. MoveTo(cli);
  247. // - CLIHeader
  248. // Cb 4
  249. // MajorRuntimeVersion 2
  250. // MinorRuntimeVersion 2
  251. Advance(8);
  252. // Metadata 8
  253. metadata = ReadDataDirectory();
  254. // Flags 4
  255. image.Attributes = (ModuleAttributes)ReadUInt32();
  256. // EntryPointToken 4
  257. image.EntryPointToken = ReadUInt32();
  258. // Resources 8
  259. image.Resources = ReadDataDirectory();
  260. // StrongNameSignature 8
  261. image.StrongName = ReadDataDirectory();
  262. // CodeManagerTable 8
  263. // VTableFixups 8
  264. // ExportAddressTableJumps 8
  265. // ManagedNativeHeader 8
  266. }
  267. void ReadMetadata()
  268. {
  269. MoveTo(metadata);
  270. if (ReadUInt32() != 0x424a5342)
  271. throw new BadImageFormatException();
  272. // MajorVersion 2
  273. // MinorVersion 2
  274. // Reserved 4
  275. Advance(8);
  276. var version = ReadZeroTerminatedString(ReadInt32());
  277. image.Runtime = Mixin.ParseRuntime(version);
  278. // Flags 2
  279. Advance(2);
  280. var streams = ReadUInt16();
  281. var section = image.GetSectionAtVirtualAddress(metadata.VirtualAddress);
  282. if (section == null)
  283. throw new BadImageFormatException();
  284. image.MetadataSection = section;
  285. for (int i = 0; i < streams; i++)
  286. ReadMetadataStream(section);
  287. if (image.TableHeap != null)
  288. ReadTableHeap();
  289. }
  290. void ReadMetadataStream(Section section)
  291. {
  292. // Offset 4
  293. uint start = metadata.VirtualAddress - section.VirtualAddress + ReadUInt32(); // relative to the section start
  294. // Size 4
  295. uint size = ReadUInt32();
  296. var name = ReadAlignedString(16);
  297. switch (name)
  298. {
  299. case "#~":
  300. case "#-":
  301. image.TableHeap = new TableHeap(section, start, size);
  302. break;
  303. case "#Strings":
  304. image.StringHeap = new StringHeap(section, start, size);
  305. break;
  306. case "#Blob":
  307. image.BlobHeap = new BlobHeap(section, start, size);
  308. break;
  309. case "#GUID":
  310. image.GuidHeap = new GuidHeap(section, start, size);
  311. break;
  312. case "#US":
  313. image.UserStringHeap = new UserStringHeap(section, start, size);
  314. break;
  315. }
  316. }
  317. void ReadTableHeap()
  318. {
  319. var heap = image.TableHeap;
  320. uint start = heap.Section.PointerToRawData;
  321. MoveTo(heap.Offset + start);
  322. // Reserved 4
  323. // MajorVersion 1
  324. // MinorVersion 1
  325. Advance(6);
  326. // HeapSizes 1
  327. var sizes = ReadByte();
  328. // Reserved2 1
  329. Advance(1);
  330. // Valid 8
  331. heap.Valid = ReadInt64();
  332. // Sorted 8
  333. heap.Sorted = ReadInt64();
  334. for (int i = 0; i < TableHeap.TableCount; i++)
  335. {
  336. if (!heap.HasTable((Table)i))
  337. continue;
  338. heap.Tables[i].Length = ReadUInt32();
  339. }
  340. SetIndexSize(image.StringHeap, sizes, 0x1);
  341. SetIndexSize(image.GuidHeap, sizes, 0x2);
  342. SetIndexSize(image.BlobHeap, sizes, 0x4);
  343. ComputeTableInformations();
  344. }
  345. static void SetIndexSize(Heap heap, uint sizes, byte flag)
  346. {
  347. if (heap == null)
  348. return;
  349. heap.IndexSize = (sizes & flag) > 0 ? 4 : 2;
  350. }
  351. int GetTableIndexSize(Table table)
  352. {
  353. return image.GetTableIndexSize(table);
  354. }
  355. int GetCodedIndexSize(CodedIndex index)
  356. {
  357. return image.GetCodedIndexSize(index);
  358. }
  359. void ComputeTableInformations()
  360. {
  361. uint offset = (uint)BaseStream.Position - image.MetadataSection.PointerToRawData; // header
  362. int stridx_size = image.StringHeap.IndexSize;
  363. int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2;
  364. var heap = image.TableHeap;
  365. var tables = heap.Tables;
  366. for (int i = 0; i < TableHeap.TableCount; i++)
  367. {
  368. var table = (Table)i;
  369. if (!heap.HasTable(table))
  370. continue;
  371. int size;
  372. switch (table)
  373. {
  374. case Table.Module:
  375. size = 2 // Generation
  376. + stridx_size // Name
  377. + (image.GuidHeap.IndexSize * 3); // Mvid, EncId, EncBaseId
  378. break;
  379. case Table.TypeRef:
  380. size = GetCodedIndexSize(CodedIndex.ResolutionScope) // ResolutionScope
  381. + (stridx_size * 2); // Name, Namespace
  382. break;
  383. case Table.TypeDef:
  384. size = 4 // Flags
  385. + (stridx_size * 2) // Name, Namespace
  386. + GetCodedIndexSize(CodedIndex.TypeDefOrRef) // BaseType
  387. + GetTableIndexSize(Table.Field) // FieldList
  388. + GetTableIndexSize(Table.Method); // MethodList
  389. break;
  390. case Table.FieldPtr:
  391. size = GetTableIndexSize(Table.Field); // Field
  392. break;
  393. case Table.Field:
  394. size = 2 // Flags
  395. + stridx_size // Name
  396. + blobidx_size; // Signature
  397. break;
  398. case Table.MethodPtr:
  399. size = GetTableIndexSize(Table.Method); // Method
  400. break;
  401. case Table.Method:
  402. size = 8 // Rva 4, ImplFlags 2, Flags 2
  403. + stridx_size // Name
  404. + blobidx_size // Signature
  405. + GetTableIndexSize(Table.Param); // ParamList
  406. break;
  407. case Table.ParamPtr:
  408. size = GetTableIndexSize(Table.Param); // Param
  409. break;
  410. case Table.Param:
  411. size = 4 // Flags 2, Sequence 2
  412. + stridx_size; // Name
  413. break;
  414. case Table.InterfaceImpl:
  415. size = GetTableIndexSize(Table.TypeDef) // Class
  416. + GetCodedIndexSize(CodedIndex.TypeDefOrRef); // Interface
  417. break;
  418. case Table.MemberRef:
  419. size = GetCodedIndexSize(CodedIndex.MemberRefParent) // Class
  420. + stridx_size // Name
  421. + blobidx_size; // Signature
  422. break;
  423. case Table.Constant:
  424. size = 2 // Type
  425. + GetCodedIndexSize(CodedIndex.HasConstant) // Parent
  426. + blobidx_size; // Value
  427. break;
  428. case Table.CustomAttribute:
  429. size = GetCodedIndexSize(CodedIndex.HasCustomAttribute) // Parent
  430. + GetCodedIndexSize(CodedIndex.CustomAttributeType) // Type
  431. + blobidx_size; // Value
  432. break;
  433. case Table.FieldMarshal:
  434. size = GetCodedIndexSize(CodedIndex.HasFieldMarshal) // Parent
  435. + blobidx_size; // NativeType
  436. break;
  437. case Table.DeclSecurity:
  438. size = 2 // Action
  439. + GetCodedIndexSize(CodedIndex.HasDeclSecurity) // Parent
  440. + blobidx_size; // PermissionSet
  441. break;
  442. case Table.ClassLayout:
  443. size = 6 // PackingSize 2, ClassSize 4
  444. + GetTableIndexSize(Table.TypeDef); // Parent
  445. break;
  446. case Table.FieldLayout:
  447. size = 4 // Offset
  448. + GetTableIndexSize(Table.Field); // Field
  449. break;
  450. case Table.StandAloneSig:
  451. size = blobidx_size; // Signature
  452. break;
  453. case Table.EventMap:
  454. size = GetTableIndexSize(Table.TypeDef) // Parent
  455. + GetTableIndexSize(Table.Event); // EventList
  456. break;
  457. case Table.EventPtr:
  458. size = GetTableIndexSize(Table.Event); // Event
  459. break;
  460. case Table.Event:
  461. size = 2 // Flags
  462. + stridx_size // Name
  463. + GetCodedIndexSize(CodedIndex.TypeDefOrRef); // EventType
  464. break;
  465. case Table.PropertyMap:
  466. size = GetTableIndexSize(Table.TypeDef) // Parent
  467. + GetTableIndexSize(Table.Property); // PropertyList
  468. break;
  469. case Table.PropertyPtr:
  470. size = GetTableIndexSize(Table.Property); // Property
  471. break;
  472. case Table.Property:
  473. size = 2 // Flags
  474. + stridx_size // Name
  475. + blobidx_size; // Type
  476. break;
  477. case Table.MethodSemantics:
  478. size = 2 // Semantics
  479. + GetTableIndexSize(Table.Method) // Method
  480. + GetCodedIndexSize(CodedIndex.HasSemantics); // Association
  481. break;
  482. case Table.MethodImpl:
  483. size = GetTableIndexSize(Table.TypeDef) // Class
  484. + GetCodedIndexSize(CodedIndex.MethodDefOrRef) // MethodBody
  485. + GetCodedIndexSize(CodedIndex.MethodDefOrRef); // MethodDeclaration
  486. break;
  487. case Table.ModuleRef:
  488. size = stridx_size; // Name
  489. break;
  490. case Table.TypeSpec:
  491. size = blobidx_size; // Signature
  492. break;
  493. case Table.ImplMap:
  494. size = 2 // MappingFlags
  495. + GetCodedIndexSize(CodedIndex.MemberForwarded) // MemberForwarded
  496. + stridx_size // ImportName
  497. + GetTableIndexSize(Table.ModuleRef); // ImportScope
  498. break;
  499. case Table.FieldRVA:
  500. size = 4 // RVA
  501. + GetTableIndexSize(Table.Field); // Field
  502. break;
  503. case Table.EncLog:
  504. case Table.EncMap:
  505. size = 4;
  506. break;
  507. case Table.Assembly:
  508. size = 16 // HashAlgId 4, Version 4 * 2, Flags 4
  509. + blobidx_size // PublicKey
  510. + (stridx_size * 2); // Name, Culture
  511. break;
  512. case Table.AssemblyProcessor:
  513. size = 4; // Processor
  514. break;
  515. case Table.AssemblyOS:
  516. size = 12; // Platform 4, Version 2 * 4
  517. break;
  518. case Table.AssemblyRef:
  519. size = 12 // Version 2 * 4 + Flags 4
  520. + (blobidx_size * 2) // PublicKeyOrToken, HashValue
  521. + (stridx_size * 2); // Name, Culture
  522. break;
  523. case Table.AssemblyRefProcessor:
  524. size = 4 // Processor
  525. + GetTableIndexSize(Table.AssemblyRef); // AssemblyRef
  526. break;
  527. case Table.AssemblyRefOS:
  528. size = 12 // Platform 4, Version 2 * 4
  529. + GetTableIndexSize(Table.AssemblyRef); // AssemblyRef
  530. break;
  531. case Table.File:
  532. size = 4 // Flags
  533. + stridx_size // Name
  534. + blobidx_size; // HashValue
  535. break;
  536. case Table.ExportedType:
  537. size = 8 // Flags 4, TypeDefId 4
  538. + (stridx_size * 2) // Name, Namespace
  539. + GetCodedIndexSize(CodedIndex.Implementation); // Implementation
  540. break;
  541. case Table.ManifestResource:
  542. size = 8 // Offset, Flags
  543. + stridx_size // Name
  544. + GetCodedIndexSize(CodedIndex.Implementation); // Implementation
  545. break;
  546. case Table.NestedClass:
  547. size = GetTableIndexSize(Table.TypeDef) // NestedClass
  548. + GetTableIndexSize(Table.TypeDef); // EnclosingClass
  549. break;
  550. case Table.GenericParam:
  551. size = 4 // Number, Flags
  552. + GetCodedIndexSize(CodedIndex.TypeOrMethodDef) // Owner
  553. + stridx_size; // Name
  554. break;
  555. case Table.MethodSpec:
  556. size = GetCodedIndexSize(CodedIndex.MethodDefOrRef) // Method
  557. + blobidx_size; // Instantiation
  558. break;
  559. case Table.GenericParamConstraint:
  560. size = GetTableIndexSize(Table.GenericParam) // Owner
  561. + GetCodedIndexSize(CodedIndex.TypeDefOrRef); // Constraint
  562. break;
  563. default:
  564. throw new NotSupportedException();
  565. }
  566. tables[i].RowSize = (uint)size;
  567. tables[i].Offset = offset;
  568. offset += (uint)size * tables[i].Length;
  569. }
  570. }
  571. public static Image ReadImageFrom(Stream stream)
  572. {
  573. try
  574. {
  575. var reader = new ImageReader(stream);
  576. reader.ReadImage();
  577. return reader.image;
  578. }
  579. catch (EndOfStreamException e)
  580. {
  581. throw new BadImageFormatException(Mixin.GetFullyQualifiedName(stream), e);
  582. }
  583. }
  584. }
  585. }