windows内核安全(三)

NT框架

Posted by volcanohatred on April 20, 2018

内核模式驱动框架

内核模式驱动程序KMD,文件后缀.sys。 cpu
KMD通过接收和处理I/O请求包(IRP)的数据块与I/O管理器进行交互。用户模式应用程序通过windows api的调用与硬件设备进行通讯。为了向驱动程序传递信息,I/O管理器把IRP的地址传递给KMD,IRP将在用户模式和内核模式之间传递命令和数据。

NT驱动框架

NT式驱动程序模型是一种比较老式的驱动程序模型,但适用于现有的Windows系统。而对于WDM式驱动来说,它支持即插即用功能要导入的头文件为wdm.h。这是Windows2000后加入的新的驱动模型,比NT式驱动更加复杂一些,完成一个设备操作,至少要两个驱动设备共同完成,分别是物理设备对象(PDO)和功能设备对象(FDO),FDO会附加在PDO上。这里只讲解NT框架。
C代码及注释:

#include <ntddk.h>        //标准驱动头文件

//设备名称 比如C盘对应的设备名:\Device\HarddiskVolume3
#define DEVICE_NAME L"\\device\\ntmodeldrv"    

//用户可见驱动名称 类似C盘
#define LINK_NAME L"\\dosdevices\\ntmodeldrv"  
#define IOCTRL_BASE 0x800          // 0x000-0x7FF被微软占用

#define MYIOCTRL_CODE(i) \
   CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE+i, METHOD_BUFFERED,FILE_ANY_ACCESS)

#define CTL_HELLO MYIOCTRL_CODE(0)
#define CTL_PRINT MYIOCTRL_CODE(1)
#define CTL_BYE MYIOCTRL_CODE(2)

NTSTATUS DispatchCommon(PDEVICE_OBJECT pObject, PIRP pIrp) //释放IRP
{
   pIrp->IoStatus.Status = STATUS_SUCCESS;                  
   pIrp->IoStatus.Information = 0;      //返回R3,不再往下发,过滤驱动会继续下发

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);     //终止IRP

   return STATUS_SUCCESS;            //返回IO管理器
}

NTSTATUS DispatchCreate(PDEVICE_OBJECT pObject, PIRP pIrp)  //打开文件
{
   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = 0;

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;
}

NTSTATUS DispatchRead(PDEVICE_OBJECT pObject, PIRP pIrp)  //读
{
   PVOID pReadBuffer = NULL;    //要读的buffer地址
   ULONG uReadLength = 0;      //要读的buffer长度
   PIO_STACK_LOCATION pStack = NULL;
   ULONG uMin = 0;
   ULONG uHelloStr = 0;

   uHelloStr = (wcslen(L"hello world") + 1) * sizeof(WCHAR);  //* sizeof(WCHAR)等价于x2

   //第一步,拿到缓存的地址和长度(irp分头和栈)
   //从头部拿缓存地址
   pReadBuffer = pIrp->AssociatedIrp.SystemBuffer;            //SystemBuffer(buffered io),MdlAddress(direct io),UserBuffer(neither io)
   //从栈上拿缓存长度
   pStack = IoGetCurrentIrpStackLocation(pIrp);
   uReadLength = pStack->Parameters.Read.Length;   //Length为应用层缓冲区长度

   //第二步:读,写等操作
   uMin = uReadLength>uHelloStr ? uHelloStr : uReadLength;   //传最小的值(安全考虑)字符串的话字符串长度-1
   RtlCopyMemory(pReadBuffer, L"hello world", uMin);         //内核中拷贝内存函数:RtlCopyMemory

   //第三步,完成IRP
   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = uMin;        //实际读的长度
   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;

}

NTSTATUS DispatchWrite(PDEVICE_OBJECT pObject, PIRP pIrp)
{
   PVOID pWriteBuff = NULL;
   ULONG uWriteLength = 0;
   PIO_STACK_LOCATION pStack = NULL;

   PVOID pBuffer = NULL;

   pWriteBuff = pIrp->AssociatedIrp.SystemBuffer;

   pStack = IoGetCurrentIrpStackLocation(pIrp);
   uWriteLength = pStack->Parameters.Write.Length;

   //分配内存,需要指定分页内存还是非分页内存,非分页内存级别要求高,TSET给内存打标签(4字节),低位优先
   pBuffer = ExAllocatePoolWithTag(PagedPool, uWriteLength, 'TSET');  
   if (pBuffer == NULL)
   {
      pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 
      pIrp->IoStatus.Information = 0;
      IoCompleteRequest(pIrp, IO_NO_INCREMENT);
      return STATUS_INSUFFICIENT_RESOURCES;
   }

   memset(pBuffer, 0, uWriteLength);

   RtlCopyMemory(pBuffer, pWriteBuff, uWriteLength);

   ExFreePool(pBuffer);      //释放
   pBuffer = NULL;  


   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = uWriteLength;

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;

}

