Skip to content

Commit

Permalink
PE: add tests for TLS parser and characteristics constants
Browse files Browse the repository at this point in the history
  • Loading branch information
kkent030315 committed Oct 29, 2024
1 parent d096260 commit 156d5c5
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 0 deletions.
121 changes: 121 additions & 0 deletions src/pe/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

/// Indicates 1-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_1BYTES: u32 = 0x00100000;
/// Indicates 2-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_2BYTES: u32 = 0x00200000;
/// Indicates 4-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_4BYTES: u32 = 0x00300000;
/// Indicates 8-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_8BYTES: u32 = 0x00400000;
/// Indicates 16-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_16BYTES: u32 = 0x00500000;
/// Indicates 32-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_32BYTES: u32 = 0x00600000;
/// Indicates 64-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_64BYTES: u32 = 0x00700000;
/// Indicates 128-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_128BYTES: u32 = 0x00800000;
/// Indicates 256-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_256BYTES: u32 = 0x00900000;
/// Indicates 512-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_512BYTES: u32 = 0x00A00000;
/// Indicates 1024-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_1024BYTES: u32 = 0x00B00000;
/// Indicates 2048-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_2048BYTES: u32 = 0x00D00000;
/// Indicates 4096-byte alignment for Thread Local Storage (TLS) dacharacteristicsta field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_4096BYTES: u32 = 0x00C00000;
/// Indicates 8192-byte alignment for Thread Local Storage (TLS) characteristics field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_8192BYTES: u32 = 0x00E00000;
/// Mask for isolating alignment information from the characteristics field field in [`ImageTlsDirectory::characteristics`]
pub const TLS_CHARACTERISTICS_ALIGN_MASK: u32 = 0x00F00000;

/// Represents the TLS directory `IMAGE_TLS_DIRECTORY64`.
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
Expand All @@ -28,6 +59,24 @@ pub struct ImageTlsDirectory {
/// The size of the zero fill.
pub size_of_zero_fill: u32,
/// The characteristics of the TLS.
///
/// Contains one or more bitflags of:
///
/// - [`TLS_CHARACTERISTICS_ALIGN_1BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_2BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_4BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_8BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_16BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_32BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_64BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_128BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_256BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_512BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_1024BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_2048BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_4096BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_8192BYTES`]
/// - [`TLS_CHARACTERISTICS_ALIGN_MASK`]
pub characteristics: u32,
}

Expand Down Expand Up @@ -245,3 +294,75 @@ impl<'a> TlsData<'a> {
}))
}
}

#[cfg(test)]
mod tests {
use super::TLS_CHARACTERISTICS_ALIGN_8BYTES;

const SUDO_EXE_BIN: &[u8] = include_bytes!("../../tests/bins/pe/sudo.exe.bin");
const TINYRUST_LLD_MALFORMED_TLS_CALLBACKS_BIN: &[u8] =
include_bytes!("../../tests/bins/pe/tinyrust_lld_malformed_tls_callbacks.exe.bin");
const TINYRUST_LLD_WITH_TLS_BIN: &[u8] =
include_bytes!("../../tests/bins/pe/tinyrust_lld_with_tls.exe.bin");
const TINYRUST_LLD_NO_TLS_BIN: &[u8] =
include_bytes!("../../tests/bins/pe/tinyrust_lld_no_tls.exe.bin");

#[test]
fn parse_tinyrust_no_tls() {
let binary = crate::pe::PE::parse(TINYRUST_LLD_NO_TLS_BIN).expect("Unable to parse binary");
assert_eq!(binary.tls_data.is_none(), true);
}

#[test]
fn parse_tinyrust_with_tls() {
let binary =
crate::pe::PE::parse(TINYRUST_LLD_WITH_TLS_BIN).expect("Unable to parse binary");
assert_eq!(binary.tls_data.is_some(), true);
let tls_data = binary.tls_data.unwrap();
let dir = tls_data.image_tls_directory;

assert_eq!(tls_data.callbacks, vec![0x140001000, 0x140008160]);
assert_eq!(dir.address_of_callbacks, 0x140019860);

let raw_data_expect: &[u8] = &[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
assert_eq!(tls_data.raw_data.is_some_and(|x| x.len() == 0x50), true);
assert_eq!(tls_data.raw_data, Some(raw_data_expect));
assert_eq!(dir.start_address_of_raw_data, 0x14001E000);
assert_eq!(dir.end_address_of_raw_data, 0x14001E050);

assert_eq!(tls_data.slot, Some(0));
assert_eq!(dir.address_of_index, 0x14001C170);

assert_eq!(dir.size_of_zero_fill, 0x0);
assert_eq!(dir.characteristics, TLS_CHARACTERISTICS_ALIGN_8BYTES);
}

/// Contains two valid callbacks, but null-terminator is (intentionally, for test)
/// malformed with 8-bytes `08 07 06 05 04 03 02 01` (LE).
#[test]
fn parse_tinyrust_malformed_tls_callbacks() {
let binary = crate::pe::PE::parse(TINYRUST_LLD_MALFORMED_TLS_CALLBACKS_BIN);
if let Err(crate::error::Error::Malformed(msg)) = binary {
assert_eq!(msg, "cannot map tls callback (0x807060504030201)");
} else {
panic!("Expected a Malformed error but got Result::Ok");
}
}

/// Binaries compiled with MSVC linker may generate an binary that
/// its TLS directory contains `AddressOfIndex` field that only
/// present in virtual address when mapped to virtual memory.
///
/// Issue: <https://github.com/m4b/goblin/issues/424>
#[test]
fn parse_sudo_slot_nonexist_in_raw() {
let binary = crate::pe::PE::parse(SUDO_EXE_BIN).expect("Unable to parse binary");
assert_eq!(binary.tls_data.is_some(), true);
let tls_data = binary.tls_data.unwrap();
assert_eq!(tls_data.slot, None);
}
}
Binary file added tests/bins/pe/sudo.exe.bin
Binary file not shown.
Binary file not shown.
Binary file added tests/bins/pe/tinyrust_lld_no_tls.exe.bin
Binary file not shown.
Binary file added tests/bins/pe/tinyrust_lld_with_tls.exe.bin
Binary file not shown.

0 comments on commit 156d5c5

Please sign in to comment.