PEResourceClasses.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. /// <summary>
  24. /// (Unmanaged) Resource Elements consist of PEResourceDirectories
  25. /// or PEResourceData elements. Resource directories may be nested up
  26. /// to three deep sorted on Type, Name and Language in that order.
  27. /// </summary>
  28. public abstract class PEResourceElement
  29. {
  30. private int id;
  31. private string name;
  32. public PEResourceElement() { }
  33. public int Id
  34. {
  35. get { return id; }
  36. set { id = value; }
  37. }
  38. public string Name
  39. {
  40. get { return name; }
  41. set { name = value; }
  42. }
  43. protected internal abstract uint Size();
  44. /// <summary>
  45. /// Write out the unmanaged resource data.
  46. /// </summary>
  47. /// <param name="dest">The Binary Writer</param>
  48. /// <param name="baseOffset">File position at start of .rsrc section</param>
  49. /// <param name="RVA">RVA of .rsrc section when loaded</param>
  50. protected internal abstract void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA);
  51. protected internal long offset;
  52. protected const uint HeaderSize = 16;
  53. protected const uint EntrySize = 8;
  54. }
  55. /// <summary>
  56. /// ResourceDirectory entries, as defined in Winnt.h
  57. /// as type struct _IMAGE_RESOURCE_DIRECTORY.
  58. /// </summary>
  59. public class PEResourceDirectory : PEResourceElement {
  60. private uint date = 0;
  61. private ushort majver = 1;
  62. private ushort minver = 0;
  63. public uint Date { get { return date; } set { date = value; } }
  64. public ushort MajVer { get { return majver; } set { majver = value; } }
  65. public ushort MinVer { get { return minver; } set { minver = value; } }
  66. //private ArrayList subItems = new ArrayList();
  67. private List<PEResourceElement> elements = new List<PEResourceElement>();
  68. public int Count() { return elements.Count; }
  69. /// <summary>
  70. /// Programmatically create unmanaged resource.
  71. /// </summary>
  72. public PEResourceDirectory() { }
  73. /// <summary>
  74. /// Read unmanged resource directory structure from PE-file.
  75. /// </summary>
  76. /// <param name="reader"></param>
  77. internal void PopulateResourceDirectory(PEReader reader, long baseOffset) {
  78. PEResourceElement resElement = null;
  79. PEResourceDirectory resDirectory;
  80. PEResourceData resData;
  81. int junk = reader.ReadInt32(); // Must be zero.
  82. this.date = reader.ReadUInt32(); // Time stamp.
  83. this.majver = reader.ReadUInt16();
  84. this.minver = reader.ReadUInt16();
  85. int numNameEntries = reader.ReadUInt16(); // Number of named entries.
  86. int numIdntEntries = reader.ReadUInt16(); // Number of ID entries.
  87. for (int i = 0; i < numNameEntries; i++) {
  88. uint nameOrId = reader.ReadUInt32();
  89. uint elemOfst = reader.ReadUInt32();
  90. if ((elemOfst & 0x80000000) != 0) // Element is subdirectory.
  91. resElement = new PEResourceDirectory();
  92. else
  93. resElement = new PEResourceData();
  94. resElement.Name = ReadName(reader, baseOffset + nameOrId & 0x7fffffff);
  95. resElement.offset = baseOffset + (long)(elemOfst & 0x7fffffff);
  96. this.AddElement(resElement);
  97. }
  98. // Read the Ident entries.
  99. for (int i = 0; i < numIdntEntries; i++) {
  100. uint nameOrId = reader.ReadUInt32();
  101. uint elemOfst = reader.ReadUInt32();
  102. if ((elemOfst & 0x80000000) != 0) // Element is subdirectory.
  103. resElement = new PEResourceDirectory();
  104. else
  105. resElement = new PEResourceData();
  106. resElement.Id = (ushort)nameOrId;
  107. resElement.offset = baseOffset + (long)(elemOfst & 0x7fffffff);
  108. this.AddElement(resElement);
  109. }
  110. // Now recurse to get subdirectories/the real data.
  111. foreach (PEResourceElement elem in this.elements) {
  112. if ((resDirectory = elem as PEResourceDirectory) != null) {
  113. reader.BaseStream.Seek(resDirectory.offset, SeekOrigin.Begin);
  114. resDirectory.PopulateResourceDirectory(reader, baseOffset);
  115. }
  116. else if ((resData = elem as PEResourceData) != null) {
  117. reader.BaseStream.Seek(resData.offset, SeekOrigin.Begin);
  118. resData.PopulateResourceData(reader, baseOffset);
  119. }
  120. }
  121. }
  122. private string ReadName(BinaryReader rdr, long offset) {
  123. long savedPos = rdr.BaseStream.Position;
  124. rdr.BaseStream.Seek(offset, SeekOrigin.Begin);
  125. ushort nLength = rdr.ReadUInt16();
  126. char[] name = new char[nLength];
  127. for (int i = 0; i < nLength; i++)
  128. name[i] = (char)rdr.ReadUInt16();
  129. return new string(name);
  130. }
  131. public bool HasData() {
  132. return elements.Count > 0;
  133. }
  134. public void AddElement(PEResourceElement el) {
  135. //subItems.Add(el);
  136. elements.Add(el);
  137. }
  138. /// <summary>
  139. /// Total file-space size of all child elements
  140. /// </summary>
  141. private uint subSize;
  142. /// <summary>
  143. /// File-space needed for all names of this directory
  144. /// </summary>
  145. private uint nameSize;
  146. /// <summary>
  147. /// File-space taken up by this directory
  148. /// </summary>
  149. private uint dirSize;
  150. /// <summary>
  151. /// Number of named elements. These come first in list.
  152. /// </summary>
  153. private uint numNamed;
  154. private uint numIds { get { return (uint)elements.Count - numNamed; } }
  155. protected internal override uint Size() {
  156. nameSize = 0;
  157. numNamed = 0;
  158. subSize = 0;
  159. //for (int i = 0; i < subItems.Count; i++)
  160. foreach (PEResourceElement elem in this.elements) {
  161. subSize += elem.Size();
  162. if (elem.Name != null) {
  163. nameSize += 2 + (uint)elem.Name.Length * 2;
  164. numNamed++;
  165. }
  166. }
  167. dirSize = (uint)elements.Count * EntrySize + HeaderSize;
  168. return dirSize + nameSize + subSize;
  169. }
  170. /// <summary>
  171. /// Write out the unmanaged resource rooted at this directory.
  172. /// </summary>
  173. /// <param name="dest">The Binary Writer</param>
  174. /// <param name="RVA">RVA of this .rsrc section</param>
  175. internal void Write(BinaryWriter dest, uint RVA) {
  176. Size();
  177. dest.Flush();
  178. uint baseOffset = (uint)dest.BaseStream.Position;
  179. this.Write(dest, baseOffset, 0, RVA);
  180. }
  181. protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA) {
  182. uint nameOffset = currentOffset + this.dirSize;
  183. uint targetOffset = currentOffset + this.dirSize;
  184. dest.Write((uint)0); // characteristics
  185. dest.Write(date); // datetime
  186. dest.Write(majver);
  187. dest.Write(minver);
  188. dest.Write((ushort)numNamed);
  189. dest.Write((ushort)numIds);
  190. currentOffset += HeaderSize;
  191. // Write out the named items.
  192. foreach (PEResourceElement elem in elements) {
  193. if (elem.Name != null) {
  194. dest.Write((uint)(nameOffset | 0x80000000));
  195. if (elem is PEResourceDirectory)
  196. dest.Write((uint)(targetOffset | 0x80000000));
  197. else
  198. dest.Write((uint)targetOffset);
  199. nameOffset += 2 + (uint)elem.Name.Length * 2;
  200. targetOffset += (uint)elem.Size();
  201. currentOffset += EntrySize;
  202. }
  203. }
  204. // Write out the items with ID.
  205. foreach (PEResourceElement elem in elements) {
  206. if (elem.Name == null) {
  207. dest.Write(elem.Id);
  208. if (elem is PEResourceDirectory)
  209. dest.Write((uint)(targetOffset | 0x80000000));
  210. else
  211. dest.Write((uint)targetOffset);
  212. currentOffset += EntrySize;
  213. targetOffset += elem.Size();
  214. }
  215. }
  216. // Write out the name strings.
  217. foreach (PEResourceElement elem in elements) {
  218. string s = elem.Name;
  219. if (s != null) {
  220. dest.Write((ushort)s.Length);
  221. byte[] b = System.Text.Encoding.Unicode.GetBytes(s);
  222. dest.Write(b);
  223. }
  224. }
  225. currentOffset += this.nameSize;
  226. // Now recurse to the children.
  227. foreach (PEResourceElement elem in elements) {
  228. elem.Write(dest, baseOffset, currentOffset, RVA);
  229. currentOffset += elem.Size();
  230. }
  231. }
  232. }
  233. public class PEResourceData : PEResourceElement
  234. {
  235. public PEResourceData() { }
  236. int codepage = 0;
  237. byte[] data;
  238. public int CodePage { get { return codepage; } set { codepage = value; } }
  239. public byte[] Data { get { return data; } set { data = value; } }
  240. protected internal override uint Size()
  241. {
  242. return 16 + (uint)Data.Length;
  243. }
  244. /// <summary>
  245. /// Read the binary data from the PE file.
  246. /// </summary>
  247. /// <param name="reader"></param>
  248. internal void PopulateResourceData(PEReader reader, long baseOffset) {
  249. uint dataRVA = reader.ReadUInt32();
  250. int dataLength = reader.ReadInt32();
  251. this.codepage = reader.ReadInt32();
  252. uint junk = reader.ReadUInt32(); // Must be zero.
  253. reader.BaseStream.Seek(reader.GetOffset(dataRVA), SeekOrigin.Begin);
  254. data = new byte[dataLength];
  255. int numberRead = reader.BaseStream.Read(data, 0, dataLength);
  256. }
  257. protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA)
  258. {
  259. dest.Write((uint)(currentOffset + HeaderSize) + RVA);
  260. dest.Write((uint)data.Length);
  261. dest.Write((uint)codepage);
  262. dest.Write((uint)0);
  263. dest.Write(data);
  264. }
  265. }
  266. }