2

我有一个用 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, $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.

编辑

在内核驱动程序“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;
}
4

1 回答 1

3

在调用DeviceIOControl(IOCTL_PATH_DELETE)时,当您传入szInputand时szOutput,您正在将单个WideChar类型转换为 a PWideChar,因此包含的任何数值WideChar都会被错误地解释为内存地址,这是错误的。所以你最终传递了无效的内存地址,这很容易解释ERROR_NOACCESS错误。

更改或干脆完全摆脱类型转换,PWideChar(szInput[0])按原样传递。与 相同。PWideChar(@szInput[0])@szInputszOutput

if not DeviceIOControl(driver, IOCTL_PATH_DELETE, @szInput, SizeOf(szInput), @szOutput, SizeOf(szOutput), BytesReturned, nil) then

另外,您对GetLastError()on的使用CreateFileW()是错误的。的返回值GetLastError()不确定的并且无效,除非CreateFileW()返回INVALID_HANDLE_VALUE,或者如果您使用dwCreationDisposition=CREATE_ALWAYSor dwCreationDisposition=OPEN_ALWAYS,您都没有使用。

DriverDevice := CreateFileW(...);
Result := DriverDevice <> INVALID_HANDLE_VALUE;
if not Result then
  Writeln('CreateFileW() - Error: ' + IntToStr(GetLastError))
else
  Writeln('CreateFileW() - Success!');

更新:您的内核驱动程序需要一个指向以空字符结尾的宽字符串的指针。但是您的 Delphi 代码正在传递一个指向以空字符结尾的字符串的指针。这就是您的内核代码崩溃的原因。您需要删除内核代码中不必要的间接级别:

//Path = *(PCWSTR*)ioBuffer;
Path = (PCWSTR)ioBuffer;

此外,您的调用dprintf()期待一个窄字符串,但您传递给它的是一个宽字符串。要打印宽字符串,您需要使用%S而不是%s,例如:

dprintf("%S\n", Path); 

附带说明一下,您的 Delphi 代码正在泄漏由CreateServiceW(). 您需要致电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;
于 2020-07-21T02:10:00.600 回答