PEFile.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /*
  2. * PERWAPI - An API for Reading and Writing PE Files
  3. *
  4. * Copyright (c) Diane Corney, Queensland University of Technology, 2004.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the PERWAPI Copyright as included with this
  8. * distribution in the file PERWAPIcopyright.rtf.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY as is explained in the copyright notice.
  12. *
  13. * The author may be contacted at d.corney@qut.edu.au
  14. *
  15. * Version Date: 26/01/07
  16. */
  17. using System;
  18. using System.IO;
  19. using System.Collections;
  20. using System.Collections.Generic;
  21. namespace QUT.PERWAPI
  22. {
  23. /**************************************************************************/
  24. /**************************************************************************/
  25. /**************************************************************************/
  26. // Base Class for PE Files
  27. /**************************************************************************/
  28. /**************************************************************************/
  29. /**************************************************************************/
  30. /// <summary>
  31. /// Base class for the PEFile (starting point)
  32. /// </summary>
  33. public class PEFile : Module
  34. {
  35. private string outputDir, fileName;
  36. private Stream outStream;
  37. private Assembly thisAssembly;
  38. PEWriter output;
  39. MetaDataOut metaData;
  40. System.IO.FileStream unmanagedResources; // Unmanaged resources read from a file.
  41. internal PEResourceDirectory unmanagedResourceRoot; // Unmanaged resources added programmatically.
  42. internal MetaDataTables metaDataTables;
  43. internal PEFileVersionInfo versionInfo;
  44. private Dictionary<string, AssemblyRef> asmRefDict = new Dictionary<string, AssemblyRef>();
  45. /*-------------------- Constructors ---------------------------------*/
  46. /// <summary>
  47. /// Create a new PE File with the name "fileName". If "fileName" ends in ".dll" then
  48. /// the file is a dll, otherwise it is an exe file. This PE File has no assembly.
  49. /// </summary>
  50. /// <param name="fileName">Name for the output file.</param>
  51. public PEFile(string fileName)
  52. : base(fileName)
  53. {
  54. //PrimitiveType.ClearAddedFlags(); // KJG 18-April-2005 - Now done in MetaDataOut
  55. this.fileName = fileName;
  56. metaData = new MetaDataOut();
  57. versionInfo = new PEFileVersionInfo();
  58. versionInfo.SetDefaults(fileName);
  59. }
  60. /// <summary>
  61. /// Create a new PE File with the name "fileName". If "fileName" ends in ".dll" then
  62. /// the file is a dll, otherwise it is an exe file. This file has an Assembly called
  63. /// "assemblyName".
  64. /// </summary>
  65. /// <param name="fileName">Name for the output file</param>
  66. /// <param name="assemblyName">Name of the assembly</param>
  67. public PEFile(string fileName, string assemblyName)
  68. : base(fileName)
  69. {
  70. //PrimitiveType.ClearAddedFlags(); // KJG 18-April-2005 - Now done in MetaDataOut
  71. this.fileName = fileName;
  72. thisAssembly = new Assembly(assemblyName, this);
  73. metaData = new MetaDataOut();
  74. versionInfo = new PEFileVersionInfo();
  75. versionInfo.SetDefaults(fileName);
  76. }
  77. /// <summary>
  78. /// Read a PE file and create all the data structures to represent it
  79. /// </summary>
  80. /// <param name="filename">The file name of the PE file</param>
  81. /// <returns>PEFile object representing "filename"</returns>
  82. public static PEFile ReadPEFile(string filename)
  83. {
  84. return PEReader.ReadPEFile(filename, false);
  85. }
  86. /// <summary>
  87. /// Read an existing PE File and return the exported interface
  88. /// (ie. anything that was specified as public).
  89. /// All the MetaData structures will be Refs.
  90. /// </summary>
  91. /// <param name="filename">The name of the pe file</param>
  92. /// <returns>The AssemblyRef or ModuleRef describing the exported interface of the specified file</returns>
  93. public static ReferenceScope ReadExportedInterface(string filename)
  94. {
  95. return PEReader.GetExportedInterface(filename);
  96. }
  97. public static PEFile ReadPublicClasses(string filename)
  98. {
  99. PEFile pefile = PEReader.ReadPEFile(filename, true);
  100. ArrayList newClasses = new ArrayList();
  101. for (int i = 0; i < pefile.classes.Count; i++)
  102. {
  103. ClassDef aClass = (ClassDef)pefile.classes[i];
  104. if (aClass.isPublic()) newClasses.Add(aClass);
  105. }
  106. pefile.classes = newClasses;
  107. return pefile;
  108. }
  109. /*---------------------------- public set and get methods ------------------------------*/
  110. /// <summary>
  111. /// Get the version of .NET for this PE file
  112. /// </summary>
  113. /// <returns>.NET version</returns>
  114. public NetVersion GetNetVersion()
  115. {
  116. return versionInfo.netVersion;
  117. }
  118. /// <summary>
  119. /// Set the .NET version for this PE file
  120. /// </summary>
  121. /// <param name="nVer">.NET version</param>
  122. public void SetNetVersion(NetVersion nVer)
  123. {
  124. versionInfo.netVersion = nVer;
  125. versionInfo.netVerString = MetaData.versions[(int)versionInfo.netVersion];
  126. if ((nVer == NetVersion.Whidbey40) || (nVer == NetVersion.Whidbey41))
  127. {
  128. versionInfo.tsMinVer = 1;
  129. }
  130. else
  131. {
  132. versionInfo.tsMinVer = 0;
  133. }
  134. if (nVer == NetVersion.Whidbey50)
  135. {
  136. versionInfo.tsMajVer = 2;
  137. }
  138. GenericParam.extraField = nVer <= NetVersion.Whidbey40;
  139. if (Diag.DiagOn && GenericParam.extraField)
  140. Console.WriteLine("Writing extra field for GenericParams");
  141. }
  142. /// <summary>
  143. /// Get the .NET version for this PE file
  144. /// </summary>
  145. /// <returns>string representing the .NET version</returns>
  146. public string GetNetVersionString()
  147. {
  148. return versionInfo.netVerString.Trim();
  149. }
  150. /// <summary>
  151. /// Make a descriptor for an external assembly to this PEFile (.assembly extern)
  152. /// </summary>
  153. /// <param name="assemName">the external assembly name</param>
  154. /// <returns>a descriptor for this external assembly</returns>
  155. public AssemblyRef MakeExternAssembly(string assemName) {
  156. AssemblyRef result = null;
  157. if (assemName.CompareTo(MSCorLib.mscorlib.Name()) == 0) return MSCorLib.mscorlib;
  158. else if (!asmRefDict.TryGetValue(assemName, out result)) {
  159. result = new AssemblyRef(assemName);
  160. asmRefDict[assemName] = result;
  161. }
  162. return result;
  163. }
  164. /// <summary>
  165. /// Make a descriptor for an external module to this PEFile (.module extern)
  166. /// </summary>
  167. /// <param name="name">the external module name</param>
  168. /// <returns>a descriptor for this external module</returns>
  169. public ModuleRef MakeExternModule(string name)
  170. {
  171. return new ModuleRef(name);
  172. }
  173. /// <summary>
  174. /// Set the directory that the PE File will be written to.
  175. /// The default is the current directory.
  176. /// </summary>
  177. /// <param name="outputDir">The directory to write the PE File to.</param>
  178. public void SetOutputDirectory(string outputDir)
  179. {
  180. this.outputDir = outputDir;
  181. }
  182. /// <summary>
  183. /// Direct PE File output to an existing stream, instead of creating
  184. /// a new file.
  185. /// </summary>
  186. /// <param name="output">The output stream</param>
  187. public void SetOutputStream(Stream output)
  188. {
  189. this.outStream = output;
  190. }
  191. /// <summary>
  192. /// Specify if this PEFile is a .dll or .exe
  193. /// </summary>
  194. public void SetIsDLL(bool isDll)
  195. {
  196. versionInfo.isDLL = isDll;
  197. if (isDll)
  198. versionInfo.characteristics = FileImage.dllCharacteristics;
  199. else
  200. versionInfo.characteristics = FileImage.exeCharacteristics;
  201. }
  202. /// <summary>
  203. /// Set the subsystem (.subsystem) (Default is Windows Console mode)
  204. /// </summary>
  205. /// <param name="subS">subsystem value</param>
  206. public void SetSubSystem(SubSystem subS)
  207. {
  208. versionInfo.subSystem = subS;
  209. }
  210. /// <summary>
  211. /// Set the flags (.corflags)
  212. /// </summary>
  213. /// <param name="flags">the flags value</param>
  214. public void SetCorFlags(CorFlags flags)
  215. {
  216. versionInfo.corFlags = flags;
  217. }
  218. public string GetFileName()
  219. {
  220. return fileName;
  221. }
  222. public void SetFileName(string filename)
  223. {
  224. this.fileName = filename;
  225. }
  226. /// <summary>
  227. /// Get a Meta Data Element from this PE file
  228. /// </summary>
  229. /// <param name="token">The meta data token for the required element</param>
  230. /// <returns>The meta data element denoted by token</returns>
  231. public MetaDataElement GetElement(uint token)
  232. {
  233. if (buffer != null)
  234. return buffer.GetTokenElement(token);
  235. if (metaDataTables != null)
  236. return metaDataTables.GetTokenElement(token);
  237. return null;
  238. }
  239. /// <summary>
  240. /// Add an unmanaged resource to this PEFile
  241. /// </summary>
  242. public void AddUnmanagedResourceFile(string resFilename)
  243. {
  244. if (!System.IO.File.Exists(resFilename))
  245. throw (new FileNotFoundException("Unmanaged Resource File Not Found", resFilename));
  246. // unmanagedResources = System.IO.File.OpenRead(resFilename);
  247. throw new NotYetImplementedException("Unmanaged Resources from input files are not yet implemented");
  248. }
  249. /// <summary>
  250. /// Add a managed resource to this PEFile. The resource will be embedded in this PE file.
  251. /// </summary>
  252. /// <param name="resName">The name of the managed resource</param>
  253. /// <param name="resBytes">The managed resource</param>
  254. /// <param name="isPublic">Access for the resource</param>
  255. public void AddManagedResource(string resName, byte[] resBytes, bool isPublic)
  256. {
  257. resources.Add(new ManifestResource(this, resName, resBytes, isPublic));
  258. }
  259. /// <summary>
  260. /// Add a managed resource from another assembly.
  261. /// </summary>
  262. /// <param name="resName">The name of the resource</param>
  263. /// <param name="assem">The assembly where the resource is</param>
  264. /// <param name="isPublic">Access for the resource</param>
  265. public void AddExternalManagedResource(string resName, AssemblyRef assem, bool isPublic)
  266. {
  267. resources.Add(new ManifestResource(this, resName, assem, 0, isPublic));
  268. }
  269. /// <summary>
  270. /// Add a managed resource from another file in this assembly.
  271. /// </summary>
  272. /// <param name="resName">The name of the resource</param>
  273. /// <param name="assem">The assembly where the resource is</param>
  274. /// <param name="isPublic">Access for the resource</param>
  275. public void AddExternalManagedResource(string resName, ResourceFile resFile, uint offset, bool isPublic)
  276. {
  277. resources.Add(new ManifestResource(this, resName, resFile, offset, isPublic));
  278. }
  279. /// <summary>
  280. /// Add a managed resource from another module in this assembly.
  281. /// </summary>
  282. /// <param name="resName">The name of the resource</param>
  283. /// <param name="assem">The assembly where the resource is</param>
  284. /// <param name="isPublic">Access for the resource</param>
  285. public void AddExternalManagedResource(string resName, ModuleRef mod, uint offset, bool isPublic)
  286. {
  287. resources.Add(new ManifestResource(this, resName, mod.modFile, offset, isPublic));
  288. }
  289. /// <summary>
  290. /// Add a managed resource from another assembly.
  291. /// </summary>
  292. /// <param name="mr"></param>
  293. /// <param name="isPublic"></param>
  294. public void AddExternalManagedResource(ManifestResource mr, bool isPublic)
  295. {
  296. resources.Add(new ManifestResource(this, mr, isPublic));
  297. }
  298. /// <summary>
  299. /// Find a resource
  300. /// </summary>
  301. /// <param name="name">The name of the resource</param>
  302. /// <returns>The resource with the name "name" or null </returns>
  303. public ManifestResource GetResource(string name)
  304. {
  305. for (int i = 0; i < resources.Count; i++)
  306. {
  307. if (((ManifestResource)resources[i]).Name == name)
  308. return (ManifestResource)resources[i];
  309. }
  310. return null;
  311. }
  312. public ManifestResource[] GetResources()
  313. {
  314. return (ManifestResource[])resources.ToArray(typeof(ManifestResource));
  315. }
  316. /// <summary>
  317. /// Get the descriptor for this assembly. The PEFile must have been
  318. /// created with hasAssembly = true
  319. /// </summary>
  320. /// <returns>the descriptor for this assembly</returns>
  321. public Assembly GetThisAssembly()
  322. {
  323. return thisAssembly;
  324. }
  325. public AssemblyRef[] GetImportedAssemblies()
  326. {
  327. return buffer.GetAssemblyRefs();
  328. }
  329. public string[] GetNamesOfImports()
  330. {
  331. return buffer.GetAssemblyRefNames();
  332. }
  333. /*------------------------------------------ Output Methods -------------------------------*/
  334. private void BuildMetaData()
  335. {
  336. BuildMDTables(metaData);
  337. metaData.BuildCode();
  338. if (thisAssembly != null)
  339. {
  340. thisAssembly.BuildMDTables(metaData);
  341. }
  342. metaData.BuildMDTables(); // DoCustomAttributes, BuildSignatures for each in metadata tables
  343. }
  344. /// <summary>
  345. /// Write out the PEFile (the "bake" function)
  346. /// </summary>
  347. /// <param name="debug">include debug information</param>
  348. public void WritePEFile(bool writePDB)
  349. {
  350. if (outStream == null)
  351. {
  352. if (outputDir != null)
  353. {
  354. if (!outputDir.EndsWith("\\"))
  355. fileName = outputDir + "\\" + fileName;
  356. else
  357. fileName = outputDir + fileName;
  358. }
  359. output = new PEWriter(versionInfo, fileName, metaData, writePDB);
  360. }
  361. else
  362. {
  363. // Check to make sure we have not been asked to write a PDB
  364. if (writePDB) throw new Exception("You can not write PDB data when writing to a stream. Please try writing to a file instead.");
  365. output = new PEWriter(versionInfo, outStream, metaData);
  366. }
  367. BuildMetaData();
  368. // If the application is roundtripping an input PE-file with
  369. // unmanaged resources, then this.unmanagedResourceRoot != null.
  370. if (this.unmanagedResourceRoot != null)
  371. output.AddUnmanagedResourceDirectory(this.unmanagedResourceRoot);
  372. output.MakeFile(versionInfo);
  373. }
  374. /// <summary>
  375. /// Makes the assembly debuggable by attaching the DebuggableAttribute
  376. /// to the Assembly. Call immediately before calling WritePEFile.
  377. /// </summary>
  378. /// <param name="allowDebug">set true to enable debugging, false otherwise</param>
  379. /// <param name="suppressOpt">set true to disable optimizations that affect debugging</param>
  380. public void MakeDebuggable(bool allowDebug, bool suppressOpt)
  381. {
  382. ClassRef debugRef = null;
  383. MethodRef dCtor = null;
  384. Type[] twoBools = new Type[] { PrimitiveType.Boolean, PrimitiveType.Boolean };
  385. debugRef = MSCorLib.mscorlib.GetClass("System.Diagnostics", "DebuggableAttribute");
  386. if (debugRef == null)
  387. debugRef = MSCorLib.mscorlib.AddClass("System.Diagnostics", "DebuggableAttribute");
  388. dCtor = debugRef.GetMethod(".ctor", twoBools);
  389. if (dCtor == null)
  390. {
  391. dCtor = debugRef.AddMethod(".ctor", PrimitiveType.Void, twoBools);
  392. dCtor.AddCallConv(CallConv.Instance);
  393. }
  394. Constant[] dbgArgs = new Constant[] { new BoolConst(allowDebug), new BoolConst(suppressOpt) };
  395. thisAssembly.AddCustomAttribute(dCtor, dbgArgs);
  396. }
  397. /// <summary>
  398. /// Write out a CIL text file for this PE file
  399. /// </summary>
  400. /// <param name="debug">include debug information</param>
  401. public void WriteCILFile(bool debug)
  402. {
  403. string cilFile = fileName.Substring(0, fileName.IndexOf('.')) + ".il";
  404. CILWriter writer = new CILWriter(cilFile, debug, this);
  405. writer.BuildCILInfo();
  406. writer.WriteFile(debug);
  407. }
  408. internal void SetThisAssembly(Assembly assem)
  409. {
  410. if (Diag.DiagOn) Console.WriteLine("Setting fileScope to assembly " + assem.Name());
  411. thisAssembly = assem;
  412. }
  413. internal void AddToResourceList(ManifestResource res)
  414. {
  415. resources.Add(res);
  416. }
  417. internal void AddToFileList()
  418. {
  419. }
  420. internal void SetDLLFlags(ushort dflags)
  421. {
  422. versionInfo.DLLFlags = dflags;
  423. }
  424. public void ReadPDB()
  425. {
  426. PDBReader reader = new PDBReader(this.fileName);
  427. foreach (ClassDef cDef in GetClasses())
  428. foreach (MethodDef mDef in cDef.GetMethods())
  429. {
  430. CILInstructions buffer = mDef.GetCodeBuffer();
  431. PDBMethod meth = reader.GetMethod((int)mDef.Token());
  432. if (meth == null)
  433. continue; // no symbols for this method
  434. PDBSequencePoint[] spList = meth.SequencePoints;
  435. MergeBuffer mergeBuffer = new MergeBuffer(buffer.GetInstructions());
  436. PDBScope outer = meth.Scope;
  437. Scope root = ReadPDBScope(outer, mergeBuffer, null, mDef);
  438. buffer.currentScope = root;
  439. bool hasRootScope = mergeBuffer.hasRootScope();
  440. if (!hasRootScope)
  441. mergeBuffer.Add(new OpenScope(root), (uint)0);
  442. foreach (PDBSequencePoint sp in spList)
  443. {
  444. PDBDocument doc = sp.Document;
  445. mergeBuffer.Add(
  446. new Line((uint)sp.Line, (uint)sp.Column, (uint)sp.EndLine, (uint)sp.EndColumn,
  447. SourceFile.GetSourceFile(doc.URL, doc.Language, doc.LanguageVendor, doc.DocumentType)),
  448. (uint)sp.Offset);
  449. }
  450. if (!hasRootScope)
  451. mergeBuffer.Add(new CloseScope(root), (uint)outer.EndOffset);
  452. buffer.SetInstructions(mergeBuffer.Instructions);
  453. }
  454. }
  455. private Scope ReadPDBScope(PDBScope scope, MergeBuffer mergeBuffer, Scope parent, MethodDef thisMeth)
  456. {
  457. Scope thisScope = new Scope(parent, thisMeth);
  458. if (parent != null) mergeBuffer.Add(new OpenScope(thisScope), (uint)scope.StartOffset);
  459. foreach (PDBVariable var in scope.Variables)
  460. thisScope.AddLocalBinding(var.Name, var.Address);
  461. foreach (PDBScope child in scope.Children)
  462. ReadPDBScope(child, mergeBuffer, thisScope, thisMeth);
  463. if (parent != null) mergeBuffer.Add(new CloseScope(thisScope), (uint)scope.EndOffset);
  464. return thisScope;
  465. }
  466. }
  467. /**************************************************************************/
  468. internal class PEFileVersionInfo
  469. {
  470. private static char[] nulls = { '\0' };
  471. internal bool fromExisting;
  472. internal ushort characteristics;
  473. internal bool isDLL;
  474. internal byte lMajor;
  475. internal byte lMinor;
  476. internal uint fileAlign;
  477. internal ushort osMajor;
  478. internal ushort osMinor;
  479. internal ushort userMajor;
  480. internal ushort userMinor;
  481. internal ushort subSysMajor;
  482. internal ushort subSysMinor;
  483. internal SubSystem subSystem;
  484. internal ushort DLLFlags = 0;
  485. internal ushort cliMajVer;
  486. internal ushort cliMinVer;
  487. internal CorFlags corFlags = CorFlags.CF_IL_ONLY;
  488. internal ushort mdMajVer;
  489. internal ushort mdMinVer;
  490. internal NetVersion netVersion;
  491. internal string netVerString;
  492. internal byte tsMajVer;
  493. internal byte tsMinVer;
  494. internal void SetDefaults(string name)
  495. {
  496. fromExisting = false;
  497. isDLL = name.EndsWith(".dll") || name.EndsWith(".DLL");
  498. if (isDLL)
  499. {
  500. characteristics = FileImage.dllCharacteristics;
  501. }
  502. else
  503. {
  504. characteristics = FileImage.exeCharacteristics;
  505. }
  506. lMajor = MetaData.LMajors[0];
  507. lMinor = 0;
  508. fileAlign = FileImage.minFileAlign;
  509. osMajor = 4;
  510. osMinor = 0;
  511. userMajor = 0;
  512. userMinor = 0;
  513. subSysMajor = 4;
  514. subSysMinor = 0;
  515. subSystem = SubSystem.Windows_CUI;
  516. DLLFlags = FileImage.DLLFlags;
  517. cliMajVer = 2;
  518. cliMinVer = 0;
  519. corFlags = CorFlags.CF_IL_ONLY;
  520. mdMajVer = 1;
  521. mdMinVer = 1; // MetaData Minor Version ECMA = 0, PEFiles = 1
  522. netVersion = NetVersion.Everett;
  523. netVerString = MetaData.versions[0];
  524. tsMajVer = 1;
  525. tsMinVer = 0;
  526. }
  527. internal void SetVersionFromString()
  528. {
  529. for (int i = 0; i < MetaData.versions.Length; i++)
  530. {
  531. if (MetaData.versions[i].Trim(nulls) == netVerString)
  532. {
  533. netVersion = (NetVersion)i;
  534. netVerString = MetaData.versions[i];
  535. }
  536. }
  537. }
  538. }
  539. }