├── .gitignore ├── ppspoofing.exe ├── screenshot.png ├── Cargo.toml ├── README.md └── src ├── main.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /ppspoofing.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xlane/ppspoofing/HEAD/ppspoofing.exe -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xlane/ppspoofing/HEAD/screenshot.png -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppspoofing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies.windows] 9 | version = "0.43.0" 10 | features = [ 11 | "Win32_Foundation", 12 | "Win32_Security", 13 | "Win32_System_WindowsProgramming", 14 | "Win32_System_SystemServices", 15 | "Win32_System_LibraryLoader", 16 | "Win32_System_Registry", 17 | "Win32_System_Threading", 18 | "Win32_System_Memory", 19 | "Win32_System_Environment", 20 | "Win32_System_SystemInformation", 21 | "Win32_UI_Shell", 22 | "Win32_UI_WindowsAndMessaging" 23 | ] 24 | 25 | [dependencies] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ppspoofing 2 | 3 | 父进程 PID 伪造技术测试用,Rust 编写。 4 | 5 | ## 使用方式 6 | 7 | ```bash 8 | cargo build 9 | cargo run -- 10 | ``` 11 | 12 | 在 `powershell` 可以这么用: 13 | 14 | ```powershell 15 | ppspoofing.exe (Get-Process -Name winlogon)[0].Id notepad.exe 16 | ``` 17 | 18 | ![x](screenshot.png) 19 | 20 | ## 利用原理 21 | 22 | 1. 检查运行权限,尝试开启 DEBUG 权限,但实际上不是所有进程都需要这一步 23 | 2. 通过 `OpenProcess` 获取到目标进程句柄,访问权限 `PROCESS_ALL_ACCESS` 24 | 3. 创建 `STARTUPINFOEXA`,调用 `InitializeProcThreadAttributeList` 初始化 25 | 4. 调用 `UpdateProcThreadAttribute` 将第一步获取的句柄值更新到 `PROC_THREAD_ATTRIBUTE_PARENT_PROCESS` 26 | 5. 调用 `CreateProcess` 创建进程,创建标志为 `CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT` 27 | 28 | ## 存在的问题 29 | 30 | 1. 在第二步,administrator 用户也无法获取到所有访问权限的 PPL 进程,影响不大,怎么解决自己研究一下 31 | 2. 某些进程作为父进程时,创建的 cmd.exe 进程会闪退,没查原因,创建 notepad.exe 就不会有问题 32 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use windows::Win32::{ 4 | Foundation::CloseHandle, 5 | System::Threading::{OpenProcess, PROCESS_ALL_ACCESS}, 6 | }; 7 | 8 | use crate::utils::proc::{self, create_process_with_handle}; 9 | 10 | fn main() { 11 | unsafe { 12 | // elevate to admin and enable debug privilege 13 | proc::elevate_to_admin(); 14 | proc::enable_debug_priv().unwrap(); 15 | 16 | // parse args 17 | let args: Vec = std::env::args().collect(); 18 | let procname = std::path::Path::new(args[0].as_str()) 19 | .file_name() 20 | .unwrap() 21 | .to_str() 22 | .unwrap(); 23 | 24 | if args.len() < 3 { 25 | println!("Usage: {} ", procname); 26 | return; 27 | } 28 | 29 | let ppid: u32 = args[1].parse().unwrap(); 30 | let cmdline = args[2].as_str(); 31 | 32 | // open target process 33 | println!("[+] get parent process handle -> {}", ppid); 34 | let phandle = OpenProcess(PROCESS_ALL_ACCESS, false, ppid).unwrap(); 35 | println!("[+] handle value -> {:#x}", phandle.0); 36 | 37 | // parent process spoofing 38 | println!("[+] create new process using parent process handle"); 39 | let pid = create_process_with_handle(phandle, cmdline).unwrap(); 40 | CloseHandle(phandle); 41 | 42 | println!("[+] new process -> {}({})", cmdline, pid); 43 | 44 | // use std::process::Command; 45 | // let _ = Command::new("cmd").args(["/c", "pause"]).status(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | pub mod proc { 3 | use std::mem::{size_of, zeroed}; 4 | 5 | use windows::{ 6 | core::{PCSTR, PCWSTR, PWSTR}, 7 | s, w, 8 | Win32::{ 9 | Foundation::{ 10 | CloseHandle, BOOL, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE, LUID, 11 | PSID, MAX_PATH, 12 | }, 13 | Security::{ 14 | AdjustTokenPrivileges, AllocateAndInitializeSid, CheckTokenMembership, FreeSid, 15 | ImpersonateSelf, LookupPrivilegeValueA, SecurityImpersonation, 16 | SE_PRIVILEGE_ENABLED, SID_IDENTIFIER_AUTHORITY, TOKEN_ADJUST_PRIVILEGES, 17 | TOKEN_PRIVILEGES, TOKEN_PRIVILEGES_ATTRIBUTES, TOKEN_QUERY, 18 | }, 19 | System::{ 20 | Memory::{GetProcessHeap, HeapAlloc, HeapFree, HEAP_NONE, HEAP_ZERO_MEMORY}, 21 | SystemServices::{DOMAIN_ALIAS_RID_ADMINS, SECURITY_BUILTIN_DOMAIN_RID}, 22 | Threading::{ 23 | CreateProcessW, DeleteProcThreadAttributeList, GetCurrentProcess, 24 | GetCurrentThread, InitializeProcThreadAttributeList, OpenProcessToken, 25 | OpenThreadToken, UpdateProcThreadAttribute, CREATE_UNICODE_ENVIRONMENT, 26 | EXTENDED_STARTUPINFO_PRESENT, LPPROC_THREAD_ATTRIBUTE_LIST, 27 | PROCESS_INFORMATION, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, 28 | STARTF_USESHOWWINDOW, STARTUPINFOEXW, 29 | }, SystemInformation::GetSystemDirectoryW, 30 | }, 31 | UI::{ 32 | Shell::ShellExecuteW, 33 | WindowsAndMessaging::{SW_SHOW, SW_SHOWDEFAULT}, 34 | }, 35 | }, 36 | }; 37 | 38 | const SECURITY_NT_AUTHORITY: [u8; 6] = [0, 0, 0, 0, 0, 5]; 39 | 40 | // check whether current process privilege is elevated 41 | #[rustfmt::skip] 42 | pub unsafe fn is_elevated() -> bool { 43 | let mut admins_group = zeroed::(); 44 | let mut authority = zeroed::(); 45 | authority.Value = SECURITY_NT_AUTHORITY; 46 | 47 | match AllocateAndInitializeSid( 48 | &mut authority, 49 | 2, 50 | SECURITY_BUILTIN_DOMAIN_RID.try_into().unwrap(), 51 | DOMAIN_ALIAS_RID_ADMINS.try_into().unwrap(), 52 | 0, 0, 0, 0, 0, 0, 53 | &mut admins_group, 54 | ).ok() 55 | { 56 | Ok(()) => { 57 | let mut is_member: BOOL = BOOL(0); 58 | match CheckTokenMembership(None, admins_group, &mut is_member).ok() { 59 | Ok(()) => { 60 | FreeSid(admins_group); 61 | if is_member.as_bool() { 62 | true 63 | } else { 64 | false 65 | } 66 | } 67 | Err(_) => { 68 | FreeSid(admins_group); 69 | false 70 | }, 71 | } 72 | } 73 | Err(_) => false, 74 | } 75 | } 76 | 77 | // elevate to admin priv 78 | pub unsafe fn elevate_to_admin() { 79 | if !is_elevated() { 80 | let mut us_filename: Vec<_> = std::env::args() 81 | .nth(0) 82 | .unwrap() 83 | .encode_utf16() 84 | .collect::>(); 85 | us_filename.push(0x0); 86 | 87 | let mut us_cmdline: Vec<_> = std::env::args() 88 | .skip(1) 89 | .map(|x| format!("\"{}\"", x)) 90 | .collect::>() 91 | .join(" ") 92 | .encode_utf16() 93 | .collect(); 94 | us_cmdline.push(0x0); 95 | 96 | ShellExecuteW( 97 | None, 98 | w!("runas"), 99 | PCWSTR::from_raw(us_filename.as_ptr()), 100 | PCWSTR::from_raw(us_cmdline.as_ptr()), 101 | None, 102 | SW_SHOWDEFAULT, 103 | ); 104 | std::process::exit(0); 105 | } 106 | } 107 | 108 | // enable or disable token privilege 109 | pub unsafe fn set_privilege( 110 | token: HANDLE, 111 | privilege_name: PCSTR, 112 | enabled: bool, 113 | ) -> Result<(), windows::core::Error> { 114 | // 获取权限id 115 | let mut luid = zeroed::(); 116 | LookupPrivilegeValueA(None, privilege_name, &mut luid).ok()?; 117 | 118 | // 设置token权限 119 | let mut tp = zeroed::(); 120 | tp.PrivilegeCount = 1; 121 | tp.Privileges[0].Luid = luid; 122 | tp.Privileges[0].Attributes = if enabled { 123 | SE_PRIVILEGE_ENABLED 124 | } else { 125 | TOKEN_PRIVILEGES_ATTRIBUTES(0x0) 126 | }; 127 | 128 | AdjustTokenPrivileges( 129 | token, 130 | false, 131 | Some(&tp), 132 | size_of::() as _, 133 | None, 134 | None, 135 | ) 136 | .ok()?; 137 | 138 | Ok(()) 139 | } 140 | 141 | // enable current process debug privilege 142 | pub unsafe fn enable_debug_priv() -> Result<(), windows::core::Error> { 143 | let mut proc_handle = INVALID_HANDLE_VALUE; 144 | OpenProcessToken( 145 | GetCurrentProcess(), 146 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 147 | &mut proc_handle, 148 | ) 149 | .ok()?; 150 | set_privilege(proc_handle, s!("SeDebugPrivilege"), true)?; 151 | CloseHandle(proc_handle); 152 | 153 | Ok(()) 154 | } 155 | 156 | // enable current thread debug privilege 157 | pub unsafe fn t_enable_debug_priv() -> Result<(), windows::core::Error> { 158 | let mut thread_handle = INVALID_HANDLE_VALUE; 159 | ImpersonateSelf(SecurityImpersonation).ok()?; 160 | OpenThreadToken( 161 | GetCurrentThread(), 162 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 163 | true, 164 | &mut thread_handle, 165 | ) 166 | .ok()?; 167 | set_privilege(thread_handle, s!("SeDebugPrivilege"), true)?; 168 | CloseHandle(thread_handle); 169 | 170 | Ok(()) 171 | } 172 | 173 | // create new process using parent process handle 174 | pub unsafe fn create_process_with_handle( 175 | handle: HANDLE, 176 | cmdline: &str, 177 | ) -> Result { 178 | let mut si: STARTUPINFOEXW = zeroed(); 179 | let mut pi: PROCESS_INFORMATION = zeroed(); 180 | let mut size: usize = 0x30; 181 | 182 | loop { 183 | if size > 1024 { 184 | return Err(windows::core::Error::from_win32()); 185 | } 186 | 187 | si.StartupInfo.cb = size_of::() as u32; 188 | si.lpAttributeList = LPPROC_THREAD_ATTRIBUTE_LIST(HeapAlloc( 189 | GetProcessHeap().unwrap(), 190 | HEAP_ZERO_MEMORY, 191 | size, 192 | )); 193 | 194 | if si.lpAttributeList.is_invalid() { 195 | return Err(windows::core::Error::from_win32()); 196 | } 197 | let ret = 198 | match InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &mut size).ok() { 199 | Ok(()) => { 200 | UpdateProcThreadAttribute( 201 | si.lpAttributeList, 202 | 0, 203 | PROC_THREAD_ATTRIBUTE_PARENT_PROCESS as usize, 204 | Some(&handle as *const _ as *mut _), 205 | size_of::(), 206 | None, 207 | None, 208 | ) 209 | .ok()?; 210 | 211 | si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; 212 | si.StartupInfo.wShowWindow = SW_SHOW.0 as _; 213 | 214 | let mut us_cmdline: Vec<_> = cmdline.encode_utf16().collect(); 215 | us_cmdline.push(0x0); 216 | 217 | let mut system_path: [u16; MAX_PATH as _] = zeroed(); 218 | GetSystemDirectoryW(Some(&mut system_path)); 219 | 220 | CreateProcessW( 221 | None, 222 | PWSTR::from_raw(us_cmdline.as_mut_ptr()), 223 | None, 224 | None, 225 | false, 226 | CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, 227 | None, 228 | PCWSTR::from_raw(system_path.as_ptr()), 229 | &mut si.StartupInfo, 230 | &mut pi, 231 | ) 232 | .ok()?; 233 | 234 | CloseHandle(pi.hThread); 235 | CloseHandle(pi.hProcess); 236 | 237 | Ok(pi.dwProcessId) 238 | } 239 | // Err(windows::core::Error::from(ERROR_INSUFFICIENT_BUFFER)) => {} 240 | Err(e) => { 241 | if e != windows::core::Error::from(ERROR_INSUFFICIENT_BUFFER) { 242 | Err(e) 243 | } else { 244 | Ok(0) 245 | } 246 | } 247 | }; 248 | 249 | if !si.lpAttributeList.is_invalid() { 250 | DeleteProcThreadAttributeList(si.lpAttributeList); 251 | } 252 | HeapFree( 253 | GetProcessHeap().unwrap(), 254 | HEAP_NONE, 255 | Some(si.lpAttributeList.0), 256 | ); 257 | 258 | match ret { 259 | Ok(0) => { 260 | continue; 261 | } 262 | Ok(pid) => { 263 | return Ok(pid); 264 | } 265 | Err(e) => { 266 | return Err(e); 267 | } 268 | } 269 | } 270 | } 271 | } 272 | --------------------------------------------------------------------------------