├── 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
--------------------------------------------------------------------------------