diff --git a/src/hooks.cpp b/src/hooks.cpp index d11ea58..fe1c464 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -1,10 +1,34 @@ #include +#include +#include #include "hooks.h" #include "logging.h" #include "process.h" #include "secdrv_ioctl.h" +template +inline T WordSwap(T w) { + USHORT temp; + + temp = ((*((USHORT*)&w) & 0xff00) >> 8); + temp |= ((*((USHORT*)&w) & 0x00ff) << 8); + + return *((T*)&temp); +} + +template +inline T DWordSwap(T dw) { + ULONG temp; + + temp = *((ULONG*)&dw) >> 24; + temp |= ((*((ULONG*)&dw) & 0x00FF0000) >> 8); + temp |= ((*((ULONG*)&dw) & 0x0000FF00) << 8); + temp |= ((*((ULONG*)&dw) & 0x000000FF) << 24); + + return *((T*)&temp); +} + NTSTATUS NTAPI hooks::NtDeviceIoControlFile_Hook(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, @@ -19,20 +43,67 @@ NTSTATUS NTAPI hooks::NtDeviceIoControlFile_Hook(HANDLE FileHandle, /* all IOCTLs will pass through this function, but it's probably fine since * secdrv uses unique control codes */ - if ( IoControlCode == secdrvIoctl::ioctlCodeMain ) { - if ( secdrvIoctl::ProcessMainIoctl(InputBuffer, + if (IoControlCode == secdrvIoctl::ioctlCodeMain) { + if (secdrvIoctl::ProcessMainIoctl(InputBuffer, InputBufferLength, OutputBuffer, - OutputBufferLength) ) { + OutputBufferLength)) { IoStatusBlock->Information = OutputBufferLength; IoStatusBlock->Status = STATUS_SUCCESS; } else IoStatusBlock->Status = STATUS_UNSUCCESSFUL; } - else if ( IoControlCode == 0xCA002813 ) { + else if (IoControlCode == 0xCA002813) { spdlog::error("IOCTL 0xCA002813 unhandled (please report!)"); IoStatusBlock->Status = STATUS_UNSUCCESSFUL; } + else if (IoControlCode == IOCTL_SCSI_PASS_THROUGH) { + // Remember input data buffer size and sense info size for later + SCSI_PASS_THROUGH* inStruct = (SCSI_PASS_THROUGH*)InputBuffer; + ULONG inSenseSize = inStruct->SenseInfoLength; + ULONG inDataSize = inStruct->DataTransferLength; + + // Execute the original function + NTSTATUS result = NtDeviceIoControlFile_Orig(FileHandle, Event, ApcRoutine, ApcContext, + IoStatusBlock, IoControlCode, InputBuffer, + InputBufferLength, OutputBuffer, + OutputBufferLength); + + // This is a workaround for a bug in Alcohol SATA controller where it doesn't return + // "LBA out of range" error for out-of-range sectors. + // + // This breaks SafeDisc disc check on later versions since it tries to read track 1 pregap + // (negative LBA) to see if the drive supports it. Alcohol doesn't return an error for these + // sectors despite not being able to output them so SafeDisc keeps happily reading pregap sectors + // and then fails the disc check since Alcohol doesn't actually output valid sector data. + if (result == STATUS_SUCCESS && inStruct && inSenseSize && inDataSize) { + UCHAR* senseBuffer = (UCHAR*)inStruct + inStruct->SenseInfoOffset; + UCHAR cmd = inStruct->Cdb[0x00]; + + if (cmd == 0x28 || cmd == 0xBE) { // READ (10), READ CD + LONG lba = DWordSwap(*(LONG*)(inStruct->Cdb + 2)); + + if (lba < 0 && inStruct->ScsiStatus == 0x00 && inStruct->DataTransferLength == 0x00) { + // If no error was returned for negative LBA but output buffer is empty, this is bugged + // Alcohol behavior and we need to manually write the error. + spdlog::info("Incorrect output from disc drive when reading sector {}, " + "manually returning LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE", lba); + + inStruct->ScsiStatus = 0x02; // CHECK_CONDITION + inStruct->SenseInfoLength = std::min(0x12ul, inSenseSize); + memset(senseBuffer, 0x00, inStruct->SenseInfoLength); + + senseBuffer[0x00] = 0xf0; // response code + senseBuffer[0x02] = 0x05; // ILLEGAL_REQUEST + senseBuffer[0x07] = 0x0a; // length + senseBuffer[0x0c] = 0x21; // LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE + senseBuffer[0x0d] = 0x00; + } + } + } + + return result; + } else { // not a secdrv request, pass to original function return NtDeviceIoControlFile_Orig(FileHandle, Event, ApcRoutine, ApcContext,