#pragma once
#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
#include <stdio.h>
#include <string_view>
#include <iostream>
#include <chrono>

#ifndef DRIVER_HPP
#define DRIVER_HPP
#define device_name "\\\\.\\MsWinZonesSecurityCache"

constexpr auto mm_copy_memory_physical = 0x1;
constexpr auto mm_copy_memory_virtual = 0x2;

struct IO_STATUS_BLOCK {
	union {
		NTSTATUS Status;
		PVOID Pointer;
	};
	ULONG_PTR Information;
};

using PIO_STATUS_BLOCK = IO_STATUS_BLOCK*;
using PIO_APC_ROUTINE = void(NTAPI*)(
	PVOID,
	PIO_STATUS_BLOCK,
	std::uint32_t);

extern "C" __int64 direct_device_control(
	HANDLE FileHandle,
	HANDLE Event,
	PIO_APC_ROUTINE ApcRoutine,
	PVOID ApcContext,
	PIO_STATUS_BLOCK IoStatusBlock,
	std::uint32_t IoControlCode,
	PVOID InputBuffer,
	std::uint32_t InputBufferLength,
	PVOID OutputBuffer,
	std::uint32_t OutputBufferLength);

namespace driver
{
	typedef enum _requests
	{
		invoke_start,
		invoke_base,
		invoke_read,
		invoke_write,
		invoke_success,
		invoke_unique,
		invoke_translate,
		invoke_read_kernel,
		invoke_dtb,
		invoke_hide_process,
	}requests, * prequests;

	class communicate_t
	{
	private:
		typedef struct _read_invoke {
			uint32_t pid;
			uintptr_t address;
			uintptr_t dtb;
			void* buffer;
			size_t size;
		} read_invoke, * pread_invoke;

		typedef struct _write_invoke {
			uint32_t pid;
			uintptr_t directory_table;
			uintptr_t address;
			void* buffer;
			size_t size;
		} write_invoke, * pwrite_invoke;

		typedef struct _base_invoke {
			uint32_t pid;
			uintptr_t handle;
			const char* name;
			size_t size;
		} base_invoke, * pbase_invoke;

		typedef struct _allocate_invoke {
			uintptr_t address;
			SIZE_T dwSize;
			DWORD flAllocationType;
		} allocate_invoke, * palloc_invoke;

		typedef struct _read_kernel_invoke {
			uintptr_t address;
			void* buffer;
			size_t size;
			uint32_t memory_type;
		} read_kernel_invoke, * pread_kernel_invoke;

		typedef struct _translate_invoke {
			uintptr_t virtual_address;
			uintptr_t directory_base;
			void* physical_address;
		} translate_invoke, * ptranslate_invoke;

		typedef struct _dtb_invoke {
			uint32_t pid;
			uintptr_t dtb;
		} dtb_invoke, * pdtb_invoke;

		typedef struct _hide_process_invoke {
			uint32_t process_id;
		} hide_process_invoke, * phide_process_invoke;

		typedef struct _invoke_data
		{
			uint32_t unique;
			requests code;
			void* data;
		}invoke_data, * pinvoke_data;

		std::int32_t m_pid = 0;
		void* m_handle = 0;

	public:
		std::uintptr_t BaseAddress = 0;
		std::uintptr_t dtb = 0;

		[[nodiscard]] bool send_cmd(void* data, requests code);
		[[nodiscard]] bool initialize_handle();
		[[nodiscard]] bool attach(int a_pid);
		[[nodiscard]] const std::uint32_t get_process_pid(const std::wstring& proc_name);
		[[nodiscard]] const std::uintptr_t get_dtb(std::uint32_t pid);
		[[nodiscard]] const std::uintptr_t get_image_base(const char* module_name);
		[[nodiscard]] const std::uintptr_t translate_address(std::uintptr_t virtual_address, std::uintptr_t directory_base);

		[[nodiscard]] bool read_kernel(const uintptr_t address, void* buffer, const size_t size, const uint32_t memory_type);
		[[nodiscard]] bool read(const uintptr_t address, void* buffer, const size_t size);
		[[nodiscard]] bool write(const uintptr_t address, void* buffer, const size_t size);

		[[nodiscard]] bool hide_process(uint32_t pid);

		template <typename t>
		[[nodiscard]] auto write_t(const uintptr_t address, t value) -> bool
		{
			return write(address, &value, sizeof(t));
		}

		template <typename t>
		[[nodiscard]] auto read_t(const uintptr_t address) -> t
		{
			t response{};
			read(address, &response, sizeof(t));
			return response;
		}

		template <typename t>
		[[nodiscard]] auto read_array(const uintptr_t address, t buffer, size_t size) -> bool
		{
			return read(address, buffer, size);
		}

		[[nodiscard]] bool is_valid(uint64_t address) const {
			return (address > 0x400000 &&
				address < 0x7FFFFFFFFFFFFFFF &&
				address != 0xCCCCCCCCCCCCCCCC &&
				address != 0 &&
				reinterpret_cast<void*>(address) != nullptr);
		}
	};
}

static driver::communicate_t* kernel = new driver::communicate_t();

inline bool is_valid(const uint64_t address) {
	if (address <= 0x400000 || address == 0xCCCCCCCCCCCCCCCC || reinterpret_cast<void*>(address) == nullptr || address > 0x7FFFFFFFFFFFFFFF) {
		return false;
	}
	return true;
}

#endif