初步实现系统级拦截应用程序取硬盘物理序列号

【详细过程】
  以前想模拟某个程序取硬盘系列号,就将一个DLL注入进去,拦截DeviceIoControl的返回值,将其修改为目标硬盘的值。
  后来看到REGMON 从SSDT着手拦截注册表操作,可以看到任何程序的读写操作,就仿着改了一下程序。经测试可以欺骗相当
  一部分程序读硬盘序列号,包括ASPROTECT及用其加密算法的子孙:)。
  
  我的思路是这样的:
   
  1、首先用程序取自己的真正的硬盘序列号,假设为XXXX
  
  2、拦截系统的ZwDeviceIoControlFile,并判断入口参数中的IoControlCode ,只有某几个特定的值用来取序列号的,
     目前在所有的程序中取硬盘序列号的,我只发现两个值,
0x7C088 和 0x4D008


第一个数 0x7C088 是对应 SMART drive(ATA) 硬盘接口,这个数在 NTDDK 的 ntdddisk.h 中定义:

代码:

   
   

//
// IOCTL support for SMART drive fault prediction.
//

#define SMART_RCV_DRIVE_DATA            CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

 


所以原文中对应的语句改成如下形式比较容易理解:

代码:

   
   

    if((SMART_RCV_DRIVE_DATA ==IoControlCode) && OutputBufferLength >DISK_SERIAL_BUFF_LENGTH){
        PUCHAR Locate = IsSubString(OutputBuffer,__DiskSerial,OutputBufferLength,DISK_SERIAL_BUFF_LENGTH);
        if(Locate){
            UCHAR i;
            for(i=0;i<20;i++){
                Locate[i]= __ChangeTo[i];
            }
        }
    }

 


在DDK中对 SMART_RCV_DRIVE_DATA 的解释如下:

代码:

   
   

SMART_RCV_DRIVE_DATA
Operation
Returns the ATA-2 identify data, the SMART thresholds, or the SMART attributes for the device. This IOCTL must be handled by drivers that support SMART.

Input
The buffer at Irp->AssociatedIrp.SystemBuffer contains a SENDCMDINPARAMS structure that describes the request being sent to the device. The irDriveRegs.bCommandReg member specifies ID_CMD when identify data is requested and SMART_CMD when SMART data is requested. If SMART data is requested, the irDriveRegs.bFeaturesReg member specifies either READ_ATTRIBUTES or READ_THRESHOLDS.

Parameters.DeviceIoControl.InputBufferLength specifies the size in bytes of the input buffer, which must be >= (sizeof(SENDCMDINPARAMS) – 1).

Parameters.DeviceIoControl.OutputBufferLength specifies the size in bytes of the output buffer, which must be >= (sizeof(SENDCMDOUTPARAMS) – 1 + 512).

Output
The driver returns the SENDCMDOUTPARAMS structure and a 512-byte buffer of drive data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block
The driver sets the Information field to (sizeof(SENDCMDOUTPARAMS) – 1 + 512) when it sets the Status field to STATUS_SUCCESS. Otherwise, the driver sets the Information field to zero and the Status field to possibly STATUS_INVALID_PARAMETER or STATUS_INSUFFICIENT_RESOURCES.

 


第二个数0x4D008 对应 SCSI 接口的硬盘,这个数在NTDDK 的 ntddscsi.h 中定义:

代码:

   
   

//
// NtDeviceIoControlFile IoControlCode values for this device.
//
// Warning:  Remember that the low two bits of the code specify how the
//           buffers are passed to the driver!
//

#define IOCTL_SCSI_MINIPORT             CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

 


在NTDDK中对 IOCTL_SCSI_MINIPORT 的解释如下:

代码:

   
   

IOCTL_SCSI_MINIPORT
Operation
Sends a special control function to an HBA-specific miniport driver. Results vary, depending on the particular miniport driver to which this request is forwarded. If the caller specifies a nonzero Length, either the input or output buffer must be at least (sizeof(SRB_IO_CONTROL) + DataBufferLength)).

Input
The buffer at Irp->AssociatedIrp.SystemBuffer must contain an SRB_IO_CONTROL structure. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be at least sizeof (SRB_IO_CONTROL), with additional storage for data if the Length field is nonzero.

