From a874ebaf71da4f94df1dba85fb3ca18441e96f42 Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Sat, 4 Mar 2023 14:31:01 +0300 Subject: [PATCH 1/9] add "ioctl" target --- .gitignore | 5 +- src/wtf/fuzzer_ioctl.cc | 187 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 src/wtf/fuzzer_ioctl.cc diff --git a/.gitignore b/.gitignore index 8084ca0..875588e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ *.out *.app -src/wtf/fuzzer_* +.vs src/build/ src/build_msvc/ -targets/ \ No newline at end of file +src/out +targets/ diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc new file mode 100644 index 0000000..b9c9eba --- /dev/null +++ b/src/wtf/fuzzer_ioctl.cc @@ -0,0 +1,187 @@ +// 1ndahous3 - March 4 2023 +#include "backend.h" +#include "targets.h" +#include + +// 1. The target works with the state captured at the breakpoint at "ntdll!NtDeviceIoControlFile()". +// 2. It's better to capture a state with the maximum possible InputBufferLength. +// 3. For "MutateIoctl = true" tescases are manually created buffers: Ioctl (4 bytes) + InputBuffer ("InputBufferLength" bytes). + +namespace Ioctl { + +constexpr bool DebugLoggingOn = false; +constexpr bool MutateIoctl = true; + +constexpr uint32_t IoctlSizeIfPresent = MutateIoctl ? sizeof(uint32_t) : 0; + +template +void Print(const char *Format, const Args_t &...args) { + fmt::print("Ioctl: "); + fmt::print(fmt::runtime(Format), args...); +} + +template +void DebugPrint(const char *Format, const Args_t &...args) { + if constexpr (DebugLoggingOn) { + fmt::print("Ioctl: "); + fmt::print(fmt::runtime(Format), args...); + } +} + +bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { + + if constexpr (MutateIoctl) { + if (BufferSize < sizeof(uint32_t)) { + Print("The testcase buffer is too small: {} bytes\n", BufferSize); + return false; + } + } else { + if (BufferSize == 0) { + Print("The testcase buffer is empty"); + return false; + } + } + + const uint8_t *IoctlBuffer = Buffer + IoctlSizeIfPresent; + uint32_t IoctlBufferSize = BufferSize - IoctlSizeIfPresent; + + // ntdll!NtDeviceIoControlFile() + // Ioctl: @rsp + 0x30 + // InputBuffer: @rsp + 0x38 + // InputBufferLength: @rsp + 0x40 + + const Gva_t Stack = Gva_t(g_Backend->Rsp()); + + const Gva_t InputBufferLengthPtr = Stack + Gva_t(0x40); + uint32_t CurrentIoctlBufferSize = g_Backend->VirtRead4(InputBufferLengthPtr); + + if (IoctlBufferSize > CurrentIoctlBufferSize) { + DebugPrint("The testcase buffer is too large: {} ({:#x}) bytes, " + "maximum for the state: {} ({:#x}) bytes \n", + BufferSize, BufferSize, + CurrentIoctlBufferSize + IoctlSizeIfPresent, + CurrentIoctlBufferSize + IoctlSizeIfPresent); + return true; + } + + if (!g_Backend->VirtWriteStructDirty(InputBufferLengthPtr, + &IoctlBufferSize)) { + Print("VirtWriteDirty (InputBufferLength) failed\n"); + return false; + } + + if constexpr (MutateIoctl) { + + const uint32_t Ioctl = *(uint32_t *)Buffer; + const Gva_t IoctlPtr = Stack + Gva_t(0x30); + + if (!g_Backend->VirtWriteStructDirty(IoctlPtr, &Ioctl)) { + Print("VirtWriteDirty (Ioctl) failed\n"); + return false; + } + } + + const Gva_t InputBufferPtr = g_Backend->VirtReadGva(Stack + Gva_t(0x38)); + if (!g_Backend->VirtWriteDirty(InputBufferPtr, IoctlBuffer, IoctlBufferSize)) { + Print("VirtWriteDirty (InputBuffer) failed\n"); + return false; + } + + return true; +} + +bool Init(const Options_t &Opts, const CpuState_t &) { + + // + // Stop the test-case once we return back from the call [NtDeviceIoControlFile] + // + + const Gva_t Rip = Gva_t(g_Backend->Rip()); + const Gva_t AfterCall = Rip + Gva_t(0x14); + if (!g_Backend->SetBreakpoint(AfterCall, [](Backend_t *Backend) { + DebugPrint("Back from kernel!\n"); + Backend->Stop(Ok_t()); + })) { + Print("Failed to SetBreakpoint AfterCall\n"); + return false; + } + + // + // NOP the calls to DbgPrintEx. + // + + if (!g_Backend->SetBreakpoint("nt!DbgPrintEx", [](Backend_t *Backend) { + const Gva_t FormatPtr = Backend->GetArgGva(2); + const std::string &Format = Backend->VirtReadString(FormatPtr); + DebugPrint("DbgPrintEx: {}", Format); + Backend->SimulateReturnFromFunction(0); + })) { + Print("Failed to SetBreakpoint DbgPrintEx\n"); + return false; + } + + // + // Make ExGenRandom deterministic. + // + // kd> ub fffff805`3b8287c4 l1 + // nt!ExGenRandom+0xe0: + // fffff805`3b8287c0 480fc7f2 rdrand rdx + const Gva_t ExGenRandom = Gva_t(g_Dbg.GetSymbol("nt!ExGenRandom") + 0xf3); + if (g_Backend->VirtRead4(ExGenRandom) != 0xb8f2c70f) { + Print("It seems that nt!ExGenRandom's code has changed, update the " + "offset!\n"); + return false; + } + + if (!g_Backend->SetBreakpoint(ExGenRandom, [](Backend_t *Backend) { + DebugPrint("Hit ExGenRandom!\n"); + Backend->Rdx(Backend->Rdrand()); + })) { + Print("Failed to SetBreakpoint ExGenRandom\n"); + return false; + } + + // + // Catch bugchecks. + // + + if (!g_Backend->SetBreakpoint("nt!KeBugCheck2", [](Backend_t *Backend) { + const uint64_t BCode = Backend->GetArg(0); + const uint64_t B0 = Backend->GetArg(1); + const uint64_t B1 = Backend->GetArg(2); + const uint64_t B2 = Backend->GetArg(3); + const uint64_t B3 = Backend->GetArg(4); + const uint64_t B4 = Backend->GetArg(5); + const std::string Filename = + fmt::format("crash-{:#x}-{:#x}-{:#x}-{:#x}-{:#x}-{:#x}", BCode, B0, + B1, B2, B3, B4); + DebugPrint("KeBugCheck2: {}\n", Filename); + Backend->Stop(Crash_t(Filename)); + })) { + Print("Failed to SetBreakpoint KeBugCheck2\n"); + return false; + } + + // + // Catch context-switches. + // + + if (!g_Backend->SetBreakpoint("nt!SwapContext", [](Backend_t *Backend) { + DebugPrint("nt!SwapContext\n"); + Backend->Stop(Cr3Change_t()); + })) { + Print("Failed to SetBreakpoint SwapContext\n"); + return false; + } + + + return true; +} + +// +// Register the target. +// + +Target_t Ioctl("ioctl", Init, InsertTestcase); + +} // namespace Ioctl \ No newline at end of file From 37c2bd4db6036bc3f8efa4937dc90e79adda0dba Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:34:35 -0800 Subject: [PATCH 2/9] Prototype generic ioctl fuzzer --- .gitignore | 1 + src/wtf/backend.cc | 23 ++++- src/wtf/backend.h | 5 +- src/wtf/fuzzer_ioctl.cc | 223 +++++++++++++++++++++++++++------------- 4 files changed, 176 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 875588e..f31260c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ *.app .vs +src/wtf/fuzzer_* src/build/ src/build_msvc/ src/out diff --git a/src/wtf/backend.cc b/src/wtf/backend.cc index 85f9489..246b619 100644 --- a/src/wtf/backend.cc +++ b/src/wtf/backend.cc @@ -145,7 +145,8 @@ bool Backend_t::SimulateReturnFromFunction(const uint64_t Return) { return true; } -bool Backend_t::SimulateReturnFrom32bitFunction(const uint32_t Return, const uint32_t StdcallArgsCount) { +bool Backend_t::SimulateReturnFrom32bitFunction( + const uint32_t Return, const uint32_t StdcallArgsCount) { // // Set return value. // @@ -164,7 +165,7 @@ bool Backend_t::SimulateReturnFrom32bitFunction(const uint32_t Return, const uin return true; } -uint64_t Backend_t::GetArg(const uint64_t Idx) { +uint64_t Backend_t::GetArg(const uint64_t Idx, Gva_t &Address) { switch (Idx) { case 0: return Rcx(); @@ -175,13 +176,25 @@ uint64_t Backend_t::GetArg(const uint64_t Idx) { case 3: return R9(); default: { - const Gva_t ArgPtr = Gva_t(Rsp() + (8 + (Idx * 8))); - return VirtRead8(ArgPtr); + Address = Gva_t(Rsp() + (8 + (Idx * 8))); + return VirtRead8(Address); } } } -Gva_t Backend_t::GetArgGva(const uint64_t Idx) { return Gva_t(GetArg(Idx)); } +uint64_t Backend_t::GetArg(const uint64_t Idx) { + Gva_t Dummy; + return GetArg(Idx, Dummy); +} + +Gva_t Backend_t::GetArgGva(const uint64_t Idx, Gva_t &Address) { + return Gva_t(GetArg(Idx, Address)); +} + +Gva_t Backend_t::GetArgGva(const uint64_t Idx) { + Gva_t Dummy; + return Gva_t(GetArg(Idx, Dummy)); +} bool Backend_t::SaveCrash(const Gva_t ExceptionAddress, const uint32_t ExceptionCode) { diff --git a/src/wtf/backend.h b/src/wtf/backend.h index fe13d9f..926a7f2 100644 --- a/src/wtf/backend.h +++ b/src/wtf/backend.h @@ -481,14 +481,17 @@ class Backend_t { // bool SimulateReturnFromFunction(const uint64_t Return); - bool SimulateReturnFrom32bitFunction(const uint32_t Return, const uint32_t StdcallArgsCount = 0); + bool SimulateReturnFrom32bitFunction(const uint32_t Return, + const uint32_t StdcallArgsCount = 0); // // Utility function that grabs function arguments according to the Windows x64 // calling convention. // + uint64_t GetArg(const uint64_t Idx, Gva_t &Address); uint64_t GetArg(const uint64_t Idx); + Gva_t GetArgGva(const uint64_t Idx, Gva_t &Address); Gva_t GetArgGva(const uint64_t Idx); // diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index b9c9eba..2d41036 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -3,106 +3,190 @@ #include "targets.h" #include -// 1. The target works with the state captured at the breakpoint at "ntdll!NtDeviceIoControlFile()". -// 2. It's better to capture a state with the maximum possible InputBufferLength. -// 3. For "MutateIoctl = true" tescases are manually created buffers: Ioctl (4 bytes) + InputBuffer ("InputBufferLength" bytes). - namespace Ioctl { constexpr bool DebugLoggingOn = false; constexpr bool MutateIoctl = true; -constexpr uint32_t IoctlSizeIfPresent = MutateIoctl ? sizeof(uint32_t) : 0; - -template -void Print(const char *Format, const Args_t &...args) { - fmt::print("Ioctl: "); - fmt::print(fmt::runtime(Format), args...); -} +#define Print(Fmt, ...) fmt::print("ioctl: " Fmt, __VA_ARGS__) template void DebugPrint(const char *Format, const Args_t &...args) { if constexpr (DebugLoggingOn) { - fmt::print("Ioctl: "); + fmt::print("ioctl: "); fmt::print(fmt::runtime(Format), args...); } } +std::unique_ptr g_LastBuffer; +size_t g_LastBufferSize = 0; + bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { - if constexpr (MutateIoctl) { - if (BufferSize < sizeof(uint32_t)) { - Print("The testcase buffer is too small: {} bytes\n", BufferSize); - return false; + // + // If we have a testcase still alive, it means we haven't hit the return + // breakpoint which is suspicious. + // + + static bool FirstTime = true; + if (!FirstTime) { + if (!g_LastBuffer || g_LastBufferSize == 0) { + Print("The testcase hasn't been reset; something is wrong\n"); + std::abort(); } } else { - if (BufferSize == 0) { - Print("The testcase buffer is empty"); - return false; - } + FirstTime = false; } - const uint8_t *IoctlBuffer = Buffer + IoctlSizeIfPresent; - uint32_t IoctlBufferSize = BufferSize - IoctlSizeIfPresent; - - // ntdll!NtDeviceIoControlFile() - // Ioctl: @rsp + 0x30 - // InputBuffer: @rsp + 0x38 - // InputBufferLength: @rsp + 0x40 - - const Gva_t Stack = Gva_t(g_Backend->Rsp()); - - const Gva_t InputBufferLengthPtr = Stack + Gva_t(0x40); - uint32_t CurrentIoctlBufferSize = g_Backend->VirtRead4(InputBufferLengthPtr); - - if (IoctlBufferSize > CurrentIoctlBufferSize) { - DebugPrint("The testcase buffer is too large: {} ({:#x}) bytes, " - "maximum for the state: {} ({:#x}) bytes \n", - BufferSize, BufferSize, - CurrentIoctlBufferSize + IoctlSizeIfPresent, - CurrentIoctlBufferSize + IoctlSizeIfPresent); - return true; - } - - if (!g_Backend->VirtWriteStructDirty(InputBufferLengthPtr, - &IoctlBufferSize)) { - Print("VirtWriteDirty (InputBufferLength) failed\n"); - return false; - } - - if constexpr (MutateIoctl) { - - const uint32_t Ioctl = *(uint32_t *)Buffer; - const Gva_t IoctlPtr = Stack + Gva_t(0x30); + // + // If we are mutating the IoControlCode, we expect at least 4 bytes. + // - if (!g_Backend->VirtWriteStructDirty(IoctlPtr, &Ioctl)) { - Print("VirtWriteDirty (Ioctl) failed\n"); + if constexpr (MutateIoctl) { + if (BufferSize < sizeof(uint32_t)) { + Print("The testcase buffer is too small: {} bytes\n", BufferSize); return false; } } - const Gva_t InputBufferPtr = g_Backend->VirtReadGva(Stack + Gva_t(0x38)); - if (!g_Backend->VirtWriteDirty(InputBufferPtr, IoctlBuffer, IoctlBufferSize)) { - Print("VirtWriteDirty (InputBuffer) failed\n"); - return false; - } + // + // Copy the testcase; we'll inject it later if we hit NtDeviceIoControlFile. + // + g_LastBufferSize = BufferSize; + g_LastBuffer = std::make_unique(BufferSize); + std::memcpy(g_LastBuffer.get(), Buffer, BufferSize); return true; } bool Init(const Options_t &Opts, const CpuState_t &) { // - // Stop the test-case once we return back from the call [NtDeviceIoControlFile] + // Break on nt!NtDeviceIoControlFile. This is at that moment that we'll insert + // the testcase. // - const Gva_t Rip = Gva_t(g_Backend->Rip()); - const Gva_t AfterCall = Rip + Gva_t(0x14); - if (!g_Backend->SetBreakpoint(AfterCall, [](Backend_t *Backend) { - DebugPrint("Back from kernel!\n"); - Backend->Stop(Ok_t()); - })) { - Print("Failed to SetBreakpoint AfterCall\n"); + if (!g_Backend->SetBreakpoint( + "nt!NtDeviceIoControlFile", [](Backend_t *Backend) { + // + // The first time we hit this breakpoint, we + // grab the return address and we set a + // breakpoint there to finish the testcase. + // + + static bool SetExitBreakpoint = false; + if (!SetExitBreakpoint) { + const auto ReturnAddress = + Backend->VirtReadGva(Gva_t(Backend->Rsp())); + if (!Backend->SetBreakpoint( + ReturnAddress, [](Backend_t *Backend) { + // + // Ok we're done! + // + + DebugPrint("Hit return breakpoint!\n"); + Backend->Stop(Ok_t()); + g_LastBuffer = nullptr; + g_LastBufferSize = 0; + })) { + Print("Failed to set breakpoint on return\n"); + std::abort(); + } + + SetExitBreakpoint = true; + } + + // + // If we don't have a testcase, then things are in a broken state, + // so abort. + // + + if (!g_LastBuffer || g_LastBufferSize == 0) { + Print("Hit NtDeviceIoControlFile w/o a testcase..?\n"); + std::abort(); + } + + // + // If we're mutating the IoControlCode, then the first 4 bytes are + // that. + // + + constexpr uint32_t IoctlSizeIfPresent = + MutateIoctl ? sizeof(uint32_t) : 0; + + // + // We can only insert testcases that are smaller or equal to the + // current size; otherwise we'll corrupt memory. To work around + // this, we truncate it if it's larger. + // + + // + // __kernel_entry NTSTATUS + // NtDeviceIoControlFile( + // [in] HANDLE FileHandle, + // [in] HANDLE Event, + // [in] PIO_APC_ROUTINE ApcRoutine, + // [in] PVOID ApcContext, + // [out] PIO_STATUS_BLOCK IoStatusBlock, + // [in] ULONG IoControlCode, + // [in] PVOID InputBuffer, + // [in] ULONG InputBufferLength, + // [out] PVOID OutputBuffer, + // [in] ULONG OutputBufferLength + // ); + // + + const uint32_t TotalInputBufferSize = + g_LastBufferSize - IoctlSizeIfPresent; + const auto MutatedIoControlCodePtr = (uint32_t *)g_LastBuffer.get(); + const uint8_t *MutatedInputBufferPtr = + g_LastBuffer.get() + IoctlSizeIfPresent; + Gva_t InputBufferSizePtr; + const auto InputBufferSize = + uint32_t(Backend->GetArg(7, InputBufferSizePtr)); + const uint32_t MutatedInputBufferSize = + std::min(TotalInputBufferSize, InputBufferSize); + + // + // Fix up InputBufferLength. + // + + if (!Backend->VirtWriteStructDirty(InputBufferSizePtr, + &MutatedInputBufferSize)) { + Print("Failed to fix up the InputBufferSize\n"); + std::abort(); + } + + // + // Insert the testcase in the InputBuffer. + // XXX: Ideally, we'd push up the testcase to the + // boundary of the buffer to have a better chance to + // catch OOBs and update the InputBuffer pointer. + // + + if (!Backend->VirtWriteDirty(Backend->GetArgGva(8), + MutatedInputBufferPtr, + MutatedInputBufferSize)) { + Print("Failed to insert the testcase\n"); + std::abort(); + } + + // + // Are we mutating IoControlCode as well? + // + + if constexpr (MutateIoctl) { + const auto MutatedIoControlCode = *MutatedIoControlCodePtr; + Gva_t IoControlCodePtr; + Backend->GetArgGva(5, IoControlCodePtr); + if (!Backend->VirtWriteStructDirty(IoControlCodePtr, + &MutatedIoControlCode)) { + Print("Failed to VirtWriteStructDirty (Ioctl) failed\n"); + std::abort(); + } + } + })) { + Print("Failed to SetBreakpoint NtDeviceIoControlFile\n"); return false; } @@ -126,8 +210,8 @@ bool Init(const Options_t &Opts, const CpuState_t &) { // kd> ub fffff805`3b8287c4 l1 // nt!ExGenRandom+0xe0: // fffff805`3b8287c0 480fc7f2 rdrand rdx - const Gva_t ExGenRandom = Gva_t(g_Dbg.GetSymbol("nt!ExGenRandom") + 0xf3); - if (g_Backend->VirtRead4(ExGenRandom) != 0xb8f2c70f) { + const Gva_t ExGenRandom = Gva_t(g_Dbg.GetSymbol("nt!ExGenRandom") + 0xe0 + 4); + if (g_Backend->VirtRead4(ExGenRandom - Gva_t(4)) != 0xf2c70f48) { Print("It seems that nt!ExGenRandom's code has changed, update the " "offset!\n"); return false; @@ -174,7 +258,6 @@ bool Init(const Options_t &Opts, const CpuState_t &) { return false; } - return true; } From 7b49c6940fa64ca2fa866510891d02cf2822ad0d Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:45:05 -0800 Subject: [PATCH 3/9] Okay, fix CI; be more consistent w/ how other modules look --- src/wtf/fuzzer_ioctl.cc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index 2d41036..3c25045 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -8,12 +8,10 @@ namespace Ioctl { constexpr bool DebugLoggingOn = false; constexpr bool MutateIoctl = true; -#define Print(Fmt, ...) fmt::print("ioctl: " Fmt, __VA_ARGS__) - template void DebugPrint(const char *Format, const Args_t &...args) { if constexpr (DebugLoggingOn) { - fmt::print("ioctl: "); + fmt::print("Ioctl: "); fmt::print(fmt::runtime(Format), args...); } } @@ -31,7 +29,7 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { static bool FirstTime = true; if (!FirstTime) { if (!g_LastBuffer || g_LastBufferSize == 0) { - Print("The testcase hasn't been reset; something is wrong\n"); + fmt::print("The testcase hasn't been reset; something is wrong\n"); std::abort(); } } else { @@ -44,7 +42,7 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { if constexpr (MutateIoctl) { if (BufferSize < sizeof(uint32_t)) { - Print("The testcase buffer is too small: {} bytes\n", BufferSize); + fmt::print("The testcase buffer is too small: {} bytes\n", BufferSize); return false; } } @@ -89,7 +87,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { g_LastBuffer = nullptr; g_LastBufferSize = 0; })) { - Print("Failed to set breakpoint on return\n"); + fmt::print("Failed to set breakpoint on return\n"); std::abort(); } @@ -102,7 +100,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { // if (!g_LastBuffer || g_LastBufferSize == 0) { - Print("Hit NtDeviceIoControlFile w/o a testcase..?\n"); + fmt::print("Hit NtDeviceIoControlFile w/o a testcase..?\n"); std::abort(); } @@ -153,7 +151,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { if (!Backend->VirtWriteStructDirty(InputBufferSizePtr, &MutatedInputBufferSize)) { - Print("Failed to fix up the InputBufferSize\n"); + fmt::print("Failed to fix up the InputBufferSize\n"); std::abort(); } @@ -167,7 +165,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { if (!Backend->VirtWriteDirty(Backend->GetArgGva(8), MutatedInputBufferPtr, MutatedInputBufferSize)) { - Print("Failed to insert the testcase\n"); + fmt::print("Failed to insert the testcase\n"); std::abort(); } @@ -181,12 +179,12 @@ bool Init(const Options_t &Opts, const CpuState_t &) { Backend->GetArgGva(5, IoControlCodePtr); if (!Backend->VirtWriteStructDirty(IoControlCodePtr, &MutatedIoControlCode)) { - Print("Failed to VirtWriteStructDirty (Ioctl) failed\n"); + fmt::print("Failed to VirtWriteStructDirty (Ioctl) failed\n"); std::abort(); } } })) { - Print("Failed to SetBreakpoint NtDeviceIoControlFile\n"); + fmt::print("Failed to SetBreakpoint NtDeviceIoControlFile\n"); return false; } @@ -200,7 +198,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { DebugPrint("DbgPrintEx: {}", Format); Backend->SimulateReturnFromFunction(0); })) { - Print("Failed to SetBreakpoint DbgPrintEx\n"); + fmt::print("Failed to SetBreakpoint DbgPrintEx\n"); return false; } @@ -212,8 +210,8 @@ bool Init(const Options_t &Opts, const CpuState_t &) { // fffff805`3b8287c0 480fc7f2 rdrand rdx const Gva_t ExGenRandom = Gva_t(g_Dbg.GetSymbol("nt!ExGenRandom") + 0xe0 + 4); if (g_Backend->VirtRead4(ExGenRandom - Gva_t(4)) != 0xf2c70f48) { - Print("It seems that nt!ExGenRandom's code has changed, update the " - "offset!\n"); + fmt::print("It seems that nt!ExGenRandom's code has changed, update the " + "offset!\n"); return false; } @@ -221,7 +219,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { DebugPrint("Hit ExGenRandom!\n"); Backend->Rdx(Backend->Rdrand()); })) { - Print("Failed to SetBreakpoint ExGenRandom\n"); + fmt::print("Failed to SetBreakpoint ExGenRandom\n"); return false; } @@ -242,7 +240,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { DebugPrint("KeBugCheck2: {}\n", Filename); Backend->Stop(Crash_t(Filename)); })) { - Print("Failed to SetBreakpoint KeBugCheck2\n"); + fmt::print("Failed to SetBreakpoint KeBugCheck2\n"); return false; } @@ -254,7 +252,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { DebugPrint("nt!SwapContext\n"); Backend->Stop(Cr3Change_t()); })) { - Print("Failed to SetBreakpoint SwapContext\n"); + fmt::print("Failed to SetBreakpoint SwapContext\n"); return false; } From 59f4a62c0dc1cbba5a4bec9883fbd1e921f41b56 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Thu, 30 Mar 2023 21:05:29 -0700 Subject: [PATCH 4/9] revert --- src/wtf/fuzzer_ioctl.cc | 222 +++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 118 deletions(-) diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index 3c25045..e0594e6 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -3,10 +3,16 @@ #include "targets.h" #include +// +// This fuzzing module expects a snapshot made at nt!NtDeviceIoControlFile. +// It is recommended to grab a snapshot with the biggest InputBufferLength +// possible. +// + namespace Ioctl { -constexpr bool DebugLoggingOn = false; -constexpr bool MutateIoctl = true; +constexpr bool DebugLoggingOn = true; +constexpr bool MutateIoctl = false; template void DebugPrint(const char *Format, const Args_t &...args) { @@ -16,26 +22,8 @@ void DebugPrint(const char *Format, const Args_t &...args) { } } -std::unique_ptr g_LastBuffer; -size_t g_LastBufferSize = 0; - bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { - // - // If we have a testcase still alive, it means we haven't hit the return - // breakpoint which is suspicious. - // - - static bool FirstTime = true; - if (!FirstTime) { - if (!g_LastBuffer || g_LastBufferSize == 0) { - fmt::print("The testcase hasn't been reset; something is wrong\n"); - std::abort(); - } - } else { - FirstTime = false; - } - // // If we are mutating the IoControlCode, we expect at least 4 bytes. // @@ -48,12 +36,104 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { } // - // Copy the testcase; we'll inject it later if we hit NtDeviceIoControlFile. + // If we're mutating the IoControlCode, then the first 4 bytes are + // that. + // + + constexpr uint32_t IoctlSizeIfPresent = MutateIoctl ? sizeof(uint32_t) : 0; + + // + // We can only insert testcases that are smaller or equal to the + // current size; otherwise we'll corrupt memory. To work around + // this, we truncate it if it's larger. + // We also modify the InputBuffer pointer to push it as close as possible from + // the end of the buffer. + // + + // + // __kernel_entry NTSTATUS + // NtDeviceIoControlFile( + // [in] HANDLE FileHandle, + // [in] HANDLE Event, + // [in] PIO_APC_ROUTINE ApcRoutine, + // [in] PVOID ApcContext, + // [out] PIO_STATUS_BLOCK IoStatusBlock, + // [in] ULONG IoControlCode, + // [in] PVOID InputBuffer, + // [in] ULONG InputBufferLength, + // [out] PVOID OutputBuffer, + // [in] ULONG OutputBufferLength + // ); + // + + const uint32_t TotalInputBufferSize = BufferSize - IoctlSizeIfPresent; + const auto MutatedIoControlCodePtr = (uint32_t *)Buffer; + const uint8_t *MutatedInputBufferPtr = Buffer + IoctlSizeIfPresent; + + // + // Calculate the maximum size we can inject into the target. Either we can + // inject it all, or we need to truncate it. + // + + Gva_t InputBufferSizePtr; + const auto InputBufferSize = + uint32_t(g_Backend->GetArg(7, InputBufferSizePtr)); + const uint32_t MutatedInputBufferSize = + std::min(TotalInputBufferSize, InputBufferSize); + + // + // Calculate the new InputBuffer address by pushing the mutated buffer as + // close as possible from its end. + // + + Gva_t InputBufferPtr; + const auto NewInputBuffer = Gva_t(g_Backend->GetArg(6, InputBufferPtr) + + InputBufferSize - MutatedInputBufferSize); + + // + // Fix up InputBufferLength. + // + + if (!g_Backend->VirtWriteStructDirty(InputBufferSizePtr, + &MutatedInputBufferSize)) { + fmt::print("Failed to fix up the InputBufferSize\n"); + std::abort(); + } + + // + // Fix up InputBuffer. + // + + if (!g_Backend->VirtWriteStructDirty(InputBufferPtr, &NewInputBuffer)) { + fmt::print("Failed to fix up the InputBuffer\n"); + std::abort(); + } + + // + // Insert the testcase at the new InputBuffer. + // + + if (!g_Backend->VirtWriteDirty(NewInputBuffer, MutatedInputBufferPtr, + MutatedInputBufferSize)) { + fmt::print("Failed to insert the testcase\n"); + std::abort(); + } + + // + // Are we mutating IoControlCode as well? // - g_LastBufferSize = BufferSize; - g_LastBuffer = std::make_unique(BufferSize); - std::memcpy(g_LastBuffer.get(), Buffer, BufferSize); + if constexpr (MutateIoctl) { + const auto MutatedIoControlCode = *MutatedIoControlCodePtr; + Gva_t IoControlCodePtr; + g_Backend->GetArgGva(5, IoControlCodePtr); + if (!g_Backend->VirtWriteStructDirty(IoControlCodePtr, + &MutatedIoControlCode)) { + fmt::print("Failed to VirtWriteStructDirty (Ioctl) failed\n"); + std::abort(); + } + } + return true; } @@ -84,104 +164,10 @@ bool Init(const Options_t &Opts, const CpuState_t &) { DebugPrint("Hit return breakpoint!\n"); Backend->Stop(Ok_t()); - g_LastBuffer = nullptr; - g_LastBufferSize = 0; })) { fmt::print("Failed to set breakpoint on return\n"); std::abort(); } - - SetExitBreakpoint = true; - } - - // - // If we don't have a testcase, then things are in a broken state, - // so abort. - // - - if (!g_LastBuffer || g_LastBufferSize == 0) { - fmt::print("Hit NtDeviceIoControlFile w/o a testcase..?\n"); - std::abort(); - } - - // - // If we're mutating the IoControlCode, then the first 4 bytes are - // that. - // - - constexpr uint32_t IoctlSizeIfPresent = - MutateIoctl ? sizeof(uint32_t) : 0; - - // - // We can only insert testcases that are smaller or equal to the - // current size; otherwise we'll corrupt memory. To work around - // this, we truncate it if it's larger. - // - - // - // __kernel_entry NTSTATUS - // NtDeviceIoControlFile( - // [in] HANDLE FileHandle, - // [in] HANDLE Event, - // [in] PIO_APC_ROUTINE ApcRoutine, - // [in] PVOID ApcContext, - // [out] PIO_STATUS_BLOCK IoStatusBlock, - // [in] ULONG IoControlCode, - // [in] PVOID InputBuffer, - // [in] ULONG InputBufferLength, - // [out] PVOID OutputBuffer, - // [in] ULONG OutputBufferLength - // ); - // - - const uint32_t TotalInputBufferSize = - g_LastBufferSize - IoctlSizeIfPresent; - const auto MutatedIoControlCodePtr = (uint32_t *)g_LastBuffer.get(); - const uint8_t *MutatedInputBufferPtr = - g_LastBuffer.get() + IoctlSizeIfPresent; - Gva_t InputBufferSizePtr; - const auto InputBufferSize = - uint32_t(Backend->GetArg(7, InputBufferSizePtr)); - const uint32_t MutatedInputBufferSize = - std::min(TotalInputBufferSize, InputBufferSize); - - // - // Fix up InputBufferLength. - // - - if (!Backend->VirtWriteStructDirty(InputBufferSizePtr, - &MutatedInputBufferSize)) { - fmt::print("Failed to fix up the InputBufferSize\n"); - std::abort(); - } - - // - // Insert the testcase in the InputBuffer. - // XXX: Ideally, we'd push up the testcase to the - // boundary of the buffer to have a better chance to - // catch OOBs and update the InputBuffer pointer. - // - - if (!Backend->VirtWriteDirty(Backend->GetArgGva(8), - MutatedInputBufferPtr, - MutatedInputBufferSize)) { - fmt::print("Failed to insert the testcase\n"); - std::abort(); - } - - // - // Are we mutating IoControlCode as well? - // - - if constexpr (MutateIoctl) { - const auto MutatedIoControlCode = *MutatedIoControlCodePtr; - Gva_t IoControlCodePtr; - Backend->GetArgGva(5, IoControlCodePtr); - if (!Backend->VirtWriteStructDirty(IoControlCodePtr, - &MutatedIoControlCode)) { - fmt::print("Failed to VirtWriteStructDirty (Ioctl) failed\n"); - std::abort(); - } } })) { fmt::print("Failed to SetBreakpoint NtDeviceIoControlFile\n"); From 2ad43e5d09c9970116af3f0079182c2b45e2f582 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Mon, 8 May 2023 16:41:07 -0700 Subject: [PATCH 5/9] make the hevd client able to deliver testcases to make it easier to debug stuff --- src/hevd_client/hevd_client.cc | 67 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/src/hevd_client/hevd_client.cc b/src/hevd_client/hevd_client.cc index 2f69f63..1b7b6b0 100644 --- a/src/hevd_client/hevd_client.cc +++ b/src/hevd_client/hevd_client.cc @@ -4,7 +4,7 @@ #include #include -int main() { +int main(int argc, char *argv[]) { HANDLE H = CreateFileA(R"(\\.\GLOBALROOT\Device\HackSysExtremeVulnerableDriver)", GENERIC_ALL, 0, nullptr, OPEN_EXISTING, 0, nullptr); @@ -13,14 +13,73 @@ int main() { return EXIT_FAILURE; } - std::array Buffer; + std::array BufferBacking = {}; if (getenv("BREAK") != nullptr) { __debugbreak(); } DWORD Returned = 0; - DeviceIoControl(H, 0xdeadbeef, Buffer.data(), Buffer.size(), Buffer.data(), - Buffer.size(), &Returned, nullptr); + DWORD IoctlCode = 0xdeadbeef; + PVOID Buffer = BufferBacking.data(); + size_t BufferSize = BufferBacking.size(); + if (argc > 1) { + FILE *File = fopen(argv[1], "rb"); + if (File == nullptr) { + printf("fopen failed, bailing.\n"); + return EXIT_FAILURE; + } + + if (fseek(File, 0, SEEK_END) != 0) { + printf("fseek to the end failed, bailing.\n"); + fclose(File); + return EXIT_FAILURE; + } + + const long R = ftell(File); + if (R == -1 || R < 0) { + printf("ftell failed, bailing.\n"); + fclose(File); + return EXIT_FAILURE; + } + + const auto TotalSize = uint32_t(R); + if (fseek(File, 0, SEEK_SET) != 0) { + printf("fseek back to the beginning failed, bailing.\n"); + fclose(File); + return EXIT_FAILURE; + } + + if (fread(&IoctlCode, sizeof(IoctlCode), 1, File) != 1) { + printf("fread failed when reading ioctl code, bailing.\n"); + fclose(File); + return EXIT_FAILURE; + } + + BufferSize = TotalSize - sizeof(uint32_t); + Buffer = calloc(BufferSize, 1); + if (Buffer == nullptr) { + printf("calloc failed, bailing.\n"); + fclose(File); + return EXIT_FAILURE; + } + + if (fread(Buffer, BufferSize, 1, File) != 1) { + printf("fread failed when reading buffer, bailing.\n"); + fclose(File); + free(Buffer); + return EXIT_FAILURE; + } + + fclose(File); + } + + DeviceIoControl(H, IoctlCode, Buffer, BufferSize, Buffer, BufferSize, + &Returned, nullptr); CloseHandle(H); + + if (Buffer != BufferBacking.data()) { + free(Buffer); + } + return EXIT_SUCCESS; } \ No newline at end of file From fbbf23e81d4d98e4f3ef732a1ba9ee27bed75707 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Mon, 8 May 2023 16:42:43 -0700 Subject: [PATCH 6/9] use the new getarggva api in hevd --- src/wtf/fuzzer_hevd.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wtf/fuzzer_hevd.cc b/src/wtf/fuzzer_hevd.cc index 44762e1..369b4ca 100644 --- a/src/wtf/fuzzer_hevd.cc +++ b/src/wtf/fuzzer_hevd.cc @@ -49,8 +49,8 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { } g_Backend->R9(IoctlBufferSize); - const Gva_t Rsp = Gva_t(g_Backend->Rsp()); - const Gva_t OutBufferSizePtr = Rsp + Gva_t(5 * sizeof(uint64_t)); + Gva_t OutBufferSizePtr; + g_Backend->GetArgGva(5, OutBufferSizePtr); if (!g_Backend->VirtWriteStructDirty(OutBufferSizePtr, &IoctlBufferSize)) { DebugPrint("VirtWriteStructDirty failed\n"); return false; From 67dc5fb6d4ac7084055b186023fc0f652df9ba30 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Mon, 8 May 2023 16:43:43 -0700 Subject: [PATCH 7/9] fix bugs --- src/wtf/fuzzer_ioctl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index e0594e6..a8f9e10 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -30,8 +30,7 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { if constexpr (MutateIoctl) { if (BufferSize < sizeof(uint32_t)) { - fmt::print("The testcase buffer is too small: {} bytes\n", BufferSize); - return false; + return true; } } @@ -154,6 +153,7 @@ bool Init(const Options_t &Opts, const CpuState_t &) { static bool SetExitBreakpoint = false; if (!SetExitBreakpoint) { + SetExitBreakpoint = true; const auto ReturnAddress = Backend->VirtReadGva(Gva_t(Backend->Rsp())); if (!Backend->SetBreakpoint( From 906cb4dcd03428f0edbbb81b2b5984522ce2bcb8 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Mon, 8 May 2023 17:29:08 -0700 Subject: [PATCH 8/9] Configure the settings right --- src/wtf/fuzzer_ioctl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index a8f9e10..00c5979 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -11,8 +11,8 @@ namespace Ioctl { -constexpr bool DebugLoggingOn = true; -constexpr bool MutateIoctl = false; +constexpr bool DebugLoggingOn = false; +constexpr bool MutateIoctl = true; template void DebugPrint(const char *Format, const Args_t &...args) { From b8a7232057d0b004fbd592ad631e9f54ea024368 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Tue, 9 May 2023 08:50:42 -0700 Subject: [PATCH 9/9] break down getarg / getargaddress in two & nodiscard a bunch of stuff --- src/wtf/backend.cc | 29 ++++++++++------- src/wtf/backend.h | 70 +++++++++++++++++++++++------------------ src/wtf/fuzzer_hevd.cc | 3 +- src/wtf/fuzzer_ioctl.cc | 16 +++++----- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/src/wtf/backend.cc b/src/wtf/backend.cc index 246b619..e133db3 100644 --- a/src/wtf/backend.cc +++ b/src/wtf/backend.cc @@ -165,7 +165,17 @@ bool Backend_t::SimulateReturnFrom32bitFunction( return true; } -uint64_t Backend_t::GetArg(const uint64_t Idx, Gva_t &Address) { +Gva_t Backend_t::GetArgAddress(const uint64_t Idx) { + if (Idx <= 3) { + fmt::print("The first four arguments are stored in registers (@rcx, @rdx, " + "@r8, @r9) which means you cannot get their addresses.\n"); + std::abort(); + } + + return Gva_t(Rsp() + (8 + (Idx * 8))); +} + +uint64_t Backend_t::GetArg(const uint64_t Idx) { switch (Idx) { case 0: return Rcx(); @@ -176,24 +186,19 @@ uint64_t Backend_t::GetArg(const uint64_t Idx, Gva_t &Address) { case 3: return R9(); default: { - Address = Gva_t(Rsp() + (8 + (Idx * 8))); - return VirtRead8(Address); + return VirtRead8(GetArgAddress(Idx)); } } } -uint64_t Backend_t::GetArg(const uint64_t Idx) { - Gva_t Dummy; - return GetArg(Idx, Dummy); -} +Gva_t Backend_t::GetArgGva(const uint64_t Idx) { return Gva_t(GetArg(Idx)); } -Gva_t Backend_t::GetArgGva(const uint64_t Idx, Gva_t &Address) { - return Gva_t(GetArg(Idx, Address)); +std::pair Backend_t::GetArgAndAddress(const uint64_t Idx) { + return {GetArg(Idx), GetArgAddress(Idx)}; } -Gva_t Backend_t::GetArgGva(const uint64_t Idx) { - Gva_t Dummy; - return Gva_t(GetArg(Idx, Dummy)); +std::pair Backend_t::GetArgAndAddressGva(const uint64_t Idx) { + return {GetArgGva(Idx), GetArgAddress(Idx)}; } bool Backend_t::SaveCrash(const Gva_t ExceptionAddress, diff --git a/src/wtf/backend.h b/src/wtf/backend.h index 926a7f2..db4376b 100644 --- a/src/wtf/backend.h +++ b/src/wtf/backend.h @@ -322,17 +322,17 @@ class Backend_t { // Read a uint64_t. // - uint64_t VirtRead8(const Gva_t Gva) const; - Gva_t VirtReadGva(const Gva_t Gva) const; - Gpa_t VirtReadGpa(const Gva_t Gva) const; + [[nodiscard]] uint64_t VirtRead8(const Gva_t Gva) const; + [[nodiscard]] Gva_t VirtReadGva(const Gva_t Gva) const; + [[nodiscard]] Gpa_t VirtReadGpa(const Gva_t Gva) const; // // Read a basic string. // template - std::basic_string<_Ty> VirtReadBasicString(const Gva_t StringGva, - const uint64_t MaxLength) const { + [[nodiscard]] std::basic_string<_Ty> + VirtReadBasicString(const Gva_t StringGva, const uint64_t MaxLength) const { using BasicString_t = std::basic_string<_Ty>; using ValueType_t = typename BasicString_t::value_type; @@ -432,15 +432,15 @@ class Backend_t { // Read a basic_string. // - std::string VirtReadString(const Gva_t Gva, - const uint64_t MaxLength = 256) const; + [[nodiscard]] std::string + VirtReadString(const Gva_t Gva, const uint64_t MaxLength = 256) const; // // Read a basic_string (used to read wchar_t* in Windows guests). // - std::u16string VirtReadWideString(const Gva_t Gva, - const uint64_t MaxLength = 256) const; + [[nodiscard]] std::u16string + VirtReadWideString(const Gva_t Gva, const uint64_t MaxLength = 256) const; // // Write in virtual memory. Optionally track dirtiness on the memory range. @@ -489,80 +489,90 @@ class Backend_t { // calling convention. // - uint64_t GetArg(const uint64_t Idx, Gva_t &Address); - uint64_t GetArg(const uint64_t Idx); - Gva_t GetArgGva(const uint64_t Idx, Gva_t &Address); - Gva_t GetArgGva(const uint64_t Idx); + [[nodiscard]] uint64_t GetArg(const uint64_t Idx); + [[nodiscard]] Gva_t GetArgGva(const uint64_t Idx); + + // + // Utility function to get the address of a function argument. Oftentimes, you + // need to overwrite an argument that isn't stored in registers which means + // you need to calculate yourself where it is stored on the stack. This + // function gives you its address. + // + + [[nodiscard]] Gva_t GetArgAddress(const uint64_t Idx); + [[nodiscard]] std::pair GetArgAndAddress(const uint64_t Idx); + [[nodiscard]] std::pair GetArgAndAddressGva(const uint64_t Idx); + // // Shortcuts to grab / set some registers. // - uint64_t Rsp(); + [[nodiscard]] uint64_t Rsp(); void Rsp(const uint64_t Value); void Rsp(const Gva_t Value); - uint64_t Rbp(); + [[nodiscard]] uint64_t Rbp(); void Rbp(const uint64_t Value); void Rbp(const Gva_t Value); - uint64_t Rip(); + [[nodiscard]] uint64_t Rip(); void Rip(const uint64_t Value); void Rip(const Gva_t Value); - uint64_t Rax(); + [[nodiscard]] uint64_t Rax(); void Rax(const uint64_t Value); void Rax(const Gva_t Value); - uint64_t Rbx(); + [[nodiscard]] uint64_t Rbx(); void Rbx(const uint64_t Value); void Rbx(const Gva_t Value); - uint64_t Rcx(); + [[nodiscard]] uint64_t Rcx(); void Rcx(const uint64_t Value); void Rcx(const Gva_t Value); - uint64_t Rdx(); + [[nodiscard]] uint64_t Rdx(); void Rdx(const uint64_t Value); void Rdx(const Gva_t Value); - uint64_t Rsi(); + [[nodiscard]] uint64_t Rsi(); void Rsi(const uint64_t Value); void Rsi(const Gva_t Value); - uint64_t Rdi(); + [[nodiscard]] uint64_t Rdi(); void Rdi(const uint64_t Value); void Rdi(const Gva_t Value); - uint64_t R8(); + [[nodiscard]] uint64_t R8(); void R8(const uint64_t Value); void R8(const Gva_t Value); - uint64_t R9(); + [[nodiscard]] uint64_t R9(); void R9(const uint64_t Value); void R9(const Gva_t Value); - uint64_t R10(); + [[nodiscard]] uint64_t R10(); void R10(const uint64_t Value); void R10(const Gva_t Value); - uint64_t R11(); + [[nodiscard]] uint64_t R11(); void R11(const uint64_t Value); void R11(const Gva_t Value); - uint64_t R12(); + [[nodiscard]] uint64_t R12(); void R12(const uint64_t Value); void R12(const Gva_t Value); - uint64_t R13(); + [[nodiscard]] uint64_t R13(); void R13(const uint64_t Value); void R13(const Gva_t Value); - uint64_t R14(); + [[nodiscard]] uint64_t R14(); void R14(const uint64_t Value); void R14(const Gva_t Value); - uint64_t R15(); + [[nodiscard]] uint64_t R15(); void R15(const uint64_t Value); void R15(const Gva_t Value); diff --git a/src/wtf/fuzzer_hevd.cc b/src/wtf/fuzzer_hevd.cc index 369b4ca..5b8de0c 100644 --- a/src/wtf/fuzzer_hevd.cc +++ b/src/wtf/fuzzer_hevd.cc @@ -49,8 +49,7 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { } g_Backend->R9(IoctlBufferSize); - Gva_t OutBufferSizePtr; - g_Backend->GetArgGva(5, OutBufferSizePtr); + const auto &OutBufferSizePtr = g_Backend->GetArgAddress(5); if (!g_Backend->VirtWriteStructDirty(OutBufferSizePtr, &IoctlBufferSize)) { DebugPrint("VirtWriteStructDirty failed\n"); return false; diff --git a/src/wtf/fuzzer_ioctl.cc b/src/wtf/fuzzer_ioctl.cc index 00c5979..4d32fa9 100644 --- a/src/wtf/fuzzer_ioctl.cc +++ b/src/wtf/fuzzer_ioctl.cc @@ -74,20 +74,19 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { // inject it all, or we need to truncate it. // - Gva_t InputBufferSizePtr; - const auto InputBufferSize = - uint32_t(g_Backend->GetArg(7, InputBufferSizePtr)); + const auto &[InputBufferSize, InputBufferSizePtr] = + g_Backend->GetArgAndAddress(7); const uint32_t MutatedInputBufferSize = - std::min(TotalInputBufferSize, InputBufferSize); + std::min(TotalInputBufferSize, uint32_t(InputBufferSize)); // // Calculate the new InputBuffer address by pushing the mutated buffer as // close as possible from its end. // - Gva_t InputBufferPtr; - const auto NewInputBuffer = Gva_t(g_Backend->GetArg(6, InputBufferPtr) + - InputBufferSize - MutatedInputBufferSize); + const auto &[InputBuffer, InputBufferPtr] = g_Backend->GetArgAndAddress(6); + const auto NewInputBuffer = + Gva_t(InputBuffer + InputBufferSize - MutatedInputBufferSize); // // Fix up InputBufferLength. @@ -124,8 +123,7 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) { if constexpr (MutateIoctl) { const auto MutatedIoControlCode = *MutatedIoControlCodePtr; - Gva_t IoControlCodePtr; - g_Backend->GetArgGva(5, IoControlCodePtr); + const auto &IoControlCodePtr = g_Backend->GetArgAddress(5); if (!g_Backend->VirtWriteStructDirty(IoControlCodePtr, &MutatedIoControlCode)) { fmt::print("Failed to VirtWriteStructDirty (Ioctl) failed\n");