Created
September 28, 2022 06:54
-
-
Save MaulingMonkey/40a91f0c4e217f74509280ec29e951c4 to your computer and use it in GitHub Desktop.
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
# ... | |
[dependencies] | |
windows.version = "0.41" | |
# ... |
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
#![forbid(unsafe_op_in_unsafe_fn)] | |
use windows::{ | |
Win32::Foundation::ERROR_MORE_DATA, | |
Win32::System::Services::*, | |
Win32::Security::*, | |
}; | |
use core::mem::align_of; | |
fn main() { | |
let sc_manager = unsafe { OpenSCManagerW(None, None, SC_MANAGER_ENUMERATE_SERVICE) }.expect("unable to open services for enumeration"); | |
let mut services = Vec::new(); | |
let services = unsafe { get_services_status(sc_manager, SERVICE_WIN32, SERVICE_STATE_ALL, &mut services) }.expect("unable to enumerate services"); | |
for service in services { | |
let service = service.into_inner(); | |
let service = ( | |
unsafe { service.lpServiceName.to_string() }.unwrap(), | |
unsafe { service.lpDisplayName.to_string() }.unwrap(), | |
service.ServiceStatus | |
); | |
dbg!(service); | |
} | |
} | |
/// \[[microsoft.com](https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-enumservicesstatusw) EnumServicesStatusW | |
/// | |
/// ### Safety | |
/// * May be UB if `sc_manager` isn't a valid service manager handle | |
/// * May be UB if `sc_manager` is simultaniously accessed from another thread | |
/// * May be UB if `service_type` or `service_state` aren't valid enumeration values | |
/// | |
/// It's also a bad idea to copy the returned ENUM_SERVICE_STATUSW s, as lpServiceName etc. doesn't properly borrow `buffer`. | |
/// However, it's `unsafe` to dereference those already. | |
unsafe fn get_services_status<'b>( | |
sc_manager: SC_HANDLE, | |
service_type: ENUM_SERVICE_TYPE, | |
service_state: ENUM_SERVICE_STATE, | |
buffer: &'b mut Vec<u8>, | |
) -> windows::core::Result<&'b [Unaligned<ENUM_SERVICE_STATUSW>]> { | |
buffer.clear(); | |
let mut bytes = 0; | |
let mut services = 0; | |
match unsafe { EnumServicesStatusW(sc_manager, service_type, service_state, None, 0, &mut bytes, &mut services, None) }.ok() { | |
Ok(()) => {}, // OK | |
Err(e) if e == ERROR_MORE_DATA.into() => {}, // OK | |
Err(e) => return Err(e), | |
} | |
buffer.reserve(bytes as _); | |
assert!(buffer.as_ptr() as usize % align_of::<ENUM_SERVICE_STATUSW>() == 0, "bug: vec buffer insufficiently aligned to read service status"); | |
// TODO: instead either: | |
// 1. overallocate buffer and then round ptr up to alignment | |
// 2. use wio::VariableSizedBox https://github.com/retep998/wio-rs/blob/9bf021178b2d02485f1bd35e6cff41bf52d4a9a2/src/vsb.rs | |
// 3. use firehazard::alloc::CBox::new_oversized? https://docs.rs/firehazard/*/firehazard/alloc/struct.CBox.html#method.new_oversized | |
// Only UB if passing a misaligned buffer to EnumServicesStatusW is UB | |
// Unaligned<T> prevents UB on the Rust side of things, although that's rather pointless thanks to this assert | |
unsafe { EnumServicesStatusW(sc_manager, service_type, service_state, Some(buffer.as_mut_ptr().cast()), bytes, &mut bytes, &mut services, None) }.ok()?; | |
unsafe { buffer.set_len(bytes as _) }; | |
Ok(unsafe { core::slice::from_raw_parts(buffer.as_ptr().cast(), services.try_into().unwrap_or(!0)) }) | |
} | |
#[repr(packed)] struct Unaligned<T>(T); | |
impl<T: Copy> Clone for Unaligned<T> { fn clone(&self) -> Self { *self }} // TODO: fancy manuallydrop shenannigans to weaken constraint to T: Clone | |
impl<T: Copy> Copy for Unaligned<T> {} | |
#[allow(dead_code)] impl<T> Unaligned<T> { | |
pub fn new(value: T) -> Self { Self(value) } | |
pub fn into_inner(self) -> T { let Self(value) = self; value } | |
} | |
const _ : () = assert!(core::mem::align_of::<Unaligned<ENUM_SERVICE_STATUSW>>() == 1, "Unaligned<T> failed to unalign T"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment