diff --git a/README.md b/README.md index 0285271..3731bb9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# NTDLL Integrity Checker +# Usermode rootkit detector This project is designed to inspect and compare the NTDLL memory sections of running processes against the disk version of `ntdll.dll` in Windows systems. Its primary goal is to detect potential modifications indicative of usermode rootkits. Since the only way for usermode rootkit to exist is hooking NtResumeThread, or its neighbouring functions during thread creation to achieve persistence, checking ntdll's integrity is a viable way of observing rootkit's presence. Other methods of persistence include patching import table, however it is not reliable, since ntdll does not have an import table, thus leaving the .text section the only candidate for usermode hooks. @@ -7,12 +7,13 @@ This project is designed to inspect and compare the NTDLL memory sections of run - Parses `ntdll.dll` directly from disk to retrieve the `.text` section. - Parses the `.text` section of `ntdll.dll` loaded in each running process's memory. - Compares these sections to identify discrepancies. +- Works for both wow64 and 64bit processes. - Provides a summary of potentially patched processes, aiding in the detection of usermode rootkits. ## Requirements - Windows operating system. -- Administrator privileges for process memory inspection. +- (Optional) Administrator privileges for process memory inspection. ## Building @@ -22,4 +23,10 @@ This project is designed to inspect and compare the NTDLL memory sections of run 2. Compile either via a provided make.bat (gcc toolchain) or on your own with msvc or something. +## Notes + +Tested against publicly available usermode rootkits: + +- [r77](https://bytecode77.com/) +- [My own one](https://github.com/forentfraps/rootkit-userland), since it does not replicate itself, however it does detect the altered processes diff --git a/main.c b/main.c index f9a5f28..586a066 100644 --- a/main.c +++ b/main.c @@ -499,42 +499,6 @@ int iterate_processes(unsigned char *stock_text64, int stock_size64, } return 0; } -BOOL SetDebugPrivileges() { - HANDLE hToken = NULL; - TOKEN_PRIVILEGES tokenPrivileges; - - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { - printf("Failed to open process token. Error: %lu\n", GetLastError()); - return FALSE; - } - - if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, - &tokenPrivileges.Privileges[0].Luid)) { - printf("Failed to lookup privilege value. Error: %lu\n", GetLastError()); - CloseHandle(hToken); - return FALSE; - } - - tokenPrivileges.PrivilegeCount = 1; - tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, - (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) { - printf("Failed to adjust token privileges. Error: %lu\n", GetLastError()); - CloseHandle(hToken); - return FALSE; - } - - if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { - printf("The token does not have the specified privilege.\n"); - CloseHandle(hToken); - return FALSE; - } - - CloseHandle(hToken); // Close the token handle - return TRUE; // Successfully enabled debug privileges -} BOOL ReadRemoteProcessMemory(DWORD processId, LPCVOID baseAddress, LPVOID buffer, SIZE_T size) { @@ -557,20 +521,12 @@ BOOL ReadRemoteProcessMemory(DWORD processId, LPCVOID baseAddress, CloseHandle(processHandle); return TRUE; } -int main1() { - void *buffer = HeapAlloc(GetProcessHeap(), 0, 200); - ReadRemoteProcessMemory(11180, (LPCVOID)0x76f90000, buffer, 200); -} - int main() { - void *virt_text; void *stock_text32; void *stock_text64; - int virt_size; int stock_size32; int stock_size64; DWORD base_addr32; - // SetDebugPrivileges(); parse_disk_ntdll64(&stock_text64, &stock_size64); parse_disk_ntdll32(&stock_text32, &stock_size32, &base_addr32);