Skip to content
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 Alcohol SATA controller where it doesn't return
// "LBA out of range" error for out-of-range sectors (also affects 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. 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 && 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
// 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);

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
30 changes: 18 additions & 12 deletions src/logging.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/ringbuffer_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/cfg/helpers.h>

#include "logging.h"
Expand All @@ -14,22 +15,23 @@ 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 */
auto logger = std::make_shared<spdlog::logger>("ringbuffer", ringbufferSink);
spdlog::initialize_logger(logger);
spdlog::set_default_logger(logger);

spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
Expand All @@ -40,25 +42,29 @@ 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());
const auto logger = spdlog::stdout_logger_mt("stdout");
spdlog::set_default_logger(logger);
}

// temporarily remove formatting since ringbuffer logs are already formatted
spdlog::set_pattern("%v");

std::vector<std::string> logMessages = ringbufferSink->last_formatted();
std::vector<spdlog::details::log_msg_buffer> logMessages = ringbufferSink->last_raw();

// output all logs in buffer to file
for (const auto& message : logMessages) {
spdlog::info(message);
for (const auto sink : spdlog::default_logger()->sinks()) {
for (const auto& message : logMessages) {
sink->log(message);
}
sink->flush();
}
// restore formatting
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
}