NTSTATUS DispatchIoctrl(PDEVICE_OBJECT pObject, PIRP pIrp)
{
   ULONG uIoctrlCode = 0;
   PVOID pInputBuff = NULL;
   PVOID pOutputBuff = NULL;

   ULONG uInputLength = 0;
   ULONG uOutputLength = 0;
   PIO_STACK_LOCATION pStack = NULL;

   pInputBuff = pOutputBuff = pIrp->AssociatedIrp.SystemBuffer; 

   pStack = IoGetCurrentIrpStackLocation(pIrp);
   uInputLength = pStack->Parameters.DeviceIoControl.InputBufferLength;
   uOutputLength = pStack->Parameters.DeviceIoControl.OutputBufferLength;


   uIoctrlCode = pStack->Parameters.DeviceIoControl.IoControlCode;

   switch (uIoctrlCode)
   {
   case CTL_HELLO:
      DbgPrint("Hello iocontrol\n");
      break;
   case CTL_PRINT:
      DbgPrint("%ws\n", pInputBuff);
      //*(DWORD *)pOutputBuff =2;
      break;
   case CTL_BYE:
      DbgPrint("Goodbye iocontrol\n");
      break;
   default:
      DbgPrint("Unknown iocontrol\n");

   }

   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = 0;//sizeof(DWORD);
   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;

}

NTSTATUS DispatchClean(PDEVICE_OBJECT pObject, PIRP pIrp)
{
   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = 0;

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;
}

NTSTATUS DispatchClose(PDEVICE_OBJECT pObject, PIRP pIrp)
{
   pIrp->IoStatus.Status = STATUS_SUCCESS;
   pIrp->IoStatus.Information = 0;

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   return STATUS_SUCCESS;
}


VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
   UNICODE_STRING uLinkName = { 0 };
   RtlInitUnicodeString(&uLinkName, LINK_NAME);
   IoDeleteSymbolicLink(&uLinkName);

   IoDeleteDevice(pDriverObject->DeviceObject);

   DbgPrint("Driver unloaded\n");

}

NTSTATUS DriverEntry                //程序入口
(
   PDRIVER_OBJECT pDriverObject,          
   PUNICODE_STRING pRegPath      //pRegPath:注册表路径
)
{
   UNICODE_STRING uDeviceName = { 0 };  //UNICODE_STRING内核中表示字符串的方法
   UNICODE_STRING uLinkName = { 0 };
   NTSTATUS ntStatus = 0;           //驱动中返回值0代表成功
   PDEVICE_OBJECT pDeviceObject = NULL;
   ULONG i = 0;

   DbgPrint("Driver load begin\n");

   RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);  //将设备名的宏定义转化为UNICODE_STRING类型
   RtlInitUnicodeString(&uLinkName, LINK_NAME);

   //创建设备对象
   ntStatus = IoCreateDevice  //IoCreateDevice定义在wdm.h中
   (
      pDriverObject,
      0,                    //DeviceExtensionSize设备扩展长度
      &uDeviceName, 
      FILE_DEVICE_UNKNOWN,  //DeviceType设备类型
      0,                      //DeviceCharacteristics设备特征
      FALSE,    //Exclusive驱动对象是否独占,为了安全设为TRUE
      &pDeviceObject          //传指针(指针的指针)
   );                          //返回&pDeviceObject

   if (!NT_SUCCESS(ntStatus))     //宏定义:#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
   {
      DbgPrint("IoCreateDevice failed:%x", ntStatus);
      return ntStatus;
   }

   //DO_BUFFERED_IO规定R3和R0之间read和write通信的方式:
   //1,buffered io 缓存会拷贝一次,安全,但是效率低
   //2,direct io 先映射到物理地址,然后R0和R3共用,效率高
   //3,neither io  R3直接传到R0
   //DO_DEVICE_INITIALIZING   防止初始化之前发送IO请求

   pDeviceObject->Flags |= DO_BUFFERED_IO;     

   //创建符号链接
   ntStatus = IoCreateSymbolicLink(&uLinkName, &uDeviceName);
   if (!NT_SUCCESS(ntStatus))       
   {
      DbgPrint("IoCreateSymbolicLink failed:%x\n", ntStatus);
      IoDeleteDevice(pDeviceObject);       //如果创建失败,删除之前创建的设备对象
      return ntStatus;
   }

   for (i = 0; i<IRP_MJ_MAXIMUM_FUNCTION + 1; i++)          //初始化分发函数
   {
      pDriverObject->MajorFunction[i] = DispatchCommon;
   }

   //初始化重要的分发函数
   pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;    //打开文件
   pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;  //读,数据从R0->R3
   pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite; //写,数据从R3->R0
   pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctrl; //关键函数,任何功能,可以实现所有功能
   pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchClean;
   pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;

   pDriverObject->DriverUnload = DriverUnload;            //卸载驱动

   DbgPrint("Driver load ok!\n");

   return STATUS_SUCCESS;
}

