[Solved] WinAPI NetLocalGroupEnum crash

I am trying to get a list of the Local Groups (and eventually the users within those groups) on my Windows system. Using NetLocalGroupEnum in a C++ example worked fine, but when I convert this to Rust's WinAPI it crashes with exit code 3221225477. I have successfully used similar WinAPI code with NetUserEnum to grab the list of local user accounts, but the difference between them appears to be the PDWORD_PTR here, which I suspect I am using incorrectly in my call.

use std::ptr;
use winapi::{
    shared::{
        basetsd::PDWORD_PTR,
        lmcons::MAX_PREFERRED_LENGTH,
        minwindef::{DWORD, LPDWORD},
    },
    um::lmaccess::NetLocalGroupEnum,
};

fn main() {

    // [LPCWSTR] If this parameter is NULL, the local computer is used.
    let server = ptr::null();

    // [DWORD] information level of the data. 1 = LOCALGROUP_INFO_1
    let level: DWORD = 1;

    // [LPBYTE] Pointer to the address of the buffer that receives the information structure
    let mut group_buffer: Vec<u8> = Vec::with_capacity((MAX_PREFERRED_LENGTH) as usize);
    let group_buffer_ptr = group_buffer.as_mut_ptr() as *mut _ as *mut _;

    // [LPDWORD] count of elements actually enumerated.
    let entries_read: LPDWORD = 0u32 as *mut _;

    // [LPDWORD] approximate total number of entries that could have been enumerated from the current resume position
    let total_entries: LPDWORD = 0u32 as *mut _;

    // [PDWORD_PTR] resume handle that is used to continue an existing local group search
    let resume_handle: PDWORD_PTR = ptr::null_mut();

    // fails with exit code: 3221225477
    let ret = unsafe {
        NetLocalGroupEnum(
            server,
            level,
            group_buffer_ptr,
            MAX_PREFERRED_LENGTH,
            entries_read,
            total_entries,
            resume_handle,
        )
    };

    println!("NetLocalGroupEnum ret = {}", ret);
}

Thanks for any help!

1 Like

Disclaimer: I have no idea about what this function does and how WinAPI works.

For entries_read and total_entries you're passing 0u32 as *mut _ which is basically a null ptr. I think what you want might be passing a pointer to variable containing 0.

let mut entries_read: DWORD = 0;
let mut total_entries: DWORD = 0;

NetLocalGroupEnum(
    ...
    &mut entries_read,
    &mut total_entries,
    ...
)

Also, at this line:

let group_buffer_ptr = group_buffer.as_mut_ptr() as *mut _ as *mut _;

The casting is incorrect. group_buffer.as_mut_ptr() gives you *mut u8, which is the same type as LPBYTE. But in the signature of the function, you have *LPBYTE. What you should do instead is pass address to group_buffer_ptr variable. Also, it seems that Windows will allocate memory itself, so you shouldn't create a vec here:

let mut group_buffer_ptr: LPBYTE = ptr::null_mut();

NetLocalroupEnum(
    server,
    level,
    &mut group_buffer_ptr,
    ...
);

// NetApiBufferFree here


In general, avoid using as to cast pointers for FFI, as they cause troubles. Usually you can rely on &mut coercing to *mut and & coercing to *const. Use as only if API actually requires casting (eg. when it takes void*).

3 Likes

Thank you, @krdln - that was very helpful. I am now getting the correct zero return code, and the group_buffer is being populated with data. I'm now going to work on iterating through the buffer. Thanks again!

1 Like