├── BattleyePresentCheck.sln ├── BattleyePresentCheck ├── BattleyePresentCheck.vcxproj ├── BattleyePresentCheck.vcxproj.filters ├── BattleyePresentCheck.vcxproj.user ├── main.cpp ├── utils.cpp └── utils.h └── README.md /BattleyePresentCheck.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32014.148 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BattleyePresentCheck", "BattleyePresentCheck\BattleyePresentCheck.vcxproj", "{0F058B8C-C17E-4F73-868B-F3262614F584}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Debug|x64.ActiveCfg = Debug|x64 17 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Debug|x64.Build.0 = Debug|x64 18 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Debug|x86.ActiveCfg = Debug|Win32 19 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Debug|x86.Build.0 = Debug|Win32 20 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Release|x64.ActiveCfg = Release|x64 21 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Release|x64.Build.0 = Release|x64 22 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Release|x86.ActiveCfg = Release|Win32 23 | {0F058B8C-C17E-4F73-868B-F3262614F584}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C706EC4F-E9D5-44D3-94CF-0AB19A3F744F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /BattleyePresentCheck/BattleyePresentCheck.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {0f058b8c-c17e-4f73-868b-f3262614f584} 25 | BattleyePresentCheck 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | ntdll.lib;%(AdditionalDependencies) 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /BattleyePresentCheck/BattleyePresentCheck.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Quelldateien 20 | 21 | 22 | Headerdateien 23 | 24 | 25 | 26 | 27 | Headerdateien 28 | 29 | 30 | -------------------------------------------------------------------------------- /BattleyePresentCheck/BattleyePresentCheck.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /BattleyePresentCheck/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "utils.h" 8 | 9 | 10 | typedef HRESULT(__stdcall* D3D11PresentHook) (IDXGISwapChain* This, UINT SyncInterval, UINT Flags); 11 | volatile static D3D11PresentHook oPresent = NULL; 12 | uintptr_t** present_pointer; 13 | IDXGISwapChain* globalchain; 14 | extern "C" HRESULT __stdcall hookD3D11Present(IDXGISwapChain * This, UINT SyncInterval, UINT Flags) 15 | { 16 | printf("[>] Handler called! Saving swapchain\n"); 17 | globalchain = This; 18 | *present_pointer = (uintptr_t*)oPresent; 19 | return oPresent(This, SyncInterval, Flags); 20 | } 21 | 22 | 23 | std::unordered_map>modules{ 24 | { "gameoverlayrenderer64.dll", std::tuple("33 F6 83 E5 F7 44 8B C5 8B D6 49 8B CE FF 15", 15) }, 25 | { "DiscordHook64.dll", std::tuple("48 89 D9 89 FA 41 89 F0 FF 15", 10) }, 26 | { "overlay64.dll", std::tuple("48 8B 5C 24 40 44 8B 44 24 30 8B 54 24 38 48 8B CB FF 15", 19) }, 27 | { "DiscordHook64.dll", std::tuple("44 8B C7 8B D6 48 8B CB FF 15", 10) } 28 | }; 29 | 30 | bool func_anomaly(uintptr_t& present) { 31 | bool found_anomaly = false; 32 | 33 | while (true) { 34 | 35 | 36 | while (true) { 37 | while (*(BYTE*)present == 0xE9) { 38 | if (*(DWORD*)(present + 5) == 0xCCCCCCCC) 39 | found_anomaly = true; 40 | present += *(signed int*)(present + 1) + 5; 41 | } 42 | if (*(WORD*)present != 0x25FF) 43 | break; 44 | present = *(uintptr_t*)(present + 6); 45 | } 46 | 47 | if (*(WORD*)present != 0xB848 || *((WORD*)present + 5) != 0xE0FF) 48 | break; 49 | present = *(uintptr_t*)(present + 2); 50 | found_anomaly = true; 51 | } 52 | return found_anomaly; 53 | } 54 | 55 | bool find_memory_anomaly(uintptr_t present) { 56 | bool found_anomaly = false; 57 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 58 | MEMORY_BASIC_INFORMATION mbi2 = { 0 }; 59 | NTSTATUS v27 = NtQueryVirtualMemory((HANDLE) - 1, (LPVOID)present, MemoryBasicInformation, &mbi, 48i64, 0) < 0; 60 | NTSTATUS v7 = v27; 61 | NTSTATUS v28; 62 | bool v31 = v27 63 | || mbi.State != MEM_COMMIT 64 | || mbi.Type != MEM_IMAGE 65 | && (mbi.Type != MEM_PRIVATE 66 | || mbi.State != MEM_FREE 67 | || *((uintptr_t*)present + 20) != 0xEBFFFFFF41058D48ui64 68 | || (present = *((uintptr_t*)present - 3), 69 | v28 = NtQueryVirtualMemory((HANDLE) - 1i64, (LPVOID)present, MemoryBasicInformation, &mbi2, 48i64, 0) < 0, 70 | v7 = v28) 71 | || mbi2.State != MEM_COMMIT 72 | || mbi2.Type != MEM_IMAGE) 73 | || mbi.Protect != 16 && mbi.Protect != 32 && mbi.Protect != 64 && mbi.Protect != 128 74 | || *(uintptr_t*)present == 0x74894808245C8948i64 75 | && (*((uintptr_t*)present + 1) == 0x4140EC8348571024i64 76 | || *((uintptr_t*)present + 1) == 0x5518247C89481024i64) 77 | || *(uintptr_t*)present == 0x57565520245C8948i64 78 | || *(uintptr_t*)present == 0x4157551824748948i64 79 | || *(uintptr_t*)present == 0x8D48564157565540ui64 80 | || *(DWORD*)present == 1220840264 && *((WORD*)present + 2) == 22665 81 | || *(uintptr_t*)present == 0x5741564157565340i64 82 | || *(uintptr_t*)present == 0x5741564155C48B48i64 83 | || *(uintptr_t*)present == 0x4156415441575540i64; 84 | found_anomaly = v31; 85 | return found_anomaly; 86 | } 87 | 88 | int main() { 89 | bool reported = false; 90 | 91 | //General report 92 | { 93 | 94 | uintptr_t base = (uintptr_t)GetModuleHandleA("dxgi.dll"); 95 | if (base) { 96 | PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base; 97 | PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew); 98 | DWORD code_base = nt->OptionalHeader.BaseOfCode; 99 | DWORD code_size = nt->OptionalHeader.SizeOfCode; 100 | uintptr_t code_start = code_base + base; 101 | uintptr_t sig_base = utils::scanpattern(code_start, code_size, "48 C7 40 B8 FE FF FF FF 48 89 58 18"); 102 | if (sig_base) { 103 | uintptr_t swap_chain = sig_base + *(signed int*)(sig_base + 0x25) + 0x29; 104 | uintptr_t present = *(uintptr_t*)(swap_chain + 0x40); 105 | MEMORY_BASIC_INFORMATION mbi_globalchain = { 0 }; 106 | NTSTATUS globalchainstatus = NtQueryVirtualMemory((HANDLE)-1, (PVOID)swap_chain, MemoryBasicInformation, (PVOID)&mbi_globalchain, sizeof(mbi_globalchain), 0); 107 | if (globalchainstatus || func_anomaly(present)) { 108 | reported = true; 109 | printf("[!!!] Anomaly at: %p\n", present); 110 | } 111 | 112 | else 113 | { 114 | printf("[>] No anomalys found jump chain ends at %p\n", present); 115 | printf("[>] Verifying memory!\n"); 116 | if (find_memory_anomaly(present)) { 117 | printf("[!!!] Memory anomaly at destination!\n"); 118 | reported = true; 119 | } 120 | 121 | } 122 | printf("==GENERAL REPORT==\n"); 123 | printf("Report: %x %x\n", 0x47, 0x1); 124 | printf("Present: %p\n", present); 125 | printf("First 32 bytes...\n"); 126 | printf("Allocation base: %p\n", mbi_globalchain.AllocationBase); 127 | printf("Base address: %p\n", mbi_globalchain.BaseAddress); 128 | } 129 | 130 | 131 | } 132 | 133 | } 134 | 135 | //Module specific detections 136 | int idx = 0; 137 | for (auto pair : modules) { 138 | if (uintptr_t base = (uintptr_t)GetModuleHandleA(pair.first.c_str())) { 139 | present_pointer = 0; 140 | PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base; 141 | PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew); 142 | DWORD code_base = nt->OptionalHeader.BaseOfCode; 143 | DWORD code_size = nt->OptionalHeader.SizeOfCode; 144 | uintptr_t code_start = code_base + base; 145 | 146 | uintptr_t sig_base = utils::scanpattern(code_start, code_size, std::get<0>(pair.second).c_str()); 147 | if (!sig_base) 148 | continue; 149 | printf("[>] Sig at %p\n", sig_base); 150 | if (idx == 0) { //gameoverlayrenderer64.dll 151 | uintptr_t jmp_dst = sig_base - 0x45; 152 | if (*(BYTE*)jmp_dst == 0xE8 && 153 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 5, *(DWORD*)jmp_dst != 0x83485340)) { 154 | present_pointer = (uintptr_t**)&jmp_dst; 155 | } 156 | 157 | } 158 | else if (idx == 1) { //DiscordHook64.dll 159 | uintptr_t jmp_dst = sig_base - 0x13; 160 | if (*(BYTE*)jmp_dst == 0xE8 && 161 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 5, *(BYTE*)jmp_dst == 0xE9) && 162 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 15, *(BYTE*)jmp_dst == 0xE9) && 163 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 5, *(BYTE*)jmp_dst != 0x56535540) 164 | ) { 165 | present_pointer = (uintptr_t**)&jmp_dst; 166 | } 167 | 168 | else { 169 | jmp_dst = sig_base - 0xA6; 170 | if (*(BYTE*)jmp_dst == 0xE9 && 171 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 5, *(BYTE*)jmp_dst == 0xE9) && 172 | (jmp_dst += *(signed int*)(jmp_dst + 1) + 6, **(uintptr_t**)jmp_dst == 0x2454891824448944) 173 | ) { 174 | present_pointer = (uintptr_t**)jmp_dst; 175 | } 176 | } 177 | } 178 | 179 | if(!present_pointer) { //overlay64.dll or DiscordHook64.dll 180 | present_pointer = (uintptr_t**)(sig_base + std::get<1>(pair.second)+ *(DWORD*)(sig_base + std::get<1>(pair.second)) + 4); 181 | } 182 | if (present_pointer && *present_pointer) { 183 | 184 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 185 | NTSTATUS status = NtQueryVirtualMemory((HANDLE) - 1, *present_pointer, MemoryBasicInformation, (PVOID) & mbi, sizeof(mbi), 0); 186 | if (status || mbi.State != MEM_COMMIT || mbi.Type != MEM_PRIVATE || mbi.Protect != PAGE_EXECUTE_READWRITE || *(DWORD*)(*present_pointer) == 0x50C03148) { //xor rax, rax push rax 187 | printf("[!!!] Present is invalid memory! %p %p\n", status, &mbi); 188 | reported = true; 189 | 190 | } 191 | 192 | printf("[>] Present pointer in %s at %p\n", pair.first.c_str(), present_pointer); 193 | 194 | oPresent = (D3D11PresentHook)*present_pointer; 195 | *present_pointer = (uintptr_t*)hookD3D11Present; 196 | 197 | Sleep(1000); //Sleep and hope that present got called once 198 | printf("[>] Found SwapChain at %p\n", globalchain); 199 | MEMORY_BASIC_INFORMATION mbi_globalchain = { 0 }; 200 | NTSTATUS globalchainstatus = NtQueryVirtualMemory((HANDLE)-1, globalchain, MemoryBasicInformation, (PVOID)&mbi_globalchain, sizeof(mbi_globalchain), 0); 201 | uintptr_t present_from_vtable = *(uintptr_t*)(*(uintptr_t*)(globalchain)+0x40); 202 | if (globalchainstatus || func_anomaly(present_from_vtable)) { 203 | reported = true; 204 | printf("[!!!] Anomaly at: %p\n", present_from_vtable); 205 | } 206 | 207 | else 208 | { 209 | printf("[>] No anomalys found jump chain ends at %p\n", present_from_vtable); 210 | printf("[>] Verifying memory!\n"); 211 | if (find_memory_anomaly(present_from_vtable)) { 212 | printf("[!!!] Memory anomaly at destination!\n"); 213 | reported = true; 214 | } 215 | 216 | } 217 | } 218 | } 219 | idx++; 220 | } 221 | if (reported) { 222 | printf("[!!!] You received reports!\n"); 223 | } 224 | else { 225 | printf("[>] No reports! You're good to go\n"); 226 | } 227 | } 228 | 229 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL module 230 | DWORD fdwReason, // reason for calling function 231 | LPVOID lpReserved) 232 | { 233 | switch (fdwReason) { 234 | case DLL_PROCESS_ATTACH: { 235 | AllocConsole(); 236 | FILE* f; 237 | freopen_s(&f, "CONOUT$", "w", stdout); 238 | main(); 239 | break; 240 | } 241 | 242 | } 243 | return TRUE; 244 | } 245 | -------------------------------------------------------------------------------- /BattleyePresentCheck/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | uintptr_t utils::scanpattern(uintptr_t base, int size, const char* signature) 4 | { 5 | static auto patternToByte = [](const char* pattern) 6 | { 7 | auto bytes = std::vector{}; 8 | const auto start = const_cast(pattern); 9 | const auto end = const_cast(pattern) + strlen(pattern); 10 | 11 | for (auto current = start; current < end; ++current) 12 | { 13 | if (*current == '?') 14 | { 15 | ++current; 16 | if (*current == '?') 17 | ++current; 18 | bytes.push_back(-1); 19 | } 20 | else { bytes.push_back(strtoul(current, ¤t, 16)); } 21 | } 22 | return bytes; 23 | }; 24 | auto patternBytes = patternToByte(signature); 25 | const auto scanBytes = reinterpret_cast(base); 26 | 27 | const auto s = patternBytes.size(); 28 | const auto d = patternBytes.data(); 29 | 30 | for (auto i = 0ul; i < size - s; ++i) 31 | { 32 | bool found = true; 33 | for (auto j = 0ul; j < s; ++j) 34 | { 35 | if (scanBytes[i + j] != d[j] && d[j] != -1) 36 | { 37 | found = false; 38 | break; 39 | } 40 | } 41 | if (found) { return reinterpret_cast(&scanBytes[i]); } 42 | } 43 | return NULL; 44 | } 45 | -------------------------------------------------------------------------------- /BattleyePresentCheck/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace utils { 5 | uintptr_t scanpattern(uintptr_t base, int size, const char* signature); 6 | } 7 | using MEMORY_INFORMATION_CLASS = enum _MEMORY_INFORMATION_CLASS { 8 | MemoryBasicInformation 9 | }; 10 | extern "C" NTSTATUS DECLSPEC_IMPORT NTAPI NtQueryVirtualMemory(HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PresentHookDetection --------------------------------------------------------------------------------