Skip to content

Commit e56d40a

Browse files
committed
Add ability to examine exe header info, add command to set ASLR bit
1 parent c9ecf8c commit e56d40a

2 files changed

Lines changed: 97 additions & 5 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Example output:
1818
Usage: memory-mirror.exe <COMMAND>
1919
2020
Commands:
21+
info Print image info
22+
set-aslr Set the ASLR bit on the specified image
2123
list List any running processes that are available for dumping
2224
list-modules List the modules of the provided process
2325
list-regions List the memory regions of the provided process

src/main.rs

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use std::path::PathBuf;
66

77
use clap::{ArgAction, Parser, Subcommand};
88
use indicatif::ProgressIterator;
9-
use pelite::{pe32, pe64, Wrap};
9+
use pelite::image::{IMAGE_DATA_DIRECTORY, IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_TLS, IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLLCHARACTERISTICS_GUARD_CF, IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLLCHARACTERISTICS_NO_SEH, IMAGE_DLLCHARACTERISTICS_NX_COMPAT};
10+
use pelite::pe::Pe;
11+
use pelite::{pe32, pe64, FileMap, Wrap};
1012

1113
mod process;
1214

@@ -27,7 +29,25 @@ struct Args {
2729

2830
#[derive(Debug, Subcommand)]
2931
enum Commands {
30-
32+
/// Print image info.
33+
Info {
34+
/// Path to the image you want to enable/disable ASLR for.
35+
#[arg(short, long)]
36+
image: PathBuf,
37+
},
38+
/// Set the ASLR bit on the specified image.
39+
SetAslr {
40+
/// Path to the image you want to enable/disable ASLR for.
41+
#[arg(short, long)]
42+
image: PathBuf,
43+
/// Whether the ASLR bit should be on or off.
44+
#[arg(action = ArgAction::Set, long, required = true)]
45+
enabled: bool,
46+
/// Path to write the modified image to, if this isn't specified it will do it in
47+
/// in-place.
48+
#[arg(short, long)]
49+
out: Option<PathBuf>,
50+
},
3151
/// List any running processes that are available for dumping.
3252
List,
3353
/// List the modules of the provided process.
@@ -64,6 +84,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
6484
let args = Args::parse();
6585

6686
match &args.command {
87+
Commands::Info { image } => {
88+
let map = FileMap::open(image)?;
89+
let pe = pe64::PeFile::from_bytes(&map)?;
90+
print_pe(&pe)?;
91+
}
92+
Commands::SetAslr { image, enabled, out } => {
93+
let mut buffer = std::fs::read(image)?;
94+
patch_dynamic_base(&mut buffer, *enabled)?;
95+
96+
let out = out.clone().unwrap_or(image.clone());
97+
std::fs::write(out, &buffer)?;
98+
99+
println!("Patched DYNAMIC_BASE to {enabled}");
100+
}
67101
Commands::List => {
68102
for process in get_dumpable_processes().iter() {
69103
println!("{}\t{}", process.pid, process.name)
@@ -178,11 +212,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
178212
Ok(())
179213
}
180214

181-
/// Patch the PE header to make PointerToRawData point to the virtual start of the exe.
215+
fn patch_dynamic_base(buffer: &mut [u8], enabled: bool) -> pelite::Result<()> {
216+
// Make sure we're operating on a valid PE image.
217+
let _ = pelite::PeFile::from_bytes(&*buffer)?;
218+
let (_dos, nt, _dirs, _sections) = unsafe { pe64::headers_mut(buffer) };
219+
220+
if enabled {
221+
nt.OptionalHeader.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
222+
} else {
223+
nt.OptionalHeader.DllCharacteristics &= !IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
224+
}
225+
226+
Ok(())
227+
}
228+
229+
/// Patch the PE header to make PointerToRawData point to the virtual start of the image.
182230
fn patch_section_headers(buffer: &mut [u8]) -> pelite::Result<()> {
183-
let pe = pe64::PeFile::from_bytes(&*buffer)?;
231+
let _ = pe64::PeFile::from_bytes(&*buffer)?;
184232

185-
// Safety: pelite already validated `buffer` as a PE in the line above.
233+
// Safety: pelite already validated `buffer` as a PE by PeFile::from_bytes.
186234
let (_dos, _nt, _dirs, sections) = unsafe { pe64::headers_mut(buffer) };
187235

188236
for sh in sections.iter_mut() {
@@ -248,3 +296,45 @@ fn merge_ranges(mut ranges: Vec<Range<isize>>) -> Vec<Range<isize>> {
248296

249297
out
250298
}
299+
300+
fn dd_present(dd: &[IMAGE_DATA_DIRECTORY], idx: usize) -> bool {
301+
dd.get(idx)
302+
.map(|d| d.VirtualAddress != 0 && d.Size != 0)
303+
.unwrap_or(false)
304+
}
305+
306+
fn print_pe<'a, P: pelite::pe::Pe<'a>>(pe: &P) -> Result<(), Box<dyn std::error::Error>> {
307+
fn has(flags: u16, flag: u16) -> bool {
308+
(flags & flag) != 0
309+
}
310+
311+
let coff = pe.file_header();
312+
let opt = pe.optional_header();
313+
let dll = opt.DllCharacteristics;
314+
let dd = pe.data_directory();
315+
316+
let ep_rva = opt.AddressOfEntryPoint as u64;
317+
let base = opt.ImageBase;
318+
let ep_va = base + ep_rva;
319+
320+
let aslr = has(dll, IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE);
321+
let nx = has(dll, IMAGE_DLLCHARACTERISTICS_NX_COMPAT);
322+
let heva = has(dll, IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA);
323+
let cfg = has(dll, IMAGE_DLLCHARACTERISTICS_GUARD_CF);
324+
let no_seh = has(dll, IMAGE_DLLCHARACTERISTICS_NO_SEH);
325+
326+
let relocs = dd_present(dd, IMAGE_DIRECTORY_ENTRY_BASERELOC);
327+
328+
println!("x64 sec={} ts=0x{:08x}", coff.NumberOfSections, coff.TimeDateStamp);
329+
println!("base=0x{:x} ep=0x{:x} (va=0x{:x}) img=0x{:x}", base, ep_rva, ep_va, opt.SizeOfImage);
330+
println!("aslr={} relocs={} nx={} heva={} cfg={} seh={}", aslr, relocs, nx, heva, cfg, !no_seh);
331+
println!(
332+
"dirs: imp={} exp={} tls={} dbg={}",
333+
dd_present(dd, IMAGE_DIRECTORY_ENTRY_IMPORT) as u8,
334+
dd_present(dd, IMAGE_DIRECTORY_ENTRY_EXPORT) as u8,
335+
dd_present(dd, IMAGE_DIRECTORY_ENTRY_TLS) as u8,
336+
dd_present(dd, IMAGE_DIRECTORY_ENTRY_DEBUG) as u8,
337+
);
338+
339+
Ok(())
340+
}

0 commit comments

Comments
 (0)