PEResourceClasses.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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 = this.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. rdr.BaseStream.Seek(savedPos, SeekOrigin.Begin);
  130. return new string(name);
  131. }
  132. public bool HasData() {
  133. return elements.Count > 0;
  134. }
  135. public void AddElement(PEResourceElement el) {
  136. //subItems.Add(el);
  137. elements.Add(el);
  138. }
  139. /// <summary>
  140. /// Total file-space size of all child elements
  141. /// </summary>
  142. private uint subSize;
  143. /// <summary>
  144. /// File-space needed for all names of this directory
  145. /// </summary>
  146. private uint nameSize;
  147. /// <summary>
  148. /// File-space taken up by this directory
  149. /// </summary>
  150. private uint dirSize;
  151. /// <summary>
  152. /// Number of named elements. These come first in list.
  153. /// </summary>
  154. private uint numNamed;
  155. private uint numIds { get { return (uint)elements.Count - numNamed; } }
  156. protected internal override uint Size() {
  157. nameSize = 0;
  158. numNamed = 0;
  159. subSize = 0;
  160. //for (int i = 0; i < subItems.Count; i++)
  161. foreach (PEResourceElement elem in this.elements) {
  162. subSize += elem.Size();
  163. if (elem.Name != null) {
  164. nameSize += 2 + (uint)elem.Name.Length * 2;
  165. numNamed++;
  166. }
  167. }
  168. dirSize = (uint)elements.Count * EntrySize + HeaderSize;
  169. return dirSize + nameSize + subSize;
  170. }
  171. /// <summary>
  172. /// Write out the unmanaged resource rooted at this directory.
  173. /// </summary>
  174. /// <param name="dest">The Binary Writer</param>
  175. /// <param name="RVA">RVA of this .rsrc section</param>
  176. internal void Write(BinaryWriter dest, uint RVA) {
  177. Size();
  178. dest.Flush();
  179. uint baseOffset = (uint)dest.BaseStream.Position;
  180. this.Write(dest, baseOffset, 0, RVA);
  181. }
  182. protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA) {
  183. uint nameOffset = currentOffset + this.dirSize;
  184. uint targetOffset = currentOffset + this.dirSize;
  185. dest.Write((uint)0); // characteristics
  186. dest.Write(date); // datetime
  187. dest.Write(majver);
  188. dest.Write(minver);
  189. dest.Write((ushort)numNamed);
  190. dest.Write((ushort)numIds);
  191. currentOffset += HeaderSize;
  192. // Write out the named items.
  193. foreach (PEResourceElement elem in elements) {
  194. if (elem.Name != null) {
  195. dest.Write((uint)(nameOffset | 0x80000000));
  196. if (elem is PEResourceDirectory)
  197. dest.Write((uint)(targetOffset | 0x80000000));
  198. else
  199. dest.Write((uint)targetOffset);
  200. nameOffset += 2 + (uint)elem.Name.Length * 2;
  201. targetOffset += (uint)elem.Size();
  202. currentOffset += EntrySize;
  203. }
  204. }
  205. // Write out the items with ID.
  206. foreach (PEResourceElement elem in elements) {
  207. if (elem.Name == null) {
  208. dest.Write(elem.Id);
  209. if (elem is PEResourceDirectory)
  210. dest.Write((uint)(targetOffset | 0x80000000));
  211. else
  212. dest.Write((uint)targetOffset);
  213. currentOffset += EntrySize;
  214. targetOffset += elem.Size();
  215. }
  216. }
  217. // Write out the name strings.
  218. foreach (PEResourceElement elem in elements) {
  219. string s = elem.Name;
  220. if (s != null) {
  221. dest.Write((ushort)s.Length);
  222. byte[] b = System.Text.Encoding.Unicode.GetBytes(s);
  223. dest.Write(b);
  224. }
  225. }
  226. currentOffset += this.nameSize;
  227. // Now recurse to the children.
  228. foreach (PEResourceElement elem in elements) {
  229. elem.Write(dest, baseOffset, currentOffset, RVA);
  230. currentOffset += elem.Size();
  231. }
  232. }
  233. }
  234. public class PEResourceData : PEResourceElement
  235. {
  236. public PEResourceData() { }
  237. int codepage = 0;
  238. byte[] data;
  239. public int CodePage { get { return codepage; } set { codepage = value; } }
  240. public byte[] Data { get { return data; } set { data = value; } }
  241. protected internal override uint Size()
  242. {
  243. return 16 + (uint)Data.Length;
  244. }
  245. /// <summary>
  246. /// Read the binary data from the PE file.
  247. /// </summary>
  248. /// <param name="reader"></param>
  249. internal void PopulateResourceData(PEReader reader, long baseOffset) {
  250. uint dataRVA = reader.ReadUInt32();
  251. int dataLength = reader.ReadInt32();
  252. this.codepage = reader.ReadInt32();
  253. uint junk = reader.ReadUInt32(); // Must be zero.
  254. reader.BaseStream.Seek(reader.GetOffset(dataRVA), SeekOrigin.Begin);
  255. data = new byte[dataLength];
  256. int numberRead = reader.BaseStream.Read(data, 0, dataLength);
  257. }
  258. protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA)
  259. {
  260. dest.Write((uint)(currentOffset + HeaderSize) + RVA);
  261. dest.Write((uint)data.Length);
  262. dest.Write((uint)codepage);
  263. dest.Write((uint)0);
  264. dest.Write(data);
  265. }
  266. }
  267. }