PEFile.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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. if (thisAssembly != null)
  332. {
  333. thisAssembly.BuildMDTables(metaData);
  334. }
  335. metaData.BuildMDTables(); // DoCustomAttributes, BuildSignatures for each in metadata tables
  336. }
  337. /// <summary>
  338. /// Write out the PEFile (the "bake" function)
  339. /// </summary>
  340. /// <param name="debug">include debug information</param>
  341. public void WritePEFile(bool writePDB)
  342. {
  343. if (outStream == null)
  344. {
  345. if (outputDir != null)
  346. {
  347. if (!outputDir.EndsWith("\\"))
  348. fileName = outputDir + "\\" + fileName;
  349. else
  350. fileName = outputDir + fileName;
  351. }
  352. output = new PEWriter(versionInfo, fileName, metaData, writePDB);
  353. }
  354. else
  355. {
  356. // Check to make sure we have not been asked to write a PDB
  357. if (writePDB) throw new Exception("You can not write PDB data when writing to a stream. Please try writing to a file instead.");
  358. output = new PEWriter(versionInfo, outStream, metaData);
  359. }
  360. BuildMetaData();
  361. // If the application is roundtripping an input PE-file with
  362. // unmanaged resources, then this.unmanagedResourceRoot != null.
  363. if (this.unmanagedResourceRoot != null)
  364. output.AddUnmanagedResourceDirectory(this.unmanagedResourceRoot);
  365. output.MakeFile(versionInfo);
  366. }
  367. /// <summary>
  368. /// Makes the assembly debuggable by attaching the DebuggableAttribute
  369. /// to the Assembly. Call immediately before calling WritePEFile.
  370. /// </summary>
  371. /// <param name="allowDebug">set true to enable debugging, false otherwise</param>
  372. /// <param name="suppressOpt">set true to disable optimizations that affect debugging</param>
  373. public void MakeDebuggable(bool allowDebug, bool suppressOpt)
  374. {
  375. ClassRef debugRef = null;
  376. MethodRef dCtor = null;
  377. Type[] twoBools = new Type[] { PrimitiveType.Boolean, PrimitiveType.Boolean };
  378. debugRef = MSCorLib.mscorlib.GetClass("System.Diagnostics", "DebuggableAttribute");
  379. if (debugRef == null)
  380. debugRef = MSCorLib.mscorlib.AddClass("System.Diagnostics", "DebuggableAttribute");
  381. dCtor = debugRef.GetMethod(".ctor", twoBools);
  382. if (dCtor == null)
  383. {
  384. dCtor = debugRef.AddMethod(".ctor", PrimitiveType.Void, twoBools);
  385. dCtor.AddCallConv(CallConv.Instance);
  386. }
  387. Constant[] dbgArgs = new Constant[] { new BoolConst(allowDebug), new BoolConst(suppressOpt) };
  388. thisAssembly.AddCustomAttribute(dCtor, dbgArgs);
  389. }
  390. /// <summary>
  391. /// Write out a CIL text file for this PE file
  392. /// </summary>
  393. /// <param name="debug">include debug information</param>
  394. public void WriteCILFile(bool debug)
  395. {
  396. string cilFile = fileName.Substring(0, fileName.IndexOf('.')) + ".il";
  397. CILWriter writer = new CILWriter(cilFile, debug, this);
  398. writer.BuildCILInfo();
  399. writer.WriteFile(debug);
  400. }
  401. internal void SetThisAssembly(Assembly assem)
  402. {
  403. if (Diag.DiagOn) Console.WriteLine("Setting fileScope to assembly " + assem.Name());
  404. thisAssembly = assem;
  405. }
  406. internal void AddToResourceList(ManifestResource res)
  407. {
  408. resources.Add(res);
  409. }
  410. internal void AddToFileList()
  411. {
  412. }
  413. internal void SetDLLFlags(ushort dflags)
  414. {
  415. versionInfo.DLLFlags = dflags;
  416. }
  417. public void ReadPDB()
  418. {
  419. PDBReader reader = new PDBReader(this.fileName);
  420. foreach (ClassDef cDef in GetClasses())
  421. foreach (MethodDef mDef in cDef.GetMethods())
  422. {
  423. CILInstructions buffer = mDef.GetCodeBuffer();
  424. PDBMethod meth = reader.GetMethod((int)mDef.Token());
  425. if (meth == null)
  426. continue; // no symbols for this method
  427. PDBSequencePoint[] spList = meth.SequencePoints;
  428. MergeBuffer mergeBuffer = new MergeBuffer(buffer.GetInstructions());
  429. PDBScope outer = meth.Scope;
  430. Scope root = ReadPDBScope(outer, mergeBuffer, null, mDef);
  431. buffer.currentScope = root;
  432. bool hasRootScope = mergeBuffer.hasRootScope();
  433. if (!hasRootScope)
  434. mergeBuffer.Add(new OpenScope(root), (uint)0);
  435. foreach (PDBSequencePoint sp in spList)
  436. {
  437. PDBDocument doc = sp.Document;
  438. mergeBuffer.Add(
  439. new Line((uint)sp.Line, (uint)sp.Column, (uint)sp.EndLine, (uint)sp.EndColumn,
  440. SourceFile.GetSourceFile(doc.URL, doc.Language, doc.LanguageVendor, doc.DocumentType)),
  441. (uint)sp.Offset);
  442. }
  443. if (!hasRootScope)
  444. mergeBuffer.Add(new CloseScope(root), (uint)outer.EndOffset);
  445. buffer.SetInstructions(mergeBuffer.Instructions);
  446. }
  447. }
  448. private Scope ReadPDBScope(PDBScope scope, MergeBuffer mergeBuffer, Scope parent, MethodDef thisMeth)
  449. {
  450. Scope thisScope = new Scope(parent, thisMeth);
  451. if (parent != null) mergeBuffer.Add(new OpenScope(thisScope), (uint)scope.StartOffset);
  452. foreach (PDBVariable var in scope.Variables)
  453. thisScope.AddLocalBinding(var.Name, var.Address);
  454. foreach (PDBScope child in scope.Children)
  455. ReadPDBScope(child, mergeBuffer, thisScope, thisMeth);
  456. if (parent != null) mergeBuffer.Add(new CloseScope(thisScope), (uint)scope.EndOffset);
  457. return thisScope;
  458. }
  459. }
  460. /**************************************************************************/
  461. internal class PEFileVersionInfo
  462. {
  463. private static char[] nulls = { '\0' };
  464. internal bool fromExisting;
  465. internal ushort characteristics;
  466. internal bool isDLL;
  467. internal byte lMajor;
  468. internal byte lMinor;
  469. internal uint fileAlign;
  470. internal ushort osMajor;
  471. internal ushort osMinor;
  472. internal ushort userMajor;
  473. internal ushort userMinor;
  474. internal ushort subSysMajor;
  475. internal ushort subSysMinor;
  476. internal SubSystem subSystem;
  477. internal ushort DLLFlags = 0;
  478. internal ushort cliMajVer;
  479. internal ushort cliMinVer;
  480. internal CorFlags corFlags = CorFlags.CF_IL_ONLY;
  481. internal ushort mdMajVer;
  482. internal ushort mdMinVer;
  483. internal NetVersion netVersion;
  484. internal string netVerString;
  485. internal byte tsMajVer;
  486. internal byte tsMinVer;
  487. internal void SetDefaults(string name)
  488. {
  489. fromExisting = false;
  490. isDLL = name.EndsWith(".dll") || name.EndsWith(".DLL");
  491. if (isDLL)
  492. {
  493. characteristics = FileImage.dllCharacteristics;
  494. }
  495. else
  496. {
  497. characteristics = FileImage.exeCharacteristics;
  498. }
  499. lMajor = MetaData.LMajors[0];
  500. lMinor = 0;
  501. fileAlign = FileImage.minFileAlign;
  502. osMajor = 4;
  503. osMinor = 0;
  504. userMajor = 0;
  505. userMinor = 0;
  506. subSysMajor = 4;
  507. subSysMinor = 0;
  508. subSystem = SubSystem.Windows_CUI;
  509. DLLFlags = FileImage.DLLFlags;
  510. cliMajVer = 2;
  511. cliMinVer = 0;
  512. corFlags = CorFlags.CF_IL_ONLY;
  513. mdMajVer = 1;
  514. mdMinVer = 1; // MetaData Minor Version ECMA = 0, PEFiles = 1
  515. netVersion = NetVersion.Everett;
  516. netVerString = MetaData.versions[0];
  517. tsMajVer = 1;
  518. tsMinVer = 0;
  519. }
  520. internal void SetVersionFromString()
  521. {
  522. for (int i = 0; i < MetaData.versions.Length; i++)
  523. {
  524. if (MetaData.versions[i].Trim(nulls) == netVerString)
  525. {
  526. netVersion = (NetVersion)i;
  527. netVerString = MetaData.versions[i];
  528. }
  529. }
  530. }
  531. }
  532. }