///|
/// The application manifest (`RT_MANIFEST`, resource type 24).
///
/// The manifest is where an executable declares its UAC level, DPI awareness and
/// side-by-side dependencies, so reading it turns "is this app going to ask for
/// administrator rights" into a string comparison.
///|
/// The raw manifest text, if the image carries one.
pub fn Pe::manifest(self : Pe) -> String? raise PeError {
let resources = self.resources()
for entry in resources {
let is_manifest = match entry.type_id {
Some(id) => id == 24
None =>
match entry.type_name {
Some(name) => name == "MANIFEST"
None => false
}
}
if is_manifest {
match self.rva_to_offset(entry.rva) {
Some(offset) => {
let end = offset + entry.size
if end <= self.bytes.length() {
return Some(decode_utf8(self.bytes, offset, end))
}
return None
}
None => return None
}
}
}
None
}
///|
/// The `requestedExecutionLevel` value from the manifest, e.g. `asInvoker`,
/// `highestAvailable` or `requireAdministrator`.
pub fn Pe::manifest_execution_level(self : Pe) -> String? raise PeError {
match self.manifest() {
None => None
Some(text) => extract_attribute(text, "requestedExecutionLevel", "level")
}
}
///|
/// Find `needle`, then the `attribute="value"` that follows it.
fn extract_attribute(
text : String,
needle : String,
attribute : String,
) -> String? {
let chars : Array[Char] = []
for c in text {
chars.push(c)
}
let start = find_text(chars, needle, 0)
if start < 0 {
return None
}
let marker = attribute + "=\""
let attr_start = find_text(chars, marker, start)
if attr_start < 0 {
return None
}
let value_start = attr_start + marker.length()
let mut i = value_start
let sb = StringBuilder()
while i < chars.length() && chars[i] != '"' {
sb.write_char(chars[i])
i = i + 1
}
Some(sb.to_string())
}
///|
fn find_text(chars : Array[Char], needle : String, from : Int32) -> Int32 {
let target : Array[Char] = []
for c in needle {
target.push(c)
}
if target.length() == 0 || chars.length() < target.length() {
return -1
}
let mut i = from
while i + target.length() <= chars.length() {
let mut j = 0
let mut same = true
while j < target.length() {
if chars[i + j] != target[j] {
same = false
j = target.length()
} else {
j = j + 1
}
}
if same {
return i
}
i = i + 1
}
-1
}
///|
/// Decode UTF-8 into a string, replacing code points outside the BMP with `?`.
fn decode_utf8(bytes : Bytes, start : Int32, end : Int32) -> String {
let sb = StringBuilder()
let mut i = start
while i < end && i < bytes.length() {
let b0 = bytes[i].to_int()
if b0 < 0x80 {
sb.write_char(Int32::unsafe_to_char(b0))
i = i + 1
} else if b0 < 0xE0 && i + 1 < end {
let b1 = bytes[i + 1].to_int()
let code = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
sb.write_char(Int32::unsafe_to_char(code))
i = i + 2
} else if b0 < 0xF0 && i + 2 < end {
let b1 = bytes[i + 1].to_int()
let b2 = bytes[i + 2].to_int()
let code = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
sb.write_char(Int32::unsafe_to_char(code))
i = i + 3
} else if i + 3 < end {
sb.write_char('?')
i = i + 4
} else {
sb.write_char('?')
i = i + 1
}
}
sb.to_string()
}