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) 时,当您传入 szInputszOutput 时,您正在类型转换 单个 WideCharPWideChar,因此 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_ALWAYSdwCreationDisposition=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;