Is anyone familiar with the undocumented ObReferenceObjectByName windows kernel function? - windows

I read a very fascinating article that was about programming drivers using the wdk, and one of the functions it used is called ObReferenceObjectByName. This function has given me lots of headaches. The first bad thing is that it's not documented by microsoft. The second thing, is that the language used in the article was C++, and I want to keep my code in plain ol' C. I know that most of the time this shouldn't be a problem, but I haven't - for the life of me - been able to figure out how to include this function.
The code in the article goes something like:
extern "C"{
#include <ntifs.h>
NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName(PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext OPTIONAL,
PVOID* Object);
}
I've been trying to replicate this for hours. I tried declaring it without the 'extern' keyword, I tried changing the calling convention, I tried changing the includes... I always end up with the error "unresolved external symbol...".
I'm absolutely stumped, so if anyone could offer some advice, I'd be grateful. Thanks.

You wouldn't be reading http://www.codeproject.com/KB/recipes/keystroke-hook.aspx and trying to create your own Keyboard Logger would you?
Anyways, instead of using this, call ZwCreateFile then ObReferenceObjectByHandle instead.

Here is a test C code compiled and built with no problems:
#include <ntddk.h>
NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext OPTIONAL,
PVOID* Object
);
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
ObReferenceObjectByName(0, 0, 0, 0, 0, 0, 0, 0);
return STATUS_SUCCESS;
}

I don't know this API, but I can give you a trick that might help you diagnose the problem.
at a command prompt that has MSVC tools in the path
link /dump /exports ???.dll
where ???.dll is the dll were you expect this function to be. This will give you a complete list of exported symbol names and will tell you two things. 1) is the symbol there? and 2) is it being decorated the same as your attempted prototype.
For 32 bit kernel, you should expect this to be called _ObReferenceObjectByName#64,

Related

How to get args from WinMain or wWinMain in Rust?

