summaryrefslogtreecommitdiff
path: root/drivers/pe_bliss/pe_base.h
blob: a84faf547c084577a49d7292eb0abd314beb810d (plain)
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
#pragma once
#include <string>
#include <vector>
#include <istream>
#include <ostream>
#include <map>
#include "pe_exception.h"
#include "pe_structures.h"
#include "utils.h"
#include "pe_section.h"
#include "pe_properties.h"

//Please don't remove this information from header
//PEBliss 1.0.0
//(c) DX 2011 - 2012, http://kaimi.ru
//Free to use for commertial and non-commertial purposes, modification and distribution

// == more important ==
//TODO: compact import rebuilder
//TODO: remove sections in the middle
//== less important ==
//TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway)
//TODO: delay import directory
//TODO: write message tables
//TODO: write string tables
//TODO: read security information
//TODO: read full .NET information

namespace pe_bliss
{
//Portable executable class
class pe_base
{
public: //CONSTRUCTORS
	//Constructor from stream
	pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true);

	//Constructor of empty PE-file
	explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui);

	pe_base(const pe_base& pe);
	pe_base& operator=(const pe_base& pe);

public:
	~pe_base();

public: //STUB
	//Strips stub MSVS overlay, if any
	void strip_stub_overlay();
	//Fills stub MSVS overlay with specified byte
	void fill_stub_overlay(char c);
	//Sets stub MSVS overlay
	void set_stub_overlay(const std::string& data);
	//Returns stub overlay contents
	const std::string& get_stub_overlay() const;


public: //DIRECTORIES
	//Returns true if directory exists
	bool directory_exists(uint32_t id) const;
	//Removes directory
	void remove_directory(uint32_t id);

	//Returns directory RVA
	uint32_t get_directory_rva(uint32_t id) const;
	//Returns directory size
	uint32_t get_directory_size(uint32_t id) const;

	//Sets directory RVA (just a value of PE header, no moving occurs)
	void set_directory_rva(uint32_t id, uint32_t rva);
	//Sets directory size (just a value of PE header, no moving occurs)
	void set_directory_size(uint32_t id, uint32_t size);

	//Strips only zero DATA_DIRECTORY entries to count = min_count
	//Returns resulting number of data directories
	//strip_iat_directory - if true, even not empty IAT directory will be stripped
	uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true);

	//Returns true if image has import directory
	bool has_imports() const;
	//Returns true if image has export directory
	bool has_exports() const;
	//Returns true if image has resource directory
	bool has_resources() const;
	//Returns true if image has security directory
	bool has_security() const;
	//Returns true if image has relocations
	bool has_reloc() const;
	//Returns true if image has TLS directory
	bool has_tls() const;
	//Returns true if image has config directory
	bool has_config() const;
	//Returns true if image has bound import directory
	bool has_bound_import() const;
	//Returns true if image has delay import directory
	bool has_delay_import() const;
	//Returns true if image has COM directory
	bool is_dotnet() const;
	//Returns true if image has exception directory
	bool has_exception_directory() const;
	//Returns true if image has debug directory
	bool has_debug() const;

	//Returns subsystem value
	uint16_t get_subsystem() const;
	//Sets subsystem value
	void set_subsystem(uint16_t subsystem);
	//Returns true if image has console subsystem
	bool is_console() const;
	//Returns true if image has Windows GUI subsystem
	bool is_gui() const;

	//Sets required operation system version
	void set_os_version(uint16_t major, uint16_t minor);
	//Returns required operation system version (minor word)
	uint16_t get_minor_os_version() const;
	//Returns required operation system version (major word)
	uint16_t get_major_os_version() const;

	//Sets required subsystem version
	void set_subsystem_version(uint16_t major, uint16_t minor);
	//Returns required subsystem version (minor word)
	uint16_t get_minor_subsystem_version() const;
	//Returns required subsystem version (major word)
	uint16_t get_major_subsystem_version() const;

