Undefined reference to function even though it should be reachable - winapi

Error message:
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
|
<a lot of paths and args>
C:\Users\wikto\IdeaProjects\test/src/main.rs:297: undefined reference to `WaitForDebugEventEx'
Part of main.rs, I can share the rest if needed.
mod winapi;
use winapi::*;
unsafe fn debug_loop(&mut self, process_id: DWORD) {
let mut continue_status = DBG_CONTINUE;
let mut debug_event = std::mem::zeroed::<DEBUG_EVENT>();
let debug_event = &mut debug_event;
while WaitForDebugEventEx(debug_event, INFINITE) != 0 {
//something
}
}
Part of winapi.rs
#[link(name = "Kernel32")]
extern "C" {
pub fn WaitForDebugEventEx(
lpDebugEvent: *mut DEBUG_EVENT,
dwMilliseconds: DWORD,
) -> BOOL;
}
Is there anything I'm doing wrong or maybe it's a problem with mingw32-gcc?

Related

ReadProcessMemory returning errors

When I use the ReadProcessMemory function from the windows-rs package it returns 0. This is wrong, so I used the GetLastError() function and it returned "Only part of a ReadProcessMemory or WriteProcessMemory request was completed."
Here is the code for ReadProcessMemory:
unsafe{
let mut healthValue: i32 = 0;
ReadProcessMemory(
hProcess,
0xE6A848 as *mut c_void,
healthValue as *mut c_void,
std::mem::size_of::<i32> as usize,
&mut 0
);
println!("{}", healthValue);
match GetLastError().ok() {
Ok(_) => println!("no eror"),
Err(t) => println!("error: {}", t),
}
};
The code to get handle to the process:
let hProcess: HANDLE;
unsafe{
match OpenProcess(PROCESS_ALL_ACCESS, false, procId as u32) {
Ok(T) => hProcess = T,
Err(A) => {
hProcess = INVALID_HANDLE_VALUE;
println!("hproc is INVALID");
}
}
}
I have checked and the ProcId is right, I have used cheat engine to get the memory address, both apps are run in 32-bit, and my trainer is being run as admin. None of these have helped.
Fixed it by changing healthValue as *mut c_void to ptr::addr_of_mut!(healthValue) as *mut c_void.

How to safely wrap win32's PSTR or PWSTR in an Option type (e.g. Option<&OsStr>)?

I am trying to create a safe wrapper around some Win32 APIs using Microsoft's windows crate like so:
use windows::{Win32::Foundation::*, Win32::System::Threading::*};
fn create_process(app_name: &std::ffi::OsStr) -> bool {
let mut startup_info: STARTUPINFOW = unsafe { std::mem::zeroed() };
startup_info.cb = std::mem::size_of::<STARTUPINFOW>() as u32;
let mut process_info: PROCESS_INFORMATION = unsafe {std::mem::zeroed() };
unsafe {
let success = CreateProcessW(
app_name, // lpapplicationname
None, // lpcommandname
std::ptr::null(), // lpprocessattributes
std::ptr::null(), // lpthreadattributes
true, // binherithandles
CREATE_SUSPENDED, // dwcreationflags
std::ptr::null(), // lpenvironment
&startup_info, // lpstartupinfo
&mut process_info // lpprocessinformation
).as_bool();
success
}
}
fn main() {
let app = std::ffi::OsStr::new("C:\\Windows\\system32\\notepad.exe");
let success = create_process(app);
print!("{}", success);
}
This works as expected.
However, the documentation for CreateProcessW states that
The lpApplicationName parameter can be NULL.
Thus, I would like to wrap the &OsStr in an Option<&OsStr> so I can use None when no lpApplicationName is needed.
But I cannot find a way to convert from Option<&OsStr> to anything that satisfies IntoParam<'a, PWSTR> for lpApplicationName.
As of #1801 &OsStr should implement IntoParam<PWSTR>. Since null() also implements it, you should be able to write:
use windows::core::{IntoParam, Param};
let app_name_param: Param<'_, PWSTR> = if let Some(app_name) = app_name {
app_name.into_param()
} else {
Param::None
};
...and pass app_name_param to CreateProcessW.
(I can't test this because I run on Linux, where Foundation/mod.rs fails to compile due to requiring std::os::windows::ffi::OsStrExt.)

How to fix exit code 0xc0000005 (STATUS_ACCESS_VIOLATION) on NtAllocateVirtualMemory?

I am trying to allocate space in memory with ntdll.dll. I am using the NtApi and winapi crates.
When I try to allocate, I get the next error:
exit code: 0xc0000005, STATUS_ACCESS_VIOLATION
How do I need to send the pointer to NtAllocateVirtualMemory()?
Why does VirtualAllocEx() work?
I understand when I call VirtualAllocEx(), the process is kernel32.dll -> ntdll.dll, so why doesn't this work when I send this to NtAllocateVirtualMemory()?
main.rs
use ntapi::ntmmapi::NtAllocateVirtualMemory;
use ntapi::ntpsapi::NtCurrentProcess;
use ntapi::winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE};
use winapi::shared::ntdef::{NT_SUCCESS};
use ntapi::_core::ptr::null_mut;
fn main() {
unsafe {
// let null_ptr=std::ptr::null();
// let null_base:*const winapi::ctypes::c_void=null_ptr as *const _;
let mut buffer=null_mut();
let status = NtAllocateVirtualMemory(
NtCurrentProcess,
*buffer,
0,
0x1000 as *mut _,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
if !NT_SUCCESS(status) {
// if status as usize == 0x0 {
println!("Allocation Fails");
} else {
println!("Allocation Success");
}
}
}
Cargo.toml
[package]
name = "allocate_null"
version = "0.1.0"
edition = "2018"
[dependencies]
winapi = {version="0.3.9", features=["ntdef","winnt","memoryapi"]}
ntapi = "0.3.6"

WMI releases my implementation of IWbemQuerySink too many times (ExecQueryAsync): refcount turns negative

I've implemented the IWbemQuerySink in rust, in order to use ExecQueryAsync. Code is available in this pull request but I'll post the relevant bits here.
TLDR;
WMI is calling Release() 5 times on my QuerySink implementation, 1 which is understandable, and 4 which are not... There is only 1 call to AddRef by WMI, so the question is: where do this 4 other calls to Release comes from and is there a way to prevent them from happening?
With a lot of details
Here is a simplified implementation, it is working great:
use winapi::{
um::wbemcli::{
{IWbemClassObject,IWbemObjectSink, IWbemObjectSinkVtbl},
WBEM_S_NO_ERROR,
},
shared::{
ntdef::HRESULT,
wtypes::BSTR,
},
ctypes::{
c_long,
},
};
use com_impl::{ComImpl, VTable, Refcount};
use wio::com::ComPtr;
#[repr(C)]
#[derive(ComImpl)]
#[interfaces(IWbemObjectSink)]
pub struct QuerySink {
vtbl: VTable<IWbemObjectSinkVtbl>,
refcount: Refcount,
}
impl QuerySink {
pub fn new() -> ComPtr<IWbemObjectSink> {
let ptr = QuerySink::create_raw();
let ptr = ptr as *mut IWbemObjectSink;
unsafe { ComPtr::from_raw(ptr) }
}
}
// AddRef and Release methods are provided by com_impl
#[com_impl::com_impl]
unsafe impl IWbemObjectSink for QuerySink {
pub unsafe fn indicate(
&self,
_lObjectCount: c_long,
_apObjArray: *mut *mut IWbemClassObject
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
pub unsafe fn set_status(
&self,
_lFlags: c_long,
_hResult: HRESULT,
_strParam: BSTR,
_pObjParam: *mut IWbemClassObject
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
}
Let's try to use it in a simple call to ExecQueryAsync:
pub fn exec_async_query_native_wrapper(
&self,
query: impl AsRef<str>,
) -> Result<(), WMIError> {
let query_language = BStr::from_str("WQL")?;
let query = BStr::from_str(query.as_ref())?;
let p_sink: ComPtr<IWbemObjectSink> = QuerySink::new();
unsafe {
check_hres((*self.svc()).ExecQueryAsync(
query_language.as_bstr(),
query.as_bstr(),
WBEM_FLAG_BIDIRECTIONAL as i32,
ptr::null_mut(),
p_sink.as_raw(),
))?;
}
Ok(())
}
When executing it in a test, this code panics because the release function is called too many times, and the Refcount becomes negative:
attempt to subtract with overflow', C:\Users\apennamen\.cargo\registry\src\github.com-1ecc6299db9ec823\com-impl-0.1.1\src\lib.rs:139:9
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\std\src\panicking.rs:493
1: core::panicking::panic_fmt
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\core\src\panicking.rs:92
2: core::panicking::panic
at /rustc/b32e6e6ac8921035177256ab6806e6ab0d4b9b94\/library\core\src\panicking.rs:50
3: com_impl::Refcount::release
at C:\Users\apennamen\.cargo\registry\src\github.com-1ecc6299db9ec823\com-impl-0.1.1\src\lib.rs:139
4: wmi::query_sink::QuerySink::__com_impl__IUnknown__Release
at .\src\query_sink.rs:31
5: CoMarshalInterface
6: CoMarshalInterface
.....
After some tests, I found that the Release method is called 4 times more than it should be by WMI. So when adding 4 calls to AddRef, the code works just fine. This is of course not satisfying as maybe tomorrow it will be 5 calls, or 10 or 2...
I don't know how to handle this problem, hopefully someone else ran into it
This is a hacky working code:
pub fn exec_async_query_native_wrapper(
&self,
query: impl AsRef<str>,
) -> Result<(), WMIError> {
let query_language = BStr::from_str("WQL")?;
let query = BStr::from_str(query.as_ref())?;
let p_sink: ComPtr<IWbemObjectSink> = QuerySink::new();
unsafe {
// FIXME hack the RefCount
for _ in 0..4 { p_sink.AddRef(); }
check_hres((*self.svc()).ExecQueryAsync(
query_language.as_bstr(),
query.as_bstr(),
WBEM_FLAG_BIDIRECTIONAL as i32,
ptr::null_mut(),
p_sink.as_raw(),
))?;
}
Ok(())
}
}
Thank you for reading this far :)
EDIT :
Here is the expanded macro code:
#[cfg(feature = "async-query")]
pub(crate) mod query_sink {
use winapi::{
um::wbemcli::{
{IWbemClassObject, IWbemObjectSink, IWbemObjectSinkVtbl}, WBEM_S_NO_ERROR,
WBEM_STATUS_COMPLETE,
},
shared::{
ntdef::HRESULT,
wtypes::BSTR,
winerror::{E_POINTER, E_FAIL},
},
ctypes::{c_long},
};
use com_impl::{ComImpl, VTable, Refcount};
use log::{trace, warn};
use std::ptr::NonNull;
use wio::com::ComPtr;
use crate::result_enumerator::IWbemClassWrapper;
use crate::WMIError;
/// Implementation for [IWbemObjectSink](https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nn-wbemcli-iwbemobjectsink).
/// This [Sink](https://en.wikipedia.org/wiki/Sink_(computing))
/// receives asynchronously the result of the query,
/// through Indicate calls. When finished,the SetStatus method
/// is called.
/// # <https://learn.microsoft.com/fr-fr/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer-asynchronously>
#[repr(C)]
#[interfaces(IWbemObjectSink)]
pub struct QuerySink {
vtbl: VTable<IWbemObjectSinkVtbl>,
refcount: Refcount,
}
impl QuerySink {
fn create_raw() -> *mut Self {
Box::into_raw(Box::new(QuerySink {
vtbl: <Self as com_impl::BuildVTable<_>>::static_vtable(),
refcount: Default::default(),
}))
}
}
unsafe impl com_impl::BuildVTable<winapi::um::unknwnbase::IUnknownVtbl> for QuerySink {
const VTBL: winapi::um::unknwnbase::IUnknownVtbl = winapi::um::unknwnbase::IUnknownVtbl {
AddRef: Self::__com_impl__IUnknown__AddRef,
Release: Self::__com_impl__IUnknown__Release,
QueryInterface: Self::__com_impl__IUnknown__QueryInterface,
};
fn static_vtable() -> com_impl::VTable<winapi::um::unknwnbase::IUnknownVtbl> {
com_impl::VTable::new(&Self::VTBL)
}
}
#[allow(non_snake_case)]
impl QuerySink {
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__AddRef(
this: *mut winapi::um::unknwnbase::IUnknown,
) -> u32 {
let this = &*(this as *const Self);
this.refcount.add_ref()
}
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__Release(
this: *mut winapi::um::unknwnbase::IUnknown,
) -> u32 {
let ptr = this as *mut Self;
let count = (*ptr).refcount.release();
if count == 0 {
Box::from_raw(ptr);
}
count
}
#[inline(never)]
unsafe extern "system" fn __com_impl__IUnknown__QueryInterface(
this: *mut winapi::um::unknwnbase::IUnknown,
riid: *const winapi::shared::guiddef::IID,
ppv: *mut *mut winapi::ctypes::c_void,
) -> winapi::shared::winerror::HRESULT {
if ppv.is_null() {
return winapi::shared::winerror::E_POINTER;
}
if winapi::shared::guiddef::IsEqualIID(
&*riid,
&<winapi::um::unknwnbase::IUnknown as winapi::Interface>::uuidof(),
) || winapi::shared::guiddef::IsEqualIID(
&*riid,
&<IWbemObjectSink as winapi::Interface>::uuidof(),
) {
*ppv = this as *mut winapi::ctypes::c_void;
winapi::shared::winerror::S_OK
} else {
*ppv = std::ptr::null_mut();
winapi::shared::winerror::E_NOINTERFACE
}
}
}
impl QuerySink {
pub fn new() -> ComPtr<IWbemObjectSink> {
let ptr = QuerySink::create_raw();
let ptr = ptr as *mut IWbemObjectSink;
unsafe { ComPtr::from_raw(ptr) }
}
}
unsafe impl com_impl::BuildVTable<IWbemObjectSinkVtbl> for QuerySink {
const VTBL: IWbemObjectSinkVtbl = IWbemObjectSinkVtbl {
parent: <Self as com_impl::BuildVTable<_>>::VTBL,
Indicate: Self::__com_impl_stub__IWbemObjectSink__Indicate,
SetStatus: Self::__com_impl_stub__IWbemObjectSink__SetStatus,
};
fn static_vtable() -> com_impl::VTable<IWbemObjectSinkVtbl> {
com_impl::VTable::new(&Self::VTBL)
}
}
#[allow(non_snake_case)]
impl QuerySink {
#[inline(never)]
unsafe extern "system" fn __com_impl_stub__IWbemObjectSink__Indicate(
this: *mut IWbemObjectSink,
__com_arg_0: c_long,
__com_arg_1: *mut *mut IWbemClassObject,
) -> HRESULT {
let this = &*(this as *const Self);
Self::__com_impl_body__IWbemObjectSink__Indicate(this, __com_arg_0, __com_arg_1)
}
#[inline(never)]
unsafe extern "system" fn __com_impl_stub__IWbemObjectSink__SetStatus(
this: *mut IWbemObjectSink,
__com_arg_0: c_long,
__com_arg_1: HRESULT,
__com_arg_2: BSTR,
__com_arg_3: *mut IWbemClassObject,
) -> HRESULT {
let this = &*(this as *const Self);
Self::__com_impl_body__IWbemObjectSink__SetStatus(
this,
__com_arg_0,
__com_arg_1,
__com_arg_2,
__com_arg_3,
)
}
#[inline(always)]
unsafe extern "system" fn __com_impl_body__IWbemObjectSink__Indicate(
&self,
lObjectCount: c_long,
apObjArray: *mut *mut IWbemClassObject,
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
#[inline(always)]
unsafe extern "system" fn __com_impl_body__IWbemObjectSink__SetStatus(
&self,
lFlags: c_long,
_hResult: HRESULT,
_strParam: BSTR,
_pObjParam: *mut IWbemClassObject,
) -> HRESULT {
WBEM_S_NO_ERROR as i32
}
}
}

Using winapi in Rust to get a bitmap from a window

.. Hi, I have this code :
#[cfg(windows)] extern crate winapi;
use winapi::um::winuser::{FindWindowW, GetClientRect, GetWindowDC, GetDC, ReleaseDC};
use winapi::um::wingdi::{CreateBitmap, CreateCompatibleBitmap, CreateCompatibleDC, SelectObject, GetObjectW, SaveDC, DeleteDC, DeleteObject};
use std::ptr::null_mut;
use std::mem::zeroed;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
#[cfg(windows)]
fn find_window(name: &str) {
let window: Vec<u16> = OsStr::new(name).encode_wide().chain(once(0)).collect();
let hwnd = unsafe { FindWindowW(null_mut(), window.as_ptr()) };
if hwnd != null_mut() {
println!("Window found");
let mut my_rect = unsafe { zeroed::<winapi::shared::windef::RECT>() };
let _client_rect = unsafe { GetClientRect(hwnd, &mut my_rect) };
let w = my_rect.right - my_rect.left;
let h = my_rect.bottom - my_rect.top;
let hwnd_dc = unsafe { GetWindowDC(hwnd) };
let mem_dc = unsafe { CreateCompatibleDC(hwnd_dc) };
let bmp = unsafe { CreateCompatibleBitmap(mem_dc, w, h) };
//SelectObject(mem_dc, bmp); <== Problem is here
//DeleteObject(bmp); <== Same problem here
unsafe { DeleteDC(mem_dc) };
unsafe { ReleaseDC(hwnd, hwnd_dc) };
}
else {
println!("Window not found");
}
}
fn main() {
find_window("Firefox"); // just for test
}
If I cargo run this, I get :
SelectObject(mem_dc, bmp);
| ^^^ expected enum `std::ffi::c_void`, found enum `winapi::shared::windef::HBITMAP__`
So I get it, it doesn't want that bitmap pointer, but in the win32 documentation, it says that this function (SelectObject) accepts a Device Context (mem_dc) and an object created by some function like CreateCompatibleBitmap.
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject
The binding in Rust accepts the two same arguments but it seems to want that std::ffi::c_void but looking a the Rust doc, I don't get what it is exactly.
Can you help me finding what's wrong ? Thanks.
Add:
use winapi::shared::windef::{HBITMAP, HBITMAP__, HGDIOBJ, HWND, POINT, RECT, SIZE};
...
unsafe {SelectObject(mem_dc, bmp as HGDIOBJ)};
unsafe {DeleteObject(bmp as HGDIOBJ)};

Resources