I have a kernel driver written in C, where it is expecting a text of type PCWSTR
. What's the Delphi type equivalent to send a control code? I tried sending using the following code but GetLastError
reports ERROR_NOACCESS
. How to solve that?
program Driverloader;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
WinSvc,
SysUtils;
function InstallAndStartDriver(DriverPath, DriverName: WideString; out DriverDevice: THandle): Boolean;
var
hSCManager, hService: THandle;
lpServiceArgVectors: PWideChar;
begin
Result := False;
hSCManager := 0;
hSCManager := OpenSCManagerW(nil, nil, SC_MANAGER_ALL_ACCESS);
if hSCManager <> 0 then
begin
try
Writeln('OpenSCManagerW() - OK');
hService := 0;
hService := CreateServiceW(hSCManager, PWideChar(DriverName), PWideChar(DriverName), SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, PWideChar(DriverPath), nil, nil, nil, nil, nil);
hService := 0;
lpServiceArgVectors := nil;
hService := OpenServiceW(hSCManager, PWideChar(DriverName), SERVICE_ALL_ACCESS);
if hService <> 0 then
begin
try
Writeln('OpenServiceW() - OK');
if StartServiceW(hService, 0, PWideChar(lpServiceArgVectors)) then
begin
Writeln('StartServiceW() - OK');
Result := True;
end;
finally
CloseServiceHandle(hService);
end;
end;
finally
CloseServiceHandle(hSCManager);
end;
end;
if Result the
begin
DriverDevice := CreateFileW(PWideChar('\\.\' + DriverName), GENERIC_READ or GENERIC_WRITE, 0, PSECURITY_DESCRIPTOR(nil), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
Result := GetLastError() = ERROR_SUCCESS;
Writeln('CreateFileW() - ' + IntToStr(GetLastError));
end;
end;
function CTL_CODE(DeviceType, _Function, Method, Access: Cardinal): Cardinal;
begin
Result := (DeviceType shl 16) or (Access shl 14) or (_Function shl 2) or (Method);
end;
var
driver: THandle;
BytesReturned, IOCTL_PATH_DELETE: Cardinal;
szInput, szOutput: array[0..255] of WideChar;
begin
try
IOCTL_PATH_DELETE := CTL_CODE(FILE_DEVICE_UNKNOWN, $500, METHOD_BUFFERED, FILE_ANY_ACCESS);
lstrcpy(szInput, '\??\C:\Program Files\Software Folder');
if InstallAndStartDriver(IncludeTrailingPathDelimiter(GetCurrentDir) + 'MyDriver.sys', 'MyDriver', driver) then
begin
Writeln('InstallAndStartDriver() - OK');
Sleep(2000);
if not DeviceIOControl(driver, IOCTL_PATH_DELETE, PWideChar(szInput[0]), SizeOf(szInput), PWideChar(szOutput[0]), SizeOf(szOutput) * MAXBYTE, BytesReturned, nil) then
Writeln('DeviceIOControl() - Error: ' + IntToStr(GetLastError))
else
Writeln('Success! - ' + szOutput);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Edit
Receiving text on kernel driver "Dispatch" method:
NTSTATUS DrvDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack;
PVOID ioBuffer;
ULONG ioControlCode;
NTSTATUS ntStatus;
PCWSTR Path;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
switch (irpStack->MajorFunction) {
case IRP_MJ_DEVICE_CONTROL:
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode) {
case IOCTL_PATH_DELETE: {
Path = *(PCWSTR*)ioBuffer; // <-- fails and stop here
dprintf("%s\n", Path);
break;
}
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
break;
}
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
While calling DeviceIOControl(IOCTL_PATH_DELETE)
, when you pass in szInput
and szOutput
, you are type-casting a single WideChar
to a PWideChar
, so whatever numeric value that WideChar
contains will be mis-interpreted as a memory address, which is wrong. So you end up passing in invalid memory addresses, which can easily account for the ERROR_NOACCESS
error.
Change PWideChar(szInput[0])
to PWideChar(@szInput[0])
or simply get rid of the type-cast altogether, passing @szInput
as-is. Same with szOutput
.
if not DeviceIOControl(driver, IOCTL_PATH_DELETE, @szInput, SizeOf(szInput), @szOutput, SizeOf(szOutput), BytesReturned, nil) then
Also, your use of GetLastError()
on CreateFileW()
is wrong. The return value of GetLastError()
is indeterminate and not valid to use unless CreateFileW()
returns INVALID_HANDLE_VALUE
, OR if you use dwCreationDisposition=CREATE_ALWAYS
or dwCreationDisposition=OPEN_ALWAYS
, neither of which you are using.
DriverDevice := CreateFileW(...);
Result := DriverDevice <> INVALID_HANDLE_VALUE;
if not Result then
Writeln('CreateFileW() - Error: ' + IntToStr(GetLastError))
else
Writeln('CreateFileW() - Success!');
UPDATE: your kernel driver is expecting a pointer to a pointer to a null-terminated wide string. But your Delphi code is passing in a pointer to a null-terminated string. That is why your kernel code is crashing. You need to remove the unnecessary level of indirection in the kernel code:
//Path = *(PCWSTR*)ioBuffer;
Path = (PCWSTR)ioBuffer;
Also, your call to dprintf()
is expecting a narrow string, but you are passing it a wide string. To print a wide string, you need to use %S
instead of %s
, eg:
dprintf("%S\n", Path);
On a side note, your Delphi code is leaking the handle returned by CreateServiceW()
. You need to call CloseServiceHandle()
:
hService := CreateServiceW(...);
if hService <> 0 then
CloseServiceHandle(hService); // <-- ADD THIS!
lpServiceArgVectors := nil;
hService := OpenServiceW(...);
if hService <> 0 then
begin
...
end;
However, there is no good reason to immediately close a created service just to re-open it again. Use the handle that CreateServiceW()
gives you:
hService := CreateServiceW(...);
lpServiceArgVectors := nil;
if hService <> 0 then
begin
...
end;