Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a workaround for Alcohol/DT virtual drive bug #16

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 78 additions & 4 deletions src/hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
#include <ntstatus.h>
#include <winioctl.h>
#include <ntddscsi.h>

#include "hooks.h"
#include "logging.h"
#include "process.h"
#include "secdrv_ioctl.h"

template <typename T>
inline T WordSwap(T w) {
USHORT temp;

temp = ((*((USHORT*)&w) & 0xff00) >> 8);
temp |= ((*((USHORT*)&w) & 0x00ff) << 8);

return *((T*)&temp);
}

template <typename T>
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,
Expand All @@ -19,20 +43,70 @@ 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) {
spdlog::trace("IOCTL_SCSI_PASS_THROUGH called", IoControlCode);

// Remember input data buffer size and sense info size for later
PSCSI_PASS_THROUGH inStruct = (PSCSI_PASS_THROUGH)InputBuffer;
UCHAR 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 SPTD SATA controller where it doesn't return
// "LBA out of range" error for out-of-range sectors (affects both Alcohol and DAEMON Tools).
//
// 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. SPTD 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 SPTD doesn't actually output valid sector data.
if (result == STATUS_SUCCESS && inSenseSize && inDataSize) {
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
// SPTD 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);

UCHAR* senseBuffer = (UCHAR*)inStruct + inStruct->SenseInfoOffset;

inStruct->ScsiStatus = 0x02; // CHECK_CONDITION
inStruct->SenseInfoLength = std::min(0x12ui8, 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,
Expand Down
12 changes: 8 additions & 4 deletions src/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ void logging::SetupLogger() {
/* Set log level */
TCHAR envLogLevel[32767];
GetEnvironmentVariable("SAFEDISCSHIM_LOGLEVEL", envLogLevel, sizeof(envLogLevel));
if ( GetLastError() == ERROR_ENVVAR_NOT_FOUND ) {
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
#ifdef _DEBUG
spdlog::set_level(spdlog::level::trace);
#else
// don't output logs if envvar is not defined
return;
spdlog::set_level(spdlog::level::off);
#endif
}
else spdlog::cfg::helpers::load_levels(envLogLevel);

/* Return early if logs are off, so files are not created */
if ( spdlog::get_level() == spdlog::level::off )
if (spdlog::get_level() == spdlog::level::off)
return;

/* Log to ringbuffer until we can determine log file name later */
Expand All @@ -40,12 +40,16 @@ void logging::SetupLogger() {
}

void logging::SetLoggerFileName(const std::string& fileName) {
/* Return early if logs are off, so files are not created */
if (spdlog::get_level() == spdlog::level::off)
return;

try {
const auto logger = spdlog::basic_logger_mt("file",
fileName, true);
spdlog::set_default_logger(logger);
}
catch (const spdlog::spdlog_ex &ex) {
catch (const spdlog::spdlog_ex& ex) {
spdlog::info("Error logging to file ({}), logging to stdout instead.",
ex.what());
}
Expand Down