pe_assembler/helpers/pe_writer/
mod.rs1use crate::types::{DataDirectory, DosHeader, NtHeader, OptionalHeader, PeHeader, PeProgram, PeSection, SubsystemType};
6use byteorder::{LittleEndian, WriteBytesExt};
7use gaia_types::GaiaError;
8use std::io::{Seek, Write};
9
10pub trait PeWriter<W: Write + Seek> {
12 fn get_writer(&mut self) -> &mut W;
14
15 fn stream_position(&mut self) -> Result<u64, GaiaError> {
17 Ok(self.get_writer().stream_position()?)
18 }
19
20 fn write_program(&mut self, program: &PeProgram) -> Result<(), GaiaError> {
22 self.write_dos_header(&program.header.dos_header)?;
24
25 self.write_dos_stub()?;
27
28 let pe_header_offset = program.header.dos_header.e_lfanew as u64;
30 self.pad_to_offset(pe_header_offset)?;
31
32 self.write_nt_header(&program.header.nt_header)?;
34
35 self.write_coff_header(&program.header.coff_header)?;
37
38 self.write_optional_header(&program.header.optional_header)?;
40
41 for section in &program.sections {
43 self.write_section_header(section)?;
44 }
45
46 let file_alignment = program.header.optional_header.file_alignment;
48 self.align_to_boundary(file_alignment)?;
49
50 for section in &program.sections {
52 if !section.data.is_empty() {
53 self.pad_to_offset(section.pointer_to_raw_data as u64)?;
55 self.get_writer().write_all(§ion.data)?;
56
57 self.align_to_boundary(file_alignment)?;
59 }
60 }
61
62 let pointer_size: usize = if program.header.optional_header.magic == 0x020B { 8 } else { 4 };
64 self.write_import_table(&program.imports, &program.sections, pointer_size)?;
65
66 Ok(())
67 }
68
69 fn write_dos_header(&mut self, dos_header: &DosHeader) -> Result<(), GaiaError> {
71 let writer = self.get_writer();
72 writer.write_u16::<LittleEndian>(dos_header.e_magic)?;
73 writer.write_u16::<LittleEndian>(dos_header.e_cblp)?;
74 writer.write_u16::<LittleEndian>(dos_header.e_cp)?;
75 writer.write_u16::<LittleEndian>(dos_header.e_crlc)?;
76 writer.write_u16::<LittleEndian>(dos_header.e_cparhdr)?;
77 writer.write_u16::<LittleEndian>(dos_header.e_min_allocate)?;
78 writer.write_u16::<LittleEndian>(dos_header.e_max_allocate)?;
79 writer.write_u16::<LittleEndian>(dos_header.e_ss)?;
80 writer.write_u16::<LittleEndian>(dos_header.e_sp)?;
81 writer.write_u16::<LittleEndian>(dos_header.e_check_sum)?;
82 writer.write_u16::<LittleEndian>(dos_header.e_ip)?;
83 writer.write_u16::<LittleEndian>(dos_header.e_cs)?;
84 writer.write_u16::<LittleEndian>(dos_header.e_lfarlc)?;
85 writer.write_u16::<LittleEndian>(dos_header.e_ovno)?;
86 for &res in &dos_header.e_res {
87 writer.write_u16::<LittleEndian>(res)?;
88 }
89 writer.write_u16::<LittleEndian>(dos_header.e_oem_id)?;
90 writer.write_u16::<LittleEndian>(dos_header.e_oem_info)?;
91 for &res in &dos_header.e_res2 {
92 writer.write_u16::<LittleEndian>(res)?;
93 }
94 writer.write_u32::<LittleEndian>(dos_header.e_lfanew)?;
95 Ok(())
96 }
97
98 fn write_dos_stub(&mut self) -> Result<(), GaiaError> {
100 let dos_stub = b"This program cannot be run in DOS mode.\r\n$";
102 self.get_writer().write_all(dos_stub)?;
103 while self.stream_position()? < 0x80 {
105 self.get_writer().write_u8(0)?;
106 }
107 Ok(())
108 }
109
110 fn write_nt_header(&mut self, nt_header: &NtHeader) -> Result<(), GaiaError> {
112 self.get_writer().write_u32::<LittleEndian>(nt_header.signature)?;
113 Ok(())
114 }
115
116 fn write_coff_header(&mut self, coff_header: &crate::types::coff::CoffHeader) -> Result<(), GaiaError> {
118 let writer = self.get_writer();
119 writer.write_u16::<LittleEndian>(coff_header.machine)?;
120 writer.write_u16::<LittleEndian>(coff_header.number_of_sections)?;
121 writer.write_u32::<LittleEndian>(coff_header.time_date_stamp)?;
122 writer.write_u32::<LittleEndian>(coff_header.pointer_to_symbol_table)?;
123 writer.write_u32::<LittleEndian>(coff_header.number_of_symbols)?;
124 writer.write_u16::<LittleEndian>(coff_header.size_of_optional_header)?;
125 writer.write_u16::<LittleEndian>(coff_header.characteristics)?;
126 Ok(())
127 }
128
129 fn write_optional_header(&mut self, optional_header: &OptionalHeader) -> Result<(), GaiaError> {
131 let writer = self.get_writer();
132 writer.write_u16::<LittleEndian>(optional_header.magic)?;
133 writer.write_u8(optional_header.major_linker_version)?;
134 writer.write_u8(optional_header.minor_linker_version)?;
135 writer.write_u32::<LittleEndian>(optional_header.size_of_code)?;
136 writer.write_u32::<LittleEndian>(optional_header.size_of_initialized_data)?;
137 writer.write_u32::<LittleEndian>(optional_header.size_of_uninitialized_data)?;
138 writer.write_u32::<LittleEndian>(optional_header.address_of_entry_point)?;
139 writer.write_u32::<LittleEndian>(optional_header.base_of_code)?;
140
141 if optional_header.magic == 0x20b {
143 writer.write_u64::<LittleEndian>(optional_header.image_base)?;
145 } else {
146 let base_of_data = optional_header.base_of_data.unwrap_or(0);
148 writer.write_u32::<LittleEndian>(base_of_data)?;
149 writer.write_u32::<LittleEndian>(optional_header.image_base as u32)?;
150 }
151
152 writer.write_u32::<LittleEndian>(optional_header.section_alignment)?;
153 writer.write_u32::<LittleEndian>(optional_header.file_alignment)?;
154 writer.write_u16::<LittleEndian>(optional_header.major_operating_system_version)?;
155 writer.write_u16::<LittleEndian>(optional_header.minor_operating_system_version)?;
156 writer.write_u16::<LittleEndian>(optional_header.major_image_version)?;
157 writer.write_u16::<LittleEndian>(optional_header.minor_image_version)?;
158 writer.write_u16::<LittleEndian>(optional_header.major_subsystem_version)?;
159 writer.write_u16::<LittleEndian>(optional_header.minor_subsystem_version)?;
160 writer.write_u32::<LittleEndian>(optional_header.win32_version_value)?;
161 writer.write_u32::<LittleEndian>(optional_header.size_of_image)?;
162 writer.write_u32::<LittleEndian>(optional_header.size_of_headers)?;
163 writer.write_u32::<LittleEndian>(optional_header.checksum)?;
164
165 let subsystem_value = match optional_header.subsystem {
167 SubsystemType::Console => 3,
168 SubsystemType::Windows => 2,
169 SubsystemType::Native => 1,
170 _ => 3, };
172 writer.write_u16::<LittleEndian>(subsystem_value)?;
173
174 writer.write_u16::<LittleEndian>(optional_header.dll_characteristics)?;
175
176 if optional_header.magic == 0x20b {
178 writer.write_u64::<LittleEndian>(optional_header.size_of_stack_reserve)?;
180 writer.write_u64::<LittleEndian>(optional_header.size_of_stack_commit)?;
181 writer.write_u64::<LittleEndian>(optional_header.size_of_heap_reserve)?;
182 writer.write_u64::<LittleEndian>(optional_header.size_of_heap_commit)?;
183 } else {
184 writer.write_u32::<LittleEndian>(optional_header.size_of_stack_reserve as u32)?;
186 writer.write_u32::<LittleEndian>(optional_header.size_of_stack_commit as u32)?;
187 writer.write_u32::<LittleEndian>(optional_header.size_of_heap_reserve as u32)?;
188 writer.write_u32::<LittleEndian>(optional_header.size_of_heap_commit as u32)?;
189 }
190
191 writer.write_u32::<LittleEndian>(optional_header.loader_flags)?;
192 writer.write_u32::<LittleEndian>(optional_header.number_of_rva_and_sizes)?;
193
194 for data_dir in &optional_header.data_directories {
196 self.write_data_directory(data_dir)?;
197 }
198
199 Ok(())
200 }
201
202 fn write_data_directory(&mut self, data_dir: &DataDirectory) -> Result<(), GaiaError> {
204 let writer = self.get_writer();
205 writer.write_u32::<LittleEndian>(data_dir.virtual_address)?;
206 writer.write_u32::<LittleEndian>(data_dir.size)?;
207 Ok(())
208 }
209
210 fn write_section_header(&mut self, section: &PeSection) -> Result<(), GaiaError> {
212 let writer = self.get_writer();
213 let mut name_bytes = [0u8; 8];
215 let name_len = section.name.len().min(8);
216 name_bytes[..name_len].copy_from_slice(§ion.name.as_bytes()[..name_len]);
217 writer.write_all(&name_bytes)?;
218
219 writer.write_u32::<LittleEndian>(section.virtual_size)?;
220 writer.write_u32::<LittleEndian>(section.virtual_address)?;
221 writer.write_u32::<LittleEndian>(section.size_of_raw_data)?;
222 writer.write_u32::<LittleEndian>(section.pointer_to_raw_data)?;
223 writer.write_u32::<LittleEndian>(section.pointer_to_relocations)?;
224 writer.write_u32::<LittleEndian>(section.pointer_to_line_numbers)?;
225 writer.write_u16::<LittleEndian>(section.number_of_relocations)?;
226 writer.write_u16::<LittleEndian>(section.number_of_line_numbers)?;
227 writer.write_u32::<LittleEndian>(section.characteristics)?;
228 Ok(())
229 }
230
231 fn pad_to_offset(&mut self, target_offset: u64) -> Result<(), GaiaError> {
233 let current_pos = self.stream_position()?;
234 if current_pos < target_offset {
235 let padding_size = target_offset - current_pos;
236 for _ in 0..padding_size {
237 self.get_writer().write_u8(0)?;
238 }
239 }
240 Ok(())
241 }
242
243 fn align_to_boundary(&mut self, alignment: u32) -> Result<(), GaiaError> {
245 let current_pos = self.stream_position()?;
246 let remainder = current_pos % alignment as u64;
247 if remainder != 0 {
248 let padding = alignment as u64 - remainder;
249 for _ in 0..padding {
250 self.get_writer().write_u8(0)?;
251 }
252 }
253 Ok(())
254 }
255
256 fn write_import_table(
258 &mut self,
259 imports: &crate::types::tables::ImportTable,
260 sections: &[PeSection],
261 pointer_size: usize,
262 ) -> Result<(), GaiaError> {
263 if imports.entries.is_empty() {
265 return Ok(());
266 }
267
268 let idata_section = sections.iter().find(|s| s.name == ".idata");
270 if let Some(section) = idata_section {
271 self.pad_to_offset(section.pointer_to_raw_data as u64)?;
273
274 let base_rva = section.virtual_address;
275 let mut current_rva = base_rva + ((imports.entries.len() + 1) * 20) as u32; let mut dll_name_rvas = Vec::new();
279 for entry in &imports.entries {
280 dll_name_rvas.push(current_rva);
281 current_rva += (entry.dll_name.len() + 1) as u32; }
283
284 if current_rva % 2 != 0 {
286 current_rva += 1;
287 }
288
289 let mut function_name_rvas = Vec::new();
291 for entry in &imports.entries {
292 let mut entry_function_rvas = Vec::new();
293 for function in &entry.functions {
294 entry_function_rvas.push(current_rva);
295 current_rva += (2 + function.len() + 1) as u32; }
297 function_name_rvas.push(entry_function_rvas);
298 }
299
300 if current_rva % 2 != 0 {
302 current_rva += 1;
303 }
304
305 if current_rva % (pointer_size as u32) != 0 {
309 current_rva = (current_rva + (pointer_size as u32) - 1) & !((pointer_size as u32) - 1);
310 }
311
312 let mut int_rvas = Vec::new();
313 for entry in &imports.entries {
314 int_rvas.push(current_rva);
315 current_rva += ((entry.functions.len() as u32) * (pointer_size as u32) + (pointer_size as u32));
316 }
317
318 if current_rva % (pointer_size as u32) != 0 {
320 current_rva = (current_rva + (pointer_size as u32) - 1) & !((pointer_size as u32) - 1);
321 }
322
323 let mut iat_rvas = Vec::new();
324 for entry in &imports.entries {
325 iat_rvas.push(current_rva);
326 current_rva += ((entry.functions.len() as u32) * (pointer_size as u32) + (pointer_size as u32));
327 }
328
329 for (i, _entry) in imports.entries.iter().enumerate() {
331 let writer = self.get_writer();
332 if pointer_size == 8 {
335 writer.write_u32::<LittleEndian>(int_rvas[i])?; } else {
337 writer.write_u32::<LittleEndian>(0)?; }
339 writer.write_u32::<LittleEndian>(0)?; writer.write_u32::<LittleEndian>(0)?; writer.write_u32::<LittleEndian>(dll_name_rvas[i])?; writer.write_u32::<LittleEndian>(iat_rvas[i])?; }
344
345 {
347 let writer = self.get_writer();
348 for _ in 0..5 {
349 writer.write_u32::<LittleEndian>(0)?;
350 }
351 }
352
353 for entry in &imports.entries {
355 let writer = self.get_writer();
356 writer.write_all(entry.dll_name.as_bytes())?;
357 writer.write_u8(0)?; }
359
360 if self.stream_position()? % 2 != 0 {
362 self.get_writer().write_u8(0)?;
363 }
364
365 for (_i, entry) in imports.entries.iter().enumerate() {
367 for (_j, function) in entry.functions.iter().enumerate() {
368 let writer = self.get_writer();
369 writer.write_u16::<LittleEndian>(0)?; writer.write_all(function.as_bytes())?;
371 writer.write_u8(0)?; }
373 }
374
375 if self.stream_position()? % 2 != 0 {
377 self.get_writer().write_u8(0)?;
378 }
379
380 while self.stream_position()? % (pointer_size as u64) != 0 {
383 self.get_writer().write_u8(0)?;
384 }
385 for (i, entry) in imports.entries.iter().enumerate() {
386 for j in 0..entry.functions.len() {
387 let writer = self.get_writer();
388 if pointer_size == 8 {
389 writer.write_u64::<LittleEndian>(function_name_rvas[i][j] as u64)?;
390 } else {
391 writer.write_u32::<LittleEndian>(function_name_rvas[i][j])?;
392 }
393 }
394 let writer = self.get_writer();
395 if pointer_size == 8 {
396 writer.write_u64::<LittleEndian>(0)?;
397 } else {
398 writer.write_u32::<LittleEndian>(0)?;
399 }
400 }
401
402 while self.stream_position()? % (pointer_size as u64) != 0 {
406 self.get_writer().write_u8(0)?;
407 }
408 for (i, entry) in imports.entries.iter().enumerate() {
409 for _j in 0..entry.functions.len() {
410 let writer = self.get_writer();
411 if pointer_size == 8 {
412 writer.write_u64::<LittleEndian>(0)?;
414 } else {
415 writer.write_u32::<LittleEndian>(function_name_rvas[i][_j])?;
417 }
418 }
419 let writer = self.get_writer();
421 if pointer_size == 8 {
422 writer.write_u64::<LittleEndian>(0)?;
423 } else {
424 writer.write_u32::<LittleEndian>(0)?;
425 }
426 }
427
428 self.align_to_boundary(section.size_of_raw_data)?;
430 }
431
432 Ok(())
433 }
434}