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

add "ioctl" target #155

Merged
merged 9 commits into from
May 9, 2023
Merged
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
*.out
*.app

.vs
src/wtf/fuzzer_*
src/build/
src/build_msvc/
targets/
src/out
targets/
67 changes: 63 additions & 4 deletions src/hevd_client/hevd_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <array>
#include <cstdio>

int main() {
int main(int argc, char *argv[]) {
HANDLE H =
CreateFileA(R"(\\.\GLOBALROOT\Device\HackSysExtremeVulnerableDriver)",
GENERIC_ALL, 0, nullptr, OPEN_EXISTING, 0, nullptr);
Expand All @@ -13,14 +13,73 @@ int main() {
return EXIT_FAILURE;
}

std::array<uint8_t, 1024> Buffer;
std::array<uint8_t, 1024> 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;
}
24 changes: 21 additions & 3 deletions src/wtf/backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand All @@ -164,6 +165,16 @@ bool Backend_t::SimulateReturnFrom32bitFunction(const uint32_t Return, const uin
return true;
}

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:
Expand All @@ -175,14 +186,21 @@ 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);
return VirtRead8(GetArgAddress(Idx));
}
}
}

Gva_t Backend_t::GetArgGva(const uint64_t Idx) { return Gva_t(GetArg(Idx)); }

std::pair<uint64_t, Gva_t> Backend_t::GetArgAndAddress(const uint64_t Idx) {
return {GetArg(Idx), GetArgAddress(Idx)};
}

std::pair<Gva_t, Gva_t> Backend_t::GetArgAndAddressGva(const uint64_t Idx) {
return {GetArgGva(Idx), GetArgAddress(Idx)};
}

bool Backend_t::SaveCrash(const Gva_t ExceptionAddress,
const uint32_t ExceptionCode) {
const auto ExceptionCodeStr = ExceptionCodeToStr(ExceptionCode);
Expand Down
71 changes: 42 additions & 29 deletions src/wtf/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename _Ty>
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;

Expand Down Expand Up @@ -432,15 +432,15 @@ class Backend_t {
// Read a basic_string<char>.
//

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<char16_t> (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.
Expand Down Expand Up @@ -481,85 +481,98 @@ 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 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<uint64_t, Gva_t> GetArgAndAddress(const uint64_t Idx);
[[nodiscard]] std::pair<Gva_t, Gva_t> 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);

Expand Down
3 changes: 1 addition & 2 deletions src/wtf/fuzzer_hevd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ 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));
const auto &OutBufferSizePtr = g_Backend->GetArgAddress(5);
if (!g_Backend->VirtWriteStructDirty(OutBufferSizePtr, &IoctlBufferSize)) {
DebugPrint("VirtWriteStructDirty failed\n");
return false;
Expand Down
Loading