├── .cargo └── config ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples └── hellobox.rs ├── hello-box-demo.png └── src ├── error.rs ├── evelate_windows.rs ├── inject_windows.rs ├── lib.rs ├── main.rs ├── process_windows.rs ├── utils.rs └── winapi.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "i686-pc-windows-msvc" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "injrs" 3 | version = "0.1.2" 4 | description = "DLL injector library and tool written in Rust. Rust 实现的DLL注入工具/库。" 5 | authors = ["Zoe "] 6 | license = "Apache-2.0" 7 | edition = "2018" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [[example]] 12 | name = "hellobox" 13 | crate-type = ["cdylib"] 14 | 15 | 16 | [dependencies.winapi] 17 | version = "0.3.9" 18 | features = [ 19 | "memoryapi", 20 | "minwindef", 21 | "ntdef", 22 | "winuser", 23 | "tlhelp32", 24 | "psapi", 25 | "securitybaseapi", 26 | "libloaderapi", 27 | "synchapi", 28 | "wow64apiset", 29 | "processthreadsapi", 30 | "handleapi", 31 | "winbase", 32 | "impl-default", 33 | ] 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 wellwell.work, LLC by Zoe 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Hello Box Demo 4 | 5 | # `injrs` 6 | 7 | **DLL injector library and tool written in Rust. Rust 实现的DLL注入工具/库** 8 | 9 | [![CI](https://github.com/jiusanzhou/injrs/actions/workflows/ci.yml/badge.svg)](https://github.com/jiusanzhou/injrs/actions/workflows/ci.yml) 10 | [![crates.io](https://img.shields.io/crates/v/injrs.svg)](https://crates.io/crates/injrs) 11 | [![Documentation](https://docs.rs/injrs/badge.svg)](https://docs.rs/injrs) 12 | [![dependency status](https://deps.rs/repo/github/jiusanzhou/injrs/status.svg)](https://deps.rs/repo/github/jiusanzhou/injrs) 13 | [![Apache-2.0](https://img.shields.io/crates/l/injrs.svg)](https://github.com/jiusanzhou/injrs/blob/master/LICENSE) 14 | 15 |
16 | 17 | ## Install 18 | 19 | Go to [releases page](releases) download the latest binary. 20 | 21 | Or if you have rust installed, use cargo to install: 22 | ```bash 23 | cargo install injrs 24 | ``` 25 | 26 | Install rust if you don't have. 27 | ```bash 28 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 29 | ``` 30 | 31 | ## Usage 32 | 33 | At most time, you can use `injrs` as a simple tool. 34 | 35 | ```bash 36 | USAGE: 37 | injrs PROCESS_NAME/PID [Libraies...] 38 | 39 | EXAMPLES: 40 | 1. Inject test.dll to process Calc.exe 41 | $ injrs Calc.exe test.dll 42 | 43 | 2. Inject test.dll and demo.dll to process with PID: 1888 44 | $ injrs 1888 test.dll demo.dll 45 | ``` 46 | 47 | ## DLL Demo 48 | 49 | The code in [examples/hellobox](./examples/hellobox) is a simple message box dll for testing injector. 50 | 51 | You can build with command: 52 | ```bash 53 | cargo build --release --example hellobox 54 | ``` 55 | 56 | Build target will locate in: 57 | ```bash 58 | target/i686-pc-windows-msvc/release/examples/hellobox.dll 59 | ``` 60 | 61 | Try to inject the demo dll to your target process: 62 | ```bash 63 | injrs PID/PNAME target/i686-pc-windows-msvc/release/examples/hellobox.dll 64 | ``` 65 | 66 | ## Usage as library 67 | 68 | You also can write a injector project using `injrs` as a library. 69 | 70 | ```rust 71 | use injrs::process_windows::*; 72 | use injrs::inject_windows::*; 73 | 74 | fn main() { 75 | let name = "Calc.exe"; 76 | let dll = "./my-demo-dll.dll"; 77 | let process = Process::find_first_by_name(name).unwrap(); 78 | 79 | print!("inject dll to process => "); 80 | match process.inject(dll) { 81 | Err(e) => { 82 | println!("error: {}", e); 83 | }, 84 | Ok(_) => { 85 | println!("success"); 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | for more detail you can check [src/main.rs](./src/main.rs). 92 | -------------------------------------------------------------------------------- /examples/hellobox.rs: -------------------------------------------------------------------------------- 1 | use winapi::shared::minwindef::{ 2 | HINSTANCE, DWORD, LPVOID, BOOL, TRUE 3 | }; 4 | use winapi::um::winnt::{ 5 | DLL_PROCESS_DETACH, DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH 6 | }; 7 | use winapi::um::libloaderapi::{ 8 | DisableThreadLibraryCalls 9 | }; 10 | use winapi::um::winuser::{ 11 | MessageBoxA, MessageBoxW, MB_OK 12 | }; 13 | 14 | use std::ptr::null_mut; 15 | use std::ffi::OsStr; 16 | use std::os::windows::ffi::OsStrExt as _; 17 | 18 | fn wide_string(s: &str) -> Vec { 19 | OsStr::new(s).encode_wide().chain(Some(0)).collect() 20 | } 21 | 22 | fn show_message_box(caption: &str, text: &str) { 23 | unsafe{ 24 | MessageBoxW( 25 | null_mut() as _, 26 | wide_string(text).as_ptr() as _, 27 | wide_string(caption).as_ptr() as _, 28 | MB_OK 29 | ); 30 | } 31 | } 32 | 33 | // define dllmain to handle the init action 34 | #[no_mangle] 35 | #[allow(non_snake_case)] 36 | unsafe extern "system" fn DllMain(hinst: HINSTANCE, reason: DWORD, _reserved: LPVOID) -> BOOL { 37 | match reason { 38 | DLL_PROCESS_DETACH => { 39 | println!("Remove from main process."); 40 | } 41 | DLL_PROCESS_ATTACH => unsafe { 42 | println!("Attach to main process."); 43 | show_message_box("Hello World", "I'm injected by Rust."); 44 | DisableThreadLibraryCalls(hinst); 45 | }, 46 | DLL_THREAD_ATTACH => {} 47 | DLL_THREAD_DETACH => {} 48 | _ => {} 49 | }; 50 | 51 | return TRUE; 52 | } -------------------------------------------------------------------------------- /hello-box-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiusanzhou/injrs/d30ebce6200705989b5f07e4fb184abcef74b256/hello-box-demo.png -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | const UNKNOWN_ERROR_MESSAGE: &'static str = "unknown error"; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Win32Error { 7 | // Code 8 | pub code: u32, 9 | pub message: Option, 10 | } 11 | 12 | impl Win32Error { 13 | 14 | pub fn new() -> Self { 15 | Self{code: 0, message: None} 16 | } 17 | 18 | } 19 | 20 | unsafe impl Sync for Win32Error {} 21 | 22 | unsafe impl Send for Win32Error {} 23 | 24 | impl fmt::Display for Win32Error { 25 | 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | match self.message.as_ref() { 28 | Some(s) => format!("{}: {}", self.code, s), 29 | None => format!("{}: {}", self.code, UNKNOWN_ERROR_MESSAGE), 30 | }.fmt(f) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/evelate_windows.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::ptr::null_mut; 3 | use std::ffi::CString; 4 | use std::os::windows::ffi::OsStringExt; 5 | 6 | use crate::utils::*; 7 | use crate::winapi::*; 8 | 9 | // elevate privileges 10 | pub fn evelate_privileges() -> Result<(), io::Error> { 11 | let mut htk: HANDLE = null_mut(); 12 | let mut tkp = TOKEN_PRIVILEGES { 13 | PrivilegeCount: 1, 14 | Privileges: [LUID_AND_ATTRIBUTES { 15 | Attributes: SE_PRIVILEGE_ENABLED, 16 | ..Default::default() 17 | }], 18 | }; 19 | if FALSE == unsafe{OpenProcessToken(GetCurrentProcess(), 20 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &mut htk)} { 21 | println!("open process token failed"); 22 | return Err(get_last_error()); 23 | } 24 | 25 | if FALSE == unsafe{LookupPrivilegeValueA(null_mut(), 26 | CString::new(SE_DEBUG_NAME)?.as_ptr() as _, &mut tkp.Privileges[0].Luid)} { 27 | println!("lookup privilege value failed"); 28 | return Err(get_last_error()); 29 | } 30 | 31 | if FALSE == unsafe{AdjustTokenPrivileges(htk, FALSE, &mut tkp, 0, null_mut(), null_mut())} { 32 | println!("adjust token privilege failed"); 33 | return Err(get_last_error()); 34 | } 35 | 36 | Ok(()) 37 | } -------------------------------------------------------------------------------- /src/inject_windows.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::mem::{self, MaybeUninit}; 3 | use std::ptr::null_mut; 4 | use std::path::Path; 5 | 6 | use crate::utils::*; 7 | use crate::winapi::*; 8 | use crate::process_windows::*; 9 | 10 | // https://gist.github.com/sum-catnip/00491d030f69918e96369ce900b24d52 11 | 12 | // open the process 13 | // call remote thread 14 | 15 | pub trait InjectorExt { 16 | fn is_injected(&self, dll: &str) -> Result; 17 | fn inject(&self, dll: &str) -> Result<(), io::Error>; 18 | fn eject(&self, dll: &str) -> Result<(), io::Error>; 19 | } 20 | 21 | impl InjectorExt for Process { 22 | 23 | fn is_injected(&self, dll: &str) -> Result { 24 | todo!() 25 | } 26 | 27 | fn inject(&self, dll: &str) -> Result<(), io::Error> { 28 | // make sure dll exits 29 | let fullpath = Path::new(dll).canonicalize(); 30 | if fullpath.is_err() { 31 | return Err(fullpath.unwrap_err()); 32 | } 33 | let fullpath = fullpath.unwrap(); 34 | let dll = fullpath.to_str().unwrap(); 35 | 36 | let path_wstr = to_wide_string(dll); 37 | 38 | // length from wide string and add the last \0 39 | let path_len = path_wstr.len() * 2 + 1; 40 | 41 | // alloc memorry in the process to store dll path name 42 | let r_path_addr = unsafe{VirtualAllocEx(self.handle, null_mut(), path_len, 43 | MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE)}; 44 | 45 | // check if the addr is null 46 | if r_path_addr.is_null() { 47 | return Err(io::Error::new(io::ErrorKind::Other, "alloc memorry failed")); 48 | } 49 | 50 | // write dll path to the remote 51 | let r = unsafe{WriteProcessMemory(self.handle, r_path_addr, 52 | path_wstr.as_ptr() as _, path_len, null_mut())}; 53 | 54 | // check written result 55 | if r == FALSE { 56 | return Err(io::Error::new(io::ErrorKind::Other, "write data to memorry failed")); 57 | } 58 | 59 | // get method address from process 60 | let r_func_addr = unsafe{GetProcAddress( 61 | GetModuleHandleA("kernel32.dll\0".as_ptr() as _), 62 | "LoadLibraryW\0".as_ptr() as _, 63 | )}; 64 | 65 | // check if the addr is null 66 | if r_func_addr.is_null() { 67 | return Err(io::Error::new(io::ErrorKind::Other, "get load func memorry failed")); 68 | } 69 | 70 | // create remote thread to call load library 71 | let t_handle = unsafe{CreateRemoteThread( 72 | self.handle, 73 | null_mut(), 74 | 0, 75 | Some(mem::transmute(r_func_addr)), 76 | r_path_addr, 77 | 0, 78 | null_mut() 79 | )}; 80 | if t_handle.is_null() { 81 | println!("create remote thread failed"); 82 | return Err(get_last_error()); 83 | } 84 | 85 | // wait for thread 86 | let r = unsafe{WaitForSingleObject(t_handle, 100)}; // INFINITE 87 | if r == WAIT_FAILED { 88 | // println!("==== wait for single object"); 89 | return Err(get_last_error()); 90 | } 91 | 92 | // // try to get exit code of thread 93 | // let mut v = MaybeUninit::uninit(); 94 | // let r = unsafe {GetExitCodeThread(t_handle, v.as_mut_ptr())}; 95 | // if r == FALSE { 96 | // return Err(get_last_error()); 97 | // } 98 | // let v = unsafe {v.assume_init()}; 99 | 100 | // release the r_path_addr 101 | unsafe{VirtualFreeEx(self.handle, r_path_addr, 1, MEM_DECOMMIT)}; 102 | 103 | // close the thread handle 104 | unsafe{CloseHandle(t_handle)}; 105 | 106 | Ok(()) 107 | } 108 | 109 | fn eject(&self, dll: &str) -> Result<(), io::Error> { 110 | todo!() 111 | } 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | use crate::process_windows::*; 117 | use super::InjectorExt; 118 | use std::path::Path; 119 | use std::fs; 120 | 121 | #[test] 122 | fn test_path() { 123 | println!("{:?}", ::std::env::current_dir().unwrap()); 124 | // target/i686-pc-windows-msvc/release/example.dll 125 | let path = Path::new("./example").canonicalize().unwrap(); 126 | for entry in fs::read_dir(path).unwrap() { 127 | println!("===> {:?}", entry.unwrap()); 128 | } 129 | } 130 | 131 | #[test] 132 | fn inject_example() { 133 | use crate::evelate_windows::*; 134 | 135 | let _ = evelate_privileges(); 136 | 137 | println!("Hello injector!"); 138 | let p = Process::find_first_by_name("WeChat.exe").unwrap(); 139 | let r = p.inject("./example/target/i686-pc-windows-msvc/release/example.dll"); 140 | match r { 141 | Err(e) => println!("inject error: {}", e), 142 | Ok(_) => println!("inject success"), 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod process_windows; 2 | pub mod inject_windows; 3 | pub mod evelate_windows; 4 | pub mod winapi; 5 | pub mod utils; 6 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | use injrs::process_windows::*; 3 | use injrs::inject_windows::*; 4 | use injrs::evelate_windows::*; 5 | 6 | const USAGE_HELP: &str = " 7 | USAGE: 8 | injrs PROCESS_NAME/PID [Libraies...] 9 | 10 | EXAMPLES: 11 | 1. Inject test.dll to process Calc.exe 12 | $ injrs Calc.exe test.dll 13 | 14 | 2. Inject test.dll and demo.dll to process with PID: 1888 15 | $ injrs 1888 test.dll demo.dll 16 | "; 17 | 18 | fn main() { 19 | println!("Welcome to have injrs. A library injector written by Rust."); 20 | // load args 21 | let mut args = std::env::args(); 22 | if args.len() < 2 { 23 | println!("{}", USAGE_HELP); 24 | return 25 | } 26 | let pid_or_name = args.nth(1).expect("must give a process name or pid"); 27 | let dlls: Vec = std::env::args().skip(2).collect(); 28 | if dlls.len() == 0 { 29 | println!("You at least give one file to inject"); 30 | return 31 | } 32 | 33 | let process: Process; 34 | match pid_or_name.parse::() { 35 | Ok(pid) => { 36 | // process pid 37 | match Process::from_pid(pid) { 38 | None => { 39 | println!("can't find process with pid: {}", pid); 40 | return 41 | }, 42 | Some(p) => process = p 43 | } 44 | }, 45 | Err(_) => { 46 | let name = pid_or_name.as_str(); 47 | // process name 48 | match Process::find_first_by_name(name) { 49 | None => { 50 | println!("can't find process with name: {}", name); 51 | return 52 | }, 53 | Some(p) => process = p 54 | } 55 | } 56 | } 57 | 58 | let _ = evelate_privileges(); 59 | 60 | for i in dlls { 61 | print!("{} => ", i); 62 | match process.inject(i.as_str()) { 63 | Err(e) => { 64 | println!("error: {}", e); 65 | }, 66 | Ok(_) => { 67 | println!("success"); 68 | } 69 | } 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /src/process_windows.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | 3 | use crate::winapi::*; 4 | use crate::utils::*; 5 | 6 | use std::io; 7 | use std::mem::{self, MaybeUninit}; 8 | 9 | use std::ffi::OsString; 10 | use std::os::windows::ffi::OsStringExt; 11 | 12 | 13 | pub type ProcessHandle = HANDLE; 14 | 15 | 16 | #[derive(Debug, Clone, PartialEq, Eq)] 17 | pub struct Process { 18 | pub pid: u32, 19 | pub name: String, 20 | pub handle: ProcessHandle, 21 | } 22 | 23 | // Create and init the process struct 24 | impl Process { 25 | 26 | pub fn new(handle: ProcessHandle, pid: u32, name: &str) -> Self { 27 | Self { 28 | pid: pid, 29 | name: name.into(), 30 | handle, 31 | } 32 | } 33 | 34 | pub fn from_pid(pid: u32) -> Option { 35 | 36 | // open process by pid, bacause we need to write message 37 | // so for simple just open as all access flag 38 | let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) }; 39 | if handle.is_null() { 40 | return None; 41 | } 42 | 43 | let name = get_process_name(handle); 44 | 45 | Some(Self::new(handle, pid, name.as_str())) 46 | } 47 | 48 | pub fn from_pid_and_name(pid: u32, name: &str) -> Option { 49 | // open process by pid, bacause we need to write message 50 | // so for simple just open as all access flag 51 | let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) }; 52 | if handle.is_null() { 53 | return None; 54 | } 55 | 56 | Some(Self::new(handle, pid, name)) 57 | } 58 | 59 | pub fn find_first_by_name(name: &str) -> Option { 60 | match find_process_by_name(name).unwrap_or_default().first() { 61 | // TODO: ugly, shoudl implement copy trait for process 62 | Some(v) => Process::from_pid(v.pid), 63 | None => None 64 | } 65 | } 66 | 67 | pub fn find_all_by_name(name: &str) -> Vec { 68 | match find_process_by_name(name) { 69 | Ok(v) => v, 70 | Err(_) => Vec::new(), 71 | } 72 | } 73 | 74 | pub fn from_handle(handle: ProcessHandle) -> Self { 75 | 76 | let pid = unsafe { GetProcessId(handle) as u32 }; 77 | 78 | let name = get_process_name(handle); 79 | 80 | Self {pid, name, handle} 81 | } 82 | 83 | } 84 | 85 | impl Process { 86 | pub fn close(&self) -> io::Result<()> { 87 | if self.handle.is_null() { 88 | return Ok(()); 89 | } 90 | let result = unsafe{ CloseHandle(self.handle) }; 91 | if result != 0 { 92 | return Ok(()); 93 | } 94 | Err(get_last_error()) 95 | } 96 | 97 | pub fn find_module_by_name(dllname: &str) -> Option { 98 | todo!() 99 | } 100 | 101 | pub fn is_wow64(&self) -> Result { 102 | let mut is_wow64 = MaybeUninit::uninit(); 103 | let r = unsafe{IsWow64Process(self.handle, is_wow64.as_mut_ptr())}; 104 | if r == FALSE { 105 | return Err(get_last_error()); 106 | } 107 | Ok(unsafe{is_wow64.assume_init()} == TRUE) 108 | } 109 | 110 | } 111 | 112 | impl Drop for Process { 113 | fn drop(&mut self) { 114 | let _ = self.close(); 115 | } 116 | } 117 | 118 | pub fn get_process_name(handle: ProcessHandle) -> String { 119 | let mut buf = [0u16; MAX_PATH + 1]; 120 | unsafe { 121 | GetModuleBaseNameW(handle, 0 as _, buf.as_mut_ptr(), MAX_PATH as DWORD + 1); 122 | return wchar_to_string(&buf) 123 | }; 124 | } 125 | 126 | pub fn get_process_path(handle: ProcessHandle) -> String { 127 | let mut buf = [0u16; MAX_PATH + 1]; 128 | unsafe { 129 | GetModuleFileNameExW(handle, 0 as _, buf.as_mut_ptr(), MAX_PATH as DWORD + 1); 130 | return wchar_to_string(&buf); 131 | } 132 | } 133 | 134 | // TODO: accept callback function 135 | pub fn find_process_by_name(name: &str) -> Result, io::Error> { 136 | let handle = unsafe { 137 | CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 as _) 138 | }; 139 | 140 | if handle.is_null() || handle == INVALID_HANDLE_VALUE { 141 | return Err(get_last_error()); 142 | } 143 | 144 | // the result to store process list 145 | let mut result: Vec = Vec::new(); 146 | 147 | let mut _name: String; 148 | 149 | // can't reuse 150 | let mut entry: PROCESSENTRY32 = unsafe { ::std::mem::zeroed() }; 151 | entry.dwSize = mem::size_of::() as u32; 152 | 153 | while 0 != unsafe { Process32Next(handle, &mut entry) } { 154 | // extract name from entry 155 | _name = char_to_string(&entry.szExeFile); 156 | // clean entry exefile filed 157 | entry.szExeFile = unsafe { ::std::mem::zeroed() }; 158 | 159 | if name.len() > 0 && !_name.contains(name) { 160 | // ignore if name has set but not match the exefile name 161 | continue; 162 | } 163 | // parse process and push to result vec 164 | // TODO: improve reuse the name and other information 165 | match Process::from_pid_and_name(entry.th32ProcessID, _name.as_str()) { 166 | Some(v) => result.push(v), 167 | None => {}, 168 | } 169 | 170 | } 171 | 172 | Ok(result) 173 | } 174 | 175 | fn char_to_string(chars : &[i8]) -> String { 176 | chars.into_iter().map(|c| { *c as u8 as char }).collect() 177 | } 178 | 179 | pub fn wchar_to_string(slice: &[u16]) -> String { 180 | match slice.iter().position(|&x| x == 0) { 181 | Some(pos) => OsString::from_wide(&slice[..pos]) 182 | .to_string_lossy() 183 | .into_owned(), 184 | None => OsString::from_wide(slice).to_string_lossy().into_owned(), 185 | } 186 | } 187 | 188 | 189 | #[cfg(test)] 190 | mod tests { 191 | use super::*; 192 | 193 | #[test] 194 | fn normalize() { 195 | Process::new(0 as _, 0, ""); 196 | } 197 | 198 | #[test] 199 | fn get_first_process() { 200 | match Process::find_first_by_name("Code.exe") { 201 | Some(v) => { 202 | println!("get the first process: {}", v.name); 203 | }, 204 | None => {}, 205 | } 206 | } 207 | 208 | #[test] 209 | fn get_process() { 210 | println!("get process:"); 211 | match find_process_by_name("Code.exe") { 212 | Ok(v) => { 213 | println!("get process count: {}", v.len()); 214 | for x in &v { 215 | println!("{} {}", x.pid, x.name); 216 | } 217 | }, 218 | Err(e) => eprintln!("find process by name error: {}", e) 219 | } 220 | } 221 | } -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::ffi::OsStr; 3 | use std::os::windows::ffi::OsStrExt as _; 4 | 5 | #[inline(always)] 6 | pub fn get_last_error() -> io::Error { 7 | io::Error::last_os_error() 8 | } 9 | 10 | pub fn to_wide_string(s: &str) -> Vec { 11 | OsStr::new(s).encode_wide().chain(Some(0)).collect() 12 | } -------------------------------------------------------------------------------- /src/winapi.rs: -------------------------------------------------------------------------------- 1 | 2 | pub use winapi::shared::ntdef::{ 3 | HANDLE, 4 | NULL, 5 | }; 6 | 7 | pub use winapi::shared::minwindef::{ 8 | FALSE, 9 | TRUE, 10 | MAX_PATH, 11 | DWORD, 12 | HMODULE 13 | }; 14 | 15 | pub use winapi::um::processthreadsapi::{ 16 | GetProcessId, 17 | GetCurrentProcess, 18 | OpenProcess, 19 | OpenProcessToken, 20 | CreateRemoteThread, 21 | GetExitCodeThread 22 | }; 23 | 24 | pub use winapi::um::handleapi::{ 25 | CloseHandle, 26 | INVALID_HANDLE_VALUE 27 | }; 28 | 29 | pub use winapi::um::tlhelp32::{ 30 | CreateToolhelp32Snapshot, 31 | Process32Next, 32 | TH32CS_SNAPPROCESS, 33 | PROCESSENTRY32, MODULEENTRY32, 34 | }; 35 | 36 | pub use winapi::um::psapi::{ 37 | GetModuleBaseNameW, 38 | GetModuleFileNameExW 39 | }; 40 | 41 | pub use winapi::um::winnt::{ 42 | PROCESS_ALL_ACCESS, 43 | MEM_COMMIT, 44 | MEM_DECOMMIT, 45 | MEM_RELEASE, 46 | MEM_RESERVE, 47 | PAGE_READWRITE, 48 | PAGE_EXECUTE_READWRITE, 49 | TOKEN_PRIVILEGES, 50 | TOKEN_ADJUST_PRIVILEGES, 51 | TOKEN_QUERY, 52 | SE_PRIVILEGE_ENABLED, 53 | SE_DEBUG_NAME, 54 | LUID_AND_ATTRIBUTES 55 | }; 56 | 57 | pub use winapi::um::memoryapi::{ 58 | ReadProcessMemory, 59 | WriteProcessMemory, 60 | VirtualAllocEx, 61 | VirtualFreeEx, 62 | }; 63 | 64 | pub use winapi::um::libloaderapi::{ 65 | GetModuleHandleA, 66 | GetProcAddress 67 | }; 68 | 69 | pub use winapi::um::synchapi::{ 70 | WaitForSingleObject 71 | }; 72 | 73 | pub use winapi::um::wow64apiset::{ 74 | IsWow64Process 75 | }; 76 | 77 | pub use winapi::um::winbase::{ 78 | INFINITE, 79 | WAIT_FAILED, 80 | LookupPrivilegeValueA, 81 | CREATE_SUSPENDED, 82 | CREATE_NEW_CONSOLE 83 | }; 84 | 85 | pub use winapi::um::securitybaseapi::{ 86 | AdjustTokenPrivileges 87 | }; --------------------------------------------------------------------------------