Skip to content

Instantly share code, notes, and snippets.

@jabbate19
Created September 7, 2023 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jabbate19/619e61387bed07bf06d6988f115748b9 to your computer and use it in GitHub Desktop.
Save jabbate19/619e61387bed07bf06d6988f115748b9 to your computer and use it in GitHub Desktop.
Windows Service Integration in Rust w/ C API
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