安装代码:

#include <windows.h>  
#include <winsvc.h>  
#include <conio.h>  
#include <stdio.h>
#include <winioctl.h>

#pragma warning(disable:4996)

#define DRIVER_NAME "ntmodeldrv"
#define DRIVER_PATH ".\\ntmodeldrv.sys"    //要加载的驱动路径

#define IOCTRL_BASE 0x800

#define MYIOCTRL_CODE(i) \
   CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE+i, METHOD_BUFFERED,FILE_ANY_ACCESS)

#define CTL_HELLO MYIOCTRL_CODE(0)
#define CTL_PRINT MYIOCTRL_CODE(1)
#define CTL_BYE MYIOCTRL_CODE(2)

//装载NT驱动程序
BOOL LoadDriver(char* lpszDriverName, char* lpszDriverPath)
{
   //char szDriverImagePath[256] = "D:\\DriverTest\\ntmodelDrv.sys";
   char szDriverImagePath[256] = { 0 };
   //得到完整的驱动路径
   GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);

   BOOL bRet = FALSE;

   SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
   SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄

    //打开服务控制管理器
   hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

   if (hServiceMgr == NULL)
   {
      //OpenSCManager失败
      printf("OpenSCManager() Failed %d ! \n", GetLastError());
      bRet = FALSE;
      goto BeforeLeave;
   }
   else
   {
      ////OpenSCManager成功
      printf("OpenSCManager() ok ! \n");
   }

   //创建驱动所对应的服务
   hServiceDDK = CreateService(hServiceMgr,
      lpszDriverName, //驱动程序的在注册表中的名字  
      lpszDriverName, // 注册表驱动程序的 DisplayName 值  
      SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限  
      SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序  
      SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值(3) 决定启动顺序
      SERVICE_ERROR_IGNORE, //注册表驱动程序的 ErrorControl 值  
      szDriverImagePath, // 注册表驱动程序的 ImagePath 值  errcode:2
      NULL,  //GroupOrder 在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList处决定启动顺序
      NULL,
      NULL,
      NULL,
      NULL);

   DWORD dwRtn;
   //判断服务是否失败
   if (hServiceDDK == NULL)
   {
      dwRtn = GetLastError();
      if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
      {
         //由于其他原因创建服务失败
         printf("CrateService() Failed %d ! \n", dwRtn);
         bRet = FALSE;
         goto BeforeLeave;
      }
      else
      {
         //服务创建失败,是由于服务已经创立过
         printf("CrateService() Failed Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n");
      }

      // 驱动程序已经加载,只需要打开  
      hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
      if (hServiceDDK == NULL)
      {
         //如果打开服务也失败,则意味错误
         dwRtn = GetLastError();
         printf("OpenService() Failed %d ! \n", dwRtn);
         bRet = FALSE;
         goto BeforeLeave;
      }
      else
      {
         printf("OpenService() ok ! \n");
      }
   }
   else
   {
      printf("CrateService() ok ! \n");
   }

   //开启此项服务
   bRet = StartService(hServiceDDK, NULL, NULL);
   if (!bRet)
   {
      DWORD dwRtn = GetLastError();
      if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
      {
         printf("StartService() Failed %d ! \n", dwRtn);
         bRet = FALSE;
         goto BeforeLeave;
      }
      else
      {
         if (dwRtn == ERROR_IO_PENDING)
         {
            //设备被挂住
            printf("StartService() Failed ERROR_IO_PENDING ! \n");
            bRet = FALSE;
            goto BeforeLeave;
         }
         else
         {
            //服务已经开启
            printf("StartService() Failed ERROR_SERVICE_ALREADY_RUNNING ! \n");
            bRet = TRUE;
            goto BeforeLeave;
         }
      }
   }
   bRet = TRUE;
   //离开前关闭句柄
BeforeLeave:
   if (hServiceDDK)
   {
      CloseServiceHandle(hServiceDDK);
   }
   if (hServiceMgr)
   {
      CloseServiceHandle(hServiceMgr);
   }
   return bRet;
}

