1 /*
2   Copyright (C) 2009 Jeroen Frijters
3 
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7 
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11 
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19 
20   Jeroen Frijters
21   jeroen@frijters.net
22 
23 */
24 using System;
25 using BYTE = System.Byte;
26 using WORD = System.UInt16;
27 using DWORD = System.UInt32;
28 using ULONGLONG = System.UInt64;
29 using System.IO;
30 
31 namespace IKVM.Reflection.Reader
32 {
33 	sealed class MSDOS_HEADER
34 	{
35 		internal const WORD MAGIC_MZ = 0x5A4D;
36 
37 		internal WORD signature;	// 'MZ'
38 		// skip 58 bytes
39 		internal DWORD peSignatureOffset;
40 	}
41 
42 	sealed class IMAGE_NT_HEADERS
43 	{
44 		public const DWORD MAGIC_SIGNATURE = 0x00004550;	// "PE\0\0"
45 
46 		public DWORD Signature;
47 		public IMAGE_FILE_HEADER FileHeader = new IMAGE_FILE_HEADER();
48 		public IMAGE_OPTIONAL_HEADER OptionalHeader = new IMAGE_OPTIONAL_HEADER();
49 
Read(BinaryReader br)50 		internal void Read(BinaryReader br)
51 		{
52 			Signature = br.ReadUInt32();
53 			if (Signature != IMAGE_NT_HEADERS.MAGIC_SIGNATURE)
54 			{
55 				throw new BadImageFormatException();
56 			}
57 			FileHeader.Read(br);
58 			long optionalHeaderPosition = br.BaseStream.Position;
59 			OptionalHeader.Read(br);
60 			if (br.BaseStream.Position > optionalHeaderPosition + FileHeader.SizeOfOptionalHeader)
61 			{
62 				throw new BadImageFormatException();
63 			}
64 			br.BaseStream.Seek(optionalHeaderPosition + FileHeader.SizeOfOptionalHeader, SeekOrigin.Begin);
65 		}
66 	}
67 
68 	sealed class IMAGE_FILE_HEADER
69 	{
70 		public const WORD IMAGE_FILE_MACHINE_I386 = 0x014c;
71 		public const WORD IMAGE_FILE_MACHINE_IA64 = 0x0200;
72 		public const WORD IMAGE_FILE_MACHINE_AMD64 = 0x8664;
73 
74 		public const WORD IMAGE_FILE_32BIT_MACHINE = 0x0100;
75 		public const WORD IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002;
76 		public const WORD IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020;
77 		public const WORD IMAGE_FILE_DLL = 0x2000;
78 
79 		public WORD Machine;
80 		public WORD NumberOfSections;
81 		public DWORD TimeDateStamp;
82 		public DWORD PointerToSymbolTable;
83 		public DWORD NumberOfSymbols;
84 		public WORD SizeOfOptionalHeader;
85 		public WORD Characteristics;
86 
Read(BinaryReader br)87 		internal void Read(BinaryReader br)
88 		{
89 			Machine = br.ReadUInt16();
90 			NumberOfSections = br.ReadUInt16();
91 			TimeDateStamp = br.ReadUInt32();
92 			PointerToSymbolTable = br.ReadUInt32();
93 			NumberOfSymbols = br.ReadUInt32();
94 			SizeOfOptionalHeader = br.ReadUInt16();
95 			Characteristics = br.ReadUInt16();
96 		}
97 	}
98 
99 	sealed class IMAGE_OPTIONAL_HEADER
100 	{
101 		public const WORD IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
102 		public const WORD IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
103 
104 		public const WORD IMAGE_SUBSYSTEM_WINDOWS_GUI = 2;
105 		public const WORD IMAGE_SUBSYSTEM_WINDOWS_CUI = 3;
106 
107 		public WORD Magic;
108 		public BYTE MajorLinkerVersion;
109 		public BYTE MinorLinkerVersion;
110 		public DWORD SizeOfCode;
111 		public DWORD SizeOfInitializedData;
112 		public DWORD SizeOfUninitializedData;
113 		public DWORD AddressOfEntryPoint;
114 		public DWORD BaseOfCode;
115 		public DWORD BaseOfData;
116 		public ULONGLONG ImageBase;
117 		public DWORD SectionAlignment;
118 		public DWORD FileAlignment;
119 		public WORD MajorOperatingSystemVersion;
120 		public WORD MinorOperatingSystemVersion;
121 		public WORD MajorImageVersion;
122 		public WORD MinorImageVersion;
123 		public WORD MajorSubsystemVersion;
124 		public WORD MinorSubsystemVersion;
125 		public DWORD Win32VersionValue;
126 		public DWORD SizeOfImage;
127 		public DWORD SizeOfHeaders;
128 		public DWORD CheckSum;
129 		public WORD Subsystem;
130 		public WORD DllCharacteristics;
131 		public ULONGLONG SizeOfStackReserve;
132 		public ULONGLONG SizeOfStackCommit;
133 		public ULONGLONG SizeOfHeapReserve;
134 		public ULONGLONG SizeOfHeapCommit;
135 		public DWORD LoaderFlags;
136 		public DWORD NumberOfRvaAndSizes;
137 		public IMAGE_DATA_DIRECTORY[] DataDirectory;
138 
Read(BinaryReader br)139 		internal void Read(BinaryReader br)
140 		{
141 			Magic = br.ReadUInt16();
142 			if (Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
143 			{
144 				throw new BadImageFormatException();
145 			}
146 			MajorLinkerVersion = br.ReadByte();
147 			MinorLinkerVersion = br.ReadByte();
148 			SizeOfCode = br.ReadUInt32();
149 			SizeOfInitializedData = br.ReadUInt32();
150 			SizeOfUninitializedData = br.ReadUInt32();
151 			AddressOfEntryPoint = br.ReadUInt32();
152 			BaseOfCode = br.ReadUInt32();
153 			if (Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
154 			{
155 				BaseOfData = br.ReadUInt32();
156 				ImageBase = br.ReadUInt32();
157 			}
158 			else
159 			{
160 				ImageBase = br.ReadUInt64();
161 			}
162 			SectionAlignment = br.ReadUInt32();
163 			FileAlignment = br.ReadUInt32();
164 			MajorOperatingSystemVersion = br.ReadUInt16();
165 			MinorOperatingSystemVersion = br.ReadUInt16();
166 			MajorImageVersion = br.ReadUInt16();
167 			MinorImageVersion = br.ReadUInt16();
168 			MajorSubsystemVersion = br.ReadUInt16();
169 			MinorSubsystemVersion = br.ReadUInt16();
170 			Win32VersionValue = br.ReadUInt32();
171 			SizeOfImage = br.ReadUInt32();
172 			SizeOfHeaders = br.ReadUInt32();
173 			CheckSum = br.ReadUInt32();
174 			Subsystem = br.ReadUInt16();
175 			DllCharacteristics = br.ReadUInt16();
176 			if (Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
177 			{
178 				SizeOfStackReserve = br.ReadUInt32();
179 				SizeOfStackCommit = br.ReadUInt32();
180 				SizeOfHeapReserve = br.ReadUInt32();
181 				SizeOfHeapCommit = br.ReadUInt32();
182 			}
183 			else
184 			{
185 				SizeOfStackReserve = br.ReadUInt64();
186 				SizeOfStackCommit = br.ReadUInt64();
187 				SizeOfHeapReserve = br.ReadUInt64();
188 				SizeOfHeapCommit = br.ReadUInt64();
189 			}
190 			LoaderFlags = br.ReadUInt32();
191 			NumberOfRvaAndSizes = br.ReadUInt32();
192 			DataDirectory = new IMAGE_DATA_DIRECTORY[NumberOfRvaAndSizes];
193 			for (uint i = 0; i < NumberOfRvaAndSizes; i++)
194 			{
195 				DataDirectory[i] = new IMAGE_DATA_DIRECTORY();
196 				DataDirectory[i].Read(br);
197 			}
198 		}
199 	}
200 
201 	struct IMAGE_DATA_DIRECTORY
202 	{
203 		public DWORD VirtualAddress;
204 		public DWORD Size;
205 
ReadIKVM.Reflection.Reader.IMAGE_DATA_DIRECTORY206 		internal void Read(BinaryReader br)
207 		{
208 			VirtualAddress = br.ReadUInt32();
209 			Size = br.ReadUInt32();
210 		}
211 	}
212 
213 	class SectionHeader
214 	{
215 		public const DWORD IMAGE_SCN_CNT_CODE = 0x00000020;
216 		public const DWORD IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040;
217 		public const DWORD IMAGE_SCN_MEM_DISCARDABLE = 0x02000000;
218 		public const DWORD IMAGE_SCN_MEM_EXECUTE = 0x20000000;
219 		public const DWORD IMAGE_SCN_MEM_READ = 0x40000000;
220 		public const DWORD IMAGE_SCN_MEM_WRITE = 0x80000000;
221 
222 		public string Name;		// 8 byte UTF8 encoded 0-padded
223 		public DWORD VirtualSize;
224 		public DWORD VirtualAddress;
225 		public DWORD SizeOfRawData;
226 		public DWORD PointerToRawData;
227 		public DWORD PointerToRelocations;
228 		public DWORD PointerToLinenumbers;
229 		public WORD NumberOfRelocations;
230 		public WORD NumberOfLinenumbers;
231 		public DWORD Characteristics;
232 
Read(BinaryReader br)233 		internal void Read(BinaryReader br)
234 		{
235 			char[] name = new char[8];
236 			int len = 8;
237 			for (int i = 0; i < 8; i++)
238 			{
239 				byte b = br.ReadByte();
240 				name[i] = (char)b;
241 				if (b == 0 && len == 8)
242 				{
243 					len = i;
244 				}
245 			}
246 			Name = new String(name, 0, len);
247 			VirtualSize = br.ReadUInt32();
248 			VirtualAddress = br.ReadUInt32();
249 			SizeOfRawData = br.ReadUInt32();
250 			PointerToRawData = br.ReadUInt32();
251 			PointerToRelocations = br.ReadUInt32();
252 			PointerToLinenumbers = br.ReadUInt32();
253 			NumberOfRelocations = br.ReadUInt16();
254 			NumberOfLinenumbers = br.ReadUInt16();
255 			Characteristics = br.ReadUInt32();
256 		}
257 	}
258 
259 	sealed class PEReader
260 	{
261 		private MSDOS_HEADER msdos = new MSDOS_HEADER();
262 		private IMAGE_NT_HEADERS headers = new IMAGE_NT_HEADERS();
263 		private SectionHeader[] sections;
264 		private bool mapped;
265 
Read(BinaryReader br, bool mapped)266 		internal void Read(BinaryReader br, bool mapped)
267 		{
268 			this.mapped = mapped;
269 			msdos.signature = br.ReadUInt16();
270 			br.BaseStream.Seek(58, SeekOrigin.Current);
271 			msdos.peSignatureOffset = br.ReadUInt32();
272 
273 			if (msdos.signature != MSDOS_HEADER.MAGIC_MZ)
274 			{
275 				throw new BadImageFormatException();
276 			}
277 
278 			br.BaseStream.Seek(msdos.peSignatureOffset, SeekOrigin.Begin);
279 			headers.Read(br);
280 			sections = new SectionHeader[headers.FileHeader.NumberOfSections];
281 			for (int i = 0; i < sections.Length; i++)
282 			{
283 				sections[i] = new SectionHeader();
284 				sections[i].Read(br);
285 			}
286 		}
287 
288 		internal IMAGE_FILE_HEADER FileHeader
289 		{
290 			get { return headers.FileHeader; }
291 		}
292 
293 		internal IMAGE_OPTIONAL_HEADER OptionalHeader
294 		{
295 			get { return headers.OptionalHeader; }
296 		}
297 
GetComDescriptorVirtualAddress()298 		internal DWORD GetComDescriptorVirtualAddress()
299 		{
300 			return headers.OptionalHeader.DataDirectory[14].VirtualAddress;
301 		}
302 
GetDataDirectoryEntry(int index, out int rva, out int length)303 		internal void GetDataDirectoryEntry(int index, out int rva, out int length)
304 		{
305 			rva = (int)headers.OptionalHeader.DataDirectory[index].VirtualAddress;
306 			length = (int)headers.OptionalHeader.DataDirectory[index].Size;
307 		}
308 
RvaToFileOffset(DWORD rva)309 		internal long RvaToFileOffset(DWORD rva)
310 		{
311 			if (mapped)
312 			{
313 				return rva;
314 			}
315 			for (int i = 0; i < sections.Length; i++)
316 			{
317 				if (rva >= sections[i].VirtualAddress && rva < sections[i].VirtualAddress + sections[i].VirtualSize)
318 				{
319 					return sections[i].PointerToRawData + rva - sections[i].VirtualAddress;
320 				}
321 			}
322 			throw new BadImageFormatException();
323 		}
324 
GetSectionInfo(int rva, out string name, out int characteristics, out int virtualAddress, out int virtualSize, out int pointerToRawData, out int sizeOfRawData)325 		internal bool GetSectionInfo(int rva, out string name, out int characteristics, out int virtualAddress, out int virtualSize, out int pointerToRawData, out int sizeOfRawData)
326 		{
327 			for (int i = 0; i < sections.Length; i++)
328 			{
329 				if (rva >= sections[i].VirtualAddress && rva < sections[i].VirtualAddress + sections[i].VirtualSize)
330 				{
331 					name = sections[i].Name;
332 					characteristics = (int)sections[i].Characteristics;
333 					virtualAddress = (int)sections[i].VirtualAddress;
334 					virtualSize = (int)sections[i].VirtualSize;
335 					pointerToRawData = (int)sections[i].PointerToRawData;
336 					sizeOfRawData = (int)sections[i].SizeOfRawData;
337 					return true;
338 				}
339 			}
340 			name = null;
341 			characteristics = 0;
342 			virtualAddress = 0;
343 			virtualSize = 0;
344 			pointerToRawData = 0;
345 			sizeOfRawData = 0;
346 			return false;
347 		}
348 	}
349 }
350