├── APIMonitor ├── lib │ └── APIMonitor ├── makefile.mak └── src │ └── APIMonitor.cpp ├── Client ├── lib │ ├── APIEventsProcessor │ ├── Client │ ├── KernelEventsProcessor │ ├── LogParser │ ├── SQLDriver │ └── Util ├── makefile.mak └── src │ ├── APIEventsProcessor.cpp │ ├── Client.cpp │ ├── KernelEventsProcessor.cpp │ ├── LogParser.cpp │ ├── SQLDriver.cpp │ └── Util.cpp ├── CompileAndDeployClient.cmd ├── CompileAndDeployDriver.cmd ├── Driver ├── lib │ ├── FileSystemMonitor.h │ ├── KernelWhispererDriver.h │ ├── LoadImageMonitor.h │ ├── NetworkMonitor.h │ ├── ObjectMonitor.h │ ├── ProcessMonitor.h │ ├── RegistryMonitor.h │ └── Util.h └── src │ ├── FileSystemMonitor.c │ ├── KernelWhispererDriver.c │ ├── LoadImageMonitor.c │ ├── NetworkMonitor.c │ ├── ObjectMonitor.c │ ├── ProcessMonitor.c │ ├── RegistryMonitor.c │ ├── Util.c │ ├── makefile.def │ └── sources ├── KernelWhisperer.inf ├── Readme.md └── Server └── Server.py /APIMonitor/lib/APIMonitor: -------------------------------------------------------------------------------- 1 | BOOL sendAPIEvent(wchar_t* apiEventString); 2 | BOOL WINAPI dWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer,SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); 3 | BOOL WINAPI dAdjustTokenPrivileges(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength); 4 | BOOL detoursAttach(PVOID* pointerToRealFunction, PVOID pointerToProxy); 5 | BOOL detoursDetach(PVOID* pointerToRealFunction, PVOID pointerToProxy); 6 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); -------------------------------------------------------------------------------- /APIMonitor/makefile.mak: -------------------------------------------------------------------------------- 1 | all: clean apimonitor.dll 2 | 3 | clean: 4 | del /S /Q bin\* 5 | 6 | apimonitor.dll: 7 | cl /D_USRDLL /D_WINDLL /EHsc /I lib\ src\*.cpp /link /DLL /out:bin\apimonitor.dll 8 | 9 | -------------------------------------------------------------------------------- /APIMonitor/src/APIMonitor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "detours.h" 5 | #include "APIMonitor" 6 | 7 | #pragma comment(lib, "detours.lib") 8 | #pragma comment(lib, "advapi32.lib") 9 | 10 | using namespace std; 11 | 12 | #define MAX_ERROR_MESSAGE_SIZE 200 13 | #define MUTEX_NAME "KernelWhispererDetoursMutex" 14 | 15 | static HANDLE mutex; 16 | 17 | BOOL sendAPIEvent(wchar_t* apiEventString){ 18 | 19 | DWORD writtenBytes; 20 | BOOL result; 21 | std::ostringstream errorStringStream; 22 | std::wstringstream eventStringStream; 23 | FILETIME timeStampFT; 24 | ULONGLONG timeStamp; 25 | std::wstring finalEventString; 26 | HANDLE hSlot; 27 | DWORD waitResult; 28 | 29 | waitResult = WaitForSingleObject(mutex, INFINITE); 30 | 31 | switch(waitResult){ 32 | case WAIT_FAILED: 33 | OutputDebugString("APIMonitor->sendAPIEvent->WaitForSingleObject failed: WAIT_FAILED"); 34 | return FALSE; 35 | break; 36 | case WAIT_ABANDONED: 37 | OutputDebugString("APIMonitor->sendAPIEvent->WaitForSingleObject failed: WAIT_ABANDONED"); 38 | return FALSE; 39 | break; 40 | } 41 | 42 | hSlot = CreateFile("\\\\.\\mailslot\\kw_mailslot", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); 43 | if(hSlot == INVALID_HANDLE_VALUE){ 44 | errorStringStream << "APIMonitor->sendAPIEvent->CreateFile failed:" << std::hex << GetLastError(); 45 | OutputDebugString((LPCSTR) errorStringStream.str().c_str()); 46 | if(!ReleaseMutex(mutex)){ 47 | OutputDebugString("APIMonitor->sendAPIEvent->ReleaseMutex failed."); 48 | } 49 | return FALSE; 50 | } 51 | 52 | 53 | GetSystemTimeAsFileTime(&timeStampFT); 54 | timeStamp = (((ULONGLONG) timeStampFT.dwHighDateTime) << 32) + timeStampFT.dwLowDateTime; 55 | eventStringStream << L"API<-->" << timeStamp << L"<-->" << GetCurrentProcessId() << L"<-->" << std::wstring(apiEventString); 56 | finalEventString = eventStringStream.str(); 57 | result = WriteFile(hSlot, (LPCVOID) finalEventString.c_str(), (finalEventString.size()+1)*sizeof(wchar_t), &writtenBytes, (LPOVERLAPPED) NULL); 58 | 59 | if(!result){ 60 | errorStringStream << "APIMonitor->sendAPIEvent->WriteFile failed:" << std::hex << GetLastError(); 61 | OutputDebugString((LPCSTR) errorStringStream.str().c_str()); 62 | CloseHandle(hSlot); 63 | if(!ReleaseMutex(mutex)){ 64 | OutputDebugString("APIMonitor->sendAPIEvent->ReleaseMutex failed."); 65 | } 66 | return FALSE; 67 | } 68 | 69 | CloseHandle(hSlot); 70 | if(!ReleaseMutex(mutex)){ 71 | OutputDebugString("APIMonitor->sendAPIEvent->ReleaseMutex failed."); 72 | } 73 | return TRUE; 74 | } 75 | 76 | 77 | static BOOL (WINAPI *pWriteProcessMemory)(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer,SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten) = WriteProcessMemory; 78 | static BOOL (WINAPI * pAdjustTokenPrivileges)(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength) = AdjustTokenPrivileges; 79 | 80 | 81 | //BOOL WINAPI WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer,SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); 82 | BOOL WINAPI dWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer,SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten){ 83 | 84 | BOOL res = FALSE; 85 | OutputDebugString("WriteProcessMemory called.\n"); 86 | sendAPIEvent(L"WriteProcessMemory"); 87 | res = pWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); 88 | return res; 89 | 90 | } 91 | 92 | 93 | 94 | //BOOL WINAPI AdjustTokenPrivileges(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength,PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength); 95 | BOOL WINAPI dAdjustTokenPrivileges(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength){ 96 | 97 | BOOL res = FALSE; 98 | OutputDebugString("AdjustTokenPrivileges called.\n"); 99 | sendAPIEvent(L"AdjustTokenPrivileges"); 100 | res = pAdjustTokenPrivileges(TokenHandle, DisableAllPrivileges, NewState, BufferLength, PreviousState, ReturnLength); 101 | return res; 102 | 103 | } 104 | 105 | 106 | 107 | BOOL detoursAttach(PVOID* pointerToRealFunction, PVOID pointerToProxy){ 108 | 109 | DetourTransactionBegin(); 110 | 111 | DetourUpdateThread(GetCurrentThread()); 112 | DetourAttach(pointerToRealFunction, pointerToProxy); 113 | 114 | if (DetourTransactionCommit() != NO_ERROR){ 115 | OutputDebugString("APIMonitor->detoursAttach->DetourTransactionCommit failed."); 116 | return FALSE; 117 | } 118 | 119 | return TRUE; 120 | } 121 | 122 | 123 | BOOL detoursDetach(PVOID* pointerToRealFunction, PVOID pointerToProxy){ 124 | 125 | DetourTransactionBegin(); 126 | 127 | DetourUpdateThread(GetCurrentThread()); 128 | DetourDetach(pointerToRealFunction, pointerToProxy); 129 | 130 | if (DetourTransactionCommit() != NO_ERROR){ 131 | OutputDebugString("APIMonitor->detoursDetach->DetourTransactionCommit failed."); 132 | return FALSE; 133 | } 134 | 135 | return TRUE; 136 | } 137 | 138 | 139 | //AdjustTokenPrivileges 140 | 141 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){ 142 | 143 | std::ostringstream errorStringStream; 144 | 145 | if (DetourIsHelperProcess()) { 146 | return TRUE; 147 | } 148 | 149 | switch(fdwReason){ 150 | case DLL_PROCESS_ATTACH: 151 | OutputDebugString("Attached\n"); 152 | mutex = CreateMutex(NULL, FALSE, MUTEX_NAME); 153 | //If the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function. 154 | if(mutex == NULL){ 155 | errorStringStream << "APIMonitor->DllMain->CreateMutex failed:" << std::hex << GetLastError(); 156 | OutputDebugString(errorStringStream.str().c_str()); 157 | if(GetLastError() == ERROR_ACCESS_DENIED) 158 | { 159 | mutex = OpenMutex(SYNCHRONIZE ,FALSE, MUTEX_NAME); 160 | if(mutex == NULL){ 161 | errorStringStream.str(""); 162 | errorStringStream << "APIMonitor->DllMain->OpenMutex failed:" << std::hex << GetLastError(); 163 | OutputDebugString(errorStringStream.str().c_str()); 164 | return FALSE; 165 | } 166 | } 167 | else{ 168 | return FALSE; 169 | } 170 | } 171 | 172 | DisableThreadLibraryCalls(hinstDLL); 173 | DetourRestoreAfterWith(); 174 | 175 | if(!detoursAttach((PVOID*)&pWriteProcessMemory, dWriteProcessMemory)){ 176 | OutputDebugString("APIMonitor->DllMain(DLL_PROCESS_ATTACH)->detoursAttach(WriteProcessMemory) failed.\n"); 177 | } 178 | 179 | if(!detoursAttach((PVOID*)&pAdjustTokenPrivileges, dAdjustTokenPrivileges)){ 180 | OutputDebugString("APIMonitor->DllMain(DLL_PROCESS_ATTACH)->detoursDetach(AdjustTokenPrivileges) failed.\n"); 181 | } 182 | 183 | OutputDebugString("Committed Transaction (Attached).\n"); 184 | break; 185 | case DLL_PROCESS_DETACH: 186 | OutputDebugString("Detached\n"); 187 | 188 | if(!detoursDetach((PVOID*)&pWriteProcessMemory, dWriteProcessMemory)){ 189 | OutputDebugString("APIMonitor->DllMain(DLL_PROCESS_DETACH)->detoursDetach(WriteProcessMemory) failed.\n"); 190 | } 191 | 192 | 193 | if(!detoursDetach((PVOID*)&pAdjustTokenPrivileges, dAdjustTokenPrivileges)){ 194 | OutputDebugString("APIMonitor->DllMain(DLL_PROCESS_DETACH)->detoursDetach(AdjustTokenPrivileges) failed.\n"); 195 | } 196 | 197 | OutputDebugString("Committed Transaction (Detached).\n"); 198 | break; 199 | 200 | 201 | 202 | } 203 | return TRUE; 204 | 205 | 206 | } -------------------------------------------------------------------------------- /Client/lib/APIEventsProcessor: -------------------------------------------------------------------------------- 1 | class APIEventsProcessor{ 2 | 3 | public: 4 | static int run(DWORD currentPid, std::wstring hostNameWide); 5 | 6 | 7 | 8 | 9 | }; -------------------------------------------------------------------------------- /Client/lib/Client: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | void sqlInsertProxy(std::vector logComponents, DWORD currentPid); 7 | int main(); 8 | 9 | -------------------------------------------------------------------------------- /Client/lib/KernelEventsProcessor: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sqldriver" 6 | using namespace std; 7 | 8 | class KernelEventsProcessor{ 9 | 10 | public: 11 | static int run(DWORD currentPid, std::wstring hostNameWide); 12 | 13 | }; -------------------------------------------------------------------------------- /Client/lib/LogParser: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | 7 | class LogParser{ 8 | 9 | public: 10 | static std::vector parse(std::wstring logLine); 11 | private: 12 | static std::vector parseRegistryLog(std::wstring logLine); 13 | static std::vector parseFileSystemLog(std::wstring logLine); 14 | static std::vector parseNetworkLog(std::wstring logLine); 15 | static std::vector LogParser::parseProcessLog(std::wstring logLine); 16 | static std::vector LogParser::parseObjectLog(std::wstring logLine); 17 | static std::vector LogParser::parseLoadImageLog(std::wstring logLine); 18 | static std::vector LogParser::parseAPILog(std::wstring logLine); 19 | }; -------------------------------------------------------------------------------- /Client/lib/SQLDriver: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | #define DB_IP "192.168.20.135" 7 | #define DB_PORT 5000 8 | 9 | //MYSQL 10 | #define DB_INIT L"DROP DATABASE IF EXISTS Events; CREATE DATABASE IF NOT EXISTS Events; USE Events; CREATE TABLE Registry(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), Type VARCHAR(30), RegKey VARCHAR(200), Value VARCHAR(200), Data VARCHAR(500), PRIMARY KEY(ID)); CREATE TABLE File(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), Type VARCHAR(30), File VARCHAR(200), PRIMARY KEY(ID)); CREATE TABLE Network(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), Protocol VARCHAR(6), Type VARCHAR(15), LocalIP VARCHAR(15), LocalPort INTEGER, RemoteIP VARCHAR(15), RemotePort INTEGER, PRIMARY KEY(ID));CREATE TABLE Process(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), Pstatus Char(1), CommandLine VARCHAR(200), PRIMARY KEY(ID));CREATE TABLE Objects(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), ObjPid INTEGER, ObjImageFilePath VARCHAR(200), ObjType VARCHAR(30), HandleOperation VARCHAR(50), Permissions VARCHAR(500), PRIMARY KEY(ID));CREATE TABLE ImageLoads(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), HostProcessPid INTEGER, HostProcessImageFilePath VARCHAR(200), LoadedImage VARCHAR(200), PRIMARY KEY(ID)); CREATE TABLE API(ID INTEGER NOT NULL AUTO_INCREMENT, Timestamp BIGINT, Hostname VARCHAR(50), PPid INTEGER, PImageFilePath VARCHAR(200), Pid INTEGER, ImageFilePath VARCHAR(200), Function VARCHAR(50), PRIMARY KEY(ID));" 11 | 12 | #define FORMAT_DB_INSERT_REGISTRY_EVENT_FIRST L"Use Events; INSERT INTO Registry (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Type, RegKey, Value, Data) VALUES (" 13 | #define FORMAT_DB_INSERT_REGISTRY_EVENT_SECOND ");" 14 | 15 | #define FORMAT_DB_INSERT_FILE_EVENT_FIRST L"Use Events; INSERT INTO File (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Type, File) VALUES (" 16 | #define FORMAT_DB_INSERT_FILE_EVENT_SECOND ");" 17 | 18 | #define FORMAT_DB_INSERT_NETWORK_EVENT_FIRST L"Use Events; INSERT INTO Network (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Protocol, Type, LocalIP, LocalPort, RemoteIP, RemotePort) VALUES (" 19 | #define FORMAT_DB_INSERT_NETWORK_EVENT_SECOND ");" 20 | 21 | #define FORMAT_DB_INSERT_PROCESS_EVENT_FIRST L"Use Events; INSERT INTO Process (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Pstatus, CommandLine) VALUES (" 22 | #define FORMAT_DB_INSERT_PROCESS_EVENT_SECOND ");" 23 | 24 | #define FORMAT_DB_INSERT_OBJECT_EVENT_FIRST L"Use Events; INSERT INTO Objects (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, ObjPid, ObjImageFilePath, ObjType, HandleOperation, Permissions) VALUES (" 25 | #define FORMAT_DB_INSERT_OBJECT_EVENT_SECOND ");" 26 | 27 | #define FORMAT_DB_INSERT_LOAD_IMAGE_EVENT_FIRST L"Use Events; INSERT INTO ImageLoads (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, HostProcessPid, HostProcessImageFilePath, LoadedImage) VALUES (" 28 | #define FORMAT_DB_INSERT_LOAD_IMAGE_EVENT_SECOND ");" 29 | 30 | #define FORMAT_DB_INSERT_API_EVENT_FIRST L"Use Events; INSERT INTO API (Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Function) VALUES (" 31 | #define FORMAT_DB_INSERT_API_EVENT_SECOND ");" 32 | 33 | class SQLDriver{ 34 | 35 | private: 36 | SQLDriver(); 37 | static SQLDriver* sqlDriverInstance; 38 | 39 | public: 40 | int sendCommand(const wchar_t* command, size_t stringSize); 41 | int initDB(); 42 | static SQLDriver* getInstance(); 43 | void sqlInsertProxy(std::vector logComponents, DWORD currentPid, std::wstring hostname); 44 | 45 | int insertRegistryEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring type, std::wstring key, std::wstring value, std::wstring data); 46 | 47 | int insertFileEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring type, std::wstring file); 48 | 49 | int insertNetworkEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring protocol, std::wstring type, std::wstring lIP, std::wstring lPort, std::wstring remoteIP, std::wstring remotePort); 50 | 51 | int insertProcessEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring pStatus, std::wstring commandLine); 52 | 53 | int insertObjectEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring objPid, std::wstring objImageFilePath, std::wstring objType, std::wstring handleOperation, std::wstring permissions); 54 | 55 | int insertLoadImageEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring hostProcessPid, std::wstring hostProcessImageFilePath, std::wstring loadedImage); 56 | 57 | int insertAPIEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring function); 58 | }; 59 | 60 | -------------------------------------------------------------------------------- /Client/lib/Util: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | 6 | class Util { 7 | 8 | public: 9 | static std::wstring getImagePath(unsigned long long pid); 10 | static unsigned long long getParentPid(unsigned long long pid); 11 | static void setDebugPrivilege(); 12 | static std::wstring escapeSpecialCharacters(std::wstring wst); 13 | 14 | }; -------------------------------------------------------------------------------- /Client/makefile.mak: -------------------------------------------------------------------------------- 1 | all: clean client.exe 2 | 3 | clean: 4 | del /S /Q bin\* 5 | 6 | client.exe: 7 | cl /EHsc /I lib\ src\*.cpp /link /out:bin\client.exe 8 | 9 | -------------------------------------------------------------------------------- /Client/src/APIEventsProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sqldriver" 6 | #include "logparser" 7 | #include "apieventsprocessor" 8 | 9 | using namespace std; 10 | 11 | #define MAX_MESSAGE_SIZE 500 12 | 13 | int APIEventsProcessor::run(DWORD currentPid, std::wstring hostNameWide){ 14 | 15 | HANDLE hSLot; 16 | HANDLE hEvent; 17 | DWORD nextMessageSize; 18 | DWORD pendingMessages; 19 | DWORD numberOfBytesRead; 20 | wchar_t message[MAX_MESSAGE_SIZE] = {'\0'}; 21 | OVERLAPPED ov; 22 | BOOL result; 23 | DWORD error; 24 | EXPLICIT_ACCESS explicitAccessArray[1] = {'\0'}; 25 | SECURITY_ATTRIBUTES msSecurityAttributes = {'\0'}; 26 | SECURITY_DESCRIPTOR msSecurityDescriptor = {'\0'}; 27 | SID_IDENTIFIER_AUTHORITY sidAuthWorld = SECURITY_WORLD_SID_AUTHORITY; 28 | PSID pEveryoneSID; 29 | PACL pAcl; 30 | 31 | SID_IDENTIFIER_AUTHORITY sidMLA = SECURITY_MANDATORY_LABEL_AUTHORITY; 32 | PSID pLowIntegritySID; 33 | PACL pSACL; 34 | DWORD aclLength; 35 | 36 | 37 | result = InitializeSecurityDescriptor(&msSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); 38 | if(!result){ 39 | std::cout << "APIEventsProcessor->run->InitializeSecurityDescriptor failed:" << std::hex << GetLastError() << std::endl; 40 | return 1; 41 | } 42 | 43 | result = AllocateAndInitializeSid(&sidAuthWorld, 1,SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0,&pEveryoneSID); 44 | if(!result){ 45 | std::cout << "APIEventsProcessor->run->AllocateAndInitializeSid failed:" << std::hex << GetLastError() << std::endl; 46 | return 1; 47 | } 48 | 49 | 50 | explicitAccessArray[0].grfAccessPermissions = (GENERIC_READ | GENERIC_WRITE); 51 | explicitAccessArray[0].grfAccessMode = SET_ACCESS; 52 | explicitAccessArray[0].grfInheritance= NO_INHERITANCE; 53 | explicitAccessArray[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; 54 | explicitAccessArray[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; 55 | explicitAccessArray[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID; 56 | 57 | 58 | error = SetEntriesInAcl(1, explicitAccessArray, NULL, &pAcl); 59 | if(error != ERROR_SUCCESS){ 60 | std::cout << "APIEventsProcessor->run->SetEntriesInAcl failed:" << std::hex << GetLastError() << std::endl; 61 | if(pEveryoneSID) 62 | FreeSid(pEveryoneSID); 63 | if(pAcl) 64 | LocalFree(pAcl); 65 | return 1; 66 | } 67 | 68 | result = SetSecurityDescriptorDacl(&msSecurityDescriptor, TRUE, pAcl, FALSE); 69 | 70 | if(!result){ 71 | std::cout << "APIEventsProcessor->run->SetSecurityDescriptorDacl failed:" << std::hex << GetLastError() << std::endl; 72 | if(pEveryoneSID) 73 | FreeSid(pEveryoneSID); 74 | if(pAcl) 75 | LocalFree(pAcl); 76 | return 1; 77 | } 78 | 79 | /* 80 | Some processes like Internet Explorer spawn child processes with low-integrity. We need to account for those 81 | by allowing low-integrity processes to access the mailslot. We need to configure a SACL. 82 | 83 | */ 84 | 85 | result = AllocateAndInitializeSid(&sidMLA, 1, 0x1000, 0, 0, 0, 0, 0, 0, 0, &pLowIntegritySID); 86 | if(!result){ 87 | std::cout << "APIEventsProcessor->run->AllocateAndInitializeSid failed:" << std::hex << GetLastError() << std::endl; 88 | if(pEveryoneSID) 89 | FreeSid(pEveryoneSID); 90 | if(pAcl) 91 | LocalFree(pAcl); 92 | return 1; 93 | } 94 | 95 | aclLength = sizeof(ACL) + sizeof(SYSTEM_MANDATORY_LABEL_ACE) + GetLengthSid(pLowIntegritySID) - sizeof(DWORD); 96 | pSACL = (PACL) LocalAlloc(LPTR, aclLength); 97 | 98 | if(pSACL == NULL){ 99 | std::cout << "APIEventsProcessor->run->LocalAlloc failed:" << std::hex << GetLastError() << std::endl; 100 | if(pEveryoneSID) 101 | FreeSid(pEveryoneSID); 102 | if(pAcl) 103 | LocalFree(pAcl); 104 | if(pLowIntegritySID) 105 | FreeSid(pLowIntegritySID); 106 | return 1; 107 | } 108 | 109 | result = InitializeAcl(pSACL, aclLength, ACL_REVISION); 110 | if(!result){ 111 | std::cout << "APIEventsProcessor->run->InitializeAcl failed:" << std::hex << GetLastError() << std::endl; 112 | if(pEveryoneSID) 113 | FreeSid(pEveryoneSID); 114 | if(pAcl) 115 | LocalFree(pAcl); 116 | if(pLowIntegritySID) 117 | FreeSid(pLowIntegritySID); 118 | if(pSACL) 119 | LocalFree(pSACL); 120 | return 1; 121 | } 122 | 123 | result = AddMandatoryAce(pSACL, ACL_REVISION, 0, 0, pLowIntegritySID); 124 | if(!result){ 125 | std::cout << "APIEventsProcessor->run->AddMandatoryAce failed:" << std::hex << GetLastError() << std::endl; 126 | if(pEveryoneSID) 127 | FreeSid(pEveryoneSID); 128 | if(pAcl) 129 | LocalFree(pAcl); 130 | if(pLowIntegritySID) 131 | FreeSid(pLowIntegritySID); 132 | if(pSACL) 133 | LocalFree(pSACL); 134 | return 1; 135 | } 136 | 137 | result = SetSecurityDescriptorSacl(&msSecurityDescriptor, TRUE, pSACL, FALSE); 138 | if(!result){ 139 | std::cout << "APIEventsProcessor->run->SetSecurityDescriptorSacl failed:" << std::hex << GetLastError() << std::endl; 140 | if(pEveryoneSID) 141 | FreeSid(pEveryoneSID); 142 | if(pAcl) 143 | LocalFree(pAcl); 144 | if(pLowIntegritySID) 145 | FreeSid(pLowIntegritySID); 146 | if(pSACL) 147 | LocalFree(pSACL); 148 | return 1; 149 | } 150 | 151 | msSecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); 152 | msSecurityAttributes.lpSecurityDescriptor = &msSecurityDescriptor; 153 | msSecurityAttributes.bInheritHandle = FALSE; 154 | 155 | hSLot = CreateMailslot("\\\\.\\mailslot\\kw_mailslot", 0, MAILSLOT_WAIT_FOREVER, &msSecurityAttributes); 156 | 157 | if(hSLot == INVALID_HANDLE_VALUE){ 158 | std::cout << "APIEventsProcessor->run->CreateMailslot failed:" << std::hex << GetLastError() << std::endl; 159 | if(pEveryoneSID) 160 | FreeSid(pEveryoneSID); 161 | if(pAcl) 162 | LocalFree(pAcl); 163 | if(pLowIntegritySID) 164 | FreeSid(pLowIntegritySID); 165 | if(pSACL) 166 | LocalFree(pSACL); 167 | return 1; 168 | } 169 | 170 | hEvent = CreateEvent(NULL, FALSE, FALSE, "KWMailSlotEvent"); 171 | if(hEvent == NULL){ 172 | std::cout << "APIEventsProcessor->run->CreateEvent failed:" << std::hex << GetLastError() << std::endl; 173 | if(pEveryoneSID) 174 | FreeSid(pEveryoneSID); 175 | if(pAcl) 176 | LocalFree(pAcl); 177 | if(pLowIntegritySID) 178 | FreeSid(pLowIntegritySID); 179 | if(pSACL) 180 | LocalFree(pSACL); 181 | return 1; 182 | } 183 | 184 | ov.Offset = 0; 185 | ov.OffsetHigh = 0; 186 | ov.hEvent = hEvent; 187 | 188 | while(TRUE){ 189 | 190 | result = GetMailslotInfo(hSLot, (LPDWORD) NULL, &nextMessageSize, &pendingMessages, (LPDWORD) NULL); 191 | 192 | if(!result){ 193 | std::cout << "APIEventsProcessor->run->GetMailslotInfo failed: " << std::hex << GetLastError() << std::endl; 194 | continue; 195 | } 196 | 197 | 198 | if((pendingMessages > 0) && (nextMessageSize > 0)){ 199 | std::cout << "Reading" << std::endl; 200 | result = ReadFile(hSLot, message, nextMessageSize, &numberOfBytesRead, &ov); 201 | if(!result){ 202 | std::cout << "APIEventsProcessor->run->ReadFile failed: " << std::hex << GetLastError() << std::endl; 203 | continue; 204 | } 205 | std::wcout << std::wstring(message) << std::endl; 206 | SQLDriver::getInstance()->sqlInsertProxy(LogParser::parse(std::wstring(message)), currentPid, hostNameWide); 207 | memset(message, 0, numberOfBytesRead); 208 | 209 | } 210 | 211 | } 212 | if(pEveryoneSID) 213 | FreeSid(pEveryoneSID); 214 | if(pAcl) 215 | LocalFree(pAcl); 216 | if(pLowIntegritySID) 217 | FreeSid(pLowIntegritySID); 218 | if(pSACL) 219 | LocalFree(pSACL); 220 | 221 | 222 | return 0; 223 | } -------------------------------------------------------------------------------- /Client/src/Client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util" 6 | #include "kerneleventsprocessor" 7 | #include "apieventsprocessor" 8 | #include "client" 9 | 10 | using namespace std; 11 | 12 | #define MAX_HOSTNAME_BUFFER_SIZE 100 13 | #define MAX_HOSTNAME_BUFFER_SIZE_WIDE 200 14 | 15 | int main() 16 | { 17 | DWORD currentPid = GetCurrentProcessId(); 18 | SQLDriver* sqlDriver = SQLDriver::getInstance(); 19 | KernelEventsProcessor kernelEventsProcessor; 20 | APIEventsProcessor apiEventsProcessor; 21 | 22 | 23 | WSADATA wsaData; 24 | char hostName[MAX_HOSTNAME_BUFFER_SIZE] = {'\0'}; 25 | wchar_t hostNameWide[MAX_HOSTNAME_BUFFER_SIZE_WIDE] = {'\0'}; 26 | struct hostent* hEnt = NULL; 27 | std::ostringstream hostStringStream; 28 | 29 | // Initialize Winsock 30 | if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0){ 31 | std::cout << "KernelEventsProcessor->run->WSAStartup failed:" << std::hex << WSAGetLastError() << std::endl; 32 | return 1; 33 | } 34 | 35 | if(gethostname(hostName, MAX_HOSTNAME_BUFFER_SIZE) != 0){ 36 | 37 | std::cout << "KernelEventsProcessor->run->gethostname failed:" << std::hex << WSAGetLastError() << std::endl; 38 | return 1; 39 | } 40 | 41 | hEnt = gethostbyname(hostName); 42 | if(hEnt == NULL){ 43 | std::cout << "KernelEventsProcessor->run->gethostbyname failed." << std::endl; 44 | return 1; 45 | } 46 | 47 | WSACleanup(); 48 | 49 | hostStringStream << std::string(hEnt->h_name); 50 | 51 | if(mbstowcs(hostNameWide, hostStringStream.str().c_str(), MAX_HOSTNAME_BUFFER_SIZE_WIDE) <= 0) 52 | { 53 | std::cout << "KernelEventsProcessor->run->mbstowcs failed." << std::endl; 54 | return 1; 55 | } 56 | 57 | sqlDriver->initDB(); 58 | Util::setDebugPrivilege(); 59 | 60 | std::thread kEPThread (kernelEventsProcessor.run, currentPid, std::wstring(hostNameWide)); 61 | std::thread apiEPThread (apiEventsProcessor.run, currentPid, std::wstring(hostNameWide)); 62 | 63 | kEPThread.join(); 64 | apiEPThread.join(); 65 | } -------------------------------------------------------------------------------- /Client/src/KernelEventsProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "logparser" 7 | #include "util" 8 | #include "kerneleventsprocessor" 9 | 10 | using namespace std; 11 | 12 | #define IOCTL_KERNELWHISPERER_GETACTIVITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x700, METHOD_BUFFERED, FILE_ANY_ACCESS) 13 | #define MAX_LOG_BUFFER_SIZE 5000 14 | 15 | 16 | int KernelEventsProcessor::run(DWORD currentPid, std::wstring hostNameWide){ 17 | 18 | 19 | HANDLE hDevice; 20 | DWORD dwBytes; 21 | DWORD dwError = ERROR_SUCCESS; 22 | PWSTR driverRequestBuffer; 23 | 24 | 25 | driverRequestBuffer = (PWSTR) calloc(MAX_LOG_BUFFER_SIZE,1); 26 | 27 | Util::setDebugPrivilege(); 28 | 29 | hDevice = CreateFile("\\\\.\\KernelWhisperer", GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL); 30 | if (hDevice == INVALID_HANDLE_VALUE) 31 | { 32 | std::cout << "KernelEventsProcessor->run->CreateFile failed:" << std::hex << GetLastError() << std::endl; 33 | return dwError; 34 | } 35 | 36 | while(TRUE){ 37 | if (!DeviceIoControl(hDevice, IOCTL_KERNELWHISPERER_GETACTIVITY, NULL, 0, driverRequestBuffer, MAX_LOG_BUFFER_SIZE, &dwBytes, NULL)) 38 | { 39 | 40 | std::cout << "KernelEventsProcessor->run->DeviceIoControl failed:" << std::hex << GetLastError() << std::endl; 41 | CloseHandle(hDevice); 42 | return dwError; 43 | } 44 | //Dwords so at least two bytes. 45 | if((dwBytes > 2) && (dwBytes <= MAX_LOG_BUFFER_SIZE)){ 46 | SQLDriver::getInstance()->sqlInsertProxy(LogParser::parse(std::wstring(driverRequestBuffer)), currentPid, hostNameWide); 47 | memset(driverRequestBuffer, 0, dwBytes); 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /Client/src/LogParser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "logparser" 9 | #include "util" 10 | 11 | using namespace std; 12 | 13 | 14 | std::vector LogParser::parse(std::wstring logLine){ 15 | 16 | std::vector emptyVector; 17 | 18 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 19 | std::wsmatch match; 20 | bool hasMatch = false; 21 | 22 | hasMatch = std::regex_search(logLine,match,stringSeparatorRegex); 23 | 24 | if(hasMatch == false){ 25 | cout << "LogParser->parse->find error: Pipe not found on log." << endl; 26 | return emptyVector; 27 | 28 | } 29 | 30 | if(match.prefix().str().compare(L"REG") == 0){ 31 | 32 | return parseRegistryLog(logLine); 33 | 34 | } else if (match.prefix().str().compare(L"FILE") == 0){ 35 | 36 | return parseFileSystemLog(logLine); 37 | 38 | } else if (match.prefix().str().compare(L"NET") == 0){ 39 | 40 | return parseNetworkLog(logLine); 41 | } 42 | else if(match.prefix().str().compare(L"PROC") == 0){ 43 | 44 | return parseProcessLog(logLine); 45 | } 46 | 47 | else if(match.prefix().str().compare(L"OBJECT") == 0){ 48 | 49 | return parseObjectLog(logLine); 50 | } 51 | 52 | else if(match.prefix().str().compare(L"LOADIMAGE") == 0){ 53 | 54 | return parseLoadImageLog(logLine); 55 | } 56 | 57 | else if(match.prefix().str().compare(L"API") == 0){ 58 | 59 | return parseAPILog(logLine); 60 | } 61 | 62 | else{ 63 | cout << "LogParser->parse->compare error: Invalid log type." << endl; 64 | return emptyVector; 65 | } 66 | 67 | return emptyVector; 68 | 69 | } 70 | 71 | std::vector LogParser::parseRegistryLog(std::wstring logLine){ 72 | 73 | std::vector logComponents; 74 | int separatorIndex = 0; 75 | int substrBegin = 0; 76 | std::wstring tempString; 77 | 78 | std::wstring::const_iterator matchBegin = logLine.begin(); 79 | std::wstring::const_iterator matchEnd = logLine.end(); 80 | 81 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 82 | std::wsmatch match; 83 | bool hasMatch = false; 84 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 85 | 86 | separatorIndex = match.position(0); 87 | tempString = logLine.substr(substrBegin, separatorIndex); 88 | tempString = Util::escapeSpecialCharacters(tempString); 89 | logComponents.push_back(tempString); 90 | substrBegin += (separatorIndex + 4); 91 | matchBegin += (separatorIndex + 4); 92 | } 93 | 94 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 95 | tempString = Util::escapeSpecialCharacters(tempString); 96 | logComponents.push_back(tempString); 97 | return logComponents; 98 | } 99 | 100 | 101 | std::vector LogParser::parseFileSystemLog(std::wstring logLine){ 102 | 103 | std::vector logComponents; 104 | int separatorIndex = 0; 105 | int substrBegin = 0; 106 | std::wstring tempString; 107 | 108 | std::wstring::const_iterator matchBegin = logLine.begin(); 109 | std::wstring::const_iterator matchEnd = logLine.end(); 110 | 111 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 112 | std::wsmatch match; 113 | bool hasMatch = false; 114 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 115 | 116 | separatorIndex = match.position(0); 117 | tempString = logLine.substr(substrBegin, separatorIndex); 118 | tempString = Util::escapeSpecialCharacters(tempString); 119 | logComponents.push_back(tempString); 120 | substrBegin += (separatorIndex + 4); 121 | matchBegin += (separatorIndex + 4); 122 | } 123 | 124 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 125 | tempString = Util::escapeSpecialCharacters(tempString); 126 | logComponents.push_back(tempString); 127 | return logComponents; 128 | 129 | 130 | 131 | } 132 | 133 | 134 | 135 | 136 | std::vector LogParser::parseNetworkLog(std::wstring logLine){ 137 | 138 | std::vector logComponents; 139 | int separatorIndex = 0; 140 | int substrBegin = 0; 141 | std::wstring tempString; 142 | 143 | std::wstring::const_iterator matchBegin = logLine.begin(); 144 | std::wstring::const_iterator matchEnd = logLine.end(); 145 | 146 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 147 | std::wsmatch match; 148 | bool hasMatch = false; 149 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 150 | 151 | separatorIndex = match.position(0); 152 | tempString = logLine.substr(substrBegin, separatorIndex); 153 | tempString = Util::escapeSpecialCharacters(tempString); 154 | logComponents.push_back(tempString); 155 | substrBegin += (separatorIndex + 4); 156 | matchBegin += (separatorIndex + 4); 157 | } 158 | 159 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 160 | tempString = Util::escapeSpecialCharacters(tempString); 161 | logComponents.push_back(tempString); 162 | return logComponents; 163 | 164 | 165 | 166 | } 167 | 168 | 169 | std::vector LogParser::parseProcessLog(std::wstring logLine){ 170 | 171 | 172 | std::vector logComponents; 173 | int separatorIndex = 0; 174 | int substrBegin = 0; 175 | std::wstring tempString; 176 | 177 | std::wstring::const_iterator matchBegin = logLine.begin(); 178 | std::wstring::const_iterator matchEnd = logLine.end(); 179 | 180 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 181 | std::wsmatch match; 182 | bool hasMatch = false; 183 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 184 | 185 | separatorIndex = match.position(0); 186 | tempString = logLine.substr(substrBegin, separatorIndex); 187 | tempString = Util::escapeSpecialCharacters(tempString); 188 | logComponents.push_back(tempString); 189 | substrBegin += (separatorIndex + 4); 190 | matchBegin += (separatorIndex + 4); 191 | } 192 | 193 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 194 | tempString = Util::escapeSpecialCharacters(tempString); 195 | logComponents.push_back(tempString); 196 | return logComponents; 197 | 198 | 199 | } 200 | 201 | 202 | std::vector LogParser::parseObjectLog(std::wstring logLine){ 203 | 204 | std::vector logComponents; 205 | int separatorIndex = 0; 206 | int substrBegin = 0; 207 | std::wstring tempString; 208 | 209 | std::wstring::const_iterator matchBegin = logLine.begin(); 210 | std::wstring::const_iterator matchEnd = logLine.end(); 211 | 212 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 213 | std::wsmatch match; 214 | bool hasMatch = false; 215 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 216 | 217 | separatorIndex = match.position(0); 218 | tempString = logLine.substr(substrBegin, separatorIndex); 219 | tempString = Util::escapeSpecialCharacters(tempString); 220 | logComponents.push_back(tempString); 221 | substrBegin += (separatorIndex + 4); 222 | matchBegin += (separatorIndex + 4); 223 | } 224 | 225 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 226 | tempString = Util::escapeSpecialCharacters(tempString); 227 | logComponents.push_back(tempString); 228 | return logComponents; 229 | } 230 | 231 | 232 | std::vector LogParser::parseLoadImageLog(std::wstring logLine){ 233 | 234 | std::vector logComponents; 235 | int separatorIndex = 0; 236 | int substrBegin = 0; 237 | std::wstring tempString; 238 | 239 | std::wstring::const_iterator matchBegin = logLine.begin(); 240 | std::wstring::const_iterator matchEnd = logLine.end(); 241 | 242 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 243 | std::wsmatch match; 244 | bool hasMatch = false; 245 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 246 | 247 | separatorIndex = match.position(0); 248 | tempString = logLine.substr(substrBegin, separatorIndex); 249 | tempString = Util::escapeSpecialCharacters(tempString); 250 | logComponents.push_back(tempString); 251 | substrBegin += (separatorIndex + 4); 252 | matchBegin += (separatorIndex + 4); 253 | } 254 | 255 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 256 | tempString = Util::escapeSpecialCharacters(tempString); 257 | logComponents.push_back(tempString); 258 | return logComponents; 259 | } 260 | 261 | 262 | std::vector LogParser::parseAPILog(std::wstring logLine){ 263 | 264 | std::vector logComponents; 265 | int separatorIndex = 0; 266 | int substrBegin = 0; 267 | std::wstring tempString; 268 | 269 | std::wstring::const_iterator matchBegin = logLine.begin(); 270 | std::wstring::const_iterator matchEnd = logLine.end(); 271 | 272 | std::wregex stringSeparatorRegex (std::wstring(L"<-->")); 273 | std::wsmatch match; 274 | bool hasMatch = false; 275 | while (std::regex_search(matchBegin, matchEnd, match, stringSeparatorRegex)){ 276 | 277 | separatorIndex = match.position(0); 278 | tempString = logLine.substr(substrBegin, separatorIndex); 279 | tempString = Util::escapeSpecialCharacters(tempString); 280 | logComponents.push_back(tempString); 281 | substrBegin += (separatorIndex + 4); 282 | matchBegin += (separatorIndex + 4); 283 | } 284 | 285 | tempString = logLine.substr(substrBegin, logLine.length() - substrBegin); 286 | tempString = Util::escapeSpecialCharacters(tempString); 287 | logComponents.push_back(tempString); 288 | return logComponents; 289 | } 290 | -------------------------------------------------------------------------------- /Client/src/SQLDriver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "util" 12 | #include "sqldriver" 13 | 14 | using namespace std; 15 | 16 | 17 | #define DB_RESPONSE_BUFFER_LENGTH 2000 18 | #define DB_QUERY_BUFFER_LENGTH 512 19 | 20 | #pragma comment (lib, "Ws2_32.lib") 21 | #pragma comment (lib, "Mswsock.lib") 22 | #pragma comment (lib, "AdvApi32.lib") 23 | 24 | static long regEntries = 0; 25 | static long fileEntries = 0; 26 | static long networkEntries = 0; 27 | static long processEntries = 0; 28 | static long objectEntries = 0; 29 | static long imageLoadEntries = 0; 30 | static long apiEntries = 0; 31 | 32 | SQLDriver* SQLDriver::sqlDriverInstance = NULL; 33 | 34 | SQLDriver::SQLDriver(){} 35 | 36 | SQLDriver* SQLDriver::getInstance(){ 37 | 38 | if(sqlDriverInstance == NULL) 39 | sqlDriverInstance = new SQLDriver(); 40 | return sqlDriverInstance; 41 | } 42 | 43 | 44 | void SQLDriver::sqlInsertProxy(std::vector logComponents, DWORD currentPid, std::wstring hostname){ 45 | 46 | unsigned long long parentPid; 47 | std::wstring pImagePath; 48 | std::wstring imagePath; 49 | 50 | unsigned long long tempPid; 51 | std::wstring tempImagePath; //This string will contain the path for the process associated with either the thread id or pid associated with the open handle. 52 | 53 | 54 | if(logComponents.size() == 0) 55 | return; 56 | 57 | 58 | 59 | if(currentPid == stoull(logComponents[2], NULL, 0)) 60 | return; 61 | 62 | parentPid = (stoull(logComponents[2]) != 0) ? Util::getParentPid(stoull(logComponents[2])) : 0; 63 | pImagePath = (parentPid != 0) ? Util::escapeSpecialCharacters(Util::getImagePath(parentPid)) : std::wstring(L""); 64 | imagePath = (stoull(logComponents[2], NULL, 0) != 0) ? Util::escapeSpecialCharacters(Util::getImagePath(stoull(logComponents[2], NULL, 0))) : std::wstring(L""); 65 | //MAKE SURE THAT THE NUMBER OF PARAMETERS IS CORRECT. ADD ANOTHER ELEMENT TO IF. 66 | if(logComponents[0].compare(std::wstring(L"REG")) == 0){ 67 | if (logComponents[3].compare(std::wstring(L"CREATEKEY")) == 0){ 68 | (SQLDriver::getInstance())->insertRegistryEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3], logComponents[4], std::wstring(L""), std::wstring(L"")); 69 | } 70 | else if (logComponents[3].compare(std::wstring(L"SETVALUE")) == 0){ 71 | (SQLDriver::getInstance())->insertRegistryEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3], logComponents[4], logComponents[5], logComponents[6]); 72 | } 73 | } 74 | else if(logComponents[0].compare(std::wstring(L"FILE")) == 0){ 75 | (SQLDriver::getInstance())->insertFileEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3], logComponents[4]); 76 | } 77 | else if(logComponents[0].compare(std::wstring(L"NET")) == 0){ 78 | (SQLDriver::getInstance())->insertNetworkEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3], logComponents[4], logComponents[5], logComponents[6], logComponents[7], logComponents[8]); 79 | } 80 | else if(logComponents[0].compare(std::wstring(L"PROC")) == 0){ 81 | (SQLDriver::getInstance())->insertProcessEvent(logComponents[1], hostname, logComponents[3], pImagePath, logComponents[2], Util::escapeSpecialCharacters(logComponents[4]), logComponents[6], Util::escapeSpecialCharacters(logComponents[5])); 82 | } 83 | 84 | else if(logComponents[0].compare(std::wstring(L"OBJECT")) == 0){ 85 | tempPid = stoull(logComponents[4]); 86 | tempImagePath = (tempPid != 0) ? Util::escapeSpecialCharacters(Util::getImagePath(tempPid)) : std::wstring(L""); 87 | (SQLDriver::getInstance())->insertObjectEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[4], tempImagePath, logComponents[3], logComponents[5], logComponents[6]); 88 | } 89 | 90 | else if(logComponents[0].compare(std::wstring(L"LOADIMAGE")) == 0){ 91 | tempPid = stoull(logComponents[3]); 92 | tempImagePath = (tempPid != 0) ? Util::escapeSpecialCharacters(Util::getImagePath(tempPid)) : std::wstring(L""); 93 | (SQLDriver::getInstance())->insertLoadImageEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3], tempImagePath, logComponents[4]); 94 | } 95 | 96 | else if(logComponents[0].compare(std::wstring(L"API")) == 0){ 97 | (SQLDriver::getInstance())->insertAPIEvent(logComponents[1], hostname, std::to_wstring(parentPid), pImagePath, logComponents[2], imagePath, logComponents[3]); 98 | } 99 | 100 | else{ 101 | std::wcout << L"Main->sqlInsertProxy failed: Unknown Log Tag (" << logComponents[0] << L")." << std::endl; 102 | } 103 | } 104 | 105 | 106 | 107 | int SQLDriver::sendCommand(const wchar_t* command, size_t stringSize){ 108 | 109 | WSADATA wsaData; 110 | SOCKET ConnectSocket = INVALID_SOCKET; 111 | 112 | struct sockaddr_in connectionInfo; 113 | char recvbuf[DB_RESPONSE_BUFFER_LENGTH]; 114 | int iResult; 115 | int recvbuflen = DB_RESPONSE_BUFFER_LENGTH; 116 | char* charBuffer = (char*) calloc(stringSize+1,1); 117 | size_t inLeft, outLeft; 118 | int commandIndex = 0; 119 | int charBufferIndex = 0; 120 | char tempCommandChar; 121 | int wideCharToMultiByteResult; 122 | 123 | std::wcout << L"INSERT:[" << std::wstring(command) << L"]" << std::endl; 124 | wideCharToMultiByteResult = WideCharToMultiByte(CP_UTF8, 0, command, -1, charBuffer, stringSize*sizeof(wchar_t), NULL, NULL); 125 | if(wideCharToMultiByteResult == 0){ 126 | switch (GetLastError()){ 127 | case ERROR_INSUFFICIENT_BUFFER: 128 | std::cout << "SQLDriver->sendCommand->WideCharToMultiByte failed: ERROR_INSUFFICIENT_BUFFER" << std::endl; 129 | break; 130 | case ERROR_INVALID_FLAGS: 131 | std::cout << "SQLDriver->sendCommand->WideCharToMultiByte failed: ERROR_INVALID_FLAGS" << std::endl; 132 | break; 133 | case ERROR_INVALID_PARAMETER: 134 | std::cout << "SQLDriver->sendCommand->WideCharToMultiByte failed: ERROR_INVALID_PARAMETER" << std::endl; 135 | break; 136 | case ERROR_NO_UNICODE_TRANSLATION: 137 | std::cout << "SQLDriver->sendCommand->WideCharToMultiByte failed: ERROR_NO_UNICODE_TRANSLATION" << std::endl; 138 | break; 139 | default: 140 | std::cout << "SQLDriver->sendCommand->WideCharToMultiByte failed:" << std::hex << GetLastError() << std::endl; 141 | break; 142 | } 143 | 144 | return 1; 145 | 146 | } 147 | 148 | if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { 149 | std::cout << "SQLDriver->sendCommand->WSAStartup failed:" << std::hex << WSAGetLastError() << std::endl; 150 | free(charBuffer); 151 | return 1; 152 | } 153 | 154 | 155 | ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 156 | if (ConnectSocket == INVALID_SOCKET) { 157 | std::cout << "SQLDriver->sendCommand->socket failed:" << std::hex << WSAGetLastError() << std::endl; 158 | WSACleanup(); 159 | free(charBuffer); 160 | return 1; 161 | } 162 | 163 | 164 | if (ConnectSocket == INVALID_SOCKET) { 165 | std::cout << "SQLDriver->sendCommand->socket failed:" << std::hex << WSAGetLastError() << std::endl; 166 | WSACleanup(); 167 | free(charBuffer); 168 | return 1; 169 | } 170 | 171 | connectionInfo.sin_family = AF_INET; 172 | connectionInfo.sin_addr.s_addr = inet_addr(DB_IP); 173 | connectionInfo.sin_port = htons(DB_PORT); 174 | 175 | if (connect( ConnectSocket, ((struct sockaddr*)&connectionInfo), sizeof(sockaddr_in)) != 0) { 176 | std::cout << "SQLDriver->sendCommand->connect failed:" << std::hex << WSAGetLastError() << std::endl; 177 | closesocket(ConnectSocket); 178 | ConnectSocket = INVALID_SOCKET; 179 | return 1; 180 | } 181 | 182 | 183 | if (send( ConnectSocket, charBuffer, stringSize, 0 ) == SOCKET_ERROR) { 184 | std::cout << "SQLDriver->sendCommand->send failed:" << std::hex << WSAGetLastError() << std::endl; 185 | closesocket(ConnectSocket); 186 | WSACleanup(); 187 | free(charBuffer); 188 | return 1; 189 | } 190 | 191 | 192 | if (shutdown(ConnectSocket, SD_SEND) == SOCKET_ERROR) { 193 | std::cout << "SQLDriver->sendCommand->shutdown failed:" << std::hex << WSAGetLastError() << std::endl; 194 | closesocket(ConnectSocket); 195 | WSACleanup(); 196 | free(charBuffer); 197 | return 1; 198 | } 199 | 200 | do { 201 | 202 | iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); 203 | if ( iResult > 0 ) 204 | continue; 205 | else if ( iResult == 0 ) 206 | continue; 207 | else 208 | continue; 209 | 210 | } while( iResult > 0 ); 211 | 212 | closesocket(ConnectSocket); 213 | WSACleanup(); 214 | std::cout << "Response:" << std::string(recvbuf) << std::endl; 215 | std::cout << "FileEntries:" << to_string(fileEntries) << std::endl; 216 | std::cout << "RegEntries:" << to_string(regEntries) << std::endl; 217 | std::cout << "NetworkEntries:" << to_string(networkEntries) << std::endl; 218 | std::cout << "ProcessEntries:" << to_string(processEntries) << std::endl; 219 | std::cout << "ObjectEntries:" << to_string(processEntries) << std::endl; 220 | std::cout << "ImageLoadEntries:" << to_string(imageLoadEntries) << std::endl; 221 | std::cout << "ImageLoadEntries:" << to_string(apiEntries) << std::endl; 222 | free(charBuffer); 223 | return 0; 224 | 225 | } 226 | 227 | 228 | 229 | int SQLDriver::initDB(){ 230 | 231 | std::wstring dbInitString = std::wstring(DB_INIT); 232 | return sendCommand(dbInitString.c_str(), dbInitString.length()); 233 | 234 | } 235 | 236 | 237 | int SQLDriver::insertRegistryEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring type, std::wstring key, std::wstring value, std::wstring data){ 238 | 239 | 240 | std::wstringstream queryStringStream; 241 | queryStringStream << FORMAT_DB_INSERT_REGISTRY_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << L'\'' << type << L'\'' << L',' << L'\'' << key << L'\'' << L',' << L'\'' << value << L'\'' << L',' << L'\'' << data << L'\'' << FORMAT_DB_INSERT_REGISTRY_EVENT_SECOND; 242 | std::wstring finalString = queryStringStream.str(); 243 | regEntries+=1; 244 | return sendCommand(finalString.c_str(), finalString.length()); 245 | 246 | } 247 | 248 | 249 | 250 | int SQLDriver::insertFileEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring type, std::wstring file){ 251 | 252 | std::wstringstream queryStringStream; 253 | queryStringStream << FORMAT_DB_INSERT_FILE_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << L'\'' << type << L'\'' << L',' << L'\'' << file << L'\'' << FORMAT_DB_INSERT_FILE_EVENT_SECOND; 254 | std::wstring finalString = queryStringStream.str(); 255 | fileEntries+=1; 256 | 257 | return sendCommand(finalString.c_str(), finalString.length()); 258 | 259 | } 260 | 261 | 262 | int SQLDriver::insertNetworkEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring protocol, std::wstring type, std::wstring lIP, std::wstring lPort, std::wstring remoteIP, std::wstring remotePort){ 263 | 264 | std::wstringstream queryStringStream; 265 | queryStringStream << FORMAT_DB_INSERT_NETWORK_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << L'\'' << protocol << L'\'' << L',' << L'\'' << type << L'\'' << L',' << L'\'' << lIP << L'\'' << L',' << lPort << L',' << L'\'' << remoteIP << L'\'' << L',' << remotePort << FORMAT_DB_INSERT_NETWORK_EVENT_SECOND; 266 | std::wstring finalString = queryStringStream.str(); 267 | networkEntries+=1; 268 | 269 | return sendCommand(finalString.c_str(), finalString.length()); 270 | 271 | } 272 | 273 | int SQLDriver::insertProcessEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring pStatus, std::wstring commandLine){ 274 | 275 | std::wstringstream queryStringStream; 276 | queryStringStream << FORMAT_DB_INSERT_PROCESS_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << L'\'' << pStatus << L'\'' << L',' << L'\'' << commandLine << L'\'' << FORMAT_DB_INSERT_PROCESS_EVENT_SECOND; 277 | std::wstring finalString = queryStringStream.str(); 278 | processEntries+=1; 279 | 280 | return sendCommand(finalString.c_str(), finalString.length()); 281 | 282 | } 283 | 284 | 285 | int SQLDriver::insertObjectEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring objPid, std::wstring objImageFilePath, std::wstring objType, std::wstring handleOperation, std::wstring permissions){ 286 | 287 | std::wstringstream queryStringStream; 288 | queryStringStream << FORMAT_DB_INSERT_OBJECT_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << objPid << L',' << L'\'' << objImageFilePath << L'\'' << L',' << L'\'' << objType << L'\'' << L',' << L'\'' << handleOperation << L'\'' << L',' << L'\'' << permissions << L'\'' << FORMAT_DB_INSERT_OBJECT_EVENT_SECOND; 289 | std::wstring finalString = queryStringStream.str(); 290 | objectEntries+=1; 291 | 292 | return sendCommand(finalString.c_str(), finalString.length()); 293 | 294 | } 295 | 296 | 297 | int SQLDriver::insertLoadImageEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring hostProcessPid, std::wstring hostProcessImageFilePath, std::wstring loadedImage){ 298 | 299 | std::wstringstream queryStringStream; 300 | queryStringStream << FORMAT_DB_INSERT_LOAD_IMAGE_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << hostProcessPid << L',' << L'\'' << hostProcessImageFilePath << L'\'' << L',' << L'\'' << loadedImage << L'\'' << FORMAT_DB_INSERT_LOAD_IMAGE_EVENT_SECOND; 301 | std::wstring finalString = queryStringStream.str(); 302 | imageLoadEntries+=1; 303 | 304 | return sendCommand(finalString.c_str(), finalString.length()); 305 | } 306 | 307 | int SQLDriver::insertAPIEvent(std::wstring timestamp, std::wstring hostname, std::wstring ppid, std::wstring pImageFilePath, std::wstring pid, std::wstring imageFilePath, std::wstring function){ 308 | 309 | std::wstringstream queryStringStream; 310 | queryStringStream << FORMAT_DB_INSERT_API_EVENT_FIRST << timestamp << L',' << L'\'' << hostname << L'\'' << L',' << ppid << L',' << L'\'' << pImageFilePath << L'\'' << L',' << pid << L',' << L'\'' << imageFilePath << L'\'' << L',' << L'\'' << function << L'\'' << FORMAT_DB_INSERT_API_EVENT_SECOND; 311 | std::wstring finalString = queryStringStream.str(); 312 | apiEntries+=1; 313 | 314 | return sendCommand(finalString.c_str(), finalString.length()); 315 | 316 | } 317 | 318 | -------------------------------------------------------------------------------- /Client/src/Util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "util" 11 | 12 | using namespace std; 13 | 14 | #define MAX_PATH_LENGTH 1000 15 | #define MAX_PATH_LENGTH_WIDE 2000 16 | 17 | 18 | void Util::setDebugPrivilege(){ 19 | 20 | 21 | TOKEN_PRIVILEGES tp; 22 | LUID luid; 23 | HANDLE tokenHandle; 24 | std::ostringstream errorStringStream; 25 | 26 | if(!OpenProcessToken(GetCurrentProcess(),TOKEN_READ|TOKEN_WRITE,&tokenHandle)){ 27 | std::cout << "Util->setDebugPrivilege->OpenProcessToken failed:" << std::hex << GetLastError() << std::endl; 28 | return; 29 | } 30 | 31 | if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid )) // receives LUID of privilege 32 | { 33 | errorStringStream << "Util->setDebugPrivilege->LookupPrivilegeValue failed:" << std::hex << GetLastError(); 34 | std::cout << errorStringStream.str() << std::endl; 35 | return; 36 | } 37 | 38 | tp.PrivilegeCount = 1; 39 | tp.Privileges[0].Luid = luid; 40 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 41 | 42 | if ( !AdjustTokenPrivileges(tokenHandle, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) 43 | { 44 | errorStringStream << "Util->setDebugPrivilege->AdjustTokenPrivileges failed:" << std::hex << GetLastError(); 45 | std::cout << errorStringStream.str() << std::endl; 46 | return; 47 | } 48 | 49 | if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) 50 | { 51 | std::cout << "Util->setDebugPrivilege failed: the token does not have the specified privilege." << std::endl; 52 | return; 53 | } 54 | 55 | } 56 | 57 | std::wstring Util::getImagePath(unsigned long long pid){ 58 | 59 | HANDLE processHandle = NULL; 60 | char imageFilePath[MAX_PATH_LENGTH] = {'\0'}; 61 | wchar_t imageFilePathWide[MAX_PATH_LENGTH_WIDE] = {L'\0'}; 62 | std::wstring defaultString = std::wstring(L""); 63 | DWORD sizeOfBuffer = MAX_PATH_LENGTH; 64 | std::ostringstream imageFilePathStringStream; 65 | 66 | if(pid == 0) 67 | return defaultString; 68 | 69 | 70 | processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); 71 | if(processHandle == NULL){ 72 | std::cout << "Util->getParentPid->OpenProcess failed:" << std::hex << GetLastError() << std::endl; 73 | 74 | return std::wstring(imageFilePathWide); 75 | } 76 | 77 | if(QueryFullProcessImageName(processHandle, PROCESS_NAME_NATIVE, imageFilePath, &sizeOfBuffer) == 0){ 78 | std::cout << "Util->getParentPid->QueryFullProcessImageName failed:" << std::hex << GetLastError() << std::endl; 79 | CloseHandle(processHandle); 80 | return std::wstring(imageFilePathWide); 81 | } 82 | 83 | imageFilePathStringStream << std::string(imageFilePath); 84 | mbstowcs(imageFilePathWide, imageFilePathStringStream.str().c_str(), MAX_PATH_LENGTH_WIDE); 85 | 86 | CloseHandle(processHandle); 87 | return std::wstring(imageFilePathWide); 88 | } 89 | 90 | 91 | unsigned long long Util::getParentPid(unsigned long long pid){ 92 | 93 | unsigned long long dwParentProcessID = 0; 94 | HANDLE hProcessSnapshot; 95 | PROCESSENTRY32 processEntry32; 96 | 97 | if(pid == 0) 98 | return dwParentProcessID; 99 | 100 | hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ; 101 | if(hProcessSnapshot != INVALID_HANDLE_VALUE) 102 | { 103 | processEntry32.dwSize = sizeof(PROCESSENTRY32) ; 104 | if(Process32First(hProcessSnapshot, &processEntry32)) 105 | { 106 | do 107 | { 108 | if (pid == processEntry32.th32ProcessID) 109 | { 110 | dwParentProcessID = processEntry32.th32ParentProcessID ; 111 | break ; 112 | } 113 | } 114 | while(Process32Next(hProcessSnapshot, &processEntry32)); 115 | 116 | CloseHandle(hProcessSnapshot) ; 117 | } 118 | 119 | else{ 120 | 121 | std::cout << "Util->getParentPid->Process32First failed:" << std::hex << GetLastError() << std::endl; 122 | } 123 | } 124 | 125 | else{ 126 | 127 | std::cout << "Util->getParentPid->CreateToolhelp32Snapshot failed:" << std::hex << GetLastError() << std::endl; 128 | 129 | } 130 | 131 | return dwParentProcessID ; 132 | } 133 | 134 | 135 | 136 | 137 | std::wstring Util::escapeSpecialCharacters(std::wstring wst){ 138 | 139 | std::wstring tempString; 140 | std::wregex backSlashRegex (std::wstring(L"\\\\")); 141 | std::wregex singleQuoteRegex (std::wstring(L"\\'")); 142 | std::wregex doubleQuoteRegex (std::wstring(L"\"")); 143 | 144 | tempString = std::regex_replace(std::regex_replace(std::regex_replace(wst, backSlashRegex, std::wstring(L"\\\\")),singleQuoteRegex, std::wstring(L"\\'")),doubleQuoteRegex, std::wstring(L"\\\"")); 145 | return tempString; 146 | 147 | 148 | } -------------------------------------------------------------------------------- /CompileAndDeployClient.cmd: -------------------------------------------------------------------------------- 1 | SET INCLUDE=%INCLUDE%%cd%\Detours\include; 2 | SET LIB=%LIB%%cd%\Detours\lib.X86; 3 | 4 | SET MAKEFILE=makefile.mak 5 | 6 | SET DLL_DIR=APIMonitor 7 | SET DLL_NAME=apimonitor.dll 8 | 9 | SET DLL_FULL_PATH=%DLL_DIR%\bin\%DLL_NAME% 10 | SET STAGING_DIR=%DLL_DIR%\staging 11 | 12 | SET CLIENT_NAME=client.exe 13 | 14 | cd Client 15 | nmake /f %MAKEFILE% all 16 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 17 | cd bin 18 | start .\%CLIENT_NAME% 19 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 20 | cd ..\.. 21 | 22 | cd APIMonitor 23 | nmake /f %MAKEFILE% all 24 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 25 | cd .. 26 | 27 | ECHO Signing DLL 28 | signtool sign /v /f %STAGING_DIR%\testing.pfx /t http://timestamp.globalsign.com/scripts/timestamp.dll %DLL_FULL_PATH% 29 | 30 | 31 | REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" /f /v RequireSignedAppInit_DLLs /t REG_DWORD /d 0 32 | REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" /f /v LoadAppInit_DLLs /t REG_DWORD /d 1 33 | REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" /f /v AppInit_DLLs /t REG_SZ /d "C:\Users\user\Desktop\Driver\APIMON~1\bin\APIMON~1.DLL" 34 | 35 | 36 | REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows" /f /v RequireSignedAppInit_DLLs /t REG_DWORD /d 0 37 | REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows" /f /v LoadAppInit_DLLs /t REG_DWORD /d 1 38 | REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows" /f /v AppInit_DLLs /t REG_SZ /d "C:\Users\user\Desktop\Driver\APIMON~1\bin\APIMON~1.DLL" -------------------------------------------------------------------------------- /CompileAndDeployDriver.cmd: -------------------------------------------------------------------------------- 1 | REM Notes: Driver files to be compiled should be on a Driver\src (C files) abd Driver\lib (header files). The Inf file should be on the same path as this cmd file. 2 | SET DRIVER_DIR=Driver 3 | SET DRIVER_SYS=KernelWhispererDriver.sys 4 | SET SERVICE_NAME=kernelwhispererdriver 5 | SET INF_FILE=KernelWhisperer.inf 6 | SET SRC_DIR=%DRIVER_DIR%\src 7 | SET BIN_DIR=%DRIVER_DIR%\bin 8 | SET DRIVER_FULL_PATH=%BIN_DIR%\amd64\%DRIVER_SYS% 9 | SET STAGING_DIR=%DRIVER_DIR%\staging 10 | 11 | Rmdir /s %BIN_DIR% /q 12 | ::Rmdir /s %STAGING_DIR% /q 13 | mkdir %BIN_DIR% 14 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 15 | ::mkdir %STAGING_DIR% 16 | ::IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 17 | 18 | ECHO Compiling driver... 19 | cd %SRC_DIR% 20 | build 21 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 22 | cd ..\..\ 23 | 24 | 25 | 26 | ::ECHO Making PVK/CER... 27 | ::makecert -sv %STAGING_DIR%\testing.pvk -n "CN=Test Signing Cert" %STAGING_DIR%\testing.cer -b 06/19/2017 -e 06/19/2067 -r 28 | ::IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 29 | 30 | ::ECHO Making PFX file... 31 | ::pvk2pfx -pvk %STAGING_DIR%\testing.pvk -spc %STAGING_DIR%\testing.cer -pfx %STAGING_DIR%\testing.pfx 32 | ::IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 33 | 34 | 35 | ECHO Signing Driver 36 | signtool sign /v /f %STAGING_DIR%\testing.pfx /t http://timestamp.globalsign.com/scripts/timestamp.dll %DRIVER_FULL_PATH% 37 | 38 | 39 | ECHO Moving signed driver and Inf file to staging directory... 40 | cp %DRIVER_FULL_PATH% %STAGING_DIR% 41 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 42 | cp %INF_FILE% %STAGING_DIR% 43 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 44 | cd %STAGING_DIR% 45 | 46 | REM This is not working. Whenever i try to run this manually, it complains about a missing line on the INF file....Use Right0click->Install on the INF file. 47 | ECHO Installing Driver 48 | ::InstallHinfSection
49 | rundll32 setupapi, InstallHinfSection DefaultInstall 132 .\%INF_FILE% 50 | IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% 51 | cd .. 52 | 53 | ECHO Launching Driver 54 | sc start %SERVICE_NAME% 55 | -------------------------------------------------------------------------------- /Driver/lib/FileSystemMonitor.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | NTSTATUS DfUnload (FLT_FILTER_UNLOAD_FLAGS Flags); 5 | FLT_PREOP_CALLBACK_STATUS FileSystemFilterPreOperationCallback (PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext); 6 | FLT_POSTOP_CALLBACK_STATUS FileSystemFilterPostOperationCallback (PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID CompletionContext, FLT_POST_OPERATION_FLAGS Flags); 7 | 8 | extern PFLT_FILTER gFilterHandle; 9 | 10 | -------------------------------------------------------------------------------- /Driver/lib/KernelWhispererDriver.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NTSTATUS ClassDispatchUnimplemented(PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 4 | NTSTATUS ClassDeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); 5 | VOID Unload(PDRIVER_OBJECT DriverObject); 6 | VOID deployFileSystemMonitor(PDRIVER_OBJECT DriverObject); 7 | VOID deployRegistryMonitor(PDRIVER_OBJECT DriverObject); 8 | VOID deployNetworkMonitor(); 9 | VOID deployProcessMonitor(); 10 | NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); 11 | 12 | 13 | CONST FLT_OPERATION_REGISTRATION Callbacks[] = { 14 | 15 | { IRP_MJ_CREATE, 16 | 0, 17 | FileSystemFilterPreOperationCallback, 18 | FileSystemFilterPostOperationCallback }, 19 | { IRP_MJ_OPERATION_END } //This one is necessary!!!! 20 | 21 | }; 22 | 23 | CONST FLT_REGISTRATION FilterRegistration = { 24 | 25 | sizeof(FLT_REGISTRATION), // Size 26 | FLT_REGISTRATION_VERSION, // Version 27 | 0, // Flags 28 | NULL, // Context 29 | Callbacks, // Operation callbacks 30 | DfUnload, // MiniFilterUnload 31 | NULL, // InstanceSetup 32 | NULL, // InstanceQueryTeardown 33 | NULL, // InstanceTeardownStart 34 | NULL, // InstanceTeardownComplete 35 | NULL, // GenerateFileName 36 | NULL, // NormalizeNameComponent 37 | NULL, // NormalizeContextCleanup 38 | NULL, // TransactionNotification 39 | NULL // NormalizeNameComponentEx 40 | 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /Driver/lib/LoadImageMonitor.h: -------------------------------------------------------------------------------- 1 | //PLOAD_IMAGE_NOTIFY_ROUTINE PloadImageNotifyRoutine; 2 | #include 3 | 4 | void PloadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); -------------------------------------------------------------------------------- /Driver/lib/NetworkMonitor.h: -------------------------------------------------------------------------------- 1 | #define NDIS60 1 //Necessary for the network stuff. Will not work otherwise. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | DEFINE_GUID(CLSID_NetworkMonitorListen, 0xa6c5a715, 0x5c6e, 0x11d2, 0x97, 0x7a, 0x0, 0x0, 0xf8, 0x7a, 0x92, 0x6f); 9 | DEFINE_GUID(CLSID_NetworkMonitoRecvAccept, 0xa6c5a715, 0x8c6e, 0x11d2, 0x97, 0x7a, 0x1, 0x0, 0xf8, 0x7a, 0x92, 0x6f); 10 | DEFINE_GUID(CLSID_NetworkMonitorConnect, 0xa6c5a715, 0x8c6e, 0x11d2, 0x97, 0x7a, 0x0, 0x0, 0xf8, 0x9a, 0x92, 0x6f); 11 | 12 | 13 | char* uint32ToString(UINT32 ipAddress); 14 | //FWPS_CALLOUT_CLASSIFY_FN FwpsCalloutClassifyFn; 15 | VOID NTAPI FwpsCalloutClassifyFn( 16 | const FWPS_INCOMING_VALUES *inFixedValues, 17 | const FWPS_INCOMING_METADATA_VALUES *inMetaValues, 18 | void *layerData, 19 | const FWPS_FILTER *filter, 20 | UINT64 flowContext, 21 | FWPS_CLASSIFY_OUT *classifyOut 22 | ); 23 | 24 | //FWPS_CALLOUT_NOTIFY_FN FwpsCalloutNotifyFn; 25 | NTSTATUS NTAPI FwpsCalloutNotifyFn(FWPS_CALLOUT_NOTIFY_TYPE notifyType,const GUID *filterKey,FWPS_FILTER *filter); 26 | 27 | //FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FwpsCalloutFlowDeleteNotifyFn; 28 | VOID NTAPI FwpsCalloutFlowDeleteNotifyFn(UINT16 layerId, UINT32 calloutId, UINT64 flowContext); 29 | FWPS_CALLOUT* getSCalloutStructure(); 30 | 31 | ////FWPM_LAYER_ALE_AUTH_LISTEN_V4 32 | FWPS_CALLOUT* getFWPSListenCallout(); 33 | FWPM_CALLOUT* getFWPMListenCallout(); 34 | FWPM_FILTER* getFWPMListenFilter(); 35 | 36 | //FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 37 | FWPS_CALLOUT* getFWPSRecvAcceptCallout(); 38 | FWPM_CALLOUT* getFWPMRecvAcceptCallout(); 39 | FWPM_FILTER* getFWPMRecvAcceptFilter(); 40 | 41 | //FWPM_LAYER_ALE_AUTH_CONNECT_V4 42 | FWPS_CALLOUT* getFWPSConnectCallout(); 43 | FWPM_CALLOUT* getFWPMConnectCallout(); 44 | FWPM_FILTER* getFWPMConnectFilter(); 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Driver/lib/ObjectMonitor.h: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | POB_PRE_OPERATION_CALLBACK PobPreOperationCallbackProcess; 4 | POB_POST_OPERATION_CALLBACK PobPostOperationCallbackProcess; 5 | 6 | 7 | POB_PRE_OPERATION_CALLBACK PobPreOperationCallbackThread; 8 | POB_POST_OPERATION_CALLBACK PobPostOperationCallbackThread; 9 | 10 | POB_PRE_OPERATION_CALLBACK PobPreOperationCallbackDesktop; 11 | POB_POST_OPERATION_CALLBACK PobPostOperationCallbackDesktop; 12 | */ 13 | 14 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation); 15 | void PobPostOperationCallbackProcess(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation); 16 | 17 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation); 18 | void PobPostOperationCallbackThread(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation); 19 | 20 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackDesktop(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation); 21 | void PobPostOperationCallbackDesktop(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation); 22 | 23 | typedef struct RegistrationContextStruct { 24 | 25 | short dummy; 26 | 27 | } RegistrationContextStruct, *PRegistrationContextStruct; 28 | -------------------------------------------------------------------------------- /Driver/lib/ProcessMonitor.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void PcreateProcessNotifyRoutineEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); -------------------------------------------------------------------------------- /Driver/lib/RegistryMonitor.h: -------------------------------------------------------------------------------- 1 | NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2); -------------------------------------------------------------------------------- /Driver/lib/Util.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct _LOGS_NODE LOGS_NODE; 4 | typedef struct _LOGS_NODE* PLOGS_NODE; 5 | 6 | typedef struct _LOGS_NODE{ 7 | 8 | PUNICODE_STRING entryText; 9 | PLOGS_NODE next; 10 | }; 11 | 12 | typedef struct _LOGS_LIST{ 13 | 14 | PLOGS_NODE listBegin; 15 | PLOGS_NODE listEnd; 16 | ULONG listSize; 17 | 18 | } LOGS_LIST, *PLOGS_LIST; 19 | 20 | 21 | 22 | typedef struct _KERNEL_WHISPERER_REQUEST{ 23 | 24 | void* buffer; 25 | 26 | } KERNEL_WHISPERER_REQUEST, *PKERNEL_WHISPERER_REQUEST; 27 | 28 | 29 | typedef struct _KERNEL_WHISPERER_RESPONSE{ 30 | 31 | VOID* response; 32 | 33 | } KERNEL_WHISPERER_RESPONSE, *PKERNEL_WHISPERER_RESPONSE; 34 | 35 | 36 | 37 | VOID initSyncObject(); 38 | VOID deleteNode(PLOGS_NODE node); 39 | VOID addNode(PUNICODE_STRING eventString); 40 | VOID removeOldestNode(); 41 | ULONG getOldestLogString(void* outputBuffer, ULONG outputBufferSize); 42 | int hasInvalidCharacters(PUNICODE_STRING uString); 43 | 44 | 45 | -------------------------------------------------------------------------------- /Driver/src/FileSystemMonitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "FileSystemMonitor.h" 4 | #include "Util.h" 5 | 6 | #define MAX_LOG_BUFFER_SIZE 2000 7 | #define VOLUME_INFORMATION_SIZE 200 8 | #define FILE_NAME_SIZE_MAX 200 9 | 10 | PFLT_FILTER gFilterHandle; 11 | 12 | FLT_PREOP_CALLBACK_STATUS FileSystemFilterPreOperationCallback (PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext) 13 | 14 | { 15 | 16 | return FLT_PREOP_SUCCESS_WITH_CALLBACK; 17 | } 18 | 19 | 20 | FLT_POSTOP_CALLBACK_STATUS FileSystemFilterPostOperationCallback (PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID CompletionContext, FLT_POST_OPERATION_FLAGS Flags) 21 | { 22 | 23 | NTSTATUS tempStatus = STATUS_SUCCESS; 24 | WCHAR* operationResult = NULL; 25 | ULONG processId = 0; 26 | PVOID logStringBuffer; 27 | PUNICODE_STRING logString; 28 | UNICODE_STRING volumeName = {'\0'}; 29 | VOID* volumeNameBuffer; 30 | ULONG volumeBytesReturned; 31 | LARGE_INTEGER currentTime; 32 | 33 | 34 | processId = FltGetRequestorProcessId(Data); 35 | if (processId == 0){ 36 | DbgPrint("FileSystemFilter's FltGetRequestorProcessId failed."); 37 | return FLT_POSTOP_FINISHED_PROCESSING; 38 | } 39 | 40 | KeQuerySystemTime(¤tTime); 41 | 42 | switch(Data->IoStatus.Information){ 43 | case FILE_CREATED: 44 | operationResult = L"FILE_CREATED"; 45 | break; 46 | case FILE_DOES_NOT_EXIST: 47 | operationResult = L"FILE_DOES_NOT_EXIST"; 48 | break; 49 | case FILE_EXISTS: 50 | operationResult = L"FILE_EXISTS"; 51 | break; 52 | case FILE_OPENED: 53 | operationResult = L"FILE_OPENED"; 54 | break; 55 | case FILE_OVERWRITTEN: 56 | operationResult = L"FILE_OVERWRITTEN"; 57 | break; 58 | case FILE_SUPERSEDED: 59 | operationResult = L"FILE_SUPERSEDED"; 60 | break; 61 | default: 62 | operationResult = L"FILE_RESULT_UNKNOWN"; 63 | break; 64 | 65 | 66 | } 67 | 68 | if (Data->Iopb->MajorFunction == IRP_MJ_CREATE){ 69 | 70 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 71 | if (logStringBuffer == NULL){ 72 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->ExAllocatePool failed to allocate space.\n"); 73 | return FLT_POSTOP_FINISHED_PROCESSING; 74 | } 75 | 76 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 77 | 78 | volumeNameBuffer = ExAllocatePool(NonPagedPool, VOLUME_INFORMATION_SIZE); 79 | 80 | if (volumeNameBuffer == NULL){ 81 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->ExAllocatePool failed to allocate space for volume.\n"); 82 | ExFreePool(logStringBuffer); 83 | return FLT_POSTOP_FINISHED_PROCESSING; 84 | } 85 | 86 | RtlZeroMemory(volumeNameBuffer, VOLUME_INFORMATION_SIZE); 87 | volumeName.Buffer = volumeNameBuffer; 88 | volumeName.Length = 0; 89 | volumeName.MaximumLength = VOLUME_INFORMATION_SIZE; 90 | 91 | tempStatus = FltGetVolumeName(FltObjects->Volume, &volumeName, &volumeBytesReturned); 92 | if(!NT_SUCCESS(tempStatus)){ 93 | if (tempStatus == STATUS_INVALID_PARAMETER){ 94 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->FltGetVolumeName: STATUS_BUFFER_OVERFLOW\n"); 95 | } 96 | else if (tempStatus == STATUS_BUFFER_TOO_SMALL){ 97 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->FltGetVolumeName: STATUS_INVALID_PARAMETER\n"); 98 | } 99 | ExFreePool(logStringBuffer); 100 | 101 | return FLT_POSTOP_FINISHED_PROCESSING; 102 | } 103 | 104 | 105 | 106 | 107 | //This is a terrible solution but i cannot check whether the filename is valid or not. I have tried to use kernel functions to inspect the characteristics of the file being opened but with volumes, they 108 | //either cause blue screens or fail. The case where the volume is opened as a file is a corner case and requires me to check whether the string on the buffer is printable. Cannot think of a better way to do this for now. 109 | if((FltObjects->FileObject->FileName.Length > 2) && (hasInvalidCharacters(&(FltObjects->FileObject->FileName)) == 0)){//dword should have at least 2 bytes... 110 | 111 | if(FltObjects->FileObject->FileName.Buffer[0] == L'\\') 112 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%s<-->%wZ%wZ", L"FILE", currentTime.QuadPart, processId, operationResult, &volumeName, FltObjects->FileObject->FileName); 113 | else 114 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%s<-->%wZ\\%wZ", L"FILE", currentTime.QuadPart, processId, operationResult, &volumeName, FltObjects->FileObject->FileName); 115 | } 116 | else 117 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%s<-->%wZ", L"FILE", currentTime.QuadPart, processId, operationResult, &volumeName); 118 | 119 | 120 | if(!NT_SUCCESS(tempStatus)){ 121 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 122 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 123 | } 124 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 125 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 126 | } 127 | ExFreePool(logStringBuffer); 128 | 129 | return FLT_POSTOP_FINISHED_PROCESSING; 130 | } 131 | 132 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 133 | if(logString == NULL){ 134 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->ExAllocatePool failed to allocate memory for log string.\n"); 135 | ExFreePool(logStringBuffer); 136 | 137 | return FLT_POSTOP_FINISHED_PROCESSING; 138 | } 139 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 140 | 141 | RtlInitUnicodeString(logString, logStringBuffer); 142 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 143 | DbgPrint("FileSystemFilter->FileSystemFilterPostOperationCallback->RtlInitUnicodeString failed to create unicode string.\n"); 144 | ExFreePool(logStringBuffer); 145 | ExFreePool(logString); 146 | 147 | return FLT_POSTOP_FINISHED_PROCESSING; 148 | } 149 | addNode(logString); 150 | 151 | } 152 | 153 | return FLT_POSTOP_FINISHED_PROCESSING; 154 | 155 | } 156 | 157 | 158 | NTSTATUS DfUnload (FLT_FILTER_UNLOAD_FLAGS Flags){ 159 | 160 | DbgPrint("FileSystemFilter is being unloaded.\n"); 161 | FltUnregisterFilter( gFilterHandle ); 162 | return STATUS_SUCCESS; 163 | } -------------------------------------------------------------------------------- /Driver/src/KernelWhispererDriver.c: -------------------------------------------------------------------------------- 1 | #define NDIS60 1 //Necessary for the network stuff. Will not work otherwise. 2 | #include 3 | #include 4 | #include 5 | #include "FileSystemMonitor.h" 6 | #include "RegistryMonitor.h" 7 | #include "NetworkMonitor.h" 8 | #include "ProcessMonitor.h" 9 | #include "Util.h" 10 | #include "KernelWhispererDriver.h" 11 | #include "ObjectMonitor.h" 12 | #include "LoadImageMonitor.h" 13 | 14 | 15 | 16 | #define REGISTRY_MONITOR_ALTITUDE L"420000" 17 | #define SIZE_OF_REGISTRY_MONITOR_ALTITUDE_STRING 20 18 | #define SIZE_OF_REGISTRY_MONITOR_ALTITUDE_IN_BYTES SIZE_OF_REGISTRY_MONITOR_ALTITUDE_STRING*sizeof(WCHAR) 19 | #define IOCTL_KERNELWHISPERER_GETACTIVITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x700, METHOD_BUFFERED, FILE_ANY_ACCESS) 20 | 21 | 22 | #pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") 23 | 24 | //File system filter handle. 25 | PFLT_FILTER gFilterHandle; 26 | //Registry filter cookie. 27 | LARGE_INTEGER callbackRoutineCookie; 28 | PDEVICE_OBJECT pDeviceObj; 29 | 30 | PVOID objectMonitorCallbackHandle; 31 | 32 | UINT32 fwpsListenCalloutId; 33 | UINT32 fwpsRecvAcceptCalloutId; 34 | UINT32 fwpsConnectCalloutId; 35 | 36 | UINT32 fwpmListenCalloutId; 37 | UINT32 fwpmRecvAcceptCalloutId; 38 | UINT32 fwpmConnectCalloutId; 39 | 40 | UINT64 fwpmListenFilterId; 41 | UINT64 fwpmRecvAcceptFilterId; 42 | UINT64 fwpmConnectFilterId; 43 | 44 | //For the Object Monitor 45 | extern UNICODE_STRING altitude; 46 | 47 | HANDLE engineHandle; 48 | 49 | DRIVER_DISPATCH ClassDispatchUnimplemented; 50 | 51 | __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) 52 | __drv_dispatchType(IRP_MJ_CREATE) 53 | DRIVER_DISPATCH ClassDeviceControlDispatch; 54 | 55 | 56 | 57 | NTSTATUS ClassDispatchUnimplemented(PDEVICE_OBJECT DeviceObject, PIRP Irp){ 58 | 59 | PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp); 60 | Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 61 | IoCompleteRequest( Irp, IO_NO_INCREMENT ); 62 | return STATUS_INVALID_DEVICE_REQUEST; 63 | } 64 | 65 | 66 | 67 | NTSTATUS ClassDeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) 68 | 69 | 70 | { 71 | PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp); 72 | 73 | VOID* userModeBuffer = NULL; 74 | ULONG copiedBytes; 75 | 76 | if (pStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KERNELWHISPERER_GETACTIVITY){ 77 | userModeBuffer = Irp->AssociatedIrp.SystemBuffer; 78 | copiedBytes = getOldestLogString(userModeBuffer, pStack->Parameters.DeviceIoControl.OutputBufferLength); 79 | } 80 | 81 | 82 | Irp->IoStatus.Information = copiedBytes; 83 | Irp->IoStatus.Status = STATUS_SUCCESS; 84 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 85 | return STATUS_SUCCESS; 86 | 87 | } 88 | 89 | 90 | 91 | DRIVER_UNLOAD Unload; 92 | VOID Unload(PDRIVER_OBJECT DriverObject){ 93 | 94 | 95 | DbgPrint("Unloading driver.\n"); 96 | FltUnregisterFilter(gFilterHandle); 97 | CmUnRegisterCallback(callbackRoutineCookie); 98 | PsSetCreateProcessNotifyRoutineEx(PcreateProcessNotifyRoutineEx, TRUE); 99 | 100 | IoDeleteDevice(pDeviceObj); 101 | 102 | FwpmFilterDeleteById(engineHandle, fwpmListenFilterId); 103 | FwpmFilterDeleteById(engineHandle, fwpmRecvAcceptFilterId); 104 | FwpmFilterDeleteById(engineHandle, fwpmConnectFilterId); 105 | 106 | 107 | FwpmCalloutDeleteById(engineHandle, fwpmListenCalloutId); 108 | FwpmCalloutDeleteById(engineHandle, fwpmRecvAcceptCalloutId); 109 | FwpmCalloutDeleteById(engineHandle, fwpmConnectCalloutId); 110 | 111 | 112 | FwpsCalloutUnregisterById(fwpsListenCalloutId); 113 | FwpsCalloutUnregisterById(fwpsRecvAcceptCalloutId); 114 | FwpsCalloutUnregisterById(fwpsConnectCalloutId); 115 | 116 | ObUnRegisterCallbacks(&objectMonitorCallbackHandle); 117 | PsRemoveLoadImageNotifyRoutine(PloadImageNotifyRoutine); 118 | 119 | 120 | 121 | 122 | } 123 | 124 | 125 | VOID deployFileSystemMonitor(PDRIVER_OBJECT DriverObject){ 126 | 127 | NTSTATUS tempStatus; 128 | DbgPrint("Registering File System filter.\n"); 129 | tempStatus = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle); 130 | if (!NT_SUCCESS(tempStatus)){ 131 | switch(tempStatus){ 132 | case STATUS_INSUFFICIENT_RESOURCES: 133 | DbgPrint("deployFileSystemMonitor's FltRegisterFilter failed: STATUS_INSUFFICIENT_RESOURCES\n"); 134 | break; 135 | case STATUS_INVALID_PARAMETER: 136 | DbgPrint("deployFileSystemMonitor's FltRegisterFilter failed: STATUS_INVALID_PARAMETER\n"); 137 | break; 138 | case STATUS_FLT_NOT_INITIALIZED: 139 | DbgPrint("deployFileSystemMonitor's FltRegisterFilter failed: STATUS_FLT_NOT_INITIALIZED\n"); 140 | break; 141 | case STATUS_OBJECT_NAME_NOT_FOUND: 142 | DbgPrint("deployFileSystemMonitor's FltRegisterFilter failed: STATUS_OBJECT_NAME_NOT_FOUND\n"); 143 | break; 144 | default: 145 | DbgPrint("deployFileSystemMonitor's FltRegisterFilter failed:%p\n", tempStatus); 146 | break; 147 | } 148 | } 149 | 150 | else { 151 | tempStatus = FltStartFiltering(gFilterHandle); 152 | if (!NT_SUCCESS(tempStatus)){ 153 | switch(tempStatus){ 154 | case STATUS_INVALID_PARAMETER: 155 | DbgPrint("deployFileSystemMonitor's FltStartFiltering failed: STATUS_INVALID_PARAMETER\n"); 156 | break; 157 | default: 158 | DbgPrint("deployFileSystemMonitor's FltStartFiltering failed:%p\n", tempStatus); 159 | break; 160 | } 161 | FltUnregisterFilter(gFilterHandle); 162 | } 163 | 164 | 165 | } 166 | 167 | 168 | } 169 | 170 | VOID deployRegistryMonitor(PDRIVER_OBJECT DriverObject){ 171 | 172 | NTSTATUS tempStatus; 173 | WCHAR altitudeBuffer[SIZE_OF_REGISTRY_MONITOR_ALTITUDE_STRING]; 174 | UNICODE_STRING altitude; 175 | 176 | tempStatus = RtlStringCbPrintfW(altitudeBuffer, SIZE_OF_REGISTRY_MONITOR_ALTITUDE_STRING, L"%s", REGISTRY_MONITOR_ALTITUDE); 177 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 178 | DbgPrint("deployRegistryMonitor's RtlStringCbPrintfW failed to copy altitude string: STATUS_BUFFER_OVERFLOW\n"); 179 | return; 180 | } 181 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 182 | DbgPrint("deployRegistryMonitor's RtlStringCbPrintfW failed to copy altitude string: STATUS_INVALID_PARAMETER\n"); 183 | return; 184 | } 185 | 186 | RtlInitUnicodeString(&altitude, altitudeBuffer); 187 | 188 | if ((altitude.Buffer == NULL) || (altitude.Length == 0)){ 189 | DbgPrint("deployRegistryMonitor's RtlInitUnicodeString failed to create altitude unicode string.\n"); 190 | return; 191 | } 192 | 193 | 194 | tempStatus = CmRegisterCallbackEx(RegistryCallback,&altitude,DriverObject,NULL,&callbackRoutineCookie,NULL); 195 | if (tempStatus == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION){ 196 | DbgPrint("deployRegistryMonitor's CmRegisterCallbackEx failed: STATUS_FLT_INSTANCE_ALTITUDE_COLLISION\n"); 197 | return; 198 | } 199 | else if (tempStatus == STATUS_INSUFFICIENT_RESOURCES){ 200 | DbgPrint("deployRegistryMonitor'sCmRegisterCallbackEx failed: STATUS_INSUFFICIENT_RESOURCES\n"); 201 | return; 202 | } 203 | 204 | } 205 | 206 | VOID deployNetworkMonitor(){ 207 | 208 | NTSTATUS tempStatus; 209 | 210 | FWPM_SESSION fwpmSession = {'\0'}; 211 | 212 | FWPS_CALLOUT* fwpsListenCallout; 213 | FWPS_CALLOUT* fwpsRecvAcceptCallout; 214 | FWPS_CALLOUT* fwpsConnectCallout; 215 | 216 | FWPM_CALLOUT* fwpmListenCallout; 217 | FWPM_CALLOUT* fwpmRecvAcceptCallout; 218 | FWPM_CALLOUT* fwpmConnectCallout; 219 | 220 | FWPM_FILTER* fwpmListenFilter; 221 | FWPM_FILTER* fwpmRecvAcceptFilter; 222 | FWPM_FILTER* fwpmConnectFilter; 223 | 224 | 225 | //fwpmSession->flags = FWPM_SESSION_FLAG_DYNAMIC; triggers c022000b STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS on FwpmCalloutAdd 226 | tempStatus = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &fwpmSession, &engineHandle); 227 | 228 | if(engineHandle == NULL) 229 | { 230 | DbgPrint("deployNetworkMonitor's FwpmEngineOpen failed."); 231 | return; 232 | } 233 | 234 | if(!NT_SUCCESS(tempStatus)){ 235 | if (tempStatus == ERROR_SUCCESS) 236 | DbgPrint("deployNetworkMonitor's FwpmEngineOpen failed: STATUS_FWP_ALREADY_EXISTS\n"); 237 | 238 | else if (tempStatus == FWP_E_ALREADY_EXISTS) 239 | DbgPrint("deployNetworkMonitor's FwpmEngineOpen failed: FWP_E_ALREADY_EXISTS\n"); 240 | 241 | else 242 | DbgPrint("deployNetworkMonitor's FwpmEngineOpen failed: failed:%p\n", tempStatus); 243 | 244 | return; 245 | } 246 | 247 | tempStatus = FwpmTransactionBegin(engineHandle, 0); 248 | 249 | if(!NT_SUCCESS(tempStatus)){ 250 | DbgPrint("deployNetworkMonitor's FwpmTransactionBegin failed: failed:%p\n", tempStatus); 251 | 252 | return; 253 | } 254 | 255 | 256 | 257 | fwpsListenCallout = getFWPSListenCallout(); 258 | fwpsRecvAcceptCallout = getFWPSRecvAcceptCallout(); 259 | fwpsConnectCallout = getFWPSConnectCallout(); 260 | 261 | 262 | if(fwpsListenCallout == NULL){ 263 | DbgPrint("deployNetworkMonitor's getFWPS_Callout failed: fwpsListenCallout null"); 264 | 265 | return; 266 | 267 | } 268 | 269 | if(fwpsRecvAcceptCallout == NULL){ 270 | DbgPrint("deployNetworkMonitor's getFWPS_Callout failed: fwpsRecvAcceptCallout null"); 271 | ExFreePool(fwpsListenCallout); 272 | return; 273 | 274 | } 275 | 276 | if(fwpsConnectCallout == NULL){ 277 | DbgPrint("deployNetworkMonitor's getFWPS_Callout failed: fwpsConnectCallout null"); 278 | ExFreePool(fwpsListenCallout); 279 | ExFreePool(fwpsRecvAcceptCallout); 280 | 281 | return; 282 | 283 | } 284 | 285 | tempStatus = FwpsCalloutRegister(pDeviceObj, fwpsListenCallout, &fwpsListenCalloutId); 286 | if(!NT_SUCCESS(tempStatus)){ 287 | if (tempStatus == STATUS_FWP_ALREADY_EXISTS) 288 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Listen) failed: STATUS_FWP_ALREADY_EXISTS\n"); 289 | else 290 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Listen) failed:%p\n", tempStatus); 291 | 292 | ExFreePool(fwpsListenCallout); 293 | ExFreePool(fwpsRecvAcceptCallout); 294 | ExFreePool(fwpsConnectCallout); 295 | return; 296 | } 297 | 298 | tempStatus = FwpsCalloutRegister(pDeviceObj, fwpsRecvAcceptCallout, &fwpsRecvAcceptCalloutId); 299 | if(!NT_SUCCESS(tempStatus)){ 300 | if (tempStatus == STATUS_FWP_ALREADY_EXISTS) 301 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Recv/Accept) failed: STATUS_FWP_ALREADY_EXISTS\n"); 302 | else 303 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Recv/Accept) failed:%p\n", tempStatus); 304 | 305 | ExFreePool(fwpsListenCallout); 306 | ExFreePool(fwpsRecvAcceptCallout); 307 | ExFreePool(fwpsConnectCallout); 308 | return; 309 | } 310 | 311 | tempStatus = FwpsCalloutRegister(pDeviceObj, fwpsConnectCallout, &fwpsConnectCalloutId); 312 | if(!NT_SUCCESS(tempStatus)){ 313 | if (tempStatus == STATUS_FWP_ALREADY_EXISTS) 314 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Connect) failed: STATUS_FWP_ALREADY_EXISTS\n"); 315 | else 316 | DbgPrint("deployNetworkMonitor's FwpsCalloutRegister (Connect) failed:%p\n", tempStatus); 317 | 318 | ExFreePool(fwpsListenCallout); 319 | ExFreePool(fwpsRecvAcceptCallout); 320 | ExFreePool(fwpsConnectCallout); 321 | return; 322 | } 323 | 324 | 325 | fwpmListenCallout = getFWPMListenCallout(); 326 | fwpmRecvAcceptCallout = getFWPMRecvAcceptCallout(); 327 | fwpmConnectCallout =getFWPMConnectCallout(); 328 | 329 | if(fwpmListenCallout == NULL){ 330 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmListenCallout is null."); 331 | ExFreePool(fwpsListenCallout); 332 | ExFreePool(fwpsRecvAcceptCallout); 333 | ExFreePool(fwpsConnectCallout); 334 | return; 335 | 336 | } 337 | 338 | if(fwpmRecvAcceptCallout == NULL){ 339 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmListenCallout is null."); 340 | 341 | 342 | 343 | ExFreePool(fwpsListenCallout); 344 | ExFreePool(fwpsRecvAcceptCallout); 345 | ExFreePool(fwpsConnectCallout); 346 | ExFreePool(fwpmListenCallout); 347 | return; 348 | 349 | } 350 | 351 | if(fwpmConnectCallout == NULL){ 352 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmListenCallout is null."); 353 | 354 | 355 | 356 | ExFreePool(fwpsListenCallout); 357 | ExFreePool(fwpsRecvAcceptCallout); 358 | ExFreePool(fwpsConnectCallout); 359 | ExFreePool(fwpmListenCallout); 360 | ExFreePool(fwpmRecvAcceptCallout); 361 | return; 362 | 363 | } 364 | 365 | tempStatus = FwpmCalloutAdd(engineHandle, fwpmListenCallout, NULL, &fwpmListenCalloutId); 366 | if(!NT_SUCCESS(tempStatus)){ 367 | if (tempStatus == FWP_E_INVALID_PARAMETER) 368 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed: FWP_E_INVALID_PARAMETER\n"); 369 | else 370 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed:%p\n", tempStatus); 371 | 372 | 373 | 374 | ExFreePool(fwpsListenCallout); 375 | ExFreePool(fwpsRecvAcceptCallout); 376 | ExFreePool(fwpsConnectCallout); 377 | ExFreePool(fwpmListenCallout); 378 | ExFreePool(fwpmRecvAcceptCallout); 379 | ExFreePool(fwpmConnectCallout); 380 | return; 381 | } 382 | 383 | 384 | tempStatus = FwpmCalloutAdd(engineHandle, fwpmRecvAcceptCallout, NULL, &fwpmRecvAcceptCalloutId); 385 | if(!NT_SUCCESS(tempStatus)){ 386 | if (tempStatus == FWP_E_INVALID_PARAMETER) 387 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed: FWP_E_INVALID_PARAMETER\n"); 388 | else 389 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed:%p\n", tempStatus); 390 | 391 | 392 | 393 | ExFreePool(fwpsListenCallout); 394 | ExFreePool(fwpsRecvAcceptCallout); 395 | ExFreePool(fwpsConnectCallout); 396 | ExFreePool(fwpmListenCallout); 397 | ExFreePool(fwpmRecvAcceptCallout); 398 | ExFreePool(fwpmConnectCallout); 399 | return; 400 | } 401 | 402 | 403 | 404 | tempStatus = FwpmCalloutAdd(engineHandle, fwpmConnectCallout, NULL, &fwpmConnectCalloutId); 405 | if(!NT_SUCCESS(tempStatus)){ 406 | if (tempStatus == FWP_E_INVALID_PARAMETER) 407 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed: FWP_E_INVALID_PARAMETER\n"); 408 | else 409 | DbgPrint("deployNetworkMonitor's FwpmCalloutAdd failed:%p\n", tempStatus); 410 | ExFreePool(fwpsListenCallout); 411 | ExFreePool(fwpsRecvAcceptCallout); 412 | ExFreePool(fwpsConnectCallout); 413 | ExFreePool(fwpmListenCallout); 414 | ExFreePool(fwpmRecvAcceptCallout); 415 | ExFreePool(fwpmConnectCallout); 416 | return; 417 | } 418 | 419 | 420 | fwpmListenFilter = getFWPMListenFilter(); 421 | fwpmRecvAcceptFilter = getFWPMRecvAcceptFilter(); 422 | fwpmConnectFilter =getFWPMConnectFilter(); 423 | 424 | if(fwpmListenFilter == NULL){ 425 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmListenFilter is null."); 426 | ExFreePool(fwpsListenCallout); 427 | ExFreePool(fwpsRecvAcceptCallout); 428 | ExFreePool(fwpsConnectCallout); 429 | ExFreePool(fwpmListenCallout); 430 | ExFreePool(fwpmRecvAcceptCallout); 431 | ExFreePool(fwpmConnectCallout); 432 | return; 433 | 434 | } 435 | 436 | if(fwpmRecvAcceptFilter == NULL){ 437 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmRecvAcceptFilter is null."); 438 | ExFreePool(fwpsListenCallout); 439 | ExFreePool(fwpsRecvAcceptCallout); 440 | ExFreePool(fwpsConnectCallout); 441 | ExFreePool(fwpmListenCallout); 442 | ExFreePool(fwpmRecvAcceptCallout); 443 | ExFreePool(fwpmConnectCallout); 444 | ExFreePool(fwpmListenFilter); 445 | return; 446 | 447 | } 448 | 449 | if(fwpmConnectFilter == NULL){ 450 | DbgPrint("deployNetworkMonitor's getFWPM_Callout failed: fwpmConnectFilter is null."); 451 | ExFreePool(fwpsListenCallout); 452 | ExFreePool(fwpsRecvAcceptCallout); 453 | ExFreePool(fwpsConnectCallout); 454 | ExFreePool(fwpmListenCallout); 455 | ExFreePool(fwpmRecvAcceptCallout); 456 | ExFreePool(fwpmConnectCallout); 457 | ExFreePool(fwpmListenFilter); 458 | ExFreePool(fwpmRecvAcceptFilter); 459 | return; 460 | 461 | } 462 | 463 | tempStatus = FwpmFilterAdd(engineHandle, fwpmListenFilter, NULL, &fwpmListenFilterId); 464 | if(!NT_SUCCESS(tempStatus)){ 465 | if (tempStatus == ERROR_INVALID_SECURITY_DESCR) 466 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: ERROR_INVALID_SECURITY_DESCR\n"); 467 | else if (tempStatus == FWP_E_CALLOUT_NOTIFICATION_FAILED) 468 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: FWP_E_CALLOUT_NOTIFICATION_FAILED\n"); 469 | else 470 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed:%p\n", tempStatus); 471 | 472 | ExFreePool(fwpsListenCallout); 473 | ExFreePool(fwpsRecvAcceptCallout); 474 | ExFreePool(fwpsConnectCallout); 475 | ExFreePool(fwpmListenCallout); 476 | ExFreePool(fwpmRecvAcceptCallout); 477 | ExFreePool(fwpmConnectCallout); 478 | ExFreePool(fwpmListenFilter); 479 | ExFreePool(fwpmRecvAcceptFilter); 480 | ExFreePool(fwpmConnectFilter); 481 | return; 482 | } 483 | 484 | 485 | 486 | tempStatus = FwpmFilterAdd(engineHandle, fwpmRecvAcceptFilter, NULL, &fwpmListenFilterId); 487 | if(!NT_SUCCESS(tempStatus)){ 488 | if (tempStatus == ERROR_INVALID_SECURITY_DESCR) 489 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: ERROR_INVALID_SECURITY_DESCR\n"); 490 | else if (tempStatus == FWP_E_CALLOUT_NOTIFICATION_FAILED) 491 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: FWP_E_CALLOUT_NOTIFICATION_FAILED\n"); 492 | else 493 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed:%p\n", tempStatus); 494 | 495 | 496 | ExFreePool(fwpsListenCallout); 497 | ExFreePool(fwpsRecvAcceptCallout); 498 | ExFreePool(fwpsConnectCallout); 499 | ExFreePool(fwpmListenCallout); 500 | ExFreePool(fwpmRecvAcceptCallout); 501 | ExFreePool(fwpmConnectCallout); 502 | ExFreePool(fwpmListenFilter); 503 | ExFreePool(fwpmRecvAcceptFilter); 504 | ExFreePool(fwpmConnectFilter); 505 | return; 506 | } 507 | 508 | 509 | 510 | tempStatus = FwpmFilterAdd(engineHandle, fwpmConnectFilter, NULL, &fwpmConnectFilterId); 511 | if(!NT_SUCCESS(tempStatus)){ 512 | if (tempStatus == ERROR_INVALID_SECURITY_DESCR) 513 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: ERROR_INVALID_SECURITY_DESCR\n"); 514 | else if (tempStatus == FWP_E_CALLOUT_NOTIFICATION_FAILED) 515 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed: FWP_E_CALLOUT_NOTIFICATION_FAILED\n"); 516 | else 517 | DbgPrint("deployNetworkMonitor's FwpmFilterAdd failed:%p\n", tempStatus); 518 | 519 | 520 | ExFreePool(fwpsListenCallout); 521 | ExFreePool(fwpsRecvAcceptCallout); 522 | ExFreePool(fwpsConnectCallout); 523 | ExFreePool(fwpmListenCallout); 524 | ExFreePool(fwpmRecvAcceptCallout); 525 | ExFreePool(fwpmConnectCallout); 526 | ExFreePool(fwpmListenFilter); 527 | ExFreePool(fwpmRecvAcceptFilter); 528 | ExFreePool(fwpmConnectFilter); 529 | return; 530 | } 531 | 532 | tempStatus = FwpmTransactionCommit(engineHandle); 533 | 534 | if(!NT_SUCCESS(tempStatus)){ 535 | if (tempStatus == ERROR_SUCCESS) 536 | DbgPrint("deployNetworkMonitor's FwpmTransactionCommit failed: STATUS_FWP_ALREADY_EXISTS\n"); 537 | else 538 | DbgPrint("deployNetworkMonitor's FwpmTransactionCommit failed:%p\n", tempStatus); 539 | 540 | 541 | tempStatus = FwpmTransactionAbort(engineHandle); 542 | if(!NT_SUCCESS(tempStatus)){ 543 | if (tempStatus == ERROR_SUCCESS) 544 | DbgPrint("deployNetworkMonitor's FwpmTransactionAbort failed: STATUS_FWP_ALREADY_EXISTS\n"); 545 | else 546 | DbgPrint("deployNetworkMonitor's FwpmTransactionAbort failed:%p\n", tempStatus); 547 | 548 | ExFreePool(fwpsListenCallout); 549 | ExFreePool(fwpsRecvAcceptCallout); 550 | ExFreePool(fwpsConnectCallout); 551 | ExFreePool(fwpmListenCallout); 552 | ExFreePool(fwpmRecvAcceptCallout); 553 | ExFreePool(fwpmConnectCallout); 554 | ExFreePool(fwpmListenFilter); 555 | ExFreePool(fwpmRecvAcceptFilter); 556 | ExFreePool(fwpmConnectFilter); 557 | return; 558 | } 559 | } 560 | 561 | 562 | 563 | 564 | } 565 | 566 | 567 | VOID deployProcessMonitor(){ 568 | 569 | NTSTATUS tempStatus; 570 | 571 | tempStatus = PsSetCreateProcessNotifyRoutineEx(PcreateProcessNotifyRoutineEx, FALSE); 572 | 573 | if(!NT_SUCCESS(tempStatus)){ 574 | switch(tempStatus){ 575 | case STATUS_INVALID_PARAMETER: 576 | DbgPrint("deployProcessMonitor's PsSetCreateProcessNotifyRoutineEx failed: STATUS_INVALID_PARAMETER\n"); 577 | break; 578 | case STATUS_OBJECT_NAME_COLLISION: 579 | DbgPrint("deployProcessMonitor's PsSetCreateProcessNotifyRoutineEx failed: STATUS_ACCESS_DENIED\n"); 580 | break; 581 | default: 582 | DbgPrint("deployProcessMonitor's PsSetCreateProcessNotifyRoutineEx failed:%p\n", tempStatus); 583 | break; 584 | 585 | } 586 | 587 | 588 | } 589 | 590 | } 591 | 592 | 593 | VOID deployObjectMonitor(){ 594 | 595 | NTSTATUS tempStatus; 596 | UNICODE_STRING altitude; 597 | OB_CALLBACK_REGISTRATION objectCallbackRegistration; 598 | RegistrationContextStruct regContStruct; 599 | 600 | 601 | OB_OPERATION_REGISTRATION objectOperationRegistrationProcess = { 602 | 603 | PsProcessType,//POBJECT_TYPE *ObjectType; 604 | (OB_OPERATION_HANDLE_CREATE|OB_OPERATION_HANDLE_DUPLICATE),//OB_OPERATION Operations; 605 | PobPreOperationCallbackProcess, //POB_PRE_OPERATION_CALLBACK PreOperation; 606 | PobPostOperationCallbackProcess//POB_POST_OPERATION_CALLBACK PostOperation; 607 | 608 | }; 609 | 610 | 611 | OB_OPERATION_REGISTRATION objectOperationRegistrationThread = { 612 | 613 | PsThreadType,//POBJECT_TYPE *ObjectType; 614 | (OB_OPERATION_HANDLE_CREATE|OB_OPERATION_HANDLE_DUPLICATE),//OB_OPERATION Operations; 615 | PobPreOperationCallbackThread, //POB_PRE_OPERATION_CALLBACK PreOperation; 616 | PobPostOperationCallbackThread//POB_POST_OPERATION_CALLBACK PostOperation; 617 | 618 | }; 619 | 620 | 621 | /*only valid for Windows 10 622 | const OB_OPERATION_REGISTRATION objectOperationRegistrationDesktop = { 623 | 624 | ExDesktopObjectType,//POBJECT_TYPE *ObjectType; 625 | OB_OPERATION_HANDLE_CREATE|OB_OPERATION_HANDLE_DUPLICATE,//OB_OPERATION Operations; 626 | PobPreOperationCallbackDesktop, //POB_PRE_OPERATION_CALLBACK PreOperation; 627 | PobPostOperationCallbackDesktop//POB_POST_OPERATION_CALLBACK PostOperation; 628 | 629 | }*/ 630 | 631 | OB_OPERATION_REGISTRATION objectOperationRegistrationArray[2]; 632 | objectOperationRegistrationArray[0] = objectOperationRegistrationProcess; 633 | objectOperationRegistrationArray[1] = objectOperationRegistrationThread; 634 | 635 | 636 | objectCallbackRegistration.Version = OB_FLT_REGISTRATION_VERSION; 637 | objectCallbackRegistration.OperationRegistrationCount = 2; 638 | objectCallbackRegistration.RegistrationContext = ®ContStruct; 639 | 640 | 641 | 642 | RtlInitUnicodeString(&altitude, REGISTRY_MONITOR_ALTITUDE); 643 | 644 | if ((altitude.Buffer == NULL) || (altitude.Length == 0)){ 645 | DbgPrint("KernelWhispererDriver->deployObjectMonitor->RtlInitUnicodeString failed to create altitude unicode string.\n"); 646 | return; 647 | } 648 | 649 | objectCallbackRegistration.Altitude = altitude; 650 | objectCallbackRegistration.OperationRegistration = objectOperationRegistrationArray; 651 | 652 | 653 | 654 | tempStatus = ObRegisterCallbacks(&objectCallbackRegistration, &objectMonitorCallbackHandle); 655 | 656 | if(!NT_SUCCESS(tempStatus)){ 657 | switch(tempStatus){ 658 | case STATUS_FLT_INSTANCE_ALTITUDE_COLLISION: 659 | DbgPrint("deployObjectMonitor's ObRegisterCallbacks failed: STATUS_FLT_INSTANCE_ALTITUDE_COLLISION\n"); 660 | break; 661 | case STATUS_INVALID_PARAMETER: 662 | DbgPrint("deployObjectMonitor's ObRegisterCallbacks failed: STATUS_INVALID_PARAMETER\n"); 663 | break; 664 | case STATUS_ACCESS_DENIED: 665 | DbgPrint("deployObjectMonitor's ObRegisterCallbacks failed: STATUS_ACCESS_DENIED\n"); 666 | break; 667 | case STATUS_INSUFFICIENT_RESOURCES: 668 | DbgPrint("deployObjectMonitor's ObRegisterCallbacks failed: STATUS_INSUFFICIENT_RESOURCES\n"); 669 | break; 670 | default: 671 | DbgPrint("deployObjectMonitor's ObRegisterCallbacks failed: %p\n", tempStatus); 672 | break; 673 | 674 | } 675 | 676 | 677 | 678 | } 679 | 680 | } 681 | 682 | 683 | VOID deployImageLoadMonitor(){ 684 | 685 | NTSTATUS tempStatus = PsSetLoadImageNotifyRoutine(PloadImageNotifyRoutine); 686 | if(!NT_SUCCESS(tempStatus)){ 687 | switch(tempStatus){ 688 | case STATUS_INSUFFICIENT_RESOURCES: 689 | DbgPrint("deployImageLoadMonitor's PsSetLoadImageNotifyRoutine failed: STATUS_INSUFFICIENT_RESOURCES\n"); 690 | break; 691 | default: 692 | DbgPrint("deployImageLoadMonitor's PsSetLoadImageNotifyRoutine failed:%p\n", tempStatus); 693 | break; 694 | } 695 | 696 | } 697 | } 698 | 699 | DRIVER_INITIALIZE DriverEntry; 700 | NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 701 | { 702 | NTSTATUS tempStatus; 703 | 704 | UNICODE_STRING usDeviceName = RTL_CONSTANT_STRING(L"\\Device\\KernelWhisperer"); 705 | UNICODE_STRING usSymbolicLink = RTL_CONSTANT_STRING(L"\\DosDevices\\Global\\KernelWhisperer"); 706 | int idx = 0; 707 | 708 | 709 | DbgPrint("Setting IRP handlers.\n"); 710 | for (idx = 0; idx <= IRP_MJ_MAXIMUM_FUNCTION; idx++) { 711 | DriverObject->MajorFunction[idx] = ClassDispatchUnimplemented; 712 | } 713 | 714 | DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch; 715 | DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassDeviceControlDispatch; 716 | 717 | tempStatus = IoCreateDevice(DriverObject, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj); 718 | 719 | 720 | if(!NT_SUCCESS(tempStatus)){ 721 | switch(tempStatus){ 722 | case STATUS_INSUFFICIENT_RESOURCES: 723 | DbgPrint("DriverEntry's IoCreateDevice failed: STATUS_INSUFFICIENT_RESOURCES\n"); 724 | break; 725 | case STATUS_OBJECT_NAME_COLLISION: 726 | DbgPrint("DriverEntry's IoCreateDevice failed: STATUS_OBJECT_NAME_COLLISION\n"); 727 | break; 728 | default: 729 | DbgPrint("DriverEntry's IoCreateDevice failed:%p\n", tempStatus); 730 | break; 731 | 732 | } 733 | 734 | return STATUS_UNSUCCESSFUL; 735 | 736 | } 737 | 738 | else{ 739 | 740 | if(pDeviceObj == NULL){ 741 | DbgPrint("DriverEntry's IoCreateDevice failed: null device object.\n"); 742 | IoDeleteDevice(pDeviceObj); 743 | return STATUS_UNSUCCESSFUL; 744 | } 745 | 746 | tempStatus = IoCreateSymbolicLink(&usSymbolicLink, &usDeviceName); 747 | if(!NT_SUCCESS(tempStatus)){ 748 | DbgPrint("DriverEntry's IoCreateSymbolicLink failed:%p\n", tempStatus); 749 | IoDeleteDevice(pDeviceObj); 750 | return STATUS_UNSUCCESSFUL; 751 | } 752 | 753 | } 754 | 755 | initSyncObject(); 756 | 757 | /* 758 | Procedure to add more monitors: 759 | 1. Change SQLDriver SQL queries to account for new table 760 | 2. Add another parser to LogParser (header+source) 761 | 3. Add another case to Client.cpp 762 | 4. Add another case to SQLDriver (header + source) 763 | */ 764 | 765 | deployFileSystemMonitor(DriverObject); 766 | deployRegistryMonitor(DriverObject); 767 | deployNetworkMonitor(); 768 | deployProcessMonitor(); 769 | deployObjectMonitor(); 770 | deployImageLoadMonitor(); 771 | 772 | return STATUS_SUCCESS; 773 | 774 | 775 | 776 | 777 | } 778 | 779 | -------------------------------------------------------------------------------- /Driver/src/LoadImageMonitor.c: -------------------------------------------------------------------------------- 1 | #define NDIS60 1 //Necessary for the network stuff. Will not work otherwise. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "LoadImageMonitor.h" 7 | #include "Util.h" 8 | 9 | //PLOAD_IMAGE_NOTIFY_ROUTINE PloadImageNotifyRoutine; 10 | 11 | #define MAX_LOG_BUFFER_SIZE 2000 //bytes 12 | 13 | //The operating system does not call load-image notify routines when sections created with the SEC_IMAGE_NO_EXECUTE attribute are mapped to virtual memory. 14 | void PloadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo){ 15 | 16 | 17 | 18 | NTSTATUS tempStatus; 19 | ULONG processId = 0; 20 | PVOID logStringBuffer; 21 | PUNICODE_STRING logString; 22 | LARGE_INTEGER currentTime; 23 | 24 | 25 | KeQuerySystemTime(¤tTime); 26 | 27 | processId = PsGetCurrentProcessId(); 28 | 29 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 30 | if (logStringBuffer == NULL){ 31 | DbgPrint("LoadImageMonitor->PloadImageNotifyRoutine->ExAllocatePool failed to allocate space for loadimage log.\n"); 32 | return; 33 | } 34 | 35 | 36 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 37 | 38 | 39 | //The Function to detect Loaded Files has many bugs. One of them is the fact that dlls have no full names (i.e. no volume). I have tried other functions to get the volume but i am unable to. Something to correct later. 40 | //Not a big deal for my purposes. Also, we can leverage the File Table to search for DLLS or EXES opened around that time. 41 | if(FullImageName != NULL) 42 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ld<-->%wZ", L"LOADIMAGE", currentTime.QuadPart, processId, ProcessId, FullImageName); 43 | else 44 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ld<-->%wZ", L"LOADIMAGE", currentTime.QuadPart, processId, ProcessId, L"INVALID"); 45 | 46 | 47 | if(!NT_SUCCESS(tempStatus)){ 48 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 49 | DbgPrint("LoadImageMonitor->PloadImageNotifyRoutine->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 50 | } 51 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 52 | DbgPrint("LoadImageMonitor->PloadImageNotifyRoutine->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 53 | } 54 | ExFreePool(logStringBuffer); 55 | return; 56 | } 57 | 58 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 59 | if(logString == NULL){ 60 | DbgPrint("LoadImageMonitor->PloadImageNotifyRoutine->ExAllocatePool failed to allocate memory for log unicode structure.\n"); 61 | ExFreePool(logStringBuffer); 62 | return; 63 | } 64 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 65 | 66 | RtlInitUnicodeString(logString, logStringBuffer); 67 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 68 | DbgPrint("LoadImageMonitor->PloadImageNotifyRoutine->RtlInitUnicodeString failed to create unicode string.\n"); 69 | ExFreePool(logString); 70 | ExFreePool(logStringBuffer); 71 | return; 72 | } 73 | 74 | addNode(logString); 75 | return STATUS_SUCCESS; 76 | 77 | 78 | } -------------------------------------------------------------------------------- /Driver/src/NetworkMonitor.c: -------------------------------------------------------------------------------- 1 | #define NDIS60 1 //Necessary for the network stuff. Will not work otherwise. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "NetworkMonitor.h" 9 | #include "Util.h" 10 | 11 | #define SIZE_OF_IP_BUFFER 20 12 | #define MAX_LOG_BUFFER_SIZE 2000 13 | 14 | char* uint32ToString(UINT32 ipAddress){ 15 | wchar_t* ipBuffer = ExAllocatePool(NonPagedPool, SIZE_OF_IP_BUFFER*sizeof(wchar_t)); 16 | unsigned char ipOctets[] = {0,0,0,0}; 17 | NTSTATUS temptStatus; 18 | int loopIndex; 19 | 20 | RtlZeroMemory(ipBuffer, SIZE_OF_IP_BUFFER*sizeof(wchar_t)); 21 | 22 | for (loopIndex=0; loopIndex<4; loopIndex++) 23 | ipOctets[loopIndex] = (( ipAddress >> (loopIndex*8) ) & 0xFF); 24 | 25 | //Make check on return; 26 | RtlStringCbPrintfW(ipBuffer, SIZE_OF_IP_BUFFER*sizeof(wchar_t), L"%d.%d.%d.%d", ipOctets[3],ipOctets[2],ipOctets[1],ipOctets[0]); 27 | 28 | return ipBuffer; 29 | } 30 | 31 | VOID NTAPI FwpsCalloutClassifyFn(const FWPS_INCOMING_VALUES *inFixedValues, const FWPS_INCOMING_METADATA_VALUES *inMetaValues, void *layerData, const FWPS_FILTER *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT *classifyOut){ 32 | 33 | NTSTATUS tempStatus; 34 | UINT64 processId; 35 | UINT64 sourceInterfaceIndex; 36 | wchar_t* type; 37 | short unsigned dstPort; 38 | HANDLE injectionHandle; 39 | wchar_t* protocol; 40 | LARGE_INTEGER currentTime; 41 | PUNICODE_STRING logString; 42 | PVOID logStringBuffer; 43 | wchar_t* localIPAddress = NULL; 44 | wchar_t* remoteIPAddress = NULL; 45 | 46 | 47 | KeQuerySystemTime(¤tTime); 48 | processId = inMetaValues->processId; 49 | 50 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 51 | if (logStringBuffer == NULL){ 52 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn->ExAllocatePool failed to allocate space.\n"); 53 | return; 54 | } 55 | 56 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 57 | 58 | switch (inFixedValues->layerId){ 59 | case FWPS_LAYER_ALE_AUTH_LISTEN_V4: 60 | type = L"LISTEN"; 61 | protocol = L"TCP"; 62 | localIPAddress = uint32ToString(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS].value.uint32); 63 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ls<-->%ls<-->%u", L"NET", currentTime.QuadPart, processId, protocol, type, localIPAddress, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_PORT].value.uint16); 64 | break; 65 | case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: 66 | type = L"RECV/ACCEPT"; 67 | switch (inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS].value.uint8){ 68 | case 1: 69 | protocol = L"ICMP"; 70 | break; 71 | case 6: 72 | protocol = L"TCP"; 73 | break; 74 | case 17: 75 | protocol = L"UDP"; 76 | break; 77 | default: 78 | protocol = L"UNKNOWN"; 79 | break; 80 | } 81 | localIPAddress = uint32ToString(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS].value.uint32); 82 | remoteIPAddress = uint32ToString(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value.uint32); 83 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ls<-->%ls<-->%u<-->%ls<-->%u", L"NET", currentTime.QuadPart, processId, protocol, type, localIPAddress, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT].value.uint16,remoteIPAddress, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT].value.uint16); 84 | break; 85 | case FWPS_LAYER_ALE_AUTH_CONNECT_V4: 86 | type = L"CONNECT"; 87 | switch (inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8){ 88 | case 1: 89 | protocol = L"ICMP"; 90 | break; 91 | case 6: 92 | protocol = L"TCP"; 93 | break; 94 | case 17: 95 | protocol = L"UDP"; 96 | break; 97 | default: 98 | protocol = L"UNKNOWN"; 99 | break; 100 | } 101 | localIPAddress = uint32ToString(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32); 102 | remoteIPAddress = uint32ToString(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32); 103 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ls<-->%ls<-->%u<-->%ls<-->%u", L"NET", currentTime.QuadPart, processId, protocol, type, localIPAddress, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16,remoteIPAddress, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16); 104 | break; 105 | default: 106 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn error: Unknown Layer Id."); 107 | if (localIPAddress != NULL) 108 | ExFreePool(localIPAddress); 109 | if(remoteIPAddress != NULL) 110 | ExFreePool(remoteIPAddress); 111 | return; 112 | } 113 | 114 | if(!NT_SUCCESS(tempStatus)){ 115 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 116 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 117 | } 118 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 119 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 120 | } 121 | ExFreePool(logStringBuffer); 122 | if (localIPAddress != NULL) 123 | ExFreePool(localIPAddress); 124 | if(remoteIPAddress != NULL) 125 | ExFreePool(remoteIPAddress); 126 | return; 127 | } 128 | 129 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 130 | if(logString == NULL){ 131 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn->ExAllocatePool failed to allocate memory for log string.\n"); 132 | ExFreePool(logStringBuffer); 133 | if (localIPAddress != NULL) 134 | ExFreePool(localIPAddress); 135 | if(remoteIPAddress != NULL) 136 | ExFreePool(remoteIPAddress); 137 | return; 138 | } 139 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 140 | 141 | RtlInitUnicodeString(logString, logStringBuffer); 142 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 143 | DbgPrint("NetworkMonitor->FwpsCalloutClassifyFn->RtlInitUnicodeString failed to create unicode string.\n"); 144 | ExFreePool(logStringBuffer); 145 | ExFreePool(logString); 146 | if (localIPAddress != NULL) 147 | ExFreePool(localIPAddress); 148 | if(remoteIPAddress != NULL) 149 | ExFreePool(remoteIPAddress); 150 | return; 151 | } 152 | 153 | addNode(logString); 154 | 155 | if (localIPAddress != NULL) 156 | ExFreePool(localIPAddress); 157 | if(remoteIPAddress != NULL) 158 | ExFreePool(remoteIPAddress); 159 | 160 | 161 | 162 | } 163 | 164 | 165 | 166 | 167 | NTSTATUS NTAPI FwpsCalloutNotifyFn(FWPS_CALLOUT_NOTIFY_TYPE notifyType,const GUID *filterKey,FWPS_FILTER *filter){ 168 | return STATUS_SUCCESS; 169 | } 170 | 171 | 172 | VOID NTAPI FwpsCalloutFlowDeleteNotifyFn(UINT16 layerId, UINT32 calloutId, UINT64 flowContext){ 173 | 174 | } 175 | 176 | 177 | //FWPM_LAYER_ALE_AUTH_LISTEN_V4 178 | FWPS_CALLOUT* getFWPSListenCallout(){ 179 | FWPS_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPS_CALLOUT)); 180 | 181 | RtlZeroMemory(calloutStruct, sizeof(FWPS_CALLOUT)); 182 | calloutStruct->calloutKey = CLSID_NetworkMonitorListen; 183 | calloutStruct->flags = 0; 184 | calloutStruct->classifyFn = FwpsCalloutClassifyFn; 185 | calloutStruct->notifyFn = FwpsCalloutNotifyFn; 186 | calloutStruct->flowDeleteFn = FwpsCalloutFlowDeleteNotifyFn; 187 | return calloutStruct; 188 | } 189 | 190 | 191 | FWPM_CALLOUT* getFWPMListenCallout(){ 192 | 193 | FWPM_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPM_CALLOUT)); 194 | RtlZeroMemory(calloutStruct, sizeof(FWPM_CALLOUT)); 195 | 196 | 197 | calloutStruct->calloutKey = CLSID_NetworkMonitorListen; 198 | calloutStruct->displayData.name = L"NetworkMonitor Callout: TCP Listen (IPV4)."; 199 | calloutStruct->flags = FWPM_CALLOUT_FLAG_PERSISTENT; 200 | calloutStruct->applicableLayer = FWPM_LAYER_ALE_AUTH_LISTEN_V4; 201 | 202 | 203 | 204 | return calloutStruct; 205 | 206 | 207 | } 208 | 209 | 210 | FWPM_FILTER* getFWPMListenFilter(){ 211 | 212 | FWPM_FILTER* layerFilters = ExAllocatePool(NonPagedPool, sizeof(FWPM_FILTER)); 213 | RtlZeroMemory(layerFilters, sizeof(FWPM_FILTER)); 214 | 215 | 216 | layerFilters->displayData.name = L"NetworkMonitor Filter: TCP Listen (IPV4)."; 217 | layerFilters->flags = FWPM_FILTER_FLAG_PERSISTENT; 218 | layerFilters->layerKey = FWPM_LAYER_ALE_AUTH_LISTEN_V4; 219 | layerFilters->action.type = FWP_ACTION_CALLOUT_INSPECTION; 220 | layerFilters->action.calloutKey = CLSID_NetworkMonitorListen; 221 | layerFilters->numFilterConditions = 0; 222 | 223 | 224 | return layerFilters; 225 | 226 | 227 | } 228 | 229 | //FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 230 | FWPS_CALLOUT* getFWPSRecvAcceptCallout(){ 231 | FWPS_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPS_CALLOUT)); 232 | 233 | RtlZeroMemory(calloutStruct, sizeof(FWPS_CALLOUT)); 234 | calloutStruct->calloutKey = CLSID_NetworkMonitoRecvAccept; 235 | calloutStruct->flags = 0; 236 | calloutStruct->classifyFn = FwpsCalloutClassifyFn; 237 | calloutStruct->notifyFn = FwpsCalloutNotifyFn; 238 | calloutStruct->flowDeleteFn = FwpsCalloutFlowDeleteNotifyFn; 239 | return calloutStruct; 240 | } 241 | 242 | 243 | FWPM_CALLOUT* getFWPMRecvAcceptCallout(){ 244 | 245 | FWPM_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPM_CALLOUT)); 246 | 247 | RtlZeroMemory(calloutStruct, sizeof(FWPM_CALLOUT)); 248 | 249 | calloutStruct->calloutKey = CLSID_NetworkMonitoRecvAccept; 250 | calloutStruct->displayData.name = L"NetworkMonitor Callout: Incoming TCP (IPV4)."; 251 | calloutStruct->flags = FWPM_CALLOUT_FLAG_PERSISTENT; 252 | calloutStruct->applicableLayer = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; 253 | 254 | return calloutStruct; 255 | 256 | 257 | } 258 | FWPM_FILTER* getFWPMRecvAcceptFilter(){ 259 | 260 | FWPM_FILTER* layerFilters = ExAllocatePool(NonPagedPool, sizeof(FWPM_FILTER)); 261 | RtlZeroMemory(layerFilters, sizeof(FWPM_FILTER)); 262 | 263 | layerFilters->displayData.name = L"NetworkMonitor Filter: Incoming TCP (IPV4)."; 264 | layerFilters->flags = FWPM_FILTER_FLAG_PERSISTENT; 265 | layerFilters->layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; 266 | layerFilters->action.type = FWP_ACTION_CALLOUT_INSPECTION; 267 | layerFilters->action.calloutKey = CLSID_NetworkMonitoRecvAccept; 268 | layerFilters->numFilterConditions = 0; 269 | return layerFilters; 270 | 271 | } 272 | 273 | ////FWPM_LAYER_ALE_AUTH_CONNECT_V4 274 | FWPS_CALLOUT* getFWPSConnectCallout(){ 275 | 276 | FWPS_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPS_CALLOUT)); 277 | 278 | RtlZeroMemory(calloutStruct, sizeof(FWPS_CALLOUT)); 279 | calloutStruct->calloutKey = CLSID_NetworkMonitorConnect; 280 | calloutStruct->flags = 0; 281 | calloutStruct->classifyFn = FwpsCalloutClassifyFn; 282 | calloutStruct->notifyFn = FwpsCalloutNotifyFn; 283 | calloutStruct->flowDeleteFn = FwpsCalloutFlowDeleteNotifyFn; 284 | return calloutStruct; 285 | 286 | 287 | } 288 | FWPM_CALLOUT* getFWPMConnectCallout(){ 289 | 290 | FWPM_CALLOUT* calloutStruct = ExAllocatePool(NonPagedPool, sizeof(FWPM_CALLOUT)); 291 | 292 | RtlZeroMemory(calloutStruct, sizeof(FWPM_CALLOUT)); 293 | 294 | calloutStruct->calloutKey = CLSID_NetworkMonitorConnect; 295 | calloutStruct->displayData.name = L"NetworkMonitor Callout: Outbound TCP (IPV4)."; 296 | calloutStruct->flags = FWPM_CALLOUT_FLAG_PERSISTENT; 297 | calloutStruct->applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4; 298 | return calloutStruct; 299 | 300 | 301 | } 302 | FWPM_FILTER* getFWPMConnectFilter(){ 303 | 304 | FWPM_FILTER* layerFilters = ExAllocatePool(NonPagedPool, sizeof(FWPM_FILTER)); 305 | RtlZeroMemory(layerFilters, sizeof(FWPM_FILTER)); 306 | 307 | layerFilters->displayData.name = L"NetworkMonitor Filter: Outbound TCP (IPV4)."; 308 | layerFilters->flags = FWPM_FILTER_FLAG_PERSISTENT; 309 | layerFilters->layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; 310 | layerFilters->action.type = FWP_ACTION_CALLOUT_INSPECTION; 311 | layerFilters->action.calloutKey = CLSID_NetworkMonitorConnect; 312 | layerFilters->numFilterConditions = 0; 313 | return layerFilters; 314 | } 315 | -------------------------------------------------------------------------------- /Driver/src/ObjectMonitor.c: -------------------------------------------------------------------------------- 1 | #define NDIS60 1 //Necessary for the network stuff. Will not work otherwise. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "ObjectMonitor.h" 7 | #include "Util.h" 8 | 9 | #define MAX_LOG_BUFFER_SIZE 2000 //bytes 10 | #define MAX_PERMISSION_BUFFER_SIZE 500 //bytes 11 | 12 | wchar_t* accessMaskHexToString(ULONG hexMask){ 13 | 14 | NTSTATUS tempStatus; 15 | wchar_t* arrayOfPermissions[9] = {0}; 16 | PVOID permissionsStringBuffer = ExAllocatePool(NonPagedPool, MAX_PERMISSION_BUFFER_SIZE); 17 | 18 | if (permissionsStringBuffer == NULL){ 19 | DbgPrint("ObjectMonitor->accessMaskHexToString->ExAllocatePool failed to allocate space for object log.\n"); 20 | return NULL; 21 | } 22 | 23 | 24 | RtlZeroMemory(permissionsStringBuffer, MAX_PERMISSION_BUFFER_SIZE); 25 | 26 | /* 27 | 28 | https://msdn.microsoft.com/de-de/library/windows/desktop/aa374896(v=vs.85).aspx 29 | */ 30 | 31 | arrayOfPermissions[0] = ((hexMask & DELETE) != 0) ? L"DELETE" : NULL; 32 | arrayOfPermissions[1] = ((hexMask & READ_CONTROL) != 0) ? L"READ_CONTROL" : NULL; 33 | arrayOfPermissions[2] = ((hexMask & WRITE_DAC) != 0) ? L"WRITE_DAC" : NULL; 34 | arrayOfPermissions[3] = ((hexMask & WRITE_OWNER) != 0) ? L"WRITE_OWNER" : NULL; 35 | arrayOfPermissions[4] = ((hexMask & SYNCHRONIZE) != 0) ? L"SYNCHRONIZE" : NULL; 36 | 37 | 38 | arrayOfPermissions[5] = ((hexMask & GENERIC_READ) != 0) ? L"GENERIC_READ" : NULL; 39 | arrayOfPermissions[6] = ((hexMask & GENERIC_WRITE) != 0) ? L"GENERIC_WRITE" : NULL; 40 | arrayOfPermissions[7] = ((hexMask & GENERIC_EXECUTE) != 0) ? L"GENERIC_EXECUTE" : NULL; 41 | arrayOfPermissions[8] = ((hexMask & GENERIC_ALL) != 0) ? L"GENERIC_ALL" : NULL; 42 | 43 | tempStatus = RtlStringCbPrintfW(permissionsStringBuffer, MAX_PERMISSION_BUFFER_SIZE, L"|%ls|%ls|%ls|%ls|%ls|%ls|%ls|%ls|%ls|",(arrayOfPermissions[0] == NULL) ? L"-" : arrayOfPermissions[0], 44 | (arrayOfPermissions[1] == NULL) ? L"-" : arrayOfPermissions[1], 45 | (arrayOfPermissions[2] == NULL) ? L"-" : arrayOfPermissions[2], 46 | (arrayOfPermissions[3] == NULL) ? L"-" : arrayOfPermissions[3], 47 | (arrayOfPermissions[4] == NULL) ? L"-" : arrayOfPermissions[4], 48 | (arrayOfPermissions[5] == NULL) ? L"-" : arrayOfPermissions[5], 49 | (arrayOfPermissions[6] == NULL) ? L"-" : arrayOfPermissions[6], 50 | (arrayOfPermissions[7] == NULL) ? L"-" : arrayOfPermissions[7], 51 | (arrayOfPermissions[8] == NULL) ? L"-" : arrayOfPermissions[8]); 52 | if(!NT_SUCCESS(tempStatus)){ 53 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 54 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->accessMaskHexToString failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 55 | } 56 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 57 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->accessMaskHexToString failed to generate log string: STATUS_INVALID_PARAMETER\n"); 58 | } 59 | ExFreePool(permissionsStringBuffer); 60 | return NULL; 61 | } 62 | 63 | 64 | return (wchar_t*) permissionsStringBuffer; 65 | } 66 | 67 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation){ 68 | 69 | //DbgPrint("PobPreOperationCallbackProcess called.\n"); 70 | 71 | 72 | return OB_PREOP_SUCCESS; 73 | 74 | } 75 | void PobPostOperationCallbackProcess(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation){ 76 | 77 | 78 | 79 | NTSTATUS tempStatus; 80 | ULONG processId = 0; 81 | PVOID logStringBuffer; 82 | PUNICODE_STRING logString; 83 | LARGE_INTEGER currentTime; 84 | wchar_t* permissionsArray; 85 | 86 | KeQuerySystemTime(¤tTime); 87 | 88 | processId = PsGetCurrentProcessId(); 89 | 90 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 91 | if (logStringBuffer == NULL){ 92 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->ExAllocatePool failed to allocate space for object log.\n"); 93 | return STATUS_UNSUCCESSFUL; 94 | } 95 | 96 | 97 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 98 | if(NT_SUCCESS(OperationInformation->ReturnStatus)){ 99 | permissionsArray = accessMaskHexToString((OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? OperationInformation->Parameters->CreateHandleInformation.GrantedAccess : OperationInformation->Parameters->DuplicateHandleInformation.GrantedAccess); 100 | if (permissionsArray != NULL){ 101 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ld<-->%ls<-->%ls", L"OBJECT", currentTime.QuadPart, processId, L"PROCESS", PsGetProcessId(OperationInformation->Object),(OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? L"OB_OPERATION_HANDLE_CREATE" : L"OB_OPERATION_HANDLE_DUPLICATE", permissionsArray); 102 | ExFreePool(permissionsArray); 103 | } 104 | else 105 | { 106 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->accessMaskHexToString returned null.\n"); 107 | ExFreePool(logStringBuffer); 108 | return; 109 | } 110 | } 111 | else 112 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ld<-->%ls", L"OBJECT", currentTime.QuadPart, processId, L"PROCESS", PsGetProcessId(OperationInformation->Object),(OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? L"OB_OPERATION_HANDLE_CREATE" : L"OB_OPERATION_HANDLE_DUPLICATE"); 113 | 114 | 115 | 116 | if(!NT_SUCCESS(tempStatus)){ 117 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 118 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 119 | } 120 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 121 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 122 | } 123 | ExFreePool(logStringBuffer); 124 | return; 125 | } 126 | 127 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 128 | if(logString == NULL){ 129 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->ExAllocatePool failed to allocate memory for log unicode structure.\n"); 130 | ExFreePool(logStringBuffer); 131 | return; 132 | } 133 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 134 | 135 | RtlInitUnicodeString(logString, logStringBuffer); 136 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 137 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->RtlInitUnicodeString failed to create unicode string.\n"); 138 | ExFreePool(logString); 139 | ExFreePool(logStringBuffer); 140 | return; 141 | } 142 | 143 | addNode(logString); 144 | return STATUS_SUCCESS; 145 | 146 | } 147 | 148 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation){ 149 | 150 | return OB_PREOP_SUCCESS; 151 | 152 | } 153 | 154 | void PobPostOperationCallbackThread(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation){ 155 | 156 | 157 | NTSTATUS tempStatus; 158 | ULONG processId = 0; 159 | PVOID logStringBuffer; 160 | PUNICODE_STRING logString; 161 | LARGE_INTEGER currentTime; 162 | wchar_t* permissionsArray; 163 | 164 | KeQuerySystemTime(¤tTime); 165 | 166 | processId = PsGetCurrentProcessId(); 167 | 168 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 169 | if (logStringBuffer == NULL){ 170 | DbgPrint("ObjectMonitor->PobPostOperationCallbackThread->ExAllocatePool failed to allocate space for object log.\n"); 171 | return STATUS_UNSUCCESSFUL; 172 | } 173 | 174 | 175 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 176 | //Will print verbatim ACCESS_MASK. Need to understand how to translate this into a string. 177 | if(NT_SUCCESS(OperationInformation->ReturnStatus)){ 178 | permissionsArray = accessMaskHexToString((OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? OperationInformation->Parameters->CreateHandleInformation.GrantedAccess : OperationInformation->Parameters->DuplicateHandleInformation.GrantedAccess); 179 | if (permissionsArray != NULL){ 180 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ld<-->%ls<-->%ls", L"OBJECT", currentTime.QuadPart, processId, L"THREAD", PsGetThreadProcessId(OperationInformation->Object),(OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? L"OB_OPERATION_HANDLE_CREATE" : L"OB_OPERATION_HANDLE_DUPLICATE", permissionsArray); 181 | ExFreePool(permissionsArray); 182 | } 183 | else 184 | { 185 | DbgPrint("ObjectMonitor->PobPostOperationCallbackProcess->accessMaskHexToString returned null.\n"); 186 | ExFreePool(logStringBuffer); 187 | return; 188 | } 189 | } 190 | else 191 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%ld<-->%ls<-->%ld<-->%ls", L"OBJECT", currentTime.QuadPart, processId, L"THREAD", PsGetThreadProcessId(OperationInformation->Object),(OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) ? L"OB_OPERATION_HANDLE_CREATE" : L"OB_OPERATION_HANDLE_DUPLICATE"); 192 | 193 | 194 | if(!NT_SUCCESS(tempStatus)){ 195 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 196 | DbgPrint("ObjectMonitor->PobPostOperationCallbackThread->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 197 | } 198 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 199 | DbgPrint("ObjectMonitor->PobPostOperationCallbackThread->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 200 | } 201 | ExFreePool(logStringBuffer); 202 | return STATUS_UNSUCCESSFUL; 203 | } 204 | 205 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 206 | if(logString == NULL){ 207 | DbgPrint("ObjectMonitor->PobPostOperationCallbackThread->ExAllocatePool failed to allocate memory for log unicode structure.\n"); 208 | ExFreePool(logStringBuffer); 209 | return STATUS_UNSUCCESSFUL; 210 | } 211 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 212 | 213 | RtlInitUnicodeString(logString, logStringBuffer); 214 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 215 | DbgPrint("ObjectMonitor->PobPostOperationCallbackThread->RtlInitUnicodeString failed to create unicode string.\n"); 216 | ExFreePool(logString); 217 | ExFreePool(logStringBuffer); 218 | return STATUS_UNSUCCESSFUL; 219 | } 220 | 221 | addNode(logString); 222 | return STATUS_SUCCESS; 223 | 224 | } 225 | 226 | OB_PREOP_CALLBACK_STATUS PobPreOperationCallbackDesktop(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation){ 227 | 228 | //DbgPrint("PobPreOperationCallbackDesktop called.\n"); 229 | return OB_PREOP_SUCCESS; 230 | 231 | } 232 | 233 | void PobPostOperationCallbackDesktop(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation){ 234 | 235 | //DbgPrint("PobPostOperationCallbackDesktop called.\n"); 236 | 237 | } -------------------------------------------------------------------------------- /Driver/src/ProcessMonitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Util.h" 5 | 6 | #define MAX_LOG_BUFFER_SIZE 5000 7 | 8 | 9 | VOID PcreateProcessNotifyRoutineEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo){ 10 | 11 | NTSTATUS tempStatus; 12 | UINT64 processId; 13 | LARGE_INTEGER currentTime; 14 | PUNICODE_STRING logString; 15 | PVOID logStringBuffer; 16 | PUNICODE_STRING imageFileName; 17 | PUNICODE_STRING commandLine; 18 | 19 | 20 | KeQuerySystemTime(¤tTime); 21 | 22 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 23 | if (logStringBuffer == NULL){ 24 | DbgPrint("ProcessMonitor->PcreateProcessNotifyRoutineEx->ExAllocatePool failed to allocate space.\n"); 25 | return; 26 | } 27 | 28 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 29 | 30 | 31 | if(CreateInfo != NULL){ 32 | 33 | imageFileName = CreateInfo->ImageFileName != NULL ? CreateInfo->ImageFileName : NULL; 34 | commandLine = CreateInfo->CommandLine != NULL ? CreateInfo->CommandLine : NULL; 35 | 36 | if(imageFileName != NULL){ 37 | if(commandLine != NULL){ 38 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%d<-->%d<-->%wZ<-->%wZ<-->%ls", L"PROC", currentTime.QuadPart, ProcessId, CreateInfo->ParentProcessId, imageFileName, commandLine, L"S"); 39 | } 40 | else{ 41 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%d<-->%d<-->%wZ<-->%ls<-->%ls", L"PROC", currentTime.QuadPart, ProcessId, CreateInfo->ParentProcessId, imageFileName, L"-", L"S"); 42 | } 43 | 44 | } 45 | else { 46 | 47 | if(commandLine != NULL){ 48 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%d<-->%d<-->%ls<-->%wZ<-->%ls", L"PROC", currentTime.QuadPart, ProcessId, CreateInfo->ParentProcessId, L"-", commandLine, L"S"); 49 | } 50 | else{ 51 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%d<-->%d<-->%ls<-->%ls<-->%ls", L"PROC", currentTime.QuadPart, ProcessId, CreateInfo->ParentProcessId, L"-", L"-", L"S"); 52 | } 53 | 54 | } 55 | 56 | } 57 | 58 | else{ 59 | 60 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%d<-->%d<-->%ls<-->%ls<-->%ls", L"PROC", currentTime.QuadPart, ProcessId, 0, L"-", L"-", L"T"); 61 | 62 | } 63 | 64 | 65 | if(!NT_SUCCESS(tempStatus)){ 66 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 67 | DbgPrint("ProcessMonitor->PcreateProcessNotifyRoutineEx->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 68 | } 69 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 70 | DbgPrint("ProcessMonitor->PcreateProcessNotifyRoutineEx->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 71 | } 72 | ExFreePool(logStringBuffer); 73 | 74 | return; 75 | } 76 | 77 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 78 | if(logString == NULL){ 79 | DbgPrint("ProcessMonitor->PcreateProcessNotifyRoutineEx->ExAllocatePool failed to allocate memory for log string.\n"); 80 | ExFreePool(logStringBuffer); 81 | return; 82 | } 83 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 84 | 85 | RtlInitUnicodeString(logString, logStringBuffer); 86 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 87 | DbgPrint("ProcessMonitor->PcreateProcessNotifyRoutineEx->RtlInitUnicodeString failed to create unicode string.\n"); 88 | ExFreePool(logStringBuffer); 89 | ExFreePool(logString); 90 | return; 91 | } 92 | 93 | addNode(logString); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Driver/src/RegistryMonitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Util.h" 5 | #include "RegistryMonitor.h" 6 | 7 | 8 | #define MAX_LOG_BUFFER_SIZE 2000 9 | 10 | 11 | EX_CALLBACK_FUNCTION RegistryCallback; 12 | 13 | ExtractKeyPath(PVOID registryObject, PUNICODE_STRING registryPathBuffer){ 14 | 15 | NTSTATUS tempStatus = STATUS_SUCCESS; 16 | ULONG realSizeOfPobjectNameInformation; 17 | 18 | PVOID pobjectNameInformationBuffer = ExAllocatePool(NonPagedPool, 1); 19 | if (pobjectNameInformationBuffer == NULL){ 20 | DbgPrint("RegistryFilter->ExtractKeyPath->ExAllocatePool failed."); 21 | return; 22 | } 23 | 24 | 25 | RtlZeroMemory(pobjectNameInformationBuffer, 1); 26 | tempStatus = ObQueryNameString(registryObject,(POBJECT_NAME_INFORMATION) pobjectNameInformationBuffer, 1, &realSizeOfPobjectNameInformation); 27 | 28 | if (tempStatus == STATUS_INFO_LENGTH_MISMATCH){ 29 | ExFreePool(pobjectNameInformationBuffer); 30 | pobjectNameInformationBuffer = ExAllocatePool(NonPagedPool, realSizeOfPobjectNameInformation); 31 | if (pobjectNameInformationBuffer == NULL){ 32 | DbgPrint("RegistryFilter->ExtractKeyPath->ExAllocatePool failed."); 33 | return; 34 | } 35 | RtlZeroMemory(pobjectNameInformationBuffer, realSizeOfPobjectNameInformation); 36 | tempStatus = ObQueryNameString(registryObject,(POBJECT_NAME_INFORMATION) pobjectNameInformationBuffer, realSizeOfPobjectNameInformation, &realSizeOfPobjectNameInformation); 37 | if(!NT_SUCCESS(tempStatus)){ 38 | DbgPrint("RegistryFilter->ExtractKeyPath->ExAllocatePool failed."); 39 | ExFreePool(pobjectNameInformationBuffer); 40 | return; 41 | } 42 | } 43 | else{ 44 | if(!NT_SUCCESS(tempStatus)){ 45 | ExFreePool(pobjectNameInformationBuffer); 46 | return; 47 | } 48 | } 49 | 50 | registryPathBuffer->Length = ((POBJECT_NAME_INFORMATION) pobjectNameInformationBuffer)->Name.Length; 51 | registryPathBuffer->MaximumLength = ((POBJECT_NAME_INFORMATION) pobjectNameInformationBuffer)->Name.MaximumLength; 52 | registryPathBuffer->Buffer = ExAllocatePool(NonPagedPool, registryPathBuffer->MaximumLength); 53 | 54 | tempStatus = RtlUnicodeStringCopy(registryPathBuffer, (PUNICODE_STRING) pobjectNameInformationBuffer); 55 | if(!NT_SUCCESS(tempStatus)){ 56 | if(tempStatus == STATUS_BUFFER_OVERFLOW){ 57 | DbgPrint("RegistryFilter->ExtractKeyPath->RtlUnicodeStringCopy failed with error: STATUS_BUFFER_OVERFLOW."); 58 | } 59 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 60 | DbgPrint("RegistryFilter->ExtractKeyPath->RtlUnicodeStringCopy failed with error: STATUS_INVALID_PARAMETER."); 61 | } 62 | else 63 | DbgPrint("RegistryFilter->ExtractKeyPath->RtlUnicodeStringCopy failed with error: UNKNOW_ERROR."); 64 | 65 | registryPathBuffer->Length = 0; 66 | registryPathBuffer->MaximumLength = 0; 67 | registryPathBuffer->Buffer = NULL; 68 | ExFreePool(pobjectNameInformationBuffer); 69 | return; 70 | } 71 | 72 | 73 | ExFreePool(pobjectNameInformationBuffer); 74 | 75 | } 76 | 77 | 78 | NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) 79 | { 80 | NTSTATUS tempStatus; 81 | ULONG processId = 0; 82 | 83 | ULONG sizeOfKeyObjectData; 84 | HANDLE keyHandle; 85 | UNICODE_STRING registryPathBuffer; 86 | PVOID logStringBuffer; 87 | PUNICODE_STRING logString; 88 | LARGE_INTEGER currentTime; 89 | 90 | if((((int)((REG_NOTIFY_CLASS)Argument1)) != RegNtPreSetValueKey) && (((int)((REG_NOTIFY_CLASS)Argument1)) != RegNtPreCreateKeyEx) && (((int)((REG_NOTIFY_CLASS)Argument1)) != RegNtPreQueryValueKey) && (((int)((REG_NOTIFY_CLASS)Argument1)) != RegNtPreQueryKey) && (((int)((REG_NOTIFY_CLASS)Argument1)) != RegNtPreOpenKey)){ 91 | return; 92 | } 93 | 94 | KeQuerySystemTime(¤tTime); 95 | 96 | processId = PsGetCurrentProcessId(); 97 | if (processId == 0){ 98 | DbgPrint("RegistryFilter's FltGetRequestorProcessId failed."); 99 | return STATUS_UNSUCCESSFUL; 100 | } 101 | 102 | 103 | logStringBuffer = ExAllocatePool(NonPagedPool, MAX_LOG_BUFFER_SIZE); 104 | if (logStringBuffer == NULL){ 105 | DbgPrint("RegistryFilter->RegistryCallback->RegNtPreQueryValueKey->ExAllocatePool failed to allocate space for registry log.\n"); 106 | return STATUS_UNSUCCESSFUL; 107 | } 108 | 109 | 110 | RtlZeroMemory(logStringBuffer, MAX_LOG_BUFFER_SIZE); 111 | 112 | switch((int)((REG_NOTIFY_CLASS)Argument1)){ 113 | case RegNtDeleteKey: 114 | break; 115 | case RegNtPreSetValueKey: 116 | 117 | 118 | ExtractKeyPath(((PREG_QUERY_KEY_INFORMATION)Argument2)->Object, ®istryPathBuffer); 119 | if ((registryPathBuffer.Buffer == NULL) || (registryPathBuffer.Length == 0)){ 120 | DbgPrint("RegistryFilter->RegistryCallback->RegNtPreSetValueKey->ExtractKeyPath failed."); 121 | return STATUS_UNSUCCESSFUL; 122 | } 123 | //The documentation says REG_SZ/EXPAND/LINK are UNICODE strings but they are LPWSTR apparently. 124 | switch(((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Type){ 125 | case REG_SZ: 126 | //*((PUNICODE_STRING)(((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Data)) 127 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%wZ<-->%wZ<-->%ls", L"REG", currentTime.QuadPart, processId, L"SETVALUE", ®istryPathBuffer, (((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName), ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Data); 128 | break; 129 | case REG_DWORD: 130 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%wZ<-->%wZ<-->%p", L"REG", currentTime.QuadPart, processId, L"SETVALUE", ®istryPathBuffer, (((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName), (DWORD)((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Data); 131 | break; 132 | case REG_EXPAND_SZ: 133 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%wZ<-->%wZ,<-->%ls", L"REG", currentTime.QuadPart, processId, L"SETVALUE", ®istryPathBuffer, (((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName), ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Data); 134 | break; 135 | break; 136 | case REG_LINK: 137 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%wZ<-->%wZ,<-->%ls", L"REG", currentTime.QuadPart, processId, L"SETVALUE", ®istryPathBuffer, (((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName), ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Data); 138 | break; 139 | break; 140 | default: 141 | return STATUS_SUCCESS; 142 | 143 | break; 144 | } 145 | 146 | break; 147 | case RegNtPreQueryValueKey: 148 | 149 | 150 | ExtractKeyPath(((PREG_QUERY_KEY_INFORMATION)Argument2)->Object, ®istryPathBuffer); 151 | if ((registryPathBuffer.Buffer == NULL) || (registryPathBuffer.Length == 0)){ 152 | DbgPrint("RegistryFilter->RegistryCallback->RegNtPreQueryValueKey->ExtractKeyPath failed."); 153 | return STATUS_UNSUCCESSFUL; 154 | } 155 | 156 | if ((((PREG_QUERY_VALUE_KEY_INFORMATION)Argument2)->ValueName->Buffer == NULL) || (((PREG_QUERY_VALUE_KEY_INFORMATION)Argument2)->ValueName->Length == 0)) 157 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%lld<-->%lu<-->%ls<-->%wZ<-->%ls", L"REG", currentTime.QuadPart, processId, L"QUERYVALUE", ®istryPathBuffer, L""); 158 | else 159 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%lld<-->%lu<-->%ls<-->%wZ<-->%wZ", L"REG", currentTime.QuadPart, processId, L"QUERYVALUE", ®istryPathBuffer, ((PREG_QUERY_VALUE_KEY_INFORMATION)Argument2)->ValueName); 160 | break; 161 | case RegNtDeleteValueKey: 162 | break; 163 | case RegNtSetInformationKey: 164 | break; 165 | case RegNtRenameKey: 166 | break; 167 | case RegNtEnumerateKey: 168 | break; 169 | case RegNtEnumerateValueKey: 170 | break; 171 | case RegNtPreQueryKey: 172 | 173 | ExtractKeyPath(((PREG_QUERY_KEY_INFORMATION)Argument2)->Object, ®istryPathBuffer); 174 | if ((registryPathBuffer.Buffer == NULL) || (registryPathBuffer.Length == 0)){ 175 | DbgPrint("RegistryFilter->RegistryCallback->RegNtPreQueryKey->ExtractKeyPath failed."); 176 | return STATUS_UNSUCCESSFUL; 177 | } 178 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%lld<-->%lu<-->%ls<-->%wZ", L"REG", currentTime.QuadPart, processId, L"QUERYKEY", ®istryPathBuffer); 179 | 180 | break; 181 | case RegNtQueryMultipleValueKey: 182 | break; 183 | case RegNtPreCreateKeyEx: 184 | if (((*(((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName)).Buffer == NULL) || ((*(((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName)).Length == 0)) 185 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%ls", L"REG", currentTime.QuadPart, processId, L"CREATEKEY", L""); 186 | else 187 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%I64u<-->%lu<-->%ls<-->%wZ", L"REG", currentTime.QuadPart, processId, L"CREATEKEY", *(((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName)); 188 | break; 189 | //It seems that, according to https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_reg_pre_create_key_information, PREG_PRE_CREATE_KEY_INFORMATION is a pointer to both REG_PRE_CREATE_KEY_INFORMATION and REG_PRE_OPEN_KEY_INFORMATION. 190 | case RegNtPreOpenKey: 191 | if ((((PREG_PRE_CREATE_KEY_INFORMATION)Argument2)->CompleteName->Buffer == NULL) || (((PREG_PRE_CREATE_KEY_INFORMATION)Argument2)->CompleteName->Length == 0)) 192 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%lld<-->%lu<-->%ls<-->%ls", L"REG", currentTime.QuadPart, processId, L"OPENKEY", L""); 193 | else 194 | tempStatus = RtlStringCbPrintfW(logStringBuffer, MAX_LOG_BUFFER_SIZE, L"%ls<-->%lld<-->%lu<-->%ls<-->%wZ", L"REG", currentTime.QuadPart, processId, L"OPENKEY", ((PREG_PRE_CREATE_KEY_INFORMATION)Argument2)->CompleteName); 195 | break; 196 | default: 197 | break; 198 | 199 | 200 | } 201 | 202 | 203 | if(!NT_SUCCESS(tempStatus)){ 204 | if (tempStatus == STATUS_BUFFER_OVERFLOW){ 205 | DbgPrint("RegistryFilter->RegistryCallback->RtlStringCbPrintfW failed to generate log string: STATUS_BUFFER_OVERFLOW\n"); 206 | } 207 | else if (tempStatus == STATUS_INVALID_PARAMETER){ 208 | DbgPrint("RegistryFilter->RegistryCallback->RtlStringCbPrintfW failed to generate log string: STATUS_INVALID_PARAMETER\n"); 209 | } 210 | ExFreePool(logStringBuffer); 211 | return STATUS_UNSUCCESSFUL; 212 | } 213 | 214 | logString = ExAllocatePool(NonPagedPool, sizeof(UNICODE_STRING)); 215 | if(logString == NULL){ 216 | DbgPrint("RegistryFilter->RegistryCallback->ExAllocatePool failed to allocate memory for log unicode structure.\n"); 217 | ExFreePool(logStringBuffer); 218 | return STATUS_UNSUCCESSFUL; 219 | } 220 | RtlZeroMemory(logString, sizeof(UNICODE_STRING)); 221 | 222 | RtlInitUnicodeString(logString, logStringBuffer); 223 | if ((logString->Buffer == NULL) || (logString->Length == 0) || (logString->MaximumLength == 0)){ 224 | DbgPrint("RegistryFilter->RegistryCallback->RegNtPreQueryValueKey->RtlInitUnicodeString failed to create unicode string.\n"); 225 | ExFreePool(logString); 226 | ExFreePool(logStringBuffer); 227 | return STATUS_UNSUCCESSFUL; 228 | } 229 | 230 | addNode(logString); 231 | 232 | return STATUS_SUCCESS; 233 | 234 | } -------------------------------------------------------------------------------- /Driver/src/Util.c: -------------------------------------------------------------------------------- 1 | #include "Util.h" 2 | #include 3 | #include 4 | 5 | 6 | #define MAX_LIST_SIZE 50000000 7 | PLOGS_LIST globalList = NULL; 8 | PKEVENT globalListSyncEvent = NULL; 9 | 10 | VOID initSyncObject(){ 11 | 12 | globalListSyncEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT)); 13 | KeInitializeEvent(globalListSyncEvent, SynchronizationEvent, TRUE); 14 | 15 | 16 | } 17 | 18 | VOID deleteNode(PLOGS_NODE node){ 19 | RtlFreeUnicodeString(node->entryText); 20 | node->next=NULL; 21 | } 22 | 23 | VOID addNode(PUNICODE_STRING eventString){ 24 | 25 | NTSTATUS tempStatus; 26 | PLOGS_NODE logsNode = NULL; 27 | PLOGS_NODE nodeToDelete = NULL; 28 | PLOGS_LIST listHead = NULL; 29 | 30 | 31 | KeWaitForSingleObject(globalListSyncEvent, Executive, KernelMode,TRUE, NULL); 32 | if (MAX_LIST_SIZE == 0){ 33 | DbgPrint("Util->addNode->ExAllocatePool failed: MAX_LIST_SIZE is zero."); 34 | KeSetEvent(globalListSyncEvent, 0, FALSE); 35 | return; 36 | } 37 | 38 | logsNode = (PLOGS_NODE) ExAllocatePool(NonPagedPool, sizeof(LOGS_NODE)); 39 | 40 | if (logsNode == NULL){ 41 | DbgPrint("Util->addNode->ExAllocatePool failed."); 42 | KeSetEvent(globalListSyncEvent, 0, FALSE); 43 | return; 44 | } 45 | 46 | RtlZeroMemory(logsNode, sizeof(LOGS_NODE)); 47 | 48 | 49 | logsNode->entryText = eventString; 50 | logsNode->next = NULL; 51 | 52 | //Must be initialized... 53 | if(globalList == NULL) 54 | { 55 | listHead = (PLOGS_LIST) ExAllocatePool(NonPagedPool, sizeof(LOGS_LIST)); 56 | if (listHead == NULL){ 57 | DbgPrint("Util->addNode->ExAllocatePool failed: Unable to create list head."); 58 | ExFreePool(logsNode); 59 | KeSetEvent(globalListSyncEvent, 0, FALSE); 60 | return; 61 | } 62 | 63 | RtlZeroMemory(listHead, sizeof(LOGS_LIST)); 64 | listHead->listBegin = logsNode; 65 | listHead->listEnd = logsNode; 66 | listHead->listSize = 1; 67 | 68 | globalList = listHead; 69 | 70 | } 71 | else{ 72 | //List too long, we remove the first node... 73 | if(globalList->listSize > MAX_LIST_SIZE){ 74 | DbgPrint("Util->addNode list full. Droping events."); 75 | nodeToDelete = globalList->listBegin; 76 | globalList->listBegin = globalList->listBegin->next; 77 | globalList->listEnd->next = logsNode; 78 | globalList->listEnd = logsNode; 79 | 80 | deleteNode(nodeToDelete); 81 | KeSetEvent(globalListSyncEvent, 0, FALSE); 82 | return; 83 | } 84 | 85 | else { 86 | 87 | if(globalList->listBegin == NULL){ 88 | globalList->listBegin = logsNode; 89 | globalList->listEnd = logsNode; 90 | globalList->listSize = 1; 91 | KeSetEvent(globalListSyncEvent, 0, FALSE); 92 | return; 93 | } 94 | 95 | //There is only one node pointing to itself. 96 | if((globalList->listBegin->next == NULL) && (globalList->listEnd->next == NULL)){ 97 | globalList->listBegin->next = logsNode; 98 | } 99 | 100 | 101 | globalList->listEnd->next = logsNode; 102 | globalList->listEnd = logsNode; 103 | globalList->listSize++; 104 | KeSetEvent(globalListSyncEvent, 0, FALSE); 105 | return; 106 | 107 | } 108 | 109 | } 110 | 111 | 112 | KeSetEvent(globalListSyncEvent, 0, FALSE); 113 | 114 | } 115 | 116 | 117 | 118 | 119 | 120 | ULONG getOldestLogString(void* outputBuffer, ULONG outputBufferSize){ 121 | 122 | 123 | NTSTATUS tempStatus; 124 | PLOGS_NODE oldestLogEntry = NULL; 125 | PUNICODE_STRING stringCopy = NULL; 126 | PLOGS_NODE nodeToDelete = NULL; 127 | ULONG maxBytesToCopy = 0; 128 | 129 | KeWaitForSingleObject(globalListSyncEvent ,Executive,KernelMode,TRUE, NULL); 130 | 131 | 132 | if((globalList != NULL) && (globalList->listSize > 0)) 133 | { 134 | 135 | if(outputBuffer == NULL){ 136 | DbgPrint("Util->getOldestLogString failed: outputBuffer is NULL."); 137 | KeSetEvent(globalListSyncEvent, 0, FALSE); 138 | return; 139 | } 140 | 141 | if(globalList->listBegin->entryText->Length == 0) 142 | { 143 | DbgPrint("Util->getOldestLogString failed: string to be returned has length zero."); 144 | KeSetEvent(globalListSyncEvent, 0, FALSE); 145 | return; 146 | } 147 | 148 | oldestLogEntry = globalList->listBegin; 149 | maxBytesToCopy = min(oldestLogEntry->entryText->MaximumLength,outputBufferSize); 150 | RtlCopyMemory(outputBuffer, oldestLogEntry->entryText->Buffer, maxBytesToCopy); 151 | 152 | nodeToDelete = globalList->listBegin; 153 | globalList->listBegin = globalList->listBegin->next; 154 | deleteNode(nodeToDelete); 155 | globalList->listSize = (globalList->listSize == 1) ? 0 : (globalList->listSize-1); 156 | 157 | 158 | 159 | } 160 | 161 | KeSetEvent(globalListSyncEvent, 0, FALSE); 162 | return maxBytesToCopy; 163 | } 164 | 165 | 166 | int hasInvalidCharacters(PUNICODE_STRING uString){ 167 | 168 | USHORT index; 169 | wchar_t tempCharValue; 170 | 171 | if(uString == NULL){ 172 | DbgPrint("Util->hasInvalidCharacters failed: uString is NULL."); 173 | return 1; 174 | } 175 | 176 | for(index = 0;(index < uString->Length) && (index < uString->MaximumLength); ++index){ 177 | tempCharValue = uString->Buffer[index]; 178 | //If we get a zero, we can immediatelly return. This means that the string only contains one null byte, when the string is copied around there is a boundary. If the string has some valid characters and then a null, still makes sense. 179 | //As soon as we get an invalid character on the second if (here i assume within the ASCII world to make things simple), we reject the string. Later this can be extended for more characters. 180 | if((unsigned long)tempCharValue == 0) 181 | return 0; 182 | if(((unsigned long)tempCharValue < (unsigned long) 31) || ((unsigned long)tempCharValue > (unsigned long) 126)){ 183 | return 1; 184 | } 185 | 186 | } 187 | return 0; 188 | } 189 | 190 | 191 | -------------------------------------------------------------------------------- /Driver/src/makefile.def: -------------------------------------------------------------------------------- 1 | !IF 0 2 | 3 | Copyright (C) Microsoft Corporation, 1999 - 2002 4 | 5 | Module Name: 6 | 7 | makefile. 8 | 9 | Notes: 10 | 11 | DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source 12 | file to this component. This file merely indirects to the real make file 13 | that is shared by all the components of Windows NT (DDK) 14 | 15 | !ENDIF 16 | 17 | !INCLUDE $(NTMAKEENV)\makefile.def -------------------------------------------------------------------------------- /Driver/src/sources: -------------------------------------------------------------------------------- 1 | TARGETNAME=KernelWhispererDriver 2 | TARGETTYPE=DRIVER 3 | TARGETPATH=..\bin 4 | LINKER_FLAGS=/integritycheck 5 | SRCFILES=KernelWhispererDriver.c FileSystemMonitor.c RegistryMonitor.c NetworkMonitor.c ProcessMonitor.c Util.c ObjectMonitor.c LoadImageMonitor.c 6 | LIBFILES=..\lib\ 7 | 8 | TARGETLIBS= $(TARGETLIBS) \ 9 | $(IFSKIT_LIB_PATH)\fltMgr.lib \ 10 | $(DDK_LIB_PATH)\fwpkclnt.lib 11 | 12 | INCLUDES= $(LIBFILES) 13 | 14 | SOURCES=$(SRCFILES) -------------------------------------------------------------------------------- /KernelWhisperer.inf: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; Kernel Whisperer driver 3 | ;;; 4 | ;;; 5 | ;;; Copyright (c) 1999 - 2001, Microsoft Corporation 6 | ;;; 7 | 8 | [Version] 9 | Signature = "$Windows NT$" 10 | Class = "ActivityMonitor" ;This is determined by the work this filter driver does 11 | ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class 12 | Provider = %ProviderString% 13 | DriverVer = 02/18/2018,1.0 14 | CatalogFile = KernelWhisperer.cat 15 | 16 | [Manufacturer] 17 | %Msft% = Msft, NTx86.6.0, NTamd64.6.0 18 | 19 | [DestinationDirs] 20 | DefaultDestDir = 12 21 | KernelWhisperer.DriverFiles = 12 ;%windir%\system32\drivers 22 | 23 | ;; 24 | ;; Default install sections 25 | ;; 26 | 27 | [DefaultInstall] 28 | OptionDesc = %ServiceDescription% 29 | CopyFiles = KernelWhisperer.DriverFiles 30 | 31 | [DefaultInstall.Services] 32 | AddService = %ServiceName%,,KernelWhisperer.Service 33 | 34 | ;; 35 | ;; Default uninstall sections 36 | ;; 37 | 38 | [DefaultUninstall] 39 | DelFiles = KernelWhisperer.DriverFiles 40 | 41 | [DefaultUninstall.Services] 42 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 43 | 44 | ; 45 | ; Services Section 46 | ; 47 | 48 | [KernelWhisperer.Service] 49 | DisplayName = %ServiceName% 50 | Description = %ServiceDescription% 51 | ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ 52 | Dependencies = "FltMgr" 53 | ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER 54 | StartType = 3 ;SERVICE_DEMAND_START 55 | ErrorControl = 1 ;SERVICE_ERROR_NORMAL 56 | LoadOrderGroup = "KernelWhisperer" 57 | AddReg = KernelWhisperer.AddRegistry 58 | 59 | ; 60 | ; Registry Modifications 61 | ; 62 | 63 | [KernelWhisperer.AddRegistry] 64 | HKR,,"DebugFlags",0x00010001 ,0x0 65 | HKR,,"SupportedFeatures",0x00010001,0x3 66 | HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% 67 | HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% 68 | HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% 69 | 70 | ; 71 | ; Copy Files 72 | ; 73 | 74 | [KernelWhisperer.DriverFiles] 75 | %DriverName%.sys 76 | 77 | [SourceDisksFiles] 78 | KernelWhispererDriver.sys = 1,, 79 | 80 | [SourceDisksNames] 81 | 1 = %DiskId1%,,, 82 | 83 | ;; 84 | ;; String Section 85 | ;; 86 | 87 | [Strings] 88 | Msft = "Microsoft" 89 | ProviderString = "Kernel Whisperer Driver" 90 | ServiceDescription = "Kernel Whisperer Service" 91 | ServiceName = "kernelwhispererdriver" 92 | DriverName = "KernelWhispererDriver" 93 | DiskId1 = "Kernel Whisperer Device Installation Disk" 94 | 95 | ;Instances specific information. 96 | DefaultInstance = "Kernel Whisperer Driver" 97 | Instance1.Name = "Kernel Whisperer Driver" 98 | Instance1.Altitude = "420000" 99 | Instance1.Flags = 0x0 ; Allow all attachments -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Kernel Whisperer 2 | 3 | ## What is it? 4 | 5 | Kernel Whisperer encompasses four components: 6 | * Kernel module: captures events and creates internal strings with enough information to identify the sources of those events. The events are stored internally using a queue implemented using a linked list. 7 | * User-mode application: user-mode application that fetches event strings from the aforementioned queue and stores them on a database. 8 | * Detours dll loaded on all processes through APPInit registry key 9 | * Database (SQL, No-SQL, NewSQL, Graph): database where the kernel events are stored. 10 | 11 | I have developed this tool as a means to collect kernel events in a way that could allow for mining, analytics, etc. 12 | 13 | ## Which events are being caught? 14 | * Filesystem: 15 | * Created Files 16 | * Network: 17 | * TCP: outbound/inbound connect/data 18 | * UDP outbound/inbound data 19 | * ICMP outbound/inbound data 20 | * Process creation/termination 21 | * Registry: 22 | * Create Key (RegCreateKey, RegCreateKeyEx) 23 | * Set Value (RegSetValue, RegSetValueEx) 24 | * Query Key metadata (RegQueryInfoKey) 25 | * Query Value (RegQueryValue, RegQueryValueEx) 26 | * Open Key (RegOpenKey, RegOpenKeyEx) 27 | * Loaded images (i.e. executables, dlls) 28 | * Operations with object handles (e.g. creation/duplication of process/thread handles) 29 | * API calls (only a few but extendable) 30 | 31 | More events will be added to Kernel Whisperer as needed. 32 | 33 | ## How are the tables organized? 34 | * The database is called Events. 35 | * There are seven tables: 36 | * File: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Type (FILE_CREATED, FILE_OPENED, FILE_OVERWRITTEN, FILE_SUPERSEDED, FILE_EXISTS, FILE_DOES_NOT_EXIST), File 37 | * Registry: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Type (CREATEKEY, OPENKEY, QUERYKEY, QUERYVALUE, SETVALUE, OPENKEY), RegKey, Value, Data 38 | * Network: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Protocol (TCP, UDP, ICMP, UNKNOWN), Type (CONNECT, RECV/ACCEPT, LISTEN), LocalIP, LocalPort, RemoteIP, RemotePort 39 | * Process: ID, Timestamp, Hostname, PPid, PImageFilePath, Pstatus (Started, Terminated), Pid, ImageFilePath, CommandLine 40 | * ImageLoads: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, HostProcessPid, HostProcessImageFilePath, LoadedImage (i.e. path for loaded file) 41 | * Objects: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, ObjPid, ObjImageFilePath, ObjType (currently PROCESS or THREAD), HandleOperation (creation or duplication), Permissions (human-readable representation of requested permissions) 42 | * API: ID, Timestamp, Hostname, PPid, PImageFilePath, Pid, ImageFilePath, Function, 43 | 44 | ID is an auto-incremented integer generated for each entry upon insertion. I am using the tuple ID+Timestamp to identify a record since KeQuerySystemTime does not provide a timestamp with enough precision to avoid collisions (i.e. events on the same table with the same timestamp). 45 | 46 | **Note:** Currently, Kernel Whisperer will drop any database called Events before starting the insertions. You can tune this behavior by adjusting the queries on Client->lib->SQLDriver (header and source file). 47 | 48 | ## Interaction with the database 49 | I am not leveraging any API to interact with the database. I wanted Kernel Whisperer to be versatile so that i could switch to any database with a couple of adjustments. As such, i have created a simple Python server that reads queries and executes them directly on the OS through the command line. 50 | 51 | 52 | ## Ports, configurations and modules 53 | * By default, the Python server runs on port 5000 and the client will interact with that same port. The port and IP address for the database can be changed on Client->lib->SQLDriver. 54 | 55 | ## About Detours 56 | Detours is a Microsoft library that you can leverage to intercept calls to OS functions (e.g. CreateFile). This interception is achieved through a dll created by the developper and then loaded within applications. From then on, any calls to detoured functions will be first handled by the dll. As expected, Kernel Whisperer does not intercept every single function provided by Windows but it can be easily extended with functionality as needed. I am currently intercepting: 57 | * WriteProcessMemory 58 | * AdjustTokenPrivileges 59 | 60 | ## How to compile and run Kernel Whisperer? 61 | Start by creating a database on MySQL and running the Python proxy i provide on this project. Make sure you take note of the IP address for the host running the database. You need to change it on Kernel Whisperer's source (Client->lib->SQLDriver). 62 | 63 | 1. Disable Windows driver integrity check and/or enable test signatures. This depends on the operating system. Starting on Windows 7 x64 you need to sign the driver using a Certificate generated by you. For older versions, it is enough to disable integrity verification. Details of the process will be omitted (Google is your friend here). 64 | 2. Install: 65 | * WDK 7.1.0 (https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit) 66 | * Visual Studio or the developper tools 67 | 3. Compiling and deploying driver: 68 | 1. Start->Windows Driver Kits->WDK [VERSION]->Build Environments->Windows 7->x64 Checked Build Environment 69 | 2. Change directory to the folder where Kernel Whisperer is located 70 | 3. Run CompileAndDeployDriver.cmd. **NOTE:** This script will generate the certificate (amongst other stuff) and save it on Driver->staging. Once you generate the certificate you can comment the lines responsible for generating it. 71 | 4. Compiling and deploying client: 72 | 1. Download detours from https://www.microsoft.com/en-us/download/details.aspx?id=52586 and extract it to the root of Kernel Whisperer 73 | 2. Start->Microsoft Visual Studio [VS VERSION]->Visual Studio Tools->Developer Command Prompt for VS[VS VERSION] 74 | 3. Change to Kernel Whisperer's root folder and then detours folder 75 | 4. Run **nmake all** within the directory. Once compilation is over, change back to Kernel Whisperer's root folder 76 | 5. Run CompileAndDeployClient.cmd. 77 | 78 | 79 | ## Test environment 80 | 81 | Kernel Whisperer has been tested with the following environment: 82 | 83 | * Windows 7 Enterprise SP1 x64 (client + driver) + MySQL(Ver 14.14 Distrib 5.5.59) on Linux remnux 3.13.0-53-generic 84 | * Detours 3.0 Express 85 | 86 | ## Found this useful? Help me buy a new version of Ubuntu for my old laptop: 87 | https://www.paypal.com/donate/?hosted_button_id=UDFXULV3WV5GL 88 | -------------------------------------------------------------------------------- /Server/Server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import subprocess 3 | 4 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | 6 | server_address = ('0.0.0.0', 5000) 7 | sock.bind(server_address) 8 | 9 | sock.listen(1000) 10 | 11 | while True: 12 | connection, client_address = sock.accept() 13 | try: 14 | while True: 15 | data = connection.recv(4096) 16 | unicode_data = data.decode('utf-8') 17 | print(unicode_data) 18 | result = subprocess.check_output(['mysql','-u', 'root', '-e', unicode_data.rstrip()]) #If mysql fails, this will stop with an exception. Will keep it to debug erroneous situations. 19 | 20 | finally: 21 | connection.close() --------------------------------------------------------------------------------