PEFile.cs 22 KB

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