Skip to content

Commit 45f0b06

Browse files
Added a workaround for Alcohol virtual drive bug
1 parent 5d81e07 commit 45f0b06

File tree

1 file changed

+75
-4
lines changed

1 file changed

+75
-4
lines changed

Diff for: src/hooks.cpp

+75-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
11
#include <ntstatus.h>
2+
#include <winioctl.h>
3+
#include <ntddscsi.h>
24

35
#include "hooks.h"
46
#include "logging.h"
57
#include "process.h"
68
#include "secdrv_ioctl.h"
79

10+
template <typename T>
11+
inline T WordSwap(T w) {
12+
USHORT temp;
13+
14+
temp = ((*((USHORT*)&w) & 0xff00) >> 8);
15+
temp |= ((*((USHORT*)&w) & 0x00ff) << 8);
16+
17+
return *((T*)&temp);
18+
}
19+
20+
template <typename T>
21+
inline T DWordSwap(T dw) {
22+
ULONG temp;
23+
24+
temp = *((ULONG*)&dw) >> 24;
25+
temp |= ((*((ULONG*)&dw) & 0x00FF0000) >> 8);
26+
temp |= ((*((ULONG*)&dw) & 0x0000FF00) << 8);
27+
temp |= ((*((ULONG*)&dw) & 0x000000FF) << 24);
28+
29+
return *((T*)&temp);
30+
}
31+
832
NTSTATUS NTAPI hooks::NtDeviceIoControlFile_Hook(HANDLE FileHandle,
933
HANDLE Event,
1034
PIO_APC_ROUTINE ApcRoutine,
@@ -19,20 +43,67 @@ NTSTATUS NTAPI hooks::NtDeviceIoControlFile_Hook(HANDLE FileHandle,
1943

2044
/* all IOCTLs will pass through this function, but it's probably fine since
2145
* secdrv uses unique control codes */
22-
if ( IoControlCode == secdrvIoctl::ioctlCodeMain ) {
23-
if ( secdrvIoctl::ProcessMainIoctl(InputBuffer,
46+
if (IoControlCode == secdrvIoctl::ioctlCodeMain) {
47+
if (secdrvIoctl::ProcessMainIoctl(InputBuffer,
2448
InputBufferLength,
2549
OutputBuffer,
26-
OutputBufferLength) ) {
50+
OutputBufferLength)) {
2751
IoStatusBlock->Information = OutputBufferLength;
2852
IoStatusBlock->Status = STATUS_SUCCESS;
2953
}
3054
else IoStatusBlock->Status = STATUS_UNSUCCESSFUL;
3155
}
32-
else if ( IoControlCode == 0xCA002813 ) {
56+
else if (IoControlCode == 0xCA002813) {
3357
spdlog::error("IOCTL 0xCA002813 unhandled (please report!)");
3458
IoStatusBlock->Status = STATUS_UNSUCCESSFUL;
3559
}
60+
else if (IoControlCode == IOCTL_SCSI_PASS_THROUGH) {
61+
// Remember input data buffer size and sense info size for later
62+
SCSI_PASS_THROUGH* inStruct = (SCSI_PASS_THROUGH*)InputBuffer;
63+
ULONG inSenseSize = inStruct->SenseInfoLength;
64+
ULONG inDataSize = inStruct->DataTransferLength;
65+
66+
// Execute the original function
67+
NTSTATUS result = NtDeviceIoControlFile_Orig(FileHandle, Event, ApcRoutine, ApcContext,
68+
IoStatusBlock, IoControlCode, InputBuffer,
69+
InputBufferLength, OutputBuffer,
70+
OutputBufferLength);
71+
72+
// This is a workaround for a bug in Alcohol SATA controller where it doesn't return
73+
// "LBA out of range" error for out-of-range sectors.
74+
//
75+
// This breaks SafeDisc disc check on later versions since it tries to read track 1 pregap
76+
// (negative LBA) to see if the drive supports it. Alcohol doesn't return an error for these
77+
// sectors despite not being able to output them so SafeDisc keeps happily reading pregap sectors
78+
// and then fails the disc check since Alcohol doesn't actually output valid sector data.
79+
if (result == STATUS_SUCCESS && inStruct && inSenseSize && inDataSize) {
80+
UCHAR* senseBuffer = (UCHAR*)inStruct + inStruct->SenseInfoOffset;
81+
UCHAR cmd = inStruct->Cdb[0x00];
82+
83+
if (cmd == 0x28 || cmd == 0xBE) { // READ (10), READ CD
84+
LONG lba = DWordSwap(*(LONG*)(inStruct->Cdb + 2));
85+
86+
if (lba < 0 && inStruct->ScsiStatus == 0x00 && inStruct->DataTransferLength == 0x00) {
87+
// If no error was returned for negative LBA but output buffer is empty, this is bugged
88+
// Alcohol behavior and we need to manually write the error.
89+
spdlog::info("Incorrect output from disc drive when reading sector {}, "
90+
"manually returning LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE", lba);
91+
92+
inStruct->ScsiStatus = 0x02; // CHECK_CONDITION
93+
inStruct->SenseInfoLength = std::min(0x12ul, inSenseSize);
94+
memset(senseBuffer, 0x00, inStruct->SenseInfoLength);
95+
96+
senseBuffer[0x00] = 0xf0; // response code
97+
senseBuffer[0x02] = 0x05; // ILLEGAL_REQUEST
98+
senseBuffer[0x07] = 0x0a; // length
99+
senseBuffer[0x0c] = 0x21; // LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE
100+
senseBuffer[0x0d] = 0x00;
101+
}
102+
}
103+
}
104+
105+
return result;
106+
}
36107
else {
37108
// not a secdrv request, pass to original function
38109
return NtDeviceIoControlFile_Orig(FileHandle, Event, ApcRoutine, ApcContext,

0 commit comments

Comments
 (0)