2006-06-17 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / class / Mono.PEToolkit / Image.cs
1 \r
2 //\r
3 // Permission is hereby granted, free of charge, to any person obtaining\r
4 // a copy of this software and associated documentation files (the\r
5 // "Software"), to deal in the Software without restriction, including\r
6 // without limitation the rights to use, copy, modify, merge, publish,\r
7 // distribute, sublicense, and/or sell copies of the Software, and to\r
8 // permit persons to whom the Software is furnished to do so, subject to\r
9 // the following conditions:\r
10 // \r
11 // The above copyright notice and this permission notice shall be\r
12 // included in all copies or substantial portions of the Software.\r
13 // \r
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21 //\r
22 /*\r
23  * Copyright (c) 2002 Sergey Chaban <serge@wildwestsoftware.com>\r
24  */\r
25 \r
26 using System;\r
27 using System.IO;\r
28 using System.Collections;\r
29 using System.Runtime.InteropServices;\r
30 \r
31 using Mono.PEToolkit.Metadata;\r
32 \r
33 namespace Mono.PEToolkit {\r
34 \r
35         public class Image : IDisposable {\r
36 \r
37                 internal DOSHeader dosHdr;\r
38                 internal COFFHeader coffHdr;\r
39                 internal PEHeader peHdr;\r
40 \r
41                 internal CorHeader corHdr;\r
42 \r
43                 internal Hashtable sections;\r
44                 // File position right after PEHeader (NT Optional Header).\r
45                 protected long sectionsPos;\r
46 \r
47                 private MetaDataRoot mdRoot;\r
48 \r
49                 private string name;\r
50                 private bool open;\r
51                 internal BinaryReader reader;\r
52 \r
53                 public Image(string name)\r
54                 {\r
55                         this.name = name;\r
56                         open = false;\r
57                         reader = null;\r
58 \r
59                         mdRoot = null;\r
60 \r
61                         dosHdr = new DOSHeader();\r
62                         coffHdr = new COFFHeader();\r
63                         peHdr = new PEHeader();\r
64                         corHdr = new CorHeader();\r
65 \r
66                         sections = new Hashtable();\r
67                         sectionsPos = -1;\r
68                 }\r
69 \r
70                 ~Image()\r
71                 {\r
72                         Close();\r
73                 }\r
74 \r
75 \r
76                 public Hashtable Sections {\r
77                         get {\r
78                                 return sections;\r
79                         }\r
80                 }\r
81 \r
82                 public void Open()\r
83                 {\r
84                         lock (this) if (!open) {\r
85                                 FileInfo pe = new FileInfo(name);\r
86                                 if (!pe.Exists) {\r
87                                         throw new Exception("Invalid file path.");\r
88                                 }\r
89 \r
90                                 reader = new BinaryReader(new BufferedStream(pe.OpenRead()));\r
91                                 if (!reader.BaseStream.CanSeek) {\r
92                                         throw new Exception("Can't seek.");\r
93                                 }\r
94 \r
95                                 open = true;\r
96                         }\r
97                 }\r
98 \r
99                 public void Close()\r
100                 {\r
101                         lock (this) if (open) {\r
102                                 reader.Close();\r
103                                 open = false;\r
104                         }\r
105                 }\r
106 \r
107                 // IDisposable\r
108                 public void Dispose()\r
109                 {\r
110                         Close();\r
111                 }\r
112 \r
113 \r
114                 public bool IsCLI {\r
115                         get {\r
116                                 return peHdr.IsCLIImage;\r
117                         }\r
118                 }\r
119 \r
120                 public MetaDataRoot MetadataRoot {\r
121                         get {\r
122                                 return mdRoot;\r
123                         }\r
124                 }\r
125 \r
126                 /// <summary>\r
127                 /// </summary>\r
128                 public void ReadHeaders()\r
129                 {\r
130                         if (!open) {\r
131                                 throw new Exception("You must open image before trying to read it.");\r
132                         }\r
133 \r
134                         dosHdr.Read(reader);\r
135                         reader.BaseStream.Position = dosHdr.Lfanew;\r
136                         ExeSignature peSig = (ExeSignature) reader.ReadUInt16();\r
137                         if (peSig != ExeSignature.NT) {\r
138                                 throw new Exception ("Invalid image format: cannot find PE signature.");\r
139                         }\r
140                         peSig = (ExeSignature) reader.ReadUInt16();\r
141                         if (peSig != ExeSignature.NT2) {\r
142                                 throw new Exception ("Invalid image format: cannot find PE signature.");\r
143                         }\r
144 \r
145                         coffHdr.Read(reader);\r
146                         peHdr.Read(reader);\r
147                 \r
148                         sectionsPos = reader.BaseStream.Position;\r
149                         ReadSections();\r
150                         \r
151                         if (this.IsCLI) {\r
152                                 \r
153                                 reader.BaseStream.Position = RVAToVA(peHdr.CLIHdrDir.virtAddr);\r
154                                 corHdr.Read (reader);\r
155 \r
156                                 mdRoot = new MetaDataRoot(this);\r
157                                 reader.BaseStream.Position = RVAToVA(corHdr.MetaData.virtAddr);\r
158                                 mdRoot.Read(reader);\r
159                         }\r
160                         \r
161                 }\r
162 \r
163                 public void WriteHeaders (BinaryWriter writer)\r
164                 {\r
165                         dosHdr.Write (writer);\r
166                         writer.BaseStream.Position = dosHdr.Lfanew;\r
167                         writer.Write ((ushort)ExeSignature.NT);\r
168                         writer.Write ((ushort)ExeSignature.NT2);\r
169                         \r
170                         coffHdr.Write (writer);\r
171                         peHdr.Write (writer);\r
172                 \r
173                         WriteSections (writer);\r
174                         \r
175                         if (this.IsCLI) {\r
176                                 \r
177                                 writer.BaseStream.Position = RVAToVA (peHdr.CLIHdrDir.virtAddr);\r
178                                 corHdr.Write (writer);\r
179 \r
180                                 long pos = RVAToVA (corHdr.MetaData.virtAddr);\r
181                                 writer.BaseStream.Position = pos;\r
182                                 mdRoot.Write (writer);\r
183                                 \r
184                         }\r
185                         \r
186                 }\r
187 \r
188                 /// <summary>\r
189                 /// </summary>\r
190                 protected void ReadSections()\r
191                 {\r
192                         if (sectionsPos < 0) {\r
193                                 throw new Exception("Read headers first.");\r
194                         }\r
195                         reader.BaseStream.Position = sectionsPos;\r
196 \r
197                         int n = coffHdr.NumberOfSections;\r
198                         for (int i = n; --i >=0;) {\r
199                                 Section sect = new Section();\r
200                                 sect.Read(reader);\r
201                                 sections [sect.Name] = sect;\r
202                         }\r
203                 }\r
204 \r
205                 protected void WriteSections (BinaryWriter writer)\r
206                 {\r
207                         foreach (Section section in sections.Values) {\r
208                                 section.Write (writer);\r
209                         }\r
210                 }\r
211 \r
212 \r
213                 /// <summary>\r
214                 /// </summary>\r
215                 /// <param name="writer"></param>\r
216                 public void Dump(TextWriter writer)\r
217                 {\r
218                         writer.WriteLine (\r
219                                 "COFF Header:" + Environment.NewLine +\r
220                                 coffHdr.ToString() + Environment.NewLine +\r
221                                 "PE Header:" + Environment.NewLine +\r
222                                 peHdr.ToString() + Environment.NewLine +\r
223                                 "Core Header:" + Environment.NewLine +\r
224                                 corHdr.ToString()\r
225                         );\r
226                 }\r
227 \r
228 \r
229                 /// <summary>\r
230                 /// </summary>\r
231                 /// <returns></returns>\r
232                 public override string ToString()\r
233                 {\r
234                         StringWriter sw = new StringWriter();\r
235                         Dump(sw);\r
236                         return sw.ToString();\r
237                 }\r
238 \r
239 \r
240                 /// <summary>\r
241                 ///  Returns name of the section for the given RVA.\r
242                 /// </summary>\r
243                 /// <param name="rva"></param>\r
244                 /// <returns></returns>\r
245                 public string RVAToSectionName(RVA rva)\r
246                 {\r
247                         string res = null;\r
248                         foreach (Section s in Sections.Values) {\r
249                                 RVA sva = s.VirtualAddress;\r
250                                 if (rva >= sva && rva < sva + s.SizeOfRawData) {\r
251                                         res = s.Name;\r
252                                         break;\r
253                                 }\r
254                         }\r
255                         return res;\r
256                 }\r
257 \r
258                 public long RVAToVA(RVA rva)\r
259                 {\r
260                         string sectName = RVAToSectionName(rva);\r
261                         long res = 0;\r
262                         if (sectName != null) {\r
263                                 Section s = (Section) Sections [sectName];\r
264                                 res = rva + (s.PointerToRawData - s.VirtualAddress);\r
265                         }\r
266                         return res;\r
267                 }\r
268 \r
269                 public MetaDataRoot MetaDataRoot {\r
270                         get {\r
271                                 return mdRoot;\r
272                         }\r
273                 }\r
274 \r
275                 public void DumpStreamHeader(TextWriter writer, string name)\r
276                 {\r
277                         if (mdRoot == null || name == null || name == String.Empty || writer == null) return;\r
278                         writer.Write(name + " header: ");\r
279                         MDStream s = MetaDataRoot.Streams[name] as MDStream;\r
280                         if (s != null) {\r
281                                 writer.WriteLine();\r
282                                 writer.WriteLine(s);\r
283                         } else {\r
284                                 writer.WriteLine("not present.");\r
285                                 writer.WriteLine();\r
286                         }\r
287                 }\r
288 \r
289                 public void DumpStreamHeader(string name)\r
290                 {\r
291                         DumpStreamHeader(Console.Out, name);\r
292                 }\r
293 \r
294         }\r
295 \r
296 }\r
297 \r