//卸载驱动程序  
BOOL UnloadDriver(char * szSvrName)
{
   BOOL bRet = FALSE;
   SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
   SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
   SERVICE_STATUS SvrSta;
   //打开SCM管理器
   hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (hServiceMgr == NULL)
   {
      //打开SCM管理器失败
      printf("OpenSCManager() Failed %d ! \n", GetLastError());
      bRet = FALSE;
      goto BeforeLeave;
   }
   else
   {
      //打开SCM管理器失败成功
      printf("OpenSCManager() ok ! \n");
   }
   //打开驱动所对应的服务
   hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS);

   if (hServiceDDK == NULL)
   {
      //打开驱动所对应的服务失败
      printf("OpenService() Failed %d ! \n", GetLastError());
      bRet = FALSE;
      goto BeforeLeave;
   }
   else
   {
      printf("OpenService() ok ! \n");
   }
   //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。  
   if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta))
   {
      printf("ControlService() Failed %d !\n", GetLastError());
   }
   else
   {
      //打开驱动所对应的失败
      printf("ControlService() ok !\n");
   }


   //动态卸载驱动程序。  

   if (!DeleteService(hServiceDDK))
   {
      //卸载失败
      printf("DeleteSrevice() Failed %d !\n", GetLastError());
   }
   else
   {
      //卸载成功
      printf("DelServer:deleteSrevice() ok !\n");
   }

   bRet = TRUE;
BeforeLeave:
   //离开前关闭打开的句柄
   if (hServiceDDK)
   {
      CloseServiceHandle(hServiceDDK);
   }
   if (hServiceMgr)
   {
      CloseServiceHandle(hServiceMgr);
   }
   return bRet;
}

void TestDriver()
{
   //测试驱动程序  
   HANDLE hDevice = CreateFile("\\\\.\\ntmodeldrv",       //打开驱动链接,对应R0的DispatchCreate
      GENERIC_WRITE | GENERIC_READ,
      0,
      NULL,
      OPEN_EXISTING,
      0,
      NULL);
   if (hDevice != INVALID_HANDLE_VALUE)
   {
      printf("Create Device ok ! \n");
   }
   else
   {
      printf("Create Device Failed %d ! \n", GetLastError());
      return;
   }
   CHAR bufRead[1024] = { 0 };
   WCHAR bufWrite[1024] = L"Hello, world";

   DWORD dwRead = 0;
   DWORD dwWrite = 0;

   ReadFile(hDevice, bufRead, 1024, &dwRead, NULL);   //对应R0的DispatchRead
   printf("Read done!:%ws\n", bufRead);
   printf("Please press any key to write\n");
   getch();
   WriteFile(hDevice, bufWrite, (wcslen(bufWrite) + 1) * sizeof(WCHAR), &dwWrite, NULL);   //对应R0的DispatchWrite

   printf("Write done!\n");

   printf("Please press any key to deviceiocontrol\n");
   getch();
   CHAR bufInput[1024] = "Hello, world";
   CHAR bufOutput[1024] = { 0 };
   DWORD dwRet = 0;

   WCHAR bufFileInput[1024] = L"c:\\docs\\hi.txt";

   printf("Please press any key to send PRINT\n");
   getch();
   DeviceIoControl(hDevice,   //对应R0的DispatchIoctrl
      CTL_PRINT,
      bufFileInput,
      sizeof(bufFileInput),
      bufOutput,
      sizeof(bufOutput),
      &dwRet,
      NULL);
   printf("Please press any key to send HELLO\n");
   getch();
   DeviceIoControl(hDevice,
      CTL_HELLO,
      NULL,
      0,
      NULL,
      0,
      &dwRet,
      NULL);
   printf("Please press any key to send BYE\n");
   getch();
   DeviceIoControl(hDevice,
      CTL_BYE,
      NULL,
      0,
      NULL,
      0,
      &dwRet,
      NULL);
   printf("DeviceIoControl done!\n");
   CloseHandle(hDevice);
}

int main(int argc, char* argv[])
{
   //加载驱动
   BOOL bRet = LoadDriver(DRIVER_NAME, DRIVER_PATH);
   if (!bRet)
   {
      printf("LoadNTDriver error\n");
      return 0;
   }
   //加载成功

   printf("press any key to create device!\n");
   getch();

   TestDriver();

   //这时候你可以通过注册表,或其他查看符号连接的软件验证。
   printf("press any key to stop service!\n");
   getch();

   //卸载驱动
   bRet = UnloadDriver(DRIVER_NAME);
   if (!bRet)
   {
      printf("UnloadNTDriver error\n");
      return 0;
   }


   return 0;
}

>