├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── NullifyZwDetectMemoryRead ├── dllmain.cpp └── dllmain.h ├── README.md └── ZwDetectMemoryRead ├── main.cpp └── main.h /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "NullifyZwDetectMemoryRead/minhook"] 2 | path = NullifyZwDetectMemoryRead/minhook 3 | url = git@github.com:TsudaKageyu/minhook.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for Target, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required (VERSION 3.22) 5 | 6 | project ("ZwDetectMemoryRead") 7 | 8 | # ------------for ZwDetectMemoryRead---------- 9 | add_executable (${PROJECT_NAME} "ZwDetectMemoryRead/main.h" "ZwDetectMemoryRead/main.cpp") 10 | 11 | # --------for NullifyZwDetectMemoryRead------------ 12 | include_directories(${CMAKE_CURRENT_LIST_DIR}/NullifyZwDetectMemoryRead/minhook/include) 13 | FILE(GLOB_RECURSE MINHOOK_SOURCE CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/NullifyZwDetectMemoryRead/minhook/src/*.c) 14 | FILE(GLOB_RECURSE MINHOOK_HEADER CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/NullifyZwDetectMemoryRead/minhook/src/*.h) 15 | FILE(GLOB_RECURSE MINHOOK_HDE_SOURCE CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/NullifyZwDetectMemoryRead/minhook/src/hde/*.c) 16 | FILE(GLOB_RECURSE MINHOOK_HDE_HEADER CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/NullifyZwDetectMemoryRead/minhook/src/hde/*.h) 17 | 18 | add_library (NullifyZwDetectMemoryRead SHARED "NullifyZwDetectMemoryRead/dllmain.cpp" "NullifyZwDetectMemoryRead/dllmain.cpp" ${MINHOOK_SOURCE} ${MINHOOK_HEADER} ${MINHOOK_HDE_SOURCE} ${MINHOOK_HDE_HEADER}) 19 | target_compile_features(NullifyZwDetectMemoryRead PRIVATE cxx_std_20) 20 | 21 | set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) 22 | 23 | # TODO: Add tests and install targets if needed. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 pseuxide 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NullifyZwDetectMemoryRead/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "dllmain.h" 2 | #include 3 | #include "MinHook.h" 4 | 5 | #define DEBUG 6 | 7 | #ifdef DEBUG 8 | #define ALLOCCONSOLE()\ 9 | {\ 10 | AllocConsole();\ 11 | freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout);\ 12 | } 13 | #define FREECONSOLE()\ 14 | {\ 15 | fclose(stdout);\ 16 | FreeConsole();\ 17 | } 18 | #else 19 | #define LOGHEX(name, val) 20 | #define ALLOCCONSOLE() 21 | #define FREECONSOLE() 22 | #endif 23 | 24 | using namespace std; 25 | 26 | void Detach() 27 | { 28 | FREECONSOLE() 29 | } 30 | 31 | ZwQueryVirtualMemory_t oZwQueryVirtualMemory = NULL; 32 | 33 | NTSTATUS DetourZwQueryVirtualMemory(HANDLE hProcess, PVOID address, int indices, PVOID buffer, SIZE_T bufferLength, PSIZE_T returnLength) { 34 | NTSTATUS status = oZwQueryVirtualMemory(hProcess, address, indices, buffer, bufferLength, returnLength); 35 | if (indices != 4) { 36 | return status; 37 | } 38 | reinterpret_cast(buffer)->VirtualAttributes.Valid = 0; 39 | return status; 40 | } 41 | 42 | DWORD WINAPI fMain(LPVOID lpParameter) 43 | { 44 | ALLOCCONSOLE(); 45 | HMODULE ntdll = GetModuleHandle("ntdll.dll"); 46 | ZwQueryVirtualMemory_t ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)GetProcAddress(ntdll, "ZwQueryVirtualMemory"); 47 | 48 | if (MH_STATUS status = MH_Initialize(); status != MH_OK) { 49 | cout << "Error at MH_Initialize: " << status << endl; 50 | return 1; 51 | } 52 | // Note that u don't pass &ZwQueryVirtualMemory for pTarget. ZwQueryVirtualMemory itself is already a pointer. 53 | if (MH_STATUS status = MH_CreateHook(ZwQueryVirtualMemory, &DetourZwQueryVirtualMemory, reinterpret_cast(&oZwQueryVirtualMemory)); status != MH_OK) { 54 | cout << "Error at MH_CreateHook: " << status << endl; 55 | return 1; 56 | } 57 | // same here 58 | if (MH_STATUS status = MH_EnableHook(ZwQueryVirtualMemory); status != MH_OK) { 59 | cout << "Error at MH_EnableHook: " << status << endl; 60 | return 1; 61 | } 62 | else { 63 | cout << "Hook has been placed." << endl; 64 | } 65 | 66 | while(true) 67 | { 68 | if (GetAsyncKeyState(VK_DELETE) & 1) { 69 | if (MH_DisableHook(ZwQueryVirtualMemory) != MH_OK) { 70 | cout << "Error at MH_DisableHook" << endl; 71 | } 72 | break; 73 | } 74 | Sleep(10); 75 | } 76 | FreeLibraryAndExitThread(static_cast(lpParameter), EXIT_SUCCESS); 77 | } 78 | 79 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) 80 | { 81 | if (dwReason == DLL_PROCESS_ATTACH) 82 | { 83 | DisableThreadLibraryCalls(hModule); 84 | 85 | HANDLE hThread = CreateThread(nullptr, 0, fMain, hModule, 0, nullptr); 86 | if (hThread) 87 | { 88 | CloseHandle(hThread); 89 | } 90 | } else if (dwReason == DLL_PROCESS_DETACH && !lpReserved) { 91 | Detach(); 92 | } 93 | return TRUE; 94 | } -------------------------------------------------------------------------------- /NullifyZwDetectMemoryRead/dllmain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using ZwQueryVirtualMemory_t = NTSTATUS(__fastcall*) (HANDLE, PVOID, int, PVOID, SIZE_T, PSIZE_T); 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZwDetectMemoryRead 2 | 3 | This is based on this [gist](https://gist.github.com/dkrutsko/d6118638b0ef711b30bfcfe5b083d067) by [@dkrutsko](https://github.com/dkrutsko). all credit should off to him. 4 | 5 | While this gist is approaching with [QueryWorkingSetEx](https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-queryworkingsetex), 6 | this piece of code is using [ZwQueryVirtualMemory](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwqueryvirtualmemory). 7 | What got my attention is, official document says ZwQueryVirtualMemory solely support MEMORY_BASIC_INFORMATION, but actually supports PSAPI_WORKING_SET_EX_INFORMATION under the hood. I managed to figure it out by observing it with debugger. 8 | 9 | It must not be something new technique but was cool for me anyways. 10 | 11 | # How to see this works in action 12 | 13 | This program first allocates arbitrary 0x1000 size memory and print the address, so u read it with whatever way. either RPM or internally get the pointer of the address and dereference it. Then this program will detect the read operation. 14 | -------------------------------------------------------------------------------- /ZwDetectMemoryRead/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | #define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 ) 9 | 10 | // can be __stdcall for some reason. 11 | using ZwQueryVirtualMemory_t = NTSTATUS(__fastcall*) (HANDLE, PVOID, int, PVOID, SIZE_T, PSIZE_T); 12 | 13 | int main() 14 | { 15 | auto address = VirtualAlloc (nullptr, 0x1000, 16 | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 17 | 18 | cout << "read the address --> 0x" << hex << address << endl; 19 | 20 | HMODULE ntdll = GetModuleHandle("ntdll.dll"); 21 | 22 | auto ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)GetProcAddress(ntdll, "ZwQueryVirtualMemory"); 23 | 24 | if (!ZwQueryVirtualMemory) { 25 | cout << "ZwQueryVirtualMemory pointer couldn't be retrieved" << endl; 26 | } 27 | 28 | while (true) { 29 | PSAPI_WORKING_SET_EX_INFORMATION info = { 0 }; 30 | info.VirtualAddress = address; 31 | 32 | // 4 is the indicies indicates MemoryWorkingSetList 33 | auto status = ZwQueryVirtualMemory(NtCurrentProcess(), NULL, 4, &info, sizeof info, NULL); 34 | 35 | // if Valid is 1, it means memory read has been detected 36 | if (info.VirtualAttributes.Valid) 37 | cout << "the memory has been read." << endl; 38 | 39 | if(GetAsyncKeyState(VK_F1) & 1) { 40 | break; 41 | } 42 | Sleep(300); 43 | } 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /ZwDetectMemoryRead/main.h: -------------------------------------------------------------------------------- 1 | // Target.h : Include file for standard system include files, 2 | // or project specific include files. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // TODO: Reference additional headers your program requires here. 9 | --------------------------------------------------------------------------------