Created
September 7, 2023 15:17
-
-
Save jabbate19/619e61387bed07bf06d6988f115748b9 to your computer and use it in GitHub Desktop.
Windows Service Integration in Rust w/ C API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use windows_sys::Win32::{ | |
System::{ | |
Services::{SERVICE_STATUS, ControlService, OpenServiceA, SC_MANAGER_ALL_ACCESS, SERVICE_STATUS_PROCESS, ENUM_SERVICE_STATUS_PROCESSA, OpenSCManagerA, EnumServicesStatusExA, SC_ENUM_PROCESS_INFO, SERVICE_STATE_ALL, SERVICE_WIN32, SC_MANAGER_ENUMERATE_SERVICE, SC_MANAGER_CONNECT} | |
}, | |
Foundation::GetLastError | |
}; | |
use anyhow::{anyhow, Result}; | |
use std::{ | |
ffi::{CStr, CString}, | |
ptr::null | |
}; | |
#[derive(Debug)] | |
pub struct ServiceStatusProcess { | |
service_type: u32, | |
current_state: u32, | |
controls_accepted: u32, | |
win32_exit_code: u32, | |
service_specific_exit_code: u32, | |
check_point: u32, | |
wait_hint: u32, | |
process_id: u32, | |
service_flags: u32 | |
} | |
impl From<SERVICE_STATUS_PROCESS> for ServiceStatusProcess { | |
fn from(value: SERVICE_STATUS_PROCESS) -> Self { | |
ServiceStatusProcess { | |
service_type: value.dwServiceType, | |
current_state: value.dwCurrentState, | |
controls_accepted: value.dwControlsAccepted, | |
win32_exit_code: value.dwWin32ExitCode, | |
service_specific_exit_code: value.dwServiceSpecificExitCode, | |
check_point: value.dwCheckPoint, | |
wait_hint: value.dwWaitHint, | |
process_id: value.dwProcessId, | |
service_flags: value.dwServiceFlags | |
} | |
} | |
} | |
#[derive(Debug)] | |
pub struct ServiceStatus { | |
service_type: u32, | |
current_state: u32, | |
controls_accepted: u32, | |
win32_exit_code: u32, | |
service_specific_exit_code: u32, | |
check_point: u32, | |
wait_hint: u32, | |
} | |
impl From<[u32; 7]> for ServiceStatus { | |
fn from(value: [u32;7]) -> Self { | |
ServiceStatus { | |
service_type: value[0], | |
current_state: value[1], | |
controls_accepted: value[2], | |
win32_exit_code: value[3], | |
service_specific_exit_code: value[4], | |
check_point: value[5], | |
wait_hint: value[6] | |
} | |
} | |
} | |
#[derive(Debug)] | |
pub struct EnumServiceStatusProcess { | |
service_name: String, | |
display_name: String, | |
service_status_process: ServiceStatusProcess | |
} | |
pub fn get_services() -> Result<Vec<EnumServiceStatusProcess>> { | |
let mut service_vec: Vec<EnumServiceStatusProcess> = Vec::new(); | |
unsafe { | |
let mgr = OpenSCManagerA(null(), null(), SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); | |
if mgr == 0 { | |
return Err(anyhow!("Failed to get SC Manager: {}", GetLastError() as u32)) | |
} | |
let mut num_services: u32 = 0; | |
let mut remaining_bytes: u32 = 0; | |
let handle: *mut u32 = 0 as *mut u32; | |
let mut services = [0u8; 250000]; | |
let service_enum = EnumServicesStatusExA( | |
mgr, // hscmanager | |
SC_ENUM_PROCESS_INFO, // infolevel | |
SERVICE_WIN32, // dwservicetype | |
SERVICE_STATE_ALL, // dwservicestate | |
&mut services[0] as *mut u8, // lpservices | |
250000, | |
&mut remaining_bytes as *mut u32, // pcbbytesneeded | |
&mut num_services as *mut u32, // lpservicesreturned | |
handle, // lpresumehandle | |
null() // pszgroupname | |
); | |
if service_enum == 0 { | |
return Err(anyhow!("Failed to get Services: {}", GetLastError() as u32)) | |
} | |
for i in 0..num_services { | |
let service = (&services[0] as *const u8 as *const ENUM_SERVICE_STATUS_PROCESSA).offset(i as isize); | |
service_vec.push(EnumServiceStatusProcess { | |
service_name: CStr::from_ptr((*service).lpServiceName as *const i8).to_str().unwrap().to_owned(), | |
display_name: CStr::from_ptr((*service).lpDisplayName as *const i8).to_str().unwrap().to_owned(), | |
service_status_process: ServiceStatusProcess::from((*service).ServiceStatusProcess) | |
}); | |
} | |
} | |
Ok(service_vec) | |
} | |
fn service_action(service: String, action: String) -> Result<ServiceStatus> { | |
let action_hex = match action.as_str() { | |
"continue" => 0x00000003, | |
"interrogate" => 0x00000004, | |
"netbindadd" => 0x00000007, | |
"netbinddisable" => 0x0000000A, | |
"netbindenable" => 0x00000009, | |
"netbindremove" => 0x00000008, | |
"paramchange" => 0x00000006, | |
"pause" => 0x00000002, | |
"stop" => 0x00000001, | |
a => return Err(anyhow!("Bad Action: {}", a)) | |
}; | |
let c_service = CString::new(service)?; | |
let mut status_data = [0u32; 7]; | |
unsafe { | |
let mgr = OpenSCManagerA(null(), null(), SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); | |
if mgr == 0 { | |
return Err(anyhow!("Failed to get SC Manager: {}", GetLastError() as u32)) | |
} | |
let service = OpenServiceA(mgr, c_service.as_ptr() as *const u8 ,SC_MANAGER_ALL_ACCESS); | |
if service == 0 { | |
return Err(anyhow!("Failed to get Service: {}", GetLastError() as u32)) | |
} | |
let status: *mut u32 = &mut status_data as *mut u32; | |
let control = ControlService(service, action_hex, status as *mut SERVICE_STATUS); | |
if control == 0 { | |
return Err(anyhow!("Failed to control Service: {}", GetLastError() as u32)) | |
} | |
} | |
Ok(ServiceStatus::from(status_data)) | |
} | |
pub fn main() -> Result<()> { | |
service_action("InstallService".to_string(), "stop".to_string())?; | |
println!("{:#?}", get_services()?); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment