DeviceIoControl - GetLastError: ERROR_NOACCESS - 998
DeviceIoControl - GetLastError: ERROR_NOACCESS - 998
我有一个用 C 编写的内核驱动程序,它需要 PCWSTR
类型的文本。 Delphi 相当于发送控制码的类型是什么?我尝试使用以下代码发送,但 GetLastError
报告 ERROR_NOACCESS
。如何解决?
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, 0, 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.
编辑
在内核驱动程序“Dispatch”方法上接收文本:
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;
}
在调用 DeviceIOControl(IOCTL_PATH_DELETE)
时,当您传入 szInput
和 szOutput
时,您正在类型转换 单个 WideChar
到 PWideChar
,因此 WideChar
包含的任何数值都将被错误解释为内存地址,这是错误的。所以你最终传递了无效的内存地址,这很容易解释 ERROR_NOACCESS
错误。
将 PWideChar(szInput[0])
更改为 PWideChar(@szInput[0])
或完全摆脱类型转换,按原样传递 @szInput
。与 szOutput
.
相同
if not DeviceIOControl(driver, IOCTL_PATH_DELETE, @szInput, SizeOf(szInput), @szOutput, SizeOf(szOutput), BytesReturned, nil) then
此外,您在 CreateFileW()
上使用 GetLastError()
是错误的。 GetLastError()
的 return 值是 不确定的 且无效,除非 CreateFileW()
returns INVALID_HANDLE_VALUE
,或者如果您使用 dwCreationDisposition=CREATE_ALWAYS
或 dwCreationDisposition=OPEN_ALWAYS
,两者均未使用。
DriverDevice := CreateFileW(...);
Result := DriverDevice <> INVALID_HANDLE_VALUE;
if not Result then
Writeln('CreateFileW() - Error: ' + IntToStr(GetLastError))
else
Writeln('CreateFileW() - Success!');
UPDATE:您的内核驱动程序需要一个指向空终止宽字符串的指针。但是您的 Delphi 代码传递了一个指向以空字符结尾的字符串的指针。这就是您的内核代码崩溃的原因。您需要删除内核代码中不必要的间接级别:
//Path = *(PCWSTR*)ioBuffer;
Path = (PCWSTR)ioBuffer;
此外,您对 dprintf()
的调用需要一个窄字符串,但您传递给它的是一个宽字符串。打印宽字符串需要用%S
代替%s
,eg:
dprintf("%S\n", Path);
附带说明一下,您的 Delphi 代码正在泄漏由 CreateServiceW()
编辑的句柄 return。您需要致电 CloseServiceHandle()
:
hService := CreateServiceW(...);
if hService <> 0 then
CloseServiceHandle(hService); // <-- ADD THIS!
lpServiceArgVectors := nil;
hService := OpenServiceW(...);
if hService <> 0 then
begin
...
end;
但是,没有充分的理由立即关闭创建的服务只是为了重新打开它。使用 CreateServiceW()
给你的句柄:
hService := CreateServiceW(...);
lpServiceArgVectors := nil;
if hService <> 0 then
begin
...
end;
我有一个用 C 编写的内核驱动程序,它需要 PCWSTR
类型的文本。 Delphi 相当于发送控制码的类型是什么?我尝试使用以下代码发送,但 GetLastError
报告 ERROR_NOACCESS
。如何解决?
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, 0, 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.
编辑
在内核驱动程序“Dispatch”方法上接收文本:
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;
}
在调用 DeviceIOControl(IOCTL_PATH_DELETE)
时,当您传入 szInput
和 szOutput
时,您正在类型转换 单个 WideChar
到 PWideChar
,因此 WideChar
包含的任何数值都将被错误解释为内存地址,这是错误的。所以你最终传递了无效的内存地址,这很容易解释 ERROR_NOACCESS
错误。
将 PWideChar(szInput[0])
更改为 PWideChar(@szInput[0])
或完全摆脱类型转换,按原样传递 @szInput
。与 szOutput
.
if not DeviceIOControl(driver, IOCTL_PATH_DELETE, @szInput, SizeOf(szInput), @szOutput, SizeOf(szOutput), BytesReturned, nil) then
此外,您在 CreateFileW()
上使用 GetLastError()
是错误的。 GetLastError()
的 return 值是 不确定的 且无效,除非 CreateFileW()
returns INVALID_HANDLE_VALUE
,或者如果您使用 dwCreationDisposition=CREATE_ALWAYS
或 dwCreationDisposition=OPEN_ALWAYS
,两者均未使用。
DriverDevice := CreateFileW(...);
Result := DriverDevice <> INVALID_HANDLE_VALUE;
if not Result then
Writeln('CreateFileW() - Error: ' + IntToStr(GetLastError))
else
Writeln('CreateFileW() - Success!');
UPDATE:您的内核驱动程序需要一个指向空终止宽字符串的指针。但是您的 Delphi 代码传递了一个指向以空字符结尾的字符串的指针。这就是您的内核代码崩溃的原因。您需要删除内核代码中不必要的间接级别:
//Path = *(PCWSTR*)ioBuffer;
Path = (PCWSTR)ioBuffer;
此外,您对 dprintf()
的调用需要一个窄字符串,但您传递给它的是一个宽字符串。打印宽字符串需要用%S
代替%s
,eg:
dprintf("%S\n", Path);
附带说明一下,您的 Delphi 代码正在泄漏由 CreateServiceW()
编辑的句柄 return。您需要致电 CloseServiceHandle()
:
hService := CreateServiceW(...);
if hService <> 0 then
CloseServiceHandle(hService); // <-- ADD THIS!
lpServiceArgVectors := nil;
hService := OpenServiceW(...);
if hService <> 0 then
begin
...
end;
但是,没有充分的理由立即关闭创建的服务只是为了重新打开它。使用 CreateServiceW()
给你的句柄:
hService := CreateServiceW(...);
lpServiceArgVectors := nil;
if hService <> 0 then
begin
...
end;