I am trying to learn how to work with raw Win32 API's and am following the tutorial here, but cannot, for the life of me, figure out how to pass int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) function signature to work. I do understand the int WINAPI isn't needed...but how do I get all of those parameters to pass to WinAPI calls? Especially hInstance and nCmdShow?
My Goal
Get hInstance and nShowCmd from
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {}
into a Rust program, perhaps something like:
fn main(/* args, such as hInstance, nShowCmd here /*) {
}
or, the more likely way:
fn main() {
std::env::/* hopefully something like args() /*;
}
What I've tried
I've tried getting the args, but this just passes the command-line arguments that I used to spawn the program, just as args[0] being the name of the program, which is the expected behavior. Also, calling args_os() gives the same result.
I've also tried to set the windows subsystem, but the previous behavior is the same, not the desired behavior...
#![windows_subsystem = "windows"]
I am able to get the hInstance handle by manually calling GetModuleHandle() and passing in a null pointer, but have no idea how to get the nShowCmd manually.
Important Note
I am using the windows crate, which is what I would like to use.
Any help to this eluding mystery would be much appreciated!
P.S. My window does open, and everything works fine, as expected, including working with FFI, and all the crazies involved there, lol. But I just would like to understand how this is done. One can get by without the nShowCmd, but I would really like to be able to understand how this is done in rust. I also cannot overwrite the fn main() function signature, so not sure how to go about it.
WinMain is the user-provided entry point of a Windows application. The raw application entry point as seen by the OS is far simpler:
DWORD CALLBACK RawEntryPoint(void);
It is now up to language support libraries to recover the startup information and call into the user-provided entry point (see WinMain is just the conventional name for the Win32 process entry point for details):
GetModuleHandle(NULL) for hInstance
hPrevInstance is always NULL in 32-bit and 64-bit Windows
GetCommandLine for the unparsed command line passed to the program
GetStartupInfo for lots of state information, including the wShowWindow that corresponds to nCmdShow
If you have Visual Studio installed you can have a look inside exe_common.inl to see how the C and C++ support libraries go about this.
With Rust things are unfortunately more complex. Even though the compiler and linker repurpose MSVC's CRT implementation responsible for extracting the information that would be passed into WinMain, I'm not aware of a way to get a hold of this from Rust.
You're going to have to recover that information manually. Getting to the nCmdShow parameter is a bit more involved, so let's illustrate that here:
// build.rs
// Using windows-rs 0.17.2; version 0.10.0 and later should be just fine
fn main() {
windows::build!(Windows::Win32::System::Threading::GetStartupInfoW,)
}
// src/main.rs
mod bindings {
windows::include_bindings!();
}
use bindings::Windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};
fn main() {
let mut si = STARTUPINFOW {
cb: std::mem::size_of::<STARTUPINFOW>() as u32,
..Default::default()
};
unsafe { GetStartupInfoW(&mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
With that you now have access to a value that corresponds to the nCmdShow parameter passed into WinMain when compiling for C or C++ (roughly, anyway). Ideally you would need to see whether dwFlags contains the STARTF_USESHOWWINDOW bit, and fabricate a reasonable default when it doesn't.
That said, I'm not even sure what purpose the nCmdShow argument passed into WinMain serves. As explained under ShowWindow, using that value doesn't have any effect when it is populated from caller-provided information.
Update 2021-10-28
Starting with version 0.22.1 the windows crate comes with pre-built bindings, making it a lot easier to consume the Windows API. The following implements the same program using the pre-built bindings in place of compile-time code generation.
Cargo.toml
[package]
name = "startup_info"
version = "0.0.0"
edition = "2021"
[dependencies.windows]
version = "0.22.1"
features = ["Win32_Foundation", "Win32_System_Threading"]
main.rs
use windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};
fn main() {
let mut si = STARTUPINFOW {
cb: std::mem::size_of::<STARTUPINFOW>() as u32,
..Default::default()
};
unsafe { GetStartupInfoW(&mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
There is this function in Kernel32: GetStartupInfo() that in windows-rs seems to be mapped to bindings::Windows::Win32::System::Threading::GetStartupInfoW.
This function fills a STARTUPINFOW structure that has, between a lot of useful fields, WORD wShowWindow that has the same value as the last argument in WinMain().
The funny thing about WinMain is that there is nothing magic in it, it is just the function that the real entry point function, WinMainCRTStartup calls from the CRT initialization code. You can get an idea of how it does its thing by looking at the equivalent Wine source code. There you can see that your idea of calling GetModuleHandle(NULL) to get the hInstance is the right one.

optimization options and usage of windows dll calls

I am building a dll for windows, using a Makefile, using cl.exe. I am using VS2015.. this dll uses CNG (bcrypt) for encryption operations, and bcryptr is loaded dynamically using loadlibrary call.
When i build with /Od option to disable optimization, i have no issues with any functionality. but if i use any optimization option /O1, /O2, /Ox, i see the strangest thing happen.. once i retrieve the address for a bcrypt function, such as BCryptGetFipsAlgorithmMode, using GetProcAddress, and then i make the call to that function ptr, the call stack goes away. This results in exception when the calling function tries to return.. it looks almost like when one calls a callback doesn't have the CALLBACK prefix, but i dont see the connection..
That bcrypt function's prototype looks like this:
NTSTATUS WINAPI BCryptGetFipsAlgorithmMode( __out BOOLEAN *pfEnabled)
and WINAPI seems to be defined:
define WINAPI __stdcall
Is there something I am missing? what does optimization have to do with this?
Any help would be appreciated.. Thank You!
Heres the code:
NTSTATUS GetFipsAlgorithmMode(BOOLEAN *pfEnabled )
{
FARPROC pBCryptGetFipsAlgorithmMode = NULL;
NTSTATUS (*_BCryptGetFipsAlgorithmMode)( __out BOOLEAN *);
NTSTATUS status = SPGC_ERR_LIBRARY_ADDRESS_LOOKUP_FAILURE;
if(g_hBCRYPTDLL != NULL)
{
pBCryptGetFipsAlgorithmMode = GetProcAddress(g_hBCRYPTDLL, _T("BCryptGetFipsAlgorithmMode"));
if(pBCryptGetFipsAlgorithmMode != NULL)
{
_BCryptGetFipsAlgorithmMode = (NTSTATUS (*)( __out BOOLEAN *)) pBCryptGetFipsAlgorithmMode;
status = _BCryptGetFipsAlgorithmMode(pfEnabled);
}
}
return status;
}
step over the call to _BCryptGetFipsAlgorithmMode(), and the call stack basically gets cleared.

mciSendString with Visual C++ - Parameters?

I am not a native C++ programmer, so I need some help with the following:
I got this code working:
#pragma comment(lib, "winmm.lib")
LPCWSTR openCDCommand = L"set cdaudio door open";
//comes from Windows.h, needs winmm.lib see header includes
int errCode = mciSendString(openCDCommand, 0, 0, 0);
Questions:
Do I need to work with LPCWSTR? Why didn't I find a System::String example?
How should I handle 'string concatination'? I cant simply do L"foo"+L"baar"?
Am I on the totally wrong way to play sounds with mciSendString? (actually I really want to use MCI Command and MCI sendString as i did in other projects before)
Is there another way to include the external function mciSendString so it can handle handles?
The signature of mciSendString is
MCIERROR mciSendString(
LPCTSTR lpszCommand,
LPTSTR lpszReturnString,
UINT cchReturn,
HANDLE hwndCallback);
So, regarding the first 2 parameters, in unicode it will be a wchar pointer and in multibyte it will be a char pointer. It's the signature. You cannot change that and you shouldn't worry about that.
std::wstring someString( L"Foo" );
someString.append( L"bar ");
I would play sound with core audio API, waveOut or maybe DirectSound. But, not with the mciSendString().
I'm afraid I don't understand this one.. can you explain it better?
This now works for me - took some time, but maybe in future this will help others:
#include "vcclr.h" // compile with /clr(!)
int Player::mciSendStringHandle(String ^ givenHandle)
{
pin_ptr<const wchar_t> wch = PtrToStringChars(givenHandle);
return mciSendString(wch, 0, 0, 0);
}

World's most simple Windows driver

I have a slightly odd problem involving a MoGo mouse failing to charge when put in the cartridge slot of my Windows XP laptop. Long story, but one suggestion to fix it is to write a bespoke driver which only says "I'm functioning OK: don't turn the power off".
I'm figuring that this should be next to trivial, but my only experience of drivers is to download and install them through provided MSIs. I realised that I don't know:
What language they're written in.
What conventions they must follow.
How they are associated with their respective hardware.
Where they are located.
Or indeed, anything at all...
I also haven't found anything staggeringly helpful on the web - probably because they are aimed at a far higher level than I'm at.
Any insights would be welcome.
Microsoft provides a "Hello World" driver example in their documentation. This is an example of "World's most simple Windows driver". Unfortunately, it's 13 pages long and thus not a good fit for a StackOverflow answer.
The language they are written in is C++.
To get started, be sure you have Microsoft Visual Studio, the Windows
SDK, and the Windows Driver Kit (WDK) installed.
Their example contains one file called Driver.c that looks like this:
#include <ntddk.h>
#include <wdf.h>
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
// NTSTATUS variable to record success or failure
NTSTATUS status = STATUS_SUCCESS;
// Allocate the driver configuration object
WDF_DRIVER_CONFIG config;
// Print "Hello World" for DriverEntry
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
// Initialize the driver configuration object to register the
// entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
WDF_DRIVER_CONFIG_INIT(&config,
KmdfHelloWorldEvtDeviceAdd
);
// Finally, create the driver object
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE
);
return status;
}
NTSTATUS
KmdfHelloWorldEvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
// We're not using the driver object,
// so we need to mark it as unreferenced
UNREFERENCED_PARAMETER(Driver);
NTSTATUS status;
// Allocate the device object
WDFDEVICE hDevice;
// Print "Hello World"
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
// Create the device object
status = WdfDeviceCreate(&DeviceInit,
WDF_NO_OBJECT_ATTRIBUTES,
&hDevice
);
return status;
}
Full details found here:
https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/writing-a-very-small-kmdf--driver

IDebugSymbols::GetNameByOffset and overloaded functions

I'm using IDebugSymbols::GetNameByOffset and I'm finding that I get the same symbol name for different functions that overload the same name.
E.g. The code I'm looking up the symbols for might be as follows:
void SomeFunction(int) {..}
void SomeFunction(float) {..}
At runtime, when I have an address of an instruction from each of these functions I'd like to use GetNameByOffset and tell the two apart somehow. I've experimented with calling SetSymbolOptions toggling the SYMOPT_UNDNAME and SYMOPT_NO_CPP flags as documented here, but this didn't work.
Does anyone know how to tell these to symbols apart in the debugger engine universe?
Edit: Please see me comment on the accepted answer for a minor amendment to the proposed solution.
Quote from dbgeng.h:
// A symbol name may not be unique, particularly
// when overloaded functions exist which all
// have the same name. If GetOffsetByName
// finds multiple matches for the name it
// can return any one of them. In that
// case it will return S_FALSE to indicate
// that ambiguity was arbitrarily resolved.
// A caller can then use SearchSymbols to
// find all of the matches if it wishes to
// perform different disambiguation.
STDMETHOD(GetOffsetByName)(
THIS_
__in PCSTR Symbol,
__out PULONG64 Offset
) PURE;
So, I would get the name with IDebugSymbols::GetNameByOffset() (it comes back like "module!name" I believe), make sure it is an overload (if you're not sure) using IDebugSymbols::GetOffsetByName() (which is supposed to return S_FALSE for multiple overloads), and look up all possibilities with this name using StartSymbolMatch()/EndSymbolMatch(). Not a one liner though (and not really helpful for that matter...)
Another option would be to go with
HRESULT
IDebugSymbols3::GetFunctionEntryByOffset(
IN ULONG64 Offset,
IN ULONG Flags,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BufferNeeded
);
// It can be used to retrieve FPO data on a particular function:
FPO_DATA fpo;
HRESULT hres=m_Symbols3->GetFunctionEntryByOffset(
addr, // Offset
0, // Flags
&fpo, // Buffer
sizeof(fpo), // BufferSize
0 // BufferNeeded
));
and then use fpo.cdwParams for basic parameter size discrimination (cdwParams=size of parameters)

Resources