Skip to main content

secmem_proc/
components.rs

1//! Module containing hardening and anti-tracing components, which can be
2//! combined into an appropriate hardening routine.
3//!
4//! The available components in this module are target/arch specific.
5//!
6//! You probably want to use the higher level platform independent configuration
7//! API in [`crate::config`] instead; or the [`crate::harden_process`] function
8//! which uses default configuration.
9
10use crate::error::{Result, ResultExt as _, Traced};
11use crate::internals;
12use anyhow::Context;
13
14/// Check whether a tracer is attached via the appropriate WinAPI calls.
15///
16/// * Type: anti-tracing
17/// * Targets: windows
18/// * API: WinAPI (stable)
19///
20/// # Errors
21/// Returns an error when the system interface returns an error. Returns
22/// `BeingTraced` when the process is being traced.
23#[cfg(windows)]
24pub fn check_tracer_winapi() -> Result {
25    const TR: Traced = Traced::DEFAULT;
26
27    if internals::win32::is_debugger_present() {
28        return Result::create_being_traced(TR);
29    };
30
31    let res = unsafe {
32        internals::win32::is_remote_debugger_present(internals::win32::get_process_handle())
33    };
34    match res {
35        Ok(true) => {
36            return Result::create_being_traced(TR);
37        },
38        Ok(false) => {},
39        Err(e) => {
40            return Result::create_err(
41                e.context("Failed to check whether a tracer is present via WinAPI"),
42            );
43        },
44    }
45
46    Result::create_ok()
47}
48
49/// Check whether a tracer is attached using (undocumented) implementation
50/// details of the OS.
51///
52/// * Type: anti-tracing
53/// * Targets: windows
54/// * API: unstable
55///
56/// # Errors
57/// Returns `BeingTraced` when the process is being traced.
58#[cfg(all(windows, feature = "unstable"))]
59pub fn check_tracer_unstable() -> Result {
60    const TR: Traced = Traced::DEFAULT;
61
62    let res = unsafe { internals::win32::is_kernelflag_debugger_present() };
63    if res {
64        return Result::create_being_traced(TR);
65    }
66
67    Result::create_ok()
68}
69
70/// Hide thread from debugger using (undocumented) Windows native API.
71///
72/// * Type: anti-tracing
73/// * Targets: windows
74/// * API: NtApi (unstable)
75///
76/// # Errors
77/// Returns an error when the system interface returns an error.
78#[cfg(all(windows, feature = "unstable"))]
79pub fn hide_thread_from_debugger_ntapi() -> Result {
80    // SAFETY: `internals::win32::get_thread_handle()` gives a valid thread handle
81    unsafe { internals::win32::hide_thread_from_debugger(internals::win32::get_thread_handle()) }
82        .context("Failed hide the thread from potential tracers")?;
83    Result::create_ok()
84}
85
86/// Limit user access to process completely by setting an empty DACL (i.e. no
87/// access allowed at all) for the process.
88///
89/// For fine-grained control over the DACL, use the API in [`crate::win_acl`].
90///
91/// * Type: memory-security, disable-debug
92/// * Targets: windows
93/// * API: WinApi (stable)
94///
95/// # Errors
96/// Returns an error when the system interface returns an error.
97#[cfg(windows)]
98pub fn set_empty_dacl_winapi() -> Result {
99    const CTX: &str = "Failed to set a process DACL";
100
101    // Specify the ACL we want to create
102    let acl_spec = crate::win_acl::EmptyAcl;
103
104    // Create ACL and set as process DACL
105    let acl = acl_spec.create().context(CTX)?;
106    acl.set_process_dacl_protected().context(CTX)?;
107    Result::create_ok()
108}
109
110/// Limit user access to process by setting a restrictive DACL for the process.
111/// The access that is granted to the user owning the current process is given
112/// by `access_mask`. Other users don't get any permissions.
113///
114/// For fine-grained control over the DACL, use the API in [`crate::win_acl`].
115///
116/// * Type: memory-security, disable-debug
117/// * Targets: windows
118/// * API: WinApi (stable)
119///
120/// # Errors
121/// Returns an error when the system interface returns an error.
122#[cfg(windows)]
123pub fn set_custom_dacl_winapi(access_mask: crate::win_acl::ProcessAccessRights) -> Result {
124    use crate::win_acl::{SidRef, WellKnownSidBuffer};
125    const CTX: &str = "Failed to set a process DACL";
126
127    // First obtain the well-known owner rights SID
128    let mut buf = WellKnownSidBuffer::new();
129    let sid = SidRef::well_known(&mut buf, windows::Win32::Security::WinCreatorOwnerRightsSid)?;
130
131    // Now specify the ACL we want to create
132    let acl_spec = crate::win_acl::EmptyAcl;
133    let acl_spec = crate::win_acl::AddAllowAceAcl::new(acl_spec, access_mask, sid);
134
135    // Create ACL and set as process DACL
136    let acl = acl_spec.create().context(CTX)?;
137    acl.set_process_dacl_protected().context(CTX)?;
138    Result::create_ok()
139}
140
141/// Limit user access to process by setting a default restrictive DACL for the
142/// process.
143///
144/// For fine-grained control over the DACL, use the API in [`crate::win_acl`].
145///
146/// * Type: memory-security, disable-debug
147/// * Targets: windows
148/// * API: WinApi (stable)
149///
150/// # Errors
151/// Returns an error when the system interface returns an error.
152#[cfg(windows)]
153pub fn set_default_dacl_winapi() -> Result {
154    use windows::Win32::System::Threading::{
155        PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE, PROCESS_TERMINATE,
156    };
157    let access_mask = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE | PROCESS_SYNCHRONIZE;
158    set_custom_dacl_winapi(access_mask)
159}
160
161/// Disable core dumps for this process.
162///
163/// * Type: dont-dump
164/// * Targets: posix/unix
165/// * API: rlimit (stable)
166///
167/// # Errors
168/// Returns an error when the system or libc interface returns an error.
169#[cfg(unix)]
170pub fn disable_core_dumps_rlimit() -> Result {
171    use crate::error::private::ResultExt as _;
172
173    const RESOURCE: rustix::process::Resource = rustix::process::Resource::Core;
174    let rlim = rustix::process::Rlimit {
175        current: Some(0),
176        maximum: Some(0),
177    };
178    rustix::process::setrlimit(RESOURCE, rlim)
179        .map_anyhow()
180        .context("Failed to disable core-dumps via rlimit")?;
181    Result::create_ok()
182}
183
184/// Disable tracing for this process.
185///
186/// * Type: memory-security, disable-tracing, dont-dump
187/// * Targets: linux, freebsd, macos
188/// * API: prctl (stable) / ptrace (stable) (on macOS)
189///
190/// # Errors
191/// Returns an error when the system or libc interface returns an error.
192#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
193pub fn disable_tracing_prctl() -> Result {
194    internals::prctl::set_process_nontraceable()
195        .context("Failed to disable ptrace-ability of the process")?;
196    Result::create_ok()
197}
198
199/// Check whether a tracer is attached via the procfs virtual filesystem.
200///
201/// * Type: anti-tracing
202/// * Targets: linux
203/// * API: procfs (stable)
204///
205/// # Errors
206/// Returns an error when the required procfs file `/proc/self/status` could not
207/// be opened, or if it doesn't contain a valid `TracerPid` entry. Returns
208/// `BeingTraced` when the process is being traced.
209#[cfg(all(target_os = "linux", feature = "std"))]
210pub fn check_tracer_procfs() -> Result {
211    if let Some(pid) = internals::std::is_tracer_present()
212        .context("Failed to check tracer presence via /proc/self/status")?
213    {
214        return Result::create_being_traced(Traced::from_pid(pid));
215    };
216    Result::create_ok()
217}
218
219/// Check whether a tracer is attached via the procfs virtual filesystem.
220///
221/// * Type: anti-tracing
222/// * Targets: freebsd
223/// * API: prctl (stable)
224///
225/// # Errors
226/// Returns an error when the system or libc interface returns an error.
227/// Returns `BeingTraced` when the process is being traced.
228#[cfg(target_os = "freebsd")]
229pub fn check_tracer_prctl() -> Result {
230    if let Some(pid) = internals::prctl::is_tracer_present()
231        .context("Failed to check tracer presence via procctl")?
232    {
233        return Result::create_being_traced(Traced::from_pid(pid));
234    };
235    Result::create_ok()
236}