ntprocesses/process/
build.rs1use std::path::PathBuf;
2use thiserror::Error;
3use windows::Win32::System::Threading::{
4 CREATE_SUSPENDED, PROCESS_ACCESS_RIGHTS, PROCESS_CREATION_FLAGS,
5};
6
7use super::{Created, Process, ProcessError, Snapshot, NT};
8
9#[derive(Error, Debug)]
10pub enum ProcessBuilderError {
11 #[error("Could not build the Process with these arguments.")]
12 InvalidArgument,
13 #[error(transparent)]
14 ProcessError(#[from] ProcessError),
15}
16
17type Result<X> = std::result::Result<X, ProcessBuilderError>;
18
19#[derive(Default)]
21pub struct Create;
22#[derive(Default)]
24pub struct Attach;
25
26#[derive(Default)]
27pub struct ProcessBuilder<T> {
28 name: Option<String>,
29 process_id: Option<u32>,
30 permissions: Option<PROCESS_ACCESS_RIGHTS>,
31 file_path: Option<PathBuf>,
32 creation_flags: PROCESS_CREATION_FLAGS,
33 exe_args: Option<String>,
34 _marker: std::marker::PhantomData<T>,
35}
36
37impl ProcessBuilder<Attach> {
38 pub fn permissions(mut self, permissions: PROCESS_ACCESS_RIGHTS) -> Self {
40 self.permissions = Some(permissions);
41 self
42 }
43
44 pub fn process_name(mut self, name: &str) -> Self {
45 self.name = Some(name.to_string());
46 self
47 }
48
49 pub fn process_id(mut self, pid: u32) -> Self {
50 self.process_id = Some(pid);
51 self
52 }
53
54 fn pre_build(&self) -> Result<()> {
55 if self.process_id.is_some() && self.name.is_some() {
57 return Err(ProcessBuilderError::InvalidArgument);
58 }
59
60 if self.permissions.is_none() {
62 return Err(ProcessBuilderError::InvalidArgument);
63 }
64
65 Ok(())
66 }
67
68 pub fn build_from_snapshot(self) -> Result<Process<Snapshot>> {
69 self.pre_build()?;
70
71 if let Some(name) = self.name {
72 return Ok(Process::<Snapshot>::from_name(
73 &name,
74 self.permissions.unwrap(),
75 )?);
76 }
77
78 if let Some(pid) = self.process_id {
79 return Ok(Process::<Snapshot>::from_pid(
80 pid,
81 self.permissions.unwrap(),
82 )?);
83 }
84
85 return Err(ProcessBuilderError::InvalidArgument);
86 }
87
88 pub fn build_from_nt(self) -> Result<Process<NT>> {
89 self.pre_build()?;
90
91 if let Some(name) = self.name {
92 return Ok(Process::<NT>::from_name(&name, self.permissions.unwrap())?);
93 }
94
95 if let Some(pid) = self.process_id {
96 return Ok(Process::<NT>::from_pid(pid, self.permissions.unwrap())?);
97 }
98
99 return Err(ProcessBuilderError::InvalidArgument);
100 }
101}
102
103impl ProcessBuilder<Create> {
104 pub fn file_path(mut self, path: PathBuf) -> Self {
105 self.file_path = Some(path);
106 self
107 }
108
109 pub fn suspended(mut self) -> Self {
111 self.creation_flags |= CREATE_SUSPENDED;
112 self
113 }
114
115 pub fn flags(mut self, flags: PROCESS_CREATION_FLAGS) -> Self {
117 self.creation_flags |= flags;
118 self
119 }
120
121 pub fn args(mut self, args: &str) -> Self {
122 self.exe_args = Some(args.to_string());
123 self
124 }
125
126 pub fn spawn(self) -> Result<Process<Created>> {
127 if self.file_path.is_none() {
129 return Err(ProcessBuilderError::InvalidArgument);
130 }
131
132 Ok(Process::<Created>::from_path(
133 self.file_path.unwrap(),
134 &self.exe_args.unwrap_or_default(),
135 self.creation_flags,
136 )?)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
144 use pretty_assertions::assert_eq;
145 use windows::Win32::System::Threading::PROCESS_ALL_ACCESS;
146
147 #[test]
148 fn builder_attach_snapshot() -> Result<()> {
149 let process_id = Process::<Snapshot>::get_current_process_id();
151
152 ProcessBuilder::<Attach>::default()
153 .permissions(PROCESS_ALL_ACCESS)
154 .process_id(process_id)
155 .build_from_snapshot()?;
156
157 Ok(())
158 }
159
160 #[test]
161 fn builder_attach_nt() -> Result<()> {
162 let process_id = Process::<NT>::get_current_process_id();
164
165 ProcessBuilder::<Attach>::default()
166 .permissions(PROCESS_ALL_ACCESS)
167 .process_id(process_id)
168 .build_from_nt()?;
169
170 Ok(())
171 }
172
173 #[test]
174 fn builder_create_process() -> Result<()> {
175 let process_id = Process::<Snapshot>::get_current_process_id();
177
178 let target_process = ProcessBuilder::<Attach>::default()
179 .permissions(PROCESS_ALL_ACCESS)
180 .process_id(process_id)
181 .build_from_snapshot()?;
182
183 let process = ProcessBuilder::<Create>::default()
184 .file_path(target_process.get_full_path()?)
185 .suspended()
186 .args("-test")
187 .spawn()?;
188
189 process.kill()?;
190
191 Ok(())
192 }
193
194 #[test]
195 fn builder_attach_invalid_config() {
196 let process_id = Process::<Snapshot>::get_current_process_id();
198
199 let should_err = ProcessBuilder::<Attach>::default()
200 .permissions(PROCESS_ALL_ACCESS)
201 .process_id(process_id)
202 .process_name("random.exe")
203 .build_from_snapshot();
204
205 assert_eq!(should_err.is_err(), true)
206 }
207}