Output
An updated SRB_IO_CONTROL structure is returned to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block
The Information field contains the number of bytes returned in the output buffer. The Status field indicates the results of the operation.


    
     如果IoControlCode为上面的值,读取系统原有的ZwDeviceIoControlFile返回BUFFER,并用串匹配方法查找 这个返回值中存在
     不存在XXXX,如果存在,替换为你要欺骗的值.
  
  代码很简单:
  
  UCHAR __DiskSerial[DISK_SERIAL_BUFF_LENGTH]={0};
  UCHAR __ChangeTo  [DISK_SERIAL_BUFF_LENGTH]={0};
  
  //一个简单的低率串匹配算法 ,判断一个串S1是不是另外一个串S2的子串
  PUCHAR IsSubString(PUCHAR String, PUCHAR SubString ,ULONG StringLength ,ULONG SubStringLength)
  {
      ULONG i,j;
      for(i=0;i<StringLength - SubStringLength +1 ;i++){
          for(j=0;j<SubStringLength;j++){
              if(String[i+j]!=SubString[j])
                  break;
          }
          if(j==SubStringLength)  //match a substring
              return String+i;
      }
      return NULL;
  }
  
  //----------------------------------------------------------------------
  //
  //  Our own routine for ZwDeviceIocontrolFile
  //  We change the hard disk serial number value requested by user
  //
  //----------------------------------------------------------------------
  NTSTATUS HookZwDeviceIoControlFile(
                                    IN HANDLE FileHandle,
                                    IN HANDLE Event OPTIONAL,
                                    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                    IN PVOID ApcContext OPTIONAL,
                                    OUT PIO_STATUS_BLOCK IoStatusBlock,
                                    IN ULONG IoControlCode,
                                    IN PVOID InputBuffer OPTIONAL,
                                    IN ULONG InputBufferLength,
                                    OUT PVOID OutputBuffer OPTIONAL,
                                    IN ULONG OutputBufferLength
                                    )
  {
      NTSTATUS rc;
  
      rc = RealZwDeviceIoControlFile (
          FileHandle,
          Event,
          ApcRoutine,
          ApcContext,
          IoStatusBlock,
          IoControlCode,
          InputBuffer,
          InputBufferLength,
          OutputBuffer,
          OutputBufferLength
      );
      //判断IoControlcode是不是取序列号的值
      if((0x7c088 ==IoControlCode) && OutputBufferLength >DISK_SERIAL_BUFF_LENGTH){
  
          //判断返回值中是否包含当前的硬盘序列号,是的话用假的替换
          PUCHAR Locate = IsSubString(OutputBuffer,__DiskSerial,OutputBufferLength,DISK_SERIAL_BUFF_LENGTH);
          if(Locate){
              UCHAR i;
              for(i=0;i<20;i++){
                  Locate[i]= __ChangeTo[i];
              }
          }
      }
      return(rc);
  }
  
  
  目前,驱动只处理了简单的几个应用层的消息,包括停止欺骗,开始欺骗,设置新的欺骗值。
  
  BOOLEAN  HDHookDeviceControl( IN PFILE_OBJECT FileObject, IN BOOLEAN Wait,
                                IN PVOID InputBuffer, IN ULONG InputBufferLength,
                                OUT PVOID OutputBuffer, IN ULONG OutputBufferLength,
                                IN ULONG IoControlCode, OUT PIO_STATUS_BLOCK IoStatus,
                                IN PDEVICE_OBJECT DeviceObject ) {
      BOOLEAN                 retval = FALSE;
      ULONG i;
  
      // Its a message from our GUI!
      IoStatus->Status      = STATUS_SUCCESS; // Assume success
      IoStatus->Information = 0;              // Assume nothing returned
  
  
      switch ( IoControlCode ) {
  
      //  开始欺骗
  
      case HDHOOK_HOOK:
          HookStart();
          break;
  
      // 停止欺骗
  
      case HDHOOK_UNHOOK:
          HookStop();
          break;
  
      // 告诉驱动当前自己硬盘的序列号值为多少
  
      case HDHOOK_SETSELFVALUE:
        if( InputBufferLength < DISK_SERIAL_BUFF_LENGTH || InputBuffer == NULL){
          IoStatus->Status = STATUS_INVALID_PARAMETER;
          break;
        }
        for(i=0; i< DISK_SERIAL_BUFF_LENGTH ;i++)
          __DiskSerial[i] = ((UCHAR *)InputBuffer)[i];
  
          break;
  
      // 设置新的欺骗的硬盘序列号
  
      case HDHOOK_SETEMULABLEVALUE:
        if( InputBufferLength < DISK_SERIAL_BUFF_LENGTH || InputBuffer == NULL){
          IoStatus->Status = STATUS_INVALID_PARAMETER;
          break;
        }
  
        for(i=0;i< DISK_SERIAL_BUFF_LENGTH ;i++)
          __ChangeTo[i] = ((UCHAR *)InputBuffer)[i];
  
          break;
  
    //返回驱动的版本号
  
    case HDHOOK_VERSION:
          if ( OutputBufferLength < sizeof(ULONG) ||
               OutputBuffer == NULL ) {
              IoStatus->Status = STATUS_INVALID_PARAMETER;
              break;
          }
  
          *(ULONG *)OutputBuffer = REGMONVERSION;
          IoStatus->Information = sizeof(ULONG);
          break;
  
      default:
          IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST;
          break;
      }
      return TRUE;
  }
  
  
  
  应用层程序可以通过如下简单代码与驱动进行通信:
  
  #define HDHOOK_HOOK                  (ULONG) CTL_CODE( FILE_DEVICE_REGMON, 0x00, METHOD_BUFFERED, FILE_ANY_ACCESS )
  #define HDHOOK_UNHOOK                (ULONG) CTL_CODE( FILE_DEVICE_REGMON, 0x01, METHOD_BUFFERED, FILE_ANY_ACCESS )
  #define HDHOOK_VERSION               (ULONG) CTL_CODE( FILE_DEVICE_REGMON, 0x02, METHOD_BUFFERED, FILE_ANY_ACCESS )
  #define HDHOOK_SETSELFVALUE          (ULONG) CTL_CODE( FILE_DEVICE_REGMON, 0x03, METHOD_BUFFERED, FILE_ANY_ACCESS )
  #define HDHOOK_SETEMULABLEVALUE      (ULONG) CTL_CODE( FILE_DEVICE_REGMON, 0x04, METHOD_BUFFERED, FILE_ANY_ACCESS )
  
  #define DISK_SERIAL_BUFF_LENGTH      20
  
  //设置新的序列号模拟值
  DeviceIoControl(__SysHandle,HDHOOK_SETEMULABLEVALUE,szEmulSerial,DISK_SERIAL_BUFF_LENGTH, NULL, 0, &dwDummy, NULL) ;
  
  //告诉驱动自己的硬盘序列号
  DeviceIoControl(__SysHandle,HDHOOK_SETSELFVALUE,szBuffer,DISK_SERIAL_BUFF_LENGTH, NULL, 0, &dwDummy, NULL) ;
  
  //开始拦截
  DeviceIoControl(__SysHandle,HDHOOK_HOOK,NULL,0,NULL,0,&dwDummy,NULL) ;
  
  
  //停止拦截
  DeviceIoControl(__SysHandle,HDHOOK_UNHOOK,NULL,0,NULL,0,&dwDummy,NULL) ;
  
  其中__SysHandle是安装驱动的句柄
  
  
  这篇文章的思路来自regmon(regmon版权申明我保留在文件中),写出来是方便调试那些有正版注册号且以硬盘序列号生成注册码的程序,方便一 下大家。
  
  附件中包含我写的一个简单的UI,用来与驱动通信。
  
  如果你觉得有用,希望有人能完成如下功能:
  1、进程过滤功能,只对特定程序拦截。
  2、考虑将上面的那个串匹配算法改得高效些,数据结构教材上有现成的:)
  3、那个返回值中有硬盘厂家,磁道数等信息,完成完全模拟,还有IoControlCode 的完善。
  
  若改好了希望能传一份给我 :)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值