public: //PE HEADER
	//Returns DOS header
	const pe_win::image_dos_header& get_dos_header() const;
	pe_win::image_dos_header& get_dos_header();

	//Returns PE header start (e_lfanew)
	int32_t get_pe_header_start() const;

	//Returns file alignment
	uint32_t get_file_alignment() const;
	//Sets file alignment, checking the correctness of its value
	void set_file_alignment(uint32_t alignment);

	//Returns size of image
	uint32_t get_size_of_image() const;

	//Returns image entry point
	uint32_t get_ep() const;
	//Sets image entry point (just a value of PE header)
	void set_ep(uint32_t new_ep);

	//Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
	uint32_t get_number_of_rvas_and_sizes() const;
	//Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
	void set_number_of_rvas_and_sizes(uint32_t number);

	//Returns PE characteristics
	uint16_t get_characteristics() const;
	//Sets PE characteristics (a value inside header)
	void set_characteristics(uint16_t ch);
	//Clears PE characteristics flag
	void clear_characteristics_flags(uint16_t flags);
	//Sets PE characteristics flag
	void set_characteristics_flags(uint16_t flags);
	//Returns true if PE characteristics flag set
	bool check_characteristics_flag(uint16_t flag) const;
	
	//Returns DLL Characteristics
	uint16_t get_dll_characteristics() const;
	//Sets DLL Characteristics
	void set_dll_characteristics(uint16_t characteristics);

	//Returns size of headers
	uint32_t get_size_of_headers() const;
	//Returns size of optional header
	uint16_t get_size_of_optional_header() const;

	//Returns PE signature
	uint32_t get_pe_signature() const;

	//Returns magic value
	uint32_t get_magic() const;

	//Returns image base for PE32 and PE64 respectively
	uint32_t get_image_base_32() const;
	void get_image_base(uint32_t& base) const;
	//Sets image base for PE32 and PE64 respectively
	uint64_t get_image_base_64() const;
	void get_image_base(uint64_t& base) const;

	//Sets new image base
	void set_image_base(uint32_t base);
	void set_image_base_64(uint64_t base);

	//Sets heap size commit for PE32 and PE64 respectively
	void set_heap_size_commit(uint32_t size);
	void set_heap_size_commit(uint64_t size);
	//Sets heap size reserve for PE32 and PE64 respectively
	void set_heap_size_reserve(uint32_t size);
	void set_heap_size_reserve(uint64_t size);
	//Sets stack size commit for PE32 and PE64 respectively
	void set_stack_size_commit(uint32_t size);
	void set_stack_size_commit(uint64_t size);
	//Sets stack size reserve for PE32 and PE64 respectively
	void set_stack_size_reserve(uint32_t size);
	void set_stack_size_reserve(uint64_t size);

	//Returns heap size commit for PE32 and PE64 respectively
	uint32_t get_heap_size_commit_32() const;
	void get_heap_size_commit(uint32_t& size) const;
	uint64_t get_heap_size_commit_64() const;
	void get_heap_size_commit(uint64_t& size) const;
	//Returns heap size reserve for PE32 and PE64 respectively
	uint32_t get_heap_size_reserve_32() const;
	void get_heap_size_reserve(uint32_t& size) const;
	uint64_t get_heap_size_reserve_64() const;
	void get_heap_size_reserve(uint64_t& size) const;
	//Returns stack size commit for PE32 and PE64 respectively
	uint32_t get_stack_size_commit_32() const;
	void get_stack_size_commit(uint32_t& size) const;
	uint64_t get_stack_size_commit_64() const;
	void get_stack_size_commit(uint64_t& size) const;
	//Returns stack size reserve for PE32 and PE64 respectively
	uint32_t get_stack_size_reserve_32() const;
	void get_stack_size_reserve(uint32_t& size) const;
	uint64_t get_stack_size_reserve_64() const;
	void get_stack_size_reserve(uint64_t& size) const;

	//Updates virtual size of image corresponding to section virtual sizes
	void update_image_size();

	//Returns checksum of PE file from header
	uint32_t get_checksum() const;
	//Sets checksum of PE file
	void set_checksum(uint32_t checksum);
	
	//Returns timestamp of PE file from header
	uint32_t get_time_date_stamp() const;
	//Sets timestamp of PE file
	void set_time_date_stamp(uint32_t timestamp);
	
	//Returns Machine field value of PE file from header
	uint16_t get_machine() const;
	//Sets Machine field value of PE file
	void set_machine(uint16_t machine);

	//Returns data from the beginning of image
	//Size = SizeOfHeaders
	const std::string& get_full_headers_data() const;
	
	typedef std::multimap<uint32_t, std::string> debug_data_list;
	//Returns raw list of debug data
	const debug_data_list& get_raw_debug_data_list() const;
	
	//Reads and checks DOS header
	static void read_dos_header(std::istream& file, pe_win::image_dos_header& header);
	
	//Returns sizeof() nt headers
	uint32_t get_sizeof_nt_header() const;
	//Returns sizeof() optional headers
	uint32_t get_sizeof_opt_headers() const;
	//Returns raw nt headers data pointer
	const char* get_nt_headers_ptr() const;
	
	//Sets size of headers (to NT headers)
	void set_size_of_headers(uint32_t size);
	//Sets size of optional headers (to NT headers)
	void set_size_of_optional_header(uint16_t size);
	
	//Sets base of code
	void set_base_of_code(uint32_t base);
	//Returns base of code
	uint32_t get_base_of_code() const;

