///|
pub fn PEImage::security_diagnostics(self : Self) -> Array[Diagnostic] {
let diagnostics = []
let load_config = self.load_config() catch { PeError(_, _) => None }
if !has_bit(self.dll_characteristics, 0x0040) {
diagnostics.push({
severity: Warning,
code: "pe.no-aslr",
message: "DYNAMIC_BASE is not set; the image does not opt into ASLR",
})
}
if !has_bit(self.dll_characteristics, 0x0100) {
diagnostics.push({
severity: Warning,
code: "pe.no-nx",
message: "NX_COMPAT is not set; executable memory may remain writable",
})
}
if !has_bit(self.dll_characteristics, 0x4000) {
diagnostics.push({
severity: Warning,
code: "pe.no-cfg",
message: "GUARD_CF is not set; Control Flow Guard metadata is absent",
})
} else {
let guard_ready = match load_config {
Some(config) =>
config.guard_cf_function_count.unwrap_or(0) > 0 &&
config.guard_cf_function_table.unwrap_or(0UL) != 0UL
None => false
}
if !guard_ready {
diagnostics.push({
severity: Warning,
code: "pe.cfg-metadata-missing",
message: "GUARD_CF is set but load-config has no guard function table",
})
}
}
if self.is_64_bit() && !has_bit(self.dll_characteristics, 0x0020) {
diagnostics.push({
severity: Info,
code: "pe.no-high-entropy-va",
message: "HIGH_ENTROPY_VA is not set for this 64-bit image",
})
}
for section in self.sections {
if has_bit(section.characteristics.to_int(), 0x80000000) &&
has_bit(section.characteristics.to_int(), 0x20000000) {
diagnostics.push({
severity: Critical,
code: "pe.rwx-section",
message: "section '\{section.name}' is both writable and executable",
})
}
}
if self.entry_point != 0UL {
let mut executable_entry = false
for section in self.sections {
let start = section.virtual_address
let span = if section.virtual_size > section.raw_size {
section.virtual_size
} else {
section.raw_size
}
if self.entry_point >= start &&
self.entry_point < start + span.to_uint64() &&
has_bit(section.characteristics.to_int(), 0x20000000) {
executable_entry = true
}
}
if !executable_entry {
diagnostics.push({
severity: Warning,
code: "pe.entrypoint-not-executable",
message: "AddressOfEntryPoint is not inside an executable section",
})
}
}
if has_bit(self.dll_characteristics, 0x0040) {
let has_relocations = match self.directory(5) {
Some(directory) => directory.virtual_address != 0UL && directory.size > 0
None => false
}
if !has_relocations {
diagnostics.push({
severity: Warning,
code: "pe.aslr-without-relocations",
message: "DYNAMIC_BASE is set but no base-relocation directory is present",
})
}
}
if !self.is_64_bit() && !has_bit(self.dll_characteristics, 0x0400) {
let safe_seh = match load_config {
Some(config) =>
config.se_handler_count.unwrap_or(0) > 0 &&
config.se_handler_table.unwrap_or(0UL) != 0UL
None => false
}
if !safe_seh {
diagnostics.push({
severity: Warning,
code: "pe.no-safeseh",
message: "32-bit image has neither NO_SEH nor SafeSEH metadata",
})
}
}
match load_config {
Some(config) if config.security_cookie.unwrap_or(0UL) == 0UL =>
diagnostics.push({
severity: Warning,
code: "pe.no-security-cookie",
message: "load-config does not declare a /GS security cookie",
})
_ => ()
}
let signed = match self.directory(4) {
Some(directory) => directory.virtual_address != 0UL && directory.size != 0
None => false
}
if signed {
diagnostics.push({
severity: Info,
code: "pe.certificate-table",
message: "certificate table is present; this tool does not validate signatures",
})
} else {
diagnostics.push({
severity: Warning,
code: "pe.unsigned",
message: "no Authenticode certificate table is present",
})
}
diagnostics
}
///|
fn has_bit(value : Int32, mask : Int32) -> Bool {
(value & mask) != 0
}