-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathPE.cs
266 lines (215 loc) · 8.86 KB
/
PE.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
* Serana - Copyright (c) 2018 - 2020 r0da [r0da@protonmail.ch]
*
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to
* Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
*
* By using Serana, you agree to the above license and its terms.
*
* Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were
* made. You must do so in any reasonable manner, but not in any way that suggests the licensor
* endorses you or your use.
*
* Non-Commercial - You may not use the material (Serana) for commercial purposes.
*
* No-Derivatives - If you remix, transform, or build upon the material (Serana), you may not distribute the
* modified material. You are, however, allowed to submit the modified works back to the original
* Serana project in attempt to have it added to the original project.
*
* You may not apply legal terms or technological measures that legally restrict others
* from doing anything the license permits.
*
* No warranties are given.
*/
using Serana.Engine.Headers;
using Serana.Engine.Headers.Types;
using Serana.Engine.Resource;
using Serana.Engine.Section;
using Serana.Engine.Streams;
using Serana.Engine.Exceptions;
using Serana.Engine.Import;
using System.Collections.Generic;
using System.IO;
using System;
namespace Serana.Engine
{
public class PE
{
/// <summary>
/// True if the PE object is not loaded from a file
/// </summary>
public readonly bool isMemoryPE;
/// <summary>
/// The header object of the PE file
/// </summary>
public Header header;
public Sections sections;
public Imports imports;
public Resources resources;
private Reader reader;
public readonly string fileName;
// TODO : BIG FILES
public readonly int endOfData;
/// <summary>
/// True if there is more data after the sections
/// </summary>
public readonly bool EOFOverflow = false;
/// <summary>
/// Size of the overflow data
/// TODO : BIG FILES
/// </summary>
public readonly int overflowSize = 0;
/// <summary>
/// Create a PE object from a file
/// </summary>
/// <param name="filePath"></param>
public PE(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("Fail to open the executable");
this.fileName = new FileInfo(filePath).Name;
this.reader = new Reader(filePath);
this.header = new Header(this.reader);
this.sections = new Sections(this.reader, this.header);
// some times, executable could be import less (nasm compiled asm)
// so idk how to handle this ...
// throw new Exception("The import address table is not in any sections");
if (isImportPresent())
this.imports = new Imports(this.reader, this.header, this.sections);
if(isResourcePresent())
this.resources = new Resources(this.reader, this.header, this.sections);
SectionEntry lastEntry = this.sections.getLastEntry();
// calculate the EOF address
this.endOfData = lastEntry.header.pointerToRawData.getValue() + lastEntry.header.sizeOfRawData.getValue();
// check if there is more data
this.EOFOverflow = this.endOfData != (int)this.reader.size();
if (this.EOFOverflow)
overflowSize = (int)this.reader.size() - this.endOfData;
this.isMemoryPE = false;
}
/// <summary>
/// Indicate if import information exists
/// </summary>
/// <returns>True if import directory is valid</returns>
public bool isImportPresent()
{
var importVirtualAddress = this.header.dataDirectoryHeader.importTableAddressDirectory.getVirtualAddress();
// get the section that content the import address table
SectionEntry importAddressTableSection = this.sections.getSectionFromVirtualAddress(importVirtualAddress);
return importAddressTableSection != null;
}
/// <summary>
/// Create a PE object from memory
/// </summary>
/// <param name="baseOfCode"></param>
/// <param name="baseOfData"></param>
/// <param name="fileAlignment"></param>
/// <param name="sectionAlignment"></param>
public PE(int baseOfCode, int baseOfData, int fileAlignment, int sectionAlignment)
{
if (baseOfCode > baseOfData)
throw new Exception("baseOfCode is upper than baseOfData");
if (sectionAlignment < fileAlignment)
throw new Exception("fileAlignment is upper than sectionAlignment");
this.isMemoryPE = true;
// create a default header
this.header = new Header(baseOfCode, baseOfData, fileAlignment, sectionAlignment);
this.sections = new Sections(this.header);
// TODO : handle imports
//this.imports = new Imports(this.reader, this.header, this.sections);
// TODO : handle resources
//this.resources = new Resources(this.reader, this.header, this.sections);
}
/// <summary>
/// Indicate if ASLR is enable in the file
/// </summary>
/// <returns>True if ASLR is enable</returns>
public bool isASLR()
{
return ((this.header.optionalHeader.DLLCharacteristics.getValue()) & (int)DllCharacteristics.DYNAMIC_BASE) > 0;
}
/// <summary>
/// Indicate if DEP is enable in the file
/// </summary>
/// <returns>True if DEP is enable</returns>
public bool isDEP()
{
return ((this.header.optionalHeader.DLLCharacteristics.getValue()) & (int)DllCharacteristics.NX_COMPAT) > 0;
}
/// <summary>
/// Indicate if there is resources in the file
/// </summary>
/// <returns>True if resources is the executable</returns>
public bool isResourcePresent()
{
return this.header.dataDirectoryHeader.resourceDirectory.getVirtualAddress() > 0
&& this.header.dataDirectoryHeader.resourceDirectory.getSize() > 0;
}
/// <summary>
/// Indicate if the executable is .NET
/// </summary>
/// <returns>True if is .NET</returns>
public bool isDOTNET()
{
return this.header.dataDirectoryHeader.netHeaderDirectory.getVirtualAddress() > 0
&& this.header.dataDirectoryHeader.netHeaderDirectory.getSize() > 0;
}
/// <summary>
/// Indicate if PE is 32 bit
/// NOTE : Simple proxy
/// </summary>
/// <returns>True if is 32 bit</returns>
public bool is32Bit()
{
return this.header.is32Bit;
}
/// <summary>
/// Set and calculate the file checksum
/// </summary>
public void fixChecksum()
{
// TODO
}
/// <summary>
/// Export raw bytes if the executable
/// </summary>
/// <returns>Array of byte representing the executable</returns>
public List<byte> export()
{
List<byte> peBuffer = new List<byte>();
// adding Commun Headers
Utils.addArrayToList<byte>(peBuffer, this.header.export().ToArray());
// adding Section Headers
Utils.addArrayToList<byte>(peBuffer, this.sections.exportHeaders().ToArray());
// TODO : handle imports
// TODO : handle resources
// adding Sections
Utils.addArrayToList<byte>(peBuffer, this.sections.exportSectionsData().ToArray());
// adding overflowed data
if(EOFOverflow)
Utils.addArrayToList<byte>(peBuffer, this.overflowData());
return peBuffer;
}
/// <summary>
/// Return overflowed data
/// </summary>
/// <returns>Array of byte of overflowed data</returns>
public byte[] overflowData()
{
if (!this.EOFOverflow)
throw new NoOverflowDataException();
if (this.isMemoryPE)
throw new Exception("Overflow data is not yet supported");
return this.reader.readBytes(this.endOfData, this.overflowSize);
}
/// <summary>
/// Dispose the stream
/// </summary>
public void Dispose()
{
if(!this.isMemoryPE)
this.reader.Dispose();
}
}
}