PEFile.cs 23 KB

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