├── README.md ├── .gitmodules ├── .gitignore ├── hijacken ├── hijacken.vcxproj.filters ├── Utils.h ├── ProcessScan.h ├── Utils.cpp ├── Hijacken.cpp ├── PEParser.h ├── ProcessScan.cpp ├── ImageScan.h ├── Commands.h ├── hijacken.vcxproj ├── System.h ├── PEParser.cpp ├── Commands.cpp ├── ImageScan.cpp └── System.cpp └── hijacken.sln /README.md: -------------------------------------------------------------------------------- 1 | # HiJacken Toolset 2 | Dll hijacking toolset 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "hijacken/ntlib"] 2 | path = hijacken/ntlib 3 | url = https://github.com/JKornev/NTlib 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.sdf 2 | /*.suo 3 | /Debug 4 | /x64 5 | /hijacken/Debug 6 | /hijacken/x64 7 | /hijacken/*.user 8 | /Release 9 | /hijacken/Release 10 | -------------------------------------------------------------------------------- /hijacken/hijacken.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /hijacken.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hijacken", "hijacken\hijacken.vcxproj", "{D37B1B81-F34C-4977-B350-6242F2F46468}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Debug|Win32.Build.0 = Debug|Win32 18 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Debug|x64.ActiveCfg = Debug|x64 19 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Debug|x64.Build.0 = Debug|x64 20 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Release|Win32.ActiveCfg = Release|Win32 21 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Release|Win32.Build.0 = Release|Win32 22 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Release|x64.ActiveCfg = Release|x64 23 | {D37B1B81-F34C-4977-B350-6242F2F46468}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /hijacken/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Utils 9 | { 10 | // ================= 11 | 12 | class Exception 13 | { 14 | private: 15 | std::wstring _message; 16 | unsigned int _code; 17 | 18 | public: 19 | Exception(unsigned int code, const wchar_t* format, ...); 20 | Exception(const wchar_t* format, ...); 21 | 22 | std::wstring GetMessage(); 23 | unsigned int GetCode(); 24 | }; 25 | 26 | static const unsigned int NoExceptionCode = -1; 27 | 28 | // ================= 29 | 30 | class Explanation : public Utils::Exception 31 | { 32 | public: 33 | Explanation(const wchar_t* message) : Utils::Exception(message) {} 34 | }; 35 | 36 | // ================= 37 | 38 | class Arguments 39 | { 40 | private: 41 | std::vector _arguments; 42 | unsigned int _index; 43 | 44 | public: 45 | 46 | Arguments(int argc, wchar_t* argv[], int start = 1); 47 | 48 | size_t GetAmount(); 49 | 50 | bool Probe(std::wstring& arg); 51 | bool SwitchToNext(); 52 | bool GetNext(std::wstring& arg); 53 | 54 | bool IsEnded(); 55 | }; 56 | 57 | // ================= 58 | 59 | class SeparatedStrings : public std::vector 60 | { 61 | public: 62 | SeparatedStrings(const std::wstring& str, wchar_t seporator); 63 | }; 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /hijacken/ProcessScan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "System.h" 4 | 5 | namespace Engine 6 | { 7 | class ProcessScanEngine 8 | { 9 | public: 10 | 11 | enum class DetectionDirType 12 | { 13 | Executable, 14 | Current, 15 | Users, 16 | Environment, 17 | LoadedModule 18 | }; 19 | 20 | enum class DetectionFileType 21 | { 22 | Executable, 23 | LoadedModule 24 | }; 25 | 26 | void Scan(DWORD pid, System::TokenAccessChecker& access); 27 | 28 | virtual void NotifyWritableDirectory(DetectionDirType detection, std::wstring& dirPath) = 0; 29 | virtual void NotifyWritableFile(DetectionFileType detection, std::wstring& filePath) = 0; 30 | 31 | static const wchar_t* ConvertDirDetectionToString(DetectionDirType detection); 32 | static const wchar_t* ConvertFileDetectionToString(DetectionFileType detection); 33 | 34 | private: 35 | 36 | void ScanImage(System::TokenAccessChecker& access, System::ProcessInformation& info); 37 | void ScanCurrentDirectory(System::TokenAccessChecker& access, System::ProcessEnvironmentBlock& peb); 38 | void ScanEnvironmentPaths(System::TokenAccessChecker& access, System::ProcessEnvironmentBlock& peb); 39 | void ScanModules(System::TokenAccessChecker& access, System::ProcessInformation& info); 40 | 41 | bool IsFileWritable(std::wstring path, System::TokenAccessChecker& access); 42 | bool IsDirWritable(std::wstring path, System::TokenAccessChecker& access); 43 | }; 44 | }; -------------------------------------------------------------------------------- /hijacken/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include 3 | 4 | namespace Utils 5 | { 6 | 7 | // ================= 8 | 9 | Exception::Exception(unsigned int code, const wchar_t* format, ...) : 10 | _code(code) 11 | { 12 | wchar_t buffer[256]; 13 | 14 | va_list args; 15 | va_start(args, format); 16 | _vsnwprintf_s(buffer, _countof(buffer), _TRUNCATE, format, args); 17 | va_end(args); 18 | 19 | _message = buffer; 20 | } 21 | 22 | Exception::Exception(const wchar_t* format, ...) : 23 | _code(NoExceptionCode) 24 | { 25 | wchar_t buffer[256]; 26 | 27 | va_list args; 28 | va_start(args, format); 29 | _vsnwprintf_s(buffer, _countof(buffer), _TRUNCATE, format, args); 30 | va_end(args); 31 | 32 | _message = buffer; 33 | } 34 | 35 | std::wstring Exception::GetMessage() 36 | { 37 | return _message; 38 | } 39 | 40 | unsigned int Exception::GetCode() 41 | { 42 | return _code; 43 | } 44 | 45 | // ================= 46 | 47 | Arguments::Arguments(int argc, wchar_t* argv[], int start) : 48 | _index(0) 49 | { 50 | for (int i = start; i < argc; i++) 51 | _arguments.push_back(argv[i]); 52 | } 53 | 54 | size_t Arguments::GetAmount() 55 | { 56 | return _arguments.size(); 57 | } 58 | 59 | bool Arguments::Probe(std::wstring& arg) 60 | { 61 | if (_index >= _arguments.size()) 62 | return false; 63 | 64 | arg = _arguments[_index]; 65 | return true; 66 | } 67 | 68 | bool Arguments::SwitchToNext() 69 | { 70 | if (_index >= _arguments.size()) 71 | return false; 72 | 73 | _index++; 74 | return true; 75 | } 76 | 77 | bool Arguments::GetNext(std::wstring& arg) 78 | { 79 | if (_index >= _arguments.size()) 80 | return false; 81 | 82 | arg = _arguments[_index++]; 83 | return true; 84 | } 85 | 86 | bool Arguments::IsEnded() 87 | { 88 | return (_index >= _arguments.size()); 89 | } 90 | 91 | // ================= 92 | 93 | SeparatedStrings::SeparatedStrings(const std::wstring& str, wchar_t seporator) 94 | { 95 | size_t startOffset = 0; 96 | auto endOffset = str.find(seporator); 97 | 98 | if (endOffset == std::wstring::npos) 99 | { 100 | push_back(str); 101 | return; 102 | } 103 | 104 | while (endOffset != std::wstring::npos) 105 | { 106 | if (startOffset != endOffset) 107 | push_back(std::wstring(&str[startOffset], &str[endOffset])); 108 | startOffset = endOffset + 1; 109 | endOffset = str.find(seporator, startOffset); 110 | } 111 | 112 | if (startOffset < str.size()) 113 | push_back(std::wstring(str.c_str() + startOffset)); 114 | } 115 | 116 | }; -------------------------------------------------------------------------------- /hijacken/Hijacken.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include "Commands.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace 9 | { 10 | void SwitchConsoleToUTF16Mode() 11 | { 12 | _setmode(_fileno(stdout), _O_U16TEXT); 13 | _setmode(_fileno(stderr), _O_U16TEXT); 14 | } 15 | 16 | bool PrintUsageIfNeeded(Utils::Arguments& args) 17 | { 18 | std::wstring command; 19 | 20 | if (!args.Probe(command)) 21 | return false; 22 | 23 | if (command != L"/help" && command != L"/?") 24 | return false; 25 | 26 | //TODO: usage 27 | 28 | return true; 29 | } 30 | 31 | Commands::CommandPtr ChooseCommand(Utils::Arguments& args) 32 | { 33 | std::wstring command, sub; 34 | Commands::CommandPtr ptr; 35 | 36 | if (!args.GetNext(command)) 37 | throw Utils::Explanation(L"Error, invalid usage. Please use 'hijacken /?'"); 38 | 39 | if (command == L"/scan") 40 | { 41 | if (!args.GetNext(sub)) 42 | throw Utils::Explanation(L"Error, /scan argument isn't presented"); 43 | 44 | if (sub == L"file") 45 | ptr = Commands::CommandPtr(new Commands::ScanFile()); 46 | else if (sub == L"directory") 47 | ptr = Commands::CommandPtr(new Commands::ScanDirectory()); 48 | else if (sub == L"process") 49 | ptr = Commands::CommandPtr(new Commands::ScanProcess()); 50 | else if (sub == L"processes") 51 | ptr = Commands::CommandPtr(new Commands::ScanProcesses()); 52 | else if (sub == L"autorun") 53 | ptr = Commands::CommandPtr(new Commands::ScanAutorun()); 54 | else if (sub == L"task") 55 | ptr = Commands::CommandPtr(new Commands::ScanTask()); 56 | else if (sub == L"tasks") 57 | ptr = Commands::CommandPtr(new Commands::ScanTasks()); 58 | else if (sub == L"service") 59 | ptr = Commands::CommandPtr(new Commands::ScanService()); 60 | else if (sub == L"services") 61 | ptr = Commands::CommandPtr(new Commands::ScanServices()); 62 | else if (sub == L"system") 63 | ptr = Commands::CommandPtr(new Commands::ScanSystem()); 64 | else 65 | throw Utils::Explanation(L"Error, invalid /scan argument"); 66 | } 67 | else if (command == L"/makedll") 68 | { 69 | ptr = Commands::CommandPtr(new Commands::MakeDll()); 70 | } 71 | else 72 | { 73 | throw Utils::Explanation(L"Error, invalid command. Please use 'hijacken /?'"); 74 | } 75 | 76 | return ptr; 77 | } 78 | } 79 | 80 | int wmain(int argc, wchar_t* argv[]) 81 | { 82 | SwitchConsoleToUTF16Mode(); 83 | 84 | try 85 | { 86 | Utils::Arguments arguments(argc, argv); 87 | 88 | if (!arguments.GetAmount()) 89 | throw Utils::Explanation(L"Welcome to Hijacken. Please use 'hijacken /?' to get a usage information"); 90 | 91 | if (PrintUsageIfNeeded(arguments)) 92 | return 0; 93 | 94 | auto command = ChooseCommand(arguments); 95 | command->LoadArgs(arguments); 96 | command->Perform(); 97 | } 98 | catch (Utils::Explanation& explanation) 99 | { 100 | std::wcerr << explanation.GetMessage() << std::endl; 101 | return explanation.GetCode(); 102 | } 103 | catch (Utils::Exception& exception) 104 | { 105 | std::wcerr << L"Unhandled exception, program has been terminated" << std::endl; 106 | std::wcerr << L" reason: " << exception.GetMessage() << std::endl; 107 | return exception.GetCode(); 108 | } 109 | catch (std::exception& exception) 110 | { 111 | std::wcerr << L"Unhandled STD exception, program has been terminated" << std::endl; 112 | std::wcerr << L" reason: " << exception.what() << std::endl; 113 | return Utils::NoExceptionCode; 114 | } 115 | catch (...) 116 | { 117 | std::wcerr << L"Unhandled exception, program has been terminated" << std::endl; 118 | std::wcerr << L" An internal error occured, there is no more information" << std::endl; 119 | return Utils::NoExceptionCode; 120 | } 121 | 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /hijacken/PEParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "System.h" 4 | #include 5 | #include 6 | 7 | namespace PEParser 8 | { 9 | 10 | // ================= 11 | 12 | typedef public std::vector ImportTable; 13 | 14 | // ================= 15 | 16 | class ResourceData; 17 | class ResourceDirectory; 18 | typedef std::vector ResourceDataSet; 19 | typedef std::vector ResourceDirectorySet; 20 | 21 | // ================= 22 | 23 | class ResourceEntry 24 | { 25 | public: 26 | 27 | enum class NamedResourceType { 28 | HasName, 29 | HasId, 30 | Unnamed 31 | }; 32 | 33 | ResourceEntry(); 34 | ResourceEntry(std::wstring& name); 35 | ResourceEntry(unsigned short id); 36 | 37 | bool operator==(const ResourceEntry& other) const; 38 | 39 | NamedResourceType GetType() const; 40 | 41 | std::wstring GetName() const; 42 | unsigned short GetID() const; 43 | 44 | private: 45 | NamedResourceType _type; 46 | std::wstring _name; 47 | unsigned short _id; 48 | }; 49 | 50 | // ================= 51 | 52 | class ResourceData : public ResourceEntry 53 | { 54 | private: 55 | unsigned long _offset; 56 | unsigned long _size; 57 | unsigned long _codepage; 58 | 59 | public: 60 | ResourceData(); 61 | ResourceData(std::wstring& name, unsigned long offset, System::ImageMapping& image); 62 | ResourceData(unsigned short id, unsigned long offset, System::ImageMapping& image); 63 | 64 | std::vector ReadData(System::ImageMapping& image); 65 | 66 | private: 67 | void LoadDataEntry(unsigned long offset, System::ImageMapping& image); 68 | }; 69 | 70 | // ================= 71 | 72 | class ResourceDirectory : public ResourceEntry 73 | { 74 | private: 75 | ResourceDirectorySet _dirs; 76 | ResourceDataSet _data; 77 | 78 | public: 79 | ResourceDirectory(); 80 | ResourceDirectory(std::wstring& name); 81 | ResourceDirectory(unsigned short id); 82 | 83 | void Push(ResourceDirectory& dir); 84 | void Push(ResourceData& data); 85 | 86 | const ResourceDirectorySet& GetDirs() const; 87 | const ResourceDataSet& GetData() const; 88 | }; 89 | 90 | 91 | // ================= 92 | 93 | class Resources 94 | { 95 | private: 96 | ResourceDirectory _root; 97 | 98 | public: 99 | Resources(); 100 | Resources(ResourceDirectory& root); 101 | 102 | const ResourceDirectory& GetRoot() const; 103 | }; 104 | 105 | // ================= 106 | 107 | class ResourceUtils 108 | { 109 | public: 110 | static const ResourceDirectory& FindDirectory(const ResourceDirectory& dir, const ResourceEntry& id); 111 | static const ResourceData& FindData(const ResourceDirectory& dir, const ResourceEntry& id); 112 | static const std::vector LoadFirstResource(const Resources& resources, System::ImageMapping& image, const ResourceEntry& id); 113 | }; 114 | 115 | // ================= 116 | 117 | class Image 118 | { 119 | protected: 120 | 121 | struct SectionRegion 122 | { 123 | DWORD rawOffset; 124 | DWORD rawSize; 125 | DWORD virtualOffset; 126 | DWORD virtualSize; 127 | }; 128 | 129 | System::ImageMapping& _mapping; 130 | std::vector _sections; 131 | 132 | System::Bitness _bitness; 133 | 134 | void* GetAddressByRVA(DWORD rva); 135 | std::string LoadStringByRVA(DWORD rva); 136 | std::wstring LoadWStringByRVA(DWORD rva); 137 | 138 | public: 139 | Image(System::ImageMapping& mapping); 140 | virtual ~Image() = default; 141 | 142 | System::Bitness GetBitness(); 143 | 144 | virtual ImportTable LoadImportTable() = 0; 145 | virtual ImportTable LoadDelayImportTable() = 0; 146 | virtual Resources LoadResources() = 0; 147 | }; 148 | 149 | typedef std::shared_ptr ImagePtr; 150 | 151 | // ================= 152 | 153 | class ImageFactory 154 | { 155 | private: 156 | static System::Bitness GetImageBitness(System::ImageMapping& mapping); 157 | 158 | public: 159 | ImagePtr GetImage(System::ImageMapping& mapping); 160 | }; 161 | 162 | // ================= 163 | 164 | template 165 | class ImageImpl : public Image 166 | { 167 | private: 168 | T* _header; 169 | 170 | public: 171 | ImageImpl(System::ImageMapping& mapping); 172 | virtual ~ImageImpl() = default; 173 | 174 | ImportTable LoadImportTable() override; 175 | ImportTable LoadDelayImportTable() override; 176 | 177 | Resources LoadResources() override; 178 | 179 | private: 180 | void LoadResourceDirectory(DWORD resourceBase, DWORD resourceDir, ResourceDirectory& resource); 181 | }; 182 | 183 | } 184 | -------------------------------------------------------------------------------- /hijacken/ProcessScan.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessScan.h" 2 | 3 | namespace Engine 4 | { 5 | 6 | void ProcessScanEngine::Scan(DWORD pid, System::TokenAccessChecker& access) 7 | { 8 | System::ProcessInformation info(pid); 9 | System::ProcessEnvironmentBlock peb(info); 10 | 11 | ScanImage(access, info); 12 | ScanCurrentDirectory(access, peb); 13 | ScanEnvironmentPaths(access, peb); 14 | ScanModules(access, info); 15 | } 16 | 17 | void ProcessScanEngine::ScanImage(System::TokenAccessChecker& access, System::ProcessInformation& info) 18 | { 19 | try 20 | { 21 | std::wstring imageDir; 22 | info.GetImageDirectory(imageDir); 23 | 24 | if (IsDirWritable(imageDir, access)) 25 | { 26 | NotifyWritableDirectory(DetectionDirType::Executable, imageDir); 27 | return; 28 | } 29 | 30 | std::wstring imageFile; 31 | info.GetImagePath(imageFile); 32 | 33 | if (IsFileWritable(imageFile, access)) 34 | { 35 | NotifyWritableFile(DetectionFileType::Executable, imageFile); 36 | return; 37 | } 38 | } 39 | catch (...) 40 | { 41 | } 42 | } 43 | 44 | void ProcessScanEngine::ScanCurrentDirectory(System::TokenAccessChecker& access, System::ProcessEnvironmentBlock& peb) 45 | { 46 | try 47 | { 48 | std::wstring currentDirPath; 49 | peb.GetCurrentDir(currentDirPath); 50 | 51 | if (IsDirWritable(currentDirPath, access)) 52 | { 53 | NotifyWritableDirectory(DetectionDirType::Current, currentDirPath); 54 | return; 55 | } 56 | } 57 | catch (...) 58 | { 59 | } 60 | } 61 | 62 | void ProcessScanEngine::ScanEnvironmentPaths(System::TokenAccessChecker& access, System::ProcessEnvironmentBlock& peb) 63 | { 64 | std::wstring pathSet; 65 | auto env = peb.GetProcessEnvironment(); 66 | 67 | if (!env->GetValue(L"Path", pathSet) && !env->GetValue(L"PATH", pathSet) && !env->GetValue(L"path", pathSet)) 68 | throw Utils::Exception(L"Can't obtain 'Path' environment variable"); 69 | 70 | Utils::SeparatedStrings paths(pathSet, L';'); 71 | 72 | for (auto& dir : paths) 73 | { 74 | try 75 | { 76 | if (IsDirWritable(dir, access)) 77 | NotifyWritableDirectory(DetectionDirType::Environment, dir); 78 | } 79 | catch (...) 80 | { 81 | } 82 | } 83 | } 84 | 85 | void ProcessScanEngine::ScanModules(System::TokenAccessChecker& access, System::ProcessInformation& info) 86 | { 87 | System::ModulesSnapshot snapshot(info.GetProcess()->GetProcessID()); 88 | HMODULE module; 89 | 90 | while (snapshot.GetNextModule(module)) 91 | { 92 | std::wstring modulePath, moduleDir; 93 | info.GetModulePath(module, modulePath); 94 | 95 | System::FileUtils::ExtractFileDirectory(modulePath, moduleDir); 96 | 97 | if (IsDirWritable(moduleDir, access)) 98 | { 99 | NotifyWritableDirectory(DetectionDirType::LoadedModule, moduleDir); 100 | continue; 101 | } 102 | 103 | if (IsFileWritable(modulePath, access)) 104 | { 105 | NotifyWritableFile(DetectionFileType::LoadedModule, modulePath); 106 | continue; 107 | } 108 | } 109 | } 110 | 111 | bool ProcessScanEngine::IsFileWritable(std::wstring path, System::TokenAccessChecker& access) 112 | { 113 | System::File file(path.c_str()); 114 | System::SecurityDescriptor descriptor(file); 115 | return access.IsFileObjectAccessible(descriptor, FILE_WRITE_DATA); 116 | } 117 | 118 | bool ProcessScanEngine::IsDirWritable(std::wstring path, System::TokenAccessChecker& access) 119 | { 120 | System::Directory directory(path.c_str()); 121 | System::SecurityDescriptor descriptor(directory); 122 | return access.IsFileObjectAccessible(descriptor, FILE_ADD_FILE); 123 | } 124 | 125 | const wchar_t* ProcessScanEngine::ConvertDirDetectionToString(DetectionDirType detection) 126 | { 127 | switch (detection) 128 | { 129 | case DetectionDirType::Executable: 130 | return L"Executable directory"; 131 | case DetectionDirType::Current: 132 | return L"Current directory"; 133 | case DetectionDirType::Users: 134 | return L"Users directory"; 135 | case DetectionDirType::Environment: 136 | return L"Environment directory"; 137 | case DetectionDirType::LoadedModule: 138 | return L"Module directory"; 139 | default: 140 | break; 141 | } 142 | return L"Unknown"; 143 | } 144 | 145 | const wchar_t* ProcessScanEngine::ConvertFileDetectionToString(DetectionFileType detection) 146 | { 147 | switch (detection) 148 | { 149 | case DetectionFileType::Executable: 150 | return L"Executable file"; 151 | case DetectionFileType::LoadedModule: 152 | return L"Loaded module"; 153 | default: 154 | break; 155 | } 156 | return L"Unknown"; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /hijacken/ImageScan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "System.h" 4 | #include "PEParser.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Engine 10 | { 11 | // ================= 12 | 13 | class ImageDirectory 14 | { 15 | public: 16 | 17 | enum class Type 18 | { 19 | Base, 20 | System32, 21 | System, 22 | Windows, 23 | Current, 24 | Environment, 25 | FullPath, 26 | SxS, 27 | Unknown 28 | }; 29 | 30 | enum class State 31 | { 32 | Existing, 33 | NotExisting, 34 | Overlapped 35 | }; 36 | 37 | private: 38 | 39 | std::wstring _directory; 40 | Type _type; 41 | bool _accessible; 42 | State _state; 43 | 44 | public: 45 | 46 | ImageDirectory(); 47 | ImageDirectory(Type type, const std::wstring& imageDir, const System::TokenAccessChecker& access); 48 | 49 | bool operator==(const ImageDirectory& compared) const; 50 | 51 | const std::wstring& GetPath() const; 52 | Type GetType() const; 53 | State GetState() const; 54 | bool IsAccessible() const; 55 | 56 | }; 57 | 58 | typedef std::vector ImageDirectories; 59 | 60 | // ================= 61 | 62 | class LoadImageOrder 63 | { 64 | private: 65 | ImageDirectories _order; 66 | ImageDirectories _orderWow64; 67 | bool _wow64mode; 68 | 69 | public: 70 | LoadImageOrder(const std::wstring& imageDir, const std::wstring& currentDir, const System::EnvironmentVariables& envVars, const System::TokenAccessChecker& access); 71 | 72 | void SetWow64Mode(bool value); 73 | 74 | const ImageDirectories& GetOrder() const; 75 | const ImageDirectory& GetBaseDir() const; 76 | static bool IsSafeSearchEnabled(); 77 | 78 | 79 | private: 80 | void LoadEnvironmentVariables(const System::EnvironmentVariables& envVars, bool wow64mode, const System::TokenAccessChecker& access); 81 | }; 82 | 83 | // ================= 84 | 85 | class ImageScanOrder : public LoadImageOrder 86 | { 87 | public: 88 | ImageScanOrder(const std::wstring& imageDir, const std::wstring& currentDir, const System::EnvironmentVariables& envVars, const System::TokenAccessChecker& access); 89 | 90 | ImageDirectory FindDllDirectory(const std::wstring& dllname) const; 91 | 92 | private: 93 | bool DirContainsDll(const std::wstring& dllname, ImageDirectory& dir) const; 94 | 95 | }; 96 | 97 | // ================= 98 | 99 | class DllCache 100 | { 101 | private: 102 | std::unordered_set _dlls; 103 | public: 104 | bool InsertOnlyNew(const std::wstring& dllName); 105 | bool Contain(const std::wstring& dllName); 106 | }; 107 | 108 | // ================= 109 | 110 | class KnownDlls 111 | { 112 | private: 113 | DllCache _known; 114 | DllCache _knownWow64; 115 | DllCache _excluded; 116 | 117 | bool _supportWow64; 118 | 119 | public: 120 | KnownDlls(); 121 | 122 | bool Contain(std::wstring& dllName, System::Bitness bitness); 123 | 124 | private: 125 | 126 | void LoadExcludedDlls(); 127 | void UnwindImports(const std::wstring& dllName, bool wow64mode); 128 | }; 129 | 130 | // ================= 131 | 132 | class ActivationContextStack 133 | { 134 | private: 135 | std::vector _stack; 136 | 137 | public: 138 | void Push(System::ActivationContext& context); 139 | void Pop(); 140 | 141 | bool IsLibrarySxS(const std::wstring& dllName, std::wstring& sxsDir); 142 | 143 | }; 144 | 145 | class LoadManifestAndPush 146 | { 147 | public: 148 | LoadManifestAndPush(System::ImageMapping& module, const std::wstring& imageDir, ActivationContextStack& stack); 149 | 150 | private: 151 | std::vector ReadManifestFromResources(System::ImageMapping& module); 152 | std::vector NormalizeManifest(const std::vector& manifest); 153 | std::wstring SafeManifestToTempFile(const std::vector& manifest); 154 | }; 155 | 156 | // ================= 157 | 158 | class ImageScanContext 159 | { 160 | private: 161 | 162 | std::shared_ptr _image; 163 | PEParser::ImagePtr _parser; 164 | 165 | std::wstring _imagePath; 166 | std::wstring _imageDir; 167 | std::wstring _imageFile; 168 | System::Bitness _bitness; 169 | 170 | DllCache _scannedDlls; 171 | 172 | const System::TokenAccessChecker& _accessChecker; 173 | 174 | ActivationContextStack _actxStack; 175 | 176 | public: 177 | ImageScanContext(const std::wstring& imagePath, const System::TokenAccessChecker& access); 178 | 179 | System::ImageMapping GetAppImage() const; 180 | const PEParser::ImagePtr GetAppParser() const; 181 | 182 | const std::wstring& GetAppPath() const; 183 | const std::wstring& GetAppDirectory() const; 184 | const std::wstring& GetAppFileName() const; 185 | System::Bitness GetAppBitness() const; 186 | 187 | DllCache& GetDllsCache(); 188 | 189 | const System::TokenAccessChecker& GetAccessChecker() const; 190 | 191 | ActivationContextStack& GetActivationContextStack(); 192 | 193 | private: 194 | void LoadDefaultSystemActivationContext(); 195 | }; 196 | 197 | // ================= 198 | 199 | class ImageScanEngine : public System::Wow64NoFsRedirection 200 | { 201 | private: 202 | 203 | bool _unwindImports; 204 | bool _scanDelayLoad; 205 | bool _checkAccessible; 206 | 207 | KnownDlls _knownDlls; 208 | 209 | public: 210 | 211 | void SetOptionUnwindImport(bool enable); 212 | void SetOptionUnwindDelayLoadImport(bool enable); 213 | void SetOptionAccessibleOnly(bool enable); 214 | 215 | void Scan(std::wstring& imagePath, System::EnvironmentVariables& envVars, System::TokenAccessChecker& access); 216 | 217 | private: 218 | 219 | void ScanModule(ImageScanContext& context, std::wstring& dllName, ImageScanOrder& order); 220 | void ScanImports(System::ImageMapping& module, ImageScanContext& context, ImageScanOrder& order); 221 | void ScanImports(const PEParser::ImagePtr& image, ImageScanContext& context, ImageScanOrder& order); 222 | void PerformExistingModuleAction(ImageScanContext& context, std::wstring& dllName, ImageDirectory& dir, ImageScanOrder& order); 223 | void PerformNotExistingModuleAction(ImageScanContext& context, std::wstring& dllName, ImageDirectory& dir, ImageScanOrder& order); 224 | void PerformSxSModuleAction(ImageScanContext& context, std::wstring& dllName, std::wstring& sxsDir, ImageScanOrder& order); 225 | 226 | std::vector CollectVulnerableDirs(const ImageDirectory& last, ImageScanOrder& order); 227 | 228 | static bool IsFileWritable(const std::wstring& path, const System::TokenAccessChecker& access); 229 | static bool IsDirectoryWritable(const std::wstring& path, const System::TokenAccessChecker& access); 230 | 231 | protected: 232 | 233 | virtual void NotifyLoadImageOrder(LoadImageOrder& dir); 234 | virtual void NotifyVulnerableDll(ImageDirectory& dir, std::wstring& dll, bool writtable, std::vector& vulnDirs); 235 | virtual void NotifyVulnerableSxSDll(ImageDirectory& dir, std::wstring& dll, bool writtable); 236 | }; 237 | }; 238 | -------------------------------------------------------------------------------- /hijacken/Commands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Utils.h" 4 | #include "System.h" 5 | #include "ProcessScan.h" 6 | #include "ImageScan.h" 7 | #include 8 | 9 | namespace Commands 10 | { 11 | // ================= 12 | 13 | class ICommand 14 | { 15 | public: 16 | virtual ~ICommand() {} 17 | 18 | virtual void LoadArgs(Utils::Arguments& args) = 0; 19 | virtual void Perform() = 0; 20 | }; 21 | 22 | typedef std::shared_ptr CommandPtr; 23 | 24 | // ================= 25 | 26 | class ImpersonationOptions 27 | { 28 | private: 29 | DWORD _tokenSourceProcessId; 30 | bool _changeIntegrityLevel; 31 | System::IntegrityLevel _tokenIntegrityLevel; 32 | 33 | protected: 34 | ImpersonationOptions(); 35 | 36 | void LoadArgs(Utils::Arguments& args); 37 | System::ImpersonateTokenPtr CraftToken(); 38 | 39 | static void PrintTokenInformation(System::ImpersonateTokenPtr& token); 40 | static const wchar_t* ConvertIntegrityLevelToString(System::IntegrityLevel level); 41 | 42 | private: 43 | void ChangeIntegrity(System::ImpersonateTokenPtr& token, System::IntegrityLevel level); 44 | static System::IntegrityLevel ConvertStrToIntegrityLevel(std::wstring& level); 45 | }; 46 | 47 | // ================= 48 | 49 | class SystemOptions 50 | { 51 | private: 52 | bool _scanElevated; 53 | 54 | protected: 55 | SystemOptions(); 56 | 57 | void LoadArgs(Utils::Arguments& args); 58 | bool ShouldScanProcess(System::ImpersonateTokenPtr& token, DWORD targetProcessId); 59 | }; 60 | 61 | // ================= 62 | 63 | class EnvironmentOptions 64 | { 65 | private: 66 | enum class EnvironmentSource 67 | { 68 | System, 69 | User, 70 | Process, 71 | Inherit, 72 | Off 73 | }; 74 | 75 | EnvironmentSource _envSource; 76 | DWORD _sourceProcessId; 77 | 78 | protected: 79 | EnvironmentOptions(); 80 | 81 | void LoadArgs(Utils::Arguments& args); 82 | 83 | System::EnvironmentVariablesPtr GetEnvironment(); 84 | }; 85 | 86 | // ================= 87 | 88 | class ScanFile : 89 | public ICommand, 90 | protected ImpersonationOptions, 91 | protected EnvironmentOptions, 92 | protected Engine::ImageScanEngine 93 | { 94 | private: 95 | bool _unwindImports; 96 | bool _scanDelayLoad; 97 | bool _checkAccess; 98 | 99 | bool _firstFound; 100 | 101 | std::wstring _filePath; 102 | 103 | void ScanImage(std::string path); 104 | 105 | public: 106 | ScanFile(); 107 | virtual ~ScanFile() {} 108 | 109 | void LoadArgs(Utils::Arguments& args) override; 110 | void Perform() override; 111 | 112 | protected: 113 | void NotifyLoadImageOrder(Engine::LoadImageOrder& dirs) override; 114 | void NotifyVulnerableDll(Engine::ImageDirectory& dir, std::wstring& dll, bool writtable, std::vector& vulnDirs) override; 115 | void NotifyVulnerableSxSDll(Engine::ImageDirectory& dir, std::wstring& dll, bool writtable) override; 116 | 117 | public: 118 | static const wchar_t* ConvertImageDirTypeToString(Engine::ImageDirectory::Type type); 119 | static const wchar_t* ConvertImageDirStateToString(const Engine::ImageDirectory& dir); 120 | }; 121 | 122 | // ================= 123 | 124 | class ScanDirectory : 125 | public ICommand, 126 | protected SystemOptions 127 | { 128 | private: 129 | 130 | public: 131 | ScanDirectory(); 132 | virtual ~ScanDirectory() {} 133 | 134 | void LoadArgs(Utils::Arguments& args) override; 135 | void Perform() override; 136 | }; 137 | 138 | // ================= 139 | 140 | class ScanProcess : 141 | public ICommand, 142 | protected ImpersonationOptions, 143 | protected Engine::ProcessScanEngine 144 | { 145 | private: 146 | DWORD _targetProcessId; 147 | 148 | std::map> _detectedDirs; 149 | std::map> _detectedFiles; 150 | 151 | public: 152 | ScanProcess(); 153 | virtual ~ScanProcess() {} 154 | 155 | void LoadArgs(Utils::Arguments& args) override; 156 | void Perform() override; 157 | 158 | protected: 159 | void NotifyWritableDirectory(DetectionDirType detection, std::wstring& dirPath) override; 160 | void NotifyWritableFile(DetectionFileType detection, std::wstring& filePath) override; 161 | }; 162 | 163 | // ================= 164 | 165 | class ScanProcesses : 166 | public ICommand, 167 | protected SystemOptions, 168 | protected ImpersonationOptions, 169 | protected Engine::ProcessScanEngine 170 | { 171 | private: 172 | 173 | std::map> _detectedDirs; 174 | std::map> _detectedFiles; 175 | 176 | public: 177 | ScanProcesses(); 178 | virtual ~ScanProcesses() {} 179 | 180 | void LoadArgs(Utils::Arguments& args) override; 181 | void Perform() override; 182 | 183 | protected: 184 | void NotifyWritableDirectory(DetectionDirType detection, std::wstring& dirPath) override; 185 | void NotifyWritableFile(DetectionFileType detection, std::wstring& filePath) override; 186 | }; 187 | 188 | // ================= 189 | 190 | class ScanAutorun : 191 | public ICommand, 192 | protected SystemOptions 193 | { 194 | private: 195 | 196 | public: 197 | ScanAutorun(); 198 | virtual ~ScanAutorun() {} 199 | 200 | void LoadArgs(Utils::Arguments& args) override; 201 | void Perform() override; 202 | }; 203 | 204 | // ================= 205 | 206 | class ScanTask : public ICommand 207 | { 208 | private: 209 | 210 | public: 211 | ScanTask(); 212 | virtual ~ScanTask() {} 213 | 214 | void LoadArgs(Utils::Arguments& args) override; 215 | void Perform() override; 216 | }; 217 | 218 | // ================= 219 | 220 | class ScanTasks : 221 | public ICommand, 222 | protected SystemOptions 223 | { 224 | private: 225 | 226 | public: 227 | ScanTasks(); 228 | virtual ~ScanTasks() {} 229 | 230 | void LoadArgs(Utils::Arguments& args) override; 231 | void Perform() override; 232 | }; 233 | 234 | // ================= 235 | 236 | class ScanService : public ICommand 237 | { 238 | private: 239 | 240 | public: 241 | ScanService(); 242 | virtual ~ScanService() {} 243 | 244 | void LoadArgs(Utils::Arguments& args) override; 245 | void Perform() override; 246 | }; 247 | 248 | // ================= 249 | 250 | class ScanServices : 251 | public ICommand, 252 | protected SystemOptions 253 | { 254 | private: 255 | 256 | public: 257 | ScanServices(); 258 | virtual ~ScanServices() {} 259 | 260 | void LoadArgs(Utils::Arguments& args) override; 261 | void Perform() override; 262 | }; 263 | 264 | // ================= 265 | 266 | class ScanSystem : public ICommand 267 | { 268 | private: 269 | 270 | public: 271 | ScanSystem(); 272 | virtual ~ScanSystem() {} 273 | 274 | void LoadArgs(Utils::Arguments& args) override; 275 | void Perform() override; 276 | }; 277 | 278 | // ================= 279 | 280 | class MakeDll : public ICommand 281 | { 282 | private: 283 | 284 | public: 285 | MakeDll(); 286 | virtual ~MakeDll() {} 287 | 288 | void LoadArgs(Utils::Arguments& args) override; 289 | void Perform() override; 290 | }; 291 | }; 292 | -------------------------------------------------------------------------------- /hijacken/hijacken.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {D37B1B81-F34C-4977-B350-6242F2F46468} 23 | Win32Proj 24 | hijacken 25 | 26 | 27 | 28 | Application 29 | true 30 | v120 31 | Unicode 32 | 33 | 34 | Application 35 | true 36 | v120 37 | Unicode 38 | 39 | 40 | Application 41 | false 42 | v120 43 | true 44 | Unicode 45 | 46 | 47 | Application 48 | false 49 | v120 50 | true 51 | Unicode 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | ntlib\include;$(IncludePath) 72 | ntlib\library\x86;$(LibraryPath) 73 | 74 | 75 | true 76 | ntlib\include\;$(IncludePath) 77 | ntlib\library\x64;$(LibraryPath) 78 | 79 | 80 | false 81 | ntlib\include;$(IncludePath) 82 | ntlib\library\x86;$(LibraryPath) 83 | 84 | 85 | false 86 | ntlib\include\;$(IncludePath) 87 | ntlib\library\x64;$(LibraryPath) 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | ntlib.lib;%(AdditionalDependencies) 101 | 102 | 103 | 104 | 105 | 106 | 107 | Level3 108 | Disabled 109 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 110 | 111 | 112 | Console 113 | true 114 | ntlib.lib;%(AdditionalDependencies) 115 | 116 | 117 | 118 | 119 | Level3 120 | 121 | 122 | MaxSpeed 123 | true 124 | true 125 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | ntlib.lib;%(AdditionalDependencies) 133 | 134 | 135 | 136 | 137 | Level3 138 | 139 | 140 | MaxSpeed 141 | true 142 | true 143 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 144 | 145 | 146 | Console 147 | true 148 | true 149 | true 150 | ntlib.lib;%(AdditionalDependencies) 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /hijacken/System.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Utils.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace System 9 | { 10 | enum class Bitness 11 | { 12 | Arch32, 13 | Arch64 14 | }; 15 | 16 | // ================= 17 | 18 | class LastError 19 | { 20 | private: 21 | DWORD _code; 22 | 23 | public: 24 | LastError(); 25 | 26 | HANDLE Proxymize(HANDLE handle); 27 | 28 | DWORD GetCode() const; 29 | }; 30 | 31 | // ================= 32 | 33 | class Handle : private std::shared_ptr 34 | { 35 | public: 36 | typedef void(*DestroyObjectRoutine)(HANDLE object); 37 | 38 | private: 39 | static void ObjectDeleter(HANDLE object); 40 | 41 | public: 42 | Handle(); 43 | Handle(HANDLE object, DestroyObjectRoutine destroyer = &ObjectDeleter); 44 | 45 | bool IsValid() const; 46 | 47 | HANDLE GetNativeHandle() const; 48 | 49 | protected: 50 | 51 | void SetHandle(HANDLE object, DestroyObjectRoutine destroyer = &ObjectDeleter); 52 | }; 53 | 54 | // ================= 55 | 56 | class Process : public Handle 57 | { 58 | private: 59 | DWORD _processId; 60 | 61 | template 62 | void ReadMemoryToContainer(void* address, T& buffer, size_t size); 63 | 64 | static void WithoutRelease(HANDLE object); 65 | 66 | public: 67 | Process(DWORD processId, DWORD access = PROCESS_ALL_ACCESS); 68 | Process(HANDLE process); 69 | 70 | DWORD GetProcessID(); 71 | 72 | void ReadMemory(void* address, std::string& buffer, size_t size); 73 | void ReadMemory(void* address, std::wstring& buffer, size_t size); 74 | void WriteMemory(void* address, std::string& buffer, bool unprotect = true); 75 | void WriteMemory(void* address, std::wstring& buffer, bool unprotect = true); 76 | }; 77 | 78 | typedef std::shared_ptr ProcessPtr; 79 | 80 | // ================= 81 | 82 | class ProcessesSnapshot : protected Handle 83 | { 84 | private: 85 | bool _fromStart; 86 | 87 | public: 88 | ProcessesSnapshot(); 89 | 90 | bool GetNextProcess(DWORD& processId); 91 | bool GetNextProcess(DWORD& processId, std::wstring& name); 92 | void ResetWalking(); 93 | }; 94 | 95 | // ================= 96 | 97 | class ModulesSnapshot : protected Handle 98 | { 99 | private: 100 | bool _fromStart; 101 | 102 | public: 103 | ModulesSnapshot(DWORD processId); 104 | 105 | bool GetNextModule(HMODULE& module); 106 | bool GetNextModule(HMODULE& module, std::wstring& name); 107 | void ResetWalking(); 108 | }; 109 | 110 | // ================= 111 | 112 | class EnvironmentVariables; 113 | typedef std::shared_ptr EnvironmentVariablesPtr; 114 | 115 | class ProcessEnvironmentBlock; 116 | typedef std::shared_ptr ProcessEnvironmentBlockPtr; 117 | 118 | // ================= 119 | 120 | class ProcessInformation 121 | { 122 | private: 123 | 124 | ProcessPtr _process; 125 | 126 | PPEB _pebAddress; 127 | ProcessEnvironmentBlockPtr _peb; 128 | 129 | public: 130 | ProcessInformation(DWORD processId); 131 | 132 | ProcessPtr GetProcess(); 133 | 134 | PPEB GetPEBAddress(); 135 | ProcessEnvironmentBlockPtr GetProcessEnvironmentBlock(); 136 | 137 | void GetImagePath(std::wstring& path); 138 | void GetImageDirectory(std::wstring& directory); 139 | 140 | void GetModulePath(HMODULE module, std::wstring& path); 141 | 142 | static Bitness GetCurrentProcessBitness(); 143 | static EnvironmentVariablesPtr GetCurrentEnvironmentVariables(); 144 | }; 145 | 146 | // ================= 147 | 148 | class ProcessEnvironmentBlock 149 | { 150 | private: 151 | ProcessPtr _process; 152 | 153 | std::string _pebBuffer; 154 | PPEB _peb; 155 | 156 | std::string _paramsBuffer; 157 | PRTL_USER_PROCESS_PARAMETERS _params; 158 | std::wstring _paramsEnv; 159 | 160 | std::wstring _currentDirectory; 161 | 162 | void LoadProcessParameters(); 163 | 164 | public: 165 | ProcessEnvironmentBlock(ProcessInformation& processInfo); 166 | 167 | EnvironmentVariablesPtr GetProcessEnvironment(); 168 | 169 | void GetCurrentDir(std::wstring& directory); 170 | }; 171 | 172 | // ================= 173 | 174 | enum class IntegrityLevel 175 | { 176 | Untrusted, 177 | //BelowLow, ??? 178 | Low, 179 | //MediumLow, ??? 180 | Medium, 181 | MediumPlus, 182 | High, 183 | System, 184 | Protected, 185 | Secure 186 | }; 187 | 188 | class TokenBase : public Handle 189 | { 190 | protected: 191 | TokenBase() {} 192 | 193 | public: 194 | void SetPrivilege(wchar_t* privelege, bool enable); 195 | 196 | IntegrityLevel GetIntegrityLevel(); 197 | void SetIntegrityLevel(IntegrityLevel level); 198 | 199 | HANDLE GetLinkedToken(); 200 | void SetLinkedToken(HANDLE token); 201 | 202 | void GetUserNameString(std::wstring& userName); 203 | void GetUserSIDString(std::wstring& sid); 204 | 205 | bool IsElevated(); 206 | 207 | private: 208 | PSID AllocateSidByIntegrityLevel(IntegrityLevel level); 209 | }; 210 | 211 | class PrimaryToken : public TokenBase 212 | { 213 | public: 214 | PrimaryToken(Process& source, DWORD access = TOKEN_ALL_ACCESS, bool duplicate = false); 215 | PrimaryToken(HANDLE token, bool duplicate = false); 216 | }; 217 | 218 | typedef std::shared_ptr PrimaryTokenPtr; 219 | 220 | class ImpersonateToken : public TokenBase 221 | { 222 | public: 223 | ImpersonateToken(Process& source, DWORD access = TOKEN_ALL_ACCESS); 224 | ImpersonateToken(HANDLE token, bool duplicate = false); 225 | }; 226 | 227 | typedef std::shared_ptr ImpersonateTokenPtr; 228 | 229 | class SecurityDescriptor 230 | { 231 | private: 232 | PSECURITY_DESCRIPTOR _descriptor; 233 | PACL _dacl; 234 | PSID _owner; 235 | PSID _group; 236 | 237 | public: 238 | SecurityDescriptor(Handle& file); 239 | ~SecurityDescriptor(); 240 | 241 | PSECURITY_DESCRIPTOR GetNativeSecurityDescriptor(); 242 | }; 243 | 244 | class TokenAccessChecker 245 | { 246 | private: 247 | ImpersonateToken _token; 248 | 249 | public: 250 | TokenAccessChecker(Process& process); 251 | TokenAccessChecker(ImpersonateToken& token); 252 | 253 | bool IsFileObjectAccessible(SecurityDescriptor& descriptor, DWORD desiredAccess) const; 254 | }; 255 | 256 | // ================= 257 | 258 | class File : public Handle 259 | { 260 | public: 261 | File(const wchar_t* path , DWORD access = READ_CONTROL, DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, bool newFile = false); 262 | 263 | void Write(void* buffer, size_t size); 264 | 265 | void SetDeleteOnClose(); 266 | }; 267 | 268 | class ImageMapping : public Handle 269 | { 270 | private: 271 | void* _mapping; 272 | size_t _mappingSize; 273 | 274 | public: 275 | ImageMapping(const wchar_t* path); 276 | ~ImageMapping(); 277 | 278 | void* GetAddress(); 279 | size_t GetSize(); 280 | }; 281 | 282 | class FileUtils 283 | { 284 | public: 285 | static std::wstring BuildPath(const std::wstring& directory, const std::wstring& file); 286 | static void ExtractFileDirectory(const std::wstring& path, std::wstring& directory); 287 | static void ExtractFileName(const std::wstring& path, std::wstring& name); 288 | static void NormalizePath(std::wstring& path); 289 | static bool IsPathRelative(const std::wstring& path); 290 | static bool PathExists(const std::wstring& path); 291 | static File CreateTempFile(std::wstring& path, DWORD access = READ_CONTROL, DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE); 292 | static std::wstring ApplyWow64Redirection(const std::wstring& path); 293 | static bool RedirectDirectory(std::wstring& path, const std::wstring& from, const std::wstring& to); 294 | static std::wstring FindFirstMatchedFile(const std::wstring& path); 295 | }; 296 | 297 | // ================= 298 | 299 | class Directory : public Handle 300 | { 301 | public: 302 | Directory(const wchar_t* path, DWORD access = READ_CONTROL, DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE); 303 | 304 | static bool IsDirectory(const wchar_t* path); //TODO: move to directory utils 305 | }; 306 | 307 | // ================= 308 | 309 | class SystemInformation 310 | { 311 | public: 312 | static Bitness GetSystemBitness(); 313 | 314 | static std::wstring GetSystem32Dir(); 315 | static std::wstring GetSysWow64Dir(); 316 | static std::wstring GetSystemDir(); 317 | static std::wstring GetWindowsDir(); 318 | static std::wstring GetTempDir(); 319 | static std::wstring GetLastGoodSystem32Dir(); 320 | static std::wstring GetLastGoodSysWow64Dir(); 321 | }; 322 | 323 | class Wow64NoFsRedirection 324 | { 325 | bool _revert; 326 | PVOID _old; 327 | public: 328 | Wow64NoFsRedirection(); 329 | ~Wow64NoFsRedirection(); 330 | }; 331 | 332 | // ================= 333 | 334 | enum class BaseKeys 335 | { 336 | Root, 337 | CurrentConfig, 338 | CurrentUser, 339 | LocalMachine 340 | }; 341 | 342 | class RegistryKey 343 | { 344 | private: 345 | HKEY _hkey; 346 | 347 | public: 348 | RegistryKey(BaseKeys base, const wchar_t* key, DWORD access = KEY_QUERY_VALUE); 349 | ~RegistryKey(); 350 | 351 | HKEY GetNativeHKEY() const; 352 | 353 | private: 354 | HKEY ConvertBaseToHKEY(BaseKeys base); 355 | }; 356 | 357 | // ================= 358 | 359 | enum class RegistryValueType 360 | { 361 | None, 362 | String, 363 | ExpandString, 364 | Binary, 365 | Dword, 366 | DwordBigEndian, 367 | Link, 368 | MultiString, 369 | ResourceList, 370 | FullResourceDescrition, 371 | ResourceRequirementsList, 372 | Qword, 373 | Unknown 374 | }; 375 | 376 | class RegistryValue 377 | { 378 | private: 379 | RegistryValueType _type; 380 | std::wstring _value; 381 | 382 | RegistryValueType ConvertToRegistryType(DWORD type); 383 | 384 | public: 385 | RegistryValue(const RegistryKey& key, const wchar_t* value); 386 | 387 | RegistryValueType GetType() const; 388 | const std::wstring& GetValue() const; 389 | }; 390 | 391 | // ================= 392 | 393 | class RegistryDwordValue 394 | { 395 | private: 396 | DWORD _value; 397 | 398 | public: 399 | RegistryDwordValue(const RegistryValue& value); 400 | RegistryDwordValue(const RegistryKey& key, const wchar_t* value); 401 | 402 | DWORD GetValue() const; 403 | 404 | private: 405 | void LoadDword(const RegistryValue& value); 406 | }; 407 | 408 | // ================= 409 | 410 | class RegistryMultiStringValue : public std::vector 411 | { 412 | public: 413 | RegistryMultiStringValue(const RegistryValue& value); 414 | RegistryMultiStringValue(const RegistryKey& key, const wchar_t* value); 415 | private: 416 | void LoadStrings(const RegistryValue& value); 417 | }; 418 | 419 | typedef std::map RegistryValues; 420 | 421 | // ================= 422 | 423 | class RegistryStringValue 424 | { 425 | private: 426 | std::wstring _value; 427 | 428 | public: 429 | RegistryStringValue(const RegistryValue& value); 430 | RegistryStringValue(const RegistryKey& key, const wchar_t* value); 431 | 432 | const std::wstring& GetValue() const; 433 | 434 | private: 435 | void LoadRegString(const RegistryValue& value); 436 | }; 437 | 438 | // ================= 439 | 440 | class RegistryExpandedStringValue 441 | { 442 | private: 443 | std::wstring _value; 444 | 445 | public: 446 | RegistryExpandedStringValue(const RegistryValue& value); 447 | RegistryExpandedStringValue(const RegistryKey& key, const wchar_t* value); 448 | 449 | const std::wstring& GetValue() const; 450 | 451 | private: 452 | void LoadRegString(const RegistryValue& value); 453 | }; 454 | 455 | // ================= 456 | 457 | class EnumRegistryValues 458 | { 459 | private: 460 | RegistryValues _values; 461 | 462 | public: 463 | EnumRegistryValues(BaseKeys base, const wchar_t* key); 464 | 465 | const RegistryValues GetValues() const; 466 | }; 467 | 468 | // ================= 469 | 470 | class EnvironmentVariables 471 | { 472 | private: 473 | std::map _variables; 474 | 475 | public: 476 | EnvironmentVariables() = default; 477 | EnvironmentVariables(LPWCH environment); 478 | EnvironmentVariables(const std::wstring& environment); 479 | EnvironmentVariables(const RegistryValues& values); 480 | 481 | bool GetValue(const wchar_t* key, std::wstring& output) const; 482 | }; 483 | 484 | // ================= 485 | 486 | class ActivationContext : public Handle 487 | { 488 | public: 489 | ActivationContext(const wchar_t* path, const wchar_t* assemblyDir = nullptr); 490 | ActivationContext(ImageMapping& image); 491 | ActivationContext(bool loadDefault = false); 492 | 493 | private: 494 | static void DestroyActivationContext(HANDLE object); 495 | }; 496 | 497 | // ================= 498 | 499 | enum class ActivationContextRunLevelType 500 | { 501 | Unspecified, 502 | AsInvoker, 503 | HighestAvailable, 504 | RequireAdmin, 505 | Unknown 506 | }; 507 | 508 | class ActivationContextRunLevel 509 | { 510 | private: 511 | ActivationContextRunLevelType _runLevel; 512 | bool _uiAccess; 513 | 514 | public: 515 | ActivationContextRunLevel(ActivationContext& context); 516 | 517 | ActivationContextRunLevelType GetRunLevel() const; 518 | bool GetUIAccess() const; 519 | }; 520 | 521 | // ================= 522 | 523 | class Assembly 524 | { 525 | private: 526 | std::wstring _assemblyDirID; 527 | std::vector _assemblyFiles; 528 | 529 | public: 530 | Assembly(std::wstring& assemblyDirID, std::vector& assemblyFiles); 531 | 532 | const std::wstring& GetID() const; 533 | const std::vector& GetFiles() const; 534 | }; 535 | 536 | // ================= 537 | 538 | class ActivationContextAssemblies : public std::vector 539 | { 540 | private: 541 | //std::vector _assemblies; 542 | 543 | public: 544 | ActivationContextAssemblies(ActivationContext& context); 545 | 546 | private: 547 | bool QueryAssembly(ActivationContext& context, DWORD index, std::vector& buffer); 548 | bool QueryAssemblyFile(ActivationContext& context, DWORD index, DWORD fileIndex, std::vector& buffer); 549 | 550 | }; 551 | 552 | // ================= 553 | 554 | class ApplyDefaultSystemActivationContext : protected ActivationContext 555 | { 556 | private: 557 | ULONG_PTR _cookie; 558 | public: 559 | ApplyDefaultSystemActivationContext(); 560 | ~ApplyDefaultSystemActivationContext(); 561 | }; 562 | 563 | // ================= 564 | 565 | class ActivationContextUtils 566 | { 567 | public: 568 | static std::wstring LookupSxSDirUsingDefaultSystemActivationContext(const std::wstring& dll); 569 | }; 570 | }; 571 | -------------------------------------------------------------------------------- /hijacken/PEParser.cpp: -------------------------------------------------------------------------------- 1 | #include "PEParser.h" 2 | #include 3 | 4 | namespace PEParser 5 | { 6 | // ================= 7 | 8 | ResourceEntry::ResourceEntry() : 9 | _type(NamedResourceType::Unnamed), 10 | _id(0) 11 | { 12 | } 13 | 14 | ResourceEntry::ResourceEntry(std::wstring& name) : 15 | _type(NamedResourceType::HasName), 16 | _name(name), 17 | _id(0) 18 | { 19 | } 20 | 21 | ResourceEntry::ResourceEntry(unsigned short id) : 22 | _type(NamedResourceType::HasId), 23 | _id(id) 24 | { 25 | } 26 | 27 | bool ResourceEntry::operator==(const ResourceEntry& other) const 28 | { 29 | if (_type != other.GetType()) 30 | return false; 31 | 32 | if (_type == NamedResourceType::HasName) 33 | return (_name == other.GetName()); 34 | 35 | if (_type == NamedResourceType::HasId) 36 | return (_id == other.GetID()); 37 | 38 | return true; 39 | } 40 | 41 | ResourceEntry::NamedResourceType ResourceEntry::GetType() const 42 | { 43 | return _type; 44 | } 45 | 46 | std::wstring ResourceEntry::GetName() const 47 | { 48 | return _name; 49 | } 50 | 51 | unsigned short ResourceEntry::GetID() const 52 | { 53 | return _id; 54 | } 55 | 56 | // ================= 57 | 58 | ResourceDirectory::ResourceDirectory() 59 | { 60 | } 61 | 62 | ResourceDirectory::ResourceDirectory(std::wstring& name) : 63 | ResourceEntry(name) 64 | { 65 | } 66 | 67 | ResourceDirectory::ResourceDirectory(unsigned short id) : 68 | ResourceEntry(id) 69 | { 70 | } 71 | 72 | void ResourceDirectory::Push(ResourceDirectory& dir) 73 | { 74 | _dirs.push_back(dir); 75 | } 76 | 77 | void ResourceDirectory::Push(ResourceData& data) 78 | { 79 | _data.push_back(data); 80 | } 81 | 82 | const ResourceDirectorySet& ResourceDirectory::GetDirs() const 83 | { 84 | return _dirs; 85 | } 86 | 87 | const ResourceDataSet& ResourceDirectory::GetData() const 88 | { 89 | return _data; 90 | } 91 | 92 | // ================= 93 | 94 | ResourceData::ResourceData() 95 | { 96 | } 97 | 98 | ResourceData::ResourceData(std::wstring& name, unsigned long offset, System::ImageMapping& image) : 99 | ResourceEntry(name) 100 | { 101 | LoadDataEntry(offset, image); 102 | } 103 | 104 | ResourceData::ResourceData(unsigned short id, unsigned long offset, System::ImageMapping& image) : 105 | ResourceEntry(id) 106 | { 107 | LoadDataEntry(offset, image); 108 | } 109 | 110 | std::vector ResourceData::ReadData(System::ImageMapping& image) 111 | { 112 | //TODO: it's incorrect to parse a data without section info, rewrite it 113 | std::vector buffer; 114 | if (_offset + _size > image.GetSize()) 115 | throw Utils::Exception(L"Resource data out of range"); 116 | 117 | auto ptr = reinterpret_cast(image.GetAddress()) + _offset; 118 | return std::vector(ptr, ptr + _size); 119 | } 120 | 121 | void ResourceData::LoadDataEntry(unsigned long offset, System::ImageMapping& image) 122 | { 123 | auto imagePtr = reinterpret_cast(image.GetAddress()); 124 | auto imageSize = image.GetSize(); 125 | 126 | if (offset > imageSize) 127 | throw Utils::Exception(L"Resource data entry out of range"); 128 | 129 | auto entry = reinterpret_cast(imagePtr + offset); 130 | _offset = entry->OffsetToData; 131 | _size = entry->Size; 132 | _codepage = entry->CodePage; 133 | } 134 | 135 | // ================= 136 | 137 | Resources::Resources() 138 | { 139 | } 140 | 141 | Resources::Resources(ResourceDirectory& root) : 142 | _root(root) 143 | { 144 | } 145 | 146 | const ResourceDirectory& Resources::GetRoot() const 147 | { 148 | return _root; 149 | } 150 | 151 | // ================= 152 | 153 | const ResourceDirectory& ResourceUtils::FindDirectory(const ResourceDirectory& dir, const ResourceEntry& id) 154 | { 155 | for (auto& entry : dir.GetDirs()) 156 | if (entry == id) 157 | return entry; 158 | 159 | throw Utils::Exception(L"Can't find a specific resource directory"); 160 | } 161 | 162 | const ResourceData& ResourceUtils::FindData(const ResourceDirectory& dir, const ResourceEntry& id) 163 | { 164 | for (auto& entry : dir.GetData()) 165 | if (entry == id) 166 | return entry; 167 | 168 | throw Utils::Exception(L"Can't find a specific resource data"); 169 | } 170 | 171 | const std::vector ResourceUtils::LoadFirstResource(const Resources& resources, System::ImageMapping& image, const ResourceEntry& id) 172 | { 173 | std::vector buffer; 174 | 175 | const auto& root = resources.GetRoot(); 176 | auto getFirstDir = [](const ResourceDirectory& dir) 177 | { 178 | auto dirs = dir.GetDirs(); 179 | if (!dirs.size()) 180 | throw Utils::Exception(L"A resource directory is empty"); 181 | 182 | return *dirs.begin(); 183 | }; 184 | auto getFirstData = [](const ResourceDirectory& dir) 185 | { 186 | auto data = dir.GetData(); 187 | if (!data.size()) 188 | throw Utils::Exception(L"A resource directory is empty"); 189 | 190 | return *data.begin(); 191 | }; 192 | 193 | auto second = getFirstDir(FindDirectory(root, id)); 194 | auto third = getFirstData(second); 195 | 196 | return third.ReadData(image); 197 | } 198 | 199 | // ================= 200 | 201 | ImagePtr ImageFactory::GetImage(System::ImageMapping& mapping) 202 | { 203 | auto bitness = GetImageBitness(mapping); 204 | if (bitness == System::Bitness::Arch32) 205 | return ImagePtr(new ImageImpl(mapping)); 206 | else if (bitness == System::Bitness::Arch64) 207 | return ImagePtr(new ImageImpl(mapping)); 208 | 209 | throw Utils::Exception(L"Unknown image bitness"); 210 | } 211 | 212 | System::Bitness ImageFactory::GetImageBitness(System::ImageMapping& mapping) 213 | { 214 | auto imageSize = mapping.GetSize(); 215 | 216 | if (imageSize < 0x1000) 217 | throw Utils::Exception(L"Invalid image size, can't be less 0x1000"); 218 | 219 | auto dos = reinterpret_cast(mapping.GetAddress()); 220 | if (dos->e_magic != IMAGE_DOS_SIGNATURE) 221 | throw Utils::Exception(L"Invalid DOS signature"); 222 | 223 | if (sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + dos->e_lfanew > 0x1000) 224 | throw Utils::Exception(L"Invalid NT headers size, can't be bigger than 0x1000"); 225 | 226 | auto signature = *reinterpret_cast(reinterpret_cast(dos) + dos->e_lfanew); 227 | if (signature != IMAGE_NT_SIGNATURE) 228 | throw Utils::Exception(L"Invalid NT signature"); 229 | 230 | auto header = reinterpret_cast( 231 | reinterpret_cast(dos) + dos->e_lfanew + sizeof(DWORD) 232 | ); 233 | if (header->Machine == IMAGE_FILE_MACHINE_I386) 234 | return System::Bitness::Arch32; 235 | else if (header->Machine == IMAGE_FILE_MACHINE_AMD64) 236 | return System::Bitness::Arch64; 237 | 238 | throw Utils::Exception(L"Unknown PE architecture"); 239 | } 240 | 241 | // ================= 242 | 243 | Image::Image(System::ImageMapping& mapping) : 244 | _mapping(mapping) 245 | { 246 | } 247 | 248 | void* Image::GetAddressByRVA(DWORD rva) 249 | { 250 | if (rva > _mapping.GetSize()) 251 | throw Utils::Exception(L"RVA offset %x overflows the mapping", rva); 252 | 253 | return reinterpret_cast(reinterpret_cast(_mapping.GetAddress()) + rva); 254 | } 255 | 256 | std::string Image::LoadStringByRVA(DWORD rva) 257 | { 258 | char* str = reinterpret_cast(GetAddressByRVA(rva)); 259 | 260 | size_t i; 261 | for (i = 0; str[i] != '\0'; i++) 262 | if (i > _mapping.GetSize()) 263 | throw Utils::Exception(L"String RVA %x overflows the mapping", rva); 264 | 265 | return std::string(str, str + i); 266 | } 267 | 268 | std::wstring Image::LoadWStringByRVA(DWORD rva) 269 | { 270 | wchar_t* str = reinterpret_cast(GetAddressByRVA(rva)); 271 | 272 | size_t i; 273 | for (i = 0; str[i] != '\0'; i++) 274 | if (i * sizeof(wchar_t) > _mapping.GetSize()) 275 | throw Utils::Exception(L"String RVA %x overflows the mapping", rva); 276 | 277 | return std::wstring(str, str + i); 278 | } 279 | 280 | System::Bitness Image::GetBitness() 281 | { 282 | return _bitness; 283 | } 284 | 285 | // ================= 286 | 287 | template 288 | ImageImpl::ImageImpl(System::ImageMapping& mapping) : 289 | Image(mapping), 290 | _header(nullptr) 291 | { 292 | if (sizeof(T) == sizeof(IMAGE_NT_HEADERS32)) 293 | _bitness = System::Bitness::Arch32; 294 | else if (sizeof(T) == sizeof(IMAGE_NT_HEADERS64)) 295 | _bitness = System::Bitness::Arch64; 296 | else 297 | throw Utils::Exception(L"Invalid architecture"); 298 | 299 | if (mapping.GetSize() < 0x1000) 300 | throw Utils::Exception(L"Invalid image size, can't be less 0x1000"); 301 | 302 | auto dos = reinterpret_cast(mapping.GetAddress()); 303 | if (dos->e_magic != IMAGE_DOS_SIGNATURE) 304 | throw Utils::Exception(L"Invalid DOS signature"); 305 | 306 | if (sizeof(T) + dos->e_lfanew > 0x1000) 307 | throw Utils::Exception(L"Invalid NT headers size, can't be bigger than 0x1000"); 308 | 309 | _header = reinterpret_cast(reinterpret_cast(dos) + dos->e_lfanew); 310 | if (_header->Signature != IMAGE_NT_SIGNATURE) 311 | throw Utils::Exception(L"Invalid NT signature"); 312 | 313 | if (_bitness == System::Bitness::Arch32 && _header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) 314 | throw Utils::Exception(L"Invalid NT architecture"); 315 | else if (_bitness == System::Bitness::Arch64 && _header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) 316 | throw Utils::Exception(L"Invalid NT architecture"); 317 | 318 | if (!_header->FileHeader.NumberOfSections) 319 | return; 320 | 321 | auto sectionOffset = dos->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + _header->FileHeader.SizeOfOptionalHeader; 322 | if (sectionOffset + (sizeof(IMAGE_SECTION_HEADER) * _header->FileHeader.NumberOfSections) >= mapping.GetSize()) 323 | throw Utils::Exception(L"Invalid sections offset"); 324 | 325 | auto sections = reinterpret_cast(reinterpret_cast(mapping.GetAddress()) + sectionOffset); 326 | 327 | for (auto i = 0; i < _header->FileHeader.NumberOfSections; i++) 328 | { 329 | //TODO: alignment for offset and size 330 | SectionRegion region; 331 | region.rawOffset = sections[i].PointerToRawData; 332 | region.rawSize = sections[i].SizeOfRawData; 333 | region.virtualOffset = sections[i].VirtualAddress; 334 | region.virtualSize = sections[i].Misc.VirtualSize; 335 | _sections.push_back(region); 336 | } 337 | } 338 | 339 | template 340 | ImportTable ImageImpl::LoadImportTable() 341 | { 342 | ImportTable table; 343 | auto imageSize = _mapping.GetSize(); 344 | auto importDirOffset = _header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 345 | if (!importDirOffset) 346 | return table; 347 | 348 | auto imports = reinterpret_cast(Image::GetAddressByRVA(importDirOffset)); 349 | DWORD importPeakOffset = importDirOffset + sizeof(IMAGE_IMPORT_DESCRIPTOR); 350 | for (int i = 0; true; i++, importPeakOffset += sizeof(IMAGE_IMPORT_DESCRIPTOR)) 351 | { 352 | if (importPeakOffset > imageSize) 353 | throw Utils::Exception(L"Invalid import descriptor table"); 354 | 355 | if (!imports[i].Characteristics) 356 | break; 357 | 358 | std::string dllname = Image::LoadStringByRVA(imports[i].Name); 359 | table.push_back(dllname); 360 | } 361 | 362 | return table; 363 | } 364 | 365 | template 366 | ImportTable ImageImpl::LoadDelayImportTable() 367 | { 368 | ImportTable table; 369 | auto imageSize = _mapping.GetSize(); 370 | auto delayDirOffset = _header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress; 371 | if (!delayDirOffset) 372 | return table; 373 | 374 | auto imports = reinterpret_cast(Image::GetAddressByRVA(delayDirOffset)); 375 | DWORD importPeakOffset = delayDirOffset + sizeof(IMAGE_DELAYLOAD_DESCRIPTOR); 376 | for (int i = 0; true; i++, importPeakOffset += sizeof(IMAGE_IMPORT_DESCRIPTOR)) 377 | { 378 | if (importPeakOffset > imageSize) 379 | throw Utils::Exception(L"Invalid import descriptor table"); 380 | 381 | if (!imports[i].DllNameRVA) 382 | break; 383 | 384 | std::string dllname = Image::LoadStringByRVA(imports[i].DllNameRVA); 385 | table.push_back(dllname); 386 | } 387 | 388 | return table; 389 | } 390 | 391 | template 392 | Resources ImageImpl::LoadResources() 393 | { 394 | Resources resources; 395 | auto resourceDirOffset = _header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; 396 | if (!resourceDirOffset) 397 | return resources; 398 | 399 | ResourceDirectory directory; 400 | LoadResourceDirectory(resourceDirOffset, resourceDirOffset, directory); 401 | return Resources(directory); 402 | } 403 | 404 | template 405 | void ImageImpl::LoadResourceDirectory(DWORD baseOffset, DWORD dirOffset, ResourceDirectory& parent) 406 | { 407 | //TODO: - add protection against a resource recursion 408 | // - simplify this routine 409 | auto imageSize = _mapping.GetSize(); 410 | 411 | auto peakOffset = dirOffset + sizeof(IMAGE_RESOURCE_DIRECTORY); 412 | if (peakOffset > imageSize) 413 | throw Utils::Exception(L"Resource table out of range"); 414 | 415 | auto dir = reinterpret_cast(Image::GetAddressByRVA(dirOffset)); 416 | 417 | auto loadEntries = [&](size_t count) 418 | { 419 | auto entries = reinterpret_cast(Image::GetAddressByRVA(peakOffset)); 420 | peakOffset += count * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY); 421 | if (peakOffset > imageSize) 422 | throw Utils::Exception(L"Resource entries table out of range"); 423 | 424 | return entries; 425 | }; 426 | 427 | auto names = loadEntries(dir->NumberOfNamedEntries); 428 | auto ids = loadEntries(dir->NumberOfIdEntries); 429 | 430 | for (int i = 0; i < dir->NumberOfNamedEntries; i++) 431 | { 432 | if (!names[i].NameIsString) 433 | continue; 434 | 435 | auto loadName = [&](DWORD offset) 436 | { 437 | auto str = reinterpret_cast(Image::GetAddressByRVA(baseOffset + offset)); 438 | 439 | offset += baseOffset + sizeof(WORD); 440 | if (offset > imageSize) 441 | throw Utils::Exception(L"Resource entries table out of range"); 442 | 443 | if (offset + (str->Length * sizeof(wchar_t)) > imageSize) 444 | throw Utils::Exception(L"Resource entries table out of range"); 445 | 446 | return std::wstring(str->NameString, str->NameString + str->Length); 447 | }; 448 | 449 | auto name = loadName(names[i].NameOffset); 450 | 451 | if (names[i].DataIsDirectory) 452 | { 453 | ResourceDirectory child(name); 454 | LoadResourceDirectory(baseOffset, baseOffset + names[i].OffsetToDirectory, child); 455 | parent.Push(child); 456 | } 457 | else 458 | { 459 | ResourceData data(name, baseOffset + names[i].OffsetToData, _mapping); 460 | parent.Push(data); 461 | } 462 | } 463 | 464 | for (int i = 0; i < dir->NumberOfIdEntries; i++) 465 | { 466 | if (ids[i].NameIsString) 467 | continue; 468 | 469 | if (ids[i].DataIsDirectory) 470 | { 471 | ResourceDirectory child(ids[i].Id); 472 | LoadResourceDirectory(baseOffset, baseOffset + ids[i].OffsetToDirectory, child); 473 | parent.Push(child); 474 | } 475 | else 476 | { 477 | ResourceData data(ids[i].Id, baseOffset + ids[i].OffsetToData, _mapping); 478 | parent.Push(data); 479 | } 480 | } 481 | } 482 | 483 | } 484 | -------------------------------------------------------------------------------- /hijacken/Commands.cpp: -------------------------------------------------------------------------------- 1 | #include "Commands.h" 2 | #include 3 | 4 | namespace Commands 5 | { 6 | // ================= 7 | 8 | ImpersonationOptions::ImpersonationOptions() : 9 | _tokenSourceProcessId(::GetCurrentProcessId()), 10 | _tokenIntegrityLevel(System::IntegrityLevel::Untrusted), 11 | _changeIntegrityLevel(false) 12 | { 13 | } 14 | 15 | void ImpersonationOptions::LoadArgs(Utils::Arguments& args) 16 | { 17 | std::wstring command; 18 | 19 | if (!args.Probe(command)) 20 | return; 21 | 22 | if (command == L"/impersonate") 23 | { 24 | args.SwitchToNext(); 25 | 26 | if (!args.GetNext(command)) 27 | throw Utils::Exception(L"Invalid impersonation argument '%s'", command.c_str()); 28 | 29 | _tokenSourceProcessId = _wtoi(command.c_str()); 30 | 31 | if (!args.Probe(command)) 32 | return; 33 | } 34 | 35 | if (command == L"/integrity") 36 | { 37 | args.SwitchToNext(); 38 | 39 | if (!args.GetNext(command)) 40 | throw Utils::Exception(L"Invalid impersonation argument '%s'", command.c_str()); 41 | 42 | _tokenIntegrityLevel = ConvertStrToIntegrityLevel(command); 43 | _changeIntegrityLevel = true; 44 | } 45 | } 46 | 47 | System::ImpersonateTokenPtr ImpersonationOptions::CraftToken() 48 | { 49 | System::Process target(_tokenSourceProcessId, PROCESS_QUERY_INFORMATION); 50 | auto token = System::ImpersonateTokenPtr(new System::ImpersonateToken(target, TOKEN_ADJUST_DEFAULT | TOKEN_QUERY)); 51 | 52 | if (_changeIntegrityLevel) 53 | ChangeIntegrity(token, _tokenIntegrityLevel); 54 | 55 | return token; 56 | } 57 | 58 | void ImpersonationOptions::ChangeIntegrity(System::ImpersonateTokenPtr& token, System::IntegrityLevel expectedIntegrity) 59 | { 60 | auto currentIntegrity = token->GetIntegrityLevel(); 61 | 62 | if (currentIntegrity == expectedIntegrity) 63 | return; 64 | 65 | if (currentIntegrity < expectedIntegrity) 66 | throw Utils::Exception(L"Access token can't be elevated to higher integrity level"); 67 | 68 | if (currentIntegrity == System::IntegrityLevel::High) 69 | { 70 | token.reset(new System::ImpersonateToken(token->GetLinkedToken())); 71 | currentIntegrity = token->GetIntegrityLevel(); 72 | if (currentIntegrity == System::IntegrityLevel::High) 73 | throw Utils::Exception(L"Attempt to elevate token has been failed"); 74 | } 75 | 76 | if (currentIntegrity != expectedIntegrity) 77 | token->SetIntegrityLevel(expectedIntegrity); 78 | 79 | if (token->GetIntegrityLevel() == expectedIntegrity) 80 | return; 81 | 82 | throw Utils::Exception(L"Attempt to prepare token with specific integrity level has been failed"); 83 | } 84 | 85 | System::IntegrityLevel ImpersonationOptions::ConvertStrToIntegrityLevel(std::wstring& level) 86 | { 87 | auto integrity = System::IntegrityLevel::Untrusted; 88 | 89 | if (level == L"medium") 90 | integrity = System::IntegrityLevel::Medium; 91 | else if (level == L"low") 92 | integrity = System::IntegrityLevel::Low; 93 | else if (level == L"untrusted") 94 | integrity = System::IntegrityLevel::Untrusted; 95 | else 96 | throw Utils::Exception(L"Unknown integrity level '%d'", integrity); 97 | 98 | return integrity; 99 | } 100 | 101 | void ImpersonationOptions::PrintTokenInformation(System::ImpersonateTokenPtr& token) 102 | { 103 | std::wstring str; 104 | 105 | std::wcout << L"Access token information:" << std::endl; 106 | token->GetUserNameString(str); 107 | std::wcout << L" " << str << std::endl; 108 | token->GetUserSIDString(str); 109 | std::wcout << L" " << str << std::endl; 110 | std::wcout << L" Integrity: " << ConvertIntegrityLevelToString(token->GetIntegrityLevel()) 111 | << L", " << (token->IsElevated() ? L"Elevated" : L"Not-Elevated") << std::endl; 112 | std::wcout << std::endl; 113 | } 114 | 115 | const wchar_t* ImpersonationOptions::ConvertIntegrityLevelToString(System::IntegrityLevel level) 116 | { 117 | switch (level) 118 | { 119 | case System::IntegrityLevel::Untrusted: 120 | return L"Untrusted"; 121 | case System::IntegrityLevel::Low: 122 | return L"Low"; 123 | case System::IntegrityLevel::Medium: 124 | return L"Medium"; 125 | case System::IntegrityLevel::MediumPlus: 126 | return L"MediumPlus"; 127 | case System::IntegrityLevel::High: 128 | return L"High"; 129 | case System::IntegrityLevel::System: 130 | return L"System"; 131 | case System::IntegrityLevel::Protected: 132 | return L"Protected"; 133 | case System::IntegrityLevel::Secure: 134 | return L"Secure"; 135 | default: 136 | break; 137 | } 138 | return L"Unknown"; 139 | } 140 | 141 | // ================= 142 | 143 | SystemOptions::SystemOptions() : 144 | _scanElevated(false) 145 | { 146 | } 147 | 148 | void SystemOptions::LoadArgs(Utils::Arguments& args) 149 | { 150 | std::wstring command; 151 | 152 | if (!args.Probe(command)) 153 | return; 154 | 155 | if (command == L"/elevation") 156 | { 157 | _scanElevated = true; 158 | args.SwitchToNext(); 159 | } 160 | } 161 | 162 | bool SystemOptions::ShouldScanProcess(System::ImpersonateTokenPtr& token, DWORD targetProcessId) 163 | { 164 | if (!_scanElevated) 165 | return true; 166 | 167 | System::Process process(targetProcessId, PROCESS_QUERY_INFORMATION); 168 | System::ImpersonateToken targetToken(process); 169 | 170 | auto sourceIntegrity = token->GetIntegrityLevel(); 171 | auto targetIntegrity = targetToken.GetIntegrityLevel(); 172 | 173 | return (sourceIntegrity < targetIntegrity); 174 | } 175 | 176 | // ================= 177 | 178 | EnvironmentOptions::EnvironmentOptions() : 179 | _envSource(EnvironmentSource::User), 180 | _sourceProcessId(0) 181 | { 182 | } 183 | 184 | void EnvironmentOptions::LoadArgs(Utils::Arguments& args) 185 | { 186 | std::wstring command; 187 | 188 | if (!args.Probe(command)) 189 | return; 190 | 191 | if (command != L"/environment") 192 | return; 193 | 194 | args.SwitchToNext(); 195 | 196 | if (!args.GetNext(command)) 197 | return; 198 | 199 | if (command == L"system") 200 | { 201 | _envSource = EnvironmentSource::System; 202 | } 203 | else if (command == L"user") 204 | { 205 | _envSource = EnvironmentSource::User; 206 | } 207 | else if (command == L"process") 208 | { 209 | std::wstring param; 210 | 211 | if (!args.GetNext(param)) 212 | throw Utils::Exception(L"Not enough arguments");//TODO 213 | 214 | _sourceProcessId = _wtoi(param.c_str()); 215 | _envSource = EnvironmentSource::Process; 216 | } 217 | else if (command == L"inherit") 218 | { 219 | _envSource = EnvironmentSource::Inherit; 220 | } 221 | else if (command == L"off") 222 | { 223 | _envSource = EnvironmentSource::Off; 224 | } 225 | else 226 | { 227 | throw Utils::Exception(L"Invalid environment argument '%s'", command.c_str()); 228 | } 229 | } 230 | 231 | System::EnvironmentVariablesPtr EnvironmentOptions::GetEnvironment() 232 | { 233 | System::EnvironmentVariablesPtr env; 234 | 235 | if (_envSource == EnvironmentSource::User) 236 | { 237 | System::EnumRegistryValues user(System::BaseKeys::CurrentUser, L"Environment"); 238 | System::EnumRegistryValues system( 239 | System::BaseKeys::LocalMachine, 240 | L"System\\CurrentControlSet\\Control\\Session Manager\\Environment" 241 | ); 242 | 243 | auto vars = user.GetValues(); 244 | auto& sysVars = system.GetValues(); 245 | vars.insert(sysVars.begin(), sysVars.end()); 246 | env.reset(new System::EnvironmentVariables(vars)); 247 | } 248 | else if (_envSource == EnvironmentSource::System) 249 | { 250 | System::EnumRegistryValues system( 251 | System::BaseKeys::LocalMachine, 252 | L"System\\CurrentControlSet\\Control\\Session Manager\\Environment" 253 | ); 254 | env.reset(new System::EnvironmentVariables(system.GetValues())); 255 | } 256 | else if (_envSource == EnvironmentSource::Process) 257 | { 258 | System::ProcessInformation info(_sourceProcessId); 259 | auto peb = info.GetProcessEnvironmentBlock(); 260 | env = peb->GetProcessEnvironment(); 261 | } 262 | else if (_envSource == EnvironmentSource::Inherit) 263 | { 264 | env = System::ProcessInformation::GetCurrentEnvironmentVariables(); 265 | } 266 | else if (_envSource == EnvironmentSource::Off) 267 | { 268 | env.reset(new System::EnvironmentVariables()); 269 | } 270 | 271 | return env; 272 | } 273 | 274 | // ================= 275 | 276 | ScanFile::ScanFile() : 277 | _unwindImports(true), 278 | _scanDelayLoad(true), 279 | _checkAccess(false), 280 | _firstFound(false) 281 | { 282 | } 283 | 284 | void ScanFile::LoadArgs(Utils::Arguments& args) 285 | { 286 | std::wstring option; 287 | 288 | if (!args.Probe(option)) 289 | throw Utils::Exception(L"Not enough arguments"); 290 | 291 | if (option == L"/nounwinding") 292 | { 293 | _unwindImports = false; 294 | 295 | args.SwitchToNext(); 296 | if (!args.Probe(option)) 297 | throw Utils::Exception(L"Not enough arguments"); 298 | } 299 | 300 | if (option == L"/nodelay") 301 | { 302 | _scanDelayLoad = false; 303 | 304 | args.SwitchToNext(); 305 | if (!args.Probe(option)) 306 | throw Utils::Exception(L"Not enough arguments"); 307 | } 308 | 309 | if (option == L"/accessible") 310 | { 311 | _checkAccess = true; 312 | 313 | args.SwitchToNext(); 314 | ImpersonationOptions::LoadArgs(args); 315 | } 316 | 317 | EnvironmentOptions::LoadArgs(args); 318 | 319 | if (!args.GetNext(_filePath)) 320 | throw Utils::Exception(L"Not enough arguments"); 321 | } 322 | 323 | void ScanFile::Perform() 324 | { 325 | auto token = ImpersonationOptions::CraftToken(); 326 | System::TokenAccessChecker access(*token); 327 | 328 | auto env = EnvironmentOptions::GetEnvironment(); 329 | 330 | Engine::ImageScanEngine::SetOptionUnwindImport(_unwindImports); 331 | Engine::ImageScanEngine::SetOptionUnwindDelayLoadImport(_scanDelayLoad); 332 | Engine::ImageScanEngine::SetOptionAccessibleOnly(_checkAccess); 333 | 334 | Engine::ImageScanEngine::Scan(_filePath, *env, access); 335 | } 336 | 337 | void ScanFile::NotifyLoadImageOrder(Engine::LoadImageOrder& dirs) 338 | { 339 | std::wcout << std::endl; 340 | std::wcout << L"Safe search: " << (Engine::LoadImageOrder::IsSafeSearchEnabled() ? L"enabled" : L"disabled") << std::endl; 341 | std::wcout << L"Expected image load order:" << std::endl; 342 | 343 | int i = 0; 344 | for (auto& dir : dirs.GetOrder()) 345 | std::wcout << L" " << ++i << L". " << ConvertImageDirStateToString(dir) << " [" << ConvertImageDirTypeToString(dir.GetType()) << "] " << dir.GetPath() << std::endl; 346 | 347 | std::wcout << std::endl; 348 | 349 | std::wcout << L" * attributes meaning:" << std::endl; 350 | std::wcout << L" N - directory doesn't exist" << std::endl; 351 | std::wcout << L" A - directory existing and accessible" << std::endl; 352 | std::wcout << L" O - a name of the directory is owned by a file, it should break an image loading when loader meets a file instead of directory" << std::endl; 353 | 354 | std::wcout << std::endl; 355 | } 356 | 357 | void ScanFile::NotifyVulnerableDll(Engine::ImageDirectory& dir, std::wstring& dll, bool writtable, std::vector& vulnDirs) 358 | { 359 | if (!_firstFound) 360 | { 361 | _firstFound = true; 362 | std::wcout << L"Vulnerable DLLs:" << std::endl << std::endl; 363 | } 364 | 365 | std::wcout << L" " << dll << (writtable ? L", is writable" : L"") << std::endl; 366 | std::wcout << L" Location: " << (dir.GetType() != Engine::ImageDirectory::Type::Unknown ? dir.GetPath() : L"not found") << std::endl; 367 | std::wcout << L" Type: " << ConvertImageDirTypeToString(dir.GetType()) << std::endl; 368 | 369 | if (dir.GetType() != Engine::ImageDirectory::Type::Base) 370 | { 371 | std::wcout << L" Vulnerable dirs:" << std::endl; 372 | 373 | int i = 0; 374 | for (auto& vulnDir : vulnDirs) 375 | std::wcout << L" " << ++i << L". [" << ConvertImageDirTypeToString(vulnDir->GetType()) << "] " << vulnDir->GetPath() << std::endl; 376 | } 377 | 378 | std::wcout << std::endl; 379 | } 380 | 381 | void ScanFile::NotifyVulnerableSxSDll(Engine::ImageDirectory& dir, std::wstring& dll, bool writtable) 382 | { 383 | if (!_firstFound) 384 | { 385 | _firstFound = true; 386 | std::wcout << L"Vulnerable DLLs:" << std::endl << std::endl; 387 | } 388 | 389 | std::wcout << L" " << dll << (writtable ? L", is writable" : L"") << std::endl; 390 | std::wcout << L" Type: " << ConvertImageDirTypeToString(dir.GetType()) << std::endl; 391 | std::wcout << L" Vulnerable dir: " << std::endl << L" " << dir.GetPath() << std::endl; 392 | std::wcout << std::endl; 393 | } 394 | 395 | const wchar_t* ScanFile::ConvertImageDirTypeToString(Engine::ImageDirectory::Type type) 396 | { 397 | switch (type) 398 | { 399 | case Engine::ImageDirectory::Type::Base: 400 | return L"Base"; 401 | case Engine::ImageDirectory::Type::System32: 402 | return L"System32"; 403 | case Engine::ImageDirectory::Type::System: 404 | return L"System"; 405 | case Engine::ImageDirectory::Type::Windows: 406 | return L"Windows"; 407 | case Engine::ImageDirectory::Type::Current: 408 | return L"Current"; 409 | case Engine::ImageDirectory::Type::Environment: 410 | return L"Environment"; 411 | case Engine::ImageDirectory::Type::FullPath: 412 | return L"FullPath"; 413 | case Engine::ImageDirectory::Type::SxS: 414 | return L"Side-by-Side"; 415 | default: 416 | break; 417 | } 418 | return L"Unknown"; 419 | } 420 | 421 | const wchar_t* ScanFile::ConvertImageDirStateToString(const Engine::ImageDirectory& dir) 422 | { 423 | auto state = dir.GetState(); 424 | if (state == Engine::ImageDirectory::State::NotExisting) 425 | return L"N"; 426 | else if (state == Engine::ImageDirectory::State::Overlapped) 427 | return L"O"; 428 | else if (state == Engine::ImageDirectory::State::Existing && dir.IsAccessible()) 429 | return L"A"; 430 | 431 | return L" "; 432 | } 433 | 434 | // ================= 435 | 436 | ScanDirectory::ScanDirectory() 437 | { 438 | } 439 | 440 | void ScanDirectory::LoadArgs(Utils::Arguments& args) 441 | { 442 | } 443 | 444 | void ScanDirectory::Perform() 445 | { 446 | } 447 | 448 | // ================= 449 | 450 | ScanProcess::ScanProcess() : _targetProcessId(0) 451 | { 452 | System::Process self(::GetCurrentProcess()); 453 | System::PrimaryToken token(self); 454 | token.SetPrivilege(L"SeDebugPrivilege", true); 455 | } 456 | 457 | void ScanProcess::LoadArgs(Utils::Arguments& args) 458 | { 459 | std::wstring command; 460 | 461 | if (!args.GetNext(command)) 462 | throw Utils::Exception(L"Invalid command argument '%s'", command.c_str()); 463 | 464 | _targetProcessId = _wtoi(command.c_str()); 465 | 466 | ImpersonationOptions::LoadArgs(args); 467 | 468 | if (!args.IsEnded()) 469 | throw Utils::Exception(L"Too much arguments"); 470 | } 471 | 472 | void ScanProcess::Perform() 473 | { 474 | auto token = ImpersonationOptions::CraftToken(); 475 | System::TokenAccessChecker access(*token); 476 | 477 | ImpersonationOptions::PrintTokenInformation(token); 478 | 479 | Engine::ProcessScanEngine::Scan(_targetProcessId, access); 480 | 481 | for (auto& detection : _detectedDirs) 482 | { 483 | std::wcout << ConvertDirDetectionToString(detection.first) << L":" << std::endl; 484 | for (auto& dir : detection.second) 485 | std::wcout << L" " << dir << std::endl; 486 | } 487 | 488 | for (auto& detection : _detectedFiles) 489 | { 490 | std::wcout << ConvertFileDetectionToString(detection.first) << L":" << std::endl; 491 | for (auto& file : detection.second) 492 | std::wcout << L" " << file << std::endl; 493 | } 494 | } 495 | 496 | void ScanProcess::NotifyWritableDirectory(DetectionDirType detection, std::wstring& dirPath) 497 | { 498 | _detectedDirs[detection].insert(dirPath); 499 | } 500 | 501 | void ScanProcess::NotifyWritableFile(DetectionFileType detection, std::wstring& filePath) 502 | { 503 | _detectedFiles[detection].insert(filePath); 504 | } 505 | 506 | // ================= 507 | 508 | ScanProcesses::ScanProcesses() 509 | { 510 | System::Process self(::GetCurrentProcess()); 511 | System::PrimaryToken token(self); 512 | token.SetPrivilege(L"SeDebugPrivilege", true); 513 | 514 | if (token.GetIntegrityLevel() < System::IntegrityLevel::High) 515 | std::wcout << std::endl 516 | << L" Warning! Hijacken has been run without administrator rights." << std::endl 517 | << L" Therefore scan is limited to an accessible scope of processes." << std::endl 518 | << std::endl; 519 | } 520 | 521 | void ScanProcesses::LoadArgs(Utils::Arguments& args) 522 | { 523 | SystemOptions::LoadArgs(args); 524 | ImpersonationOptions::LoadArgs(args); 525 | 526 | if (!args.IsEnded()) 527 | throw Utils::Exception(L"Too much arguments"); 528 | } 529 | 530 | void ScanProcesses::Perform() 531 | { 532 | auto token = ImpersonationOptions::CraftToken(); 533 | System::TokenAccessChecker access(*token); 534 | 535 | ImpersonationOptions::PrintTokenInformation(token); 536 | 537 | std::wcout << L"==============" << std::endl; 538 | std::wcout << L" FINDINGS" << std::endl; 539 | std::wcout << L"==============" << std::endl << std::endl; 540 | 541 | DWORD processId; 542 | std::wstring processName; 543 | System::ProcessesSnapshot snapshot; 544 | while (snapshot.GetNextProcess(processId, processName)) 545 | { 546 | enum { IdlePID = 0, SystemPID = 4 }; 547 | 548 | if (processId == IdlePID || processId == SystemPID) 549 | continue; 550 | 551 | try 552 | { 553 | if (!SystemOptions::ShouldScanProcess(token, processId)) 554 | continue; 555 | 556 | Engine::ProcessScanEngine::Scan(processId, access); 557 | 558 | if (!_detectedDirs.empty() || !_detectedFiles.empty()) 559 | { 560 | std::wcout << L"Process " << processId << L", " << processName << std::endl; 561 | 562 | for (auto& detection : _detectedDirs) 563 | { 564 | std::wcout << L" " << Engine::ProcessScanEngine::ConvertDirDetectionToString(detection.first) << L":" << std::endl; 565 | for (auto& dir : detection.second) 566 | std::wcout << L" " << dir << std::endl; 567 | } 568 | 569 | for (auto& detection : _detectedFiles) 570 | { 571 | std::wcout << L" " << Engine::ProcessScanEngine::ConvertFileDetectionToString(detection.first) << L":" << std::endl; 572 | for (auto& file : detection.second) 573 | std::wcout << L" " << file << std::endl; 574 | } 575 | 576 | std::wcout << std::endl; 577 | } 578 | } 579 | catch (Utils::Exception&) 580 | { 581 | //std::wcout << L"Process with ID " << processId << L" has been skipped, reason: " << exception.GetMessage() << std::endl; 582 | } 583 | 584 | _detectedDirs.clear(); 585 | _detectedFiles.clear(); 586 | } 587 | } 588 | 589 | void ScanProcesses::NotifyWritableDirectory(DetectionDirType detection, std::wstring& dirPath) 590 | { 591 | _detectedDirs[detection].insert(dirPath); 592 | } 593 | 594 | void ScanProcesses::NotifyWritableFile(DetectionFileType detection, std::wstring& filePath) 595 | { 596 | _detectedFiles[detection].insert(filePath); 597 | } 598 | 599 | // ================= 600 | 601 | ScanAutorun::ScanAutorun() 602 | { 603 | } 604 | 605 | void ScanAutorun::LoadArgs(Utils::Arguments& args) 606 | { 607 | } 608 | 609 | void ScanAutorun::Perform() 610 | { 611 | } 612 | 613 | // ================= 614 | 615 | ScanTask::ScanTask() 616 | { 617 | } 618 | 619 | void ScanTask::LoadArgs(Utils::Arguments& args) 620 | { 621 | } 622 | 623 | void ScanTask::Perform() 624 | { 625 | } 626 | 627 | // ================= 628 | 629 | ScanTasks::ScanTasks() 630 | { 631 | } 632 | 633 | void ScanTasks::LoadArgs(Utils::Arguments& args) 634 | { 635 | } 636 | 637 | void ScanTasks::Perform() 638 | { 639 | } 640 | 641 | // ================= 642 | 643 | ScanService::ScanService() 644 | { 645 | } 646 | 647 | void ScanService::LoadArgs(Utils::Arguments& args) 648 | { 649 | } 650 | 651 | void ScanService::Perform() 652 | { 653 | } 654 | 655 | // ================= 656 | 657 | ScanServices::ScanServices() 658 | { 659 | } 660 | 661 | void ScanServices::LoadArgs(Utils::Arguments& args) 662 | { 663 | } 664 | 665 | void ScanServices::Perform() 666 | { 667 | } 668 | 669 | // ================= 670 | 671 | ScanSystem::ScanSystem() 672 | { 673 | } 674 | 675 | void ScanSystem::LoadArgs(Utils::Arguments& args) 676 | { 677 | } 678 | 679 | void ScanSystem::Perform() 680 | { 681 | } 682 | 683 | // ================= 684 | 685 | MakeDll::MakeDll() 686 | { 687 | } 688 | 689 | void MakeDll::LoadArgs(Utils::Arguments& args) 690 | { 691 | } 692 | 693 | void MakeDll::Perform() 694 | { 695 | } 696 | }; -------------------------------------------------------------------------------- /hijacken/ImageScan.cpp: -------------------------------------------------------------------------------- 1 | #include "ImageScan.h" 2 | #include 3 | #include 4 | 5 | namespace Engine 6 | { 7 | // ================= 8 | 9 | ImageDirectory::ImageDirectory() : 10 | _type(ImageDirectory::Type::Unknown), 11 | _accessible(false), 12 | _state(State::NotExisting) 13 | { 14 | } 15 | 16 | ImageDirectory::ImageDirectory(Type type, const std::wstring& imageDir, const System::TokenAccessChecker& access) : 17 | _directory(imageDir), 18 | _accessible(false), 19 | _type(type), 20 | _state(State::Existing) 21 | { 22 | if (!System::FileUtils::PathExists(_directory.c_str())) 23 | { 24 | _state = State::NotExisting; 25 | return; 26 | } 27 | 28 | if (!System::Directory::IsDirectory(_directory.c_str())) 29 | _state = State::Overlapped; 30 | 31 | System::Directory directory(_directory.c_str()); 32 | System::SecurityDescriptor descriptor(directory); 33 | _accessible = access.IsFileObjectAccessible(descriptor, FILE_ADD_FILE); 34 | } 35 | 36 | bool ImageDirectory::operator==(const ImageDirectory& compared) const 37 | { 38 | if (_type != compared._type) 39 | return false; 40 | 41 | if (_state != compared._state) 42 | return false; 43 | 44 | if (_accessible != compared._accessible) 45 | return false; 46 | 47 | if (_directory != compared._directory) 48 | return false; 49 | 50 | return true; 51 | } 52 | 53 | const std::wstring& ImageDirectory::GetPath() const 54 | { 55 | return _directory; 56 | } 57 | 58 | ImageDirectory::Type ImageDirectory::GetType() const 59 | { 60 | return _type; 61 | } 62 | 63 | ImageDirectory::State ImageDirectory::GetState() const 64 | { 65 | return _state; 66 | } 67 | 68 | bool ImageDirectory::IsAccessible() const 69 | { 70 | return _accessible; 71 | } 72 | 73 | // ================= 74 | 75 | LoadImageOrder::LoadImageOrder(const std::wstring& imageDir, const std::wstring& currentDir, const System::EnvironmentVariables& envVars, const System::TokenAccessChecker& access) : 76 | _wow64mode(false) 77 | { 78 | auto supportWow64 = (System::SystemInformation::GetSystemBitness() == System::Bitness::Arch64); 79 | bool safeSearch = IsSafeSearchEnabled(); 80 | 81 | auto putDirectory = [&](ImageDirectory::Type type, const std::wstring& dir) 82 | { 83 | _order.emplace_back(type, dir, access); 84 | if (supportWow64) 85 | _orderWow64.emplace_back(type, dir, access); 86 | }; 87 | 88 | // Base image dir 89 | putDirectory(ImageDirectory::Type::Base, imageDir); 90 | 91 | // Current dir 92 | if (!safeSearch) 93 | putDirectory(ImageDirectory::Type::Current, imageDir); 94 | 95 | // System32 and SysWOW64 dirs 96 | _order.emplace_back( 97 | ImageDirectory::Type::System32, 98 | System::SystemInformation::GetSystem32Dir(), 99 | access 100 | ); 101 | if (supportWow64) 102 | _orderWow64.emplace_back( 103 | ImageDirectory::Type::System32, 104 | System::SystemInformation::GetSysWow64Dir(), 105 | access 106 | ); 107 | 108 | // System dir 109 | putDirectory(ImageDirectory::Type::System, System::SystemInformation::GetSystemDir()); 110 | 111 | // Windows dir 112 | putDirectory(ImageDirectory::Type::Windows, System::SystemInformation::GetWindowsDir()); 113 | 114 | // Current dir 115 | if (safeSearch) 116 | putDirectory(ImageDirectory::Type::Current, imageDir); 117 | 118 | // Environment dirs 119 | LoadEnvironmentVariables(envVars, supportWow64, access); 120 | } 121 | 122 | void LoadImageOrder::SetWow64Mode(bool value) 123 | { 124 | _wow64mode = value; 125 | } 126 | 127 | const ImageDirectories& LoadImageOrder::GetOrder() const 128 | { 129 | if (_wow64mode) 130 | return _orderWow64; 131 | 132 | return _order; 133 | } 134 | 135 | const ImageDirectory& LoadImageOrder::GetBaseDir() const 136 | { 137 | for (const auto& dir : _order) 138 | if (dir.GetType() == ImageDirectory::Type::Base) 139 | return dir; 140 | 141 | throw Utils::Exception(L"Base dir hasn't been found"); 142 | } 143 | 144 | bool LoadImageOrder::IsSafeSearchEnabled() 145 | { 146 | //TODO: seems like when value isn't present than system think that SafeSearch is enabled, 147 | // needs to be clarified 148 | bool enabled = true; 149 | 150 | try 151 | { 152 | System::RegistryKey key( 153 | System::BaseKeys::LocalMachine, 154 | L"System\\CurrentControlSet\\Control\\Session Manager" 155 | ); 156 | System::RegistryDwordValue value(key, L"SafeDllSearchMode"); 157 | enabled = (value.GetValue() != 0); 158 | } 159 | catch (...) 160 | { 161 | } 162 | 163 | return enabled; 164 | } 165 | 166 | void LoadImageOrder::LoadEnvironmentVariables(const System::EnvironmentVariables& envVars, bool wow64mode, const System::TokenAccessChecker& access) 167 | { 168 | std::wstring rawPaths; 169 | 170 | if (!envVars.GetValue(L"Path", rawPaths) && !envVars.GetValue(L"PATH", rawPaths) && !envVars.GetValue(L"path", rawPaths)) 171 | return; 172 | 173 | Utils::SeparatedStrings paths(rawPaths, L';'); 174 | 175 | for (auto& dir : paths) 176 | { 177 | if (dir.empty()) 178 | continue; 179 | 180 | if (*dir.rbegin() == L'\\') 181 | dir.resize(dir.size() - 1); 182 | 183 | _order.emplace_back(ImageDirectory::Type::Environment, dir, access); 184 | if (wow64mode) 185 | _orderWow64.emplace_back( 186 | ImageDirectory::Type::Environment, 187 | System::FileUtils::ApplyWow64Redirection(dir), 188 | access 189 | ); 190 | } 191 | 192 | return; 193 | } 194 | 195 | // ================= 196 | 197 | ImageScanOrder::ImageScanOrder(const std::wstring& imageDir, const std::wstring& currentDir, const System::EnvironmentVariables& envVars, const System::TokenAccessChecker& access) : 198 | LoadImageOrder(imageDir, currentDir, envVars, access) 199 | { 200 | } 201 | 202 | ImageDirectory ImageScanOrder::FindDllDirectory(const std::wstring& dllname) const 203 | { 204 | auto dirs = LoadImageOrder::GetOrder(); 205 | 206 | for (auto& dir : dirs) 207 | if (DirContainsDll(dllname, dir)) 208 | return dir; 209 | 210 | return ImageDirectory(); 211 | } 212 | 213 | bool ImageScanOrder::DirContainsDll(const std::wstring& dllname, ImageDirectory& dir) const 214 | { 215 | auto path = System::FileUtils::BuildPath( 216 | dir.GetPath(), 217 | dllname 218 | ); 219 | 220 | return System::FileUtils::PathExists(path.c_str()); 221 | } 222 | 223 | // ================= 224 | 225 | KnownDlls::KnownDlls() 226 | { 227 | _supportWow64 = (System::SystemInformation::GetSystemBitness() == System::Bitness::Arch64); 228 | 229 | System::EnumRegistryValues knowndlls( 230 | System::BaseKeys::LocalMachine, 231 | L"System\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs" 232 | ); 233 | auto& values = knowndlls.GetValues(); 234 | 235 | LoadExcludedDlls(); 236 | 237 | auto loadKnownDll = [&](const std::wstring& dll) 238 | { 239 | if (_excluded.Contain(dll)) 240 | return; 241 | 242 | if (_known.InsertOnlyNew(dll)) 243 | UnwindImports(dll, false); 244 | 245 | if (_supportWow64 && _knownWow64.InsertOnlyNew(dll)) 246 | UnwindImports(dll, true); 247 | }; 248 | 249 | loadKnownDll(L"ntdll.dll"); 250 | loadKnownDll(L"kernel32.dll"); 251 | loadKnownDll(L"kernelbase.dll"); 252 | 253 | for (auto& value : values) 254 | { 255 | if (value.first == L"DllDirectory" || value.first == L"DllDirectory32") 256 | continue; 257 | 258 | if (value.second.GetType() != System::RegistryValueType::String) 259 | continue; 260 | 261 | auto dllName = std::wstring(value.second.GetValue().c_str()); 262 | std::transform(dllName.begin(), dllName.end(), dllName.begin(), towlower); 263 | loadKnownDll(dllName); 264 | } 265 | } 266 | 267 | bool KnownDlls::Contain(std::wstring& dllName, System::Bitness bitness) 268 | { 269 | return _known.Contain(dllName); 270 | } 271 | 272 | void KnownDlls::LoadExcludedDlls() 273 | { 274 | try 275 | { 276 | System::RegistryKey key( 277 | System::BaseKeys::LocalMachine, 278 | L"System\\CurrentControlSet\\Control\\Session Manager" 279 | ); 280 | System::RegistryMultiStringValue excludedList(key, L"ExcludeFromKnownDlls"); 281 | 282 | for (auto& dllName : excludedList) 283 | { 284 | std::transform(dllName.begin(), dllName.end(), dllName.begin(), towlower); 285 | _excluded.InsertOnlyNew(dllName); 286 | } 287 | } 288 | catch (...) 289 | { 290 | } 291 | } 292 | 293 | void KnownDlls::UnwindImports(const std::wstring& dllName, bool wow64mode) 294 | { 295 | auto dllPath = System::FileUtils::BuildPath( 296 | wow64mode ? 297 | System::SystemInformation::GetSysWow64Dir() 298 | : System::SystemInformation::GetSystem32Dir(), 299 | dllName 300 | ); 301 | 302 | System::ImageMapping mapping(dllPath.c_str()); 303 | PEParser::ImageFactory factory; 304 | auto image = factory.GetImage(mapping); 305 | 306 | if (wow64mode && image->GetBitness() == System::Bitness::Arch64) 307 | throw Utils::Exception(L"Invalid knowndll '%s' bitness", dllName.c_str()); 308 | else if (!wow64mode && image->GetBitness() != System::SystemInformation::GetSystemBitness()) 309 | throw Utils::Exception(L"Invalid knowndll '%s' bitness", dllName.c_str()); 310 | 311 | auto imports = image->LoadImportTable(); 312 | for (auto& import : imports) 313 | { 314 | auto dll = std::wstring(import.begin(), import.end()); 315 | std::transform(dll.begin(), dll.end(), dll.begin(), towlower); 316 | auto& known = (wow64mode ? _knownWow64 : _known); 317 | if (known.InsertOnlyNew(dll)) 318 | UnwindImports(dll, wow64mode); 319 | } 320 | } 321 | 322 | // ================= 323 | 324 | bool DllCache::InsertOnlyNew(const std::wstring& dllName) 325 | { 326 | return _dlls.insert(dllName).second; 327 | } 328 | 329 | bool DllCache::Contain(const std::wstring& dllName) 330 | { 331 | return (_dlls.find(dllName) != _dlls.end()); 332 | } 333 | 334 | // ================= 335 | 336 | void ActivationContextStack::Push(System::ActivationContext& context) 337 | { 338 | _stack.emplace_back(context); 339 | } 340 | 341 | void ActivationContextStack::Pop() 342 | { 343 | _stack.pop_back(); 344 | } 345 | 346 | bool ActivationContextStack::IsLibrarySxS(const std::wstring& dllName, std::wstring& sxsDir) 347 | { 348 | if (!_stack.size()) 349 | return false; 350 | 351 | for (auto& assemblies = _stack.rbegin(); assemblies != _stack.rend(); assemblies++) 352 | for (const auto& assembly : *assemblies) 353 | for (const auto& library : assembly.GetFiles()) 354 | if (_wcsicmp(dllName.c_str(), library.c_str()) == 0) 355 | { 356 | sxsDir = assembly.GetID(); 357 | return true; 358 | } 359 | 360 | return false; 361 | } 362 | 363 | // ================= 364 | 365 | LoadManifestAndPush::LoadManifestAndPush(System::ImageMapping& module, const std::wstring& imageDir, ActivationContextStack& stack) 366 | { 367 | std::wstring tempName; 368 | 369 | try 370 | { 371 | auto manifest = ReadManifestFromResources(module); 372 | manifest = NormalizeManifest(manifest); 373 | tempName = SafeManifestToTempFile(manifest); 374 | System::ActivationContext context( 375 | tempName.c_str(), 376 | imageDir.c_str() 377 | ); 378 | stack.Push(context); 379 | } 380 | catch (Utils::Exception&) 381 | { 382 | } 383 | 384 | if (!tempName.empty()) 385 | { 386 | System::File deleter(tempName.c_str(), DELETE, 0); 387 | deleter.SetDeleteOnClose(); 388 | } 389 | } 390 | 391 | std::vector LoadManifestAndPush::ReadManifestFromResources(System::ImageMapping& module) 392 | { 393 | PEParser::ImageFactory factory; 394 | auto image = factory.GetImage(module); 395 | auto resources = image->LoadResources(); 396 | return PEParser::ResourceUtils::LoadFirstResource(resources, module, PEParser::ResourceEntry(24)); 397 | } 398 | 399 | std::vector LoadManifestAndPush::NormalizeManifest(const std::vector& manifest) 400 | { 401 | //TODO: manifest.xml normalization 402 | return manifest; 403 | } 404 | 405 | std::wstring LoadManifestAndPush::SafeManifestToTempFile(const std::vector& manifest) 406 | { 407 | std::wstring name; 408 | auto temp = System::FileUtils::CreateTempFile(name, FILE_WRITE_ACCESS, FILE_SHARE_READ); 409 | temp.Write(const_cast(&manifest[0]), manifest.size()); 410 | return name; 411 | } 412 | 413 | // ================= 414 | 415 | ImageScanContext::ImageScanContext(const std::wstring& imagePath, const System::TokenAccessChecker& access) : 416 | _imagePath(imagePath), 417 | _accessChecker(access) 418 | { 419 | System::FileUtils::NormalizePath(_imagePath); 420 | System::FileUtils::ExtractFileDirectory(_imagePath, _imageDir); 421 | System::FileUtils::ExtractFileName(_imagePath, _imageFile); 422 | 423 | LoadDefaultSystemActivationContext(); 424 | 425 | _image.reset(new System::ImageMapping(_imagePath.c_str())); 426 | PEParser::ImageFactory factory; 427 | _parser = factory.GetImage(*_image); 428 | _bitness = _parser->GetBitness(); 429 | } 430 | 431 | void ImageScanContext::LoadDefaultSystemActivationContext() 432 | { 433 | auto looking = System::SystemInformation::GetWindowsDir(); 434 | looking += L"\\WinSxS\\Manifests\\"; 435 | auto manifest = looking; 436 | 437 | if (_bitness == System::Bitness::Arch64) 438 | looking += L"amd64_microsoft.windows.systemcompatible_*"; 439 | else 440 | looking += L"x86_microsoft.windows.systemcompatible_*"; 441 | 442 | manifest += System::FileUtils::FindFirstMatchedFile(looking); 443 | System::ActivationContext actx(manifest.c_str()); 444 | _actxStack.Push(actx); 445 | } 446 | 447 | System::ImageMapping ImageScanContext::GetAppImage() const 448 | { 449 | return *_image; 450 | } 451 | 452 | const PEParser::ImagePtr ImageScanContext::GetAppParser() const 453 | { 454 | return _parser; 455 | } 456 | 457 | const std::wstring& ImageScanContext::GetAppPath() const 458 | { 459 | return _imagePath; 460 | } 461 | 462 | const std::wstring& ImageScanContext::GetAppDirectory() const 463 | { 464 | return _imageDir; 465 | } 466 | 467 | const std::wstring& ImageScanContext::GetAppFileName() const 468 | { 469 | return _imageFile; 470 | } 471 | 472 | System::Bitness ImageScanContext::GetAppBitness() const 473 | { 474 | return _bitness; 475 | } 476 | 477 | DllCache& ImageScanContext::GetDllsCache() 478 | { 479 | return _scannedDlls; 480 | } 481 | 482 | const System::TokenAccessChecker& ImageScanContext::GetAccessChecker() const 483 | { 484 | return _accessChecker; 485 | } 486 | 487 | ActivationContextStack& ImageScanContext::GetActivationContextStack() 488 | { 489 | return _actxStack; 490 | } 491 | 492 | // ================= 493 | 494 | void ImageScanEngine::SetOptionUnwindImport(bool enable) 495 | { 496 | _unwindImports = enable; 497 | } 498 | 499 | void ImageScanEngine::SetOptionUnwindDelayLoadImport(bool enable) 500 | { 501 | _scanDelayLoad = enable; 502 | } 503 | 504 | void ImageScanEngine::SetOptionAccessibleOnly(bool enable) 505 | { 506 | _checkAccessible = enable; 507 | } 508 | 509 | void ImageScanEngine::Scan(std::wstring& imagePath, System::EnvironmentVariables& envVars, System::TokenAccessChecker& access) 510 | { 511 | ImageScanContext context(imagePath, access); 512 | 513 | bool wow64mode = false; 514 | if (System::SystemInformation::GetSystemBitness() == System::Bitness::Arch64 && context.GetAppBitness() == System::Bitness::Arch32) 515 | wow64mode = true; 516 | 517 | //TODO: don't need to calculate order each time scan started, 518 | // in other way we can calculate it on constructor and 519 | // change an image dir or current dir before we start a 520 | // scan. 521 | ImageScanOrder order(context.GetAppDirectory(), context.GetAppDirectory(), envVars, context.GetAccessChecker()); 522 | order.SetWow64Mode(wow64mode); 523 | NotifyLoadImageOrder(order); 524 | 525 | //TODO: do we really need to open mapping again? context already did it 526 | System::ImageMapping mapping(context.GetAppPath().c_str()); 527 | LoadManifestAndPush appManifest(mapping, context.GetAppDirectory(), context.GetActivationContextStack()); 528 | 529 | ScanImports(context.GetAppImage(), context, order); 530 | } 531 | 532 | void ImageScanEngine::ScanModule(ImageScanContext& context, std::wstring& dllName, ImageScanOrder& order) 533 | { 534 | try 535 | { 536 | auto actxStack = context.GetActivationContextStack(); 537 | auto scannedDlls = context.GetDllsCache(); 538 | auto access = context.GetAccessChecker(); 539 | auto bitness = context.GetAppBitness(); 540 | 541 | std::transform(dllName.begin(), dllName.end(), dllName.begin(), towlower); 542 | 543 | std::wstring sxsDir; 544 | if (actxStack.IsLibrarySxS(dllName, sxsDir)) 545 | { 546 | // When we process a SxS DLL we should care about multiple paths for the same DLL name. 547 | // For instance: 548 | // C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.7601.18837_none_ec86b8d6858ec0bc\comctl32.dll 549 | // C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.24460_none_2b1e532a457961ba\comctl32.dll 550 | // Therefore we put to the dll cache a dll name with SxS directory, for examples above it would be: 551 | // x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.7601.18837_none_ec86b8d6858ec0bc\comctl32.dll 552 | // x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.24460_none_2b1e532a457961ba\comctl32.dll 553 | 554 | std::wstring cached = sxsDir; 555 | cached += L"\\"; 556 | cached += dllName; 557 | 558 | if (!scannedDlls.InsertOnlyNew(cached)) 559 | return; 560 | 561 | PerformSxSModuleAction(context, dllName, sxsDir, order); 562 | } 563 | else 564 | { 565 | if (!scannedDlls.InsertOnlyNew(dllName)) 566 | return; 567 | 568 | if (_knownDlls.Contain(dllName, bitness)) 569 | return; 570 | 571 | auto dir = order.FindDllDirectory(dllName); 572 | 573 | if (dir.GetType() == ImageDirectory::Type::Unknown) 574 | PerformNotExistingModuleAction(context, dllName, dir, order); 575 | else 576 | PerformExistingModuleAction(context, dllName, dir, order); 577 | } 578 | } 579 | catch (Utils::Exception&) 580 | { 581 | std::wcout << L"Skipped: Exception while processing the dll '" << dllName << L"'" << std::endl; 582 | } 583 | } 584 | 585 | void ImageScanEngine::ScanImports(System::ImageMapping& module, ImageScanContext& context, ImageScanOrder& order) 586 | { 587 | PEParser::ImageFactory factory; 588 | auto image = factory.GetImage(module); 589 | ScanImports(image, context, order); 590 | } 591 | 592 | void ImageScanEngine::ScanImports(const PEParser::ImagePtr& image, ImageScanContext& context, ImageScanOrder& order) 593 | { 594 | if (image->GetBitness() != context.GetAppBitness()) 595 | throw Utils::Exception(L"Image bitness mismatched"); 596 | 597 | auto scan = [&](PEParser::ImportTable& imports) 598 | { 599 | for (auto& import : imports) 600 | { 601 | std::wstring importDll(import.begin(), import.end()); 602 | if (!System::FileUtils::IsPathRelative(importDll)) 603 | { 604 | std::wcout << L"Skipped: Non-relative path of dll '" << importDll << L"'" << std::endl; 605 | continue; 606 | } 607 | 608 | ScanModule(context, std::wstring(import.begin(), import.end()), order); 609 | } 610 | }; 611 | 612 | scan(image->LoadImportTable()); 613 | if (_scanDelayLoad) 614 | scan(image->LoadDelayImportTable()); 615 | } 616 | 617 | void ImageScanEngine::PerformExistingModuleAction(ImageScanContext& context, std::wstring& dllName, ImageDirectory& dir, ImageScanOrder& order) 618 | { 619 | auto dllPath = System::FileUtils::BuildPath(dir.GetPath(), dllName); 620 | 621 | auto vulnerableDirs = CollectVulnerableDirs(dir, order); 622 | 623 | bool writtable = false; 624 | if (_checkAccessible && IsFileWritable(dllPath, context.GetAccessChecker())) 625 | writtable = true; 626 | 627 | if (dir.GetType() != ImageDirectory::Type::Base && !vulnerableDirs.empty()) 628 | NotifyVulnerableDll(dir, dllName, writtable, vulnerableDirs); 629 | else if (dir.GetType() == ImageDirectory::Type::Base && writtable) 630 | NotifyVulnerableDll(dir, dllName, true, vulnerableDirs); 631 | 632 | if (_unwindImports) 633 | { 634 | System::ImageMapping mapping(dllPath.c_str()); 635 | LoadManifestAndPush dllManifest(mapping, dir.GetPath(), context.GetActivationContextStack()); 636 | ScanImports(mapping, context, order); 637 | } 638 | } 639 | 640 | void ImageScanEngine::PerformNotExistingModuleAction(ImageScanContext& context, std::wstring& dllName, ImageDirectory& dir, ImageScanOrder& order) 641 | { 642 | auto vulnerableDirs = CollectVulnerableDirs(dir, order); 643 | NotifyVulnerableDll(dir, dllName, false, vulnerableDirs); 644 | } 645 | 646 | void ImageScanEngine::PerformSxSModuleAction(ImageScanContext& context, std::wstring& dllName, std::wstring& sxsDir, ImageScanOrder& order) 647 | { 648 | std::vector vulnerableDirs; 649 | auto base = order.GetBaseDir(); 650 | 651 | auto sxsLocal = base.GetPath(); 652 | sxsLocal += L"\\"; 653 | sxsLocal += context.GetAppFileName(); 654 | sxsLocal += L".Local\\"; 655 | sxsLocal += sxsDir; 656 | 657 | ImageDirectory sxsLocalDir(ImageDirectory::Type::SxS, sxsLocal, context.GetAccessChecker()); 658 | 659 | if (_checkAccessible) 660 | { 661 | // 1. Can create Local dir 662 | // 2. Can overwrite local file 663 | auto baseDir = base.GetPath(); 664 | auto dllPath = System::FileUtils::BuildPath(sxsLocal, dllName); 665 | if (System::FileUtils::PathExists(dllPath) && IsFileWritable(dllPath, context.GetAccessChecker())) 666 | NotifyVulnerableSxSDll(sxsLocalDir, dllName, true); 667 | else if (System::FileUtils::PathExists(baseDir) && IsDirectoryWritable(baseDir, context.GetAccessChecker())) 668 | NotifyVulnerableSxSDll(sxsLocalDir, dllName, false); 669 | } 670 | 671 | auto sxsGlobal = System::SystemInformation::GetWindowsDir(); 672 | sxsGlobal += L"\\WinSxS\\"; 673 | sxsGlobal += sxsDir; 674 | 675 | ImageDirectory sxsGlobalDir(ImageDirectory::Type::SxS, sxsGlobal, context.GetAccessChecker()); 676 | auto sxsPath = System::FileUtils::BuildPath(sxsGlobal, dllName); 677 | 678 | if (_checkAccessible) 679 | { 680 | // 3. Can overwrite public file 681 | if (IsFileWritable(sxsPath, context.GetAccessChecker())) 682 | NotifyVulnerableSxSDll(sxsGlobalDir, dllName, true); 683 | } 684 | 685 | if (_unwindImports) 686 | { 687 | System::ImageMapping mapping(sxsPath.c_str()); 688 | LoadManifestAndPush dllManifest(mapping, sxsGlobalDir.GetPath(), context.GetActivationContextStack()); 689 | ScanImports(mapping, context, order); 690 | } 691 | } 692 | 693 | std::vector ImageScanEngine::CollectVulnerableDirs(const ImageDirectory& last, ImageScanOrder& order) 694 | { 695 | std::vector vulnerableDirs; 696 | 697 | for (const auto& dir : order.GetOrder()) 698 | { 699 | if (dir == last) 700 | break; 701 | 702 | if (_checkAccessible) 703 | { 704 | if (dir.IsAccessible()) 705 | vulnerableDirs.push_back(&dir); 706 | } 707 | else 708 | { 709 | vulnerableDirs.push_back(&dir); 710 | } 711 | } 712 | 713 | return vulnerableDirs; 714 | } 715 | 716 | void ImageScanEngine::NotifyLoadImageOrder(LoadImageOrder& dir) 717 | { 718 | // Stub, does nothing here 719 | } 720 | 721 | void ImageScanEngine::NotifyVulnerableDll(ImageDirectory& dir, std::wstring& dll, bool writtable, std::vector& vulnDirs) 722 | { 723 | // Stub, does nothing here 724 | } 725 | 726 | void ImageScanEngine::NotifyVulnerableSxSDll(ImageDirectory& dir, std::wstring& dll, bool writtable) 727 | { 728 | // Stub, does nothing here 729 | } 730 | 731 | bool ImageScanEngine::IsFileWritable(const std::wstring& path, const System::TokenAccessChecker& access) 732 | { 733 | System::File file(path.c_str()); 734 | System::SecurityDescriptor descriptor(file); 735 | return access.IsFileObjectAccessible(descriptor, FILE_WRITE_DATA); 736 | } 737 | 738 | bool ImageScanEngine::IsDirectoryWritable(const std::wstring& path, const System::TokenAccessChecker& access) 739 | { 740 | System::Directory dir(path.c_str()); 741 | System::SecurityDescriptor descriptor(dir); 742 | return access.IsFileObjectAccessible(descriptor, FILE_ADD_SUBDIRECTORY); 743 | } 744 | }; 745 | -------------------------------------------------------------------------------- /hijacken/System.cpp: -------------------------------------------------------------------------------- 1 | #include "System.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #pragma comment(lib, "shlwapi.lib") 11 | 12 | namespace System 13 | { 14 | // ================= 15 | 16 | LastError::LastError() : 17 | _code(::GetLastError()) 18 | { 19 | } 20 | 21 | HANDLE LastError::Proxymize(HANDLE handle) 22 | { 23 | _code = ::GetLastError(); 24 | return handle; 25 | } 26 | 27 | DWORD LastError::GetCode() const 28 | { 29 | return _code; 30 | } 31 | 32 | // ================= 33 | 34 | Handle::Handle() : 35 | std::shared_ptr(0, &ObjectDeleter) 36 | { 37 | } 38 | 39 | Handle::Handle(HANDLE object, DestroyObjectRoutine destroyer) : 40 | std::shared_ptr(object, destroyer) 41 | { 42 | } 43 | 44 | void Handle::ObjectDeleter(HANDLE object) 45 | { 46 | if (object && object != INVALID_HANDLE_VALUE) 47 | ::CloseHandle(object); 48 | } 49 | 50 | bool Handle::IsValid() const 51 | { 52 | auto object = get(); 53 | return (object && object != INVALID_HANDLE_VALUE); 54 | } 55 | 56 | HANDLE Handle::GetNativeHandle() const 57 | { 58 | return get(); 59 | } 60 | 61 | void Handle::SetHandle(HANDLE object, DestroyObjectRoutine destroyer) 62 | { 63 | reset(object, destroyer); 64 | } 65 | 66 | // ================= 67 | 68 | Process::Process(DWORD processId, DWORD access) : 69 | Handle(::OpenProcess(access, FALSE, processId)), 70 | _processId(processId) 71 | { 72 | if (!Handle::IsValid()) 73 | throw Utils::Exception(::GetLastError(), L"OpenProcess(pid:%d) failed with code %d", _processId, ::GetLastError()); 74 | } 75 | 76 | Process::Process(HANDLE process) 77 | { 78 | _processId = ::GetProcessId(process); 79 | 80 | if (process == ::GetCurrentProcess()) 81 | { 82 | Handle::SetHandle(process, &WithoutRelease); 83 | } 84 | else 85 | { 86 | if (!::DuplicateHandle(::GetCurrentProcess(), process, ::GetCurrentProcess(), &process, 0, FALSE, DUPLICATE_SAME_ACCESS)) 87 | throw Utils::Exception(::GetLastError(), L"DuplicateHandle() failed with code %d", ::GetLastError()); 88 | Handle::SetHandle(process); 89 | } 90 | } 91 | 92 | DWORD Process::GetProcessID() 93 | { 94 | return _processId; 95 | } 96 | 97 | template 98 | void Process::ReadMemoryToContainer(void* address, T& buffer, size_t size) 99 | { 100 | SIZE_T readed; 101 | 102 | buffer.resize(size / sizeof(buffer[0])); 103 | 104 | if (!::ReadProcessMemory( 105 | Handle::GetNativeHandle(), 106 | address, 107 | const_cast(reinterpret_cast(buffer.c_str())), 108 | size, 109 | &readed)) 110 | throw Utils::Exception(GetLastError(), L"ReadProcessMemory(pid:%d) failed with code %d", _processId, GetLastError()); 111 | 112 | if (readed != size) 113 | throw Utils::Exception(L"ReadProcessMemory(pid:%d) can't read full chunk", _processId); 114 | } 115 | 116 | void Process::ReadMemory(void* address, std::string& buffer, size_t size) 117 | { 118 | ReadMemoryToContainer(address, buffer, size); 119 | } 120 | 121 | void Process::ReadMemory(void* address, std::wstring& buffer, size_t size) 122 | { 123 | ReadMemoryToContainer(address, buffer, size); 124 | } 125 | 126 | void Process::WriteMemory(void* address, std::string& buffer, bool unprotect) 127 | { 128 | SIZE_T written = 0; 129 | 130 | auto result = ::WriteProcessMemory(Handle::GetNativeHandle(), address, const_cast(buffer.c_str()), buffer.size(), &written); 131 | if (!result && unprotect) 132 | { 133 | DWORD old; 134 | if (::VirtualProtectEx(Handle::GetNativeHandle(), address, buffer.size(), PAGE_EXECUTE_READWRITE, &old)) 135 | { 136 | result = ::WriteProcessMemory(Handle::GetNativeHandle(), address, const_cast(buffer.c_str()), buffer.size(), &written); 137 | ::VirtualProtectEx(Handle::GetNativeHandle(), address, buffer.size(), old, &old); 138 | if (!result) 139 | throw Utils::Exception(::GetLastError(), L"WriteProcessMemory(pid:%d) failed with code %d", _processId, ::GetLastError()); 140 | } 141 | else 142 | { 143 | throw Utils::Exception(::GetLastError(), L"VirtualProtectEx(pid:%d) failed with code %d", _processId, ::GetLastError()); 144 | } 145 | } 146 | 147 | if (written != buffer.size()) 148 | throw Utils::Exception(L"Error, WriteProcessMemory() can't write full chunk"); 149 | } 150 | 151 | void Process::WithoutRelease(HANDLE object) 152 | { 153 | } 154 | 155 | // ================= 156 | 157 | ProcessesSnapshot::ProcessesSnapshot() : 158 | Handle(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) 159 | { 160 | if (!Handle::IsValid()) 161 | throw Utils::Exception(::GetLastError(), L"CreateToolhelp32Snapshot(processes) failed with code %d", ::GetLastError()); 162 | 163 | ResetWalking(); 164 | } 165 | 166 | bool ProcessesSnapshot::GetNextProcess(DWORD& processId) 167 | { 168 | std::wstring name; 169 | return GetNextProcess(processId, name); 170 | } 171 | 172 | bool ProcessesSnapshot::GetNextProcess(DWORD& processId, std::wstring& name) 173 | { 174 | PROCESSENTRY32W entry = {}; 175 | entry.dwSize = sizeof(entry); 176 | 177 | if (_fromStart) 178 | { 179 | if (!::Process32FirstW(Handle::GetNativeHandle(), &entry)) 180 | return false; 181 | 182 | _fromStart = false; 183 | } 184 | else 185 | { 186 | if (!::Process32NextW(Handle::GetNativeHandle(), &entry)) 187 | return false; 188 | } 189 | 190 | name = entry.szExeFile; 191 | processId = entry.th32ProcessID; 192 | return true; 193 | } 194 | 195 | void ProcessesSnapshot::ResetWalking() 196 | { 197 | _fromStart = true; 198 | } 199 | 200 | // ================= 201 | 202 | ModulesSnapshot::ModulesSnapshot(DWORD processId) : 203 | Handle(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId)) 204 | { 205 | if (!Handle::IsValid()) 206 | throw Utils::Exception(::GetLastError(), L"CreateToolhelp32Snapshot(modules) failed with code %d", ::GetLastError()); 207 | 208 | ResetWalking(); 209 | } 210 | 211 | bool ModulesSnapshot::GetNextModule(HMODULE& module) 212 | { 213 | MODULEENTRY32W entry = {}; 214 | entry.dwSize = sizeof(entry); 215 | 216 | if (_fromStart) 217 | { 218 | if (!::Module32FirstW(Handle::GetNativeHandle(), &entry)) 219 | return false; 220 | 221 | _fromStart = false; 222 | } 223 | else 224 | { 225 | if (!::Module32NextW(Handle::GetNativeHandle(), &entry)) 226 | return false; 227 | } 228 | 229 | module = entry.hModule; 230 | return true; 231 | } 232 | 233 | bool ModulesSnapshot::GetNextModule(HMODULE& module, std::wstring& name) 234 | { 235 | MODULEENTRY32W entry = {}; 236 | entry.dwSize = sizeof(entry); 237 | 238 | if (_fromStart) 239 | { 240 | if (!::Module32FirstW(Handle::GetNativeHandle(), &entry)) 241 | return false; 242 | 243 | _fromStart = false; 244 | } 245 | else 246 | { 247 | if (!::Module32NextW(Handle::GetNativeHandle(), &entry)) 248 | return false; 249 | } 250 | 251 | name = entry.szModule; 252 | module = entry.hModule; 253 | return true; 254 | } 255 | 256 | void ModulesSnapshot::ResetWalking() 257 | { 258 | _fromStart = true; 259 | } 260 | 261 | // ================= 262 | 263 | ProcessInformation::ProcessInformation(DWORD processId) : _pebAddress(nullptr) 264 | { 265 | _process.reset( 266 | new Process( 267 | processId, 268 | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ 269 | ) 270 | ); 271 | } 272 | 273 | ProcessPtr ProcessInformation::GetProcess() 274 | { 275 | return _process; 276 | } 277 | 278 | PPEB ProcessInformation::GetPEBAddress() 279 | { 280 | PROCESS_BASIC_INFORMATION basic; 281 | DWORD written; 282 | 283 | if (!_pebAddress) 284 | { 285 | 286 | auto status = ::NtQueryInformationProcess(_process->GetNativeHandle(), ProcessBasicInformation, &basic, sizeof(basic), &written); 287 | if (!NT_SUCCESS(status)) 288 | throw Utils::Exception(status, L"NtQueryInformationProcess(pid:%d) failed with code %08X", _process->GetProcessID(), status); 289 | 290 | _pebAddress = reinterpret_cast(basic.PebBaseAddress); 291 | } 292 | 293 | return _pebAddress; 294 | } 295 | 296 | ProcessEnvironmentBlockPtr ProcessInformation::GetProcessEnvironmentBlock() 297 | { 298 | if (!_peb.get()) 299 | _peb.reset(new ProcessEnvironmentBlock(*this)); 300 | 301 | return _peb; 302 | } 303 | 304 | void ProcessInformation::GetImagePath(std::wstring& path) 305 | { 306 | DWORD written; 307 | PUNICODE_STRING imagePath; 308 | std::string buffer; 309 | 310 | buffer.resize(MAX_PATH * 2); 311 | 312 | auto status = ::NtQueryInformationProcess( 313 | _process->GetNativeHandle(), 314 | ProcessImageFileNameWin32, 315 | const_cast(buffer.c_str()), 316 | static_cast(buffer.size()), 317 | &written 318 | ); 319 | if (!NT_SUCCESS(status)) 320 | throw Utils::Exception(status, L"NtQueryInformationProcess(pid:%d) failed with code %08X", _process->GetProcessID(), status); 321 | 322 | if (buffer.size() < sizeof(UNICODE_STRING)) 323 | throw Utils::Exception(L"Buffer received from pid:%d is crowed", _process->GetProcessID()); 324 | 325 | imagePath = reinterpret_cast(const_cast(buffer.c_str())); 326 | 327 | if (buffer.size() < sizeof(UNICODE_STRING)+imagePath->Length) 328 | throw Utils::Exception(L"String received from pid:%d is crowed", _process->GetProcessID()); 329 | 330 | auto chars = imagePath->Length / sizeof(wchar_t); 331 | auto begin = reinterpret_cast(imagePath->Buffer); 332 | auto end = reinterpret_cast(imagePath->Buffer + chars); 333 | 334 | path.insert(path.begin(), begin, end); 335 | } 336 | 337 | void ProcessInformation::GetImageDirectory(std::wstring& directory) 338 | { 339 | std::wstring path; 340 | GetImagePath(path); 341 | System::FileUtils::ExtractFileDirectory(path, directory); 342 | } 343 | 344 | void ProcessInformation::GetModulePath(HMODULE module, std::wstring& path) 345 | { 346 | wchar_t buffer[MAX_PATH * 2]; 347 | 348 | auto result = ::GetModuleFileNameExW(_process->GetNativeHandle(), module, buffer, _countof(buffer)); 349 | if (!result) 350 | throw Utils::Exception(L"GetModuleFileNameExW(pid:%d) failed with code %d", _process->GetProcessID(), ::GetLastError()); 351 | 352 | path = buffer; 353 | } 354 | 355 | Bitness ProcessInformation::GetCurrentProcessBitness() 356 | { 357 | return (sizeof(void*) == sizeof(long long) ? Bitness::Arch64 : Bitness::Arch32); 358 | } 359 | 360 | EnvironmentVariablesPtr ProcessInformation::GetCurrentEnvironmentVariables() 361 | { 362 | auto vars = ::GetEnvironmentStringsW(); 363 | if (!vars) 364 | throw Utils::Exception(L"Can't receive a current environment variables"); 365 | return EnvironmentVariablesPtr(new EnvironmentVariables(vars)); 366 | } 367 | 368 | // ================= 369 | 370 | ProcessEnvironmentBlock::ProcessEnvironmentBlock(ProcessInformation& processInfo) : _peb(nullptr) 371 | { 372 | auto pebPtr = processInfo.GetPEBAddress(); 373 | if (!pebPtr) 374 | throw Utils::Exception(L"Can't get a PEB for this process"); 375 | 376 | processInfo.GetProcess()->ReadMemory(pebPtr, _pebBuffer, sizeof(PEB)); 377 | _peb = reinterpret_cast( 378 | const_cast(_pebBuffer.c_str()) 379 | ); 380 | _process = processInfo.GetProcess(); 381 | } 382 | 383 | EnvironmentVariablesPtr ProcessEnvironmentBlock::GetProcessEnvironment() 384 | { 385 | LoadProcessParameters(); 386 | 387 | if (!_paramsEnv.size()) 388 | _process->ReadMemory(_params->Environment, _paramsEnv, _params->EnvironmentSize); 389 | 390 | return EnvironmentVariablesPtr(new EnvironmentVariables(_paramsEnv)); 391 | } 392 | 393 | void ProcessEnvironmentBlock::GetCurrentDir(std::wstring& directory) 394 | { 395 | LoadProcessParameters(); 396 | 397 | if (!_currentDirectory.size()) 398 | _process->ReadMemory(_params->CurrentDirectory.DosPath.Buffer, _currentDirectory, _params->CurrentDirectory.DosPath.Length); 399 | 400 | directory = _currentDirectory; 401 | } 402 | 403 | void ProcessEnvironmentBlock::LoadProcessParameters() 404 | { 405 | if (_paramsBuffer.size()) 406 | return; 407 | 408 | _process->ReadMemory(_peb->ProcessParameters, _paramsBuffer, sizeof(RTL_USER_PROCESS_PARAMETERS)); 409 | _params = reinterpret_cast( 410 | const_cast(_paramsBuffer.c_str()) 411 | ); 412 | } 413 | 414 | 415 | // ================= 416 | 417 | EnvironmentVariables::EnvironmentVariables(LPWCH environment) 418 | { 419 | auto start = environment; 420 | auto end = environment + wcslen(environment); 421 | 422 | while (start != end) 423 | { 424 | auto entry = std::wstring(start, end); 425 | 426 | auto keyEnd = entry.find(L'='); 427 | if (keyEnd != 0 && keyEnd != std::wstring::npos) 428 | _variables[std::wstring(&entry[0], &entry[keyEnd])] = std::wstring(&entry[keyEnd + 1], &entry[entry.size()]); 429 | 430 | start = end + 1; 431 | end = start + wcslen(start); 432 | } 433 | } 434 | 435 | EnvironmentVariables::EnvironmentVariables(const std::wstring& environment) 436 | { 437 | size_t startOffset = 0; 438 | auto endOffset = environment.find(L'\0'); 439 | //TODO: what if only one var 440 | 441 | while (endOffset != std::wstring::npos) 442 | { 443 | auto entry = std::wstring(environment.c_str() + startOffset); 444 | 445 | auto keyEnd = entry.find(L'='); 446 | if (keyEnd != 0 && keyEnd != std::wstring::npos) 447 | _variables[std::wstring(&entry[0], &entry[keyEnd])] = std::wstring(&entry[keyEnd + 1], &entry[entry.size()]); 448 | 449 | startOffset = endOffset + 1; 450 | endOffset = environment.find(L'\0', startOffset); 451 | } 452 | } 453 | 454 | EnvironmentVariables::EnvironmentVariables(const RegistryValues& values) 455 | { 456 | for (auto& value : values) 457 | { 458 | auto type = value.second.GetType(); 459 | if (type == RegistryValueType::String) 460 | { 461 | _variables[value.first] = value.second.GetValue(); 462 | } 463 | else if (type == RegistryValueType::ExpandString) 464 | { 465 | RegistryExpandedStringValue expanded(value.second); 466 | _variables[value.first] = expanded.GetValue(); 467 | } 468 | //TODO: what happens if we meet another type? 469 | } 470 | } 471 | 472 | bool EnvironmentVariables::GetValue(const wchar_t* key, std::wstring& output) const 473 | { 474 | auto value = _variables.find(std::wstring(key)); 475 | 476 | if (value == _variables.end()) 477 | return false; 478 | 479 | output = value->second; 480 | return true; 481 | } 482 | 483 | // ================= 484 | 485 | void TokenBase::SetPrivilege(wchar_t* privelege, bool enable) 486 | { 487 | TOKEN_PRIVILEGES priveleges = {}; 488 | LUID luid = {}; 489 | 490 | if (!::LookupPrivilegeValueW(NULL, privelege, &luid)) 491 | throw Utils::Exception(::GetLastError(), L"LookupPrivilegeValue() failed with code %d", ::GetLastError()); 492 | 493 | priveleges.PrivilegeCount = 1; 494 | priveleges.Privileges[0].Luid = luid; 495 | priveleges.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0); 496 | 497 | //TODO: query privelege if it's different than set it and check getlasterror() 498 | if (!::AdjustTokenPrivileges(Handle::GetNativeHandle(), FALSE, &priveleges, sizeof(priveleges), NULL, NULL) /*|| ::GetLastError() != ERROR_SUCCESS*/) 499 | throw Utils::Exception(::GetLastError(), L"AdjustTokenPrivileges() failed with code %d", ::GetLastError()); 500 | } 501 | 502 | IntegrityLevel TokenBase::GetIntegrityLevel() 503 | { 504 | std::string buffer; 505 | DWORD written = 0; 506 | 507 | buffer.resize(64); 508 | 509 | auto result = ::GetTokenInformation( 510 | Handle::GetNativeHandle(), 511 | TokenIntegrityLevel, 512 | const_cast(buffer.c_str()), 513 | static_cast(buffer.size()), 514 | &written 515 | ); 516 | 517 | if (!result && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) 518 | { 519 | buffer.resize(written); 520 | result = ::GetTokenInformation( 521 | Handle::GetNativeHandle(), 522 | TokenIntegrityLevel, 523 | const_cast(buffer.c_str()), 524 | static_cast(buffer.size()), 525 | &written 526 | ); 527 | } 528 | 529 | if (!result) 530 | throw Utils::Exception(::GetLastError(), L"GetTokenInformation() failed with code %d", ::GetLastError()); 531 | 532 | auto mandatory = reinterpret_cast( 533 | const_cast(buffer.c_str()) 534 | ); 535 | 536 | if (!::IsValidSid(mandatory->Label.Sid)) 537 | throw Utils::Exception(L"A seed you received isn't valid"); 538 | 539 | IntegrityLevel level; 540 | auto sub = *::GetSidSubAuthority(mandatory->Label.Sid, 0); 541 | 542 | switch (sub) 543 | { 544 | case SECURITY_MANDATORY_UNTRUSTED_RID: 545 | level = IntegrityLevel::Untrusted; 546 | break; 547 | case SECURITY_MANDATORY_LOW_RID: 548 | level = IntegrityLevel::Low; 549 | break; 550 | case SECURITY_MANDATORY_MEDIUM_RID: 551 | level = IntegrityLevel::Medium; 552 | break; 553 | case SECURITY_MANDATORY_MEDIUM_PLUS_RID: 554 | level = IntegrityLevel::MediumPlus; 555 | break; 556 | case SECURITY_MANDATORY_HIGH_RID: 557 | level = IntegrityLevel::High; 558 | break; 559 | case SECURITY_MANDATORY_SYSTEM_RID: 560 | level = IntegrityLevel::System; 561 | break; 562 | case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: 563 | level = IntegrityLevel::Protected; 564 | break; 565 | //case 0x6000: 566 | // level = ???; 567 | // break; 568 | case 0x7000: 569 | level = IntegrityLevel::Secure; 570 | break; 571 | default: 572 | throw Utils::Exception(L"Unknown mandatory authority %x", sub); 573 | } 574 | 575 | return level; 576 | } 577 | 578 | void TokenBase::SetIntegrityLevel(IntegrityLevel level) 579 | { 580 | auto sid = AllocateSidByIntegrityLevel(level); 581 | 582 | TOKEN_MANDATORY_LABEL label = {}; 583 | label.Label.Attributes = SE_GROUP_INTEGRITY; 584 | label.Label.Sid = sid; 585 | 586 | auto result = ::SetTokenInformation(Handle::GetNativeHandle(), TokenIntegrityLevel, &label, sizeof(label)+::GetLengthSid(sid)); 587 | auto error = ::GetLastError(); 588 | 589 | ::LocalFree(sid); 590 | 591 | if (!result) 592 | throw Utils::Exception(error, L"SetTokenInformation(IntegrityLevel) failed with code %d", error); 593 | } 594 | 595 | HANDLE TokenBase::GetLinkedToken() 596 | { 597 | DWORD written = 0; 598 | HANDLE linked; 599 | 600 | if (!::GetTokenInformation(Handle::GetNativeHandle(), TokenLinkedToken, &linked, sizeof(linked), &written)) 601 | throw Utils::Exception(::GetLastError(), L"SetTokenInformation(LinkedToken) failed with code %d", ::GetLastError()); 602 | 603 | return linked; 604 | } 605 | 606 | void TokenBase::SetLinkedToken(HANDLE token) 607 | { 608 | if (!::SetTokenInformation(Handle::GetNativeHandle(), TokenLinkedToken, &token, sizeof(token))) 609 | throw Utils::Exception(::GetLastError(), L"SetTokenInformation(LinkedToken) failed with code %d", ::GetLastError()); 610 | } 611 | 612 | void TokenBase::GetUserNameString(std::wstring& userName) 613 | { 614 | DWORD written = 0; 615 | char buffer[256]; 616 | 617 | if (!::GetTokenInformation(Handle::GetNativeHandle(), TokenUser, &buffer, sizeof(buffer), &written)) 618 | throw Utils::Exception(::GetLastError(), L"GetTokenInformation(TokenUser) failed with code %d", ::GetLastError()); 619 | 620 | auto user = reinterpret_cast(buffer); 621 | 622 | wchar_t name[256], domain[256]; 623 | DWORD nameSize = _countof(name), 624 | domainSize = _countof(domain); 625 | SID_NAME_USE type; 626 | if (!::LookupAccountSidW(NULL, user->User.Sid, name, &nameSize, domain, &domainSize, &type)) 627 | throw Utils::Exception(::GetLastError(), L"LookupAccountSidW() failed with code %d", ::GetLastError()); 628 | 629 | if (domainSize > 1) 630 | { 631 | userName = domain; 632 | userName += L"\\"; 633 | userName += name; 634 | } 635 | else 636 | { 637 | userName = name; 638 | } 639 | } 640 | 641 | void TokenBase::GetUserSIDString(std::wstring& sid) 642 | { 643 | DWORD written = 0; 644 | char buffer[256]; 645 | 646 | if (!::GetTokenInformation(Handle::GetNativeHandle(), TokenUser, &buffer, sizeof(buffer), &written)) 647 | throw Utils::Exception(::GetLastError(), L"GetTokenInformation(TokenUser) failed with code %d", ::GetLastError()); 648 | 649 | LPWSTR sidString = nullptr; 650 | auto user = reinterpret_cast(buffer); 651 | if (!::ConvertSidToStringSidW(user->User.Sid, &sidString)) 652 | throw Utils::Exception(::GetLastError(), L"ConvertSidToStringSid() failed with code %d", ::GetLastError()); 653 | 654 | sid = sidString; 655 | } 656 | 657 | bool TokenBase::IsElevated() 658 | { 659 | DWORD written = 0; 660 | TOKEN_ELEVATION elevated; 661 | 662 | if (!::GetTokenInformation(Handle::GetNativeHandle(), TokenElevation, &elevated, sizeof(elevated), &written)) 663 | throw Utils::Exception(::GetLastError(), L"GetTokenInformation(TokenElevation) failed with code %d", ::GetLastError()); 664 | 665 | return (elevated.TokenIsElevated ? true : false); 666 | } 667 | 668 | PSID TokenBase::AllocateSidByIntegrityLevel(IntegrityLevel level) 669 | { 670 | std::wstring sidName; 671 | 672 | switch (level) 673 | { 674 | case IntegrityLevel::Untrusted: 675 | sidName = L"S-1-16-0"; 676 | break; 677 | case IntegrityLevel::Low: 678 | sidName = L"S-1-16-4096"; 679 | break; 680 | case IntegrityLevel::Medium: 681 | sidName = L"S-1-16-8192"; 682 | break; 683 | case IntegrityLevel::MediumPlus: 684 | sidName = L"S-1-16-8448"; 685 | break; 686 | case IntegrityLevel::High: 687 | sidName = L"S-1-16-12288"; 688 | break; 689 | case IntegrityLevel::System: 690 | sidName = L"S-1-16-16384"; 691 | break; 692 | case IntegrityLevel::Protected: 693 | sidName = L"S-1-16-20480"; 694 | break; 695 | case IntegrityLevel::Secure: 696 | sidName = L"S-1-16-28672"; 697 | break; 698 | default: 699 | throw Utils::Exception(L"Unknown integrity level %d", level); 700 | break; 701 | }; 702 | 703 | PSID sid = NULL; 704 | if (!::ConvertStringSidToSidW(sidName.c_str(), &sid)) 705 | throw Utils::Exception(::GetLastError(), L"ConvertStringSidToSid() failed with code %d", ::GetLastError()); 706 | 707 | return sid; 708 | } 709 | 710 | // ================= 711 | 712 | PrimaryToken::PrimaryToken(Process& source, DWORD access, bool duplicate) 713 | { 714 | HANDLE object = nullptr; 715 | 716 | if (!::OpenProcessToken(source.GetNativeHandle(), access | (duplicate ? TOKEN_DUPLICATE : 0), &object)) 717 | throw Utils::Exception(::GetLastError(), L"OpenProcessToken(pid:%d) failed with code %d", source.GetProcessID(), ::GetLastError()); 718 | 719 | if (duplicate) 720 | { 721 | Handle primary(object); 722 | if (!::DuplicateTokenEx(object, 0, NULL, SecurityImpersonation, TokenPrimary, &object)) 723 | throw Utils::Exception(::GetLastError(), L"DuplicateToken(pid:%d) failed with code %d", source.GetProcessID(), ::GetLastError()); 724 | } 725 | 726 | Handle::SetHandle(object); 727 | } 728 | 729 | PrimaryToken::PrimaryToken(HANDLE token, bool duplicate) 730 | { 731 | if (duplicate) 732 | { 733 | Handle primary(token); 734 | if (!::DuplicateTokenEx(token, 0, NULL, SecurityImpersonation, TokenPrimary, &token)) 735 | throw Utils::Exception(::GetLastError(), L"DuplicateToken() failed with code %d", ::GetLastError()); 736 | } 737 | 738 | Handle::SetHandle(token); 739 | } 740 | 741 | // ================= 742 | 743 | ImpersonateToken::ImpersonateToken(Process& source, DWORD access) 744 | { 745 | HANDLE object = nullptr; 746 | 747 | if (!::OpenProcessToken(source.GetNativeHandle(), access | TOKEN_DUPLICATE, &object)) 748 | throw Utils::Exception(::GetLastError(), L"OpenProcessToken(pid:%d) failed with code %d", source.GetProcessID(), ::GetLastError()); 749 | 750 | Handle primary(object); 751 | if (!::DuplicateTokenEx(object, access, NULL, SecurityImpersonation, TokenImpersonation, &object)) 752 | throw Utils::Exception(::GetLastError(), L"DuplicateToken(pid:%d) failed with code %d", source.GetProcessID(), ::GetLastError()); 753 | 754 | Handle::SetHandle(object); 755 | } 756 | 757 | ImpersonateToken::ImpersonateToken(HANDLE token, bool duplicate) 758 | { 759 | if (duplicate) 760 | { 761 | Handle primary(token); 762 | if (!::DuplicateTokenEx(token, 0, NULL, SecurityImpersonation, TokenImpersonation, &token)) 763 | throw Utils::Exception(::GetLastError(), L"DuplicateToken() failed with code %d", ::GetLastError()); 764 | } 765 | 766 | Handle::SetHandle(token); 767 | } 768 | 769 | // ================= 770 | 771 | SecurityDescriptor::SecurityDescriptor(Handle& file) : 772 | _descriptor(nullptr), 773 | _dacl(nullptr) 774 | { 775 | auto result = ::GetSecurityInfo( 776 | file.GetNativeHandle(), 777 | SE_FILE_OBJECT, 778 | DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, 779 | &_owner, 780 | &_group, 781 | &_dacl, 782 | NULL, 783 | &_descriptor 784 | ); 785 | if (result != ERROR_SUCCESS) 786 | throw Utils::Exception(::GetLastError(), L"GetSecurityInfo() failed with code %d", ::GetLastError()); 787 | } 788 | 789 | SecurityDescriptor::~SecurityDescriptor() 790 | { 791 | ::LocalFree(_descriptor); 792 | } 793 | 794 | PSECURITY_DESCRIPTOR SecurityDescriptor::GetNativeSecurityDescriptor() 795 | { 796 | return _descriptor; 797 | } 798 | 799 | // ================= 800 | 801 | TokenAccessChecker::TokenAccessChecker(Process& process) : 802 | _token(process, TOKEN_DUPLICATE | TOKEN_QUERY) 803 | { 804 | } 805 | 806 | TokenAccessChecker::TokenAccessChecker(ImpersonateToken& token) : 807 | _token(token) 808 | { 809 | } 810 | 811 | bool TokenAccessChecker::IsFileObjectAccessible(SecurityDescriptor& descriptor, DWORD desiredAccess) const 812 | { 813 | BOOL accessStatus = FALSE; 814 | GENERIC_MAPPING mapping = {}; 815 | PRIVILEGE_SET PrivilegeSet; 816 | DWORD dwPrivSetSize = sizeof(PRIVILEGE_SET); 817 | DWORD dwAccessAllowed = 0; 818 | 819 | mapping.GenericRead = FILE_GENERIC_READ; 820 | mapping.GenericWrite = FILE_GENERIC_WRITE; 821 | mapping.GenericAll = FILE_GENERIC_READ | FILE_GENERIC_WRITE; 822 | 823 | MapGenericMask(&desiredAccess, &mapping); 824 | 825 | if (!::IsValidSecurityDescriptor(descriptor.GetNativeSecurityDescriptor())) 826 | throw Utils::Exception(::GetLastError(), L"IsValidSecurityDescriptor() failed with code %d", ::GetLastError()); 827 | 828 | if (!::AccessCheck(descriptor.GetNativeSecurityDescriptor(), _token.GetNativeHandle(), desiredAccess, &mapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessAllowed, &accessStatus)) 829 | throw Utils::Exception(::GetLastError(), L"AccessCheck() failed with code %d", ::GetLastError()); 830 | 831 | return (accessStatus != FALSE); 832 | } 833 | 834 | // ================= 835 | 836 | File::File(const wchar_t* path, DWORD access, DWORD share, bool newFile) : 837 | Handle(::CreateFileW(path, access, share, NULL, newFile ? OPEN_ALWAYS : OPEN_EXISTING, 0, NULL)) 838 | { 839 | if (!Handle::IsValid()) 840 | throw Utils::Exception(::GetLastError(), L"CreateFileW(file) failed with code %d", ::GetLastError()); 841 | } 842 | 843 | void File::Write(void* buffer, size_t size) 844 | { 845 | DWORD written = 0; 846 | if (!::WriteFile(Handle::GetNativeHandle(), buffer, size, &written, NULL)) 847 | throw Utils::Exception(::GetLastError(), L"WriteFile() failed with code %d", ::GetLastError()); 848 | } 849 | 850 | void File::SetDeleteOnClose() 851 | { 852 | FILE_DISPOSITION_INFO info = {}; 853 | info.DeleteFileW = TRUE; 854 | if (!::SetFileInformationByHandle(Handle::GetNativeHandle(), FileDispositionInfo, &info, sizeof(info))) 855 | throw Utils::Exception(::GetLastError(), L"SetFileInformationByHandle() failed with code %d", ::GetLastError()); 856 | } 857 | 858 | // ================= 859 | 860 | ImageMapping::ImageMapping(const wchar_t* path) : _mappingSize(0) 861 | { 862 | File file(path, GENERIC_READ); 863 | 864 | Handle::SetHandle( 865 | ::CreateFileMappingW( 866 | file.GetNativeHandle(), 867 | NULL, 868 | PAGE_READONLY | SEC_IMAGE, 869 | 0, 870 | 0, 871 | NULL 872 | ) 873 | ); 874 | if (!Handle::IsValid()) 875 | throw Utils::Exception(::GetLastError(), L"CreateFileMappingW(image) failed with code %d", ::GetLastError()); 876 | 877 | _mapping = ::MapViewOfFile(Handle::GetNativeHandle(), FILE_MAP_READ, 0, 0, 0); 878 | if (!_mapping) 879 | throw Utils::Exception(::GetLastError(), L"MapViewOfFile(image) failed with code %d", ::GetLastError()); 880 | } 881 | 882 | ImageMapping::~ImageMapping() 883 | { 884 | ::UnmapViewOfFile(_mapping); 885 | } 886 | 887 | void* ImageMapping::GetAddress() 888 | { 889 | return _mapping; 890 | } 891 | 892 | size_t ImageMapping::GetSize() 893 | { 894 | if (_mappingSize) 895 | return _mappingSize; 896 | 897 | MEMORY_BASIC_INFORMATION info; 898 | if (!::VirtualQuery(_mapping, &info, sizeof(info))) 899 | throw Utils::Exception(::GetLastError(), L"VirtualQuery() failed with code %d", ::GetLastError()); 900 | 901 | auto regionBase = info.AllocationBase; 902 | size_t regionSize = 0; 903 | void* regionPtr = nullptr; 904 | do 905 | { 906 | if (regionBase != info.AllocationBase) 907 | break; 908 | 909 | regionSize += info.RegionSize; 910 | regionPtr = reinterpret_cast(reinterpret_cast(info.BaseAddress) + info.RegionSize); 911 | } 912 | while (::VirtualQuery(regionPtr, &info, sizeof(info))); 913 | 914 | if (::GetLastError() != ERROR_SUCCESS) 915 | throw Utils::Exception(::GetLastError(), L"VirtualQuery() failed with code %d", ::GetLastError()); 916 | 917 | return _mappingSize = regionSize; 918 | } 919 | 920 | // ================= 921 | 922 | std::wstring FileUtils::BuildPath(const std::wstring& directory, const std::wstring& file) 923 | { 924 | std::wstring path = directory; 925 | path += L"\\"; 926 | path += file; 927 | return path; 928 | } 929 | 930 | void FileUtils::ExtractFileDirectory(const std::wstring& path, std::wstring& directory) 931 | { 932 | auto index = path.rfind('\\'); 933 | if (index == std::wstring::npos) 934 | directory.clear(); 935 | else 936 | directory = path.substr(0, index); 937 | } 938 | 939 | void FileUtils::ExtractFileName(const std::wstring& path, std::wstring& name) 940 | { 941 | auto index = path.rfind('\\'); 942 | if (index != std::wstring::npos && index + 1 <= path.size()) 943 | name = path.substr(index + 1); 944 | else 945 | name.clear(); 946 | } 947 | 948 | void FileUtils::NormalizePath(std::wstring& path) 949 | { 950 | std::vector buffer; 951 | buffer.resize(MAX_PATH); 952 | 953 | for (int i = 0; i < 5; i++) 954 | { 955 | wchar_t* output; 956 | auto length = ::GetFullPathNameW(path.c_str(), static_cast(buffer.size()), &buffer[0], &output); 957 | if (!length) 958 | { 959 | throw Utils::Exception(L"GetFullPathNameW() failed with code %d", ::GetLastError()); 960 | } 961 | else if (length && output) 962 | { 963 | path = &buffer[0]; 964 | return; 965 | } 966 | 967 | buffer.resize(length + buffer.size()); 968 | } 969 | 970 | throw Utils::Exception(L"Can't normalize path"); 971 | } 972 | 973 | bool FileUtils::IsPathRelative(const std::wstring& path) 974 | { 975 | return !!::PathIsRelativeW(path.c_str()); 976 | } 977 | 978 | bool FileUtils::PathExists(const std::wstring& path) 979 | { 980 | return !!::PathFileExistsW(path.c_str()); 981 | } 982 | 983 | File FileUtils::CreateTempFile(std::wstring& path, DWORD access, DWORD share) 984 | { 985 | auto temp = SystemInformation::GetTempDir(); 986 | 987 | wchar_t name[MAX_PATH] = {}; 988 | if (!::GetTempFileNameW(temp.c_str(), L"tmp", 0, name)) 989 | throw Utils::Exception(::GetLastError(), L"GetTempFileNameW() failed with code %d", ::GetLastError()); 990 | 991 | path = name; 992 | 993 | return File(path.c_str(), access, share, true); 994 | } 995 | 996 | std::wstring FileUtils::ApplyWow64Redirection(const std::wstring& path) 997 | { 998 | std::wstring newPath = path; 999 | 1000 | auto sys32dir = SystemInformation::GetSystem32Dir(); 1001 | auto sys64dir = SystemInformation::GetSysWow64Dir(); 1002 | 1003 | if (RedirectDirectory(newPath, sys32dir, sys64dir)) 1004 | return newPath; 1005 | 1006 | auto lastgood32dir = SystemInformation::GetLastGoodSystem32Dir(); 1007 | auto lastgood64dir = SystemInformation::GetLastGoodSysWow64Dir(); 1008 | 1009 | if (RedirectDirectory(newPath, lastgood32dir, lastgood64dir)) 1010 | return newPath; 1011 | 1012 | return path; 1013 | } 1014 | 1015 | bool FileUtils::RedirectDirectory(std::wstring& path, const std::wstring& from, const std::wstring& to) 1016 | { 1017 | if (_wcsnicmp(path.c_str(), from.c_str(), from.size()) != 0) 1018 | return false; 1019 | 1020 | auto newPath = to; 1021 | 1022 | if (path.size() > from.size()) 1023 | newPath += &path[from.size()]; 1024 | 1025 | path = newPath; 1026 | 1027 | return true; 1028 | } 1029 | 1030 | std::wstring FileUtils::FindFirstMatchedFile(const std::wstring& path) 1031 | { 1032 | WIN32_FIND_DATAW info; 1033 | 1034 | auto found = ::FindFirstFileW(path.c_str(), &info); 1035 | if (found == INVALID_HANDLE_VALUE) 1036 | throw Utils::Exception(::GetLastError(), L"FindFirstFileW() failed with code %d", ::GetLastError()); 1037 | 1038 | FindClose(found); 1039 | 1040 | return info.cFileName; 1041 | } 1042 | 1043 | // ================= 1044 | 1045 | Directory::Directory(const wchar_t* path, DWORD access, DWORD share) : 1046 | Handle(::CreateFileW(path, access, share, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) 1047 | { 1048 | if (!Handle::IsValid()) 1049 | throw Utils::Exception(::GetLastError(), L"CreateFileW(dir) failed with code %d", ::GetLastError()); 1050 | } 1051 | 1052 | bool Directory::IsDirectory(const wchar_t* path) 1053 | { 1054 | DWORD attribs = ::GetFileAttributesW(path); 1055 | if (attribs == INVALID_FILE_ATTRIBUTES) 1056 | return false; 1057 | return (attribs & FILE_ATTRIBUTE_DIRECTORY ? true : false); 1058 | } 1059 | 1060 | // ================= 1061 | 1062 | Bitness SystemInformation::GetSystemBitness() 1063 | { 1064 | SYSTEM_INFO info; 1065 | ::GetNativeSystemInfo(&info); 1066 | 1067 | Bitness bitness; 1068 | switch (info.wProcessorArchitecture) 1069 | { 1070 | case PROCESSOR_ARCHITECTURE_AMD64: 1071 | case 12/*PROCESSOR_ARCHITECTURE_ARM64*/: 1072 | case PROCESSOR_ARCHITECTURE_IA64: 1073 | bitness = Bitness::Arch64; 1074 | break; 1075 | case PROCESSOR_ARCHITECTURE_INTEL: 1076 | case PROCESSOR_ARCHITECTURE_ARM: 1077 | bitness = Bitness::Arch32; 1078 | break; 1079 | default: 1080 | throw Utils::Exception(L"Unknown architecture %d", info.wProcessorArchitecture); 1081 | } 1082 | 1083 | return bitness; 1084 | } 1085 | 1086 | std::wstring SystemInformation::GetSystem32Dir() 1087 | { 1088 | wchar_t buffer[MAX_PATH]; 1089 | 1090 | if (!::GetSystemDirectoryW(buffer, _countof(buffer))) 1091 | throw Utils::Exception(::GetLastError(), L"GetSystemDirectoryW() failed with code %d", ::GetLastError()); 1092 | 1093 | return buffer; 1094 | } 1095 | 1096 | std::wstring SystemInformation::GetSysWow64Dir() 1097 | { 1098 | wchar_t buffer[MAX_PATH]; 1099 | 1100 | if (!::GetWindowsDirectoryW(buffer, _countof(buffer))) 1101 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1102 | 1103 | wcscat_s(buffer, L"\\SysWOW64"); 1104 | 1105 | return buffer; 1106 | } 1107 | 1108 | std::wstring SystemInformation::GetSystemDir() 1109 | { 1110 | wchar_t buffer[MAX_PATH]; 1111 | 1112 | if (!::GetWindowsDirectoryW(buffer, _countof(buffer))) 1113 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1114 | 1115 | wcscat_s(buffer, L"\\System"); 1116 | 1117 | return buffer; 1118 | } 1119 | 1120 | std::wstring SystemInformation::GetWindowsDir() 1121 | { 1122 | wchar_t buffer[MAX_PATH]; 1123 | 1124 | if (!::GetWindowsDirectoryW(buffer, _countof(buffer))) 1125 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1126 | 1127 | return buffer; 1128 | } 1129 | 1130 | std::wstring SystemInformation::GetTempDir() 1131 | { 1132 | wchar_t buffer[MAX_PATH]; 1133 | 1134 | if (!::GetTempPathW(_countof(buffer), buffer)) 1135 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1136 | 1137 | return buffer; 1138 | } 1139 | 1140 | std::wstring SystemInformation::GetLastGoodSystem32Dir() 1141 | { 1142 | wchar_t buffer[MAX_PATH]; 1143 | 1144 | if (!::GetWindowsDirectoryW(buffer, _countof(buffer))) 1145 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1146 | 1147 | wcscat_s(buffer, L"\\lastgood\\System32"); 1148 | 1149 | return buffer; 1150 | } 1151 | 1152 | std::wstring SystemInformation::GetLastGoodSysWow64Dir() 1153 | { 1154 | wchar_t buffer[MAX_PATH]; 1155 | 1156 | if (!::GetWindowsDirectoryW(buffer, _countof(buffer))) 1157 | throw Utils::Exception(::GetLastError(), L"GetCurrentDirectoryW() failed with code %d", ::GetLastError()); 1158 | 1159 | wcscat_s(buffer, L"\\lastgood\\SysWOW64"); 1160 | 1161 | return buffer; 1162 | } 1163 | 1164 | // ================= 1165 | 1166 | Wow64NoFsRedirection::Wow64NoFsRedirection() : _revert(false) 1167 | { 1168 | if (SystemInformation::GetSystemBitness() != Bitness::Arch64) 1169 | return; 1170 | 1171 | if (ProcessInformation::GetCurrentProcessBitness() == Bitness::Arch64) 1172 | return; 1173 | 1174 | if (!::Wow64DisableWow64FsRedirection(&_old)) 1175 | throw Utils::Exception(::GetLastError(), L"Wow64DisableWow64FsRedirection() failed with code %d", ::GetLastError()); 1176 | 1177 | _revert = true; 1178 | } 1179 | 1180 | Wow64NoFsRedirection::~Wow64NoFsRedirection() 1181 | { 1182 | if (!_revert) 1183 | return; 1184 | 1185 | if (!::Wow64RevertWow64FsRedirection(_old)) 1186 | throw Utils::Exception(::GetLastError(), L"Wow64RevertWow64FsRedirection() failed with code %d", ::GetLastError()); 1187 | } 1188 | 1189 | // ================= 1190 | 1191 | RegistryKey::RegistryKey(BaseKeys base, const wchar_t* key, DWORD access) : _hkey(0) 1192 | { 1193 | auto result = ::RegOpenKeyExW(ConvertBaseToHKEY(base), key, 0, access, &_hkey); 1194 | if (result != ERROR_SUCCESS) 1195 | throw Utils::Exception(result, L"RegOpenKeyExW() failed with code %d", result); 1196 | } 1197 | 1198 | RegistryKey::~RegistryKey() 1199 | { 1200 | ::RegCloseKey(_hkey); 1201 | } 1202 | 1203 | HKEY RegistryKey::GetNativeHKEY() const 1204 | { 1205 | return _hkey; 1206 | } 1207 | 1208 | HKEY RegistryKey::ConvertBaseToHKEY(BaseKeys base) 1209 | { 1210 | switch (base) 1211 | { 1212 | case BaseKeys::Root: 1213 | return HKEY_CLASSES_ROOT; 1214 | case BaseKeys::CurrentConfig: 1215 | return HKEY_CURRENT_CONFIG; 1216 | case BaseKeys::CurrentUser: 1217 | return HKEY_CURRENT_USER; 1218 | case BaseKeys::LocalMachine: 1219 | return HKEY_LOCAL_MACHINE; 1220 | default: 1221 | break; 1222 | } 1223 | throw Utils::Exception(L"Unknown base registry key"); 1224 | } 1225 | 1226 | // ================= 1227 | 1228 | RegistryValue::RegistryValue(const RegistryKey& key, const wchar_t* value) : 1229 | _type(RegistryValueType::Unknown) 1230 | { 1231 | DWORD size = 0x400; 1232 | 1233 | _value.resize(size / sizeof(wchar_t)); 1234 | 1235 | while (true) //TODO: max amount of attempts 1236 | { 1237 | DWORD type; 1238 | auto result = ::RegQueryValueExW( 1239 | key.GetNativeHKEY(), 1240 | value, 1241 | NULL, 1242 | &type, 1243 | reinterpret_cast( 1244 | const_cast(_value.c_str()) 1245 | ), 1246 | &size 1247 | ); 1248 | if (result == ERROR_MORE_DATA) 1249 | { 1250 | _value.resize(_value.size() + 0x100); 1251 | continue; 1252 | } 1253 | else if (result != ERROR_SUCCESS) 1254 | { 1255 | throw Utils::Exception(result, L"RegQueryValueExW failed with code %d", result); 1256 | } 1257 | 1258 | _type = ConvertToRegistryType(type); 1259 | 1260 | size_t charsSize = size / sizeof(wchar_t); 1261 | charsSize += (size % 2 ? 1 : 0); 1262 | _value.resize(charsSize); 1263 | break; 1264 | } 1265 | } 1266 | 1267 | RegistryValueType RegistryValue::GetType() const 1268 | { 1269 | return _type; 1270 | } 1271 | 1272 | const std::wstring& RegistryValue::GetValue() const 1273 | { 1274 | return _value; 1275 | } 1276 | 1277 | RegistryValueType RegistryValue::ConvertToRegistryType(DWORD type) 1278 | { 1279 | switch (type) 1280 | { 1281 | case REG_NONE: 1282 | return RegistryValueType::None; 1283 | case REG_SZ: 1284 | return RegistryValueType::String; 1285 | case REG_EXPAND_SZ: 1286 | return RegistryValueType::ExpandString; 1287 | case REG_BINARY: 1288 | return RegistryValueType::Binary; 1289 | case REG_DWORD: 1290 | return RegistryValueType::Dword; 1291 | case REG_DWORD_BIG_ENDIAN: 1292 | return RegistryValueType::DwordBigEndian; 1293 | case REG_LINK: 1294 | return RegistryValueType::Link; 1295 | case REG_MULTI_SZ: 1296 | return RegistryValueType::MultiString; 1297 | case REG_RESOURCE_LIST: 1298 | return RegistryValueType::ResourceList; 1299 | case REG_FULL_RESOURCE_DESCRIPTOR: 1300 | return RegistryValueType::FullResourceDescrition; 1301 | case REG_RESOURCE_REQUIREMENTS_LIST: 1302 | return RegistryValueType::ResourceRequirementsList; 1303 | case REG_QWORD: 1304 | return RegistryValueType::Qword; 1305 | default: 1306 | break; 1307 | } 1308 | return RegistryValueType::Unknown; 1309 | } 1310 | 1311 | // ================= 1312 | 1313 | RegistryDwordValue::RegistryDwordValue(const RegistryValue& value) 1314 | { 1315 | LoadDword(value); 1316 | } 1317 | 1318 | RegistryDwordValue::RegistryDwordValue(const RegistryKey& key, const wchar_t* value) 1319 | { 1320 | RegistryValue regValue(key, value); 1321 | LoadDword(regValue); 1322 | } 1323 | 1324 | DWORD RegistryDwordValue::GetValue() const 1325 | { 1326 | return _value; 1327 | } 1328 | 1329 | void RegistryDwordValue::LoadDword(const RegistryValue& value) 1330 | { 1331 | if (value.GetType() != RegistryValueType::Dword) 1332 | throw Utils::Exception(L"Not a dword value, type:%d", value.GetType()); 1333 | 1334 | auto& data = value.GetValue(); 1335 | if (data.size() < sizeof(DWORD)) 1336 | throw Utils::Exception(L"Invalid dword data size %d", data.size()); 1337 | 1338 | _value = *reinterpret_cast(data.c_str()); 1339 | } 1340 | 1341 | // ================= 1342 | 1343 | RegistryMultiStringValue::RegistryMultiStringValue(const RegistryValue& value) 1344 | { 1345 | LoadStrings(value); 1346 | } 1347 | 1348 | RegistryMultiStringValue::RegistryMultiStringValue(const RegistryKey& key, const wchar_t* value) 1349 | { 1350 | RegistryValue regValue(key, value); 1351 | LoadStrings(regValue); 1352 | } 1353 | 1354 | void RegistryMultiStringValue::LoadStrings(const RegistryValue& value) 1355 | { 1356 | if (value.GetType() != RegistryValueType::MultiString) 1357 | throw Utils::Exception(L"Not a multi-string value, type:%d", value.GetType()); 1358 | 1359 | auto data = value.GetValue(); 1360 | const wchar_t* string = data.c_str(); 1361 | auto size = data.size(); 1362 | for (size_t i = 0; i < size; i++) 1363 | { 1364 | if (string) 1365 | { 1366 | if (data[i] != L'\0') 1367 | continue; 1368 | 1369 | std::vector::push_back(string); 1370 | string = nullptr; 1371 | } 1372 | else 1373 | { 1374 | if (data[i] == L'\0') 1375 | continue; 1376 | 1377 | string = &data[i]; 1378 | } 1379 | } 1380 | 1381 | if (string) 1382 | std::vector::push_back(string); 1383 | } 1384 | 1385 | // ================= 1386 | 1387 | RegistryStringValue::RegistryStringValue(const RegistryValue& value) 1388 | { 1389 | LoadRegString(value); 1390 | } 1391 | 1392 | RegistryStringValue::RegistryStringValue(const RegistryKey& key, const wchar_t* value) 1393 | { 1394 | RegistryValue regValue(key, value); 1395 | LoadRegString(regValue); 1396 | } 1397 | 1398 | const std::wstring& RegistryStringValue::GetValue() const 1399 | { 1400 | return _value; 1401 | } 1402 | 1403 | void RegistryStringValue::LoadRegString(const RegistryValue& value) 1404 | { 1405 | if (value.GetType() != RegistryValueType::String) 1406 | throw Utils::Exception(L"Not a string value, type:%d", value.GetType()); 1407 | 1408 | _value = value.GetValue(); 1409 | } 1410 | 1411 | // ================= 1412 | 1413 | RegistryExpandedStringValue::RegistryExpandedStringValue(const RegistryValue& value) 1414 | { 1415 | LoadRegString(value); 1416 | } 1417 | 1418 | RegistryExpandedStringValue::RegistryExpandedStringValue(const RegistryKey& key, const wchar_t* value) 1419 | { 1420 | RegistryValue regValue(key, value); 1421 | LoadRegString(regValue); 1422 | } 1423 | 1424 | const std::wstring& RegistryExpandedStringValue::GetValue() const 1425 | { 1426 | return _value; 1427 | } 1428 | 1429 | void RegistryExpandedStringValue::LoadRegString(const RegistryValue& value) 1430 | { 1431 | if (value.GetType() != RegistryValueType::ExpandString) 1432 | throw Utils::Exception(L"Not an expanded string value, type:%d", value.GetType()); 1433 | 1434 | // ExpandEnvironmentStrings insted of PathUnExpandEnvStringsA 1435 | //_value = value.GetValue(); 1436 | 1437 | auto unexpanded = value.GetValue(); 1438 | _value.resize(unexpanded.size() + 0x200); 1439 | 1440 | for (int i = 0; i < 5; i++) 1441 | { 1442 | auto result = ::ExpandEnvironmentStringsW( 1443 | unexpanded.c_str(), 1444 | const_cast(_value.c_str()), 1445 | static_cast(_value.size()) 1446 | ); 1447 | if (result) 1448 | { 1449 | _value.resize(result); 1450 | return; 1451 | } 1452 | 1453 | if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) 1454 | { 1455 | _value.resize(_value.size() + 0x200); 1456 | continue; 1457 | } 1458 | 1459 | throw Utils::Exception(L"Can't expand registry value, code: %d", ::GetLastError()); 1460 | } 1461 | 1462 | throw Utils::Exception(L"Can't expand registry value"); 1463 | } 1464 | 1465 | // ================= 1466 | 1467 | EnumRegistryValues::EnumRegistryValues(BaseKeys base, const wchar_t* key) 1468 | { 1469 | RegistryKey hkey(base, key); 1470 | std::wstring name; 1471 | 1472 | DWORD index = 0; 1473 | while (true) 1474 | { 1475 | name.resize(0x100); 1476 | 1477 | DWORD nameSize = static_cast(name.size()), nameType; 1478 | auto result = ::RegEnumValueW( 1479 | hkey.GetNativeHKEY(), 1480 | index, 1481 | const_cast(name.c_str()), 1482 | &nameSize, 1483 | NULL, 1484 | &nameType, 1485 | NULL, 1486 | NULL 1487 | ); 1488 | if (result == ERROR_MORE_DATA) 1489 | { 1490 | name.resize(name.size() + 0x100); 1491 | continue; 1492 | } 1493 | else if (result == ERROR_NO_MORE_ITEMS) 1494 | { 1495 | break; 1496 | } 1497 | else if (result != ERROR_SUCCESS) 1498 | { 1499 | throw Utils::Exception(result, L"RegEnumValueW failed with code %d", result); 1500 | } 1501 | 1502 | name.resize(wcslen(name.c_str())); 1503 | _values.emplace(name, RegistryValue(hkey, name.c_str())); 1504 | index++; 1505 | } 1506 | } 1507 | 1508 | const RegistryValues EnumRegistryValues::GetValues() const 1509 | { 1510 | return _values; 1511 | } 1512 | 1513 | // ================= 1514 | 1515 | ActivationContext::ActivationContext(const wchar_t* path, const wchar_t* assemblyDir) 1516 | { 1517 | ACTCTXW context = {}; 1518 | context.cbSize = sizeof(context); 1519 | context.lpSource = path; 1520 | 1521 | if (assemblyDir) 1522 | { 1523 | context.lpAssemblyDirectory = assemblyDir; 1524 | context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; 1525 | } 1526 | 1527 | LastError lastError; 1528 | Handle::SetHandle(lastError.Proxymize(::CreateActCtxW(&context)), &DestroyActivationContext); 1529 | 1530 | if (!Handle::IsValid()) 1531 | throw Utils::Exception(lastError.GetCode(), L"CreateActCtxW() failed with code %d", lastError.GetCode()); 1532 | } 1533 | 1534 | ActivationContext::ActivationContext(ImageMapping& image) 1535 | { 1536 | ACTCTXW context = {}; 1537 | context.cbSize = sizeof(context); 1538 | context.dwFlags = ACTCTX_FLAG_HMODULE_VALID; 1539 | context.hModule = reinterpret_cast(image.GetAddress()); 1540 | 1541 | LastError lastError; 1542 | Handle::SetHandle(lastError.Proxymize(::CreateActCtxW(&context)), &DestroyActivationContext); 1543 | 1544 | if (!Handle::IsValid()) 1545 | throw Utils::Exception(lastError.GetCode(), L"CreateActCtxW() failed with code %d", lastError.GetCode()); 1546 | } 1547 | 1548 | ActivationContext::ActivationContext(bool loadDefault) 1549 | { 1550 | HANDLE context = nullptr; 1551 | 1552 | if (!loadDefault && !::GetCurrentActCtx(&context)) 1553 | throw Utils::Exception(::GetLastError(), L"GetCurrentActCtx() failed with code %d", ::GetLastError()); 1554 | 1555 | // A default activation context might be NULL 1556 | 1557 | if (context) 1558 | Handle::SetHandle(context, &DestroyActivationContext); 1559 | } 1560 | 1561 | void ActivationContext::DestroyActivationContext(HANDLE object) 1562 | { 1563 | ::ReleaseActCtx(object); 1564 | } 1565 | 1566 | // ================= 1567 | 1568 | ActivationContextRunLevel::ActivationContextRunLevel(ActivationContext& context) 1569 | { 1570 | ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION info; 1571 | SIZE_T written; 1572 | 1573 | if (!::QueryActCtxW(0, context.GetNativeHandle(), NULL, RunlevelInformationInActivationContext, &info, sizeof(info), &written)) 1574 | throw Utils::Exception(::GetLastError(), L"QueryActCtxW() failed with code %d", ::GetLastError()); 1575 | 1576 | _uiAccess = (info.UiAccess != 0); 1577 | switch (info.RunLevel) 1578 | { 1579 | case ACTCTX_RUN_LEVEL_UNSPECIFIED: 1580 | _runLevel = ActivationContextRunLevelType::Unspecified; 1581 | break; 1582 | case ACTCTX_RUN_LEVEL_AS_INVOKER: 1583 | _runLevel = ActivationContextRunLevelType::AsInvoker; 1584 | break; 1585 | case ACTCTX_RUN_LEVEL_HIGHEST_AVAILABLE: 1586 | _runLevel = ActivationContextRunLevelType::HighestAvailable; 1587 | break; 1588 | case ACTCTX_RUN_LEVEL_REQUIRE_ADMIN: 1589 | _runLevel = ActivationContextRunLevelType::RequireAdmin; 1590 | break; 1591 | default: 1592 | _runLevel = ActivationContextRunLevelType::Unknown; 1593 | break; 1594 | } 1595 | } 1596 | 1597 | ActivationContextRunLevelType ActivationContextRunLevel::GetRunLevel() const 1598 | { 1599 | return _runLevel; 1600 | } 1601 | 1602 | bool ActivationContextRunLevel::GetUIAccess() const 1603 | { 1604 | return _uiAccess; 1605 | } 1606 | 1607 | // ================= 1608 | 1609 | Assembly::Assembly(std::wstring& assemblyDirID, std::vector& assemblyFiles) : 1610 | _assemblyDirID(assemblyDirID), 1611 | _assemblyFiles(assemblyFiles) 1612 | { 1613 | } 1614 | 1615 | const std::wstring& Assembly::GetID() const 1616 | { 1617 | return _assemblyDirID; 1618 | } 1619 | 1620 | const std::vector& Assembly::GetFiles() const 1621 | { 1622 | return _assemblyFiles; 1623 | } 1624 | 1625 | // ================= 1626 | 1627 | ActivationContextAssemblies::ActivationContextAssemblies(ActivationContext& context) 1628 | { 1629 | std::vector buffer(1024); 1630 | 1631 | for (unsigned long i = 1; true; i++) 1632 | { 1633 | auto index = i + 1; 1634 | 1635 | if (!QueryAssembly(context, index, buffer)) 1636 | break; 1637 | 1638 | auto assemblyInfo = (PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION)&buffer[0]; 1639 | 1640 | if (!assemblyInfo->lpAssemblyDirectoryName) 1641 | continue; 1642 | 1643 | std::wstring assemblyDirName = assemblyInfo->lpAssemblyDirectoryName; 1644 | std::vector assemblyFiles; 1645 | 1646 | for (unsigned long a = 0; true; a++) 1647 | { 1648 | if (!QueryAssemblyFile(context, i, a, buffer)) 1649 | break; 1650 | 1651 | auto fileInfo = (PASSEMBLY_FILE_DETAILED_INFORMATION)&buffer[0]; 1652 | assemblyFiles.emplace_back(fileInfo->lpFileName); 1653 | } 1654 | 1655 | emplace_back(assemblyDirName, assemblyFiles); 1656 | } 1657 | } 1658 | 1659 | bool ActivationContextAssemblies::QueryAssembly(ActivationContext& context, DWORD index, std::vector& buffer) 1660 | { 1661 | DWORD result = ERROR_SUCCESS; 1662 | SIZE_T written = 0; 1663 | 1664 | if (!::QueryActCtxW(0, context.GetNativeHandle(), &index, AssemblyDetailedInformationInActivationContext, &buffer[0], buffer.size(), &written)) 1665 | { 1666 | result = GetLastError(); 1667 | if (result == ERROR_INSUFFICIENT_BUFFER) 1668 | { 1669 | auto newSize = buffer.size() * 2; 1670 | 1671 | if (newSize >= written) 1672 | buffer.resize(buffer.size() * 2); 1673 | else 1674 | buffer.resize(written); 1675 | 1676 | if (::QueryActCtxW(0, context.GetNativeHandle(), &index, AssemblyDetailedInformationInActivationContext, &buffer[0], buffer.size(), &written)) 1677 | result = ERROR_SUCCESS; 1678 | else 1679 | result = GetLastError(); 1680 | } 1681 | } 1682 | 1683 | return (result == ERROR_SUCCESS); 1684 | } 1685 | 1686 | bool ActivationContextAssemblies::QueryAssemblyFile(ActivationContext& context, DWORD index, DWORD fileIndex, std::vector& buffer) 1687 | { 1688 | ACTIVATION_CONTEXT_QUERY_INDEX queryIndex = { index, fileIndex }; 1689 | DWORD result = ERROR_SUCCESS; 1690 | SIZE_T written = 0; 1691 | 1692 | if (!::QueryActCtxW(0, context.GetNativeHandle(), &queryIndex, FileInformationInAssemblyOfAssemblyInActivationContext, &buffer[0], buffer.size(), &written)) 1693 | { 1694 | result = GetLastError(); 1695 | if (result == ERROR_INSUFFICIENT_BUFFER) 1696 | { 1697 | auto newSize = buffer.size() * 2; 1698 | 1699 | if (newSize >= written) 1700 | buffer.resize(buffer.size() * 2); 1701 | else 1702 | buffer.resize(written); 1703 | 1704 | if (::QueryActCtxW(0, context.GetNativeHandle(), &queryIndex, FileInformationInAssemblyOfAssemblyInActivationContext, &buffer[0], buffer.size(), &written)) 1705 | result = ERROR_SUCCESS; 1706 | else 1707 | result = GetLastError(); 1708 | } 1709 | } 1710 | 1711 | return (result == ERROR_SUCCESS); 1712 | } 1713 | 1714 | // ================= 1715 | 1716 | ApplyDefaultSystemActivationContext::ApplyDefaultSystemActivationContext() : 1717 | ActivationContext(true), 1718 | _cookie(0) 1719 | { 1720 | if (!::ActivateActCtx(Handle::GetNativeHandle(), &_cookie)) 1721 | throw Utils::Exception(::GetLastError(), L"ActivateActCtx() failed with code %d", ::GetLastError()); 1722 | } 1723 | 1724 | ApplyDefaultSystemActivationContext::~ApplyDefaultSystemActivationContext() 1725 | { 1726 | if (!::DeactivateActCtx(0, _cookie)) 1727 | throw Utils::Exception(::GetLastError(), L"DeactivateActCtx() failed with code %d", ::GetLastError()); 1728 | } 1729 | 1730 | // ================= 1731 | 1732 | std::wstring ActivationContextUtils::LookupSxSDirUsingDefaultSystemActivationContext(const std::wstring& dll) 1733 | { 1734 | //TODO: move it to NTLib 1735 | static NTSTATUS (NTAPI*RtlDosApplyFileIsolationRedirection_Ustr)( 1736 | DWORD Flags, 1737 | IN PUNICODE_STRING OriginalName, 1738 | IN PUNICODE_STRING Extension, 1739 | IN OUT PUNICODE_STRING StaticString, 1740 | IN OUT PUNICODE_STRING DynamicString, 1741 | IN OUT PUNICODE_STRING *NewName, 1742 | IN PULONG NewFlags, 1743 | IN PSIZE_T FileNameSize, 1744 | IN PSIZE_T RequiredLength) = 0; 1745 | 1746 | if (!RtlDosApplyFileIsolationRedirection_Ustr) 1747 | *(FARPROC*)&RtlDosApplyFileIsolationRedirection_Ustr = ::GetProcAddress( 1748 | ::GetModuleHandle(L"ntdll.dll"), 1749 | "RtlDosApplyFileIsolationRedirection_Ustr" 1750 | ); 1751 | if (!RtlDosApplyFileIsolationRedirection_Ustr) 1752 | throw Utils::Exception(L"Default system activation context query is unsupported"); 1753 | 1754 | ApplyDefaultSystemActivationContext defaultContext; 1755 | 1756 | UNICODE_STRING dllName, staticString; 1757 | PUNICODE_STRING newName; 1758 | wchar_t buffer[MAX_PATH] = {}; 1759 | ULONG newFlags; 1760 | SIZE_T newSize, required; 1761 | 1762 | ::RtlInitUnicodeString(&dllName, const_cast(dll.c_str())); 1763 | 1764 | staticString.Length = 0; 1765 | staticString.MaximumLength = sizeof(buffer) - sizeof(wchar_t); 1766 | staticString.Buffer = buffer; 1767 | 1768 | auto status = RtlDosApplyFileIsolationRedirection_Ustr(1, &dllName, NULL, &staticString, NULL, &newName, &newFlags, &newSize, &required); 1769 | if (!NT_SUCCESS(status)) 1770 | throw Utils::Exception(status, L"RtlDosApplyFileIsolationRedirection_Ustr() failed with code %08X", status); 1771 | 1772 | return std::wstring(staticString.Buffer); 1773 | } 1774 | }; --------------------------------------------------------------------------------