Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 406 lines (344 sloc) 15.099 kB
28a7c49 @kg First commit
kg authored
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.IO;
5 using System.Linq;
6 using System.Runtime.InteropServices;
7
8 namespace Squared.PE {
9 public class PEReader : BinaryReader {
10 public PEReader (Stream stream)
11 : base(stream) {
12 }
13
14 public bool CheckHeader (string token) {
15 var expected = Encoding.ASCII.GetBytes(token);
16 var actual = ReadBytes(expected.Length);
17 return expected.SequenceEqual(actual);
18 }
19
20 public unsafe void ReadStruct<T> (out T result)
21 where T : struct {
22
23 int count = Marshal.SizeOf(typeof(T));
24 byte[] buffer = ReadBytes(count);
25 fixed (byte* bufferPtr = buffer)
26 result = (T)Marshal.PtrToStructure(new IntPtr(bufferPtr), typeof(T));
27 }
28
29 public unsafe T[] ReadStructArray<T> (int count)
30 where T : struct {
31
32 var result = new T[count];
33 var type = typeof(T);
34
35 int size = Marshal.SizeOf(type);
36 var buffer = new byte[size];
37
38 fixed (byte* bufferPtr = buffer) {
39 var bufferIntPtr = new IntPtr(bufferPtr);
40 for (int i = 0; i < count; i++) {
41 BaseStream.Read(buffer, 0, size);
42 result[i] = (T)Marshal.PtrToStructure(bufferIntPtr, type);
43 }
44 }
45
46 return result;
47 }
48 }
49
50 public class PortableExecutable {
51 [StructLayout(LayoutKind.Sequential, Pack=1)]
52 public struct ImageFileHeader {
53 public UInt16 Machine;
54 public UInt16 NumberOfSections;
55 public UInt32 TimeDateStamp;
56 public UInt32 PointerToSymbolTable;
57 public UInt32 NumberOfSymbols;
58 public UInt16 SizeOfOptionalHeader;
59 public UInt16 Characteristics;
60 }
61
62 [StructLayout(LayoutKind.Sequential, Pack=1)]
63 public struct ImageOptionalHeader {
64 public UInt16 Magic;
65 public UInt16 LinkerVersion;
66 public UInt32 SizeOfCode;
67 public UInt32 SizeOfInitializedData;
68 public UInt32 SizeOfUninitializedData;
69 public UInt32 AddressOfEntryPoint;
70 public UInt32 BaseOfCode;
71 public UInt32 BaseOfData;
72 public UInt32 ImageBase;
73 public UInt32 SectionAlignment;
74 public UInt32 FileAlignment;
75 public UInt32 OperatingSystemVersion;
76 public UInt32 ImageVersion;
77 public UInt32 SubsystemVersion;
78 public UInt32 Reserved1;
79 public UInt32 SizeOfImage;
80 public UInt32 SizeOfHeaders;
81 public UInt32 CheckSum;
82 public UInt16 Subsystem;
83 public UInt16 DllCharacteristics;
84 public UInt32 SizeOfStackReserve;
85 public UInt32 SizeOfStackCommit;
86 public UInt32 SizeOfHeapReserve;
87 public UInt32 SizeOfHeapCommit;
88 public UInt32 LoaderFlags;
89 public UInt32 NumberOfRvaAndSizes;
90 }
91
92 [StructLayout(LayoutKind.Sequential, Pack = 1)]
93 public struct ImageDataDirectoryHeader {
94 public UInt32 VirtualAddress;
95 public UInt32 Size;
96 }
97
98 public enum DataDirectoryType {
99 Export = 0,
100 Import = 1,
101 Resource = 2,
102 Exception = 3,
103 Security = 4,
104 BaseRelocation = 5,
105 Debug = 6,
106 Copyright = 7,
107 GlobalPointer = 8,
108 ThreadLocalStorage = 9,
109 LoadConfig = 10,
110 BoundImport = 11,
111 ImportAddressTable = 12,
112 DelayImport = 13,
113 COMDescriptor = 14
114 }
115
116 [StructLayout(LayoutKind.Sequential, Pack = 1)]
117 public unsafe struct ImageSectionHeader {
118 public fixed byte _Name[8];
119 public UInt32 Misc;
120 public UInt32 VirtualAddress;
121 public UInt32 SizeOfRawData;
122 public UInt32 PointerToRawData;
123 public UInt32 PointerToRelocations;
124 public UInt32 PointerToLinenumbers;
125 public UInt16 NumberOfRelocations;
126 public UInt16 NumberOfLinenumbers;
127 public UInt32 Characteristics;
128
129 public unsafe string Name {
130 get {
131 fixed (byte * name = _Name)
132 return new String((sbyte *)name);
133 }
134 }
135 }
136
137 [StructLayout(LayoutKind.Sequential, Pack = 1)]
138 public struct RelocationBlockHeader {
139 public UInt32 VirtualAddress;
140 public UInt32 BlockSizeInclusive;
141 }
142
143 public struct Relocation {
144 public UInt32 VirtualAddress;
145 public RelocationType Type;
146 }
147
148 public enum RelocationType : byte {
149 Absolute = 0,
150 High = 1,
151 Low = 2,
152 HighLow = 3,
153 HighAdj = 4,
154 MIPS_JmpAddr = 5,
155 Section = 6,
156 Rel32 = 7
157 }
158
159 [StructLayout(LayoutKind.Sequential, Pack = 1)]
160 public struct ImageImportDescriptor {
161 public UInt32 OriginalFirstThunkRVA;
162 public UInt32 TimeDateStamp;
163 public UInt32 ForwarderChain;
164 public UInt32 ModuleNameRVA;
165 public UInt32 FirstThunkRVA;
166 }
167
168 public struct Import {
169 public string ModuleName;
170 public string FunctionName;
171 // The virtual address where the address of the imported function should be stored
172 public UInt32 FunctionAddressDestination;
173 }
174
175 [Flags]
176 public enum SectionCharacteristics : uint {
177 ContainsCode = 0x00000020,
178 ContainsInitializedData = 0x00000040,
179 ContainsUninitializedData = 0x00000080,
180 LinkInfo = 0x00000200,
181 LinkRemove = 0x00000800,
182 LinkCOMDAT = 0x00001000,
183 MemExecute = 0x20000000,
184 MemRead = 0x40000000,
185 MemWrite = 0x80000000,
186 }
187
188 public class Section {
189 public string Name;
190 public UInt32 VirtualAddress;
191 public UInt32 Size;
192 public byte[] RawData;
193 public SectionCharacteristics Characteristics;
194 }
195
196 public class DataDirectory {
197 public DataDirectoryType Type;
198 public Section Section;
199 public UInt32 Offset;
200 public UInt32 Size;
201 }
202
203 public ImageFileHeader FileHeader;
204 public ImageOptionalHeader OptionalHeader;
205 public ImageDataDirectoryHeader[] DataDirectoryHeaders;
206 public ImageSectionHeader[] SectionHeaders;
207 public Dictionary<DataDirectoryType, DataDirectory> DataDirectories;
208 public Dictionary<string, Section> Sections;
209 public Import[] Imports;
210 public Relocation[] Relocations;
211
212 public PortableExecutable (Stream stream) {
213 using (var sr = new PEReader(stream)) {
214 if (!sr.CheckHeader("MZ"))
215 throw new Exception("Invalid DOS header");
216
217 stream.Seek(60, SeekOrigin.Begin);
218 var headerOffset = sr.ReadUInt32();
219 stream.Seek(headerOffset, SeekOrigin.Begin);
220
221 if (!sr.CheckHeader("PE\0\0"))
222 throw new Exception("Invalid PE header");
223
224 sr.ReadStruct(out FileHeader);
225 sr.ReadStruct(out OptionalHeader);
226
227 DataDirectoryHeaders = sr.ReadStructArray<ImageDataDirectoryHeader>((int)OptionalHeader.NumberOfRvaAndSizes);
228 SectionHeaders = sr.ReadStructArray<ImageSectionHeader>(FileHeader.NumberOfSections);
229
230 LoadSections(sr);
231 }
232
233 LoadDataDirectories();
234 LoadImports();
235 LoadRelocations();
236 }
237
238 public void LoadSections (PEReader sr) {
239 Sections = new Dictionary<string, Section>(FileHeader.NumberOfSections);
240 foreach (var sectionHeader in SectionHeaders) {
241 sr.BaseStream.Seek(sectionHeader.PointerToRawData, SeekOrigin.Begin);
242 var section = new Section {
243 Name = sectionHeader.Name,
244 VirtualAddress = sectionHeader.VirtualAddress,
245 Size = sectionHeader.SizeOfRawData,
246 RawData = sr.ReadBytes((int)sectionHeader.SizeOfRawData),
247 Characteristics = (SectionCharacteristics)sectionHeader.Characteristics
248 };
249
250 Sections[section.Name] = section;
251 }
252 }
253
254 public void LoadDataDirectories () {
255 DataDirectories = new Dictionary<DataDirectoryType, DataDirectory>(DataDirectoryHeaders.Length);
256 for (int i = 0; i < DataDirectoryHeaders.Length; i++) {
257 var ddh = DataDirectoryHeaders[i];
258 if (ddh.Size == 0)
259 continue;
260
261 var begin = ddh.VirtualAddress;
262 var end = begin + ddh.Size;
263 var section = SectionFromVirtualAddressRange(begin, end);
264
265 DataDirectoryType type = (DataDirectoryType)i;
266 DataDirectories[type] = new DataDirectory {
267 Type = type,
268 Offset = ddh.VirtualAddress - section.VirtualAddress,
269 Section = section,
270 Size = ddh.Size
271 };
272 }
273 }
274
275 public unsafe void LoadImports () {
276 var result = new List<Import>();
277 var dd = DataDirectories[DataDirectoryType.Import];
278 var type = typeof(ImageImportDescriptor);
279 var size = Marshal.SizeOf(type);
280 int index = 0;
281
282 fixed (byte* bufferPtr = dd.Section.RawData)
283 while (true) {
284 var descriptor = (ImageImportDescriptor)Marshal.PtrToStructure(
285 new IntPtr(bufferPtr + dd.Offset + (size * index)), type
286 );
287 if (descriptor.FirstThunkRVA == 0)
288 break;
289 else
290 index += 1;
291
292 var moduleName = new String((sbyte*)bufferPtr + descriptor.ModuleNameRVA - dd.Section.VirtualAddress);
293
294 UInt32* rvaPtr = (UInt32*)(bufferPtr + descriptor.OriginalFirstThunkRVA - dd.Section.VirtualAddress);
295 UInt32 addressDestination = descriptor.FirstThunkRVA;
296 while (*rvaPtr != 0) {
297 var functionHintRva = *rvaPtr;
298 var functionNameRva = functionHintRva + 2;
299 result.Add(new Import {
300 ModuleName = moduleName,
301 FunctionName = new String((sbyte*)bufferPtr + functionNameRva - dd.Section.VirtualAddress),
302 FunctionAddressDestination = addressDestination
303 });
304
305 rvaPtr++;
306 addressDestination += 4;
307 }
308 }
309
310 Imports = result.ToArray();
311 }
312
313 public unsafe void LoadRelocations () {
314 var result = new List<Relocation>();
315
316 var dd = DataDirectories[DataDirectoryType.BaseRelocation];
317 var type = typeof(RelocationBlockHeader);
318 var size = Marshal.SizeOf(type);
319 uint offset = dd.Offset;
320
321 fixed (byte* bufferPtr = dd.Section.RawData)
322 while (true) {
323 if ((offset + size) >= dd.Size)
324 break;
325
326 var blockHeader = (RelocationBlockHeader)Marshal.PtrToStructure(
327 new IntPtr(bufferPtr + offset), type
328 );
329
330 uint numRelocations = (blockHeader.BlockSizeInclusive - (uint)size) / 2;
331 for (uint i = 0; i < numRelocations; i++) {
332 UInt16 relocationRaw = *(UInt16*)(bufferPtr + offset + size + (i * 2));
333 result.Add(new Relocation {
334 Type = (RelocationType)((relocationRaw >> 12) & 0xf),
335 VirtualAddress = (UInt32)((relocationRaw & 0xfff) + blockHeader.VirtualAddress)
336 });
337 }
338
339 offset += blockHeader.BlockSizeInclusive;
340 }
341
342 Relocations = result.ToArray();
343 }
344
345 public void Rebase (UInt32 newBaseAddress) {
346 var oldBaseAddress = OptionalHeader.ImageBase;
347 long delta = (long)newBaseAddress - (long)oldBaseAddress;
348
349 OptionalHeader.ImageBase = newBaseAddress;
350
351 int numRelocations = 0;
352 byte[] buffer;
353 foreach (var relocation in Relocations) {
354 if (relocation.Type == RelocationType.Absolute)
355 continue;
356 else if (relocation.Type != RelocationType.HighLow)
357 throw new Exception(String.Format("Relocation type not implemented: {0}", relocation.Type));
358
359 var section = SectionFromVirtualAddress(relocation.VirtualAddress);
360 var offset = relocation.VirtualAddress - section.VirtualAddress;
361 UInt32 value = BitConverter.ToUInt32(section.RawData, (int)offset);
362 value = (UInt32)(value + delta);
363 buffer = BitConverter.GetBytes(value);
364 Array.Copy(buffer, 0, section.RawData, offset, buffer.Length);
365
366 numRelocations += 1;
367 }
368 }
369
370 public void ResolveImports () {
371 int numImports = 0;
372
373 foreach (var import in Imports) {
374 var hModule = Win32.LoadLibrary(import.ModuleName);
375 if (hModule == IntPtr.Zero)
376 throw new Exception(String.Format("Module load failed: {0}", import.ModuleName));
377
378 try {
379 var procAddress = Win32.GetProcAddress(hModule, import.FunctionName);
380 if (procAddress == 0)
381 throw new Exception(String.Format("Unresolved import: {0}:{1}", import.ModuleName, import.FunctionName));
382
383 var section = SectionFromVirtualAddress(import.FunctionAddressDestination);
384 var offset = import.FunctionAddressDestination - section.VirtualAddress;
385 var bytes = BitConverter.GetBytes(procAddress);
386 Array.Copy(bytes, 0, section.RawData, offset, bytes.Length);
387
388 numImports += 1;
389 } finally {
390 Win32.FreeLibrary(hModule);
391 }
392 }
393 }
394
395 public Section SectionFromVirtualAddressRange (UInt32 addressBegin, UInt32 addressEnd) {
396 return Sections.Values.First(
397 (s) => (addressBegin >= s.VirtualAddress) && (addressEnd < s.VirtualAddress + s.Size)
398 );
399 }
400
401 public Section SectionFromVirtualAddress (UInt32 address) {
402 return SectionFromVirtualAddressRange(address, address);
403 }
404 }
405 }
Something went wrong with that request. Please try again.