/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// Windows Vista 64Bit 미만에 운영체제에서 ( Windows XP 64Bit, Windows 2003 Server 64Bit )
// 32Bit 프로세스에서 IOCTL_SCSI_PASS_THROUGH_DIRECT 를 내릴 때 DeviceIoControl이 실패하게 되는데 이러한 문제점을
// 하드코딩하여 수정하는 코드입니다.
//
// 정확하게 말하자면 실패 위치는.. WinDbg로 디버깅을 하면서 차례대로 내려갔을 때..
//
// fffffadf`f543f48c e80ffaffff call SCSIPORT!SpHandleIoctlScsiPassThrough (fffffadf`f543eea0)
//
// SCSIPORT!SpHandleIoctlScsiPassThrough:
// fffffadf`f543eea0 4883ec28 sub rsp,28h
// fffffadf`f543eea4 48895c2438 mov qword ptr [rsp+38h],rbx
// fffffadf`f543eea9 4889742440 mov qword ptr [rsp+40h],rsi
// fffffadf`f543eeae 488bf1 mov rsi,rcx
// fffffadf`f543eeb1 488b4940 mov rcx,qword ptr [rcx+40h]
// fffffadf`f543eeb5 48897c2448 mov qword ptr [rsp+48h],rdi
// fffffadf`f543eeba 488bfa mov rdi,rdx
// fffffadf`f543eebd e84e0b0000 call SCSIPORT!SpSendPassThrough (fffffadf`f543fa10) <= 에러 발생위치
//
// fffffadf`f543fae2 e869720000 call SCSIPORT!PortSendPassThrough (fffffadf`f5446d50) < = 여기서 에러남
//
// fffffadf`f5446e8a e8a1000000 call SCSIPORT!PortPassThroughInitialize (fffffadf`f5446f30) <= 여기서 에러남
//
// SCSIPORT!PortPassThroughInitialize 함수내부에서
// fffffadf`f5447151 3b4d08 cmp ecx,dword ptr [rbp+8] ss:0018:fffffadf`f6ea0b6c=00000001
// 다음과 같이 값을 비교하게 되는데 dword ptr [rbp+8] ss:0018:fffffadf`f6ea0b6c 에 값이 0x01이어서 조건문에 의해 에러가 발생하게 됩니다.
//
// 클론시디에 경우에는
/*
.text:0000000000011CC7 call sub_16200
.text:0000000000011CCC lea rcx, [rsp+1D8h+var_158]
.text:0000000000011CD4 mov [rsp+1D8h+var_158], r12d
.text:0000000000011CDC call cs:RtlGetVersion <= 버전을 비교하는 함수를 호출..
.text:0000000000011CE2 cmp eax, r15d
.text:0000000000011CE5 jl short loc_11CF1
.text:0000000000011CE7 cmp [rsp+1D8h+var_154], 6 <= 6이라는 값을 비교하는걸 보니 dwMajorVersion와 비교할 가능성이 매우 높습니다.
.text:0000000000011CEF jnb short loc_11D0D // 부호 없는 데이터, 작지 않을 때
.text:0000000000011CF1
.text:0000000000011CF1 loc_11CF1: ; CODE XREF: HwFindAdapter+F1j <= 작으면 이곳이 호출됩니다.
.text:0000000000011CF1 mov rax, [rbx+148h]
.text:0000000000011CF8 mov cs:qword_1A230, rax
.text:0000000000011CFF lea rax, SetHalGetAdapter <= 그리고 SetHalGetAdapter 에 주소를 넣습니다.
.text:0000000000011D06 mov [rbx+148h], rax
.text:0000000000011D0D loc_11D0D: ; CODE XREF: HwFindAdapter+FBj
.text:0000000000011D0D mov eax, cs:dword_1A238
.text:0000000000011D13 mov [rsi+17F8h], eax
*/
// 위에 코드가 실행 된 후에 HalGetAdapter 함수를 SCSIPort 내부에서 클론시디 드라이버에 있는 HalGetAdapter를 호출하게 되는데 어떻게 해서 그렇게 했는지 모르겠습니다.
// 중요한것은 저렇게 루틴을 탔을 경우에만 IOCTL_SCSI_PASS_THROUGH_DIRECT가 전송이 되고 그렇지 않으면 전송이 되지 않습니다.
//
// dword ptr [rbp+8] ss:0018:fffffadf`f6ea0b6c <= 해당 주소를 조사한 결과
// (UCHAR *)(g_pDriverObject->DeviceObject->DeviceExtension) + 0x3EC 위치에 값이 있었습니다.
// 비교는 ecx 레지스터와 비교를 하게 되므로 32Bit 값일것입니다.
//
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
#define HACK_IOCTL_SCSI_PASS_THROUGH_DIRECT_OFFSET 0x3EC
PVOID g_HackDeviceExtensionPointer = NULL;
VOID Hack64BitIOCTL_SCSI_PASS_THROUGH_DIRECT()
{
if (g_HackDeviceExtensionPointer != NULL)
{
KdPrint(("H.A.C.K IOCTL_SCSI_PASS_THROUGH_DIRECT\n"));
*(DWORD *)(((UCHAR *)g_HackDeviceExtensionPointer + HACK_IOCTL_SCSI_PASS_THROUGH_DIRECT_OFFSET)) = 0x00000011;
}
}
VOID InitHack64BitIOCTL_SCSI_PASS_THROUGH_DIRECT(PDRIVER_OBJECT pDriverObject)
{
NTSTATUS ntStatus;
RTL_OSVERSIONINFOW rtlOsVersionInfoW;
ntStatus = RtlGetVersion(&rtlOsVersionInfoW);
if (NT_SUCCESS(ntStatus) == TRUE)
{
if (rtlOsVersionInfoW.dwMajorVersion < 6)
{
g_HackDeviceExtensionPointer = pDriverObject->DeviceObject->DeviceExtension;
}
}
else
{
NOTHING
}
}
InitHack64BitIOCTL_SCSI_PASS_THROUGH_DIRECT 함수에 인자 pDriverObject는 DriverEntry에서 받은 DriverObject를 넣으면 되며, HwFindAdapter에서 호출되어야 합니다.