public: //ADDRESS CONVERTIONS
	//Virtual Address (VA) to Relative Virtual Address (RVA) convertions
	//for PE32 and PE64 respectively
	//bound_check checks integer overflow
	uint32_t va_to_rva(uint32_t va, bool bound_check = true) const;
	uint32_t va_to_rva(uint64_t va, bool bound_check = true) const;

	//Relative Virtual Address (RVA) to Virtual Address (VA) convertions
	//for PE32 and PE64 respectively
	uint32_t rva_to_va_32(uint32_t rva) const;
	void rva_to_va(uint32_t rva, uint32_t& va) const;
	uint64_t rva_to_va_64(uint32_t rva) const;
	void rva_to_va(uint32_t rva, uint64_t& va) const;

	//RVA to RAW file offset convertion (4gb max)
	uint32_t rva_to_file_offset(uint32_t rva) const;
	//RAW file offset to RVA convertion (4gb max)
	uint32_t file_offset_to_rva(uint32_t offset) const;

	//RVA from section raw data offset
	static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start);

public: //IMAGE SECTIONS
	//Returns number of sections from PE header
	uint16_t get_number_of_sections() const;

	//Updates number of sections in PE header
	uint16_t update_number_of_sections();

	//Returns section alignment
	uint32_t get_section_alignment() const;

	//Returns section list
	section_list& get_image_sections();
	const section_list& get_image_sections() const;

	//Realigns all sections, if you made any changes to sections or alignments
	void realign_all_sections();
	//Resligns section with specified index
	void realign_section(uint32_t index);

	//Returns section from RVA inside it
	section& section_from_rva(uint32_t rva);
	const section& section_from_rva(uint32_t rva) const;
	//Returns section from directory ID
	section& section_from_directory(uint32_t directory_id);
	const section& section_from_directory(uint32_t directory_id) const;
	//Returns section from VA inside it for PE32 and PE64 respectively
	section& section_from_va(uint32_t va);
	const section& section_from_va(uint32_t va) const;
	section& section_from_va(uint64_t va);
	const section& section_from_va(uint64_t va) const;
	//Returns section from file offset (4gb max)
	section& section_from_file_offset(uint32_t offset);
	const section& section_from_file_offset(uint32_t offset) const;

	//Returns section TOTAL RAW/VIRTUAL data length from RVA inside section
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	uint32_t section_data_length_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 and PE64 respectively
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	uint32_t section_data_length_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;

	//Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds)
	uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const;
	//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds)
	uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const;
	uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const;

	//Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva"
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	uint32_t section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 and PE64 respectively
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	uint32_t section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	uint32_t section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	//Returns corresponding section data pointer from RVA inside section
	char* section_data_from_rva(uint32_t rva, bool include_headers = false);
	const char* section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	//Returns corresponding section data pointer from VA inside section for PE32 and PE64 respectively
	char* section_data_from_va(uint32_t va, bool include_headers = false);
	const char* section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
	char* section_data_from_va(uint64_t va, bool include_headers = false);
	const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;

	//Returns corresponding section data pointer from RVA inside section "s" (checks bounds)
	char* section_data_from_rva(section& s, uint32_t rva);
	const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const;
	//Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds)
	char* section_data_from_va(section& s, uint32_t va); //Always returns raw data
	const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const;
	char* section_data_from_va(section& s, uint64_t va); //Always returns raw data
	const char* section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const;

	//Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function)
	template<typename T>
	T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const
	{
		if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T)))
		{
			const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment());
			//Don't check for underflow here, comparsion is unsigned
			if(data.size() < rva - s.get_virtual_address() + sizeof(T))
				throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists);

			return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address());
		}

		throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
	}

	//Returns corresponding section data pointer from RVA inside section (checks rva, checks sizes, the most safe function)
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	template<typename T>
	T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const
	{
		//if RVA is inside of headers and we're searching them too...
		if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length()))
			return *reinterpret_cast<const T*>(&full_headers_data_[rva]);

		const section& s = section_from_rva(rva);
		const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment());
		//Don't check for underflow here, comparsion is unsigned
		if(data.size() < rva - s.get_virtual_address() + sizeof(T))
			throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists);

		return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address());
	}

	//Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function)
	template<typename T>
	T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const
	{
		return section_data_from_rva<T>(s, va_to_rva(va), datatype);
	}

	template<typename T>
	T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const
	{
		return section_data_from_rva<T>(s, va_to_rva(va), datatype);
	}

	//Returns corresponding section data pointer from VA inside section (checks rva, checks sizes, the most safe function)
	//If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
	template<typename T>
	T section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const
	{
		return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers);
	}

	template<typename T>
	T section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const
	{
		return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers);
	}

	//Returns section and offset (raw data only) from its start from RVA
	const std::pair<uint32_t, const section*> section_and_offset_from_rva(uint32_t rva) const;

	//Sets virtual size of section "s"
	//Section must be free (not bound to any image)
	//or the last section of this image
	//Function calls update_image_size automatically in second case
	void set_section_virtual_size(section& s, uint32_t vsize);

	//Represents section expand type for expand_section function
	enum section_expand_type
	{
		expand_section_raw, //Section raw data size will be expanded
		expand_section_virtual //Section virtual data size will be expanded
	};

	//Expands section raw or virtual size to hold data from specified RVA with specified size
	//Section must be free (not bound to any image)
	//or the last section of this image
	//Returns true if section was expanded
	bool expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand);

	//Adds section to image
	//Returns last section
	section& add_section(section s);
	//Prepares section to later add it to image (checks and recalculates virtual and raw section size)
	//Section must be prepared by this function before calling add_section
	void prepare_section(section& s);

	//Returns true if sectios "s" is already attached to this PE file
	bool section_attached(const section& s) const;


