diff --git a/README.md b/README.md
index fca2382..da1b5e8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+# kdstinker
-# unfairgame
+Driver dumper for manually mapped drivers mapped with Kdmapper. Kdmapper is used by thousands of cheats to map unsigned code into the kernel. This tool was created to easily intercept and dump to disk manually mapped drivers for analysis.
-All modules have been sent to both EAC and BattlEye, I am choosing not to release valorant modules.
+
\ No newline at end of file
diff --git a/unfairgame-dll/crt.cpp b/unfairgame-dll/crt.cpp
new file mode 100644
index 0000000..3f063f6
--- /dev/null
+++ b/unfairgame-dll/crt.cpp
@@ -0,0 +1,109 @@
+#include "crt.h"
+
+int strcmp(const char* p1, const char* p2)
+{
+ const unsigned char* s1 = (const unsigned char*)p1;
+ const unsigned char* s2 = (const unsigned char*)p2;
+ unsigned char c1, c2;
+ do
+ {
+ c1 = (unsigned char)*s1++;
+ c2 = (unsigned char)*s2++;
+ if (c1 == '\0')
+ return c1 - c2;
+ } while (c1 == c2);
+ return c1 - c2;
+}
+
+size_t strlen(const char* str)
+{
+ const char* char_ptr;
+ const unsigned long int* longword_ptr;
+ unsigned long int longword, himagic, lomagic;
+ /* Handle the first few characters by reading one character at a time.
+ Do this until CHAR_PTR is aligned on a longword boundary. */
+ for (char_ptr = str; ((unsigned long int) char_ptr
+ & (sizeof(longword) - 1)) != 0;
+ ++char_ptr)
+ if (*char_ptr == '\0')
+ return char_ptr - str;
+ /* All these elucidatory comments refer to 4-byte longwords,
+ but the theory applies equally well to 8-byte longwords. */
+ longword_ptr = (unsigned long int*) char_ptr;
+ /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits
+ the "holes." Note that there is a hole just to the left of
+ each byte, with an extra at the end:
+ bits: 01111110 11111110 11111110 11111111
+ bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
+ The 1-bits make sure that carries propagate to the next 0-bit.
+ The 0-bits provide holes for carries to fall into. */
+ himagic = 0x80808080L;
+ lomagic = 0x01010101L;
+ if (sizeof(longword) > 4)
+ {
+ /* 64-bit version of the magic. */
+ /* Do the shift in two steps to avoid a warning if long has 32 bits. */
+ himagic = ((himagic << 16) << 16) | himagic;
+ lomagic = ((lomagic << 16) << 16) | lomagic;
+ }
+ if (sizeof(longword) > 8)
+ abort();
+ /* Instead of the traditional loop which tests each character,
+ we will test a longword at a time. The tricky part is testing
+ if *any of the four* bytes in the longword in question are zero. */
+ for (;;)
+ {
+ longword = *longword_ptr++;
+ if (((longword - lomagic) & ~longword & himagic) != 0)
+ {
+ /* Which of the bytes was the zero? If none of them were, it was
+ a misfire; continue the search. */
+ const char* cp = (const char*)(longword_ptr - 1);
+ if (cp[0] == 0)
+ return cp - str;
+ if (cp[1] == 0)
+ return cp - str + 1;
+ if (cp[2] == 0)
+ return cp - str + 2;
+ if (cp[3] == 0)
+ return cp - str + 3;
+ if (sizeof(longword) > 4)
+ {
+ if (cp[4] == 0)
+ return cp - str + 4;
+ if (cp[5] == 0)
+ return cp - str + 5;
+ if (cp[6] == 0)
+ return cp - str + 6;
+ if (cp[7] == 0)
+ return cp - str + 7;
+ }
+ }
+ }
+}
+
+void* operator new(std::size_t size)
+{
+ return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+}
+
+void operator delete(void* addr, std::size_t size)
+{
+ VirtualFree(addr, size, NULL);
+}
+
+size_t wcslen(const wchar_t* s)
+{
+ size_t len = 0;
+ while (s[len] != L'\0')
+ {
+ if (s[++len] == L'\0')
+ return len;
+ if (s[++len] == L'\0')
+ return len;
+ if (s[++len] == L'\0')
+ return len;
+ ++len;
+ }
+ return len;
+}
\ No newline at end of file
diff --git a/unfairgame-dll/crt.h b/unfairgame-dll/crt.h
new file mode 100644
index 0000000..81ba851
--- /dev/null
+++ b/unfairgame-dll/crt.h
@@ -0,0 +1,10 @@
+#pragma once
+#include
+#include
+
+int strcmp(const char* p1, const char* p2);
+size_t strlen(const char* str);
+
+void* operator new(std::size_t size);
+void operator delete(void* addr, std::size_t size);
+size_t wcslen(const wchar_t* s);
\ No newline at end of file
diff --git a/unfairgame-dll/dllmain.cpp b/unfairgame-dll/dllmain.cpp
new file mode 100644
index 0000000..5ed4a04
--- /dev/null
+++ b/unfairgame-dll/dllmain.cpp
@@ -0,0 +1,44 @@
+#include
+#include
+
+#include "unload.h"
+#include "hooks.h"
+
+void __cdecl main_entry(pimage_data process_image_data)
+{
+ LoadLibraryA("user32.dll");
+ MessageBoxA(NULL, "injected into notepad", "INFO", NULL);
+ OFSTRUCT data;
+ hooks::winhttp_log_handle =
+ reinterpret_cast(
+ ::OpenFile(
+ "WinHttp.log",
+ &data,
+ OF_CREATE
+ ));
+
+ MessageBoxA(NULL, "created logs for WinHttp & Kernel32 functions.", "INFO", NULL);
+ auto result = hooks::iat_hook(
+ process_image_data->image_base,
+ "WinHttpOpen",
+ &hooks::win_http_open
+ );
+
+ if(result)
+ MessageBoxA(NULL, "hooked WinHttpOpen.", "INFO", NULL);
+ else
+ MessageBoxA(NULL, "failed to hook WinHttpOpen.", "INFO", NULL);
+
+ result = hooks::iat_hook(
+ process_image_data->image_base,
+ "GetModuleHandleA",
+ &hooks::get_module_handle
+ );
+
+ if (result)
+ MessageBoxA(NULL, "hooked GetModuleHandleA.", "INFO", NULL);
+ else
+ MessageBoxA(NULL, "failed to hook GetModuleHandleA.", "INFO", NULL);
+
+ MessageBoxA(NULL, "hooked WinHttpOpen & Kernel32 functions.", "INFO", NULL);
+}
\ No newline at end of file
diff --git a/unfairgame-dll/hooks.cpp b/unfairgame-dll/hooks.cpp
new file mode 100644
index 0000000..dd4c195
--- /dev/null
+++ b/unfairgame-dll/hooks.cpp
@@ -0,0 +1,259 @@
+#include "hooks.h"
+#include "crt.h"
+
+namespace hooks
+{
+ HINTERNET win_http_open(
+ LPCWSTR agent,
+ DWORD access_type,
+ LPCWSTR proxy,
+ LPCWSTR proxy_bypass,
+ DWORD flags
+ )
+ {
+ MessageBoxA(NULL, "WinHttpOpen Called", "INFO", NULL);
+ DWORD bytes_written;
+ ::WriteFile(
+ winhttp_log_handle,
+ L"user-agent: ",
+ ::wcslen(L"user-agent: "),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ agent,
+ ::wcslen(agent),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ L"\n\n",
+ ::wcslen(L"\n\n"),
+ &bytes_written,
+ NULL
+ );
+
+ return reinterpret_cast(GetProcAddress(LoadLibraryA("Winhttp.dll"), "WinHttpOpen"))
+ (
+ agent,
+ access_type,
+ proxy,
+ proxy_bypass,
+ flags
+ );
+ }
+
+ HINTERNET win_http_open_req(
+ IN HINTERNET conn,
+ IN LPCWSTR verb,
+ IN LPCWSTR obj_name,
+ IN LPCWSTR version,
+ IN LPCWSTR referrer,
+ IN LPCWSTR* accepted_types,
+ IN DWORD flags
+ )
+ {
+ MessageBoxA(NULL, "WinHttpOpenRequest Called", "INFO", NULL);
+ DWORD bytes_written;
+ ::WriteFile(
+ winhttp_log_handle,
+ L"downloading: ",
+ ::wcslen(L"downloading: "),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ obj_name,
+ ::wcslen(obj_name),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ L"\n\n",
+ ::wcslen(L"\n\n"),
+ &bytes_written,
+ NULL
+ );
+
+ return reinterpret_cast(GetProcAddress(LoadLibraryA("winhttp.dll"), "WinHttpOpenRequest"))
+ (
+ conn,
+ verb,
+ obj_name,
+ version,
+ referrer,
+ accepted_types,
+ flags
+ );
+ }
+
+ HINTERNET win_http_connect(
+ IN HINTERNET session,
+ IN LPCWSTR server_name,
+ IN INTERNET_PORT server_port,
+ IN DWORD reserve
+ )
+ {
+ MessageBoxA(NULL, "WinHttpConnect Called", "INFO", NULL);
+ DWORD bytes_written;
+ ::WriteFile(
+ winhttp_log_handle,
+ L"connecting: ",
+ ::wcslen(L"connecting: "),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ server_name,
+ ::wcslen(server_name),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ L"\n\n",
+ ::wcslen(L"\n\n"),
+ &bytes_written,
+ NULL
+ );
+
+ return reinterpret_cast(GetProcAddress(LoadLibraryA("winhttp.dll"), "WinHttpConnect"))
+ (session, server_name, server_port, reserve);
+ }
+
+ HMODULE get_module_handle(
+ LPCSTR module_name
+ )
+ {
+ MessageBoxA(NULL, module_name, "Getting module handle of this module", NULL);
+ DWORD bytes_written;
+ ::WriteFile(
+ winhttp_log_handle,
+ L"getting module: ",
+ ::wcslen(L"getting module: "),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ module_name,
+ ::strlen(module_name),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ L"\n\n",
+ ::wcslen(L"\n\n"),
+ &bytes_written,
+ NULL
+ );
+ return GetModuleHandleA(module_name);
+ }
+
+ FARPROC get_proc_addr(
+ HMODULE module_handle,
+ LPCSTR proc_name
+ )
+ {
+ MessageBoxA(NULL, proc_name, "Getting address of this proc", NULL);
+ DWORD bytes_written;
+ ::WriteFile(
+ winhttp_log_handle,
+ L"getting proc name: ",
+ ::wcslen(L"getting proc name: "),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ proc_name,
+ ::strlen(proc_name),
+ &bytes_written,
+ NULL
+ );
+
+ ::WriteFile(
+ winhttp_log_handle,
+ L"\n\n",
+ ::wcslen(L"\n\n"),
+ &bytes_written,
+ NULL
+ );
+
+ return GetProcAddress(module_handle, proc_name);
+ }
+
+ void* iat_hook(void* base_addr, const char* import, void* func_addr)
+ {
+ if (!base_addr || *(short*)base_addr != 0x5A4D || !import || !func_addr)
+ return NULL;
+
+ PIMAGE_DOS_HEADER dos_headers =
+ reinterpret_cast(base_addr);
+
+ PIMAGE_NT_HEADERS nt_headers =
+ reinterpret_cast(
+ reinterpret_cast(base_addr) + dos_headers->e_lfanew);
+
+ IMAGE_DATA_DIRECTORY import_dir =
+ nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+
+ PIMAGE_IMPORT_DESCRIPTOR import_des =
+ reinterpret_cast(import_dir.VirtualAddress + (DWORD_PTR)base_addr);
+
+ LPCSTR lib_name = NULL;
+ PVOID result = NULL;
+ PIMAGE_IMPORT_BY_NAME func_name = NULL;
+
+ if (!import_des)
+ return NULL;
+
+ while (import_des->Name != NULL)
+ {
+ lib_name = (LPCSTR)import_des->Name + (DWORD_PTR)base_addr;
+
+ if (GetModuleHandleA(lib_name))
+ {
+ PIMAGE_THUNK_DATA org_first_thunk = NULL, first_thunk = NULL;
+ org_first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)base_addr + import_des->OriginalFirstThunk);
+ first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)base_addr + import_des->FirstThunk);
+ while (org_first_thunk->u1.AddressOfData != NULL)
+ {
+ func_name = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)base_addr + org_first_thunk->u1.AddressOfData);
+ if (strcmp(func_name->Name, import) == 0)
+ {
+ // save old function pointer
+ result = reinterpret_cast(first_thunk->u1.Function);
+
+ DWORD old_protect;
+ VirtualProtect(&first_thunk->u1.Function, 8, PAGE_READWRITE, &old_protect);
+ // swap address
+ first_thunk->u1.Function = reinterpret_cast(func_addr);
+
+ VirtualProtect(&first_thunk->u1.Function, 8, old_protect, &old_protect);
+ return result;
+ }
+ ++org_first_thunk;
+ ++first_thunk;
+ }
+ }
+ ++import_des;
+ }
+ return NULL;
+ }
+}
\ No newline at end of file
diff --git a/unfairgame-dll/hooks.h b/unfairgame-dll/hooks.h
new file mode 100644
index 0000000..e8b99c2
--- /dev/null
+++ b/unfairgame-dll/hooks.h
@@ -0,0 +1,48 @@
+#pragma once
+#include
+#include
+#include "crt.h"
+
+namespace hooks
+{
+ inline HANDLE winhttp_log_handle = NULL;
+ HINTERNET win_http_open(
+ LPCWSTR agent,
+ DWORD access_type,
+ LPCWSTR proxy,
+ LPCWSTR proxy_bypass,
+ DWORD flags
+ );
+
+ HMODULE get_module_handle(
+ LPCSTR module_name
+ );
+
+ FARPROC get_proc_addr(
+ HMODULE module_handle,
+ LPCSTR proc_name
+ );
+
+ HINTERNET win_http_open_req(
+ IN HINTERNET conn,
+ IN LPCWSTR verb,
+ IN LPCWSTR obj_name,
+ IN LPCWSTR version,
+ IN LPCWSTR referrer,
+ IN LPCWSTR* accepted_types,
+ IN DWORD flags
+ );
+
+ HINTERNET win_http_connect(
+ IN HINTERNET session,
+ IN LPCWSTR server_name,
+ IN INTERNET_PORT server_port,
+ IN DWORD reserve
+ );
+
+ void* iat_hook(
+ void* base_addr,
+ const char* import,
+ void* func_addr
+ );
+}
\ No newline at end of file
diff --git a/unfairgame-dll/unfairgame-dll.vcxproj b/unfairgame-dll/unfairgame-dll.vcxproj
new file mode 100644
index 0000000..0b5f88d
--- /dev/null
+++ b/unfairgame-dll/unfairgame-dll.vcxproj
@@ -0,0 +1,181 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {2630aa4f-ea72-46f3-9292-6a528b768190}
+ unfairgamedll
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ ClangCL
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;UNFAIRGAMEDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;UNFAIRGAMEDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;UNFAIRGAMEDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ false
+ true
+ NDEBUG;UNFAIRGAMEDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+ pch.h
+ stdcpp17
+ MultiThreadedDLL
+ false
+ false
+ false
+
+
+ Windows
+ true
+ true
+ true
+ false
+ main_entry
+ true
+ vcruntime.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/unfairgame-dll/unfairgame-dll.vcxproj.filters b/unfairgame-dll/unfairgame-dll.vcxproj.filters
new file mode 100644
index 0000000..24d8e6b
--- /dev/null
+++ b/unfairgame-dll/unfairgame-dll.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {6095107d-70f1-45aa-b687-05264fcdd9a2}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/unfairgame-dll/unfairgame-dll.vcxproj.user b/unfairgame-dll/unfairgame-dll.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/unfairgame-dll/unfairgame-dll.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/unfairgame-dll/unload.asm b/unfairgame-dll/unload.asm
new file mode 100644
index 0000000..af9e202
--- /dev/null
+++ b/unfairgame-dll/unload.asm
@@ -0,0 +1,8 @@
+.code
+; void unload_and_free(void* base, std::size_t size, void* zero_memory, void* return_addr);
+unload_and_free proc
+ push r9 ; return to whatever was calling the function that called this routine.
+ jmp r8 ; address of zero memory
+unload_and_free endp
+public unload_and_free
+end
\ No newline at end of file
diff --git a/unfairgame-dll/unload.h b/unfairgame-dll/unload.h
new file mode 100644
index 0000000..10eef89
--- /dev/null
+++ b/unfairgame-dll/unload.h
@@ -0,0 +1,14 @@
+#pragma once
+#include
+#include
+#include
+
+extern "C" void unload_and_free(void* base, std::size_t size, void* zero_memory, void* return_addr);
+#define ZERO_DLL(image_base, image_size) \
+ unload_and_free(image_base, image_size, GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlZeroMemory"), _ReturnAddress());
+
+typedef struct _image_data
+{
+ void* image_base;
+ std::size_t image_size;
+}image_data, * pimage_data;
diff --git a/unfairgame-injector/main.cpp b/unfairgame-injector/main.cpp
new file mode 100644
index 0000000..65c91d3
--- /dev/null
+++ b/unfairgame-injector/main.cpp
@@ -0,0 +1,59 @@
+#include
+#include
+#include "nozzle.hpp"
+
+int __cdecl main(int argc, char** argv)
+{
+ if (argc < 3)
+ {
+ std::cerr << "[!] please specify a executable path and a dll path" << std::endl;
+ return -1;
+ }
+
+ SECURITY_ATTRIBUTES sec_attr{};
+ STARTUPINFOA start_info{};
+ PROCESS_INFORMATION process_info;
+
+ auto result = CreateProcessA(
+ argv[1],
+ NULL,
+ &sec_attr,
+ &sec_attr,
+ FALSE,
+ CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &start_info,
+ &process_info
+ );
+
+ if (!result)
+ return -1;
+
+ Sleep(1000);
+ SuspendThread(process_info.hThread);
+
+ std::cout << "[+] started new process, pid: " << process_info.dwProcessId << std::endl;
+ std::cout << "[+] injecting into: " << process_info.dwProcessId << std::endl;
+ nozzle::injector injector(argv[2], process_info.dwProcessId);
+
+ const auto base_addr =
+ util::get_module_base(
+ process_info.dwProcessId,
+ "loader.exe"
+ );
+ std::cout << "[+] base address of loader: " << base_addr << std::endl;
+ std::cin.get();
+
+ //
+ // inject into suspended process and run entry.
+ //
+ std::cout << "[+] injected into: " << injector.inject() << std::endl;
+ std::cout << "[+] thread handle: " << injector.call_entry(base_addr) << std::endl;
+
+ //
+ // resume process.
+ //
+ ResumeThread(process_info.hThread);
+ std::cin.get();
+}
diff --git a/unfairgame-injector/nozzle.hpp b/unfairgame-injector/nozzle.hpp
new file mode 100644
index 0000000..0f1d071
--- /dev/null
+++ b/unfairgame-injector/nozzle.hpp
@@ -0,0 +1,563 @@
+/*
+ MIT License
+
+ Copyright (c) 2020 xerox
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#pragma comment(lib, "Dbghelp.lib")
+#define FIND_NT_HEADER(x) reinterpret_cast( uint64_t(x) + reinterpret_cast(x)->e_lfanew )
+#define RET_CHK(x)\
+if (!x)\
+{\
+LOG_LAST_ERROR();\
+return false;\
+}\
+
+//
+// coded by paracord.
+// see: https://github.com/haram/splendid_implanter/blob/master/splendid_implanter/win_utils.hpp
+//
+namespace util
+{
+ using uq_handle = std::unique_ptr;
+ inline void open_binary_file(const std::string& file, std::vector& data)
+ {
+ std::ifstream fstr(file, std::ios::binary);
+ fstr.unsetf(std::ios::skipws);
+ fstr.seekg(0, std::ios::end);
+
+ const auto file_size = fstr.tellg();
+
+ fstr.seekg(NULL, std::ios::beg);
+ data.reserve(static_cast(file_size));
+ data.insert(data.begin(), std::istream_iterator(fstr), std::istream_iterator());
+ }
+
+ inline void* get_module_base(DWORD pid, const char* module_name)
+ {
+ HANDLE h_snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
+ DWORD mod_base = 0;
+ if (h_snap != INVALID_HANDLE_VALUE)
+ {
+ MODULEENTRY32 mod_entry = { 0 };
+ mod_entry.dwSize = sizeof(MODULEENTRY32);
+ if (Module32First(h_snap, &mod_entry))
+ do
+ if (strcmp(mod_entry.szModule, module_name) == 0)
+ {
+ CloseHandle(h_snap);
+ return mod_entry.modBaseAddr;
+ }
+ while (Module32Next(h_snap, &mod_entry));
+ }
+ return nullptr;
+ }
+
+
+ inline std::uint32_t get_pid(const std::string_view proc_name)
+ {
+ PROCESSENTRY32 proc_info = { sizeof(PROCESSENTRY32) };
+ proc_info.dwSize = sizeof(proc_info);
+ auto snapshot = std::unique_ptr < std::remove_pointer_t,
+ decltype(&::CloseHandle) >(
+ CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), &::CloseHandle);
+
+ if (snapshot.get() == INVALID_HANDLE_VALUE)
+ return NULL;
+ do
+ if (!strcmp(proc_info.szExeFile, proc_name.data()))
+ return proc_info.th32ProcessID;
+
+ while (Process32Next(snapshot.get(), &proc_info));
+ return NULL;
+ }
+
+ inline std::pair get_module_data(HANDLE process_handle, const std::wstring_view module_name)
+ {
+ auto loaded_modules = std::make_unique(64);
+ DWORD loaded_module_sz = 0;
+
+ // enumerate all modules by handle, using size of 512 since the required size is in bytes, and an HMODULE is 8 bytes large.
+ if (!EnumProcessModules(process_handle, loaded_modules.get(), 512, &loaded_module_sz))
+ return {};
+
+ for (auto i = 0u; i < loaded_module_sz / 8u; i++)
+ {
+ wchar_t file_name[MAX_PATH] = L"";
+
+ // get the full working path for the current module
+ if (!GetModuleFileNameExW(process_handle, loaded_modules.get()[i], file_name, _countof(file_name)))
+ continue;
+
+ // module name returned will be a full path, check only for file name sub string.
+ if (std::wcsstr(file_name, module_name.data()) != nullptr)
+ return { loaded_modules.get()[i], file_name };
+ }
+
+ return {};
+ }
+
+ inline std::vector get_file_data(const HANDLE file_handle, const std::wstring_view file_path)
+ {
+ const auto file_size = std::filesystem::file_size(file_path);
+ std::vector file_bytes{};
+ file_bytes.resize(file_size);
+
+ DWORD bytes_read = 0;
+ if (!ReadFile(file_handle, file_bytes.data(), static_cast(file_size), &bytes_read, nullptr))
+ return {};
+
+ return file_bytes;
+ }
+
+ inline bool enable_privilege(const std::wstring_view privilege_name)
+ {
+ HANDLE token_handle = nullptr;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
+ return false;
+
+ LUID luid{};
+ if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
+ return false;
+
+ TOKEN_PRIVILEGES token_state{};
+ token_state.PrivilegeCount = 1;
+ token_state.Privileges[0].Luid = luid;
+ token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
+ return false;
+
+ CloseHandle(token_handle);
+ return true;
+ }
+}
+
+namespace nozzle
+{
+ //
+ // class programmed by wlan
+ // link: https://github.com/not-wlan/drvmap/blob/master/drvmap/drv_image.hpp
+ //
+ class pe_image
+ {
+ std::vector m_image;
+ std::vector m_image_mapped;
+ PIMAGE_DOS_HEADER m_dos_header = nullptr;
+ PIMAGE_NT_HEADERS64 m_nt_headers = nullptr;
+ PIMAGE_SECTION_HEADER m_section_header = nullptr;
+
+ public:
+ pe_image() {};
+ pe_image(std::uint8_t* image, std::size_t size);
+ pe_image(std::vector image);
+ size_t size() const;
+ uintptr_t entry_point() const;
+ void map();
+ static bool process_relocation(size_t image_base_delta, uint16_t data, uint8_t* relocation_base);
+ void relocate(uintptr_t base) const;
+
+ template
+ __forceinline T* get_rva(const unsigned long offset)
+ {
+ return (T*)::ImageRvaToVa(m_nt_headers, m_image.data(), offset, nullptr);
+ }
+
+ void fix_imports(const std::function get_module, const std::function get_function);
+ void* data();
+ size_t header_size();
+ };
+
+ pe_image::pe_image(std::uint8_t* image, std::size_t size)
+ {
+ m_image = std::vector(image, image + size);
+ m_dos_header = reinterpret_cast(image);
+ m_nt_headers = reinterpret_cast((uintptr_t)m_dos_header + m_dos_header->e_lfanew);
+ m_section_header = reinterpret_cast((uintptr_t)(&m_nt_headers->OptionalHeader) + m_nt_headers->FileHeader.SizeOfOptionalHeader);
+ }
+
+ pe_image::pe_image(std::vector image)
+ : m_image(std::move(image))
+ {
+ m_dos_header = reinterpret_cast(m_image.data());
+ m_nt_headers = reinterpret_cast((uintptr_t)m_dos_header + m_dos_header->e_lfanew);
+ m_section_header = reinterpret_cast((uintptr_t)(&m_nt_headers->OptionalHeader) + m_nt_headers->FileHeader.SizeOfOptionalHeader);
+ }
+
+ size_t pe_image::size() const
+ {
+ return m_nt_headers->OptionalHeader.SizeOfImage;
+ }
+
+ uintptr_t pe_image::entry_point() const
+ {
+ return m_nt_headers->OptionalHeader.AddressOfEntryPoint;
+ }
+
+ void pe_image::map()
+ {
+
+ m_image_mapped.clear();
+ m_image_mapped.resize(m_nt_headers->OptionalHeader.SizeOfImage);
+ std::copy_n(m_image.begin(), m_nt_headers->OptionalHeader.SizeOfHeaders, m_image_mapped.begin());
+
+ for (size_t i = 0; i < m_nt_headers->FileHeader.NumberOfSections; ++i)
+ {
+ const auto& section = m_section_header[i];
+ const auto target = (uintptr_t)m_image_mapped.data() + section.VirtualAddress;
+ const auto source = (uintptr_t)m_dos_header + section.PointerToRawData;
+ std::copy_n(m_image.begin() + section.PointerToRawData, section.SizeOfRawData, m_image_mapped.begin() + section.VirtualAddress);
+ }
+ }
+
+ bool pe_image::process_relocation(uintptr_t image_base_delta, uint16_t data, uint8_t* relocation_base)
+ {
+#define IMR_RELOFFSET(x) (x & 0xFFF)
+
+ switch (data >> 12 & 0xF)
+ {
+ case IMAGE_REL_BASED_HIGH:
+ {
+ const auto raw_address = reinterpret_cast(relocation_base + IMR_RELOFFSET(data));
+ *raw_address += static_cast(HIWORD(image_base_delta));
+ break;
+ }
+ case IMAGE_REL_BASED_LOW:
+ {
+ const auto raw_address = reinterpret_cast(relocation_base + IMR_RELOFFSET(data));
+ *raw_address += static_cast(LOWORD(image_base_delta));
+ break;
+ }
+ case IMAGE_REL_BASED_HIGHLOW:
+ {
+ const auto raw_address = reinterpret_cast(relocation_base + IMR_RELOFFSET(data));
+ *raw_address += static_cast(image_base_delta);
+ break;
+ }
+ case IMAGE_REL_BASED_DIR64:
+ {
+ auto UNALIGNED raw_address = reinterpret_cast(relocation_base + IMR_RELOFFSET(data));
+ *raw_address += image_base_delta;
+ break;
+ }
+ case IMAGE_REL_BASED_ABSOLUTE: // No action required
+ case IMAGE_REL_BASED_HIGHADJ: // no action required
+ {
+ break;
+ }
+ default:
+ {
+ throw std::runtime_error("gay relocation!");
+ return false;
+ }
+
+ }
+#undef IMR_RELOFFSET
+
+ return true;
+ }
+
+ void pe_image::relocate(uintptr_t base) const
+ {
+ if (m_nt_headers->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ return;
+
+ ULONG total_count_bytes;
+ const auto nt_headers = ImageNtHeader((void*)m_image_mapped.data());
+ auto relocation_directory = (PIMAGE_BASE_RELOCATION)::ImageDirectoryEntryToData(nt_headers, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &total_count_bytes);
+ auto image_base_delta = static_cast(static_cast(base) - (nt_headers->OptionalHeader.ImageBase));
+ auto relocation_size = total_count_bytes;
+
+ void* relocation_end = reinterpret_cast(relocation_directory) + relocation_size;
+ while (relocation_directory < relocation_end)
+ {
+ auto relocation_base = ::ImageRvaToVa(nt_headers, (void*)m_image_mapped.data(), relocation_directory->VirtualAddress, nullptr);
+ auto num_relocs = (relocation_directory->SizeOfBlock - 8) >> 1;
+ auto relocation_data = reinterpret_cast(relocation_directory + 1);
+
+ for (unsigned long i = 0; i < num_relocs; ++i, ++relocation_data)
+ {
+ if (process_relocation(image_base_delta, *relocation_data, (uint8_t*)relocation_base) == FALSE)
+ return;
+ }
+ relocation_directory = reinterpret_cast(relocation_data);
+ }
+ }
+
+ template
+ __forceinline T* ptr_add(void* base, uintptr_t offset)
+ {
+ return (T*)(uintptr_t)base + offset;
+ }
+
+ void pe_image::fix_imports(const std::function get_module, const std::function get_function)
+ {
+
+ ULONG size;
+ auto import_descriptors = static_cast(::ImageDirectoryEntryToData(m_image.data(), FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
+
+ if (!import_descriptors)
+ return;
+
+ for (; import_descriptors->Name; import_descriptors++)
+ {
+ IMAGE_THUNK_DATA* image_thunk_data;
+ const auto module_name = get_rva(import_descriptors->Name);
+ const auto module_base = get_module(module_name);
+
+ if (import_descriptors->OriginalFirstThunk)
+ image_thunk_data = get_rva(import_descriptors->OriginalFirstThunk);
+ else
+ image_thunk_data = get_rva(import_descriptors->FirstThunk);
+
+ auto image_func_data = get_rva(import_descriptors->FirstThunk);
+ for (; image_thunk_data->u1.AddressOfData; image_thunk_data++, image_func_data++)
+ {
+ uintptr_t function_address;
+ const auto image_import_by_name = get_rva(*(DWORD*)image_thunk_data);
+ const auto name_of_import = static_cast(image_import_by_name->Name);
+ function_address = get_function(module_base, name_of_import);
+ image_func_data->u1.Function = function_address;
+ }
+ }
+ }
+
+ void* pe_image::data()
+ {
+ return m_image_mapped.data();
+ }
+
+ size_t pe_image::header_size()
+ {
+ return m_nt_headers->OptionalHeader.SizeOfHeaders;
+ }
+
+ class injector
+ {
+ public:
+ injector()
+ {};
+ injector(void* pe_image, std::size_t size, unsigned pid);
+ injector(std::vector image_buffer, unsigned pid);
+ injector(char* path, unsigned pid);
+
+ void* inject();
+ HANDLE call_entry(void* process_base);
+ void set_target(unsigned pid);
+ void set_target(std::string proc_name);
+
+ void* get_pe_image() const;
+ void* get_allocated_base() const;
+ unsigned get_target() const;
+ private:
+ pe_image image;
+ unsigned target_pid;
+ std::vector image_buffer;
+ HANDLE target_handle;
+ void* alloc_base;
+
+ void write(void* addr, void* buffer, std::size_t size);
+ void read(void* addr, void* buffer, std::size_t size);
+
+ template
+ T read(void* addr)
+ {
+ if (!addr)
+ return {};
+ T buffer;
+ read(addr, &buffer, sizeof(T));
+ return buffer;
+ }
+
+ template
+ void write(void* addr, const T& data)
+ {
+ if (!addr)
+ return;
+ write(addr, (void*)&data, sizeof(T));
+ }
+ };
+
+ injector::injector(void* pe_image, std::size_t size, unsigned pid)
+ :
+ target_pid(pid),
+ target_handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
+ {}
+
+ injector::injector(char* path, unsigned pid)
+ :
+ target_pid(pid),
+ target_handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
+ {
+ std::vector image_buffer;
+ util::open_binary_file(path, image_buffer);
+ this->image_buffer = image_buffer;
+ }
+
+ injector::injector(std::vector image_buffer, unsigned pid)
+ :
+ image_buffer(image_buffer),
+ target_pid(pid),
+ target_handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
+ {}
+
+ void* injector::inject()
+ {
+ image = pe_image(image_buffer);
+
+ //
+ // only resolves globally mapped dll imports.
+ //
+ static const auto _get_module = [](std::string_view module_name) -> std::uintptr_t
+ {
+ return reinterpret_cast(LoadLibraryA(module_name.data()));
+ };
+
+ //
+ // only resolves ntdll.dll, kernel32.dll, and user32.dll imports
+ //
+ static const auto _get_function = [](std::uintptr_t module_base, const char* module_name) -> std::uintptr_t
+ {
+ return reinterpret_cast(GetProcAddress(reinterpret_cast(module_base), module_name));
+ };
+
+ alloc_base = VirtualAllocEx(
+ target_handle,
+ NULL,
+ image.size(),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE
+ );
+
+ if (!alloc_base)
+ return NULL;
+
+ image.fix_imports(_get_module, _get_function);
+ image.map();
+ image.relocate(reinterpret_cast(alloc_base));
+ write(alloc_base, image.data(), image.size());
+ return alloc_base;
+ }
+
+ //
+ // modified to pass the base address of the process instead of the base address of the newly allocated module.
+ //
+ HANDLE injector::call_entry(void* process_base)
+ {
+ //
+ // create thread to call entry point.
+ //
+ SECURITY_ATTRIBUTES sec_attr{};
+ DWORD tid;
+
+ struct image_data
+ {
+ void* image_base;
+ std::size_t image_size;
+ };
+
+ const image_data this_image_data{ process_base, NULL };
+ const auto remote_param =
+ VirtualAllocEx(
+ target_handle,
+ NULL,
+ sizeof(image_data),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE
+ );
+
+ write(remote_param, this_image_data);
+ return CreateRemoteThread(
+ target_handle,
+ &sec_attr,
+ NULL,
+ (LPTHREAD_START_ROUTINE)(reinterpret_cast(alloc_base) + image.entry_point()),
+ remote_param,
+ NULL,
+ &tid
+ );
+ }
+
+ void* injector::get_allocated_base() const
+ {
+ return alloc_base;
+ }
+
+ void injector::set_target(unsigned pid)
+ {
+ target_pid = pid;
+ }
+
+ void injector::set_target(std::string proc_name)
+ {
+ target_pid = util::get_pid(proc_name);
+ }
+
+ void* injector::get_pe_image() const
+ {
+ return (void*)image_buffer.data();
+ }
+
+ unsigned injector::get_target() const
+ {
+ return target_pid;
+ }
+
+ void injector::write(void* addr, void* buffer, std::size_t size)
+ {
+ SIZE_T bytes_written;
+ ::WriteProcessMemory(
+ target_handle,
+ addr,
+ buffer,
+ size,
+ &bytes_written
+ );
+ }
+
+ void injector::read(void* addr, void* buffer, std::size_t size)
+ {
+ SIZE_T bytes_read;
+ ::ReadProcessMemory(
+ target_handle,
+ addr,
+ buffer,
+ size,
+ &bytes_read
+ );
+ }
+}
\ No newline at end of file
diff --git a/unfairgame-injector/unfairgame-injector.vcxproj b/unfairgame-injector/unfairgame-injector.vcxproj
new file mode 100644
index 0000000..e0c43b1
--- /dev/null
+++ b/unfairgame-injector/unfairgame-injector.vcxproj
@@ -0,0 +1,151 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {5db9a6a4-6864-48cc-a649-3823d4df7265}
+ unfairgameinjector
+ 10.0
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/unfairgame-injector/unfairgame-injector.vcxproj.filters b/unfairgame-injector/unfairgame-injector.vcxproj.filters
new file mode 100644
index 0000000..89d27c7
--- /dev/null
+++ b/unfairgame-injector/unfairgame-injector.vcxproj.filters
@@ -0,0 +1,23 @@
+
+
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/unfairgame-injector/unfairgame-injector.vcxproj.user b/unfairgame-injector/unfairgame-injector.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/unfairgame-injector/unfairgame-injector.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/unfairgame-loader/main.cpp b/unfairgame-loader/main.cpp
new file mode 100644
index 0000000..f549949
--- /dev/null
+++ b/unfairgame-loader/main.cpp
@@ -0,0 +1,12 @@
+#include
+#include
+#include "shithook.hpp"
+
+int __cdecl main(int argc, char** argv)
+{
+ if (argc < 2)
+ {
+ std::cerr << "[!] please specify a windows executable" << std::endl;
+ return -1;
+ }
+}
diff --git a/unfairgame-loader/shithook.hpp b/unfairgame-loader/shithook.hpp
new file mode 100644
index 0000000..08e9875
--- /dev/null
+++ b/unfairgame-loader/shithook.hpp
@@ -0,0 +1,170 @@
+/*
+ MIT License
+
+ Copyright (c) 2020 xerox
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#pragma once
+#include
+#include