├── .gitignore ├── LICENSE.md ├── README.md ├── flux ├── DllMain.cpp ├── MemGuard.cpp ├── MemGuard.h ├── Release │ └── flux.dll ├── flux.VC.db ├── flux.sln ├── flux.vcxproj ├── flux.vcxproj.filters ├── hook.cpp ├── hook.h ├── hook_file.cpp ├── hook_inject.cpp ├── hook_misc.cpp ├── hook_process.cpp ├── hook_registry.cpp ├── log.cpp ├── log.h ├── ntapi.h ├── whitelist.cpp ├── whitelist.h └── x64 │ └── Release │ └── flux.dll └── python ├── addSites.py ├── agent.py ├── controller.py ├── dashboard.py ├── flux ├── flux.py ├── fluxFilter.py ├── fluxFilter.txt └── pipeServer.py ├── postProcess.py ├── resultsServer.py └── rules.yr /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | 198 | # Others 199 | *.VC.opendb 200 | *.pyc 201 | *.zip 202 | *.dll 203 | *.exe -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Endgame, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###Maxwell 2 | Maxwell is an automated system which crawls the web and identifies exploit kit and watering hole activity. It can detect these events with high confidence and perform automated processing to determine which exploit kit was involved. This system can support a variety of research or network defense related initiatives. 3 | 4 | ###Management code 5 | python/controller.py 6 | Primary script for spinning virtual machines up and down. Receives jobs from the Rabbitmq job queue and sends this information to a worker virtual machine. This script is currently compatible with esxi, but could be extended to other virtualization or cloud platforms. 7 | 8 | python/resultsServer.py 9 | Receives events from the virtual machines from the Rabbitmq server. It has 2 main purposes. First, it sends messages to an elasticsearch server for permenant storage. Second, if detects a particular job is malicious, it will launch the post processing routine (postProcess.py). 10 | 11 | python/postProcess.py 12 | Processes the PCAP for a given job if it is detected as malicious. First, tcpflow extracts all sessions from the pcap. Next, all files and run through yara to look for known signatures. Finally, certain regular expressions are run over the data to look for known exploit kit traffic patterns. 13 | 14 | python/dashboard.py 15 | Convenience script for querying Maxwell data from the ElasticSearch backend. It can be run from the command line or could be used to build a web interface on top of Maxwell data. 16 | 17 | python/addSites.py 18 | Example script to parse a file that contains a list of websites and add them to the Maxwell job queue. 19 | 20 | ###VM Code 21 | python/agent.py 22 | Minimal agent that runs in each virtual machine. It monitors for the file 'job.txt' to be created. Once this occurs, it parses the job metadata and executes the corresponding analysis script. 23 | 24 | python/flux/flux.py 25 | Central script for the flux plugin. Peforms initial setup actions for flux instrumentation library such as setting the app_init dll key. Starts and stops the named pipe server and pcap collection. Launches the browser to the target URL. Completes the job after the specified timeout. 26 | 27 | python/flux/pipeServer.py 28 | Listens on a named pipe and forwards messages to the RabbitMQ server. 29 | 30 | python/flux/fluxFilter.py 31 | Filters messages as they flow through the named pipe server that match the rules file fluxFilter.txt 32 | 33 | python/flux/fluxFilter.txt 34 | Contains rules used to filter messages that match strings or regexes. 35 | 36 | ###Instrumentation Library 37 | The Flux instrumentation library is responsible for being loaded into processes across the system. After load, it will hook key APIs in order to collect events such as file writes, process creation, registry writes, and exploit specific behavior. Each event is packed in MessagePack and forwarded to the named pipe server. Flux requires diStorm and msgpack-c which are available here: 38 | https://github.com/gdabah/distorm 39 | https://github.com/msgpack/msgpack-c 40 | 41 | The following files are part of the instrumentation library: 42 | flux/DllMain.cpp 43 | flux/hook.cpp 44 | flux/hook.h 45 | flux/hook_file.cpp 46 | flux/hook_inject.cpp 47 | flux/hook_misc.cpp 48 | flux/hook_network.cpp 49 | flux/hook_process.cpp 50 | flux/hook_registry.cpp 51 | flux/log.cpp 52 | flux/log.h 53 | flux/MemGuard.cpp 54 | flux/MemGuard.h 55 | flux/ntapi.h 56 | flux/whitelist.cpp 57 | flux/whitelist.h 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /flux/DllMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "log.h" 4 | #include "hook.h" 5 | #include "whitelist.h" 6 | #include "MemGuard.h" 7 | 8 | #pragma comment(lib, "Ws2_32.lib") 9 | 10 | CRITICAL_SECTION cs; 11 | 12 | extern LONG CALLBACK VectoredHandler( 13 | _In_ PEXCEPTION_POINTERS ExceptionInfo 14 | ); 15 | 16 | static Hook Hooks[] = 17 | { 18 | /*File*/ 19 | {L"ntdll.dll", "NtWriteFile", MyNtWriteFile, (void**)&OldNtWriteFile}, 20 | {L"ntdll.dll", "NtCreateFile", MyNtCreateFile, (void**)&OldNtCreateFile}, 21 | {L"ntdll.dll", "NtQueryAttributesFile", MyNtQueryAttributesFile, (void**)&OldNtQueryAttributesFile}, 22 | 23 | /*Registry*/ 24 | {L"ntdll.dll", "NtSetValueKey", MyNtSetValueKey, (void**)&OldNtSetValueKey}, 25 | {L"ntdll.dll", "NtOpenKeyEx", MyNtOpenKeyEx, (void**)&OldNtOpenKeyEx}, 26 | 27 | /*Process*/ 28 | {L"ntdll.dll", "NtCreateUserProcess", MyNtCreateUserProcess, (void**)&OldNtCreateUserProcess}, 29 | {L"ntdll.dll", "NtCreateProcess", MyNtCreateProcess, (void**)&OldNtCreateProcess}, 30 | {L"ntdll.dll", "NtCreateProcessEx", MyNtCreateProcessEx, (void**)&OldNtCreateProcessEx}, 31 | 32 | /*Misc*/ 33 | {L"ntdll.dll", "NtDelayExecution", MyNtDelayExecution, (void**)&OldNtDelayExecution}, 34 | {L"ntdll.dll", "NtFreeVirtualMemory", MyNtFreeVirtualMemory, (void**)&OldNtFreeVirtualMemory}, 35 | 36 | }; 37 | 38 | 39 | void FluxMain() 40 | { 41 | if (ProcessWhitelist()) 42 | return; 43 | 44 | HANDLE hThread = INVALID_HANDLE_VALUE; 45 | //hThread = CreateThread(0, 0, WorkThread, 0, 0, 0); 46 | 47 | DWORD retVal = MaxLog::InitLog(); 48 | if (retVal) 49 | { 50 | //printf("InitLog error: %d\n", retVal); 51 | } 52 | 53 | if (strcmp(MaxLog::g_baseExe, "WerFault.exe") == 0) 54 | { 55 | LOG("s", "WerFault", "Application Crash"); 56 | } 57 | 58 | if (hThread != INVALID_HANDLE_VALUE) 59 | { 60 | CloseHandle(hThread); 61 | } 62 | 63 | InitializeCriticalSection(&cs); 64 | 65 | for (int i = 0; i < ARRAYSIZE(Hooks); i++) 66 | { 67 | InstallHook(&Hooks[i]); 68 | } 69 | 70 | InitEAF(); 71 | } 72 | 73 | BOOL WINAPI DllMain( 74 | _In_ HINSTANCE hinstDLL, 75 | _In_ DWORD fdwReason, 76 | _In_ LPVOID lpvReserved 77 | ) 78 | { 79 | 80 | switch (fdwReason) 81 | { 82 | case DLL_PROCESS_ATTACH: 83 | FluxMain(); 84 | break; 85 | case DLL_PROCESS_DETACH: 86 | break; 87 | case DLL_THREAD_ATTACH: 88 | break; 89 | case DLL_THREAD_DETACH: 90 | break; 91 | } 92 | return true; 93 | 94 | } -------------------------------------------------------------------------------- /flux/MemGuard.cpp: -------------------------------------------------------------------------------- 1 | #include "MemGuard.h" 2 | #include "log.h" 3 | #include "ntapi.h" 4 | #include 5 | 6 | void * guardList[20]; 7 | int gCount = 0; 8 | extern const char *g_baseExe; 9 | 10 | void PageGuard(void * Addr) 11 | { 12 | MEMORY_BASIC_INFORMATION mbi; 13 | VirtualQuery(Addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); 14 | if (mbi.State == MEM_COMMIT) 15 | { 16 | if (!(mbi.Protect & PAGE_GUARD)) 17 | { 18 | DWORD flNewProtect = 0; 19 | DWORD flOldProtect = 0; 20 | flNewProtect = mbi.Protect | PAGE_GUARD; 21 | VirtualProtect(Addr, 0x1000, flNewProtect, &flOldProtect); 22 | } 23 | } 24 | } 25 | 26 | DWORD WINAPI GuardThread( 27 | _In_ LPVOID lpParameter 28 | ) 29 | { 30 | while (1) 31 | { 32 | for (int i = 0; i < gCount; i++) 33 | { 34 | PageGuard(guardList[i]); 35 | } 36 | Sleep(10); 37 | } 38 | } 39 | 40 | void * ModuleEAT(DWORD_PTR base) 41 | { 42 | IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)base; 43 | 44 | IMAGE_NT_HEADERS * pNtHeader = (IMAGE_NT_HEADERS *)((DWORD)pDosHeader->e_lfanew + base); 45 | 46 | IMAGE_EXPORT_DIRECTORY * pExport = (IMAGE_EXPORT_DIRECTORY *)(pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + base); 47 | 48 | return (void*)(pExport->Name + base); 49 | } 50 | 51 | void * ModuleIAT(DWORD_PTR base) 52 | { 53 | IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)base; 54 | 55 | IMAGE_NT_HEADERS * pNtHeader = (IMAGE_NT_HEADERS *)((DWORD)pDosHeader->e_lfanew + base); 56 | 57 | IMAGE_IMPORT_DESCRIPTOR * pImport = (IMAGE_IMPORT_DESCRIPTOR *)(pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + base); 58 | 59 | return (void*)(pImport->Name + base); 60 | } 61 | 62 | LONG CALLBACK VectoredHandler( 63 | _In_ PEXCEPTION_POINTERS ExceptionInfo 64 | ) 65 | { 66 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) 67 | { 68 | wchar_t modSource[MAX_PATH]; 69 | wchar_t modTarget[MAX_PATH]; 70 | 71 | if (MaxLog::AddressToModule((DWORD_PTR)ExceptionInfo->ExceptionRecord->ExceptionInformation[1], modTarget, MAX_PATH)) 72 | { 73 | MaxLog::AddressToModule((DWORD_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress, modSource, MAX_PATH); 74 | 75 | MEMORY_BASIC_INFORMATION lpBuffer; 76 | memset(&lpBuffer, '\0', sizeof(MEMORY_BASIC_INFORMATION)); 77 | VirtualQuery((void*)ExceptionInfo->ExceptionRecord->ExceptionAddress, &lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)); 78 | 79 | // we could even get more granular with this, and white list specific functions 80 | if ((lpBuffer.State & MEM_COMMIT) && wcscmp(modSource, L"ntdll.dll") != 0 81 | && wcscmp(modSource, L"IEShims.dll") != 0 82 | && wcscmp(modSource, L"apphelp.dll") != 0 83 | && wcscmp(modSource, L"msvcrt.dll") != 0 84 | && wcscmp(modSource, L"shlwapi.dll") != 0) 85 | { 86 | LOG("suuppp", "EAF", "GuardPage", "ModSource", modSource, "ModTarget", modTarget, "SourceAddress", (DWORD_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress, "TargetAddres", (DWORD_PTR)ExceptionInfo->ExceptionRecord->ExceptionInformation[1], "MemType", lpBuffer.Type); 87 | 88 | char outFile[MAX_PATH]; 89 | sprintf_s(outFile, MAX_PATH, "C:\\%s_EAF_%x", MaxLog::g_baseExe, lpBuffer.BaseAddress); 90 | 91 | //LOG("sb", "FileName", outFile, "FileData", lpBuffer.BaseAddress, lpBuffer.RegionSize); 92 | 93 | HANDLE hFile = CreateFileA(outFile, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); 94 | if (hFile != INVALID_HANDLE_VALUE) 95 | { 96 | DWORD dWritten = 0; 97 | WriteFile(hFile, lpBuffer.BaseAddress, lpBuffer.RegionSize, &dWritten, 0); 98 | CloseHandle(hFile); 99 | } 100 | 101 | 102 | } 103 | } 104 | 105 | // Virtual Query ExceptionAddress, see if its outside MEM_IMAGE, or other sketchy address 106 | // Or maybe just whitelist the areas it normally comes from 107 | 108 | 109 | return EXCEPTION_CONTINUE_EXECUTION; 110 | 111 | } 112 | else if (ExceptionInfo->ExceptionRecord->ExceptionCode >= STATUS_ACCESS_VIOLATION && ExceptionInfo->ExceptionRecord->ExceptionCode <= STATUS_SXS_INVALID_DEACTIVATION) 113 | { 114 | /* Uncomment this to enable logging of crashes/access violations/etc */ 115 | //LOG("ll", "ExceptionCode", ExceptionInfo->ExceptionRecord->ExceptionCode, "ExceptionAddress", ExceptionInfo->ExceptionRecord->ExceptionAddress); 116 | 117 | wchar_t modSource[MAX_PATH]; 118 | MaxLog::AddressToModule((DWORD_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress, modSource, MAX_PATH); 119 | if (_wcsicmp(modSource, L"kernel32.dll") != 0 && _wcsicmp(modSource, L"kernelbase.dll") != 0) 120 | //if ( _wcsicmp(modSource, L"flux32.dll") == 0 ) 121 | { 122 | LOG("xu", "ExceptionCode", ExceptionInfo->ExceptionRecord->ExceptionCode, "Module", modSource); 123 | } 124 | 125 | } 126 | return EXCEPTION_CONTINUE_SEARCH; 127 | //return EXCEPTION_EXECUTE_HANDLER; 128 | } 129 | 130 | PWCHAR wcsistr(PWCHAR wcs1, PWCHAR wcs2) 131 | { 132 | const wchar_t *s1, *s2; 133 | const wchar_t l = towlower(*wcs2); 134 | const wchar_t u = towupper(*wcs2); 135 | 136 | if (!*wcs2) 137 | return wcs1; 138 | 139 | for (; *wcs1; ++wcs1) 140 | { 141 | if (*wcs1 == l || *wcs1 == u) 142 | { 143 | s1 = wcs1 + 1; 144 | s2 = wcs2 + 1; 145 | 146 | while (*s1 && *s2 && towlower(*s1) == towlower(*s2)) 147 | ++s1, ++s2; 148 | 149 | if (!*s2) 150 | return wcs1; 151 | } 152 | } 153 | 154 | return NULL; 155 | } 156 | 157 | 158 | VOID CALLBACK LdrDllNotification( 159 | _In_ ULONG NotificationReason, 160 | _In_ PLDR_DLL_NOTIFICATION_DATA NotificationData, 161 | _In_opt_ PVOID Context 162 | ) 163 | { 164 | if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED) 165 | { 166 | //printf("Loaded %ws\n", NotificationData->Loaded.BaseDllName->Buffer); 167 | if (wcsistr(NotificationData->Loaded.BaseDllName->Buffer, L"flash")) 168 | { 169 | //LOG("s", "Flash", "Loaded"); 170 | // Protect flash IAT 171 | guardList[gCount] = ModuleIAT((DWORD_PTR)NotificationData->Loaded.DllBase); 172 | gCount++; 173 | 174 | // We could also protect MZ header, but would need to filter flash._ValidateImageBase() 175 | // http://www.bigmessowires.com/2015/10/02/what-happens-before-main/. 176 | 177 | } 178 | } 179 | 180 | } 181 | // http://phrack.org/issues/63/15.html - shellcode techniques/detections 182 | 183 | void InitEAF() 184 | { 185 | AddVectoredExceptionHandler(0, VectoredHandler); 186 | 187 | // Get memory location of kernel32 188 | DWORD_PTR kernel32 = (DWORD_PTR)GetModuleHandle(L"kernel32.dll"); 189 | // Protect MZ header 190 | guardList[gCount] = (void*)kernel32; 191 | gCount++; 192 | // Protect Export Address Table 193 | guardList[gCount] = ModuleEAT(kernel32); 194 | gCount++; 195 | // Protect Imports 196 | guardList[gCount] = ModuleIAT(kernel32); 197 | gCount++; 198 | 199 | DWORD_PTR ntdll = (DWORD_PTR)GetModuleHandle(L"ntdll.dll"); 200 | // Protect MZ header 201 | guardList[gCount] = (void*)ntdll; 202 | gCount++; 203 | // Protect Export Address Table 204 | guardList[gCount] = ModuleEAT(ntdll); 205 | gCount++; 206 | // Protect Imports 207 | guardList[gCount] = ModuleIAT(ntdll); 208 | gCount++; 209 | 210 | DWORD_PTR kernelbase = (DWORD_PTR)GetModuleHandle(L"kernelbase.dll"); 211 | // Protect MZ header 212 | guardList[gCount] = (void*)kernelbase; 213 | gCount++; 214 | // Protect Export Address Table 215 | guardList[gCount] = ModuleEAT(kernelbase); 216 | gCount++; 217 | // Protect Imports 218 | guardList[gCount] = ModuleIAT(kernelbase); 219 | gCount++; 220 | 221 | /* 222 | race condition w/ IE associated with this- iexplore.exe Exception Source: rpcrt4.dll Code: 0xC0020043 223 | if (strcmp(MaxLog::g_baseExe, "iexplore.exe") == 0) 224 | { 225 | // Will need to filter msvcrt.dll for this 226 | // Protect MZ header 227 | guardList[gCount] = (void*)GetModuleHandle(0); 228 | gCount++; 229 | } 230 | */ 231 | 232 | _LdrRegisterDllNotification LdrRegisterDllNotification = (_LdrRegisterDllNotification)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "LdrRegisterDllNotification"); 233 | if (LdrRegisterDllNotification) 234 | { 235 | void * cookie; 236 | LdrRegisterDllNotification(0, LdrDllNotification, 0, &cookie); 237 | } 238 | CreateThread(0, 0, GuardThread, 0, 0, 0); 239 | } 240 | -------------------------------------------------------------------------------- /flux/MemGuard.h: -------------------------------------------------------------------------------- 1 | #ifndef __EAF_H__ 2 | #define __EAF_H__ 3 | #include 4 | #include 5 | 6 | LONG CALLBACK VectoredHandler( 7 | _In_ PEXCEPTION_POINTERS ExceptionInfo 8 | ); 9 | 10 | void InitEAF(); 11 | bool AddressToModule(DWORD_PTR Addr, wchar_t * modName, unsigned int size); 12 | 13 | 14 | extern void * guardList[20]; 15 | extern int gCount; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /flux/Release/flux.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/Maxwell/db4d8b189ff2f39f7c5235bb6a1a9974f7532a8c/flux/Release/flux.dll -------------------------------------------------------------------------------- /flux/flux.VC.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/Maxwell/db4d8b189ff2f39f7c5235bb6a1a9974f7532a8c/flux/flux.VC.db -------------------------------------------------------------------------------- /flux/flux.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flux", "flux.vcxproj", "{09A05749-DA54-43D9-8117-45B7C588A0C9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Debug|x64.ActiveCfg = Debug|x64 17 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Debug|x64.Build.0 = Debug|x64 18 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Debug|x86.ActiveCfg = Debug|Win32 19 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Debug|x86.Build.0 = Debug|Win32 20 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Release|x64.ActiveCfg = Release|x64 21 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Release|x64.Build.0 = Release|x64 22 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Release|x86.ActiveCfg = Release|Win32 23 | {09A05749-DA54-43D9-8117-45B7C588A0C9}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /flux/flux.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {09A05749-DA54-43D9-8117-45B7C588A0C9} 42 | Win32Proj 43 | flux 44 | 8.1 45 | 46 | 47 | 48 | DynamicLibrary 49 | true 50 | v140 51 | Unicode 52 | 53 | 54 | DynamicLibrary 55 | false 56 | v140 57 | true 58 | Unicode 59 | 60 | 61 | DynamicLibrary 62 | true 63 | v140 64 | Unicode 65 | 66 | 67 | DynamicLibrary 68 | false 69 | v140 70 | true 71 | Unicode 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | true 93 | 94 | 95 | true 96 | 97 | 98 | false 99 | 100 | 101 | false 102 | 103 | 104 | 105 | 106 | 107 | Level3 108 | Disabled 109 | WIN32;_DEBUG;_WINDOWS;_USRDLL;FLUX_EXPORTS;%(PreprocessorDefinitions) 110 | true 111 | 112 | 113 | Windows 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | Level3 122 | Disabled 123 | _DEBUG;_WINDOWS;_USRDLL;FLUX_EXPORTS;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Windows 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | 135 | 136 | MaxSpeed 137 | true 138 | true 139 | WIN32;NDEBUG;_WINDOWS;_USRDLL;FLUX_EXPORTS;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | Level3 152 | 153 | 154 | MaxSpeed 155 | true 156 | true 157 | NDEBUG;_WINDOWS;_USRDLL;FLUX_EXPORTS;%(PreprocessorDefinitions) 158 | true 159 | 160 | 161 | Windows 162 | true 163 | true 164 | true 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /flux/flux.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | -------------------------------------------------------------------------------- /flux/hook.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "distorm\distorm.h" 3 | #include "hook.h" 4 | 5 | #ifdef _WIN64 6 | #pragma comment(lib,"distorm\\distorm64.lib") 7 | #else 8 | #pragma comment(lib,"distorm\\distorm32.lib") 9 | #endif 10 | 11 | #ifdef _WIN64 12 | void InstallHook(Hook * hook) 13 | { 14 | DWORD dwOld; 15 | LPVOID trampAddr = NULL; 16 | int trampSize = 0; 17 | 18 | // allocate tramp buffer 19 | trampAddr = VirtualAlloc(0, 37, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 20 | unsigned __int64 trampAddrInt = (unsigned __int64)trampAddr; 21 | 22 | memset(trampAddr, '\x90', 37); 23 | 24 | // find target function 25 | PVOID targetFunc = (PVOID)GetProcAddress(GetModuleHandle(hook->libName), hook->functionName); 26 | 27 | if (targetFunc == 0) 28 | return; 29 | 30 | 31 | // distorm code 32 | // How many instructions to allocate on stack. 33 | #define MAX_INSTRUCTIONS 32 34 | // Holds the result of the decoding. 35 | _DecodeResult res; 36 | // Default offset for buffer is 0. 37 | _OffsetType offset = 0; 38 | // Decoded instruction information - the Decode will write the results here. 39 | _DecodedInst decodedInstructions[MAX_INSTRUCTIONS]; 40 | // decodedInstructionsCount indicates how many instructions were written to the result array. 41 | unsigned int decodedInstructionsCount = 0; 42 | // Default decoding mode is 32 bits. 43 | _DecodeType dt = Decode64Bits; 44 | 45 | // Decode the buffer at given offset (virtual address). 46 | res = distorm_decode(offset, (const unsigned char*)targetFunc, 32, dt, decodedInstructions, MAX_INSTRUCTIONS, &decodedInstructionsCount); 47 | 48 | if (res == DECRES_INPUTERR) 49 | return; 50 | 51 | unsigned int totalSize = 0; 52 | 53 | for (unsigned int x = 0; x < decodedInstructionsCount; x++) 54 | { 55 | if (totalSize >= 12) 56 | break; 57 | totalSize += decodedInstructions[x].size; 58 | } 59 | // end distorm code 60 | //log("Total size of tramp: %d", totalSize); 61 | trampSize = totalSize; 62 | 63 | 64 | *hook->oldFunc = (void*)trampAddr; 65 | 66 | unsigned __int64 targetFuncInt = (unsigned __int64)targetFunc; 67 | 68 | //copy first x bytes of function to tramp 69 | memcpy(trampAddr, targetFunc, totalSize); 70 | //create a jump to original function+totalSize from tramp 71 | trampAddrInt += totalSize; 72 | memcpy((PVOID)trampAddrInt, "\x48\xb8", 2); 73 | trampAddrInt += 2; 74 | targetFuncInt += totalSize; 75 | memcpy((PVOID)trampAddrInt, &targetFuncInt, 8); 76 | trampAddrInt += 8; 77 | memcpy((PVOID)trampAddrInt, "\xff\xe0", 2); 78 | // trampoline has been constructed 79 | 80 | //reset pointer 81 | targetFuncInt = (unsigned __int64)targetFunc; 82 | 83 | //set target function writeable, should probably set its old permissions for stealth 84 | VirtualProtect((LPVOID)targetFunc, 37, PAGE_EXECUTE_READWRITE, &dwOld); 85 | 86 | //intercept target function, send all calls to my function 87 | unsigned __int64 myFuncInt = (unsigned __int64)hook->myFunc; 88 | memcpy((PVOID)targetFuncInt, "\x48\xb8", 2); 89 | targetFuncInt += 2; 90 | memcpy((PVOID)targetFuncInt, &myFuncInt, 8); 91 | targetFuncInt += 8; 92 | memcpy((PVOID)targetFuncInt, "\xff\xe0", 2); 93 | targetFuncInt += 2; 94 | 95 | // fix memory protection for hooked function 96 | VirtualProtect((LPVOID)targetFunc, 37, dwOld, &dwOld); 97 | 98 | // hooking is now complete 99 | 100 | } 101 | #else 102 | 103 | void InstallHook(Hook * hook) 104 | { 105 | DWORD dwOld; 106 | LPVOID trampAddr = NULL; 107 | int trampSize = 0; 108 | 109 | // allocate tramp buffer 110 | trampAddr = VirtualAlloc(0, 37, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 111 | 112 | DWORD trampAddrPtr = (DWORD)trampAddr; 113 | 114 | memset(trampAddr, '\x90', 37); 115 | 116 | // find target function 117 | PVOID targetFunc = (PVOID)GetProcAddress(GetModuleHandle(hook->libName), hook->functionName); 118 | 119 | if (targetFunc == 0) 120 | return; 121 | 122 | // distorm code 123 | // How many instructions to allocate on stack. 124 | #define MAX_INSTRUCTIONS 32 125 | // Holds the result of the decoding. 126 | _DecodeResult res; 127 | // Default offset for buffer is 0. 128 | _OffsetType offset = 0; 129 | // Decoded instruction information - the Decode will write the results here. 130 | _DecodedInst decodedInstructions[MAX_INSTRUCTIONS]; 131 | // decodedInstructionsCount indicates how many instructions were written to the result array. 132 | unsigned int decodedInstructionsCount = 0; 133 | // Default decoding mode is 32 bits. 134 | _DecodeType dt = Decode32Bits; 135 | 136 | // Decode the buffer at given offset (virtual address). 137 | res = distorm_decode(offset, (const unsigned char*)targetFunc, 32, dt, decodedInstructions, MAX_INSTRUCTIONS, &decodedInstructionsCount); 138 | 139 | if (res == DECRES_INPUTERR) 140 | return; 141 | 142 | unsigned int totalSize = 0; 143 | 144 | for (unsigned int x = 0; x < decodedInstructionsCount; x++) 145 | { 146 | if (totalSize >= 5) 147 | break; 148 | totalSize += decodedInstructions[x].size; 149 | } 150 | // end distorm code 151 | //log("Total size of tramp: %d", totalSize); 152 | 153 | trampSize = totalSize; 154 | 155 | *hook->oldFunc = (void*)trampAddr; 156 | 157 | DWORD targetFuncPtr = (DWORD)targetFunc; 158 | 159 | ULONG bytes = 20; 160 | //set target function writeable 161 | //if (strncmp(hook->funcName, "NtProtectVirtualMemory", sizeof("NtProtectVirtualMemory") -1) == 0) 162 | VirtualProtect((LPVOID)targetFunc, 37, PAGE_EXECUTE_READWRITE, &dwOld); 163 | //else 164 | // Old_NtProtectVirtualMemory(GetCurrentProcess(), &targetFunc, &bytes, PAGE_EXECUTE_READWRITE, &dwOld); 165 | 166 | //copy instructions of function to tramp 167 | memcpy(trampAddr, targetFunc, totalSize); 168 | //create a jump to original function+5 from tramp 169 | trampAddrPtr += totalSize; 170 | memcpy((PVOID)trampAddrPtr, "\xe9", 1); 171 | // offset = destination - address of e9 - 5 172 | int myOffset = (int)targetFuncPtr + totalSize - (int)trampAddrPtr - 5; 173 | trampAddrPtr += 1; 174 | memcpy((PVOID)trampAddrPtr, &myOffset, 4); 175 | // trampoline has been constructed 176 | 177 | //reset pointer 178 | targetFuncPtr = (DWORD)targetFunc; 179 | 180 | //intercept target function, send all calls to my function 181 | DWORD myFuncPtr = (DWORD)hook->myFunc; 182 | memcpy((PVOID)targetFuncPtr, "\xe9", 1); 183 | // offset = destination - address of e9 - 5 184 | myOffset = (int)myFuncPtr - (int)targetFuncPtr - 5; 185 | targetFuncPtr += 1; 186 | memcpy((PVOID)targetFuncPtr, &myOffset, 4); 187 | 188 | // fix memory protection for hooked function 189 | VirtualProtect((LPVOID)targetFunc, 37, dwOld, &dwOld); 190 | 191 | } 192 | 193 | #endif 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /flux/hook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ntapi.h" 3 | 4 | extern NTSTATUS(WINAPI * OldNtSetValueKey) 5 | ( 6 | HANDLE KeyHandle, 7 | PUNICODE_STRING ValueName, 8 | ULONG TitleIndex, 9 | ULONG Type, 10 | PVOID Data, 11 | ULONG DataSize 12 | ); 13 | 14 | NTSTATUS WINAPI MyNtSetValueKey 15 | ( 16 | HANDLE KeyHandle, 17 | PUNICODE_STRING ValueName, 18 | ULONG TitleIndex, 19 | ULONG Type, 20 | PVOID Data, 21 | ULONG DataSize 22 | ); 23 | 24 | extern NTSTATUS(WINAPI * OldNtOpenKeyEx) 25 | ( 26 | PHANDLE KeyHandle, 27 | ACCESS_MASK DesiredAccess, 28 | POBJECT_ATTRIBUTES ObjectAttributes, 29 | ULONG OpenOptions 30 | ); 31 | 32 | NTSTATUS WINAPI MyNtOpenKeyEx 33 | ( 34 | PHANDLE KeyHandle, 35 | ACCESS_MASK DesiredAccess, 36 | POBJECT_ATTRIBUTES ObjectAttributes, 37 | ULONG OpenOptions 38 | ); 39 | 40 | extern NTSTATUS(NTAPI * OldNtCreateUserProcess) 41 | ( 42 | PHANDLE ProcessHandle, 43 | PHANDLE ThreadHandle, 44 | ACCESS_MASK ProcessDesiredAccess, 45 | ACCESS_MASK ThreadDesiredAccess, 46 | POBJECT_ATTRIBUTES ProcessObjectAttributes, 47 | POBJECT_ATTRIBUTES ThreadObjectAttributes, 48 | ULONG ProcessFlags, 49 | ULONG ThreadFlags, 50 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters, 51 | void * CreateInfo, 52 | void * AttributeList 53 | ); 54 | 55 | NTSTATUS NTAPI MyNtCreateUserProcess 56 | ( 57 | PHANDLE ProcessHandle, 58 | PHANDLE ThreadHandle, 59 | ACCESS_MASK ProcessDesiredAccess, 60 | ACCESS_MASK ThreadDesiredAccess, 61 | POBJECT_ATTRIBUTES ProcessObjectAttributes, 62 | POBJECT_ATTRIBUTES ThreadObjectAttributes, 63 | ULONG ProcessFlags, 64 | ULONG ThreadFlags, 65 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters, 66 | void * CreateInfo, 67 | void * AttributeList 68 | ); 69 | 70 | 71 | extern NTSTATUS(WINAPI * OldNtCreateProcess) 72 | ( 73 | PHANDLE ProcessHandle, 74 | ACCESS_MASK DesiredAccess, 75 | POBJECT_ATTRIBUTES ObjectAttributes, 76 | HANDLE ParentProcess, 77 | BOOLEAN InheritObjectTable, 78 | HANDLE SectionHandle, 79 | HANDLE DebugPort, 80 | HANDLE ExceptionPort 81 | ); 82 | 83 | NTSTATUS WINAPI MyNtCreateProcess 84 | ( 85 | PHANDLE ProcessHandle, 86 | ACCESS_MASK DesiredAccess, 87 | POBJECT_ATTRIBUTES ObjectAttributes, 88 | HANDLE ParentProcess, 89 | BOOLEAN InheritObjectTable, 90 | HANDLE SectionHandle, 91 | HANDLE DebugPort, 92 | HANDLE ExceptionPort 93 | ); 94 | 95 | extern NTSTATUS(WINAPI * OldNtCreateProcessEx) 96 | ( 97 | PHANDLE ProcessHandle, 98 | ACCESS_MASK DesiredAccess, 99 | POBJECT_ATTRIBUTES ObjectAttributes, 100 | HANDLE ParentProcess, 101 | ULONG Flags, 102 | HANDLE SectionHandle, 103 | HANDLE DebugPort, 104 | HANDLE ExceptionPort, 105 | BOOLEAN InJob 106 | ); 107 | 108 | NTSTATUS WINAPI MyNtCreateProcessEx 109 | ( 110 | PHANDLE ProcessHandle, 111 | ACCESS_MASK DesiredAccess, 112 | POBJECT_ATTRIBUTES ObjectAttributes, 113 | HANDLE ParentProcess, 114 | ULONG Flags, 115 | HANDLE SectionHandle, 116 | HANDLE DebugPort, 117 | HANDLE ExceptionPort, 118 | BOOLEAN InJob 119 | ); 120 | 121 | extern NTSTATUS(WINAPI * OldNtDelayExecution) 122 | ( 123 | BOOLEAN Alertable, 124 | PLARGE_INTEGER DelayInterval 125 | ); 126 | NTSTATUS WINAPI MyNtDelayExecution 127 | ( 128 | BOOLEAN Alertable, 129 | PLARGE_INTEGER DelayInterval 130 | ); 131 | 132 | 133 | extern NTSTATUS(WINAPI * OldNtProtectVirtualMemory) 134 | ( 135 | HANDLE ProcessHandle, 136 | PVOID *BaseAddress, 137 | PULONG NumberOfBytesToProtect, 138 | ULONG NewAccessProtection, 139 | PULONG OldAccessProtection 140 | ); 141 | 142 | NTSTATUS WINAPI MyNtProtectVirtualMemory 143 | ( 144 | HANDLE ProcessHandle, 145 | PVOID *BaseAddress, 146 | PULONG NumberOfBytesToProtect, 147 | ULONG NewAccessProtection, 148 | PULONG OldAccessProtection 149 | ); 150 | 151 | extern NTSTATUS(WINAPI * OldNtFreeVirtualMemory) 152 | ( 153 | HANDLE ProcessHandle, 154 | PVOID *BaseAddress, 155 | PSIZE_T RegionSize, 156 | ULONG FreeType 157 | ); 158 | 159 | NTSTATUS WINAPI MyNtFreeVirtualMemory 160 | ( 161 | HANDLE ProcessHandle, 162 | PVOID *BaseAddress, 163 | PSIZE_T RegionSize, 164 | ULONG FreeType 165 | ); 166 | 167 | extern NTSTATUS(WINAPI * OldNtWriteFile) 168 | ( 169 | HANDLE FileHandle, 170 | HANDLE Event, 171 | PVOID ApcRoutine, 172 | PVOID ApcContext, 173 | PVOID IoStatusBlock, 174 | PVOID Buffer, 175 | ULONG Length, 176 | PLARGE_INTEGER ByteOffset, 177 | PULONG Key 178 | ); 179 | 180 | NTSTATUS WINAPI MyNtWriteFile 181 | ( 182 | HANDLE FileHandle, 183 | HANDLE Event, 184 | PVOID ApcRoutine, 185 | PVOID ApcContext, 186 | PVOID IoStatusBlock, 187 | PVOID Buffer, 188 | ULONG Length, 189 | PLARGE_INTEGER ByteOffset, 190 | PULONG Key 191 | ); 192 | 193 | extern NTSTATUS(WINAPI *OldNtCreateFile) 194 | ( 195 | PHANDLE FileHandle, 196 | ACCESS_MASK DesiredAccess, 197 | POBJECT_ATTRIBUTES ObjectAttributes, 198 | PVOID IoStatusBlock, 199 | PLARGE_INTEGER AllocationSize, 200 | ULONG FileAttributes, 201 | ULONG ShareAccess, 202 | ULONG CreateDisposition, 203 | ULONG CreateOptions, 204 | PVOID EaBuffer, 205 | ULONG EaLength 206 | ); 207 | 208 | NTSTATUS WINAPI MyNtCreateFile 209 | ( 210 | PHANDLE FileHandle, 211 | ACCESS_MASK DesiredAccess, 212 | POBJECT_ATTRIBUTES ObjectAttributes, 213 | PVOID IoStatusBlock, 214 | PLARGE_INTEGER AllocationSize, 215 | ULONG FileAttributes, 216 | ULONG ShareAccess, 217 | ULONG CreateDisposition, 218 | ULONG CreateOptions, 219 | PVOID EaBuffer, 220 | ULONG EaLength 221 | ); 222 | 223 | 224 | extern NTSTATUS(WINAPI * OldNtQueryAttributesFile) 225 | ( 226 | POBJECT_ATTRIBUTES ObjectAttributes, 227 | PFILE_BASIC_INFORMATION FileInformation 228 | ); 229 | 230 | NTSTATUS WINAPI MyNtQueryAttributesFile 231 | ( 232 | POBJECT_ATTRIBUTES ObjectAttributes, 233 | PFILE_BASIC_INFORMATION FileInformation 234 | ); 235 | 236 | typedef struct 237 | { 238 | wchar_t * libName; 239 | char * functionName; 240 | void * myFunc; 241 | void ** oldFunc; 242 | } Hook; 243 | 244 | void InstallHook(Hook * hook); 245 | -------------------------------------------------------------------------------- /flux/hook_file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "hook.h" 4 | #include "ntapi.h" 5 | #include "log.h" 6 | #include "whitelist.h" 7 | 8 | typedef NTSTATUS(NTAPI * pNtQueryInformationFile)( 9 | HANDLE FileHandle, 10 | PVOID IoStatusBlock, 11 | PVOID FileInformation, 12 | ULONG Length, 13 | FILE_INFORMATION_CLASS FileInformationClass); 14 | 15 | typedef NTSTATUS(NTAPI * pNtCreateFile)( 16 | PHANDLE FileHandle, 17 | ACCESS_MASK DesiredAccess, 18 | POBJECT_ATTRIBUTES ObjectAttributes, 19 | PVOID IoStatusBlock, 20 | PLARGE_INTEGER AllocationSize, 21 | ULONG FileAttributes, 22 | ULONG ShareAccess, 23 | ULONG CreateDisposition, 24 | ULONG CreateOptions, 25 | PVOID EaBuffer, 26 | ULONG EaLength 27 | ); 28 | 29 | bool HandleToFilePath(HANDLE FileHandle, char * filePath,wchar_t * widefilePath, int size) 30 | { 31 | char iosb[256]; 32 | char buffer[MAX_PATH * 2]; 33 | 34 | FILE_NAME_INFORMATION * fileNameInfo = (FILE_NAME_INFORMATION *)buffer; 35 | ZeroMemory(fileNameInfo, MAX_PATH * 2); 36 | // FileNameInformation = 9 37 | pNtQueryInformationFile NtQueryInformationFile = (pNtQueryInformationFile)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQueryInformationFile"); 38 | if (NtQueryInformationFile) 39 | { 40 | 41 | NTSTATUS status = NtQueryInformationFile(FileHandle, &iosb, fileNameInfo, MAX_PATH * 2, FileNameInformation); 42 | if (status == STATUS_SUCCESS) 43 | { 44 | size_t ReturnValue; 45 | int retVal = wcstombs_s(&ReturnValue, filePath, size, fileNameInfo->FileName,size); 46 | wcscpy_s(widefilePath, 512, fileNameInfo->FileName); 47 | if (ReturnValue > 0 && retVal == 0) 48 | return true; 49 | else 50 | return false; 51 | } 52 | } 53 | 54 | return false; 55 | 56 | 57 | } 58 | 59 | 60 | /* ToDo: 61 | Walk callstack for things outside loaded modules, should be able to detect shellcode dropping files. 62 | */ 63 | NTSTATUS(WINAPI * OldNtWriteFile) 64 | ( 65 | HANDLE FileHandle, 66 | HANDLE Event, 67 | PVOID ApcRoutine, 68 | PVOID ApcContext, 69 | PVOID IoStatusBlock, 70 | PVOID Buffer, 71 | ULONG Length, 72 | PLARGE_INTEGER ByteOffset, 73 | PULONG Key 74 | ); 75 | 76 | NTSTATUS WINAPI MyNtWriteFile 77 | ( 78 | HANDLE FileHandle, 79 | HANDLE Event, 80 | PVOID ApcRoutine, 81 | PVOID ApcContext, 82 | PVOID IoStatusBlock, 83 | PVOID Buffer, 84 | ULONG Length, 85 | PLARGE_INTEGER ByteOffset, 86 | PULONG Key 87 | ) 88 | { 89 | try 90 | { 91 | NTSTATUS retVal = OldNtWriteFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key); 92 | if (retVal == STATUS_SUCCESS && Length > 0) 93 | { 94 | char filePath[512]; 95 | wchar_t widefilePath[512]; 96 | if (HandleToFilePath(FileHandle, filePath, widefilePath, 512)) 97 | { 98 | if (!IsFilePathWhitelisted(widefilePath, wcslen(widefilePath))) 99 | { 100 | LOG("slb", "FileName", filePath, "Length", Length, "FileData", Buffer, Length); 101 | } 102 | } 103 | } 104 | return retVal; 105 | } 106 | catch (...) 107 | { 108 | LOG("s", "Exception", "NtWriteFile"); 109 | return -1; 110 | } 111 | } 112 | 113 | bool MemSearch(void * needle, SIZE_T needleSize, void * haystack, SIZE_T haystackSize) 114 | { 115 | if (needleSize > haystackSize) 116 | return false; 117 | 118 | for (SIZE_T i = 0; i <= haystackSize - needleSize; i++) 119 | { 120 | if (memcmp((char*)haystack + i, needle, needleSize) == 0) 121 | return true; 122 | } 123 | 124 | return false; 125 | 126 | } 127 | 128 | const wchar_t * VMDetect[] = 129 | { 130 | L"TPAutoConnSvc" 131 | L"Bitdefender Agent", 132 | L"ESET NOD32 Antivirus", 133 | L"\\FFDec\\" 134 | L"Wireshark", 135 | L"Fiddler", 136 | //L"VMware Tools", possible whiteops FP 137 | //L"VirtualBox Guest Additions", possible whiteops FP 138 | 139 | }; 140 | 141 | NTSTATUS(WINAPI *OldNtCreateFile) 142 | ( 143 | PHANDLE FileHandle, 144 | ACCESS_MASK DesiredAccess, 145 | POBJECT_ATTRIBUTES ObjectAttributes, 146 | PVOID IoStatusBlock, 147 | PLARGE_INTEGER AllocationSize, 148 | ULONG FileAttributes, 149 | ULONG ShareAccess, 150 | ULONG CreateDisposition, 151 | ULONG CreateOptions, 152 | PVOID EaBuffer, 153 | ULONG EaLength 154 | ); 155 | 156 | NTSTATUS WINAPI MyNtCreateFile 157 | ( 158 | PHANDLE FileHandle, 159 | ACCESS_MASK DesiredAccess, 160 | POBJECT_ATTRIBUTES ObjectAttributes, 161 | PVOID IoStatusBlock, 162 | PLARGE_INTEGER AllocationSize, 163 | ULONG FileAttributes, 164 | ULONG ShareAccess, 165 | ULONG CreateDisposition, 166 | ULONG CreateOptions, 167 | PVOID EaBuffer, 168 | ULONG EaLength 169 | ) 170 | { 171 | try 172 | { 173 | if (ObjectAttributes) 174 | { 175 | if (ObjectAttributes->ObjectName) 176 | { 177 | if (ObjectAttributes->ObjectName->Buffer) 178 | { 179 | for (int i = 0; i < ARRAYSIZE(VMDetect); i++) 180 | { 181 | if (MemSearch((void*)VMDetect[i], wcslen(VMDetect[i]) * 2, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length)) 182 | { 183 | LOG("o", "VMDetect", ObjectAttributes->ObjectName); 184 | } 185 | } 186 | } 187 | } 188 | } 189 | NTSTATUS retVal = OldNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); 190 | 191 | 192 | return retVal; 193 | } 194 | catch (...) 195 | { 196 | LOG("s", "Exception", "NtCreateFile"); 197 | return -1; 198 | } 199 | } 200 | 201 | NTSTATUS(WINAPI * OldNtQueryAttributesFile) 202 | ( 203 | POBJECT_ATTRIBUTES ObjectAttributes, 204 | PFILE_BASIC_INFORMATION FileInformation 205 | ); 206 | 207 | NTSTATUS WINAPI MyNtQueryAttributesFile 208 | ( 209 | POBJECT_ATTRIBUTES ObjectAttributes, 210 | PFILE_BASIC_INFORMATION FileInformation 211 | ) 212 | { 213 | 214 | try 215 | { 216 | if (ObjectAttributes) 217 | { 218 | if (ObjectAttributes->ObjectName) 219 | { 220 | if (ObjectAttributes->ObjectName->Buffer) 221 | { 222 | for (int i = 0; i < ARRAYSIZE(VMDetect); i++) 223 | { 224 | if (MemSearch((void*)VMDetect[i], wcslen(VMDetect[i]) * 2, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length)) 225 | { 226 | LOG("o", "VMDetect", ObjectAttributes->ObjectName); 227 | } 228 | } 229 | } 230 | } 231 | } 232 | NTSTATUS retVal = OldNtQueryAttributesFile(ObjectAttributes, FileInformation); 233 | 234 | return retVal; 235 | } 236 | catch (...) 237 | { 238 | LOG("s", "Exception", "NtQueryAttributesFile"); 239 | return -1; 240 | } 241 | 242 | } -------------------------------------------------------------------------------- /flux/hook_inject.cpp: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | #include "ntapi.h" 3 | #include "log.h" 4 | #include 5 | #include 6 | #include "DbgHelp.h" 7 | #include 8 | #include "whitelist.h" 9 | 10 | #pragma comment(lib, "DbgHelp.lib") 11 | 12 | // Good Reading - http://www.fuzzysecurity.com/tutorials/expDev/7.html 13 | 14 | // Exception handler wrappers around these functions for https://blogs.mcafee.com/mcafee-labs/recent-ie-0-day-unusual-case-study 15 | 16 | // ROP Detection and Bypass http://vulnfactory.org/blog/2011/09/21/defeating-windows-8-rop-mitigation/ . Alternate method, memcpy rop chain to stack 17 | 18 | // Good overview of Heap Exploits - https://www.corelan.be/index.php/2011/12/31/exploit-writing-tutorial-part-11-heap-spraying-demystified/#Heap_Spraying_on_IE10_8211_Windows_8 19 | 20 | // Good paper about EMET - https://bromiumlabs.files.wordpress.com/2014/02/bypassing-emet-4-1.pdf 21 | 22 | // KBouncer - http://www.cs.columbia.edu/~vpappas/papers/kbouncer.pdf 23 | 24 | /* 25 | EMET Critical functions - http://0xdabbad00.com/wp-content/uploads/2013/11/emet_4_1_uncovered.pdf 26 | kernel32.MapViewOfFileFromApp 27 | ntdll.NtMapViewOfSection 28 | kernelbase.MapViewOfFileEx 29 | kernelbase.MapViewOfFile 30 | kernel32.MapViewOfFileEx 31 | kernel32.MapViewOfFile 32 | ntdll.NtCreateSection 33 | kernelbase.CreateFileMappingW 34 | kernelbase.CreateFileMappingNumaW 35 | kernel32.CreateFileMappingW 36 | kernel32.CreateFileMappingA 37 | ntdll.NtCreateFile 38 | kernelbase.CreateFileW 39 | kernel32.CreateFileW 40 | kernel32.CreateFileA 41 | kernel32.WinExec 42 | ntdll.NtWriteVirtualMemory 43 | kernelbase.WriteProcessMemory 44 | kernel32.WriteProcessMemory 45 | ntdll.NtCreateThreadEx 46 | kernelbase.CreateRemoteThreadEx 47 | kernel32.CreateRemoteThreadEx 48 | kernel32.CreateRemoteThread 49 | ntdll.NtCreateProcess 50 | ntdll.NtCreateUserProcess 51 | kernel32.CreateProcessInternalW 52 | kernel32.CreateProcessInternalA 53 | kernel32.CreateProcessW 54 | kernel32.CreateProcessA 55 | ntdll.RtlCreateHeap 56 | kernelbase.HeapCreate 57 | kernel32.HeapCreate 58 | ntdll.NtAllocateVirtualMemory 59 | kernelbase.VirtualAllocEx 60 | kernelbase.VirtualAlloc 61 | kernel32.VirtualAllocEx 62 | kernel32.VirtualAlloc 63 | ntdll.LdrLoadDll 64 | kernelbase.LoadLibraryExW 65 | kernelbase.LoadLibraryExA 66 | kernel32.LoadPackagedLibrary 67 | kernel32.LoadLibraryExW 68 | kernel32.LoadLibraryExA 69 | kernel32.LoadLibraryW 70 | kernel32.LoadLibraryA 71 | ntdll.NtProtectVirtualMemory 72 | kernelbase.VirtualProtectEx 73 | kernelbase.VirtualProtect 74 | kernel32.VirtualProtectEx 75 | kernel32.VirtualProtect 76 | ntdll.LdrHotPatchRoutine 77 | 78 | */ 79 | extern CRITICAL_SECTION cs; 80 | 81 | // Detects stack pivot type behavior 82 | bool DirtyEsp() 83 | { 84 | TEB * teb = NtCurrentTeb(); 85 | 86 | CONTEXT lpContext; 87 | lpContext.ContextFlags = CONTEXT_CONTROL; 88 | if (GetThreadContext(GetCurrentThread(), &lpContext)) 89 | { 90 | if (lpContext.Esp < (DWORD_PTR)teb->NtTib.StackLimit || lpContext.Esp >= (DWORD_PTR)teb->NtTib.StackBase) 91 | { 92 | return true; 93 | } 94 | } 95 | return false; 96 | 97 | } 98 | 99 | // Function calls should come from actual call instructions 100 | // disasm call trace 101 | // http://stackoverflow.com/questions/15650528/does-stackwalk64-work-on-64-bit-windows 102 | // http://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/ 103 | 104 | bool ROPTrace() 105 | { 106 | EnterCriticalSection(&cs); 107 | CONTEXT lpContext; 108 | STACKFRAME64 stack_frame; 109 | memset(&stack_frame, 0, sizeof(stack_frame)); 110 | memset(&lpContext, 0, sizeof(lpContext)); 111 | 112 | lpContext.ContextFlags = CONTEXT_CONTROL; 113 | #if defined(_WIN64) 114 | RtlCaptureContext(&lpContext); 115 | 116 | #else 117 | __asm 118 | { 119 | Label: 120 | mov[lpContext.Ebp], ebp; 121 | mov[lpContext.Esp], esp; 122 | mov eax, [Label]; 123 | mov[lpContext.Eip], eax; 124 | } 125 | #endif 126 | 127 | stack_frame.AddrPC.Mode = AddrModeFlat; 128 | stack_frame.AddrStack.Mode = AddrModeFlat; 129 | stack_frame.AddrFrame.Mode = AddrModeFlat; 130 | #if defined(_WIN64) 131 | int machine_type = IMAGE_FILE_MACHINE_AMD64; 132 | stack_frame.AddrPC.Offset = lpContext.Rip; 133 | stack_frame.AddrFrame.Offset = lpContext.Rbp; 134 | stack_frame.AddrStack.Offset = lpContext.Rsp; 135 | stack_frame.AddrPC.Offset = 0; 136 | stack_frame.AddrStack.Offset = 0; 137 | stack_frame.AddrFrame.Offset = 0; 138 | #else 139 | int machine_type = IMAGE_FILE_MACHINE_I386; 140 | stack_frame.AddrPC.Offset = lpContext.Eip; 141 | stack_frame.AddrFrame.Offset = lpContext.Ebp; 142 | stack_frame.AddrStack.Offset = lpContext.Esp; 143 | #endif 144 | 145 | int depth = 5; 146 | while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &lpContext, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) 147 | { 148 | if (depth <= 0) 149 | break; 150 | 151 | //DWORD64 fp = stack_frame.AddrFrame.Offset; 152 | //fp += 4; 153 | 154 | if (stack_frame.AddrReturn.Offset == 0) 155 | { 156 | // this is expected for the last entry 157 | break; 158 | } 159 | if (stack_frame.AddrFrame.Offset == 0 || stack_frame.AddrPC.Offset == 0) 160 | { 161 | // sanity check 162 | break; 163 | } 164 | 165 | DWORD_PTR ra = stack_frame.AddrReturn.Offset; 166 | 167 | ra -= 6; 168 | if (!IsBadReadPtr((void*)ra, 6)) 169 | { 170 | char * ptr = (char*)ra; 171 | 172 | /*AddrPC is the address of the call instruction, AddrReturn is the return address, the address of the previous call instruction (+5). Not sure what "stack 0" might mean. */ 173 | 174 | // Standard call - 0xE8 (minus 5 bytes from returnAddr) 175 | // DWORD PTR call - 0xFF 0x15 (minus 6 bytes from returnAddr) .... example FF 15 <04 16 59 77> call dword ptr ds:[77591604h] 176 | // Call esi FF D6 177 | // call ebx FF d3 178 | if (!((ptr[0] == '\xFF' && ptr[1] == '\x15') || (ptr[1] == '\xe8') || (ptr[4] == '\xff' && ptr[5] == '\xd6') || (ptr[4] == '\xff' && ptr[5] == '\xd3') )) 179 | { 180 | /*__asm 181 | { 182 | __emit 0xCC 183 | }*/ 184 | LOG("S", "asm", ptr, 10); 185 | LeaveCriticalSection(&cs); 186 | 187 | return true; 188 | } 189 | 190 | //Todo: 64bit 191 | 192 | } 193 | 194 | 195 | depth--; 196 | 197 | } 198 | 199 | LeaveCriticalSection(&cs); 200 | 201 | return false; 202 | } 203 | bool HandleToProcessPath(HANDLE hProc, char * exePath) 204 | { 205 | DWORD len = MAX_PATH; 206 | 207 | if (GetModuleFileNameExA(hProc, 0, exePath, MAX_PATH)) 208 | { 209 | return true; 210 | } 211 | else 212 | { 213 | strcpy_s(exePath, MAX_PATH, ""); 214 | return false; 215 | } 216 | 217 | } 218 | 219 | #ifdef _WIN64 220 | typedef struct _LDR_DATA_TABLE_ENTRY { 221 | LIST_ENTRY InMemoryOrderLinks; 222 | PVOID Unk1; 223 | PVOID Unk2; 224 | PVOID DllBase; 225 | PVOID EntryPoint; 226 | ULONG SizeOfImage; 227 | UNICODE_STRING FullDllName; 228 | UNICODE_STRING BaseDllName; 229 | ULONG Flags; 230 | SHORT LoadCount; 231 | SHORT TlsIndex; 232 | LIST_ENTRY HashTableEntry; 233 | ULONG TimeDateStamp; 234 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 235 | #else 236 | typedef struct _LDR_DATA_TABLE_ENTRY { 237 | LIST_ENTRY InMemoryOrderLinks; 238 | PVOID DllBase; 239 | PVOID EntryPoint; 240 | ULONG SizeOfImage; 241 | UNICODE_STRING FullDllName; 242 | UNICODE_STRING BaseDllName; 243 | ULONG Flags; 244 | SHORT LoadCount; 245 | SHORT TlsIndex; 246 | LIST_ENTRY HashTableEntry; 247 | ULONG TimeDateStamp; 248 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 249 | #endif 250 | 251 | /*__declspec(naked) 252 | void* firstLdrDataEntry() { 253 | __asm { 254 | mov eax, fs:[0x30] // PEB 255 | mov eax, [eax + 0x0C] // PEB_LDR_DATA 256 | mov eax, [eax + 0x1C] // InInitializationOrderModuleList 257 | retn 258 | } 259 | } 260 | */ 261 | //extern "C" PVOID firstLdrDataEntry(); 262 | 263 | // aleternate technique - https://www.honeynet.org/node/571 264 | 265 | bool AddrOutsideModule2(DWORD_PTR Addr) 266 | { 267 | 268 | MEMORY_BASIC_INFORMATION lpBuffer; 269 | memset(&lpBuffer, '\0', sizeof(MEMORY_BASIC_INFORMATION)); 270 | VirtualQuery((void*)Addr, &lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)); 271 | if (lpBuffer.Type == MEM_IMAGE) 272 | return false; 273 | else 274 | return true; 275 | 276 | } 277 | 278 | // This causes some recursion issues to NtAllocateVirtualMemory/NtCreateSection 279 | bool AddressOutsideModule(DWORD_PTR Addr) 280 | { 281 | HANDLE hModuleSnap = INVALID_HANDLE_VALUE; 282 | MODULEENTRY32 me32; 283 | 284 | // Take a snapshot of all modules in the specified process. 285 | hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); 286 | if (hModuleSnap == INVALID_HANDLE_VALUE) 287 | { 288 | return false; 289 | } 290 | 291 | // Set the size of the structure before using it. 292 | me32.dwSize = sizeof(MODULEENTRY32); 293 | 294 | // Retrieve information about the first module, 295 | // and exit if unsuccessful 296 | if (!Module32First(hModuleSnap, &me32)) 297 | { 298 | CloseHandle(hModuleSnap); // clean the snapshot object 299 | return false; 300 | } 301 | 302 | // Now walk the module list of the process, 303 | do 304 | { 305 | if (((DWORD_PTR)me32.modBaseAddr <= Addr) && ((DWORD_PTR)me32.modBaseAddr + me32.modBaseSize >= Addr)) 306 | { 307 | CloseHandle(hModuleSnap); 308 | return false; 309 | } 310 | 311 | } while (Module32Next(hModuleSnap, &me32)); 312 | 313 | CloseHandle(hModuleSnap); 314 | 315 | 316 | return true; 317 | 318 | 319 | } 320 | 321 | NTSTATUS(WINAPI * OldNtProtectVirtualMemory) 322 | ( 323 | HANDLE ProcessHandle, 324 | PVOID *BaseAddress, 325 | PULONG NumberOfBytesToProtect, 326 | ULONG NewAccessProtection, 327 | PULONG OldAccessProtection 328 | ); 329 | 330 | NTSTATUS WINAPI MyNtProtectVirtualMemory 331 | ( 332 | HANDLE ProcessHandle, 333 | PVOID *BaseAddress, 334 | PULONG NumberOfBytesToProtect, 335 | ULONG NewAccessProtection, 336 | PULONG OldAccessProtection 337 | ) 338 | { 339 | try 340 | { 341 | 342 | PVOID Base = *BaseAddress; 343 | ULONG Size = *NumberOfBytesToProtect; 344 | 345 | bool drop = true; 346 | 347 | NTSTATUS retVal = OldNtProtectVirtualMemory(ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection); 348 | if (retVal == STATUS_SUCCESS) 349 | { 350 | if ((NewAccessProtection & PAGE_EXECUTE_READ || NewAccessProtection & PAGE_EXECUTE_READWRITE) && Size != 37) 351 | { 352 | /* Possibly do these checks before the Old_ call to detect the method mcafee blogged about */ 353 | if (DirtyEsp()) 354 | LOG("s", "ROP_Detection", "STATUS_STACK_BUFFER_OVERRUN"); 355 | 356 | if (NewAccessProtection & PAGE_EXECUTE_READWRITE) 357 | { 358 | if (ROPTrace()) 359 | LOG("sp", "ROP_Detection", "CALLER_CHECK", "Base", Base); 360 | } 361 | 362 | if (AddrOutsideModule2((DWORD_PTR)Base)) 363 | { 364 | // determine what memory this will actually modify 365 | MEMORY_BASIC_INFORMATION lpBuffer; 366 | memset(&lpBuffer, '\0', sizeof(MEMORY_BASIC_INFORMATION)); 367 | VirtualQuery(Base, &lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)); 368 | LOG("llllllS", "AccessProtection", NewAccessProtection, "Size", Size, "BaseAddress", Base, "QueryBase", lpBuffer.BaseAddress, "RegionSize", lpBuffer.RegionSize, "AllocationBase", lpBuffer.AllocationBase, "Buffer", Base, 10); 369 | 370 | if (drop) 371 | { 372 | char outFile[MAX_PATH]; 373 | sprintf_s(outFile, MAX_PATH, "C:\\drop\\%s_PVM_%x", "g_baseExe", lpBuffer.BaseAddress); 374 | HANDLE hFile = CreateFileA(outFile, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); 375 | if (hFile != INVALID_HANDLE_VALUE) 376 | { 377 | DWORD dWritten = 0; 378 | WriteFile(hFile, lpBuffer.BaseAddress, lpBuffer.RegionSize, &dWritten, 0); 379 | CloseHandle(hFile); 380 | drop = false; 381 | } 382 | } 383 | 384 | } 385 | } 386 | } 387 | return retVal; 388 | } 389 | catch (...) 390 | { 391 | LOG("s", "Exception", "NtProtectVirtualMemory"); 392 | return -1; 393 | } 394 | } 395 | 396 | 397 | 398 | NTSTATUS(WINAPI * OldNtFreeVirtualMemory) 399 | ( 400 | HANDLE ProcessHandle, 401 | PVOID *BaseAddress, 402 | PSIZE_T RegionSize, 403 | ULONG FreeType 404 | ); 405 | 406 | NTSTATUS WINAPI MyNtFreeVirtualMemory 407 | ( 408 | HANDLE ProcessHandle, 409 | PVOID *BaseAddress, 410 | PSIZE_T RegionSize, 411 | ULONG FreeType 412 | ) 413 | { 414 | NTSTATUS retVal; 415 | 416 | try 417 | { 418 | // Useful to dump certain packed executables 419 | 420 | MEMORY_BASIC_INFORMATION lpBuffer; 421 | PVOID Base = *BaseAddress; 422 | char * charPtr = (char*)Base; 423 | SIZE_T Size = *RegionSize; 424 | VirtualQuery(Base, &lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)); 425 | if (lpBuffer.State & MEM_COMMIT && !(lpBuffer.Type & MEM_IMAGE) && charPtr[0] == 'M' && charPtr[1] == 'Z' && charPtr[3] == '\0') 426 | { 427 | LOG("plplpS", "ProcessHandle", ProcessHandle, "Size", Size, "BaseAddress", Base, "RegionSize", lpBuffer.RegionSize, "RegionBase", lpBuffer.BaseAddress, "Buffer", Base, 10); 428 | 429 | char outFile[MAX_PATH]; 430 | sprintf_s(outFile, MAX_PATH, "C:\\drop\\%s_FVM_MZ_%x", L"base", lpBuffer.BaseAddress); 431 | HANDLE hFile = CreateFileA(outFile, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); 432 | if (hFile != INVALID_HANDLE_VALUE) 433 | { 434 | DWORD dWritten = 0; 435 | WriteFile(hFile, lpBuffer.BaseAddress, lpBuffer.RegionSize, &dWritten, 0); 436 | CloseHandle(hFile); 437 | } 438 | } 439 | } 440 | catch (...) 441 | { 442 | 443 | } 444 | 445 | retVal = OldNtFreeVirtualMemory(ProcessHandle, BaseAddress, RegionSize, FreeType); 446 | 447 | return retVal; 448 | 449 | } 450 | -------------------------------------------------------------------------------- /flux/hook_misc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hook.h" 3 | #include "ntapi.h" 4 | #include "log.h" 5 | #include "whitelist.h" 6 | 7 | 8 | NTSTATUS(WINAPI * OldNtDelayExecution) 9 | ( 10 | BOOLEAN Alertable, 11 | PLARGE_INTEGER DelayInterval 12 | ); 13 | NTSTATUS WINAPI MyNtDelayExecution 14 | ( 15 | BOOLEAN Alertable, 16 | PLARGE_INTEGER DelayInterval 17 | ) 18 | { 19 | unsigned long long milli = -DelayInterval->QuadPart / 10000; 20 | if (milli >= 1000) 21 | { 22 | LARGE_INTEGER newDelay; 23 | newDelay.QuadPart = -10000000; 24 | return OldNtDelayExecution(Alertable, &newDelay); 25 | } 26 | 27 | return OldNtDelayExecution(Alertable, DelayInterval); 28 | 29 | 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /flux/hook_process.cpp: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | #include "log.h" 3 | 4 | NTSTATUS(NTAPI * OldNtCreateUserProcess) 5 | ( 6 | PHANDLE ProcessHandle, 7 | PHANDLE ThreadHandle, 8 | ACCESS_MASK ProcessDesiredAccess, 9 | ACCESS_MASK ThreadDesiredAccess, 10 | POBJECT_ATTRIBUTES ProcessObjectAttributes, 11 | POBJECT_ATTRIBUTES ThreadObjectAttributes, 12 | ULONG ProcessFlags, 13 | ULONG ThreadFlags, 14 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters, 15 | void * CreateInfo, 16 | void * AttributeList 17 | ); 18 | 19 | NTSTATUS NTAPI MyNtCreateUserProcess 20 | ( 21 | PHANDLE ProcessHandle, 22 | PHANDLE ThreadHandle, 23 | ACCESS_MASK ProcessDesiredAccess, 24 | ACCESS_MASK ThreadDesiredAccess, 25 | POBJECT_ATTRIBUTES ProcessObjectAttributes, 26 | POBJECT_ATTRIBUTES ThreadObjectAttributes, 27 | ULONG ProcessFlags, 28 | ULONG ThreadFlags, 29 | PRTL_USER_PROCESS_PARAMETERS ProcessParameters, 30 | void * CreateInfo, 31 | void * AttributeList 32 | ) 33 | { 34 | NTSTATUS retVal; 35 | 36 | retVal = OldNtCreateUserProcess(ProcessHandle, ThreadHandle, ProcessDesiredAccess, ThreadDesiredAccess, ProcessObjectAttributes, ThreadObjectAttributes, ProcessFlags, ThreadFlags, ProcessParameters, CreateInfo, AttributeList); 37 | 38 | LOG("uu", "ImagePathName", ProcessParameters->ImagePathName.Buffer, "CommandLine", ProcessParameters->CommandLine.Buffer); 39 | 40 | return retVal; 41 | } 42 | 43 | 44 | NTSTATUS(WINAPI * OldNtCreateProcess) 45 | ( 46 | PHANDLE ProcessHandle, 47 | ACCESS_MASK DesiredAccess, 48 | POBJECT_ATTRIBUTES ObjectAttributes, 49 | HANDLE ParentProcess, 50 | BOOLEAN InheritObjectTable, 51 | HANDLE SectionHandle, 52 | HANDLE DebugPort, 53 | HANDLE ExceptionPort 54 | ); 55 | 56 | NTSTATUS WINAPI MyNtCreateProcess 57 | ( 58 | PHANDLE ProcessHandle, 59 | ACCESS_MASK DesiredAccess, 60 | POBJECT_ATTRIBUTES ObjectAttributes, 61 | HANDLE ParentProcess, 62 | BOOLEAN InheritObjectTable, 63 | HANDLE SectionHandle, 64 | HANDLE DebugPort, 65 | HANDLE ExceptionPort 66 | ) 67 | { 68 | NTSTATUS retVal; 69 | 70 | retVal = OldNtCreateProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, InheritObjectTable, SectionHandle, DebugPort, ExceptionPort); 71 | 72 | LOG("o", "FileName", ObjectAttributes->ObjectName->Buffer); 73 | 74 | return retVal; 75 | } 76 | 77 | NTSTATUS(WINAPI * OldNtCreateProcessEx) 78 | ( 79 | PHANDLE ProcessHandle, 80 | ACCESS_MASK DesiredAccess, 81 | POBJECT_ATTRIBUTES ObjectAttributes, 82 | HANDLE ParentProcess, 83 | ULONG Flags, 84 | HANDLE SectionHandle, 85 | HANDLE DebugPort, 86 | HANDLE ExceptionPort, 87 | BOOLEAN InJob 88 | ); 89 | 90 | NTSTATUS WINAPI MyNtCreateProcessEx 91 | ( 92 | PHANDLE ProcessHandle, 93 | ACCESS_MASK DesiredAccess, 94 | POBJECT_ATTRIBUTES ObjectAttributes, 95 | HANDLE ParentProcess, 96 | ULONG Flags, 97 | HANDLE SectionHandle, 98 | HANDLE DebugPort, 99 | HANDLE ExceptionPort, 100 | BOOLEAN InJob 101 | ) 102 | { 103 | NTSTATUS retVal; 104 | 105 | retVal = OldNtCreateProcessEx(ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, Flags, SectionHandle, DebugPort, ExceptionPort, InJob); 106 | 107 | LOG("o", "FileName", ObjectAttributes->ObjectName->Buffer); 108 | 109 | return retVal; 110 | } 111 | 112 | 113 | -------------------------------------------------------------------------------- /flux/hook_registry.cpp: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | #include "ntapi.h" 3 | #include "log.h" 4 | 5 | #ifndef STATUS_BUFFER_TOO_SMALL 6 | #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) 7 | #endif 8 | 9 | typedef struct _KEY_NAME_INFORMATION { 10 | ULONG NameLength; 11 | WCHAR Name[1]; 12 | } KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION; 13 | 14 | 15 | bool HandleToKey(HANDLE hKey, char * keyPath) 16 | { 17 | typedef DWORD(__stdcall *pNtQueryKey)( 18 | HANDLE KeyHandle, 19 | int KeyInformationClass, 20 | PVOID KeyInformation, 21 | ULONG Length, 22 | PULONG ResultLength); 23 | 24 | pNtQueryKey NtQueryKey = (pNtQueryKey)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQueryKey"); 25 | 26 | if (NtQueryKey) 27 | { 28 | DWORD size = 0; 29 | DWORD result = 0; 30 | result = NtQueryKey(hKey, 3, 0, 0, &size); 31 | if (result == STATUS_BUFFER_TOO_SMALL) 32 | { 33 | size = size + 2; 34 | KEY_NAME_INFORMATION * buffer = (KEY_NAME_INFORMATION*)malloc(size); // size is in bytes 35 | ZeroMemory(buffer, size); 36 | if (buffer != NULL) 37 | { 38 | result = NtQueryKey(hKey, 3, buffer, size, &size); 39 | if (result == STATUS_SUCCESS) 40 | { 41 | size_t ReturnValue; 42 | wcstombs_s(&ReturnValue, keyPath, 500, buffer->Name, 500); 43 | } 44 | } 45 | if (buffer) 46 | free(buffer); 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | 53 | NTSTATUS(WINAPI * OldNtSetValueKey) 54 | ( 55 | HANDLE KeyHandle, 56 | PUNICODE_STRING ValueName, 57 | ULONG TitleIndex, 58 | ULONG Type, 59 | PVOID Data, 60 | ULONG DataSize 61 | ); 62 | 63 | NTSTATUS WINAPI MyNtSetValueKey 64 | ( 65 | HANDLE KeyHandle, 66 | PUNICODE_STRING ValueName, 67 | ULONG TitleIndex, 68 | ULONG Type, 69 | PVOID Data, 70 | ULONG DataSize 71 | ) 72 | { 73 | try 74 | { 75 | NTSTATUS retVal = OldNtSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize); 76 | if (retVal == STATUS_SUCCESS && Type == REG_SZ) 77 | { 78 | char keyPath[512]; 79 | HandleToKey(KeyHandle, keyPath); 80 | LOG("sU", "KeyPath", keyPath, "KeyValue", Data, DataSize); 81 | } 82 | return retVal; 83 | } 84 | catch (...) 85 | { 86 | LOG("s", "Exception", "NtSetValueKey"); 87 | return -1; 88 | } 89 | } 90 | 91 | extern bool MemSearch(void * needle, SIZE_T needleSize, void * haystack, SIZE_T haystackSize); 92 | 93 | NTSTATUS (WINAPI * OldNtOpenKeyEx) 94 | ( 95 | PHANDLE KeyHandle, 96 | ACCESS_MASK DesiredAccess, 97 | POBJECT_ATTRIBUTES ObjectAttributes, 98 | ULONG OpenOptions 99 | ); 100 | 101 | NTSTATUS WINAPI MyNtOpenKeyEx 102 | ( 103 | PHANDLE KeyHandle, 104 | ACCESS_MASK DesiredAccess, 105 | POBJECT_ATTRIBUTES ObjectAttributes, 106 | ULONG OpenOptions 107 | ) 108 | { 109 | 110 | try 111 | { 112 | if (ObjectAttributes) 113 | { 114 | if (ObjectAttributes->ObjectName) 115 | { 116 | if (ObjectAttributes->ObjectName->Buffer) 117 | { 118 | if (MemSearch(L"Kaspersky", sizeof(L"Kaspersky") - 2, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length)) 119 | { 120 | LOG("o", "VMDetect", ObjectAttributes->ObjectName); 121 | } 122 | if (MemSearch(L"IeVirtualKeyboardPlugin", sizeof(L"IeVirtualKeyboardPlugin") - 2, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length)) 123 | { 124 | LOG("o", "VMDetect", ObjectAttributes->ObjectName); 125 | } 126 | 127 | } 128 | } 129 | } 130 | NTSTATUS retVal = OldNtOpenKeyEx(KeyHandle, DesiredAccess, ObjectAttributes, OpenOptions); 131 | 132 | return retVal; 133 | } 134 | catch (...) 135 | { 136 | LOG("s", "Exception", "NtOpenKey"); 137 | return -1; 138 | } 139 | 140 | } 141 | 142 | 143 | -------------------------------------------------------------------------------- /flux/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | namespace MaxLog 4 | { 5 | static CRITICAL_SECTION g_cs; 6 | static DWORD g_pid, g_ppid; 7 | static char g_modBuf[MAX_PATH]; 8 | static HANDLE g_hPipe; 9 | const char *g_baseExe; 10 | _NtQueryVirtualMemory g_NtQueryVirtualMemory = 0; 11 | _NtQueryInformationThread g_NtQueryInformationThread = 0; 12 | 13 | DWORD InitLog() 14 | { 15 | DWORD retVal = 0; 16 | DWORD pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT; 17 | g_hPipe = INVALID_HANDLE_VALUE; 18 | 19 | InitializeCriticalSection(&g_cs); 20 | GetModuleFileNameA(NULL, g_modBuf, ARRAYSIZE(g_modBuf)); 21 | 22 | g_NtQueryVirtualMemory = (_NtQueryVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryVirtualMemory"); 23 | g_NtQueryInformationThread = (_NtQueryInformationThread)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQueryInformationThread"); 24 | 25 | // extract only the filename of the process, not the entire path 26 | for (const char *p = g_baseExe = g_modBuf; *p != 0; p++) 27 | { 28 | if (*p == '\\' || *p == '/') 29 | { 30 | g_baseExe = p + 1; 31 | } 32 | } 33 | 34 | g_pid = GetCurrentProcessId(); 35 | 36 | g_hPipe = CreateFile(MAXWELL_PIPE_NAME, GENERIC_WRITE, FILE_SHARE_WRITE, 37 | 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0); 38 | 39 | if (INVALID_HANDLE_VALUE == g_hPipe) 40 | { 41 | return GetLastError(); 42 | } 43 | 44 | if (!SetNamedPipeHandleState(g_hPipe, &pipeMode, 0, 0)) 45 | { 46 | return GetLastError(); 47 | } 48 | 49 | return 0; 50 | 51 | } 52 | 53 | char * USC2toUTF8(LPCWSTR pUSC2, int nUSC2) 54 | { 55 | // Get the size for our buffer 56 | int outLen = WideCharToMultiByte(CP_UTF8, 0, pUSC2, nUSC2, 0, 0, 0, 0); 57 | 58 | // Extra byte to ensure this will be null terminated 59 | char * pUTF8 = (char*)malloc(outLen + 1); 60 | pUTF8[outLen] = 0; 61 | WideCharToMultiByte(CP_UTF8, 0, pUSC2, nUSC2, (LPSTR)pUTF8, outLen, 0, 0); 62 | 63 | return pUTF8; 64 | } 65 | 66 | wchar_t * UTF8toUSC2(char * pUTF8, int nUTF8) 67 | { 68 | // Get the size for our buffer 69 | int outLen = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pUTF8, nUTF8, 0, 0); 70 | 71 | // Extra byte to ensure this will be null terminated 72 | wchar_t * pUSC2 = (wchar_t*)malloc(outLen + 1); 73 | pUSC2[outLen] = L'\0'; 74 | MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pUTF8, nUTF8, pUSC2, outLen); 75 | 76 | return pUSC2; 77 | 78 | } 79 | 80 | void pack_str(msgpack_packer * pk, const char * sVal) 81 | { 82 | msgpack_pack_str(pk, strlen(sVal)); 83 | msgpack_pack_str_body(pk, sVal, strlen(sVal)); 84 | } 85 | 86 | void pack_str_n(msgpack_packer * pk, const char * sVal, size_t len) 87 | { 88 | msgpack_pack_str(pk, len); 89 | msgpack_pack_str_body(pk, sVal, len); 90 | } 91 | 92 | void pack_wstr(msgpack_packer * pk, const wchar_t * wVal) 93 | { 94 | char * sVal = USC2toUTF8(wVal, wcslen(wVal)); 95 | msgpack_pack_str(pk, strlen(sVal)); 96 | msgpack_pack_str_body(pk, sVal, strlen(sVal)); 97 | free(sVal); 98 | } 99 | 100 | void pack_wstr_n(msgpack_packer * pk, const wchar_t * wVal, size_t len) 101 | { 102 | char * sVal = USC2toUTF8(wVal, len); 103 | msgpack_pack_str(pk, strlen(sVal)); 104 | msgpack_pack_str_body(pk, sVal, strlen(sVal)); 105 | 106 | free(sVal); 107 | } 108 | 109 | void pack_bin_n(msgpack_packer * pk, void * vVal, size_t len) 110 | { 111 | msgpack_pack_bin(pk, len); 112 | msgpack_pack_bin_body(pk, vVal, len); 113 | } 114 | 115 | void pack_ptr(msgpack_packer * pk, DWORD_PTR val) 116 | { 117 | char buf[20]; 118 | #ifdef _AMD64_ 119 | sprintf_s(buf,20, "0x%016llX", val); 120 | #else 121 | sprintf_s(buf,20, "0x%08lX", val); 122 | #endif 123 | msgpack_pack_str(pk, strlen(buf)); 124 | msgpack_pack_str_body(pk, buf, strlen(buf)); 125 | 126 | } 127 | 128 | void pack_dword_x(msgpack_packer * pk, DWORD val) 129 | { 130 | char buf[20]; 131 | sprintf_s(buf, 20, "0x%08lX", val); 132 | msgpack_pack_str(pk, strlen(buf)); 133 | msgpack_pack_str_body(pk, buf, strlen(buf)); 134 | 135 | } 136 | 137 | void parseArgs(va_list args, const char * fmt, msgpack_packer *pk, ...) 138 | { 139 | DWORD count = strlen(fmt); 140 | for (DWORD i = 0; i < count; i++) 141 | { 142 | char type = fmt[i]; 143 | const char * key = va_arg(args, const char *); 144 | pack_str(pk, key); 145 | 146 | if (type == 's') 147 | { 148 | const char * val = va_arg(args, const char *); 149 | pack_str(pk, val); 150 | } 151 | else if (type == 'S') 152 | { 153 | const char * val = va_arg(args, const char *); 154 | size_t len = va_arg(args, size_t); 155 | pack_str_n(pk, val, len); 156 | } 157 | else if (type == 'u') 158 | { 159 | const wchar_t * val = va_arg(args, const wchar_t *); 160 | pack_wstr(pk, val); 161 | } 162 | else if (type == 'U') 163 | { 164 | const wchar_t * val = va_arg(args, const wchar_t *); 165 | size_t len = va_arg(args, size_t); 166 | pack_wstr_n(pk, val, len); 167 | } 168 | else if (type == 'i') 169 | { 170 | unsigned long val = va_arg(args, int); 171 | msgpack_pack_int32(pk, val); 172 | } 173 | else if (type == 'l') 174 | { 175 | unsigned long val = va_arg(args, unsigned long); 176 | msgpack_pack_unsigned_long(pk, val); 177 | } 178 | else if (type == 'q') 179 | { 180 | unsigned long long val = va_arg(args, unsigned long long); 181 | msgpack_pack_uint64(pk, val); 182 | } 183 | else if (type == 'o') 184 | { 185 | UNICODE_STRING* val = va_arg(args, UNICODE_STRING*); 186 | pack_wstr_n(pk, val->Buffer, val->Length / 2); 187 | } 188 | else if (type == 'x') 189 | { 190 | DWORD_PTR val = va_arg(args, DWORD); 191 | pack_dword_x(pk, val); 192 | } 193 | else if (type == 'p') 194 | { 195 | DWORD_PTR val = va_arg(args, DWORD_PTR); 196 | pack_ptr(pk, val); 197 | } 198 | else if (type == 'b') 199 | { 200 | void * val = va_arg(args, void*); 201 | size_t len = va_arg(args, size_t); 202 | pack_bin_n(pk, val, len); 203 | } 204 | else 205 | { 206 | printf("Invalid format specifier\n"); 207 | msgpack_pack_str(pk, 3); 208 | msgpack_pack_str_body(pk, "err", 3); 209 | } 210 | 211 | } 212 | 213 | 214 | } 215 | 216 | void Log(const char * fmt, ...) 217 | { 218 | va_list args; 219 | va_start(args, fmt); 220 | msgpack_sbuffer sbuf; /* buffer */ 221 | msgpack_packer pk; /* packer */ 222 | DWORD count = strlen(fmt); 223 | DWORD bytesWritten = 0; 224 | BOOL retVal; 225 | 226 | msgpack_sbuffer_init(&sbuf); /* initialize buffer */ 227 | msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); /* initialize packer */ 228 | 229 | msgpack_pack_map(&pk, count + 1); 230 | 231 | pack_str(&pk, "plugin"); 232 | pack_str(&pk, MAXWELL_PLUGIN_NAME); 233 | 234 | 235 | 236 | parseArgs(args, fmt, &pk); 237 | 238 | EnterCriticalSection(&g_cs); 239 | 240 | retVal = WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 241 | if (!retVal) 242 | { 243 | int lasterr = GetLastError(); 244 | if (ERROR_NO_DATA == lasterr) 245 | { 246 | // Pipe was closed on the other end, reconnect 247 | CloseHandle(g_hPipe); 248 | g_hPipe = INVALID_HANDLE_VALUE; 249 | if (0 == InitLog()) 250 | { 251 | // Connected succesfully, try to resend 252 | (void)WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 253 | } 254 | } 255 | else if (ERROR_INVALID_HANDLE == lasterr) 256 | { 257 | // We are not currently connected, attempt to connect 258 | if (0 == InitLog()) 259 | { 260 | // Connected succesfully, try to resend 261 | (void)WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 262 | } 263 | } 264 | else 265 | { 266 | printf("lasterr: %d\n", lasterr); 267 | } 268 | } 269 | 270 | LeaveCriticalSection(&g_cs); 271 | 272 | msgpack_sbuffer_destroy(&sbuf); 273 | 274 | return; 275 | 276 | } 277 | 278 | void LogWithMeta(const char * fmt, ...) 279 | { 280 | DWORD bytesWritten = 0; 281 | va_list args; 282 | va_start(args, fmt); 283 | msgpack_sbuffer sbuf; /* buffer */ 284 | msgpack_packer pk; /* packer */ 285 | DWORD count = 0; 286 | BOOL retVal; 287 | wchar_t threatStartModule[MAX_PATH]; 288 | ModuleThread(threatStartModule); 289 | 290 | count = strlen(fmt); 291 | 292 | const char *functionName = va_arg(args, const char *); 293 | 294 | msgpack_sbuffer_init(&sbuf); /* initialize buffer */ 295 | msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); /* initialize packer */ 296 | 297 | msgpack_pack_map(&pk, count + 5); 298 | 299 | pack_str(&pk, "plugin"); 300 | pack_str(&pk, MAXWELL_PLUGIN_NAME); 301 | 302 | pack_str(&pk, "pid"); 303 | msgpack_pack_uint32(&pk, g_pid); 304 | 305 | pack_str(&pk, "process"); 306 | pack_str(&pk, g_modBuf); 307 | 308 | pack_str(&pk, "threatStartModule"); 309 | pack_wstr(&pk, threatStartModule); 310 | 311 | pack_str(&pk, "function"); 312 | pack_str(&pk, functionName); 313 | 314 | parseArgs(args, fmt, &pk); 315 | 316 | EnterCriticalSection(&g_cs); 317 | 318 | retVal = WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 319 | if (!retVal) 320 | { 321 | int lasterr = GetLastError(); 322 | if (ERROR_NO_DATA == lasterr) 323 | { 324 | // Pipe was closed on the other end, reconnect 325 | CloseHandle(g_hPipe); 326 | g_hPipe = INVALID_HANDLE_VALUE; 327 | if (0 == InitLog()) 328 | { 329 | // Connected succesfully, try to resend 330 | (void)WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 331 | } 332 | } 333 | else if (ERROR_INVALID_HANDLE == lasterr) 334 | { 335 | // We are not currently connected, attempt to connect 336 | if (0 == InitLog()) 337 | { 338 | // Connected succesfully, try to resend 339 | (void)WriteFile(g_hPipe, sbuf.data, sbuf.size, &bytesWritten, 0); 340 | } 341 | } 342 | else 343 | { 344 | printf("lasterr: %d\n", lasterr); 345 | } 346 | } 347 | 348 | LeaveCriticalSection(&g_cs); 349 | 350 | msgpack_sbuffer_destroy(&sbuf); 351 | 352 | return; 353 | } 354 | 355 | void ModuleThread(wchar_t * module) 356 | { 357 | 358 | // This leads to a heap alloc, we don't want that for performance 359 | // swprintf_s(module, MAX_PATH, L"%ws", L""); 360 | memcpy(module, L"",20); 361 | 362 | if (!g_NtQueryInformationThread) 363 | { 364 | return; 365 | } 366 | 367 | DWORD_PTR startAddress = 0; 368 | ULONG retSize = 0; 369 | 370 | if (g_NtQueryInformationThread(GetCurrentThread(), 9, &startAddress, sizeof(PVOID), &retSize) == STATUS_SUCCESS) 371 | { 372 | AddressToModule(startAddress, module, MAX_PATH); 373 | } 374 | 375 | return; 376 | 377 | } 378 | 379 | bool AddressToModule(DWORD_PTR Addr, wchar_t * modName, unsigned int size) 380 | { 381 | NTSTATUS ntStatus = 0; 382 | SIZE_T outBufLen = 0; 383 | WCHAR stringBuffer[0x500]; 384 | PUNICODE_STRING string = (PUNICODE_STRING)stringBuffer; 385 | string->Buffer = stringBuffer + 4; 386 | string->Length = 0x0; 387 | string->MaximumLength = 1000; 388 | 389 | memcpy(modName, L"", sizeof(L"")); 390 | 391 | if (!g_NtQueryVirtualMemory) 392 | return false; 393 | 394 | ntStatus = g_NtQueryVirtualMemory(GetCurrentProcess(), (void*)Addr, MemorySectionName, string, 528, &outBufLen); 395 | 396 | if (STATUS_SUCCESS == ntStatus) 397 | { 398 | wchar_t * mod = &string->Buffer[wcslen(string->Buffer)]; 399 | while (*mod != L'\\') 400 | { 401 | mod--; 402 | } 403 | mod++; 404 | 405 | /* 406 | // For full path 407 | if (memcmp(string->Buffer, L"\\Device\\HarddiskVolume", 44) == 0) 408 | { 409 | swprintf_s(modName, size, L"%ws%ws", L"C:\\", &string->Buffer[24]); 410 | } 411 | */ 412 | memcpy(modName, mod, (wcslen(mod) + 1) * 2); 413 | return true; 414 | 415 | } 416 | 417 | return false; 418 | } 419 | 420 | 421 | } -------------------------------------------------------------------------------- /flux/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace MaxLog 7 | { 8 | 9 | #define MAXWELL_PIPE_NAME L"\\\\.\\PIPE\\Maxwell" 10 | #define MAXWELL_PLUGIN_NAME "flux" 11 | 12 | DWORD InitLog(); 13 | void Log(const char * fmt, ...); 14 | void LogWithMeta(const char * fmt, ...); 15 | void ModuleThread(wchar_t * module); 16 | bool AddressToModule(DWORD_PTR Addr, wchar_t * modName, unsigned int size); 17 | 18 | #define LOG(fmt, ...) MaxLog::LogWithMeta(fmt, &__FUNCTION__[2], ##__VA_ARGS__) 19 | 20 | typedef enum _MEMORY_INFORMATION_CLASS { 21 | MemoryBasicInformation, 22 | MemoryWorkingSetList, 23 | MemorySectionName 24 | } MEMORY_INFORMATION_CLASS; 25 | 26 | typedef NTSTATUS(WINAPI * _NtQueryVirtualMemory)( 27 | _In_ HANDLE ProcessHandle, 28 | _In_opt_ PVOID BaseAddress, 29 | _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, 30 | _Out_ PVOID MemoryInformation, 31 | _In_ SIZE_T MemoryInformationLength, 32 | _Out_opt_ PSIZE_T ReturnLength 33 | ); 34 | 35 | typedef NTSTATUS(WINAPI* _NtQueryInformationThread)( 36 | _In_ HANDLE ThreadHandle, 37 | _In_ int ThreadInformationClass, 38 | _Inout_ PVOID ThreadInformation, 39 | _In_ ULONG ThreadInformationLength, 40 | _Out_opt_ PULONG ReturnLength 41 | ); 42 | 43 | typedef struct _LSA_UNICODE_STRING { 44 | USHORT Length; 45 | USHORT MaximumLength; 46 | PWSTR Buffer; 47 | } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; 48 | 49 | extern const char *g_baseExe; 50 | 51 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000) 52 | 53 | } -------------------------------------------------------------------------------- /flux/ntapi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef LONG NTSTATUS; 7 | 8 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000) 9 | 10 | typedef struct _TEB 11 | { 12 | NT_TIB NtTib; 13 | 14 | } TEB, *PTEB; 15 | 16 | typedef struct _LSA_UNICODE_STRING { 17 | USHORT Length; 18 | USHORT MaximumLength; 19 | PWSTR Buffer; 20 | } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; 21 | 22 | typedef struct _OBJECT_ATTRIBUTES { 23 | ULONG Length; 24 | HANDLE RootDirectory; 25 | PUNICODE_STRING ObjectName; 26 | ULONG Attributes; 27 | PVOID SecurityDescriptor; 28 | PVOID SecurityQualityOfService; 29 | } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; 30 | 31 | typedef enum _FILE_INFORMATION_CLASS { 32 | FileDirectoryInformation = 1, 33 | FileFullDirectoryInformation, 34 | FileBothDirectoryInformation, 35 | FileBasicInformation, 36 | FileStandardInformation, 37 | FileInternalInformation, 38 | FileEaInformation, 39 | FileAccessInformation, 40 | FileNameInformation, 41 | FileRenameInformation, 42 | FileLinkInformation, 43 | FileNamesInformation, 44 | FileDispositionInformation, 45 | FilePositionInformation, 46 | FileFullEaInformation, 47 | FileModeInformation, 48 | FileAlignmentInformation, 49 | FileAllInformation, 50 | FileAllocationInformation, 51 | FileEndOfFileInformation, 52 | FileAlternateNameInformation, 53 | FileStreamInformation, 54 | FilePipeInformation, 55 | FilePipeLocalInformation, 56 | FilePipeRemoteInformation, 57 | FileMailslotQueryInformation, 58 | FileMailslotSetInformation, 59 | FileCompressionInformation, 60 | FileObjectIdInformation, 61 | FileCompletionInformation, 62 | FileMoveClusterInformation, 63 | FileQuotaInformation, 64 | FileReparsePointInformation, 65 | FileNetworkOpenInformation, 66 | FileAttributeTagInformation, 67 | FileTrackingInformation, 68 | FileIdBothDirectoryInformation, 69 | FileIdFullDirectoryInformation, 70 | FileValidDataLengthInformation, 71 | FileShortNameInformation, 72 | FileIoCompletionNotificationInformation, 73 | FileIoStatusBlockRangeInformation, 74 | FileIoPriorityHintInformation, 75 | FileSfioReserveInformation, 76 | FileSfioVolumeInformation, 77 | FileHardLinkInformation, 78 | FileProcessIdsUsingFileInformation, 79 | FileNormalizedNameInformation, 80 | FileNetworkPhysicalNameInformation, 81 | FileIdGlobalTxDirectoryInformation, 82 | FileIsRemoteDeviceInformation, 83 | FileAttributeCacheInformation, 84 | FileNumaNodeInformation, 85 | FileStandardLinkInformation, 86 | FileRemoteProtocolInformation, 87 | FileMaximumInformation 88 | } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; 89 | 90 | typedef struct _FILE_NAME_INFORMATION { 91 | ULONG FileNameLength; 92 | WCHAR FileName[1]; 93 | } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; 94 | 95 | #define DECLARE_UNICODE_STRING_SIZE(_var, _size) \ 96 | WCHAR _var ## _buffer[_size]; \ 97 | ZeroMemory(_var ## _buffer, _size * sizeof(WCHAR)); \ 98 | UNICODE_STRING _var = { 0, _size * sizeof(WCHAR) , _var ## _buffer } 99 | 100 | typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { 101 | ULONG Flags; //Reserved. 102 | PUNICODE_STRING FullDllName; //The full path name of the DLL module. 103 | PUNICODE_STRING BaseDllName; //The base file name of the DLL module. 104 | PVOID DllBase; //A pointer to the base address for the DLL in memory. 105 | ULONG SizeOfImage; //The size of the DLL image, in bytes. 106 | } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; 107 | 108 | typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { 109 | ULONG Flags; //Reserved. 110 | PUNICODE_STRING FullDllName; //The full path name of the DLL module. 111 | PUNICODE_STRING BaseDllName; //The base file name of the DLL module. 112 | PVOID DllBase; //A pointer to the base address for the DLL in memory. 113 | ULONG SizeOfImage; //The size of the DLL image, in bytes. 114 | } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; 115 | 116 | typedef union _LDR_DLL_NOTIFICATION_DATA { 117 | LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; 118 | LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; 119 | } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; 120 | 121 | typedef NTSTATUS( NTAPI * _LdrRegisterDllNotification) ( 122 | _In_ ULONG Flags, 123 | _In_ PVOID NotificationFunction, 124 | _In_opt_ PVOID Context, 125 | _Out_ PVOID *Cookie 126 | ); 127 | 128 | #define LDR_DLL_NOTIFICATION_REASON_LOADED 1 129 | 130 | typedef struct _CURDIR 131 | { 132 | UNICODE_STRING DosPath; 133 | PVOID Handle; 134 | } CURDIR, *PCURDIR; 135 | 136 | typedef struct _STRING 137 | { 138 | WORD Length; 139 | WORD MaximumLength; 140 | CHAR * Buffer; 141 | } STRING, *PSTRING; 142 | 143 | typedef struct _RTL_DRIVE_LETTER_CURDIR 144 | { 145 | WORD Flags; 146 | WORD Length; 147 | ULONG TimeStamp; 148 | STRING DosPath; 149 | } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; 150 | 151 | typedef struct _RTL_USER_PROCESS_PARAMETERS 152 | { 153 | ULONG MaximumLength; 154 | ULONG Length; 155 | ULONG Flags; 156 | ULONG DebugFlags; 157 | PVOID ConsoleHandle; 158 | ULONG ConsoleFlags; 159 | PVOID StandardInput; 160 | PVOID StandardOutput; 161 | PVOID StandardError; 162 | CURDIR CurrentDirectory; 163 | UNICODE_STRING DllPath; 164 | UNICODE_STRING ImagePathName; 165 | UNICODE_STRING CommandLine; 166 | PVOID Environment; 167 | ULONG StartingX; 168 | ULONG StartingY; 169 | ULONG CountX; 170 | ULONG CountY; 171 | ULONG CountCharsX; 172 | ULONG CountCharsY; 173 | ULONG FillAttribute; 174 | ULONG WindowFlags; 175 | ULONG ShowWindowFlags; 176 | UNICODE_STRING WindowTitle; 177 | UNICODE_STRING DesktopInfo; 178 | UNICODE_STRING ShellInfo; 179 | UNICODE_STRING RuntimeData; 180 | RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32]; 181 | ULONG EnvironmentSize; 182 | } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; 183 | 184 | typedef struct _FILE_BASIC_INFORMATION { 185 | LARGE_INTEGER CreationTime; 186 | LARGE_INTEGER LastAccessTime; 187 | LARGE_INTEGER LastWriteTime; 188 | LARGE_INTEGER ChangeTime; 189 | ULONG FileAttributes; 190 | } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; 191 | -------------------------------------------------------------------------------- /flux/whitelist.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "log.h" 3 | 4 | const char *process_whitelist[] = { 5 | "C:\\Python27\\python.exe", 6 | "\\\\?\\C:\\Windows\\system32\\wbem\\WMIADAP.EXE", 7 | "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\mscorsvw.exe", 8 | }; 9 | 10 | bool ProcessWhitelist() 11 | { 12 | char ProcPath[MAX_PATH]; 13 | GetModuleFileNameA(NULL, ProcPath, MAX_PATH); 14 | for (int i = 0; i < ARRAYSIZE(process_whitelist); i++) 15 | { 16 | if (strcmp(ProcPath, process_whitelist[i]) == 0) 17 | { 18 | return true; 19 | } 20 | } 21 | return false; 22 | 23 | } 24 | 25 | typedef struct 26 | { 27 | const wchar_t * filePath; 28 | int ruleType; 29 | } FileWhitelist; 30 | 31 | const FileWhitelist fileWhitelist[] = 32 | { 33 | {L"\\lsass", 0}, 34 | {L"\\wkssvc", 0}, 35 | {L"\\MsFteWds", 0}, 36 | {L"\\srvsvc", 0}, 37 | {L"\\Maxwell", 0}, 38 | {L"\\traffic.pcap", 0}, 39 | {L"\\Users\\max\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\Content.IE5\\", 1}, 40 | {L"\\Users\\max\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\", 1}, 41 | {L"\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\DOMStore\\", 1}, 42 | {L"\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\Recovery", 1}, 43 | {L"\\Users\\max\\AppData\\LocalLow\\Microsoft\\Internet Explorer\\Services\\", 1}, 44 | {L"\\Users\\max\\AppData\\LocalLow\\Microsoft\\CryptnetUrlCache\\", 1}, 45 | {L"\\Users\\max\\AppData\\Roaming\\Macromedia\\Flash Player\\", 1}, 46 | {L"\\Users\\max\\AppData\\Roaming\\Adobe\\Flash Player\\AssetCache\\", 1}, 47 | {L"\\Users\\max\\AppData\\Roaming\\Microsoft\\Internet Explorer\\UserData\\", 1}, 48 | {L"\\Users\\max\\AppData\\Local\\Microsoft\\Windows\\WER\\ReportArchive\\NonCritical", 1}, 49 | {L"\\Windows\\System32\\wbem\\Performance\\", 1}, 50 | {L"\\Windows\\Microsoft.NET\\Framework\v2.0.50727\\", 1}, 51 | {L"\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\VersionManager\\ver", 1}, 52 | {L"\\logs\\", 1}, 53 | {L"\\drop\\", 1}, 54 | }; 55 | 56 | bool IsFilePathWhitelisted(const wchar_t * filePath, size_t len) 57 | { 58 | for (size_t i = 0; i < ARRAYSIZE(fileWhitelist); i++) 59 | { 60 | if (fileWhitelist[i].ruleType == 0) 61 | { 62 | if (memcmp(filePath, fileWhitelist[i].filePath, sizeof(fileWhitelist[i].filePath)) == 0) 63 | { 64 | return true; 65 | } 66 | } 67 | else if (_wcsnicmp(filePath, fileWhitelist[i].filePath, sizeof(fileWhitelist[i].filePath)) == 0) 68 | { 69 | return true; 70 | } 71 | 72 | } 73 | return false; 74 | } 75 | -------------------------------------------------------------------------------- /flux/whitelist.h: -------------------------------------------------------------------------------- 1 | #ifndef _WHITELIST_H_ 2 | #define _WHITELIST_H_ 3 | bool ProcessWhitelist(); 4 | bool IsFilePathWhitelisted(const wchar_t * filePath, size_t len); 5 | 6 | #endif -------------------------------------------------------------------------------- /flux/x64/Release/flux.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/Maxwell/db4d8b189ff2f39f7c5235bb6a1a9974f7532a8c/flux/x64/Release/flux.dll -------------------------------------------------------------------------------- /python/addSites.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import uuid 4 | import time 5 | import pika 6 | import msgpack 7 | 8 | class RMQChannel: 9 | def __init__(self, rmqServer, channelName): 10 | self.rmqServer = rmqServer 11 | self.channelName = channelName 12 | 13 | def connect(self): 14 | for x in xrange(0,20): 15 | # Ephemeral port re-use of snapshots leads to this bizzare behavior 16 | try: 17 | self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.rmqServer)) 18 | break 19 | except pika.exceptions.ConnectionClosed: 20 | if x == 19: 21 | raise 22 | args = {"x-max-priority":10} 23 | self.channel = self.connection.channel() 24 | self.channel.queue_declare(queue=self.channelName, arguments=args) 25 | 26 | def close(self): 27 | self.channel.close() 28 | self.connection.close() 29 | 30 | def send(self, msg): 31 | data = msgpack.packb(msg) 32 | self.channel.basic_publish(exchange='',routing_key=self.channelName, body=data, properties=pika.BasicProperties(priority=2)) 33 | 34 | skip = 0 35 | total = 100000 36 | f = open("1m.txt","r") 37 | x = 0 38 | sites = "" 39 | RMQ_SERVER = '127.0.0.1' 40 | rmq = RMQChannel(RMQ_SERVER,'maxwell_queue') 41 | rmq.connect() 42 | 43 | for line in f.readlines(): 44 | if skip > 0: 45 | skip -= 1 46 | continue 47 | line = line.rstrip() 48 | x = x + 1 49 | if (x % 5) == 1: 50 | sites = "http://" + line 51 | elif x % 5 == 0: 52 | sites = sites + "," + "http://" + line 53 | print sites 54 | msg = {} 55 | msg['plugin'] = 'flux' 56 | msg['flux'] = {} 57 | msg['flux']['uuid'] = str(uuid.uuid4()) 58 | msg['flux']['url'] = sites 59 | rmq.send(msg) 60 | time.sleep(0.05) 61 | else: 62 | sites = sites + "," + "http://" + line 63 | 64 | if x >= total: 65 | break 66 | 67 | rmq.close() -------------------------------------------------------------------------------- /python/agent.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | import subprocess 5 | import base64 6 | import sys 7 | import traceback 8 | 9 | while True: 10 | try: 11 | print "Agent running.." 12 | 13 | data = "" 14 | 15 | # Loop until job.txt is found 16 | while os.path.exists("job.txt") == False: 17 | time.sleep(1) 18 | 19 | print "Received Job" 20 | 21 | # Read job.txt 22 | with open("job.txt","rb") as f: 23 | data = f.read() 24 | 25 | # Decode job from json 26 | job = json.loads(data) 27 | 28 | # execute environment setup 29 | if job.has_key('environment'): 30 | for env in job['environment']: 31 | print env 32 | os.chdir(env) 33 | proc = subprocess.Popen([sys.executable, "setup.py"]) 34 | proc.communicate() 35 | os.chdir('..') 36 | 37 | plugin = job['plugin'] 38 | 39 | # Pass plugin config as base64 encoded string 40 | pluginParam = base64.b64encode(json.dumps(job[plugin])) 41 | 42 | os.remove("job.txt") 43 | 44 | # Launch plugin 45 | print "Lauching %s" % plugin 46 | os.chdir(plugin) 47 | proc = subprocess.Popen([sys.executable, plugin + ".py", pluginParam]) 48 | proc.communicate() 49 | os.chdir('..') 50 | 51 | print "job finished" 52 | except: 53 | traceback.print_exc() 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /python/controller.py: -------------------------------------------------------------------------------- 1 | import xmlrpclib 2 | import time 3 | import sys 4 | from zipfile import ZipFile, ZIP_STORED 5 | from StringIO import StringIO 6 | import os 7 | import thread 8 | import threading 9 | import subprocess 10 | import traceback 11 | from pysphere import VIServer 12 | import re 13 | import datetime 14 | import getpass 15 | import msgpack 16 | import logging 17 | import pika 18 | import json 19 | from elasticsearch import Elasticsearch 20 | 21 | logger = logging.getLogger('maxwell') 22 | logger.setLevel(logging.DEBUG) 23 | # create console handler and set level to debug 24 | ch = logging.StreamHandler() 25 | ch.setLevel(logging.DEBUG) 26 | # create formatter 27 | formatter = logging.Formatter('%(asctime)s %(levelname)s:%(message)s') 28 | # add formatter to ch 29 | ch.setFormatter(formatter) 30 | # add ch to logger 31 | logger.addHandler(ch) 32 | 33 | # create file handler 34 | fh = logging.FileHandler('maxwell.log') 35 | fh.setLevel(logging.DEBUG) 36 | # create formatter 37 | formatter = logging.Formatter('%(asctime)s %(levelname)s:%(message)s') 38 | # add formatter to fh 39 | fh.setFormatter(formatter) 40 | # add fh to logger 41 | logger.addHandler(fh) 42 | 43 | # Configuration here 44 | MAXTIMEOUT = 200 45 | MasterURL = "http://127.0.0.1:5000" 46 | mutex = threading.Lock() 47 | MAXJOBS_PER_SNAPSHOT = 15 48 | esxUser = r'Insert_Username' 49 | esxPass = r'' 50 | esxHost = r'Insert_Esx_IP' 51 | rmqServer = "127.0.0.1" 52 | RMQ_SERVER_JOB = "Insert_RMQ_Server_IP" #5672 53 | ESHost = "http://127.0.0.1:9200" 54 | 55 | jobStatus = {} 56 | 57 | def log(vm, msg): 58 | t = str(datetime.datetime.now()) 59 | # or t = time.strftime("%Y-%m-%d %H:%M:%S") 60 | f = open("log.txt","a") 61 | f.write("[" + t + "] - [" + vm + "] - " + msg + "\r\n") 62 | f.close() 63 | print "[" + t + "] - [" + vm + "] - " + msg 64 | 65 | def TestConnection(host,port): 66 | try: 67 | #print "Testing %s:%s" % (host,port) 68 | nc = subprocess.Popen(['nc','-w','2','-zv',host,port], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 69 | out, error = nc.communicate() 70 | if error.find('succeeded') >= 0: 71 | return True 72 | return False 73 | except: 74 | traceback.print_exc() 75 | return False 76 | 77 | def Connected(): 78 | try: 79 | ping = subprocess.Popen(['ping','-c','1','google.com'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 80 | out, error = ping.communicate() 81 | m = re.search(r'time=(.*?) ms', out) 82 | if m == None: 83 | log("Main", "Ping timeout") 84 | return False 85 | delay = float(m.group(1)) 86 | log("Main", "Ping - " + str(delay)) 87 | if delay < 100: 88 | return True 89 | return False 90 | except: 91 | log("Main", "Ping - Unknown Error") 92 | return False 93 | 94 | class ESXMachine: 95 | def __init__(self, Name, Snapshot, OS, IP, userName='max', workQueue='maxwell_queue', rmqServerJob = RMQ_SERVER_JOB, maxJobsPerSnapshot = MAXJOBS_PER_SNAPSHOT): 96 | self.Name = Name 97 | self.Snapshot = Snapshot 98 | self.OS = OS 99 | self.IP = IP 100 | self.userName = userName 101 | self.workQueue = workQueue 102 | self.rmqServerJob = rmqServerJob 103 | self.maxJobsPerSnapshot = maxJobsPerSnapshot 104 | self.status = 'idle' 105 | 106 | def GetInfo(self): 107 | print self.VMXPath 108 | print self.Snapshot 109 | print self.OS 110 | print self.IP 111 | print self.status 112 | 113 | def Connect(self): 114 | self.server = VIServer() 115 | self.server.connect(esxHost, esxUser, esxPass) 116 | self.vm = self.server.get_vm_by_name(self.Name) 117 | 118 | def Start(self): 119 | try: 120 | logger.debug("%s - Connecting to ESXi" % (self.Name)) 121 | self.Connect() 122 | logger.debug("%s - Starting VM" % (self.Name)) 123 | self.vm.revert_to_named_snapshot(self.Snapshot) 124 | except: 125 | logger.error("%s - Error on Revert - %s" % (self.Name, traceback.format_exc())) 126 | 127 | def Stop(self): 128 | try: 129 | logger.debug("%s - Stopping VM" % (self.Name)) 130 | self.vm.power_off() 131 | self.server.disconnect() 132 | except: 133 | logger.error("%s - Error on Stop" % (self.Name)) 134 | 135 | def Finish(self): 136 | mutex.acquire() 137 | self.Stop() 138 | mutex.release() 139 | self.status = 'idle' 140 | logger.debug("%s - shutdown" % (self.Name)) 141 | 142 | def callback(ch, method, properties, body): 143 | logger.debug("Callback") 144 | try: 145 | msg = msgpack.unpackb(body) 146 | print msg 147 | jobStatus[msg['uuid']] = msg['status'] 148 | except: 149 | logger.error("Callback error: %s" % (traceback.format_exc())) 150 | 151 | def StatusCallback(ch, method, properties, body): 152 | try: 153 | msg = msgpack.unpackb(body) 154 | jobStatus[msg['uuid']] = msg['status'] 155 | logger.debug("Received status: %s - %s" % (msg['uuid'], msg['status'])) 156 | except: 157 | logger.error("Callback error: %s" % (traceback.format_exc())) 158 | 159 | class RMQConsumer(threading.Thread): 160 | def __init__(self, queueName, callback): 161 | threading.Thread.__init__(self) 162 | self.queueName = queueName 163 | self.daemon = True 164 | 165 | self.conn = pika.BlockingConnection(pika.ConnectionParameters( 166 | host=rmqServer)) 167 | self.statusChannel = self.conn.channel() 168 | 169 | self.statusChannel.queue_declare(queue=queueName) 170 | 171 | self.statusChannel.basic_consume(callback, 172 | queue=queueName, 173 | no_ack=True) 174 | def run(self): 175 | logger.debug("Consumer started on queue: %s" % self.queueName) 176 | self.statusChannel.start_consuming() 177 | 178 | 179 | class RMQChannel: 180 | def __init__(self, rmqServer, channelName): 181 | self.rmqServer = rmqServer 182 | self.channelName = channelName 183 | 184 | def connect(self, priority=False): 185 | for x in xrange(0,20): 186 | # Ephemeral port re-use of snapshots leads to this bizzare behavior 187 | try: 188 | self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.rmqServer)) 189 | break 190 | except pika.exceptions.ConnectionClosed: 191 | if x == 19: 192 | raise 193 | if priority: 194 | args = {"x-max-priority":10} 195 | else: 196 | args = {} 197 | self.channel = self.connection.channel() 198 | self.channel.queue_declare(queue=self.channelName, arguments=args) 199 | 200 | def close(self): 201 | self.channel.close() 202 | self.connection.close() 203 | 204 | def send(self, msg): 205 | data = msgpack.packb(msg) 206 | self.channel.basic_publish(exchange='',routing_key=self.channelName, body=data) 207 | 208 | def recv(self): 209 | method_frame, header_frame, body = self.channel.basic_get(queue=self.channelName, no_ack=True) 210 | return body 211 | 212 | def Worker(VM): 213 | uuid = "" 214 | env = [] 215 | 216 | while True: 217 | try: 218 | VM.status = 'running' 219 | VM.Start() 220 | 221 | for x in xrange(0, VM.maxJobsPerSnapshot): 222 | # grab next job 223 | queueChannel = RMQChannel(rmqServer, VM.workQueue) 224 | queueChannel.connect(priority=True) 225 | while True: 226 | body = queueChannel.recv() 227 | if body: 228 | break 229 | logger.debug("%s - work queue is empty" % (VM.Name)) 230 | time.sleep(15) 231 | queueChannel.close() 232 | job = msgpack.unpackb(body) 233 | 234 | plugin = job['plugin'] 235 | uuid = job[plugin]['uuid'] 236 | logger.debug("%s - plugin config - %s" % (VM.Name,str(job[plugin]))) 237 | logger.debug("%s - IP - %s" % (VM.Name,VM.IP)) 238 | 239 | # send start msg to resultServer 240 | if plugin == 'flux': 241 | resultServer = RMQChannel(rmqServer, "maxwell") 242 | resultServer.connect() 243 | startMsg = {'plugin':plugin, 'uuid':uuid, 'status':'started', 'url':job[plugin]['url']} 244 | if job.has_key('environment'): 245 | startMsg['environment'] = job['environment'] 246 | resultServer.send(startMsg) 247 | resultServer.close() 248 | 249 | # Update Job 250 | job[plugin]['datetime'] = datetime.datetime.utcnow().strftime("%Y%m%dT%H:%M:%S") 251 | job[plugin]['rmqServer'] = VM.rmqServerJob 252 | 253 | # copy environment data to host 254 | if job.has_key('environment'): 255 | for env in job['environment']: 256 | logger.debug("%s - Copying %s to host" % (VM.Name, env)) 257 | with open(os.devnull, 'w') as devnull: 258 | proc = subprocess.Popen(['scp','-o','UserKnownHostsFile=/dev/null','-o', 'StrictHostKeyChecking=no','-i','maxwell_key', '-r', env, VM.userName+'@'+VM.IP+':desktop/'], stdout=devnull) 259 | proc.communicate() 260 | 261 | # copy plugin to host 262 | logger.debug("%s - Copying %s to host" % (VM.Name, plugin)) 263 | with open(os.devnull, 'w') as devnull: 264 | proc = subprocess.Popen(['scp','-o','UserKnownHostsFile=/dev/null','-o', 'StrictHostKeyChecking=no','-i','maxwell_key', '-r', plugin, VM.userName+'@'+VM.IP+':desktop/'], stdout=devnull) 265 | proc.communicate() 266 | 267 | # save job to file, send to host 268 | logger.debug("%s - Copying job to host" % (VM.Name)) 269 | jobFile = uuid+'.txt' 270 | f = open(jobFile,"wb") 271 | f.write(json.dumps(job)) 272 | f.close() 273 | with open(os.devnull, 'w') as devnull: 274 | proc = subprocess.Popen(['scp','-o','UserKnownHostsFile=/dev/null','-o', 'StrictHostKeyChecking=no','-i','maxwell_key', jobFile, VM.userName+'@'+VM.IP+':desktop/job.txt'], stdout=devnull) 275 | proc.communicate() 276 | os.remove(jobFile) 277 | 278 | start = time.time() 279 | timeout = 300 280 | while True: 281 | if jobStatus.has_key(uuid) and jobStatus[uuid] == 'started': 282 | logger.debug("%s - Plugin Started after %ds" % (VM.Name, time.time() - start)) 283 | break 284 | if time.time() - start > 30: 285 | logger.error("%s - slow vm.. %s" % (VM.Name, VM.IP)) 286 | if time.time() - start > timeout: 287 | logger.error("%s - timeout initializing plugin" % (VM.Name)) 288 | quit() 289 | break 290 | time.sleep(1) 291 | 292 | if time.time() - start > 30: 293 | logger.error("%s - excessive wait time: %ds" % (VM.Name, time.time() - start)) 294 | 295 | if not jobStatus.has_key(uuid) or jobStatus[uuid] != 'started': 296 | break 297 | 298 | # Wait for job to finish 299 | maxTime = time.time() + MAXTIMEOUT 300 | while time.time() < maxTime: 301 | if jobStatus[uuid] == "finished": 302 | break 303 | time.sleep(1) 304 | 305 | if time.time() - (maxTime - MAXTIMEOUT) > 120: 306 | logger.error("%s - excessive plugin wait time: %ds" % (VM.Name, (time.time() - (maxTime - MAXTIMEOUT)))) 307 | 308 | if jobStatus[uuid] == "started": 309 | logger.error("%s - timeout waiting for plugin finish" % (VM.Name)) 310 | quit() 311 | break 312 | elif jobStatus[uuid] == "failed": 313 | logger.error("%s - status failed" % (VM.Name)) 314 | break 315 | else: 316 | logger.debug("%s - finished" % (VM.Name)) 317 | 318 | del jobStatus[uuid] 319 | 320 | # Detect 321 | es = Elasticsearch(ESHost) 322 | #query = {'query': { 'term': {'uuid':uuid} } } 323 | results = es.search(index="m_index",doc_type="maxwell", q='uuid:"'+uuid+'"') 324 | 325 | if results['hits']['total'] > 2: 326 | logger.debug("%s - malicious!" % (VM.Name)) 327 | break 328 | except: 329 | logger.error("%s - Worker, Unexpected Error - %s" % (VM.Name, traceback.format_exc())) 330 | quit() 331 | 332 | # House keeping 333 | if jobStatus.has_key(uuid): 334 | del jobStatus[uuid] 335 | 336 | 337 | if __name__ == '__main__': 338 | VMs = [] 339 | 340 | esxPass = getpass.getpass("Enter esx password: ") 341 | 342 | #Name, Snapshot, OS, IP): 343 | VMs.append(ESXMachine('VM_Clone_Name','Snapshot_Name','WIN7','VM_IP')) 344 | 345 | 346 | statusConsumer = RMQConsumer('maxwell_status', StatusCallback) 347 | statusConsumer.start() 348 | 349 | for VM in VMs: 350 | thread.start_new_thread(Worker, (VM,)) 351 | 352 | while True: 353 | time.sleep(1) 354 | 355 | -------------------------------------------------------------------------------- /python/dashboard.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import json 3 | from elasticsearch import Elasticsearch 4 | import os 5 | import pika 6 | import msgpack 7 | import datetime 8 | 9 | ESHost = "http://127.0.0.1:9200" 10 | RMQSERVER = "127.0.0.1" 11 | 12 | 13 | class RMQChannel: 14 | def __init__(self, rmqServer, channelName): 15 | self.rmqServer = rmqServer 16 | self.channelName = channelName 17 | 18 | def connect(self): 19 | self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.rmqServer)) 20 | args = {"x-max-priority":10} 21 | self.channel = self.connection.channel() 22 | self.channel.queue_declare(queue=self.channelName, arguments=args) 23 | 24 | def close(self): 25 | self.channel.close() 26 | self.connection.close() 27 | 28 | def send(self, msg): 29 | data = msgpack.packb(msg) 30 | self.channel.basic_publish(exchange='',routing_key=self.channelName, body=data, properties=pika.BasicProperties(priority=5)) 31 | 32 | class MaxwellDashboard(): 33 | def __init__(self): 34 | self.esHost = ESHost 35 | self.es = Elasticsearch(self.esHost) 36 | 37 | def PP(self,event): 38 | if event['function'] == "NtCreateUserProcess": 39 | return " ".join((event['process'],event['function'],event['ImagePathName'],event['CommandLine'])) 40 | elif event['function'] == "oredHandler": 41 | if event.has_key('EAF'): 42 | return " ".join((event['process'],event['EAF'],"Source:",event['ModSource'],"Target:",event['ModTarget'])) 43 | elif event.has_key('ExceptionCode'): 44 | return " ".join((event['process'],'Exception',"Source:",event['Module'],"Code:",event['ExceptionCode'])) 45 | elif event['function'] == "NtWriteFile": 46 | return " ".join((event['process'],event['function'],"Path:",event['FileName'])) 47 | elif event['function'] == "NtSetValueKey": 48 | return " ".join((event['process'],event['function'],event['KeyPath'],'->',event['KeyValue'])) 49 | elif event['function'] == 'Main': 50 | return " ".join((event['process'],event['WerFault'])) 51 | elif event['function'] == 'NtFreeVirtualMemory': 52 | return " ".join((event['process'],event['function'],'RegionBase',event['RegionBase'])) 53 | elif event['function'] == 'NtQueryAttributesFile' or event['function'] == 'NtCreateFile' or event['function'] == 'NtOpenKeyEx': 54 | return " ".join((event['process'],"VMDetect", event['VMDetect'])) 55 | else: 56 | print event 57 | raise Exception("Unknown Function: %s" % event['function']) 58 | 59 | def QueueSite(self, url, channel="maxwell_queue"): 60 | if url[:7] == "http://": 61 | rmq = RMQChannel(RMQ_SERVER, channel) 62 | rmq.connect() 63 | msg = {} 64 | msg['plugin'] = 'flux' 65 | msg['flux'] = {} 66 | msg['flux']['uuid'] = str(uuid.uuid4()) 67 | msg['flux']['url'] = url 68 | rmq.send(msg) 69 | rmq.close() 70 | return "Submitted" 71 | else: 72 | return "Invalid URL" 73 | 74 | def TotalJobs(self): 75 | query = {'query': { 'constant_score' : { 'filter' : { 'term': 76 | {'status':'started'} 77 | } } } } 78 | results = self.es.search(index="m_index",doc_type="maxwell", body=query) 79 | return results['hits']['total'] 80 | 81 | def Performance(self): 82 | query = {'query': { 'constant_score' : { 'filter' : { 'bool': { 'must' : [ 83 | {'term': {'status':'started'} }, 84 | {'range': {'timestamp':{"gt" : "now-1h"} } }, 85 | ] } } } } } 86 | results = self.es.search(index="m_index",doc_type="maxwell", body=query, size=10) 87 | #for hit in results['hits']['hits']: 88 | # print hit 89 | return results['hits']['total'] 90 | 91 | def GetHits(self): 92 | query = {'query': { 'constant_score' : { 'filter' : { 'bool': { 93 | 'must' : [ 94 | {'range': {'timestamp':{"gt" : "now-1w"} } }, 95 | {'term': {'status':'malicious'} }, 96 | ], 97 | } } } } } 98 | results = self.es.search(index="m_index",doc_type="maxwell", body=query, size=100, sort="timestamp:desc") 99 | uuids = [] 100 | for hit in results['hits']['hits']: 101 | uuid = hit['_source']['uuid'] 102 | if uuid not in uuids: 103 | uuids.append(uuid) 104 | return uuids 105 | 106 | def GetBasicJobInfo(self, uuid): 107 | info = {} 108 | results = self.es.search(index="m_index",doc_type="maxwell", q='uuid:"%s" AND status:started'%(uuid)) 109 | if results['hits']['total'] < 1: 110 | return 111 | hit = results['hits']['hits'][0] 112 | results = self.es.search(index="m_index",doc_type="maxwell", q='uuid:"%s"' %(uuid)) 113 | eventCount = results['hits']['total'] 114 | date = datetime.datetime.strptime(hit['_source']['timestamp'][:19], "%Y-%m-%dT%H:%M:%S") 115 | info['timestamp'] = date.strftime("%m/%d %H:%M") 116 | info['uuid'] = uuid 117 | info['url'] = hit['_source']['url'] 118 | info['eventCount'] = eventCount - 4 119 | return info 120 | 121 | def PostProcessFromUUID(self, uuid): 122 | # ToDo, grab latest post process 123 | results = self.es.search(index="m_index",doc_type="maxwell", q='uuid:"%s" AND postProcess:*'%(uuid)) 124 | if results['hits']['total'] < 1: 125 | return "" 126 | hit = results['hits']['hits'][0]['_source']['postProcess'] 127 | return hit 128 | 129 | def GetEventsByUUID(self, uuid): 130 | query = {'query': { 'constant_score' : { 'filter' : { 'bool': { 131 | 'must' : [ 132 | {'query_string': {'query':'uuid:"'+ uuid + '"'} }, 133 | {'exists': {'field':'function'} }, 134 | ], 135 | #'must_not' : [ 136 | #{'exists': {'field':'status'} }, 137 | #], 138 | } } } } } 139 | results = self.es.search(index="m_index",doc_type="maxwell", body=query, size=1000, sort="timestamp:asc") 140 | 141 | events = "" 142 | dedup = {} 143 | for hit in results['hits']['hits']: 144 | event = self.PP(hit['_source']) 145 | if not dedup.has_key(event): 146 | events += event + "\r\n" 147 | dedup[event] = None 148 | return events 149 | 150 | def DeleteResults(self, uuid): 151 | #delete by query not supported? results = self.es.delete_by_query(index="m_index",doc_type="maxwell", q='uuid:"%s"'%(uuid)) 152 | results = self.es.search(index="m_index",doc_type="maxwell", q='uuid:"%s"'%(uuid), size=1000) 153 | for hit in results['hits']['hits']: 154 | id = hit['_id'] 155 | self.es.delete(index="m_index",doc_type="maxwell", id=id) 156 | 157 | def EnumDroppedFiles(self, uuid): 158 | dropped_files = "" 159 | for root, dirs, files in os.walk(os.path.join('extracted',uuid)): 160 | for name in files: 161 | path = os.path.join(root, name).replace(os.path.join('extracted',uuid), "") 162 | dropped_files += path + "\r\n" 163 | return dropped_files 164 | 165 | if __name__ == "__main__": 166 | 167 | dash = MaxwellDashboard() 168 | print "Total jobs", dash.TotalJobs() 169 | 170 | perHr = dash.Performance() 171 | print "Performance: %d jobs per hr" % (perHr) 172 | print "Performance: %d sites per day (at 5 per job)" % (perHr * 24 * 5) 173 | hits = dash.GetHits() 174 | for uuid in hits: 175 | print "-"*30 176 | print dash.GetBasicJobInfo(uuid) 177 | print dash.PostProcessFromUUID(uuid) 178 | print "" 179 | print dash.GetEventsByUUID(uuid) 180 | print "" 181 | print "Dropped Files:\r\n" + dash.EnumDroppedFiles(uuid) 182 | 183 | -------------------------------------------------------------------------------- /python/flux/flux.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import subprocess 4 | import time 5 | import csv 6 | import re 7 | import shutil 8 | import traceback 9 | import msgpack 10 | import base64 11 | import datetime 12 | import json 13 | import pika 14 | import win32api 15 | import win32con 16 | import signal 17 | 18 | def WriteRegValue(hiveKey, key, name, data, typeId=win32con.REG_SZ): 19 | """ Write one value to Windows registry. If 'name' is empty string, writes default value. 20 | Creates subkeys as necessary""" 21 | try: 22 | keyHandle = win32api.RegOpenKeyEx(hiveKey, key, 0, win32con.KEY_ALL_ACCESS) 23 | win32api.RegSetValueEx(keyHandle, name, 0, typeId, data) 24 | win32api.RegCloseKey(keyHandle) 25 | except Exception, e: 26 | print "WriteRegValue failed:", hiveKey, name, e 27 | 28 | class RMQStatus: 29 | def __init__(self, uuid, rmqServer): 30 | self.uuid = uuid 31 | self.rmqServer = rmqServer 32 | 33 | def connect(self): 34 | for x in xrange(0,20): 35 | # Ephemeral port re-use of snapshots leads to this bizzare behavior 36 | try: 37 | self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.rmqServer)) 38 | break 39 | except pika.exceptions.ConnectionClosed: 40 | if x == 19: 41 | raise 42 | 43 | self.channel = self.connection.channel() 44 | self.channel.queue_declare(queue='maxwell_status') 45 | 46 | def close(self): 47 | self.channel.close() 48 | self.connection.close() 49 | 50 | def SendStatus(self, status): 51 | print "Sending status: %s" % status 52 | msg = {} 53 | msg['uuid'] = self.uuid 54 | msg['plugin'] = 'flux' 55 | msg['status'] = status 56 | 57 | data = msgpack.packb(msg) 58 | 59 | self.channel.basic_publish(exchange='',routing_key='maxwell_status', body=data) 60 | 61 | if __name__ == "__main__": 62 | 63 | # Load proxy if present 64 | arg = base64.b64decode(sys.argv[1]) 65 | job = json.loads(arg) 66 | 67 | # Update local time 68 | if job.has_key('datetime'): 69 | date = datetime.datetime.strptime(job['datetime'], "%Y%m%dT%H:%M:%S") 70 | win32api.SetSystemTime(date.year,date.month,0,date.day,date.hour,date.minute,date.second,0) 71 | 72 | rmqStatus = RMQStatus(job['uuid'], job['rmqServer']) 73 | rmqStatus.connect() 74 | 75 | # Send initial status 76 | rmqStatus.SendStatus("started") 77 | 78 | start = time.time() 79 | 80 | # Start Pipe Server 81 | pServer = subprocess.Popen([sys.executable, 'pipeServer.py', job['uuid'], job['rmqServer']]) 82 | 83 | # Move flux to c:\flux.dll 84 | try: 85 | os.rename("flux32.dll", r"c:\flux32.dll") 86 | except: 87 | pass 88 | try: 89 | os.rename("flux64.dll", r"c:\flux64.dll") 90 | except: 91 | pass 92 | 93 | WriteRegValue(win32con.HKEY_LOCAL_MACHINE, r"Software\Microsoft\Windows NT\CurrentVersion\Windows", "AppInit_DLLs", r"c:\flux32.dll", win32con.REG_SZ) 94 | 95 | # Launch TcpDump 96 | dump = subprocess.Popen(['tdump.exe', '-i', '3', '-w', r'c:\traffic.pcap', 'not port 9000 and not port 5672 and not port 22']) 97 | time.sleep(2) 98 | 99 | # Run Target 100 | sites = job['url'].split(",") 101 | for site in sites: 102 | os.startfile(site) 103 | 104 | try: 105 | os.mkdir(r'c:\drop') 106 | except: 107 | print "Error creating drop directory" 108 | 109 | # Sleep for X time 110 | # 70 111 | time.sleep(60) 112 | 113 | # kill TcpDump 114 | dump.kill() 115 | 116 | # send pcap to result server 117 | t1 = time.time() 118 | try: 119 | msg = {} 120 | msg['plugin'] = "flux" 121 | pipe = os.open("\\\\.\\pipe\\Maxwell",os.O_BINARY|os.O_WRONLY) 122 | f = open(r'c:\traffic.pcap',"rb") 123 | msg['pcapData'] = f.read() 124 | f.close() 125 | data = msgpack.packb(msg) 126 | print "pack",time.time()-t1 127 | t1 = time.time() 128 | os.write(pipe,data) 129 | os.close(pipe) 130 | except: 131 | traceback.print_exc() 132 | 133 | print "Time to send traffic:", time.time()-t1 134 | 135 | time.sleep(5) 136 | 137 | try: 138 | os.remove(r'c:\traffic.pcap') 139 | except: 140 | traceback.print_exc() 141 | 142 | # Send done msg to result server 143 | try: 144 | msg = {} 145 | msg['plugin'] = "flux" 146 | msg['status'] = 'finished' 147 | pipe = os.open("\\\\.\\pipe\\Maxwell",os.O_BINARY|os.O_WRONLY) 148 | data = msgpack.packb(msg) 149 | os.write(pipe,data) 150 | os.close(pipe) 151 | except: 152 | traceback.print_exc() 153 | 154 | time.sleep(1) 155 | 156 | # kill IE 157 | kill_ie = subprocess.Popen(['taskkill', '/f', '/im', 'iexplore.exe']) 158 | kill_ie.communicate() 159 | 160 | # terminate pipeServer 161 | pServer.terminate() 162 | 163 | # Send finished status to controller 164 | rmqStatus.SendStatus("finished") 165 | 166 | rmqStatus.close() 167 | 168 | print "Total plugin time: %d" % (time.time() - start) 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /python/flux/fluxFilter.py: -------------------------------------------------------------------------------- 1 | import re 2 | import json 3 | import traceback 4 | 5 | class Filter(): 6 | 7 | def __init__(self): 8 | self.msgs = {} 9 | self.rules = [] 10 | self.LoadRules() 11 | 12 | def filter(self, msg): 13 | saveMsg = True 14 | 15 | # Don't drop duplicate NtWriteFile messages 16 | if msg.has_key("function"): 17 | if msg["function"] == "NtWriteFile": 18 | saveMsg = False 19 | 20 | if msg.has_key("pcapData"): 21 | return False 22 | 23 | # Check if this message is a duplicate 24 | # In the future, we might want to log all msgs regardless 25 | if saveMsg: 26 | msgStr = str(msg) 27 | if self.msgs.has_key(msgStr): 28 | #print "Dupe" 29 | return True 30 | else: 31 | # save it for later checks 32 | self.msgs[msgStr] = None 33 | 34 | if self.IsItemWhitelisted(msg): 35 | # We want to silently pass through dropped files 36 | if msg["function"] == "NtWriteFile": 37 | msg['Filter'] = True 38 | return False 39 | 40 | #print "Whitelist" 41 | return True 42 | 43 | return False 44 | 45 | def IsItemWhitelisted(self, msg): 46 | for rule in self.rules: 47 | found = True 48 | for key in rule: 49 | if not msg.has_key(key): 50 | found = False 51 | break 52 | 53 | type, ruleVal = rule[key] 54 | 55 | if type == 0: 56 | # str equal type 57 | if msg[key] != ruleVal: 58 | found = False 59 | break 60 | 61 | elif type == 1: 62 | # str find type 63 | if msg[key].find(ruleVal) < 0: 64 | found = False 65 | break 66 | 67 | elif type == 2: 68 | # re type 69 | match = re.search(ruleVal,msg[key]) 70 | if not match: 71 | found = False 72 | break 73 | 74 | # if every key had a match 75 | if found: 76 | return True 77 | 78 | return False 79 | 80 | def LoadRules(self): 81 | print "Loading rules.." 82 | lines = open("fluxFilter.txt","rb").readlines() 83 | for line in lines: 84 | line = line.rstrip() 85 | if len(line) == 0: 86 | continue 87 | if line[:1] == "#": 88 | continue 89 | try: 90 | rule = json.loads(line, object_hook=self.ascii_encode_dict) 91 | self.rules.append(rule) 92 | except: 93 | traceback.print_exc() 94 | print "Loaded %d rules" % len(self.rules) 95 | 96 | def ascii_encode_dict(self, data): 97 | ascii_encode = lambda x: x.encode('ascii') if isinstance(x, unicode) else x 98 | return dict(map(ascii_encode, pair) for pair in data.items()) 99 | -------------------------------------------------------------------------------- /python/flux/fluxFilter.txt: -------------------------------------------------------------------------------- 1 | # Files 2 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\lsass"]} 3 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\wkssvc"]} 4 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\MsFteWds"]} 5 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\srvsvc"]} 6 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\ngen_service.log"]} 7 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\Content.IE5\\"]} 8 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\"]} 9 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\DOMStore\\"]} 10 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\LocalLow\\Microsoft\\Internet Explorer\\Services\\logo_{"]} 11 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\Recovery"]} 12 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\LocalLow\\Microsoft\\CryptnetUrlCache\\"]} 13 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Roaming\\Macromedia\\Flash Player\\"]} 14 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Roaming\\Adobe\\Flash Player\\AssetCache\\"]} 15 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Roaming\\Microsoft\\Internet Explorer\\UserData\\"]} 16 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Windows\\WER\\ReportArchive\\NonCritical"]} 17 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Windows\\System32\\wbem\\Performance\\"]} 18 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\CustomDestinations\\"]} 19 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\IECompatData\\iecompatdata.xml"]} 20 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\imagestore\\"]} 21 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\DomainSuggestions\\"]} 22 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Windows\\AppCache\\"]} 23 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Temp\\wmsetup.log"]} 24 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Internet Explorer\\VersionManager\\"]} 25 | {"function" : [0,"NtWriteFile"], "FileName" : [1, "\\Users\\max\\AppData\\Local\\Microsoft\\Windows Media\\12.0\\WMSDKNS"]} 26 | {"function" : [0,"NtWriteFile"], "FileName" : [2, "\\\\Users\\\\max\\\\AppData\\\\Local\\\\Temp\\\\[A-Za-z0-9]{4,7}\\.tmp"]} 27 | {"function" : [0,"NtWriteFile"], "FileName" : [2, "\\\\Users\\\\max\\\\AppData\\\\Local\\\\Temp\\\\~[A-Z0-9]{18}\\.TMP"]} 28 | {"function" : [0,"NtWriteFile"], "FileName" : [2, "\\\\Users\\\\max\\\\AppData\\\\Local\\\\Temp\\\\Cab[A-Z0-9]{3,6}\\\\.{5,20}\\.inf"]} 29 | 30 | # Registry Keys 31 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\WBEM"]} 32 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\REGISTRY\\USER\\.DEFAULT\\Software\\Classes\\Local Settings\\MuiCache"]} 33 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\Internet Explorer"]} 34 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\ActiveMovie\\devenum\\"]} 35 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"]} 36 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\Direct3D\\"]} 37 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\5.0\\Cache"]} 38 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "_CLASSES\\Local Settings\\MuiCache\\"]} 39 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Direct"]} 40 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\REGISTRY\\MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Direct"]} 41 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\EUDC"]} 42 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Macromedia\\FlashPlayer"]} 43 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\MediaPlayer\\"]} 44 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\MPEG2Demultiplexer"]} 45 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\MediaPlayer\\"]} 46 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "\\Software\\Microsoft\\Windows Media\\WMSDK\\Namespace"]} 47 | {"function" : [0,"NtSetValueKey"], "KeyPath" : [1, "_CLASSES\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache"]} 48 | 49 | # Process Creation 50 | {"function" : [0,"NtCreateUserProcess"], "CommandLine" : [0, "\"C:\\Program Files\\Internet Explorer\\iexplore.exe\""]} 51 | {"function" : [0,"NtCreateUserProcess"], "CommandLine" : [1, "\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" SCODEF"]} 52 | {"function" : [0,"NtCreateUserProcess"], "ImagePathName" : [0, "C:\\Program Files\\Windows Media Player\\wmplayer.exe"]} 53 | {"function" : [0,"NtCreateUserProcess"], "ImagePathName" : [0, "C:\\Program Files (x86)\\Windows Media Player\\wmplayer.exe"]} 54 | {"process" : [0, "C:\\Program Files\\Windows Media Player\\wmplayer.exe"]} 55 | {"process" : [0, "C:\\Program Files\\Windows Media Player\\setup_wm.exe"]} 56 | {"process" : [0, "C:\\Program Files (x86)\\Windows Media Player\\wmplayer.exe"]} 57 | {"process" : [0, "C:\\Program Files (x86)\\Windows Media Player\\setup_wm.exe"]} 58 | {"function" : [0,"NtCreateUserProcess"], "CommandLine" : [1, "Microsoft\\Windows\\Customer Experience Improvement Program\\Uploader"]} 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /python/flux/pipeServer.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import threading 3 | import msgpack 4 | import json 5 | import struct 6 | import ctypes 7 | import sys 8 | import importlib 9 | import traceback 10 | import time 11 | import pika 12 | import signal 13 | 14 | from uuid import uuid4 15 | 16 | from ctypes import windll 17 | from ctypes import create_string_buffer, c_uint, byref, sizeof, Structure 18 | DWORD = ctypes.c_uint32 19 | LPVOID = ctypes.c_void_p 20 | BOOL = ctypes.c_int 21 | LPSTR = ctypes.c_char_p 22 | LPWSTR = ctypes.c_wchar_p 23 | GENERIC_READ = 0x80000000 24 | GENERIC_WRITE = 0x40000000 25 | CREATE_ALWAYS = 2 26 | FILE_ATTRIBUTE_NORMAL = 0x00000080 27 | PIPE_ACCESS_INBOUND = 0x1 28 | PIPE_ACCESS_OUTBOUND = 0x2 29 | PIPE_ACCESS_DUPLEX = 0x3 30 | PIPE_TYPE_MESSAGE = 0x4 31 | PIPE_REJECT_REMOTE_CLIENTS = 0x8 32 | PIPE_READMODE_MESSAGE = 0x2 33 | PIPE_WAIT = 0 34 | PIPE_UNLIMITED_INSTANCES = 0xff 35 | NMPWAIT_USE_DEFAULT_WAIT = 0 36 | INVALID_HANDLE_VALUE = -1 37 | ERROR_BROKEN_PIPE = 109 38 | ERROR_MORE_DATA = 234 39 | 40 | BUFSIZE = 0x500000 41 | 42 | resultHost = "" 43 | port = 0 44 | uuid = "" 45 | 46 | filters = {} 47 | 48 | ''' ACL Stuff ''' 49 | # typedef struct _SECURITY_ATTRIBUTES { 50 | # DWORD nLength; 51 | # LPVOID lpSecurityDescriptor; 52 | # BOOL bInheritHandle; 53 | # } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; 54 | class SECURITY_ATTRIBUTES(Structure): 55 | _fields_ = [ 56 | ('nLength', DWORD), 57 | ('lpSecurityDescriptor', LPVOID), 58 | ('bInheritHandle', BOOL), 59 | ] 60 | 61 | class TRUSTEE(Structure): 62 | _fields_ = [ 63 | ('pMultipleTrustee', LPVOID), 64 | ('MultipleTrusteeOperation', DWORD), 65 | ('TrusteeForm', DWORD), 66 | ('TrusteeType', DWORD), 67 | ('SID', LPVOID), 68 | ] 69 | # typedef struct _EXPLICIT_ACCESS_W 70 | # { 71 | # DWORD grfAccessPermissions; 72 | # ACCESS_MODE grfAccessMode; 73 | # DWORD grfInheritance; 74 | # TRUSTEE_W Trustee; 75 | # } EXPLICIT_ACCESS_W, *PEXPLICIT_ACCESS_W, EXPLICIT_ACCESSW, *PEXPLICIT_ACCESSW; 76 | class EXPLICIT_ACCESS(Structure): 77 | _fields_ = [ 78 | ('grfAccessPermissions', DWORD), 79 | ('grfAccessMode', DWORD), 80 | ('grfInheritance', DWORD), 81 | ('Trustee', TRUSTEE), 82 | ] 83 | 84 | def Everyone_SecurityAttributes(): 85 | ''' 86 | This function creates the security attributes needed to create an object with READ/WRITE permissions to the everyone group 87 | ''' 88 | sa = SECURITY_ATTRIBUTES() 89 | ACL = create_string_buffer(28) 90 | SD = create_string_buffer(40) #size is 20 for x86 91 | ACL.raw = "\x02\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00" 92 | 93 | if 8 == struct.calcsize("P"): 94 | # x64 95 | addr = struct.pack("q", ctypes.addressof(ACL)) 96 | SD.raw = "\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + addr 97 | sa.nLength = 0x18 98 | else: 99 | # x86 100 | addr = struct.pack("l", ctypes.addressof(ACL)) 101 | SD.raw = "\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + addr 102 | sa.nLength = 0xC #0xC for x86 103 | 104 | sa.lpSecurityDescriptor = ctypes.cast(SD, LPVOID) 105 | sa.bInheritHandle = False 106 | 107 | lpSecurityAttributes = ctypes.pointer(sa) 108 | return lpSecurityAttributes 109 | 110 | ''' 111 | _CreateFileA = windll.kernel32.CreateFileA 112 | _CreateFileA.argtypes = [LPSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, DWORD] 113 | _CreateFileA.restype = DWORD 114 | 115 | lpSecurityAttributes = ctypes.pointer(sa) 116 | 117 | hFile = _CreateFileA("testfile", GENERIC_READ | GENERIC_WRITE, 0, lpSecurityAttributes, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0) 118 | print hFile 119 | ''' 120 | 121 | 122 | class PipeReceiver(threading.Thread): 123 | def __init__(self, pipeHandle): 124 | threading.Thread.__init__(self) 125 | self.daemon = True 126 | self.pipeHandle = pipeHandle 127 | 128 | def MsgIn(self, data): 129 | #print "Recieved msg" 130 | 131 | try: 132 | msg = msgpack.unpackb(data) 133 | except: 134 | traceback.print_exc() 135 | return 136 | 137 | if not filters.has_key(msg['plugin']): 138 | try: 139 | plugFilter = importlib.import_module(msg['plugin'] + "Filter") 140 | filters[msg['plugin']] = plugFilter.Filter() 141 | except ImportError: 142 | print "Error importing filter for %s" % msg['plugin'] 143 | filters[msg['plugin']] = None 144 | 145 | 146 | if filters[msg['plugin']] != None: 147 | result = filters[msg['plugin']].filter(msg) 148 | # print "FilterResult",result 149 | if result: 150 | # This msg has been filtered 151 | return 152 | 153 | ''' 154 | try: 155 | print json.dumps(msg, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ': ')) 156 | except: 157 | print msg 158 | ''' 159 | 160 | # Add UUID to message 161 | msg['uuid'] = uuid 162 | 163 | # Add datetime, milliseconds since epoch for elasticsearch 164 | # msg['timestamp'] = str(int(time.time()*1000)) 165 | 166 | data = msgpack.packb(msg) 167 | 168 | try: 169 | channel.basic_publish(exchange='',routing_key='maxwell', body=data) 170 | except: 171 | traceback.print_exc() 172 | # Results server down? 173 | pass 174 | 175 | def run(self): 176 | data = "" 177 | buf = create_string_buffer(BUFSIZE) 178 | bytes_read = c_uint() 179 | pid = c_uint() 180 | print "New receiver thread" 181 | while True: 182 | retVal = windll.kernel32.ReadFile(self.pipeHandle, 183 | byref(buf), sizeof(buf), 184 | byref(bytes_read), None) 185 | if retVal: 186 | data += buf.raw[:bytes_read.value] 187 | self.MsgIn(data) 188 | data = "" 189 | elif windll.kernel32.GetLastError() == ERROR_MORE_DATA: 190 | data += buf.raw[:bytes_read.value] 191 | elif windll.kernel32.GetLastError() == ERROR_BROKEN_PIPE: 192 | break 193 | else: 194 | print "Error reading from pipe",windll.kernel32.GetLastError() 195 | break 196 | 197 | class GracefulExit(): 198 | def __init__(self): 199 | self.exit_now = False 200 | signal.signal(signal.SIGINT, self.exit_gracefully) 201 | signal.signal(signal.SIGTERM, self.exit_gracefully) 202 | 203 | def exit_gracefully(self,signum, frame): 204 | print "signal" 205 | self.exit_now = True 206 | 207 | def SendDone(): 208 | msg['uuid'] = uuid 209 | msg['plugin'] = 'flux' 210 | msg['status'] = 'finished' 211 | data = msgpack.packb(msg) 212 | try: 213 | channel.basic_publish(exchange='',routing_key='maxwell', body=data) 214 | except: 215 | traceback.print_exc() 216 | # Results server down? 217 | pass 218 | 219 | class PipeServer(threading.Thread): 220 | 221 | def __init__(self, pipeName): 222 | threading.Thread.__init__(self) 223 | self.pipeName = pipeName 224 | self.active = True 225 | self.daemon = True 226 | 227 | def run(self): 228 | print "Listening on %s" % self.pipeName 229 | while self.active: 230 | pipeHandle = windll.kernel32.CreateNamedPipeA( 231 | self.pipeName, PIPE_ACCESS_INBOUND, 232 | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 233 | PIPE_UNLIMITED_INSTANCES, 0, BUFSIZE, 0, 0) #Everyone_SecurityAttributes()) 234 | 235 | if INVALID_HANDLE_VALUE == pipeHandle: 236 | print "Error creating pipe",windll.kernel32.GetLastError() 237 | continue 238 | 239 | if windll.kernel32.ConnectNamedPipe(pipeHandle, None) or windll.kernel32.GetLastError() == ERROR_PIPE_CONNECTED: 240 | handler = PipeReceiver(pipeHandle) 241 | handler.daemon = True 242 | handler.start() 243 | else: 244 | windll.kernel32.CloseHandle(pipeHandle) 245 | 246 | def stop(self): 247 | active = False; 248 | 249 | if __name__ == "__main__": 250 | uuid = sys.argv[1] 251 | rmqServer = sys.argv[2] 252 | 253 | for x in xrange(0,20): 254 | # Ephemeral port re-use of snapshots leads to this bizzare behavior 255 | try: 256 | connection = pika.BlockingConnection(pika.ConnectionParameters(host=rmqServer)) 257 | break 258 | except pika.exceptions.ConnectionClosed: 259 | if x == 19: 260 | raise 261 | 262 | channel = connection.channel() 263 | channel.queue_declare(queue='maxwell') 264 | 265 | pipeServer = PipeServer(r'\\.\PIPE\Maxwell') 266 | pipeServer.start() 267 | 268 | exitSignal = GracefulExit() 269 | while not exitSignal.exit_now: 270 | try: 271 | time.sleep(1) 272 | except: 273 | pass 274 | 275 | print "Shutting down" 276 | SendDone() 277 | connection.close() 278 | print "pipeServer done" 279 | 280 | -------------------------------------------------------------------------------- /python/postProcess.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import re 5 | import subprocess 6 | import yara 7 | import zlib 8 | import pylzma 9 | import os 10 | import shutil 11 | import traceback 12 | 13 | def ParseFile(data, name): 14 | 15 | rule = yara.compile("rules.yr") 16 | matches = rule.match(data=data) 17 | if len(matches) > 0: 18 | for hit in matches['main']: 19 | print name,hit['rule'] 20 | 21 | if data[:3] == 'CWS': 22 | #print "Zlib Compressed" 23 | try: 24 | new = 'FWS' + data[3:8] + zlib.decompress(data[8:]) 25 | except: 26 | print "zlib error " 27 | return 28 | ParseFile(new, name) 29 | 30 | elif data[:3] == 'ZWS': 31 | #print "lzma compressed" 32 | try: 33 | new = 'FWS' + data[3:8] + pylzma.decompress_compat(data[12:]) 34 | except: 35 | print "pylzma error " 36 | return 37 | ParseFile(new, name) 38 | 39 | elif data[:3] == "GET": 40 | 41 | # Angler 42 | search = re.search(r'(GET /.{1,25}/index.php\?PHPSESSID=.{1,6}&action=.{12}.{1,512})\r\n\r\n',data,re.S) 43 | if search: 44 | print "Angler GET",name 45 | print search.group(1) 46 | 47 | search = re.search(r'(GET .{0,100}/.{1,25}/viewtopic.php\?t=.{1,6}&f=.{12}.{1,512})\r\n\r\n',data,re.S) 48 | if search: 49 | print "Angler GET",name 50 | print search.group(1) 51 | 52 | search = re.search(r'(GET .{0,100}/.{1,25}/viewforum.php\?f=.{1,6}&sid=.{12}.{1,512})\r\n\r\n',data,re.S) 53 | if search: 54 | print "Angler GET",name 55 | print search.group(1) 56 | 57 | search = re.search(r'(GET .{0,100}/.{1,25}/search.php\?keywords=.{1,6}&fid0=.{12}.{1,512})\r\n\r\n',data,re.S) 58 | if search: 59 | print "Angler GET",name 60 | print search.group(1) 61 | 62 | search = re.search(r'(GET .{0,100}/topic/[0-9]{4,12}(-[a-z]{3,20}){3,10}/ HTTP.{1,512})\r\n\r\n',data,re.S) 63 | if search: 64 | print "Angler GET",name 65 | print search.group(1) 66 | 67 | # RIG 68 | search = re.search(r'(GET .{0,100}/\?[a-zA-Z0-9]{15}=[a-zA-Z0-9_-]{100,200} HTTP.{1,512})\r\n\r\n',data,re.S) 69 | if search: 70 | print "RIG GET",name 71 | print search.group(1) 72 | 73 | # Eltest Gate 74 | search = re.search(r'(GET /[a-z0-9\-]{80,150}/[a-z]{1,20}\.html HTTP.{1,512})\r\n\r\n',data,re.S) 75 | if search: 76 | print "EItest GET",name 77 | print search.group(1) 78 | 79 | # Magnitude 80 | search = re.search(r'(GET .{0,100}/\?[a-z0-9]{38} HTTP.{1,512})\r\n\r\n',data,re.S) 81 | if search: 82 | print "Magnitude GET",name 83 | print search.group(1) 84 | 85 | # Neutrino 86 | search = re.search(r'(GET.{1,512}Media Center PC 6\.0; rv.{1,100})\r\n\r\n',data,re.S) 87 | if search: 88 | print "Neutrino GET",name 89 | print search.group(1) 90 | else: 91 | # AfraidGate 92 | search = re.search(r'^(document\.write\(.{200,400}i\'\+\'frame.{5,20}\))',data,re.S) 93 | if search: 94 | print "AfraidGate",name 95 | print search.group(1) 96 | 97 | # Pseudo Darkleech 98 | search = re.search(r'().{3000,10000}',data,re.S) 99 | if search: 100 | print "Psuedo Darkleech",name 101 | print search.group(1) 102 | 103 | def decode_chunked(data): 104 | offset = 0 105 | encdata = '' 106 | newdata = '' 107 | offset = data.index("\r\n\r\n") + 4 # get the offset 108 | # of the data payload. you can also parse content-length header as well. 109 | encdata =data[offset:] 110 | try: 111 | while (encdata != ''): 112 | off = int(encdata[:encdata.index("\r\n")],16) 113 | if off == 0: 114 | break 115 | encdata = encdata[encdata.index("\r\n") + 2:] 116 | newdata = "%s%s" % (newdata, encdata[:off]) 117 | encdata = encdata[off+2:] 118 | 119 | except: 120 | print "Exception! decode_chunk" 121 | return "" 122 | return newdata 123 | 124 | def ProcessPCAP(file, path): 125 | try: 126 | pcapFile = os.path.join(path, file) 127 | if os.path.isfile(pcapFile) == False: 128 | return 129 | 130 | flowDir = os.path.join(path, "flow") 131 | if os.path.exists(flowDir) == False: 132 | os.mkdir(flowDir) 133 | 134 | subprocess.call(['tcpflow','-r',pcapFile,'-o',flowDir, '-a', '-d', '0']) 135 | 136 | for root, dirs, files in os.walk(flowDir): 137 | for name in files: 138 | path = os.path.join(root, name) 139 | try: 140 | file_data = open(path, "rb").read() 141 | ParseFile(file_data,name) 142 | except: 143 | print "Error " + path 144 | traceback.print_exc() 145 | 146 | 147 | shutil.rmtree(flowDir) 148 | except: 149 | traceback.print_exc() 150 | print "Exception ProcessPCAP" 151 | 152 | if __name__ == "__main__": 153 | outDir = sys.argv[1] 154 | 155 | ProcessPCAP("traffic.pcap", outDir) 156 | 157 | for root, dirs, files in os.walk(outDir): 158 | for name in files: 159 | path = os.path.join(root, name) 160 | if name == "traffic.pcap": 161 | pass 162 | else: 163 | try: 164 | f = open(path, "rb") 165 | file_data = f.read() 166 | ParseFile(file_data,name) 167 | f.close() 168 | if path[-4:] == '.exe': 169 | os.rename(path,path+'.bad') 170 | except: 171 | print traceback.print_exc() 172 | print "Error accessing " + path 173 | 174 | -------------------------------------------------------------------------------- /python/resultsServer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import SocketServer 4 | import struct 5 | import msgpack 6 | import getpass 7 | import traceback 8 | import smtplib 9 | import json 10 | import subprocess 11 | import os 12 | import shutil 13 | import time 14 | import sys 15 | from elasticsearch import Elasticsearch 16 | import datetime 17 | import pika 18 | 19 | from uuid import uuid4 20 | 21 | results = {} 22 | rmqServer = '127.0.0.1' 23 | ESHost = "http://127.0.0.1:9200" 24 | 25 | def HandleDroppedFile(msg): 26 | if not msg.has_key('FileData') or not msg.has_key('FileName') or not msg.has_key('uuid') : 27 | print "Invalid parameter" 28 | return 29 | 30 | i = msg['FileName'].rfind("\\") 31 | 32 | path = msg['FileName'][1:i] 33 | file = msg['FileName'][i+1:] 34 | 35 | path = path.replace("\\", os.path.sep) 36 | path = os.path.join("extracted", msg['uuid'], path) 37 | 38 | if not os.path.exists(path): 39 | os.makedirs(path) 40 | 41 | try: 42 | open(os.path.join(path,file), "ab").write(msg['FileData']) 43 | except: 44 | traceback.print_exc() 45 | 46 | del msg['FileData'] 47 | 48 | def HandlePCAP(msg): 49 | if not msg.has_key('pcapData') or not msg.has_key('uuid') : 50 | print "Invalid parameter" 51 | return 52 | 53 | path = "" 54 | file = "traffic.pcap" 55 | 56 | path = path.replace("\\", os.path.sep) 57 | path = os.path.join("extracted", msg['uuid'], path) 58 | 59 | if not os.path.exists(path): 60 | os.makedirs(path) 61 | 62 | open(os.path.join(path,file), "wb").write(msg['pcapData']) 63 | 64 | def PostProcess(msgs): 65 | post_txt = "" 66 | thisUuid = msgs[0]['uuid'] 67 | try: 68 | jsonOutput = json.dumps(msgs, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ': ')) 69 | except: 70 | traceback.print_exc() 71 | jsonOutput = str(msgs) 72 | 73 | try: 74 | outDir = os.path.join("extracted", thisUuid) 75 | if not os.path.exists(outDir): 76 | os.makedirs(outDir) 77 | 78 | postFile = os.path.join("extracted", thisUuid, "post.txt") 79 | f = open(postFile,"wb") 80 | 81 | subprocess.call([sys.executable, 'postProcess.py', outDir],stdout=f) 82 | f.close() 83 | post_txt = open(postFile,"rb").read() 84 | 85 | outFile = open('results' + os.sep + thisUuid, 'wb') 86 | outFile.write(post_txt + "\r\n" + jsonOutput) 87 | outFile.close() 88 | except: 89 | print "Post Processing Error" 90 | post_txt = "Post Processing Error" 91 | post_txt += "\r\n" 92 | post_txt += traceback.format_exc() 93 | 94 | return post_txt 95 | 96 | def IndexMsg(msg): 97 | msg['timestamp'] = datetime.datetime.utcnow() 98 | try: 99 | es = Elasticsearch(ESHost) 100 | result = es.index(index="m_index",doc_type="maxwell", body=msg) 101 | except: 102 | traceback.print_exc() 103 | del msg['timestamp'] 104 | 105 | def IndexPostResult(msg, result_txt): 106 | newMsg = {} 107 | newMsg['uuid'] = msg['uuid'] 108 | newMsg['plugin'] = msg['plugin'] 109 | newMsg['postProcess'] = result_txt 110 | IndexMsg(newMsg) 111 | 112 | def callback(ch, method, properties, body): 113 | 114 | try: 115 | msg = msgpack.unpackb(body) 116 | except: 117 | traceback.print_exc() 118 | return 119 | 120 | if msg.has_key('FileData'): 121 | # ToDo filter these messages after reception 122 | HandleDroppedFile(msg) 123 | if msg.has_key('Filter') and msg['Filter'] == True: 124 | return 125 | 126 | if msg.has_key('pcapData'): 127 | #print "Received pcap" 128 | HandlePCAP(msg) 129 | return 130 | 131 | print msg 132 | 133 | IndexMsg(msg) 134 | 135 | # ToDo ElasticSearch 136 | if not results.has_key(msg['uuid']): 137 | results[msg['uuid']] = [] 138 | 139 | results[msg['uuid']].append(msg) 140 | 141 | if msg.has_key('status'): 142 | if msg['status'] == "finished" and msg['plugin'] == "flux": 143 | if len(results[msg['uuid']]) > 2: 144 | # Alert 145 | msg['status'] = "malicious" 146 | IndexMsg(msg) 147 | result_txt = PostProcess(results[msg['uuid']]) 148 | IndexPostResult(msg, result_txt) 149 | else: 150 | # Clean any dropped files 151 | try: 152 | shutil.rmtree(os.path.join("extracted", msg['uuid'])) 153 | except: 154 | traceback.print_exc() 155 | 156 | del results[msg['uuid']] 157 | 158 | if __name__ == "__main__": 159 | 160 | connection = pika.BlockingConnection(pika.ConnectionParameters( 161 | host=rmqServer)) 162 | channel = connection.channel() 163 | 164 | channel.queue_declare(queue='maxwell') 165 | 166 | channel.basic_consume(callback, 167 | queue='maxwell', 168 | no_ack=True) 169 | 170 | print(' [*] Waiting for messages. To exit press CTRL+C') 171 | channel.start_consuming() 172 | -------------------------------------------------------------------------------- /python/rules.yr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/Maxwell/db4d8b189ff2f39f7c5235bb6a1a9974f7532a8c/python/rules.yr --------------------------------------------------------------------------------