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}