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(
124    access_mask: windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS,
125) -> Result {
126    use crate::win_acl::TokenUser;
127    const CTX: &str = "Failed to set a process DACL";
128
129    // First obtain the SID of the current user
130    let user = TokenUser::process_user().context(CTX)?;
131    let sid = user.sid();
132
133    // Now specify the ACL we want to create
134    let acl_spec = crate::win_acl::EmptyAcl;
135    let acl_spec = crate::win_acl::AddAllowAceAcl::new(acl_spec, access_mask, sid);
136
137    // Create ACL and set as process DACL
138    let acl = acl_spec.create().context(CTX)?;
139    acl.set_process_dacl_protected().context(CTX)?;
140    Result::create_ok()
141}
142
143/// Limit user access to process by setting a default restrictive DACL for the
144/// process.
145///
146/// For fine-grained control over the DACL, use the API in [`crate::win_acl`].
147///
148/// * Type: memory-security, disable-debug
149/// * Targets: windows
150/// * API: WinApi (stable)
151///
152/// # Errors
153/// Returns an error when the system interface returns an error.
154#[cfg(windows)]
155pub fn set_default_dacl_winapi() -> Result {
156    use windows::Win32::System::Threading::{
157        PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE, PROCESS_TERMINATE,
158    };
159    let access_mask = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE | PROCESS_SYNCHRONIZE;
160    set_custom_dacl_winapi(access_mask)
161}
162
163/// Disable core dumps for this process.
164///
165/// * Type: dont-dump
166/// * Targets: posix/unix
167/// * API: rlimit (stable)
168///
169/// # Errors
170/// Returns an error when the system or libc interface returns an error.
171#[cfg(unix)]
172pub fn disable_core_dumps_rlimit() -> Result {
173    use crate::error::private::ResultExt as _;
174
175    const RESOURCE: rustix::process::Resource = rustix::process::Resource::Core;
176    let rlim = rustix::process::Rlimit {
177        current: Some(0),
178        maximum: Some(0),
179    };
180    rustix::process::setrlimit(RESOURCE, rlim)
181        .map_anyhow()
182        .context("Failed to disable core-dumps via rlimit")?;
183    Result::create_ok()
184}
185
186/// Disable tracing for this process.
187///
188/// * Type: memory-security, disable-tracing, dont-dump
189/// * Targets: linux, freebsd, macos
190/// * API: prctl (stable) / ptrace (stable) (on macOS)
191///
192/// # Errors
193/// Returns an error when the system or libc interface returns an error.
194#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
195pub fn disable_tracing_prctl() -> Result {
196    internals::prctl::set_process_nontraceable()
197        .context("Failed to disable ptrace-ability of the process")?;
198    Result::create_ok()
199}
200
201/// Check whether a tracer is attached via the procfs virtual filesystem.
202///
203/// * Type: anti-tracing
204/// * Targets: linux
205/// * API: procfs (stable)
206///
207/// # Errors
208/// Returns an error when the required procfs file `/proc/self/status` could not
209/// be opened, or if it doesn't contain a valid `TracerPid` entry. Returns
210/// `BeingTraced` when the process is being traced.
211#[cfg(all(target_os = "linux", feature = "std"))]
212pub fn check_tracer_procfs() -> Result {
213    if let Some(pid) = internals::std::is_tracer_present()
214        .context("Failed to check tracer presence via /proc/self/status")?
215    {
216        return Result::create_being_traced(Traced::from_pid(pid));
217    };
218    Result::create_ok()
219}
220
221/// Check whether a tracer is attached via the procfs virtual filesystem.
222///
223/// * Type: anti-tracing
224/// * Targets: freebsd
225/// * API: prctl (stable)
226///
227/// # Errors
228/// Returns an error when the system or libc interface returns an error.
229/// Returns `BeingTraced` when the process is being traced.
230#[cfg(target_os = "freebsd")]
231pub fn check_tracer_prctl() -> Result {
232    if let Some(pid) = internals::prctl::is_tracer_present()
233        .context("Failed to check tracer presence via procctl")?
234    {
235        return Result::create_being_traced(Traced::from_pid(pid));
236    };
237    Result::create_ok()
238}