NetUserEnum => *mut LPBYTE into struct

Hello, I need help and I thank you in advance.

I want to retrieve all users present on a Windows machine and I try to use:
winapi::um::lmaccess::NetUserEnum.

Here is the Microsoft page of this method with C++ code at the bottom of the page:

Here's a piece of code I wrote:

    let mut buffer_ptr :Vec<u8> = Vec::with_capacity(2048 as usize);
    let mut entries_read: DWORD = 0;
    let mut total_entries: DWORD = 0;
    let mut resume_handle: DWORD = 0;

    let n_status = unsafe {
        NetUserEnum(
            null(),
            1,
            0,
            buffer_ptr.as_mut_ptr() as *mut LPBYTE,
            MAX_PREFERRED_LENGTH,
            &mut entries_read,
            &mut total_entries,
            &mut resume_handle,
        )
    };

    let t: USER_INFO_0 = unsafe { transmute_copy(&buffer_ptr) };

    println!("n_status {:?}", n_status);
    println!("entries_read {:?}", entries_read);
    println!("total_entries {:?}", total_entries);
    println!("buffer_ptr {:?}", buffer_ptr);
    println!("t {:?}", t.usri0_name);

Here the results of println!:

n_status 0
entries_read 5
total_entries 5
buffer_ptr []
t 0x2596b8

How can I get the username of all users?

When I call the usri0_name function, I get 0x2596b8

Thank you and have a good day.

You are printing the address of the pointer, but you need to access the memory and read the value. You can use this function to turn the pointer into a slice:

// This will compute the length
unsafe fn widestr_len(mut ptr: *const u16) -> usize {
    let mut len = 0;
    while *ptr != 0 {
        ptr = ptr.add(1);
        len += 1;
    }
    len
}
// pass your pointer to this function
unsafe fn to_slice<'a>(ptr: *const u16) -> &'a [u16] {
    std::slice::from_raw_parts(ptr, widestr_len(ptr))
}

You can then use OsStringExt::from_wide to create an os string. You can then use OsString::into_string to convert it into an ordinary string you can print normally.

The conversion will fail if the string is not valid utf-8, however the winapi documentation promises to deliver valid utf-8, so you can just unwrap the call to into_string.

Hello,

Thank you for your answer. I changed the code:

    let mut buffer_ptr :Vec<u8> = Vec::with_capacity(2048 as usize);
    let mut entries_read: DWORD = 0;
    let mut total_entries: DWORD = 0;
    let mut resume_handle: DWORD = 0;

    let n_status = unsafe {
        NetUserEnum(
            null(),
            1,
            0,
            buffer_ptr.as_mut_ptr() as *mut LPBYTE,
            MAX_PREFERRED_LENGTH,
            &mut entries_read,
            &mut total_entries,
            &mut resume_handle,
        )
    };

    let user_info_0: USER_INFO_0 = unsafe { transmute_copy(&buffer_ptr) };
    let sl = unsafe { to_slice(user_info_0.usri0_name) };
    let username =  OsString::from_wide(sl).into_string().unwrap();

    println!("n_status {:?}", n_status);
    println!("entries_read {:?}", entries_read);
    println!("total_entries {:?}", total_entries);
    println!("username {}",  username);

Here is the result:

n_status 0
entries_read 5
total_entries 5
username 젠?胘?

The username seems incorrect, I'm using an alphabet with letters abcdef... I'm sorry, I'm very beginer.

Hmm that's weird, I must have misunderstood something then. I'm not familiar with the api, so I don't know then.

Ok no problem, thank you for everything and have a good end of day.

Hi

I've been playing with the same APIs in the last few days and found the following works very reliably:

fn get_local_users() -> Vec<String> {
    use winapi::{
        um::lmaccess::{
            NetUserEnum,
            USER_INFO_0
        },
        shared::{
            lmcons::MAX_PREFERRED_LENGTH,
            minwindef::{
                DWORD,
                LPDWORD
            },
            ntdef::{
                LPCWSTR
            },
            winerror::{
                ERROR_MORE_DATA
            }
        },
        
    };
    use std::ptr;
    use std::mem::transmute_copy;
    use widestring::WideCString;
    
    let mut result:Vec<String> = Vec::new();

    let server:LPCWSTR = ptr::null();
    let level: DWORD = 1;
    let filter: DWORD = 0;
    let mut entries_read: DWORD = 0;
    let mut total_entries: DWORD = 0;
    let resume_handle: LPDWORD = ptr::null_mut();

    loop {
        let mut users_buffer_ptr = ptr::null_mut();
        let api_ret = unsafe {
            NetUserEnum(server,
                level,
                filter,
                &mut users_buffer_ptr,
                MAX_PREFERRED_LENGTH,
                &mut entries_read,
                &mut total_entries,
                resume_handle)
        };

        // ADD BETTER ERROR HANDLING
        if api_ret != 0 && api_ret != ERROR_MORE_DATA {
            break // API Failed
        }

        let mut tmpbuffer = users_buffer_ptr;  // Copy pointer to temporary buffer
        for _i in 0..entries_read as usize { // Iterate over read entries
            let slice:&[u8] = unsafe { 
                std::slice::from_raw_parts(tmpbuffer, 1 as usize) 
            };
            let p_user:USER_INFO_0 = unsafe { 
                transmute_copy(&slice[0]) 
            };
            let user_name_wc:WideCString = unsafe { 
                WideCString::from_ptr_str(p_user.usri0_name) 
            };
            let user_name:String = user_name_wc.to_string_lossy();
            result.push(user_name);
            tmpbuffer = unsafe { 
                tmpbuffer.add(56)
            };
        }
        if api_ret != ERROR_MORE_DATA {
            break
        }
    }
    result
}

It's pretty similar with a number of the other APIs that return LPBYTES (NetLocalGroupEnum with a LOCALGROUP_INFO_1 requires you to add 16 to the buffer to get the next record; NetLocalGroupGetMembers with LOCALGROUP_MEMBERS_INFO_2 requires you to add 24 to get the next record; etc).

Hope this helps, or is valuable to others.

1 Like

Hi,

thank you for your answer. I will test your code in the next few days (after the new year ^^) and I will come back to give feedback.

Thank you,

Hi,

thank you very much, this code work well !!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.