【詳細過程】
以前想類比某個程式取硬碟系列號,就將一個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 的完善。
若改好了希望能傳一份給我 :)