public: //IMAGE
	//Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks)
	static pe_type get_pe_type(std::istream& file);
	//Returns PE type of this image
	pe_type get_pe_type() const;

	//Returns true if image has overlay data at the end of file
	bool has_overlay() const;

	//Realigns file (changes file alignment)
	void realign_file(uint32_t new_file_alignment);
	
	//Helper function to recalculate RAW and virtual section sizes and strip it, if necessary
	//auto_strip = strip section, if necessary
	void recalculate_section_sizes(section& s, bool auto_strip);

	// ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== //
private:
	//Image DOS header
	pe_win::image_dos_header dos_header_;
	//Rich (stub) overlay data (for MSVS)
	std::string rich_overlay_;
	//List of image sections
	section_list sections_;
	//True if image has overlay
	bool has_overlay_;
	//Raw SizeOfHeaders-sized data from the beginning of image
	std::string full_headers_data_;
	//Raw debug data for all directories
	//PointerToRawData; Data
	debug_data_list debug_data_;
	//PE or PE+ related properties
	pe_properties* props_;

	//Reads and checks DOS header
	void read_dos_header(std::istream& file);

	//Reads and checks PE headers and section headers, data
	void read_pe(std::istream& file, bool read_debug_raw_data);

	//Sets number of sections
	void set_number_of_sections(uint16_t number);
	//Sets size of image
	void set_size_of_image(uint32_t size);
	//Sets file alignment (no checks)
	void set_file_alignment_unchecked(uint32_t alignment);
	//Returns needed magic of image
	uint32_t get_needed_magic() const;
	//Returns nt headers data pointer
	char* get_nt_headers_ptr();

private:
	static const uint16_t maximum_number_of_sections = 0x60;
	static const uint32_t minimum_file_alignment = 512;

private:
	//RAW file offset to section convertion helpers (4gb max)
	section_list::const_iterator file_offset_to_section(uint32_t offset) const;
	section_list::iterator file_offset_to_section(uint32_t offset);
};
}