├── .gitignore ├── createprocess.exe ├── createprocess-win.exe ├── starting_a_child_process_as_another_user_diagram.png ├── templates ├── packed_ordered_hash.h ├── packed_ordered_hash.cpp ├── shared_lib_util.h ├── shared_lib.h ├── static_mixed_var.h ├── static_wc_mixed_var.h └── packed_ordered_hash_util.h ├── README.md └── createprocess.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore debugging and intermediate files. 2 | *.obj 3 | createprocess/* 4 | -------------------------------------------------------------------------------- /createprocess.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/createprocess-windows/HEAD/createprocess.exe -------------------------------------------------------------------------------- /createprocess-win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/createprocess-windows/HEAD/createprocess-win.exe -------------------------------------------------------------------------------- /starting_a_child_process_as_another_user_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/createprocess-windows/HEAD/starting_a_child_process_as_another_user_diagram.png -------------------------------------------------------------------------------- /templates/packed_ordered_hash.h: -------------------------------------------------------------------------------- 1 | // Ordered hash map with integer and string keys in a memory compact format. No detachable nodes but is CPU cache-friendly. 2 | // Primarily useful for array style hashes with lots of insertions (up to 2^31 values), frequent key- and index-based lookups, some iteration, and few deletions. 3 | // (C) 2017 CubicleSoft. All Rights Reserved. 4 | 5 | #ifndef CUBICLESOFT_PACKEDORDEREDHASH 6 | #define CUBICLESOFT_PACKEDORDEREDHASH 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace CubicleSoft 14 | { 15 | template 16 | class PackedOrderedHash; 17 | template 18 | class PackedOrderedHashNoCopy; 19 | 20 | template 21 | class PackedOrderedHashNode 22 | { 23 | friend class PackedOrderedHash; 24 | friend class PackedOrderedHashNoCopy; 25 | 26 | public: 27 | inline std::int64_t GetIntKey() { return IntKey; } 28 | inline char *GetStrKey() { return StrKey; } 29 | inline size_t GetStrLen() { return *(size_t *)(StrKey - sizeof(size_t)); } 30 | 31 | private: 32 | std::uint32_t PrevHashIndex; 33 | std::uint32_t NextHashIndex; 34 | 35 | std::int64_t IntKey; 36 | char *StrKey; 37 | 38 | public: 39 | T Value; 40 | }; 41 | 42 | class PackedOrderedHashUtil 43 | { 44 | public: 45 | static size_t GetDJBX33XHashKey(const std::uint8_t *Str, size_t Size, size_t InitVal); 46 | static std::uint64_t GetSipHashKey(const std::uint8_t *Str, size_t Size, std::uint64_t Key1, std::uint64_t Key2, size_t cRounds, size_t dRounds); 47 | }; 48 | 49 | // PackedOrderedHash. A packed ordered hash. 50 | #include "packed_ordered_hash_util.h" 51 | 52 | // PackedOrderedHashNoCopy. A packed ordered hash with a private copy constructor and assignment operator. 53 | #define CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 54 | #include "packed_ordered_hash_util.h" 55 | #undef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 56 | } 57 | 58 | #endif -------------------------------------------------------------------------------- /templates/packed_ordered_hash.cpp: -------------------------------------------------------------------------------- 1 | // Packed Ordered Hash Utilities. 2 | // (C) 2014 CubicleSoft. All Rights Reserved. 3 | 4 | #include "packed_ordered_hash.h" 5 | 6 | #if (defined(__GNUC__) && __GNUC__ >= 7) || (defined(__clang__) && __clang_major__ >= 12) 7 | #define FALL_THROUGH __attribute__ ((fallthrough)) 8 | #else 9 | #define FALL_THROUGH ((void)0) 10 | #endif 11 | 12 | namespace CubicleSoft 13 | { 14 | size_t PackedOrderedHashUtil::GetDJBX33XHashKey(const std::uint8_t *Str, size_t Size, size_t InitVal) 15 | { 16 | std::uint32_t Result = (std::uint32_t)InitVal; 17 | std::uint32_t y; 18 | const size_t NumLeft = Size & 3; 19 | const std::uint8_t *StrEnd = Str + Size - NumLeft; 20 | 21 | while (Str != StrEnd) 22 | { 23 | // Changes the official implementation. 24 | y = *((const std::uint32_t *)Str); 25 | 26 | Result = ((Result << 5) + Result) ^ y; 27 | 28 | Str += 4; 29 | } 30 | 31 | switch (NumLeft) 32 | { 33 | case 3: Result = ((Result << 5) + Result) ^ ((std::uint32_t)Str[2]); FALL_THROUGH; 34 | case 2: Result = ((Result << 5) + Result) ^ ((std::uint32_t)Str[1]); FALL_THROUGH; 35 | case 1: Result = ((Result << 5) + Result) ^ ((std::uint32_t)Str[0]); break; 36 | case 0: break; 37 | } 38 | 39 | return (size_t)Result; 40 | } 41 | 42 | #define ROTL(x, b) (std::uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) 43 | 44 | #define SIPROUND \ 45 | do { \ 46 | v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; v0 = ROTL(v0, 32); \ 47 | v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \ 48 | v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \ 49 | v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; v2 = ROTL(v2, 32); \ 50 | } while(0) 51 | 52 | std::uint64_t PackedOrderedHashUtil::GetSipHashKey(const std::uint8_t *Str, size_t Size, std::uint64_t Key1, std::uint64_t Key2, size_t cRounds, size_t dRounds) 53 | { 54 | // "somepseudorandomlygeneratedbytes" 55 | std::uint64_t v0 = 0x736f6d6570736575ULL; 56 | std::uint64_t v1 = 0x646f72616e646f6dULL; 57 | std::uint64_t v2 = 0x6c7967656e657261ULL; 58 | std::uint64_t v3 = 0x7465646279746573ULL; 59 | std::uint64_t Result; 60 | std::uint64_t y; 61 | size_t x; 62 | const size_t NumLeft = Size & 7; 63 | const std::uint8_t *StrEnd = Str + Size - NumLeft; 64 | 65 | Result = ((std::uint64_t)Size) << 56; 66 | v3 ^= Key2; 67 | v2 ^= Key1; 68 | v1 ^= Key2; 69 | v0 ^= Key1; 70 | while (Str != StrEnd) 71 | { 72 | // Minor change to the official implementation. (Does endianness actually matter?) 73 | y = *((const std::uint64_t *)Str); 74 | 75 | v3 ^= y; 76 | 77 | for (x = 0; x < cRounds; ++x) SIPROUND; 78 | 79 | v0 ^= y; 80 | 81 | Str += 8; 82 | } 83 | 84 | switch (NumLeft) 85 | { 86 | case 7: Result |= ((std::uint64_t)Str[6]) << 48; FALL_THROUGH; 87 | case 6: Result |= ((std::uint64_t)Str[5]) << 40; FALL_THROUGH; 88 | case 5: Result |= ((std::uint64_t)Str[4]) << 32; FALL_THROUGH; 89 | case 4: Result |= ((std::uint64_t)Str[3]) << 24; FALL_THROUGH; 90 | case 3: Result |= ((std::uint64_t)Str[2]) << 16; FALL_THROUGH; 91 | case 2: Result |= ((std::uint64_t)Str[1]) << 8; FALL_THROUGH; 92 | case 1: Result |= ((std::uint64_t)Str[0]); break; 93 | case 0: break; 94 | } 95 | 96 | v3 ^= Result; 97 | 98 | for (x = 0; x < cRounds; ++x) SIPROUND; 99 | 100 | v0 ^= Result; 101 | v2 ^= 0xff; 102 | 103 | for (x = 0; x < dRounds; ++x) SIPROUND; 104 | 105 | Result = v0 ^ v1 ^ v2 ^ v3; 106 | 107 | return Result; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /templates/shared_lib_util.h: -------------------------------------------------------------------------------- 1 | // Shared library (DLL, so, etc.) loader and function call templates. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | // NOTE: This file is intended to be included from 'shared_lib.h'. 5 | 6 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 7 | 8 | template 9 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX(FunctionUtil &Func, RetType &ReturnVal, Args... FuncArgs) 10 | { 11 | FARPROC FuncAddressPtr; 12 | 13 | if ((FuncAddressPtr = Func.GetFuncPtr()) == NULL && (FuncAddressPtr = Func.LoadFuncPtr()) == NULL) return false; 14 | 15 | ReturnVal = (*((RetType (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 16 | 17 | return true; 18 | } 19 | 20 | template 21 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID(FunctionUtil &Func, Args... FuncArgs) 22 | { 23 | FARPROC FuncAddressPtr; 24 | 25 | if ((FuncAddressPtr = Func.GetFuncPtr()) == NULL || (FuncAddressPtr = Func.LoadFuncPtr()) == NULL) return false; 26 | 27 | (*((void (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 28 | 29 | return true; 30 | } 31 | 32 | // Doesn't call FreeLibrary() to avoid issues with multiple uses of the same DLL. 33 | template 34 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE(const char *Filename, const char *FuncName, RetType &ReturnVal, Args... FuncArgs) 35 | { 36 | HMODULE ModulePtr; 37 | FARPROC FuncAddressPtr; 38 | 39 | ModulePtr = ::LoadLibraryA(Filename); 40 | if (ModulePtr == NULL) return false; 41 | 42 | FuncAddressPtr = ::GetProcAddress(ModulePtr, FuncName); 43 | if (FuncAddressPtr == NULL) return false; 44 | 45 | ReturnVal = (*((RetType (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 46 | 47 | return true; 48 | } 49 | 50 | // Doesn't call FreeLibrary() to avoid issues with multiple uses of the same DLL. 51 | template 52 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID(const char *Filename, const char *FuncName, Args... FuncArgs) 53 | { 54 | HMODULE ModulePtr; 55 | FARPROC FuncAddressPtr; 56 | 57 | ModulePtr = ::LoadLibraryA(Filename); 58 | if (ModulePtr == NULL) return false; 59 | 60 | FuncAddressPtr = ::GetProcAddress(ModulePtr, FuncName); 61 | if (FuncAddressPtr == NULL) return false; 62 | 63 | (*((void (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 64 | 65 | return true; 66 | } 67 | 68 | #else 69 | 70 | template 71 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX(FunctionUtil &Func, RetType &ReturnVal, Args... FuncArgs) 72 | { 73 | void *FuncAddressPtr; 74 | 75 | if ((FuncAddressPtr = Func.GetFuncPtr()) == NULL && (FuncAddressPtr = Func.LoadFuncPtr()) == NULL) return false; 76 | 77 | ReturnVal = (*((RetType (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 78 | 79 | return true; 80 | } 81 | 82 | template 83 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID(FunctionUtil &Func, Args... FuncArgs) 84 | { 85 | void *FuncAddressPtr; 86 | 87 | if ((FuncAddressPtr = Func.GetFuncPtr()) == NULL || (FuncAddressPtr = Func.LoadFuncPtr()) == NULL) return false; 88 | 89 | (*((void (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 90 | 91 | return true; 92 | } 93 | 94 | // Doesn't call dlclose() to avoid issues with multiple uses of the same library. 95 | template 96 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE(const char *Filename, const char *FuncName, RetType &ReturnVal, Args... FuncArgs) 97 | { 98 | void *ModulePtr; 99 | void *FuncAddressPtr; 100 | 101 | ModulePtr = dlopen(Filename, RTLD_LAZY); 102 | if (ModulePtr == NULL) return false; 103 | 104 | FuncAddressPtr = dlsym(ModulePtr, FuncName); 105 | if (FuncAddressPtr == NULL) return false; 106 | 107 | ReturnVal = (*((RetType (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 108 | 109 | return true; 110 | } 111 | 112 | // Doesn't call dlclose() to avoid issues with multiple uses of the same library. 113 | template 114 | bool CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID(const char *Filename, const char *FuncName, Args... FuncArgs) 115 | { 116 | void *ModulePtr; 117 | void *FuncAddressPtr; 118 | 119 | ModulePtr = dlopen(Filename, RTLD_LAZY); 120 | if (ModulePtr == NULL) return false; 121 | 122 | FuncAddressPtr = dlsym(ModulePtr, FuncName); 123 | if (FuncAddressPtr == NULL) return false; 124 | 125 | (*((void (CUBICLESOFT_SHARED_LIBRARY_CALLCONV *)(Args...))FuncAddressPtr))(FuncArgs...); 126 | 127 | return true; 128 | } 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /templates/shared_lib.h: -------------------------------------------------------------------------------- 1 | // Shared library (DLL, so, etc.) loader and function call templates. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_SHARED_LIBRARY 5 | #define CUBICLESOFT_SHARED_LIBRARY 6 | 7 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | namespace CubicleSoft 14 | { 15 | namespace SharedLib 16 | { 17 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 18 | 19 | class ModuleUtil 20 | { 21 | public: 22 | inline ModuleUtil(const char *Filename) : MxFilename(Filename), MxModulePtr(NULL) 23 | { 24 | } 25 | 26 | inline ~ModuleUtil() 27 | { 28 | if (MxModulePtr != NULL) ::FreeLibrary(MxModulePtr); 29 | } 30 | 31 | inline HMODULE Load() 32 | { 33 | if (MxModulePtr != NULL) return MxModulePtr; 34 | if (MxFilename == NULL) return NULL; 35 | 36 | MxModulePtr = ::LoadLibraryA(MxFilename); 37 | 38 | MxFilename = NULL; 39 | 40 | return MxModulePtr; 41 | } 42 | 43 | private: 44 | const char *MxFilename; 45 | HMODULE MxModulePtr; 46 | }; 47 | 48 | class FunctionUtil 49 | { 50 | public: 51 | inline FunctionUtil(ModuleUtil &ModuleInst, const char *FuncName) : MxModule(&ModuleInst), MxFuncName(FuncName), MxFuncPtr(NULL) 52 | { 53 | } 54 | 55 | inline FARPROC GetFuncPtr() { return MxFuncPtr; } 56 | 57 | inline FARPROC LoadFuncPtr() 58 | { 59 | if (MxFuncName == NULL) return NULL; 60 | 61 | const char *FuncName = MxFuncName; 62 | MxFuncName = NULL; 63 | 64 | HMODULE ModulePtr = MxModule->Load(); 65 | 66 | return (ModulePtr == NULL ? NULL : (MxFuncPtr = ::GetProcAddress(ModulePtr, FuncName))); 67 | } 68 | 69 | private: 70 | ModuleUtil *MxModule; 71 | const char *MxFuncName; 72 | FARPROC MxFuncPtr; 73 | }; 74 | 75 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX Call 76 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID CallVoid 77 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE CallOnce 78 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID CallOnceVoid 79 | #define CUBICLESOFT_SHARED_LIBRARY_CALLCONV 80 | #include "shared_lib_util.h" 81 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX 82 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID 83 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE 84 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID 85 | #undef CUBICLESOFT_SHARED_LIBRARY_CALLCONV 86 | 87 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX Stdcall 88 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID StdcallVoid 89 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE StdcallOnce 90 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID StdcallOnceVoid 91 | #define CUBICLESOFT_SHARED_LIBRARY_CALLCONV __stdcall 92 | #include "shared_lib_util.h" 93 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX 94 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID 95 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE 96 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID 97 | #undef CUBICLESOFT_SHARED_LIBRARY_CALLCONV 98 | 99 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX Cdecl 100 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID CdeclVoid 101 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE CdeclOnce 102 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID CdeclOnceVoid 103 | #define CUBICLESOFT_SHARED_LIBRARY_CALLCONV __cdecl 104 | #include "shared_lib_util.h" 105 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX 106 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID 107 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE 108 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID 109 | #undef CUBICLESOFT_SHARED_LIBRARY_CALLCONV 110 | 111 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX Fastcall 112 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID FastcallVoid 113 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE FastcallOnce 114 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID FastcallOnceVoid 115 | #define CUBICLESOFT_SHARED_LIBRARY_CALLCONV __fastcall 116 | #include "shared_lib_util.h" 117 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX 118 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID 119 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE 120 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID 121 | #undef CUBICLESOFT_SHARED_LIBRARY_CALLCONV 122 | 123 | #else 124 | 125 | class ModuleUtil 126 | { 127 | public: 128 | inline ModuleUtil(const char *Filename) : MxFilename(Filename), MxModulePtr(NULL) 129 | { 130 | } 131 | 132 | inline ~ModuleUtil() 133 | { 134 | if (MxModulePtr != NULL) dlclose(MxModulePtr); 135 | } 136 | 137 | inline void *Load() 138 | { 139 | if (MxModulePtr != NULL) return MxModulePtr; 140 | if (MxFilename == NULL) return NULL; 141 | 142 | MxModulePtr = dlopen(MxFilename, RTLD_LAZY); 143 | 144 | MxFilename = NULL; 145 | 146 | return MxModulePtr; 147 | } 148 | 149 | private: 150 | const char *MxFilename; 151 | void *MxModulePtr; 152 | }; 153 | 154 | class FunctionUtil 155 | { 156 | public: 157 | inline FunctionUtil(ModuleUtil &ModuleInst, const char *FuncName) : MxModule(&ModuleInst), MxFuncName(FuncName), MxFuncPtr(NULL) 158 | { 159 | } 160 | 161 | inline void *GetFuncPtr() { return MxFuncPtr; } 162 | 163 | inline void *LoadFuncPtr() 164 | { 165 | if (MxFuncName == NULL) return NULL; 166 | 167 | const char *FuncName = MxFuncName; 168 | MxFuncName = NULL; 169 | 170 | void *ModulePtr = MxModule->Load(); 171 | 172 | return (ModulePtr == NULL ? NULL : (MxFuncPtr = dlsym(ModulePtr, FuncName))); 173 | } 174 | 175 | private: 176 | ModuleUtil *MxModule; 177 | const char *MxFuncName; 178 | void *MxFuncPtr; 179 | }; 180 | 181 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX Call 182 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID CallVoid 183 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE CallOnce 184 | #define CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID CallOnceVoid 185 | #define CUBICLESOFT_SHARED_LIBRARY_CALLCONV 186 | #include "shared_lib_util.h" 187 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX 188 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_VOID 189 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE 190 | #undef CUBICLESOFT_SHARED_LIBRARY_PREFIX_ONCE_VOID 191 | #undef CUBICLESOFT_SHARED_LIBRARY_CALLCONV 192 | 193 | #endif 194 | } 195 | } 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /templates/static_mixed_var.h: -------------------------------------------------------------------------------- 1 | // A mixed, flexible variable data type (plain ol' data - POD) with all-public data access. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_STATIC_MIXED_VAR 5 | #define CUBICLESOFT_STATIC_MIXED_VAR 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace CubicleSoft 13 | { 14 | enum StaticMixedVarModes 15 | { 16 | MV_None, 17 | MV_Bool, 18 | MV_Int, 19 | MV_UInt, 20 | MV_Double, 21 | MV_Str 22 | }; 23 | 24 | // Must be used like: StaticMixedVar 25 | // Designed to be extended but not overridden. 26 | template 27 | class StaticMixedVar 28 | { 29 | public: 30 | StaticMixedVarModes MxMode; 31 | std::int64_t MxInt; 32 | double MxDouble; 33 | T MxStr; 34 | size_t MxStrPos; 35 | 36 | StaticMixedVar() : MxMode(MV_None), MxInt(0), MxDouble(0.0), MxStrPos(0) 37 | { 38 | } 39 | 40 | // Some functions for those who prefer member functions over directly accessing raw class data. 41 | inline bool IsNone() 42 | { 43 | return (MxMode == MV_None); 44 | } 45 | 46 | inline bool IsBool() 47 | { 48 | return (MxMode == MV_Bool); 49 | } 50 | 51 | inline bool IsInt() 52 | { 53 | return (MxMode == MV_Int); 54 | } 55 | 56 | inline bool IsUInt() 57 | { 58 | return (MxMode == MV_UInt); 59 | } 60 | 61 | inline bool IsDouble() 62 | { 63 | return (MxMode == MV_Double); 64 | } 65 | 66 | inline bool IsStr() 67 | { 68 | return (MxMode == MV_Str); 69 | } 70 | 71 | inline bool GetBool() 72 | { 73 | return (MxInt != 0); 74 | } 75 | 76 | inline std::int64_t GetInt() 77 | { 78 | return MxInt; 79 | } 80 | 81 | inline std::uint64_t GetUInt() 82 | { 83 | return (std::uint64_t)MxInt; 84 | } 85 | 86 | inline double GetDouble() 87 | { 88 | return MxDouble; 89 | } 90 | 91 | inline char *GetStr() 92 | { 93 | return MxStr; 94 | } 95 | 96 | inline size_t GetSize() 97 | { 98 | return MxStrPos; 99 | } 100 | 101 | inline size_t GetMaxSize() 102 | { 103 | return sizeof(MxStr); 104 | } 105 | 106 | inline void SetBool(const bool newbool) 107 | { 108 | MxMode = MV_Bool; 109 | MxInt = (int)newbool; 110 | } 111 | 112 | inline void SetInt(const std::int64_t newint) 113 | { 114 | MxMode = MV_Int; 115 | MxInt = newint; 116 | } 117 | 118 | inline void SetUInt(const std::uint64_t newint) 119 | { 120 | MxMode = MV_UInt; 121 | MxInt = (std::int64_t)newint; 122 | } 123 | 124 | inline void SetDouble(const double newdouble) 125 | { 126 | MxMode = MV_Double; 127 | MxDouble = newdouble; 128 | } 129 | 130 | void SetStr(const char *str) 131 | { 132 | MxMode = MV_Str; 133 | MxStrPos = 0; 134 | while (MxStrPos < sizeof(MxStr) - 1 && *str) 135 | { 136 | MxStr[MxStrPos++] = *str++; 137 | } 138 | MxStr[MxStrPos] = '\0'; 139 | } 140 | 141 | void SetData(const char *str, size_t size) 142 | { 143 | MxMode = MV_Str; 144 | MxStrPos = 0; 145 | while (MxStrPos < sizeof(MxStr) - 1 && size) 146 | { 147 | MxStr[MxStrPos++] = *str++; 148 | size--; 149 | } 150 | MxStr[MxStrPos] = '\0'; 151 | } 152 | 153 | template 154 | void SetFormattedStr(const char *format, Args... args) 155 | { 156 | MxMode = MV_Str; 157 | 158 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 159 | _snprintf_s(MxStr, sizeof(MxStr), _TRUNCATE, format, args...); 160 | MxStr[sizeof(MxStr) - 1] = '\0'; 161 | #else 162 | if (snprintf(MxStr, sizeof(MxStr), format, args...) < 0) MxStr[0] = '\0'; 163 | #endif 164 | 165 | MxStrPos = strlen(MxStr); 166 | } 167 | 168 | // Prepend functions only prepend if there is enough space. 169 | void PrependStr(const char *str) 170 | { 171 | size_t y = strlen(str); 172 | if (MxStrPos + y < sizeof(MxStr) - 1) 173 | { 174 | memmove(MxStr + y, MxStr, MxStrPos + 1); 175 | memcpy(MxStr, str, y); 176 | MxStrPos += y; 177 | } 178 | } 179 | 180 | void PrependData(const char *str, size_t size) 181 | { 182 | if (MxStrPos + size < sizeof(MxStr) - 1) 183 | { 184 | memmove(MxStr + size, MxStr, MxStrPos + 1); 185 | memcpy(MxStr, str, size); 186 | MxStrPos += size; 187 | } 188 | } 189 | 190 | template 191 | void PrependFormattedStr(const char *format, Args... args) 192 | { 193 | T tempbuffer; 194 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 195 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, format, args...); 196 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 197 | #else 198 | snprintf(tempbuffer, sizeof(tempbuffer), format, args...); 199 | #endif 200 | 201 | PrependStr(tempbuffer); 202 | } 203 | 204 | void PrependInt(const std::int64_t val, size_t radix = 10) 205 | { 206 | char tempbuffer[44]; 207 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 208 | } 209 | 210 | void PrependUInt(const std::uint64_t val, size_t radix = 10) 211 | { 212 | char tempbuffer[44]; 213 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 214 | } 215 | 216 | void PrependDouble(const double val, const size_t precision = 16) 217 | { 218 | char tempbuffer[100]; 219 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 220 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 221 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 222 | #else 223 | if (snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val) < 0) tempbuffer[0] = '\0'; 224 | #endif 225 | 226 | PrependStr(tempbuffer); 227 | } 228 | 229 | void AppendStr(const char *str) 230 | { 231 | while (MxStrPos < sizeof(MxStr) - 1 && *str) 232 | { 233 | MxStr[MxStrPos++] = *str++; 234 | } 235 | MxStr[MxStrPos] = '\0'; 236 | } 237 | 238 | void AppendData(const char *str, size_t size) 239 | { 240 | while (MxStrPos < sizeof(MxStr) - 1 && size) 241 | { 242 | MxStr[MxStrPos++] = *str++; 243 | size--; 244 | } 245 | MxStr[MxStrPos] = '\0'; 246 | } 247 | 248 | template 249 | void AppendFormattedStr(const char *format, Args... args) 250 | { 251 | T tempbuffer; 252 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 253 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, format, args...); 254 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 255 | #else 256 | if (snprintf(tempbuffer, sizeof(tempbuffer), format, args...) < 0) tempbuffer[0] = '\0'; 257 | #endif 258 | 259 | AppendStr(tempbuffer); 260 | } 261 | 262 | void AppendInt(const std::int64_t val, size_t radix = 10) 263 | { 264 | char tempbuffer[44]; 265 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 266 | } 267 | 268 | void AppendUInt(const std::uint64_t val, size_t radix = 10) 269 | { 270 | char tempbuffer[44]; 271 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 272 | } 273 | 274 | void AppendDouble(const double val, const size_t precision = 16) 275 | { 276 | char tempbuffer[100]; 277 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 278 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 279 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 280 | #else 281 | if (snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val) < 0) tempbuffer[0] = '\0'; 282 | #endif 283 | 284 | AppendStr(tempbuffer); 285 | } 286 | 287 | inline void AppendChar(const char chr) 288 | { 289 | if (MxStrPos < sizeof(MxStr) - 1) 290 | { 291 | MxStr[MxStrPos++] = chr; 292 | MxStr[MxStrPos] = '\0'; 293 | } 294 | } 295 | 296 | inline void AppendMissingChar(const char chr) 297 | { 298 | if ((!MxStrPos || MxStr[MxStrPos - 1] != chr) && MxStrPos < sizeof(MxStr) - 1) 299 | { 300 | MxStr[MxStrPos++] = chr; 301 | MxStr[MxStrPos] = '\0'; 302 | } 303 | } 304 | 305 | inline bool RemoveTrailingChar(const char chr) 306 | { 307 | if (!MxStrPos || MxStr[MxStrPos - 1] != chr) return false; 308 | 309 | MxStr[--MxStrPos] = '\0'; 310 | 311 | return true; 312 | } 313 | 314 | inline void SetSize(const size_t size) 315 | { 316 | if (size < sizeof(MxStr)) 317 | { 318 | MxStrPos = size; 319 | MxStr[MxStrPos] = '\0'; 320 | } 321 | } 322 | 323 | // Swiped and slightly modified from Int::ToString(). 324 | static bool IntToString(char *Result, size_t Size, std::uint64_t Num, size_t Radix = 10) 325 | { 326 | if (Size < 2) return false; 327 | 328 | size_t x = Size, z; 329 | 330 | Result[--x] = '\0'; 331 | if (!Num) Result[--x] = '0'; 332 | else 333 | { 334 | while (Num && x) 335 | { 336 | z = Num % Radix; 337 | Result[--x] = (char)(z > 9 ? z - 10 + 'A' : z + '0'); 338 | Num /= Radix; 339 | } 340 | 341 | if (Num) return false; 342 | } 343 | 344 | memmove(Result, Result + x, Size - x); 345 | 346 | return true; 347 | } 348 | 349 | static bool IntToString(char *Result, size_t Size, std::int64_t Num, size_t Radix = 10) 350 | { 351 | if (Num >= 0) return IntToString(Result, Size, (std::uint64_t)Num, Radix); 352 | 353 | if (Size < 2) return false; 354 | Result[0] = '-'; 355 | 356 | return IntToString(Result + 1, Size - 1, (std::uint64_t)-Num, Radix); 357 | } 358 | }; 359 | } 360 | 361 | #endif 362 | -------------------------------------------------------------------------------- /templates/static_wc_mixed_var.h: -------------------------------------------------------------------------------- 1 | // A mixed, flexible variable data type (plain ol' data - POD) with all-public data access. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_STATIC_WC_MIXED_VAR 5 | #define CUBICLESOFT_STATIC_WC_MIXED_VAR 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 13 | #include 14 | #else 15 | #include 16 | 17 | #ifndef WCHAR 18 | #define WCHAR wchar_t 19 | #endif 20 | #endif 21 | 22 | namespace CubicleSoft 23 | { 24 | enum StaticWCMixedVarModes 25 | { 26 | WCMV_None, 27 | WCMV_Bool, 28 | WCMV_Int, 29 | WCMV_UInt, 30 | WCMV_Double, 31 | WCMV_Str 32 | }; 33 | 34 | // Must be used like: StaticWCMixedVar 35 | // Designed to be extended but not overridden. 36 | template 37 | class StaticWCMixedVar 38 | { 39 | public: 40 | StaticWCMixedVarModes MxMode; 41 | std::int64_t MxInt; 42 | double MxDouble; 43 | T MxStr; 44 | size_t MxStrPos; 45 | 46 | StaticWCMixedVar() : MxMode(WCMV_None), MxInt(0), MxDouble(0.0), MxStrPos(0) 47 | { 48 | } 49 | 50 | // Some functions for those who prefer member functions over directly accessing raw class data. 51 | inline bool IsNone() 52 | { 53 | return (MxMode == WCMV_None); 54 | } 55 | 56 | inline bool IsBool() 57 | { 58 | return (MxMode == WCMV_Bool); 59 | } 60 | 61 | inline bool IsInt() 62 | { 63 | return (MxMode == WCMV_Int); 64 | } 65 | 66 | inline bool IsUInt() 67 | { 68 | return (MxMode == WCMV_UInt); 69 | } 70 | 71 | inline bool IsDouble() 72 | { 73 | return (MxMode == WCMV_Double); 74 | } 75 | 76 | inline bool IsStr() 77 | { 78 | return (MxMode == WCMV_Str); 79 | } 80 | 81 | inline bool GetBool() 82 | { 83 | return (MxInt != 0); 84 | } 85 | 86 | inline std::int64_t GetInt() 87 | { 88 | return MxInt; 89 | } 90 | 91 | inline std::uint64_t GetUInt() 92 | { 93 | return (std::uint64_t)MxInt; 94 | } 95 | 96 | inline double GetDouble() 97 | { 98 | return MxDouble; 99 | } 100 | 101 | inline WCHAR *GetStr() 102 | { 103 | return MxStr; 104 | } 105 | 106 | inline size_t GetSize() 107 | { 108 | return MxStrPos; 109 | } 110 | 111 | inline size_t GetMaxSize() 112 | { 113 | return sizeof(MxStr) / sizeof(WCHAR); 114 | } 115 | 116 | inline void SetBool(const bool newbool) 117 | { 118 | MxMode = WCMV_Bool; 119 | MxInt = (int)newbool; 120 | } 121 | 122 | inline void SetInt(const std::int64_t newint) 123 | { 124 | MxMode = WCMV_Int; 125 | MxInt = newint; 126 | } 127 | 128 | inline void SetUInt(const std::uint64_t newint) 129 | { 130 | MxMode = WCMV_UInt; 131 | MxInt = (std::int64_t)newint; 132 | } 133 | 134 | inline void SetDouble(const double newdouble) 135 | { 136 | MxMode = WCMV_Double; 137 | MxDouble = newdouble; 138 | } 139 | 140 | void SetStr(const WCHAR *str) 141 | { 142 | MxMode = WCMV_Str; 143 | MxStrPos = 0; 144 | while (MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1 && *str) 145 | { 146 | MxStr[MxStrPos++] = *str++; 147 | } 148 | MxStr[MxStrPos] = L'\0'; 149 | } 150 | 151 | // Doesn't do anything fancy beyond expanding characters to fill the space of a wide character. 152 | void SetStr(const char *str) 153 | { 154 | MxMode = WCMV_Str; 155 | MxStrPos = 0; 156 | while (MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1 && *str) 157 | { 158 | MxStr[MxStrPos++] = (WCHAR)*str++; 159 | } 160 | MxStr[MxStrPos] = L'\0'; 161 | } 162 | 163 | template 164 | void SetFormattedStr(const WCHAR *format, Args... args) 165 | { 166 | MxMode = WCMV_Str; 167 | 168 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 169 | swprintf_s(MxStr, sizeof(MxStr) / sizeof(WCHAR), format, args...); 170 | MxStr[sizeof(MxStr) / sizeof(WCHAR) - 1] = '\0'; 171 | #else 172 | if (swprintf(MxStr, sizeof(MxStr) / sizeof(WCHAR), format, args...) < 0) MxStr[0] = '\0'; 173 | #endif 174 | 175 | MxStrPos = wcslen(MxStr); 176 | } 177 | 178 | // Prepend functions only prepend if there is enough space. 179 | void PrependStr(const WCHAR *str) 180 | { 181 | size_t y = wcslen(str); 182 | size_t y2 = y * sizeof(WCHAR); 183 | if (MxStrPos + y < (sizeof(MxStr) / sizeof(WCHAR)) - 1) 184 | { 185 | memmove(MxStr + y, MxStr, (MxStrPos + 1) * sizeof(WCHAR)); 186 | memcpy(MxStr, str, y2); 187 | MxStrPos += y; 188 | } 189 | } 190 | 191 | // Doesn't do anything fancy beyond expanding characters to fill the space of a wide character. 192 | void PrependStr(const char *str) 193 | { 194 | size_t y = strlen(str); 195 | if (MxStrPos + y < (sizeof(MxStr) / sizeof(WCHAR)) - 1) 196 | { 197 | memmove(MxStr + y, MxStr, (MxStrPos + 1) * sizeof(WCHAR)); 198 | MxStrPos += y; 199 | 200 | for (size_t x = 0; x < y; x++) 201 | { 202 | MxStr[x] = (WCHAR)*str++; 203 | } 204 | } 205 | } 206 | 207 | template 208 | void PrependFormattedStr(const WCHAR *format, Args... args) 209 | { 210 | T tempbuffer; 211 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 212 | swprintf_s(tempbuffer, sizeof(tempbuffer) / sizeof(WCHAR), format, args...); 213 | tempbuffer[sizeof(tempbuffer) / sizeof(WCHAR) - 1] = '\0'; 214 | #else 215 | if (swprintf(tempbuffer, sizeof(tempbuffer) / sizeof(WCHAR), format, args...) < 0) tempbuffer[0] = '\0'; 216 | #endif 217 | 218 | PrependStr(tempbuffer); 219 | } 220 | 221 | void PrependInt(const std::int64_t val, size_t radix = 10) 222 | { 223 | char tempbuffer[44]; 224 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 225 | } 226 | 227 | void PrependUInt(const std::uint64_t val, size_t radix = 10) 228 | { 229 | char tempbuffer[44]; 230 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 231 | } 232 | 233 | void PrependDouble(const double val, const size_t precision = 16) 234 | { 235 | char tempbuffer[100]; 236 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 237 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 238 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 239 | #else 240 | snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val); 241 | #endif 242 | 243 | PrependStr(tempbuffer); 244 | } 245 | 246 | void AppendStr(const WCHAR *str) 247 | { 248 | while (MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1 && *str) 249 | { 250 | MxStr[MxStrPos++] = *str++; 251 | } 252 | MxStr[MxStrPos] = L'\0'; 253 | } 254 | 255 | // Doesn't do anything fancy beyond expanding characters to fill the space of a wide character. 256 | void AppendStr(const char *str) 257 | { 258 | while (MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1 && *str) 259 | { 260 | MxStr[MxStrPos++] = (WCHAR)*str++; 261 | } 262 | MxStr[MxStrPos] = L'\0'; 263 | } 264 | 265 | template 266 | void AppendFormattedStr(const WCHAR *format, Args... args) 267 | { 268 | T tempbuffer; 269 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 270 | swprintf_s(tempbuffer, sizeof(tempbuffer) / sizeof(WCHAR), format, args...); 271 | tempbuffer[sizeof(tempbuffer) / sizeof(WCHAR) - 1] = '\0'; 272 | #else 273 | if (swprintf(tempbuffer, sizeof(tempbuffer) / sizeof(WCHAR), format, args...) < 0) tempbuffer[0] = '\0'; 274 | #endif 275 | 276 | AppendStr(tempbuffer); 277 | } 278 | 279 | void AppendInt(const std::int64_t val, size_t radix = 10) 280 | { 281 | char tempbuffer[44]; 282 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 283 | } 284 | 285 | void AppendUInt(const std::uint64_t val, size_t radix = 10) 286 | { 287 | char tempbuffer[44]; 288 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 289 | } 290 | 291 | void AppendDouble(const double val, const size_t precision = 16) 292 | { 293 | char tempbuffer[100]; 294 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 295 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 296 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 297 | #else 298 | snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val); 299 | #endif 300 | 301 | AppendStr(tempbuffer); 302 | } 303 | 304 | inline void AppendChar(const WCHAR chr) 305 | { 306 | if (MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1) 307 | { 308 | MxStr[MxStrPos++] = chr; 309 | MxStr[MxStrPos] = L'\0'; 310 | } 311 | } 312 | 313 | inline void AppendMissingChar(const WCHAR chr) 314 | { 315 | if ((!MxStrPos || MxStr[MxStrPos - 1] != chr) && MxStrPos < (sizeof(MxStr) / sizeof(WCHAR)) - 1) 316 | { 317 | MxStr[MxStrPos++] = chr; 318 | MxStr[MxStrPos] = L'\0'; 319 | } 320 | } 321 | 322 | inline bool RemoveTrailingChar(const WCHAR chr) 323 | { 324 | if (!MxStrPos || MxStr[MxStrPos - 1] != chr) return false; 325 | 326 | MxStr[--MxStrPos] = L'\0'; 327 | 328 | return true; 329 | } 330 | 331 | inline void SetSize(const size_t size) 332 | { 333 | if (size < (sizeof(MxStr) / sizeof(WCHAR))) 334 | { 335 | MxStrPos = size; 336 | MxStr[MxStrPos] = L'\0'; 337 | } 338 | } 339 | 340 | // Swiped and slightly modified from Int::ToString(). 341 | static bool IntToString(char *Result, size_t Size, std::uint64_t Num, size_t Radix = 10) 342 | { 343 | if (Size < 2) return false; 344 | 345 | size_t x = Size, z; 346 | 347 | Result[--x] = '\0'; 348 | if (!Num) Result[--x] = '0'; 349 | else 350 | { 351 | while (Num && x) 352 | { 353 | z = Num % Radix; 354 | Result[--x] = (char)(z > 9 ? z - 10 + 'A' : z + '0'); 355 | Num /= Radix; 356 | } 357 | 358 | if (Num) return false; 359 | } 360 | 361 | memmove(Result, Result + x, Size - x); 362 | 363 | return true; 364 | } 365 | 366 | static bool IntToString(char *Result, size_t Size, std::int64_t Num, size_t Radix = 10) 367 | { 368 | if (Num >= 0) return IntToString(Result, Size, (std::uint64_t)Num, Radix); 369 | 370 | if (Size < 2) return false; 371 | Result[0] = '-'; 372 | 373 | return IntToString(Result + 1, Size - 1, (std::uint64_t)-Num, Radix); 374 | } 375 | }; 376 | } 377 | 378 | #endif 379 | -------------------------------------------------------------------------------- /templates/packed_ordered_hash_util.h: -------------------------------------------------------------------------------- 1 | // Ordered hash map with integer and string keys in a memory compact format. No detachable nodes but is CPU cache-friendly. 2 | // (C) 2017 CubicleSoft. All Rights Reserved. 3 | 4 | // NOTE: This file is intended to be included from 'packed_ordered_hash.h'. 5 | 6 | // Implements a packed ordered hash that grows dynamically and uses integer and string keys. 7 | template 8 | #ifdef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 9 | class PackedOrderedHashNoCopy 10 | #else 11 | class PackedOrderedHash 12 | #endif 13 | { 14 | public: 15 | // Implements djb2 (DJBX33X). 16 | // WARNING: This algorithm is weak security-wise! 17 | // https://www.youtube.com/watch?v=R2Cq3CLI6H8 18 | // https://www.youtube.com/watch?v=wGYj8fhhUVA 19 | // For much better security with a slight performance reduction, use the other constructor, which implements SipHash. 20 | #ifdef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 21 | PackedOrderedHashNoCopy 22 | #else 23 | PackedOrderedHash 24 | #endif 25 | (size_t EstimatedSize = 8, std::uint64_t HashKey = 5381) : UseSipHash(false), Key1(HashKey), Key2(0), 26 | ArrayNodes(NULL), HashNodes(NULL), Mask(0), NumNodes(0), NextNodePos(0), NumUsed(0), 27 | CompactNumUsed50(0), CompactNextNodePos75(0), CompactNumUsed90(0), ResizeNumUsed40(0) 28 | { 29 | ResizeHash(EstimatedSize); 30 | } 31 | 32 | // Keys are securely hashed via SipHash-2-4. 33 | // Assumes good (CSPRNG generated) inputs for HashKey1 and HashKey2. 34 | #ifdef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 35 | PackedOrderedHashNoCopy 36 | #else 37 | PackedOrderedHash 38 | #endif 39 | (size_t EstimatedSize, std::uint64_t HashKey1, std::uint64_t HashKey2) : UseSipHash(true), Key1(HashKey1), Key2(HashKey2), 40 | ArrayNodes(NULL), HashNodes(NULL), Mask(0), NumNodes(0), NextNodePos(0), NumUsed(0), 41 | CompactNumUsed50(0), CompactNextNodePos75(0), CompactNumUsed90(0), ResizeNumUsed40(0) 42 | { 43 | ResizeHash(EstimatedSize); 44 | } 45 | 46 | #ifdef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 47 | ~PackedOrderedHashNoCopy() 48 | #else 49 | ~PackedOrderedHash() 50 | #endif 51 | { 52 | PackedOrderedHashNode *Node = ArrayNodes, *LastNode = ArrayNodes + NextNodePos; 53 | while (Node != LastNode) 54 | { 55 | if (Node->PrevHashIndex != 0xFFFFFFFF) 56 | { 57 | Node->Value.~T(); 58 | 59 | if (Node->StrKey != NULL) delete[] (Node->StrKey - sizeof(size_t)); 60 | } 61 | 62 | Node++; 63 | } 64 | 65 | delete[] (char *)ArrayNodes; 66 | } 67 | 68 | #ifdef CUBICLESOFT_PACKEDORDEREDHASH_NOCOPYASSIGN 69 | PackedOrderedHashNoCopy(const PackedOrderedHashNoCopy &TempHash); 70 | PackedOrderedHashNoCopy &operator=(const PackedOrderedHashNoCopy &TempHash); 71 | #else 72 | PackedOrderedHash(const PackedOrderedHash &TempHash) 73 | { 74 | UseSipHash = TempHash.UseSipHash; 75 | Key1 = TempHash.Key1; 76 | Key2 = TempHash.Key2; 77 | 78 | ArrayNodes = NULL; 79 | NumNodes = 0; 80 | NumUsed = 0; 81 | 82 | InternalResizeHash(TempHash.NumNodes); 83 | NextNodePos = TempHash.NextNodePos; 84 | memcpy(HashNodes, TempHash.HashNodes, sizeof(std::uint32_t) * NumNodes); 85 | NumUsed = TempHash.NumUsed; 86 | 87 | PackedOrderedHashNode *Node = TempHash.ArrayNodes, *Node2 = ArrayNodes, *LastNode = ArrayNodes + NextNodePos; 88 | 89 | while (Node != LastNode) 90 | { 91 | Node2->PrevHashIndex = Node->PrevHashIndex; 92 | Node2->NextHashIndex = Node->NextHashIndex; 93 | Node2->IntKey = Node->IntKey; 94 | 95 | if (Node->StrKey == NULL) Node2->StrKey = NULL; 96 | else 97 | { 98 | size_t StrLen = Node->GetStrLen(); 99 | char *Str = new char[StrLen + sizeof(size_t)]; 100 | *((size_t *)Str) = StrLen; 101 | Str += sizeof(size_t); 102 | memcpy(Str, Node->StrKey, StrLen); 103 | Node2->StrKey = Str; 104 | } 105 | 106 | if (Node->PrevHashIndex != 0xFFFFFFFF) 107 | { 108 | new (&Node2->Value) T(Node->Value); 109 | } 110 | 111 | Node++; 112 | Node2++; 113 | } 114 | } 115 | 116 | PackedOrderedHash &operator=(const PackedOrderedHash &TempHash) 117 | { 118 | if (&TempHash != this) 119 | { 120 | UseSipHash = TempHash.UseSipHash; 121 | Key1 = TempHash.Key1; 122 | Key2 = TempHash.Key2; 123 | 124 | PackedOrderedHashNode *Node = ArrayNodes, *Node2, *LastNode = ArrayNodes + NextNodePos; 125 | while (Node != LastNode) 126 | { 127 | if (Node->PrevHashIndex != 0xFFFFFFFF) 128 | { 129 | Node->Value.~T(); 130 | 131 | if (Node->StrKey != NULL) delete[] (Node->StrKey - sizeof(size_t)); 132 | } 133 | 134 | Node++; 135 | } 136 | 137 | delete[] (char *)ArrayNodes; 138 | 139 | ArrayNodes = NULL; 140 | NumNodes = 0; 141 | NumUsed = 0; 142 | 143 | InternalResizeHash(TempHash.NumNodes); 144 | NextNodePos = TempHash.NextNodePos; 145 | memcpy(HashNodes, TempHash.HashNodes, sizeof(std::uint32_t) * NumNodes); 146 | NumUsed = TempHash.NumUsed; 147 | 148 | Node = TempHash.ArrayNodes; 149 | Node2 = ArrayNodes; 150 | LastNode = ArrayNodes + NextNodePos; 151 | 152 | while (Node != LastNode) 153 | { 154 | Node2->PrevHashIndex = Node->PrevHashIndex; 155 | Node2->NextHashIndex = Node->NextHashIndex; 156 | Node2->IntKey = Node->IntKey; 157 | 158 | if (Node->StrKey == NULL) Node2->StrKey = NULL; 159 | else 160 | { 161 | size_t StrLen = Node->GetStrLen(); 162 | char *Str = new char[StrLen + sizeof(size_t)]; 163 | *((size_t *)Str) = StrLen; 164 | Str += sizeof(size_t); 165 | memcpy(Str, Node->StrKey, StrLen); 166 | Node2->StrKey = Str; 167 | } 168 | 169 | if (Node->PrevHashIndex != 0xFFFFFFFF) 170 | { 171 | new (&Node2->Value) T(Node->Value); 172 | } 173 | 174 | Node++; 175 | Node2++; 176 | } 177 | } 178 | 179 | return *this; 180 | } 181 | #endif 182 | 183 | PackedOrderedHashNode *Set(const std::int64_t IntKey) 184 | { 185 | size_t Pos; 186 | std::uint64_t HashKey = GetHashKey((const std::uint8_t *)&IntKey, sizeof(std::int64_t)); 187 | PackedOrderedHashNode *Node = InternalFind(IntKey, Pos, HashKey); 188 | 189 | if (Node != NULL) return Node; 190 | 191 | // Create a new node. 192 | if (NextNodePos == NumNodes) AutoResizeHash(); 193 | 194 | Node = ArrayNodes + NextNodePos; 195 | new (&Node->Value) T; 196 | Pos = NextNodePos; 197 | NextNodePos++; 198 | 199 | // Attach the node to the start of the hash list. 200 | std::uint32_t HashPos = (std::uint32_t)HashKey & Mask; 201 | Node->PrevHashIndex = 0x80000000 | HashPos; 202 | Node->NextHashIndex = HashNodes[HashPos]; 203 | if (Node->NextHashIndex != 0xFFFFFFFF) ArrayNodes[Node->NextHashIndex].PrevHashIndex = (std::uint32_t)Pos; 204 | HashNodes[HashPos] = (std::uint32_t)Pos; 205 | 206 | Node->IntKey = IntKey; 207 | Node->StrKey = NULL; 208 | 209 | NumUsed++; 210 | 211 | return Node; 212 | } 213 | 214 | inline PackedOrderedHashNode *Set(const std::int64_t IntKey, const T &Value) 215 | { 216 | PackedOrderedHashNode *Node = Set(IntKey); 217 | Node->Value = Value; 218 | 219 | return Node; 220 | } 221 | 222 | PackedOrderedHashNode *Set(const char *StrKey, const size_t StrLen) 223 | { 224 | size_t Pos; 225 | std::uint64_t HashKey = GetHashKey((const std::uint8_t *)StrKey, StrLen); 226 | PackedOrderedHashNode *Node = InternalFind(StrKey, StrLen, Pos, HashKey); 227 | 228 | if (Node != NULL) return Node; 229 | 230 | // Create a new node. 231 | if (NextNodePos == NumNodes) AutoResizeHash(); 232 | 233 | Node = ArrayNodes + NextNodePos; 234 | Pos = NextNodePos; 235 | new (&Node->Value) T; 236 | NextNodePos++; 237 | 238 | // Attach the node to the start of the hash list. 239 | std::uint32_t HashPos = (std::uint32_t)HashKey & Mask; 240 | Node->PrevHashIndex = 0x80000000 | HashPos; 241 | Node->NextHashIndex = HashNodes[HashPos]; 242 | if (Node->NextHashIndex != 0xFFFFFFFF) ArrayNodes[Node->NextHashIndex].PrevHashIndex = (std::uint32_t)Pos; 243 | HashNodes[HashPos] = (std::uint32_t)Pos; 244 | 245 | Node->IntKey = (std::int64_t)HashKey; 246 | 247 | char *Str = new char[StrLen + sizeof(size_t)]; 248 | *((size_t *)Str) = StrLen; 249 | Str += sizeof(size_t); 250 | memcpy(Str, StrKey, StrLen); 251 | Node->StrKey = Str; 252 | 253 | NumUsed++; 254 | 255 | return Node; 256 | } 257 | 258 | inline PackedOrderedHashNode *Set(const char *StrKey, const size_t StrLen, const T &Value) 259 | { 260 | PackedOrderedHashNode *Node = Set(StrKey, StrLen); 261 | Node->Value = Value; 262 | 263 | return Node; 264 | } 265 | 266 | inline bool Unset(const std::int64_t IntKey) 267 | { 268 | return Unset(Find(IntKey)); 269 | } 270 | 271 | inline bool Unset(const char *StrKey, const size_t StrLen) 272 | { 273 | return Unset(Find(StrKey, StrLen)); 274 | } 275 | 276 | bool Unset(PackedOrderedHashNode *Node) 277 | { 278 | if (Node == NULL || Node->PrevHashIndex == 0xFFFFFFFF) return false; 279 | 280 | // Detach the node from the hash list. 281 | if (Node->NextHashIndex != 0xFFFFFFFF) ArrayNodes[Node->NextHashIndex].PrevHashIndex = Node->PrevHashIndex; 282 | 283 | if (Node->PrevHashIndex & 0x80000000) HashNodes[Node->PrevHashIndex & 0x7FFFFFFF] = Node->NextHashIndex; 284 | else ArrayNodes[Node->PrevHashIndex].NextHashIndex = Node->NextHashIndex; 285 | 286 | // Cleanup. 287 | Node->PrevHashIndex = 0xFFFFFFFF; 288 | Node->Value.~T(); 289 | if (Node->StrKey != NULL) 290 | { 291 | delete[] (Node->StrKey - sizeof(size_t)); 292 | Node->StrKey = NULL; 293 | } 294 | 295 | NumUsed--; 296 | 297 | return true; 298 | } 299 | 300 | // Returns the node in the array by index. 301 | inline PackedOrderedHashNode *Get(size_t Pos) 302 | { 303 | return (Pos >= NextNodePos || ArrayNodes[Pos].PrevHashIndex == 0xFFFFFFFF ? NULL : ArrayNodes + Pos); 304 | } 305 | 306 | // Gets the position of the node in the array. 307 | inline size_t GetPos(PackedOrderedHashNode *Node) { return (size_t)(Node - ArrayNodes); } 308 | 309 | // Finds the node in the array via the hash. 310 | inline PackedOrderedHashNode *Find(const std::int64_t IntKey) 311 | { 312 | size_t Pos; 313 | 314 | return Find(IntKey, Pos); 315 | } 316 | 317 | // Finds the node in the array via the hash. 318 | inline PackedOrderedHashNode *Find(const std::int64_t IntKey, size_t &Pos) 319 | { 320 | return InternalFind(IntKey, Pos, GetHashKey((const std::uint8_t *)&IntKey, sizeof(std::int64_t))); 321 | } 322 | 323 | // Finds the node in the array via the hash. 324 | inline PackedOrderedHashNode *Find(const char *StrKey, const size_t StrLen) 325 | { 326 | size_t Pos; 327 | 328 | return Find(StrKey, StrLen, Pos); 329 | } 330 | 331 | // Finds the node in the array via the hash. 332 | inline PackedOrderedHashNode *Find(const char *StrKey, const size_t StrLen, size_t &Pos) 333 | { 334 | return InternalFind(StrKey, StrLen, Pos, GetHashKey((const std::uint8_t *)StrKey, StrLen)); 335 | } 336 | 337 | // Iterates over the array, skipping unset nodes. Initialize the input Pos to GetNextPos() to start at the beginning. 338 | inline PackedOrderedHashNode *Next(size_t &Pos) 339 | { 340 | if (Pos >= NextNodePos) Pos = 0; 341 | else Pos++; 342 | 343 | for (; Pos < NextNodePos; Pos++) 344 | { 345 | if (ArrayNodes[Pos].PrevHashIndex != 0xFFFFFFFF) return (ArrayNodes + Pos); 346 | } 347 | 348 | return NULL; 349 | } 350 | 351 | // Iterates over the array, skipping unset nodes. Initialize the input Pos to GetNextPos() to start at the end. 352 | inline PackedOrderedHashNode *Prev(size_t &Pos) 353 | { 354 | if (Pos > NextNodePos) Pos = NextNodePos; 355 | 356 | while (Pos > 0) 357 | { 358 | Pos--; 359 | 360 | if (ArrayNodes[Pos].PrevHashIndex != 0xFFFFFFFF) return (ArrayNodes + Pos); 361 | } 362 | 363 | Pos = NextNodePos; 364 | 365 | return NULL; 366 | } 367 | 368 | // Call before iterating over the array multiple times. Then call Optimize() if this function returns true. 369 | inline bool ShouldOptimize() 370 | { 371 | return (NumUsed < CompactNumUsed50 && NextNodePos > CompactNextNodePos75); 372 | } 373 | 374 | // Compacts the array by moving elements to unset positions. 375 | bool Optimize() 376 | { 377 | if (NextNodePos == NumUsed) return true; 378 | 379 | // Find the first unset position. 380 | PackedOrderedHashNode *Node = ArrayNodes, *Node2, *LastNode = ArrayNodes + NextNodePos; 381 | while (Node != LastNode && Node->PrevHashIndex != 0xFFFFFFFF) Node++; 382 | 383 | // Copy nodes. 384 | std::uint32_t x = (std::uint32_t)(Node - ArrayNodes); 385 | for (Node2 = Node + 1; Node2 != LastNode; Node2++) 386 | { 387 | if (Node2->PrevHashIndex != 0xFFFFFFFF) 388 | { 389 | // Raw copy node. 390 | memcpy(Node, Node2, sizeof(PackedOrderedHashNode)); 391 | 392 | // Update node hash indexes. 393 | if (Node->PrevHashIndex & 0x80000000) HashNodes[Node->PrevHashIndex & 0x7FFFFFFF] = x; 394 | else ArrayNodes[Node->PrevHashIndex].NextHashIndex = x; 395 | 396 | if (Node->NextHashIndex != 0xFFFFFFFF) ArrayNodes[Node->NextHashIndex].PrevHashIndex = x; 397 | 398 | Node++; 399 | x++; 400 | } 401 | } 402 | 403 | // Cleanup extra nodes. 404 | for (; Node != LastNode; Node++) 405 | { 406 | Node->PrevHashIndex = 0xFFFFFFFF; 407 | Node->StrKey = NULL; 408 | } 409 | 410 | NextNodePos = NumUsed; 411 | 412 | return true; 413 | } 414 | 415 | // Compact when resizing the hash. 416 | 417 | // Performs automatic resizing based on several rules: 418 | // Compact instead of resizing the hash when NumUsed < 90%. 419 | // Shrink when NumUsed < 40% full. 420 | // Grows if NextNodePos is the same as NumNodes. 421 | bool AutoResizeHash() 422 | { 423 | if (NumUsed < CompactNumUsed90) return Optimize(); 424 | if (NumUsed < ResizeNumUsed40) return InternalResizeHash(NumNodes >> 1); 425 | if (NextNodePos == NumNodes) return InternalResizeHash(NumNodes << 1); 426 | 427 | return false; 428 | } 429 | 430 | bool ResizeHash(size_t NewHashSize) 431 | { 432 | size_t NewSize = 1; 433 | while (NewSize < NewHashSize) NewSize <<= 1; 434 | 435 | return InternalResizeHash(NewSize); 436 | } 437 | 438 | inline size_t GetHashSize() { return NumNodes; } 439 | inline size_t GetNextPos() { return NextNodePos; } 440 | inline size_t GetSize() { return NumUsed; } 441 | 442 | private: 443 | inline std::uint64_t GetHashKey(const std::uint8_t *Str, size_t Size) const 444 | { 445 | return (UseSipHash ? PackedOrderedHashUtil::GetSipHashKey(Str, Size, Key1, Key2, 2, 4) : (std::uint64_t)PackedOrderedHashUtil::GetDJBX33XHashKey(Str, Size, (size_t)Key1)); 446 | } 447 | 448 | PackedOrderedHashNode *InternalFind(const std::int64_t IntKey, size_t &Pos, std::uint64_t HashKey) 449 | { 450 | Pos = HashNodes[(std::uint32_t)HashKey & Mask]; 451 | while (Pos != 0xFFFFFFFF) 452 | { 453 | if (ArrayNodes[Pos].IntKey == IntKey && ArrayNodes[Pos].StrKey == NULL) return ArrayNodes + Pos; 454 | 455 | Pos = ArrayNodes[Pos].NextHashIndex; 456 | } 457 | 458 | return NULL; 459 | } 460 | 461 | PackedOrderedHashNode *InternalFind(const char *StrKey, const size_t StrLen, size_t &Pos, std::uint64_t HashKey) 462 | { 463 | std::int64_t IntKey = (std::int64_t)HashKey; 464 | Pos = HashNodes[(std::uint32_t)HashKey & Mask]; 465 | while (Pos != 0xFFFFFFFF) 466 | { 467 | if (ArrayNodes[Pos].IntKey == IntKey && ArrayNodes[Pos].StrKey != NULL && StrLen == ArrayNodes[Pos].GetStrLen() && !memcmp(StrKey, ArrayNodes[Pos].StrKey, StrLen)) return ArrayNodes + Pos; 468 | 469 | Pos = ArrayNodes[Pos].NextHashIndex; 470 | } 471 | 472 | return NULL; 473 | } 474 | 475 | bool InternalResizeHash(size_t NewHashSize) 476 | { 477 | while (NewHashSize < NumUsed) NewHashSize <<= 1; 478 | if (NewHashSize == NumNodes || (NewHashSize < 512 && NewHashSize < NumNodes)) return false; 479 | 480 | PackedOrderedHashNode *ArrayNodes2 = (PackedOrderedHashNode *)(new char[sizeof(PackedOrderedHashNode) * NewHashSize + sizeof(std::uint32_t) * NewHashSize]); 481 | std::uint32_t *HashNodes2 = (std::uint32_t *)(ArrayNodes2 + NewHashSize); 482 | 483 | memset(HashNodes2, 0xFF, sizeof(std::uint32_t) * NewHashSize); 484 | 485 | if (ArrayNodes != NULL) 486 | { 487 | std::uint64_t HashKey; 488 | std::uint32_t HashPos, Mask2, x = 0; 489 | PackedOrderedHashNode *Node = ArrayNodes, *Node2 = ArrayNodes2, *LastNode = ArrayNodes + NextNodePos; 490 | Mask2 = (std::uint32_t)(NewHashSize - 1); 491 | while (Node != LastNode) 492 | { 493 | if (Node->PrevHashIndex != 0xFFFFFFFF) 494 | { 495 | // Raw copy node. 496 | memcpy(Node2, Node, sizeof(PackedOrderedHashNode)); 497 | 498 | HashKey = (Node2->StrKey != NULL ? (std::uint64_t)Node2->IntKey : GetHashKey((const std::uint8_t *)&Node2->IntKey, sizeof(std::int64_t))); 499 | 500 | // Attach the node to the start of the hash list. 501 | HashPos = (std::uint32_t)HashKey & Mask2; 502 | Node2->PrevHashIndex = 0x80000000 | HashPos; 503 | Node2->NextHashIndex = HashNodes2[HashPos]; 504 | if (Node2->NextHashIndex != 0xFFFFFFFF) ArrayNodes2[Node2->NextHashIndex].PrevHashIndex = x; 505 | HashNodes2[HashPos] = x; 506 | 507 | Node2++; 508 | x++; 509 | } 510 | 511 | Node++; 512 | } 513 | } 514 | 515 | ArrayNodes = ArrayNodes2; 516 | HashNodes = HashNodes2; 517 | NumNodes = NewHashSize; 518 | Mask = (std::uint32_t)(NumNodes - 1); 519 | NextNodePos = NumUsed; 520 | 521 | CompactNumUsed50 = (NumNodes >> 1); 522 | CompactNextNodePos75 = NumNodes - (NumNodes >> 3); 523 | CompactNumUsed90 = NumNodes - (NumNodes / 10); 524 | ResizeNumUsed40 = (size_t)((std::uint64_t)(NumNodes << 3) / 10); 525 | 526 | return true; 527 | } 528 | 529 | bool UseSipHash; 530 | std::uint64_t Key1, Key2; 531 | 532 | PackedOrderedHashNode *ArrayNodes; 533 | std::uint32_t *HashNodes; 534 | 535 | std::uint32_t Mask; 536 | size_t NumNodes, NextNodePos, NumUsed; 537 | size_t CompactNumUsed50, CompactNextNodePos75; 538 | size_t CompactNumUsed90, ResizeNumUsed40; 539 | }; 540 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CreateProcess() Windows API Command-Line Utility 2 | ================================================ 3 | 4 | A complete, robust command-line utility to construct highly customized calls to the CreateProcess() Windows API to start new processes. Released under a MIT or LGPL license. 5 | 6 | This project is intended primarily for use from batch files (.bat) to execute other programs. If it can be done with CreateProcess(), it can be done with this command-line program. 7 | 8 | Why would you need this? One use-case would be for programs that don't play nice even with the 'start' command. For example, Apache 'httpd.exe' hangs the command-line even with 'start /B /MIN' but running this program with `/f=DETACHED_PROCESS` to start Apache and it runs completely in the background. I developed this initially for Apache (way overkill for one feature), but I've had need for this program for other things on many different occasions, including starting processes from scripting languages that don't offer sufficient facilities on Windows. 9 | 10 | Learn about security tokens, advanced usage of the CreateProcess command-line tool, and much more: 11 | 12 | [![Windows Security Objects: A Crash Course + A Brand New Way to Start Processes on Microsoft Windows video](https://user-images.githubusercontent.com/1432111/118288197-0574ec00-b489-11eb-96e5-fab0f6149171.png)](https://www.youtube.com/watch?v=pmteqkbBfAY "Windows Security Objects: A Crash Course + A Brand New Way to Start Processes on Microsoft Windows") 13 | 14 | [![Donate](https://cubiclesoft.com/res/donate-shield.png)](https://cubiclesoft.com/donate/) [![Discord](https://img.shields.io/discord/777282089980526602?label=chat&logo=discord)](https://cubiclesoft.com/product-support/github/) 15 | 16 | Features 17 | -------- 18 | 19 | * Command-line action! 20 | * Verbose mode tells you exactly how CreateProcess() will be called. No more guessing! 21 | * Can redirect stdin, stdout, and stderr to TCP/IP sockets. Avoid blocking on anonymous pipes or storing output in files! 22 | * Can use named mutexes and semaphores to control how many processes can run at the same time. 23 | * Start elevated processes (UAC support). 24 | * Start child processes as any valid user without requiring the user's credentials. Including the powerful NT AUTHORITY\SYSTEM account! 25 | * Pre-built binaries using Visual Studio (statically linked C++ runtime, minimal file size of ~162K, direct Win32 API calls). 26 | * Console and Windows subsystem variants. 27 | * Unicode support. 28 | * Offers almost everything CreateProcess() offers plus a couple of nice extras (e.g. output the process ID to a file). 29 | * Has a liberal open source license. MIT or LGPL, your choice. 30 | * Sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively. 31 | 32 | Useful Information 33 | ------------------ 34 | 35 | Running the command by itself will display the options: 36 | 37 | ``` 38 | (C) 2021 CubicleSoft. All Rights Reserved. 39 | 40 | Syntax: createprocess [options] EXEToRun [arguments] 41 | 42 | Options: 43 | /v 44 | Verbose mode. 45 | 46 | /w[=Milliseconds] 47 | Waits for the process to complete before exiting. 48 | The default behavior is to return immediately. 49 | If Milliseconds is specified, the number of milliseconds to wait. 50 | Return code, if any, is returned to caller. 51 | 52 | /pid=File 53 | Writes the process ID to the specified file. 54 | 55 | /term 56 | Used with /w=Milliseconds. 57 | Terminates the process when the wait time is up. 58 | 59 | /runelevated 60 | Calls CreateProcess() as a high Integrity Level elevated process. 61 | /w should be specified before this option. 62 | May trigger elevation. Not compatible with /term when not elevated. 63 | 64 | /elevatedtoken 65 | Uses an elevated token to create a child process. 66 | May create a temporary SYSTEM service to copy the primary token via 67 | undocumented Windows kernel APIs. 68 | 69 | /systemtoken 70 | Uses a SYSTEM token to create a child process. 71 | May create a temporary SYSTEM service to copy the primary token via 72 | undocumented Windows kernel APIs. 73 | 74 | /usetoken=PIDorSIDsAndPrivileges 75 | Uses the primary token of the specified process ID, 76 | or a process matching specific comma-separated user/group SIDs 77 | and/or a process with specific privileges. 78 | May trigger elevation. See /elevatedtoken. 79 | 80 | /createtoken=Parameters 81 | Creates a primary token from scratch. 82 | May trigger elevation. See /elevatedtoken. 83 | Uses an undocumented Windows kernel API. 84 | The 'Parameters' are semicolon separated: 85 | UserSID; 86 | GroupSID:Attr,GroupSID:Attr,...; 87 | Privilege:Attr,Privilege:Attr,...; 88 | OwnerSID; 89 | PrimaryGroupSID; 90 | DefaultDACL; 91 | SourceInHex:SourceLUID 92 | 93 | /mergeenv 94 | Merges the current environment with another user environment. 95 | Use with /elevatedtoken, /systemtoken, /usetoken, /createtoken. 96 | 97 | /mutex=MutexName 98 | Creates a mutex with the specified name. 99 | Use the named mutex with /singleton or other software 100 | to detect an already running instance. 101 | 102 | /singleton[=Milliseconds] 103 | Only starts the target process if named /mutex is the only instance. 104 | If Milliseconds is specified, the number of milliseconds to wait. 105 | 106 | /semaphore=MaxCount,SemaphoreName 107 | Creates a semaphore with the specified name and limit/count. 108 | Use the named semaphore with /multiton 109 | to limit the number of running processes. 110 | 111 | /multiton[=Milliseconds] 112 | Checks or waits for a named /semaphore. 113 | If Milliseconds is specified, the number of milliseconds to wait. 114 | 115 | /f=PriorityClass 116 | Sets the priority class of the new process. 117 | There is only one priority class per process. 118 | The 'PriorityClass' can be one of: 119 | ABOVE_NORMAL_PRIORITY_CLASS 120 | BELOW_NORMAL_PRIORITY_CLASS 121 | HIGH_PRIORITY_CLASS 122 | IDLE_PRIORITY_CLASS 123 | NORMAL_PRIORITY_CLASS 124 | REALTIME_PRIORITY_CLASS 125 | 126 | /f=CreateFlag 127 | Sets a creation flag for the new process. 128 | Multiple /f options can be specified. 129 | Each 'CreateFlag' can be one of: 130 | CREATE_DEFAULT_ERROR_MODE 131 | CREATE_NEW_CONSOLE 132 | CREATE_NEW_PROCESS_GROUP 133 | CREATE_NO_WINDOW 134 | CREATE_PROTECTED_PROCESS 135 | CREATE_PRESERVE_CODE_AUTHZ_LEVEL 136 | CREATE_SEPARATE_WOW_VDM 137 | CREATE_SHARED_WOW_VDM 138 | DEBUG_ONLY_THIS_PROCESS 139 | DEBUG_PROCESS 140 | DETACHED_PROCESS 141 | INHERIT_PARENT_AFFINITY 142 | 143 | /dir=StartDir 144 | Sets the starting directory of the new process. 145 | 146 | /desktop=Desktop 147 | Sets the STARTUPINFO.lpDesktop member to target a specific desktop. 148 | 149 | /title=WindowTitle 150 | Sets the STARTUPINFO.lpTitle member to a specific title. 151 | 152 | /x=XPositionInPixels 153 | Sets the STARTUPINFO.dwX member to a specific x-axis position, in pixels. 154 | 155 | /y=YPositionInPixels 156 | Sets the STARTUPINFO.dwY member to a specific y-axis position, in pixels. 157 | 158 | /width=WidthInPixels 159 | Sets the STARTUPINFO.dwXSize member to a specific width, in pixels. 160 | 161 | /height=HeightInPixels 162 | Sets the STARTUPINFO.dwYSize member to a specific height, in pixels. 163 | 164 | /xchars=BufferWidthInCharacters 165 | Sets the STARTUPINFO.dwXCountChars member to buffer width, in characters. 166 | 167 | /ychars=BufferHeightInCharacters 168 | Sets the STARTUPINFO.dwYCountChars member to buffer height, in characters. 169 | 170 | /f=FillAttribute 171 | Sets the STARTUPINFO.dwFillAttribute member text and background colors. 172 | Multiple /f options can be specified. 173 | Each 'FillAttribute' can be one of: 174 | FOREGROUND_RED 175 | FOREGROUND_GREEN 176 | FOREGROUND_BLUE 177 | FOREGROUND_INTENSITY 178 | BACKGROUND_RED 179 | BACKGROUND_GREEN 180 | BACKGROUND_BLUE 181 | BACKGROUND_INTENSITY 182 | 183 | /f=StartupFlag 184 | Sets the STARTUPINFO.dwFlags flag for the new process. 185 | Multiple /f options can be specified. 186 | Each 'StartupFlag' can be one of: 187 | STARTF_FORCEONFEEDBACK 188 | STARTF_FORCEOFFFEEDBACK 189 | STARTF_PREVENTPINNING 190 | STARTF_RUNFULLSCREEN 191 | STARTF_TITLEISAPPID 192 | STARTF_TITLEISLINKNAME 193 | 194 | /f=ShowWindow 195 | Sets the STARTUPINFO.wShowWindow flag for the new process. 196 | There is only one show window option per process. 197 | The 'ShowWindow' value can be one of: 198 | SW_FORCEMINIMIZE 199 | SW_HIDE 200 | SW_MAXIMIZE 201 | SW_MINIMIZE 202 | SW_RESTORE 203 | SW_SHOW 204 | SW_SHOWDEFAULT 205 | SW_SHOWMAXIMIZED 206 | SW_SHOWMINIMIZED 207 | SW_SHOWMINNOACTIVE 208 | SW_SHOWNA 209 | SW_SHOWNOACTIVATE 210 | SW_SHOWNORMAL 211 | 212 | /hotkey=HotkeyValue 213 | Sets the STARTUPINFO.hStdInput handle for the new process. 214 | Specifies the wParam member of a WM_SETHOKEY message to the new process. 215 | 216 | /socketip=IPAddress 217 | Specifies the IP address to connect to over TCP/IP. 218 | 219 | /socketport=PortNumber 220 | Specifies the port number to connect to over TCP/IP. 221 | 222 | /sockettoken=Token 223 | Specifies the token to send to each socket. 224 | Less secure than using /sockettokenlen and stdin. 225 | 226 | /sockettokenlen=TokenLength 227 | Specifies the length of the token to read from stdin. 228 | When specified, a token must be sent for each socket. 229 | 230 | /stdin=FileOrEmptyOrsocket 231 | Sets the STARTUPINFO.hStdInput handle for the new process. 232 | When this option is empty, INVALID_HANDLE_VALUE is used. 233 | When this option is 'socket', the /socket IP and port are used. 234 | When this option is not specified, the current stdin is used. 235 | 236 | /stdout=FileOrEmptyOrsocket 237 | Sets the STARTUPINFO.hStdOutput handle for the new process. 238 | When this option is empty, INVALID_HANDLE_VALUE is used. 239 | When this option is 'socket', the /socket IP and port are used. 240 | When this option is not specified, the current stdout is used. 241 | 242 | /stderr=FileOrEmptyOrstdoutOrsocket 243 | Sets the STARTUPINFO.hStdError handle for the new process. 244 | When this option is empty, INVALID_HANDLE_VALUE is used. 245 | When this option is 'stdout', the value of stdout is used. 246 | When this option is 'socket', the /socket IP and port are used. 247 | When this option is not specified, the current stderr is used. 248 | 249 | /attach[=ProcessID] 250 | Attempt to attach to a parent OR a specific process' console. 251 | Also resets standard handles back to defaults. 252 | ``` 253 | 254 | Example usage: 255 | 256 | ``` 257 | C:\>createprocess /f=DETACHED_PROCESS "C:\Program Files\Apache\httpd.exe" 258 | ``` 259 | 260 | That starts Apache with a detached console so it runs entirely in the background. 261 | 262 | Another example: 263 | 264 | ``` 265 | C:\>whoami 266 | my-pc\john-doh 267 | 268 | C:\>whoami /priv 269 | 270 | PRIVILEGES INFORMATION 271 | ---------------------- 272 | 273 | Privilege Name Description State 274 | ============================= ==================================== ======== 275 | SeShutdownPrivilege Shut down the system Disabled 276 | SeChangeNotifyPrivilege Bypass traverse checking Enabled 277 | SeUndockPrivilege Remove computer from docking station Disabled 278 | SeIncreaseWorkingSetPrivilege Increase a process working set Disabled 279 | SeTimeZonePrivilege Change the time zone Disabled 280 | 281 | C:\>set MYVAR=123 282 | 283 | C:\>createprocess /w /systemtoken /mergeenv C:\Windows\System32\cmd.exe 284 | Microsoft Windows [Version 10.0.19042.867] 285 | (c) 2020 Microsoft Corporation. All rights reserved. 286 | 287 | C:\>whoami 288 | nt authority\system 289 | 290 | C:\>whoami /priv 291 | 292 | PRIVILEGES INFORMATION 293 | ---------------------- 294 | 295 | Privilege Name Description State 296 | ========================================= ================================================================== ======== 297 | SeAssignPrimaryTokenPrivilege Replace a process level token Enabled 298 | SeLockMemoryPrivilege Lock pages in memory Enabled 299 | SeIncreaseQuotaPrivilege Adjust memory quotas for a process Enabled 300 | SeTcbPrivilege Act as part of the operating system Enabled 301 | SeSecurityPrivilege Manage auditing and security log Disabled 302 | SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled 303 | SeLoadDriverPrivilege Load and unload device drivers Disabled 304 | SeDebugPrivilege Debug programs Enabled 305 | ... [It's a fairly lengthy list of powerful privileges] 306 | SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Enabled 307 | 308 | C:\>set MYVAR 309 | MYVAR=123 310 | ``` 311 | 312 | That starts a Command Prompt child process as NT AUTHORITY\SYSTEM (the most powerful user account in Windows) in the same console session of the parent non-elevated process with full SYSTEM privileges and merges the current environment variables with the SYSTEM user's environment variables. 313 | 314 | Learn how the various `/...token` options work by watching the video linked to at the top of this repo or just [look at the insane diagram](https://github.com/cubiclesoft/createprocess-windows/blob/master/starting_a_child_process_as_another_user_diagram.png). 315 | 316 | Even the most hardcore command-line enthusiast should be drooling right now due to brain melt. Be sure to check out the source code. 317 | 318 | Windows Subsystem Variant 319 | ------------------------- 320 | 321 | While `createprocess.exe` is intended for use with console apps, `createprocess-win.exe` is intended for detached console and GUI applications. Starting `createprocess.exe` in certain situations will briefly flash a console window before starting the target process. Calling `createprocess-win.exe` instead will no longer show the console window. 322 | 323 | Why not just use `createprocess-win.exe`? Since `createprocess-win.exe` starts as a Windows GUI application, there is the tendency for it to be run in the background and may not behave as expected with various handles. The software is a little bit trickier to work with as a result. It's also a few KB larger than `createprocess.exe`. 324 | 325 | There is one additional option specifically for `createprocess-win.exe` called `/attach` which attempts to attach to the console of the parent process (if any) and will also reset the standard handles. The `/attach` option, if used, should generally be specified before other options. 326 | 327 | TCP/IP Notes 328 | ------------ 329 | 330 | The TCP/IP socket options represent a security risk so take proper precautions. Example usage can be seen in the [ProcessHelper class](https://github.com/cubiclesoft/php-misc/blob/master/support/process_helper.php). 331 | 332 | In addition, passing SOCKET handles to the target process causes problems. Sometimes the target process works just fine and sometimes it doesn't. To deal with this issue, up to three threads are started, one for each of the standard handles. Each thread routes data between its socket handle and an associated anonymous pipe of the started process. As a consequence of using the TCP/IP socket option, the `/w` option is always set so that the started process is waited on (i.e. so the threads can transfer data). This doesn't exactly matter as the `/w` option would be used anyway by the caller when passing socket options. 333 | 334 | Sources 335 | ------- 336 | 337 | The CreateProcess() API in MSDN Library has the intimate details on most options: 338 | 339 | http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx 340 | 341 | Related Tools 342 | ------------- 343 | 344 | * [PHP Process Helper Class](https://github.com/cubiclesoft/php-misc) - Uses this software to start processes on Windows in the background with non-blocking stdin/stdout/stderr via TCP/IP sockets. 345 | * [GetTokenInformation](https://github.com/cubiclesoft/gettokeninformation-windows) - Dumps information about Windows security tokens (SIDs, privileges, etc) as JSON. 346 | * [GetSIDInfo](https://github.com/cubiclesoft/getsidinfo-windows) - Dumps information about Windows Security Identifiers (SIDs) as JSON. 347 | -------------------------------------------------------------------------------- /createprocess.cpp: -------------------------------------------------------------------------------- 1 | // A simple program whose sole job is to run custom CreateProcess() commands. 2 | // Useful for executing programs from batch files that don't play nice (e.g. Apache) 3 | // or working around limitations in scripting languages. 4 | // 5 | // (C) 2021 CubicleSoft. All Rights Reserved. 6 | 7 | #define UNICODE 8 | #define _UNICODE 9 | #define _CRT_SECURE_NO_WARNINGS 10 | 11 | #ifdef _MBCS 12 | #undef _MBCS 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | //#include 25 | //#include 26 | #include 27 | #include 28 | 29 | #include "templates/static_wc_mixed_var.h" 30 | #include "templates/static_mixed_var.h" 31 | #include "templates/shared_lib.h" 32 | #include "templates/packed_ordered_hash.h" 33 | 34 | #ifndef ERROR_ELEVATION_REQUIRED 35 | #define ERROR_ELEVATION_REQUIRED 740 36 | #endif 37 | 38 | #ifndef INHERIT_PARENT_AFFINITY 39 | #define INHERIT_PARENT_AFFINITY 0x00010000L 40 | #endif 41 | 42 | #ifndef STARTF_TITLEISLINKNAME 43 | #define STARTF_TITLEISLINKNAME 0x00000800L 44 | #endif 45 | 46 | #ifndef STARTF_TITLEISAPPID 47 | #define STARTF_TITLEISAPPID 0x00001000L 48 | #endif 49 | 50 | #ifndef STARTF_PREVENTPINNING 51 | #define STARTF_PREVENTPINNING 0x00002000L 52 | #endif 53 | 54 | #ifdef SUBSYSTEM_WINDOWS 55 | // If the caller is a console application and is waiting for this application to complete, then attach to the console. 56 | void InitVerboseMode(void) 57 | { 58 | if (::AttachConsole(ATTACH_PARENT_PROCESS)) 59 | { 60 | if (::GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) 61 | { 62 | freopen("CONOUT$", "w", stdout); 63 | setvbuf(stdout, NULL, _IONBF, 0); 64 | } 65 | 66 | if (::GetStdHandle(STD_ERROR_HANDLE) != INVALID_HANDLE_VALUE) 67 | { 68 | freopen("CONOUT$", "w", stderr); 69 | setvbuf(stderr, NULL, _IONBF, 0); 70 | } 71 | } 72 | } 73 | #endif 74 | 75 | void DumpSyntax(TCHAR *currfile) 76 | { 77 | #ifdef SUBSYSTEM_WINDOWS 78 | InitVerboseMode(); 79 | #endif 80 | 81 | _tprintf(_T("(C) 2021 CubicleSoft. All Rights Reserved.\n\n")); 82 | 83 | _tprintf(_T("Syntax: %s [options] EXEToRun [arguments]\n\n"), currfile); 84 | 85 | _tprintf(_T("Options:\n")); 86 | 87 | _tprintf(_T("\t/v\n\ 88 | \tVerbose mode.\n\ 89 | \n\ 90 | \t/w[=Milliseconds]\n\ 91 | \tWaits for the process to complete before exiting.\n\ 92 | \tThe default behavior is to return immediately.\n\ 93 | \tIf Milliseconds is specified, the number of milliseconds to wait.\n\ 94 | \tReturn code, if any, is returned to caller.\n\ 95 | \n\ 96 | \t/pid=File\n\ 97 | \tWrites the process ID to the specified file.\n\ 98 | \n\ 99 | \t/term\n\ 100 | \tUsed with /w=Milliseconds.\n\ 101 | \tTerminates the process when the wait time is up.\n\ 102 | \n\ 103 | \t/runelevated\n\ 104 | \tCalls CreateProcess() as a high Integrity Level elevated process.\n\ 105 | \t/w should be specified before this option.\n\ 106 | \tMay trigger elevation. Not compatible with /term when not elevated.\n\ 107 | \n\ 108 | \t/elevatedtoken\n\ 109 | \tUses an elevated token to create a child process.\n\ 110 | \tMay create a temporary SYSTEM service to copy the primary token via\n\ 111 | \tundocumented Windows kernel APIs.\n\ 112 | \n\ 113 | \t/systemtoken\n\ 114 | \tUses a SYSTEM token to create a child process.\n\ 115 | \tMay create a temporary SYSTEM service to copy the primary token via\n\ 116 | \tundocumented Windows kernel APIs.\n\ 117 | \n\ 118 | \t/usetoken=PIDorSIDsAndPrivileges\n\ 119 | \tUses the primary token of the specified process ID,\n\ 120 | \tor a process matching specific comma-separated user/group SIDs\n\ 121 | \tand/or a process with specific privileges.\n\ 122 | \tMay trigger elevation. See /elevatedtoken.\n\ 123 | \n\ 124 | \t/createtoken=Parameters\n\ 125 | \tCreates a primary token from scratch.\n\ 126 | \tMay trigger elevation. See /elevatedtoken.\n\ 127 | \tUses an undocumented Windows kernel API.\n\ 128 | \tThe 'Parameters' are semicolon separated:\n\ 129 | \t\tUserSID;\n\ 130 | \t\tGroupSID:Attr,GroupSID:Attr,...;\n\ 131 | \t\tPrivilege:Attr,Privilege:Attr,...;\n\ 132 | \t\tOwnerSID;\n\ 133 | \t\tPrimaryGroupSID;\n\ 134 | \t\tDefaultDACL;\n\ 135 | \t\tSourceInHex:SourceLUID\n\ 136 | \n\ 137 | \t/mergeenv\n\ 138 | \tMerges the current environment with another user environment.\n\ 139 | \tUse with /elevatedtoken, /systemtoken, /usetoken, /createtoken.\n\ 140 | \n\ 141 | \t/mutex=MutexName\n\ 142 | \tCreates a mutex with the specified name.\n\ 143 | \tUse the named mutex with /singleton or other software\n\ 144 | \tto detect an already running instance.\n\ 145 | \n\ 146 | \t/singleton[=Milliseconds]\n\ 147 | \tOnly starts the target process if named /mutex is the only instance.\n\ 148 | \tIf Milliseconds is specified, the number of milliseconds to wait.\n\ 149 | \n\ 150 | \t/semaphore=MaxCount,SemaphoreName\n\ 151 | \tCreates a semaphore with the specified name and limit/count.\n\ 152 | \tUse the named semaphore with /multiton\n\ 153 | \tto limit the number of running processes.\n\ 154 | \n\ 155 | \t/multiton[=Milliseconds]\n\ 156 | \tChecks or waits for a named /semaphore.\n\ 157 | \tIf Milliseconds is specified, the number of milliseconds to wait.\n\ 158 | \n\ 159 | \t/f=PriorityClass\n\ 160 | \tSets the priority class of the new process.\n\ 161 | \tThere is only one priority class per process.\n\ 162 | \tThe 'PriorityClass' can be one of:\n\ 163 | \t\tABOVE_NORMAL_PRIORITY_CLASS\n\ 164 | \t\tBELOW_NORMAL_PRIORITY_CLASS\n\ 165 | \t\tHIGH_PRIORITY_CLASS\n\ 166 | \t\tIDLE_PRIORITY_CLASS\n\ 167 | \t\tNORMAL_PRIORITY_CLASS\n\ 168 | \t\tREALTIME_PRIORITY_CLASS\n\ 169 | \n\ 170 | \t/f=CreateFlag\n\ 171 | \tSets a creation flag for the new process.\n\ 172 | \tMultiple /f options can be specified.\n\ 173 | \tEach 'CreateFlag' can be one of:\n\ 174 | \t\tCREATE_DEFAULT_ERROR_MODE\n\ 175 | \t\tCREATE_NEW_CONSOLE\n\ 176 | \t\tCREATE_NEW_PROCESS_GROUP\n\ 177 | \t\tCREATE_NO_WINDOW\n\ 178 | \t\tCREATE_PROTECTED_PROCESS\n\ 179 | \t\tCREATE_PRESERVE_CODE_AUTHZ_LEVEL\n\ 180 | \t\tCREATE_SEPARATE_WOW_VDM\n\ 181 | \t\tCREATE_SHARED_WOW_VDM\n\ 182 | \t\tDEBUG_ONLY_THIS_PROCESS\n\ 183 | \t\tDEBUG_PROCESS\n\ 184 | \t\tDETACHED_PROCESS\n\ 185 | \t\tINHERIT_PARENT_AFFINITY\n\ 186 | \n\ 187 | \t/dir=StartDir\n\ 188 | \tSets the starting directory of the new process.\n\ 189 | \n\ 190 | \t/desktop=Desktop\n\ 191 | \tSets the STARTUPINFO.lpDesktop member to target a specific desktop.\n\ 192 | \n\ 193 | \t/title=WindowTitle\n\ 194 | \tSets the STARTUPINFO.lpTitle member to a specific title.\n\ 195 | \n\ 196 | \t/x=XPositionInPixels\n\ 197 | \tSets the STARTUPINFO.dwX member to a specific x-axis position, in pixels.\n\ 198 | \n\ 199 | \t/y=YPositionInPixels\n\ 200 | \tSets the STARTUPINFO.dwY member to a specific y-axis position, in pixels.\n\ 201 | \n\ 202 | \t/width=WidthInPixels\n\ 203 | \tSets the STARTUPINFO.dwXSize member to a specific width, in pixels.\n\ 204 | \n\ 205 | \t/height=HeightInPixels\n\ 206 | \tSets the STARTUPINFO.dwYSize member to a specific height, in pixels.\n\ 207 | \n\ 208 | \t/xchars=BufferWidthInCharacters\n\ 209 | \tSets the STARTUPINFO.dwXCountChars member to buffer width, in characters.\n\ 210 | \n\ 211 | \t/ychars=BufferHeightInCharacters\n\ 212 | \tSets the STARTUPINFO.dwYCountChars member to buffer height, in characters.\n\ 213 | \n\ 214 | \t/f=FillAttribute\n\ 215 | \tSets the STARTUPINFO.dwFillAttribute member text and background colors.\n\ 216 | \tMultiple /f options can be specified.\n\ 217 | \tEach 'FillAttribute' can be one of:\n\ 218 | \t\tFOREGROUND_RED\n\ 219 | \t\tFOREGROUND_GREEN\n\ 220 | \t\tFOREGROUND_BLUE\n\ 221 | \t\tFOREGROUND_INTENSITY\n\ 222 | \t\tBACKGROUND_RED\n\ 223 | \t\tBACKGROUND_GREEN\n\ 224 | \t\tBACKGROUND_BLUE\n\ 225 | \t\tBACKGROUND_INTENSITY\n\ 226 | \n\ 227 | \t/f=StartupFlag\n\ 228 | \tSets the STARTUPINFO.dwFlags flag for the new process.\n\ 229 | \tMultiple /f options can be specified.\n\ 230 | \tEach 'StartupFlag' can be one of:\n\ 231 | \t\tSTARTF_FORCEONFEEDBACK\n\ 232 | \t\tSTARTF_FORCEOFFFEEDBACK\n\ 233 | \t\tSTARTF_PREVENTPINNING\n\ 234 | \t\tSTARTF_RUNFULLSCREEN\n\ 235 | \t\tSTARTF_TITLEISAPPID\n\ 236 | \t\tSTARTF_TITLEISLINKNAME\n\ 237 | \n\ 238 | \t/f=ShowWindow\n\ 239 | \tSets the STARTUPINFO.wShowWindow flag for the new process.\n\ 240 | \tThere is only one show window option per process.\n\ 241 | \tThe 'ShowWindow' value can be one of:\n\ 242 | \t\tSW_FORCEMINIMIZE\n\ 243 | \t\tSW_HIDE\n\ 244 | \t\tSW_MAXIMIZE\n\ 245 | \t\tSW_MINIMIZE\n\ 246 | \t\tSW_RESTORE\n\ 247 | \t\tSW_SHOW\n\ 248 | \t\tSW_SHOWDEFAULT\n\ 249 | \t\tSW_SHOWMAXIMIZED\n\ 250 | \t\tSW_SHOWMINIMIZED\n\ 251 | \t\tSW_SHOWMINNOACTIVE\n\ 252 | \t\tSW_SHOWNA\n\ 253 | \t\tSW_SHOWNOACTIVATE\n\ 254 | \t\tSW_SHOWNORMAL\n\ 255 | \n\ 256 | \t/hotkey=HotkeyValue\n\ 257 | \tSets the STARTUPINFO.hStdInput handle for the new process.\n\ 258 | \tSpecifies the wParam member of a WM_SETHOKEY message to the new process.\n\ 259 | \n\ 260 | \t/socketip=IPAddress\n\ 261 | \tSpecifies the IP address to connect to over TCP/IP.\n\ 262 | \n\ 263 | \t/socketport=PortNumber\n\ 264 | \tSpecifies the port number to connect to over TCP/IP.\n\ 265 | \n\ 266 | \t/sockettoken=Token\n\ 267 | \tSpecifies the token to send to each socket.\n\ 268 | \tLess secure than using /sockettokenlen and stdin.\n\ 269 | \n\ 270 | \t/sockettokenlen=TokenLength\n\ 271 | \tSpecifies the length of the token to read from stdin.\n\ 272 | \tWhen specified, a token must be sent for each socket.\n\ 273 | \n\ 274 | \t/stdin=FileOrEmptyOrsocket\n\ 275 | \tSets the STARTUPINFO.hStdInput handle for the new process.\n\ 276 | \tWhen this option is empty, INVALID_HANDLE_VALUE is used.\n\ 277 | \tWhen this option is 'socket', the /socket IP and port are used.\n\ 278 | \tWhen this option is not specified, the current stdin is used.\n\ 279 | \n\ 280 | \t/stdout=FileOrEmptyOrsocket\n\ 281 | \tSets the STARTUPINFO.hStdOutput handle for the new process.\n\ 282 | \tWhen this option is empty, INVALID_HANDLE_VALUE is used.\n\ 283 | \tWhen this option is 'socket', the /socket IP and port are used.\n\ 284 | \tWhen this option is not specified, the current stdout is used.\n\ 285 | \n\ 286 | \t/stderr=FileOrEmptyOrstdoutOrsocket\n\ 287 | \tSets the STARTUPINFO.hStdError handle for the new process.\n\ 288 | \tWhen this option is empty, INVALID_HANDLE_VALUE is used.\n\ 289 | \tWhen this option is 'stdout', the value of stdout is used.\n\ 290 | \tWhen this option is 'socket', the /socket IP and port are used.\n\ 291 | \tWhen this option is not specified, the current stderr is used.\n\ 292 | \n\ 293 | \t/attach[=ProcessID]\n\ 294 | \tAttempt to attach to a parent OR a specific process' console.\n\ 295 | \tAlso resets standard handles back to defaults.\n\n")); 296 | } 297 | 298 | bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable) 299 | { 300 | HANDLE Token; 301 | TOKEN_PRIVILEGES TokenPrivs; 302 | LUID TempLuid; 303 | bool Result; 304 | 305 | if (!::LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid)) return false; 306 | 307 | if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token)) 308 | { 309 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token)) return false; 310 | } 311 | 312 | TokenPrivs.PrivilegeCount = 1; 313 | TokenPrivs.Privileges[0].Luid = TempLuid; 314 | TokenPrivs.Privileges[0].Attributes = (Enable ? SE_PRIVILEGE_ENABLED : 0); 315 | 316 | Result = (::AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, 0, NULL, NULL) && ::GetLastError() == ERROR_SUCCESS); 317 | 318 | ::CloseHandle(Token); 319 | 320 | return Result; 321 | } 322 | 323 | DWORD GxConnectPipePID = 0; 324 | HANDLE GxMainCommPipeClient = INVALID_HANDLE_VALUE; 325 | 326 | // Creates an event object to be able to timeout on for the named pipe. 327 | HANDLE CreateCommPipeConnectEventObj(DWORD pid) 328 | { 329 | CubicleSoft::StaticWCMixedVar TempVar; 330 | 331 | TempVar.SetStr(L"Global\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_CommEv_"); 332 | TempVar.AppendUInt(pid); 333 | 334 | return ::CreateEventW(NULL, FALSE, FALSE, TempVar.GetStr()); 335 | } 336 | 337 | // Attempt to connect to the named pipe. Does not emit errors. 338 | bool ConnectToMainCommPipe() 339 | { 340 | if (GxMainCommPipeClient != INVALID_HANDLE_VALUE) return true; 341 | 342 | if (!GxConnectPipePID) return false; 343 | 344 | CubicleSoft::StaticWCMixedVar TempVar; 345 | HANDLE tempevent; 346 | 347 | tempevent = CreateCommPipeConnectEventObj(GxConnectPipePID); 348 | if (tempevent == NULL) return false; 349 | 350 | TempVar.SetStr(L"\\\\.\\pipe\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_Comm_"); 351 | TempVar.AppendUInt(GxConnectPipePID); 352 | 353 | do 354 | { 355 | // Notify the pipe server that a connection is incoming. 356 | if (!::SetEvent(tempevent)) break; 357 | 358 | // Attempt to connect. 359 | GxMainCommPipeClient = ::CreateFile(TempVar.GetStr(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 360 | if (GxMainCommPipeClient != INVALID_HANDLE_VALUE) return true; 361 | 362 | if (::GetLastError() != ERROR_PIPE_BUSY) break; 363 | 364 | if (!::WaitNamedPipe(TempVar.GetStr(), 10000)) break; 365 | } while (1); 366 | 367 | ::CloseHandle(tempevent); 368 | 369 | return false; 370 | } 371 | 372 | void DisconnectMainCommPipe() 373 | { 374 | if (GxMainCommPipeClient == INVALID_HANDLE_VALUE) return; 375 | 376 | ::CloseHandle(GxMainCommPipeClient); 377 | 378 | GxMainCommPipeClient = INVALID_HANDLE_VALUE; 379 | GxConnectPipePID = 0; 380 | } 381 | 382 | HANDLE StartMainCommPipeServer() 383 | { 384 | CubicleSoft::StaticWCMixedVar TempVar; 385 | 386 | TempVar.SetStr(L"\\\\.\\pipe\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_Comm_"); 387 | TempVar.AppendUInt(::GetCurrentProcessId()); 388 | 389 | return ::CreateNamedPipe(TempVar.GetStr(), PIPE_ACCESS_DUPLEX, 0, 1, 4096, 4096, 0, NULL); 390 | } 391 | 392 | void DumpErrorMsg(const char *errorstr, const char *errorcode, DWORD winerror) 393 | { 394 | #ifdef SUBSYSTEM_WINDOWS 395 | InitVerboseMode(); 396 | #endif 397 | 398 | CubicleSoft::StaticWCMixedVar TempVar; 399 | CubicleSoft::StaticMixedVar TempVar2; 400 | 401 | printf("%s (%s)\n", errorstr, errorcode); 402 | 403 | TempVar2.SetFormattedStr("[%lu] %s (%s)", ::GetCurrentProcessId(), errorstr, errorcode); 404 | 405 | if (winerror) 406 | { 407 | LPSTR errmsg = NULL; 408 | 409 | ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, winerror, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errmsg, 0, NULL); 410 | if (errmsg == NULL) 411 | { 412 | printf("%d - Unknown error\n", winerror); 413 | TempVar2.AppendFormattedStr(" (%lu - Unknown error)", winerror); 414 | } 415 | else 416 | { 417 | size_t y = strlen(errmsg); 418 | while (y && (errmsg[y - 1] == '\r' || errmsg[y - 1] == '\n')) errmsg[--y] = '\0'; 419 | 420 | printf("%d - %s\n", winerror, errmsg); 421 | TempVar2.AppendFormattedStr(" (%lu - %s)", winerror, errmsg); 422 | ::LocalFree(errmsg); 423 | } 424 | } 425 | 426 | TempVar2.AppendStr("\n"); 427 | 428 | // Send the error message to the main process' communication pipe. 429 | if (ConnectToMainCommPipe()) 430 | { 431 | TempVar.SetStr(TempVar2.GetStr()); 432 | 433 | DWORD tempsize = TempVar.GetSize() * sizeof(WCHAR); 434 | 435 | ::WriteFile(GxMainCommPipeClient, "\x00", 1, NULL, NULL); 436 | ::WriteFile(GxMainCommPipeClient, &tempsize, 4, NULL, NULL); 437 | ::WriteFile(GxMainCommPipeClient, TempVar.GetStr(), tempsize, NULL, NULL); 438 | 439 | DisconnectMainCommPipe(); 440 | } 441 | 442 | ::Sleep(5000); 443 | } 444 | 445 | void DumpWideErrorMsg(const WCHAR *errorstr, const char *errorcode, DWORD winerror) 446 | { 447 | char *errorstr2; 448 | BOOL useddefaultchar = FALSE; 449 | int retval = ::WideCharToMultiByte(CP_ACP, 0, errorstr, -1, NULL, 0, NULL, &useddefaultchar); 450 | if (!retval) 451 | { 452 | DumpErrorMsg("Unable to convert error message to multibyte.", "wide_char_to_multibyte_failed", ::GetLastError()); 453 | 454 | return; 455 | } 456 | 457 | DWORD tempsize = (DWORD)retval; 458 | errorstr2 = (char *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tempsize); 459 | if (errorstr2 == NULL) 460 | { 461 | DumpErrorMsg("Error message buffer allocation failed.", "heap_alloc_failed", ::GetLastError()); 462 | 463 | return; 464 | } 465 | 466 | useddefaultchar = FALSE; 467 | retval = ::WideCharToMultiByte(CP_ACP, 0, errorstr, -1, errorstr2, tempsize, NULL, &useddefaultchar); 468 | if (!retval) 469 | { 470 | DumpErrorMsg("Unable to convert error message to multibyte.", "wide_char_to_multibyte_failed", ::GetLastError()); 471 | 472 | ::HeapFree(::GetProcessHeap(), 0, errorstr2); 473 | 474 | return; 475 | } 476 | 477 | size_t y = strlen(errorstr2); 478 | while (y && (errorstr2[y - 1] == '\r' || errorstr2[y - 1] == '\n')) errorstr2[--y] = '\0'; 479 | 480 | // Display the error message. 481 | DumpErrorMsg(errorstr2, errorcode, winerror); 482 | 483 | ::HeapFree(::GetProcessHeap(), 0, errorstr2); 484 | } 485 | 486 | // Waits for a client to connect and then validates the session the client is running in. 487 | bool WaitForValidCommPipeClient(HANDLE &pipehandle, HANDLE eventhandle, DWORD timeout) 488 | { 489 | while (1) 490 | { 491 | // Wait for the event object to fire (allows for timeout). 492 | if (::WaitForSingleObject(eventhandle, timeout) != WAIT_OBJECT_0) 493 | { 494 | DumpErrorMsg("An elevated process did not respond before the timeout expired.", "timeout", ::GetLastError()); 495 | 496 | return false; 497 | } 498 | 499 | // Wait for the client to connect. 500 | if (!::ConnectNamedPipe(pipehandle, NULL) && ::GetLastError() != ERROR_PIPE_CONNECTED) 501 | { 502 | DumpErrorMsg("Main communication pipe failed.", "connect_named_pipe_failed", ::GetLastError()); 503 | 504 | return false; 505 | } 506 | 507 | // Allow connections from session 0 (SYSTEM) and the current process' session. 508 | ULONG clientsession; 509 | DWORD currsessionid; 510 | if (::GetNamedPipeClientSessionId(pipehandle, &clientsession) && ::ProcessIdToSessionId(::GetCurrentProcessId(), &currsessionid) && (clientsession == 0 || clientsession == currsessionid)) 511 | { 512 | return true; 513 | } 514 | 515 | // Restart the named pipe server. 516 | ::CloseHandle(pipehandle); 517 | 518 | pipehandle = StartMainCommPipeServer(); 519 | if (pipehandle == INVALID_HANDLE_VALUE) 520 | { 521 | DumpErrorMsg("Unable to create the main communication pipe.", "start_main_comm_pipe_failed", ::GetLastError()); 522 | 523 | return false; 524 | } 525 | } 526 | } 527 | 528 | BOOL ReadFileExact(HANDLE filehandle, LPVOID buffer, DWORD numbytes) 529 | { 530 | BOOL result; 531 | DWORD bytesread; 532 | 533 | do 534 | { 535 | result = ::ReadFile(filehandle, buffer, numbytes, &bytesread, NULL); 536 | if (!result) return result; 537 | 538 | buffer = (char *)buffer + bytesread; 539 | numbytes -= bytesread; 540 | } while (numbytes); 541 | 542 | return result; 543 | } 544 | 545 | // Waits until a single status byte is received. 546 | bool WaitForCommPipeStatus(HANDLE pipehandle) 547 | { 548 | char status; 549 | 550 | if (!ReadFileExact(pipehandle, &status, 1)) 551 | { 552 | DumpErrorMsg("The main communication pipe broke.", "read_main_comm_pipe_failed", ::GetLastError()); 553 | 554 | return false; 555 | } 556 | 557 | if (status) return true; 558 | 559 | // Read the error message. 560 | DWORD tempsize; 561 | WCHAR *msgbuffer; 562 | 563 | if (!ReadFileExact(pipehandle, &tempsize, 4)) 564 | { 565 | DumpErrorMsg("Unable to read size from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 566 | 567 | return false; 568 | } 569 | 570 | if (tempsize < 1 * sizeof(WCHAR) || tempsize % sizeof(WCHAR) != 0) 571 | { 572 | DumpErrorMsg("The read size from the main communication pipe is invalid.", "read_main_comm_pipe_invalid_size", ::GetLastError()); 573 | 574 | return false; 575 | } 576 | 577 | msgbuffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tempsize); 578 | if (msgbuffer == NULL) 579 | { 580 | DumpErrorMsg("Error message buffer allocation failed.", "heap_alloc_failed", ::GetLastError()); 581 | 582 | return false; 583 | } 584 | 585 | if (!ReadFileExact(pipehandle, msgbuffer, tempsize)) 586 | { 587 | DumpErrorMsg("Unable to read error message from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 588 | 589 | return false; 590 | } 591 | 592 | tempsize /= sizeof(WCHAR); 593 | 594 | msgbuffer[tempsize - 1] = L'\0'; 595 | 596 | // Convert to DumpErrorMsg() format. 597 | DumpWideErrorMsg(msgbuffer, "main_comm_pipe_client_sent_error", 0); 598 | 599 | ::HeapFree(::GetProcessHeap(), 0, msgbuffer); 600 | 601 | return false; 602 | } 603 | 604 | bool ReadCommPipeString(HANDLE commpipehandle, WCHAR *&buffer, DWORD &tempsize, size_t minsize) 605 | { 606 | if (commpipehandle == INVALID_HANDLE_VALUE) return false; 607 | 608 | if (!ReadFileExact(commpipehandle, &tempsize, 4)) 609 | { 610 | DumpErrorMsg("Unable to read size from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 611 | 612 | return false; 613 | } 614 | 615 | if (tempsize < minsize * sizeof(WCHAR) || tempsize % sizeof(WCHAR) != 0) 616 | { 617 | DumpErrorMsg("The read size from the main communication pipe is invalid.", "read_main_comm_pipe_invalid_size", 0); 618 | 619 | return false; 620 | } 621 | 622 | buffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tempsize); 623 | if (buffer == NULL) 624 | { 625 | DumpErrorMsg("Allocation failed.", "heap_alloc_failed", ::GetLastError()); 626 | 627 | return false; 628 | } 629 | 630 | if (!ReadFileExact(commpipehandle, buffer, tempsize)) 631 | { 632 | DumpErrorMsg("Unable to read buffer from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 633 | 634 | return false; 635 | } 636 | 637 | tempsize /= sizeof(WCHAR); 638 | 639 | buffer[tempsize - 1] = L'\0'; 640 | 641 | return true; 642 | } 643 | 644 | void FreeCommPipeString(WCHAR *&buffer) 645 | { 646 | if (buffer != NULL) ::HeapFree(::GetProcessHeap(), 0, (LPVOID)buffer); 647 | } 648 | 649 | bool MakeStringArrayFromString(WCHAR *buffer, DWORD buffersize, WCHAR **&arr, int &numitems) 650 | { 651 | if (buffersize < 2) 652 | { 653 | DumpErrorMsg("Insufficient buffer size.", "invalid_buffer_size", ::GetLastError()); 654 | 655 | return false; 656 | } 657 | 658 | buffer[buffersize - 2] = L'\0'; 659 | buffer[buffersize - 1] = L'\0'; 660 | 661 | // Parse the buffer into an array of zero-terminated WCHAR strings. 662 | int x, x2; 663 | numitems = 0; 664 | for (x = 0; x < (int)buffersize - 1; x++) 665 | { 666 | if (!buffer[x]) numitems++; 667 | } 668 | 669 | arr = (WCHAR **)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, (numitems + 1) * sizeof(WCHAR *)); 670 | if (arr == NULL) 671 | { 672 | DumpErrorMsg("Pointer buffer allocation failed.", "heap_alloc_failed", ::GetLastError()); 673 | 674 | return false; 675 | } 676 | 677 | arr[0] = buffer; 678 | x2 = 1; 679 | for (x = 0; x < (int)buffersize - 2; x++) 680 | { 681 | if (!buffer[x]) arr[x2++] = buffer + x + 1; 682 | } 683 | 684 | return true; 685 | } 686 | 687 | bool ReadCommPipeStringArray(HANDLE commpipehandle, WCHAR *&buffer, WCHAR **&arr, int &numitems) 688 | { 689 | DWORD tempsize; 690 | 691 | if (!ReadCommPipeString(commpipehandle, buffer, tempsize, 2)) return false; 692 | 693 | return MakeStringArrayFromString(buffer, tempsize, arr, numitems); 694 | } 695 | 696 | void FreeCommPipeStringArray(WCHAR *&buffer, WCHAR **&arr) 697 | { 698 | if (buffer != NULL) ::HeapFree(::GetProcessHeap(), 0, (LPVOID)buffer); 699 | if (arr != NULL) ::HeapFree(::GetProcessHeap(), 0, (LPVOID)arr); 700 | } 701 | 702 | inline void FreeTokenInformation(LPVOID tinfo) 703 | { 704 | if (tinfo != NULL) ::HeapFree(::GetProcessHeap(), 0, tinfo); 705 | } 706 | 707 | LPVOID AllocateAndGetTokenInformation(HANDLE token, TOKEN_INFORMATION_CLASS infoclass, DWORD sizehint) 708 | { 709 | LPVOID tinfo; 710 | DWORD tinfosize = sizehint; 711 | bool success; 712 | 713 | tinfo = (LPVOID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tinfosize); 714 | if (tinfo == NULL) tinfosize = 0; 715 | 716 | // Resize until the buffer is big enough. 717 | while (!(success = ::GetTokenInformation(token, infoclass, tinfo, tinfosize, &tinfosize)) && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) 718 | { 719 | if (tinfo != NULL) FreeTokenInformation(tinfo); 720 | 721 | tinfo = (LPVOID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tinfosize); 722 | if (tinfo == NULL) 723 | { 724 | tinfosize = 0; 725 | 726 | break; 727 | } 728 | } 729 | 730 | return tinfo; 731 | } 732 | 733 | // Get/Duplicate the primary token of the specified process ID as a primary or impersonation token. 734 | // duptype is ignored if accessmode does not specify TOKEN_DUPLICATE. 735 | HANDLE GetTokenFromPID(DWORD pid, TOKEN_TYPE duptype, DWORD accessmode = TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY) 736 | { 737 | HANDLE tempproc; 738 | HANDLE tokenhandle, tokenhandle2; 739 | 740 | // Enable SeDebugPrivilege. 741 | SetThreadProcessPrivilege(L"SeDebugPrivilege", true); 742 | 743 | // Open a handle to the process. 744 | tempproc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); 745 | if (tempproc == NULL) tempproc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); 746 | 747 | if (tempproc == NULL) 748 | { 749 | DumpErrorMsg("Unable to open a handle to the specified process.", "open_process_failed", ::GetLastError()); 750 | 751 | return INVALID_HANDLE_VALUE; 752 | } 753 | 754 | if (!::OpenProcessToken(tempproc, accessmode, &tokenhandle) && (!(accessmode & TOKEN_QUERY_SOURCE) || !::OpenProcessToken(tempproc, accessmode & ~TOKEN_QUERY_SOURCE, &tokenhandle))) 755 | { 756 | DumpErrorMsg("Unable to open a handle to the specified process token.", "open_process_token_failed", ::GetLastError()); 757 | 758 | ::CloseHandle(tempproc); 759 | 760 | return INVALID_HANDLE_VALUE; 761 | } 762 | 763 | ::CloseHandle(tempproc); 764 | 765 | if (!(accessmode & TOKEN_DUPLICATE)) tokenhandle2 = tokenhandle; 766 | else 767 | { 768 | SECURITY_ATTRIBUTES secattr = {0}; 769 | secattr.nLength = sizeof(secattr); 770 | secattr.bInheritHandle = FALSE; 771 | secattr.lpSecurityDescriptor = NULL; 772 | 773 | if (!::DuplicateTokenEx(tokenhandle, MAXIMUM_ALLOWED, &secattr, SecurityImpersonation, duptype, &tokenhandle2)) 774 | { 775 | DumpErrorMsg("Unable to duplicate the specified process token.", "duplicate_token_ex_failed", ::GetLastError()); 776 | 777 | ::CloseHandle(tokenhandle); 778 | 779 | return INVALID_HANDLE_VALUE; 780 | } 781 | 782 | ::CloseHandle(tokenhandle); 783 | } 784 | 785 | return tokenhandle2; 786 | } 787 | 788 | void GetNumSIDsAndLUIDs(LPWSTR tokenopts, size_t &numsids, size_t &numluids) 789 | { 790 | size_t x = 0; 791 | 792 | numsids = 0; 793 | numluids = 0; 794 | while (tokenopts[x] && tokenopts[x] != L';') 795 | { 796 | for (; tokenopts[x] && tokenopts[x] != L';' && tokenopts[x] != L'S'; x++); 797 | 798 | if (tokenopts[x] == L'S') 799 | { 800 | if (tokenopts[x + 1] == L'-') numsids++; 801 | else if (tokenopts[x + 1] == L'e') numluids++; 802 | } 803 | 804 | for (; tokenopts[x] && tokenopts[x] != L';' && tokenopts[x] != L','; x++); 805 | 806 | if (tokenopts[x] == L',') x++; 807 | } 808 | } 809 | 810 | bool GetNextTokenOptsSID(LPWSTR tokenopts, size_t &x, PSID &sidbuffer) 811 | { 812 | size_t x2; 813 | WCHAR tempchr; 814 | 815 | for (x2 = x; tokenopts[x2] && tokenopts[x2] != L';' && tokenopts[x2] != L':' && tokenopts[x2] != L','; x2++); 816 | tempchr = tokenopts[x2]; 817 | tokenopts[x2] = L'\0'; 818 | 819 | bool result = ::ConvertStringSidToSidW(tokenopts + x, &sidbuffer); 820 | if (!result) 821 | { 822 | DWORD winerror = ::GetLastError(); 823 | CubicleSoft::StaticWCMixedVar TempVar; 824 | 825 | TempVar.SetFormattedStr(L"The specified SID '%ls' in the token options is invalid.", tokenopts + x); 826 | 827 | DumpWideErrorMsg(TempVar.GetStr(), "invalid_sid", winerror); 828 | } 829 | 830 | tokenopts[x2] = tempchr; 831 | x = x2; 832 | 833 | return result; 834 | } 835 | 836 | bool GetNextTokenOptsLUID(LPWSTR tokenopts, size_t &x, LUID &luidbuffer) 837 | { 838 | size_t x2; 839 | WCHAR tempchr; 840 | 841 | for (x2 = x; tokenopts[x2] && tokenopts[x2] != L';' && tokenopts[x2] != L':' && tokenopts[x2] != L','; x2++); 842 | tempchr = tokenopts[x2]; 843 | tokenopts[x2] = L'\0'; 844 | 845 | bool result = ::LookupPrivilegeValueW(NULL, tokenopts + x, &luidbuffer); 846 | if (!result) 847 | { 848 | DWORD winerror = ::GetLastError(); 849 | CubicleSoft::StaticWCMixedVar TempVar; 850 | 851 | TempVar.SetFormattedStr(L"The specified privilege '%ls' in the token options is invalid.", tokenopts + x); 852 | 853 | DumpWideErrorMsg(TempVar.GetStr(), "invalid_privilege", winerror); 854 | } 855 | 856 | tokenopts[x2] = tempchr; 857 | x = x2; 858 | 859 | return result; 860 | } 861 | 862 | DWORD GetNextTokenOptsAttrs(LPWSTR tokenopts, size_t &x) 863 | { 864 | size_t x2; 865 | WCHAR tempchr; 866 | 867 | if (tokenopts[x] != L':') return 0; 868 | x++; 869 | 870 | for (x2 = x; tokenopts[x2] && tokenopts[x2] != L';' && tokenopts[x2] != L','; x2++); 871 | tempchr = tokenopts[x2]; 872 | tokenopts[x2] = L'\0'; 873 | 874 | DWORD result = (DWORD)_wtoi(tokenopts + x); 875 | 876 | tokenopts[x2] = tempchr; 877 | x = x2; 878 | 879 | return result; 880 | } 881 | 882 | // Attempts to locate an existing token that matches the input option string. 883 | // duptype is ignored if accessmode does not specify TOKEN_DUPLICATE. 884 | // Example: FindExistingTokenFromOpts(L"S-1-16-16384,SeDebugPrivilege,SeAssignPrimaryTokenPrivilege", true) 885 | HANDLE FindExistingTokenFromOpts(LPWSTR tokenopts, TOKEN_TYPE duptype, DWORD accessmode = TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY) 886 | { 887 | if (tokenopts[0] != L'S') return GetTokenFromPID(_wtoi(tokenopts), duptype, accessmode); 888 | 889 | // Split and convert the options into SIDs and privilege LUIDs. 890 | CubicleSoft::StaticWCMixedVar TempVar; 891 | size_t x, x2, numsids, numluids; 892 | PSID *sids = NULL; 893 | LUID *luids = NULL; 894 | 895 | TempVar.SetStr(tokenopts); 896 | WCHAR *tokenopts2 = TempVar.GetStr(); 897 | 898 | GetNumSIDsAndLUIDs(tokenopts2, numsids, numluids); 899 | 900 | if (numsids) sids = (PSID *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, numsids * sizeof(PSID)); 901 | if (numluids) luids = (LUID *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, numluids * sizeof(LUID)); 902 | 903 | bool valid = true; 904 | x = 0; 905 | numsids = 0; 906 | numluids = 0; 907 | while (tokenopts2[x]) 908 | { 909 | for (; tokenopts2[x] && tokenopts2[x] != L'S'; x++); 910 | 911 | if (tokenopts2[x] == L'S') 912 | { 913 | if (tokenopts2[x + 1] == L'-') 914 | { 915 | valid = GetNextTokenOptsSID(tokenopts2, x, sids[numsids]); 916 | if (!valid) break; 917 | 918 | numsids++; 919 | } 920 | else if (tokenopts2[x + 1] == L'e') 921 | { 922 | valid = GetNextTokenOptsLUID(tokenopts2, x, luids[numluids]); 923 | if (!valid) break; 924 | 925 | numluids++; 926 | } 927 | } 928 | 929 | for (; tokenopts2[x] && tokenopts2[x] != L','; x++); 930 | 931 | if (tokenopts2[x] == L',') x++; 932 | } 933 | 934 | // Find a process that has matching SIDs and LUIDs. 935 | HANDLE result = INVALID_HANDLE_VALUE; 936 | if (valid) 937 | { 938 | // Enable SeDebugPrivilege. 939 | SetThreadProcessPrivilege(L"SeDebugPrivilege", true); 940 | 941 | // Get the list of currently running processes. 942 | HANDLE snaphandle, tempproc, proctoken, duptoken; 943 | PTOKEN_USER user = NULL; 944 | PTOKEN_GROUPS groups = NULL; 945 | PTOKEN_PRIVILEGES privs = NULL; 946 | BOOL result2; 947 | 948 | snaphandle = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 949 | if (snaphandle == INVALID_HANDLE_VALUE) DumpErrorMsg("Unable to retrieve the list of running processes.", "create_toolhelp32_snapshot_failed", ::GetLastError()); 950 | else 951 | { 952 | PROCESSENTRY32 pe32; 953 | pe32.dwSize = sizeof(PROCESSENTRY32); 954 | 955 | if (::Process32First(snaphandle, &pe32)) 956 | { 957 | do 958 | { 959 | // Open a handle to the process. 960 | tempproc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID); 961 | if (tempproc == NULL) tempproc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID); 962 | 963 | if (tempproc != NULL) 964 | { 965 | result2 = ::OpenProcessToken(tempproc, accessmode, &proctoken); 966 | if (result2 == NULL && (accessmode & TOKEN_QUERY_SOURCE)) result2 = ::OpenProcessToken(tempproc, accessmode & ~TOKEN_QUERY_SOURCE, &proctoken); 967 | // DWORD pid = ::GetProcessId(tempproc); 968 | 969 | ::CloseHandle(tempproc); 970 | 971 | if (result2) 972 | { 973 | // Load token user, groups, and privileges. 974 | user = (PTOKEN_USER)AllocateAndGetTokenInformation(proctoken, TokenUser, 4096); 975 | groups = (PTOKEN_GROUPS)AllocateAndGetTokenInformation(proctoken, TokenGroups, 4096); 976 | privs = (PTOKEN_PRIVILEGES)AllocateAndGetTokenInformation(proctoken, TokenPrivileges, 4096); 977 | 978 | if (user != NULL && groups != NULL && privs != NULL) 979 | { 980 | // Match SIDs. 981 | for (x = 0; x < numsids; x++) 982 | { 983 | if (!::EqualSid(user->User.Sid, sids[x])) 984 | { 985 | for (x2 = 0; x2 < groups->GroupCount && !::EqualSid(groups->Groups[x2].Sid, sids[x]); x2++); 986 | 987 | if (x2 >= groups->GroupCount) break; 988 | } 989 | } 990 | 991 | if (x >= numsids) 992 | { 993 | // Match privileges. 994 | for (x = 0; x < numluids; x++) 995 | { 996 | for (x2 = 0; x2 < privs->PrivilegeCount && (privs->Privileges[x2].Luid.LowPart != luids[x].LowPart || privs->Privileges[x2].Luid.HighPart != luids[x].HighPart); x2++); 997 | 998 | if (x2 >= privs->PrivilegeCount) break; 999 | } 1000 | 1001 | if (x >= numluids) 1002 | { 1003 | // Everything matches. Duplicate the token. 1004 | if (accessmode & TOKEN_DUPLICATE) 1005 | { 1006 | SECURITY_ATTRIBUTES secattr = {0}; 1007 | secattr.nLength = sizeof(secattr); 1008 | secattr.bInheritHandle = FALSE; 1009 | secattr.lpSecurityDescriptor = NULL; 1010 | 1011 | if (::DuplicateTokenEx(proctoken, MAXIMUM_ALLOWED, &secattr, SecurityImpersonation, duptype, &duptoken)) 1012 | { 1013 | result = duptoken; 1014 | } 1015 | } 1016 | else 1017 | { 1018 | result = proctoken; 1019 | proctoken = NULL; 1020 | } 1021 | } 1022 | } 1023 | } 1024 | 1025 | FreeTokenInformation((LPVOID)privs); 1026 | FreeTokenInformation((LPVOID)groups); 1027 | FreeTokenInformation((LPVOID)user); 1028 | 1029 | if (proctoken != NULL) ::CloseHandle(proctoken); 1030 | } 1031 | } 1032 | } while (result == INVALID_HANDLE_VALUE && ::Process32Next(snaphandle, &pe32)); 1033 | } 1034 | 1035 | ::CloseHandle(snaphandle); 1036 | 1037 | if (result == INVALID_HANDLE_VALUE) DumpErrorMsg("Unable to find a matching process for the token options.", "no_match_found", ::GetLastError()); 1038 | } 1039 | } 1040 | 1041 | for (x = 0; x < numsids; x++) ::FreeSid(sids[x]); 1042 | 1043 | ::HeapFree(::GetProcessHeap(), 0, (LPVOID)sids); 1044 | ::HeapFree(::GetProcessHeap(), 0, (LPVOID)luids); 1045 | 1046 | return result; 1047 | } 1048 | 1049 | typedef enum _KERNEL_PROCESS_INFORMATION_CLASS { 1050 | NtProcessBasicInformation, 1051 | NtProcessQuotaLimits, 1052 | NtProcessIoCounters, 1053 | NtProcessVmCounters, 1054 | NtProcessTimes, 1055 | NtProcessBasePriority, 1056 | NtProcessRaisePriority, 1057 | NtProcessDebugPort, 1058 | NtProcessExceptionPort, 1059 | NtProcessAccessToken, 1060 | NtProcessLdtInformation, 1061 | NtProcessLdtSize, 1062 | NtProcessDefaultHardErrorMode, 1063 | NtProcessIoPortHandlers, 1064 | NtProcessPooledUsageAndLimits, 1065 | NtProcessWorkingSetWatch, 1066 | NtProcessUserModeIOPL, 1067 | NtProcessEnableAlignmentFaultFixup, 1068 | NtProcessPriorityClass, 1069 | NtProcessWx86Information, 1070 | NtProcessHandleCount, 1071 | NtProcessAffinityMask, 1072 | NtProcessPriorityBoost, 1073 | NtMaxProcessInfoClass 1074 | } KERNEL_PROCESS_INFORMATION_CLASS, *KERNEL_PPROCESS_INFORMATION_CLASS; 1075 | 1076 | typedef struct _KERNEL_PROCESS_ACCESS_TOKEN { 1077 | HANDLE Token; 1078 | HANDLE Thread; 1079 | } KERNEL_PROCESS_ACCESS_TOKEN, *KERNEL_PPROCESS_ACCESS_TOKEN; 1080 | 1081 | CubicleSoft::SharedLib::ModuleUtil GxNTDLL("ntdll.dll"); 1082 | CubicleSoft::SharedLib::FunctionUtil GxNtCreateToken(GxNTDLL, "NtCreateToken"); 1083 | CubicleSoft::SharedLib::FunctionUtil GxRtlNtStatusToDosError(GxNTDLL, "RtlNtStatusToDosError"); 1084 | CubicleSoft::SharedLib::FunctionUtil GxNtSetInformationProcess(GxNTDLL, "NtSetInformationProcess"); 1085 | 1086 | // Attempts to create a new token based on the input string. 1087 | HANDLE CreateTokenFromOpts(LPWSTR tokenopts, bool primary, HANDLE wildcardprocesshandle = ::GetCurrentProcess(), DWORD desiredaccess = TOKEN_ALL_ACCESS) 1088 | { 1089 | // Enable or obtain and then enable SeCreateTokenPrivilege. 1090 | if (!SetThreadProcessPrivilege(L"SeCreateTokenPrivilege", true)) 1091 | { 1092 | // Obtain an impersonation token containing the privilege. 1093 | HANDLE tokenhandle = FindExistingTokenFromOpts((LPWSTR)L"SeCreateTokenPrivilege", TokenImpersonation); 1094 | 1095 | if (tokenhandle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; 1096 | 1097 | // Assign the impersonation token to the current thread. 1098 | if (!::SetThreadToken(NULL, tokenhandle)) 1099 | { 1100 | DumpErrorMsg("Unable to assign the token to the thread.", "set_thread_token_failed", ::GetLastError()); 1101 | 1102 | return INVALID_HANDLE_VALUE; 1103 | } 1104 | 1105 | // Enable the privilege. 1106 | if (!SetThreadProcessPrivilege(L"SeCreateTokenPrivilege", true)) 1107 | { 1108 | DumpErrorMsg("Unable to enable SeCreateTokenPrivilege.", "enable_privilege_failed", ::GetLastError()); 1109 | 1110 | return INVALID_HANDLE_VALUE; 1111 | } 1112 | } 1113 | 1114 | // Split and convert the options. 1115 | CubicleSoft::StaticWCMixedVar TempVar; 1116 | WCHAR tempchr; 1117 | size_t x, x2, numsids, numluids; 1118 | bool valid = true, freeusersid = false, freegroupsids = false, freeownersid = false, freeprimarygroupsid = false; 1119 | HANDLE currtoken, result = INVALID_HANDLE_VALUE; 1120 | PTOKEN_USER user = NULL; 1121 | PTOKEN_GROUPS groups = NULL; 1122 | PTOKEN_PRIVILEGES privs = NULL; 1123 | PTOKEN_OWNER owner = NULL; 1124 | PTOKEN_PRIMARY_GROUP primarygroup = NULL; 1125 | PSECURITY_DESCRIPTOR sd = NULL; 1126 | PTOKEN_DEFAULT_DACL defaultdacl = NULL; 1127 | PTOKEN_SOURCE source = NULL; 1128 | 1129 | TempVar.SetStr(tokenopts); 1130 | WCHAR *tokenopts2 = TempVar.GetStr(); 1131 | 1132 | // For wildcard options (*), use the specified process token. 1133 | if (!::OpenProcessToken(wildcardprocesshandle, TOKEN_QUERY | TOKEN_QUERY_SOURCE, &currtoken) && !::OpenProcessToken(wildcardprocesshandle, TOKEN_QUERY, &currtoken)) 1134 | { 1135 | DumpErrorMsg("Unable to obtain a handle to the process token.", "open_process_token_failed", ::GetLastError()); 1136 | 1137 | return INVALID_HANDLE_VALUE; 1138 | } 1139 | 1140 | // Get the user SID. 1141 | x = 0; 1142 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1143 | 1144 | if (tokenopts2[x] == L'*') 1145 | { 1146 | user = (PTOKEN_USER)AllocateAndGetTokenInformation(currtoken, TokenUser, 4096); 1147 | if (user == NULL) 1148 | { 1149 | DumpErrorMsg("Unable to retrieve user for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1150 | 1151 | valid = false; 1152 | } 1153 | } 1154 | else 1155 | { 1156 | user = (PTOKEN_USER)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_USER)); 1157 | if (user != NULL) 1158 | { 1159 | valid = GetNextTokenOptsSID(tokenopts2, x, user->User.Sid); 1160 | if (valid) freeusersid = true; 1161 | 1162 | // Attributes are always ignored for the user and must be 0. 1163 | user->User.Attributes = 0; 1164 | } 1165 | } 1166 | 1167 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1168 | if (tokenopts2[x] == L';') x++; 1169 | 1170 | // Get group SIDs and attributes. 1171 | if (valid) 1172 | { 1173 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1174 | 1175 | if (tokenopts2[x] == L'*') 1176 | { 1177 | groups = (PTOKEN_GROUPS)AllocateAndGetTokenInformation(currtoken, TokenGroups, 4096); 1178 | if (groups == NULL) 1179 | { 1180 | DumpErrorMsg("Unable to retrieve group SIDs and attributes for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1181 | 1182 | valid = false; 1183 | } 1184 | } 1185 | else 1186 | { 1187 | GetNumSIDsAndLUIDs(tokenopts2 + x, numsids, numluids); 1188 | 1189 | groups = (PTOKEN_GROUPS)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_GROUPS) + sizeof(SID_AND_ATTRIBUTES) * numsids); 1190 | if (groups != NULL) 1191 | { 1192 | freegroupsids = true; 1193 | 1194 | groups->GroupCount = (DWORD)numsids; 1195 | 1196 | for (x2 = 0; valid && x2 < numsids; x2++) 1197 | { 1198 | valid = GetNextTokenOptsSID(tokenopts2, x, groups->Groups[x2].Sid); 1199 | 1200 | groups->Groups[x2].Attributes = GetNextTokenOptsAttrs(tokenopts2, x); 1201 | 1202 | if (tokenopts2[x] == L',') x++; 1203 | } 1204 | 1205 | if (!valid) numsids = x2; 1206 | } 1207 | } 1208 | 1209 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1210 | if (tokenopts2[x] == L';') x++; 1211 | } 1212 | 1213 | // Get privileges and attributes. 1214 | if (valid) 1215 | { 1216 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1217 | 1218 | if (tokenopts2[x] == L'*') 1219 | { 1220 | privs = (PTOKEN_PRIVILEGES)AllocateAndGetTokenInformation(currtoken, TokenPrivileges, 4096); 1221 | if (privs == NULL) 1222 | { 1223 | DumpErrorMsg("Unable to retrieve privileges and attributes for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1224 | 1225 | valid = false; 1226 | } 1227 | } 1228 | else 1229 | { 1230 | GetNumSIDsAndLUIDs(tokenopts2 + x, numsids, numluids); 1231 | 1232 | privs = (PTOKEN_PRIVILEGES)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES) * numluids); 1233 | if (privs != NULL) 1234 | { 1235 | privs->PrivilegeCount = (DWORD)numluids; 1236 | 1237 | for (x2 = 0; valid && x2 < numluids; x2++) 1238 | { 1239 | valid = GetNextTokenOptsLUID(tokenopts2, x, privs->Privileges[x2].Luid); 1240 | 1241 | privs->Privileges[x2].Attributes = GetNextTokenOptsAttrs(tokenopts2, x); 1242 | 1243 | if (tokenopts2[x] == L',') x++; 1244 | } 1245 | 1246 | if (!valid) numluids = x2; 1247 | } 1248 | } 1249 | 1250 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1251 | if (tokenopts2[x] == L';') x++; 1252 | } 1253 | 1254 | // Get the owner SID. 1255 | if (valid) 1256 | { 1257 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1258 | 1259 | if (tokenopts2[x] == L'*') 1260 | { 1261 | owner = (PTOKEN_OWNER)AllocateAndGetTokenInformation(currtoken, TokenOwner, 4096); 1262 | if (owner == NULL) 1263 | { 1264 | DumpErrorMsg("Unable to retrieve the owner for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1265 | 1266 | valid = false; 1267 | } 1268 | } 1269 | else 1270 | { 1271 | owner = (PTOKEN_OWNER)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_OWNER)); 1272 | if (owner != NULL) 1273 | { 1274 | valid = GetNextTokenOptsSID(tokenopts2, x, owner->Owner); 1275 | if (valid) freeownersid = true; 1276 | } 1277 | } 1278 | 1279 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1280 | if (tokenopts2[x] == L';') x++; 1281 | } 1282 | 1283 | // Get the primary group SID. 1284 | if (valid) 1285 | { 1286 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1287 | 1288 | if (tokenopts2[x] == L'*') 1289 | { 1290 | primarygroup = (PTOKEN_PRIMARY_GROUP)AllocateAndGetTokenInformation(currtoken, TokenPrimaryGroup, 4096); 1291 | if (primarygroup == NULL) 1292 | { 1293 | DumpErrorMsg("Unable to retrieve the primary group for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1294 | 1295 | valid = false; 1296 | } 1297 | } 1298 | else 1299 | { 1300 | primarygroup = (PTOKEN_PRIMARY_GROUP)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_PRIMARY_GROUP)); 1301 | if (primarygroup != NULL) 1302 | { 1303 | valid = GetNextTokenOptsSID(tokenopts2, x, primarygroup->PrimaryGroup); 1304 | if (valid) freeprimarygroupsid = true; 1305 | } 1306 | } 1307 | 1308 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1309 | if (tokenopts2[x] == L';') x++; 1310 | } 1311 | 1312 | // Get the default DACL. 1313 | if (valid) 1314 | { 1315 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1316 | 1317 | if (tokenopts2[x] == L'*') 1318 | { 1319 | defaultdacl = (PTOKEN_DEFAULT_DACL)AllocateAndGetTokenInformation(currtoken, TokenDefaultDacl, 4096); 1320 | if (defaultdacl == NULL) 1321 | { 1322 | DumpErrorMsg("Unable to retrieve the default DACL for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1323 | 1324 | valid = false; 1325 | } 1326 | } 1327 | else 1328 | { 1329 | for (x2 = x; tokenopts2[x2] && tokenopts2[x2] != L';'; x2++) 1330 | { 1331 | if (tokenopts2[x2] == L'(') 1332 | { 1333 | for (; tokenopts2[x2] && tokenopts2[x2] != L')'; x2++); 1334 | } 1335 | } 1336 | tempchr = tokenopts2[x2]; 1337 | tokenopts2[x2] = L'\0'; 1338 | 1339 | defaultdacl = (PTOKEN_DEFAULT_DACL)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_DEFAULT_DACL)); 1340 | if (defaultdacl != NULL) 1341 | { 1342 | BOOL daclpresent, dacldefaulted; 1343 | 1344 | valid = ::ConvertStringSecurityDescriptorToSecurityDescriptorW(tokenopts2 + x, SDDL_REVISION_1, &sd, NULL) && ::GetSecurityDescriptorDacl(sd, &daclpresent, &defaultdacl->DefaultDacl, &dacldefaulted) && daclpresent; 1345 | if (!valid) DumpErrorMsg("The specified default DACL in the token options is invalid.", "invalid_default_dacl", ::GetLastError()); 1346 | } 1347 | 1348 | tokenopts2[x2] = tempchr; 1349 | x = x2; 1350 | } 1351 | 1352 | for (; tokenopts2[x] && tokenopts2[x] != L';'; x++); 1353 | if (tokenopts2[x] == L';') x++; 1354 | } 1355 | 1356 | // Get the source and source LUID. 1357 | if (valid) 1358 | { 1359 | for (; tokenopts2[x] && tokenopts2[x] == L' '; x++); 1360 | 1361 | if (tokenopts2[x] == L'*') 1362 | { 1363 | source = (PTOKEN_SOURCE)AllocateAndGetTokenInformation(currtoken, TokenSource, sizeof(TOKEN_SOURCE)); 1364 | if (source == NULL) 1365 | { 1366 | DumpErrorMsg("Unable to retrieve the source for the current token.", "allocate_and_get_token_info_failed", ::GetLastError()); 1367 | 1368 | valid = false; 1369 | } 1370 | } 1371 | else 1372 | { 1373 | for (x2 = x; tokenopts2[x2] && x2 - x != TOKEN_SOURCE_LENGTH * 2; x2++); 1374 | 1375 | if (tokenopts2[x2] != L':') DumpErrorMsg("The specified source in the token options is invalid.", "invalid_source", ::GetLastError()); 1376 | else 1377 | { 1378 | source = (PTOKEN_SOURCE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_SOURCE)); 1379 | 1380 | // Convert from hex. 1381 | for (x2 = x; tokenopts2[x2] && x2 - x != TOKEN_SOURCE_LENGTH * 2; x2 += 2) 1382 | { 1383 | if (tokenopts2[x2] >= L'0' && tokenopts2[x2] <= L'9') tempchr = tokenopts2[x2] - L'0'; 1384 | else if (tokenopts2[x2] >= L'A' && tokenopts2[x2] <= L'F') tempchr = tokenopts2[x2] - L'A'; 1385 | else if (tokenopts2[x2] >= L'a' && tokenopts2[x2] <= L'f') tempchr = tokenopts2[x2] - L'a'; 1386 | else tempchr = 0; 1387 | 1388 | tempchr <<= 4; 1389 | 1390 | if (tokenopts2[x2 + 1] >= L'0' && tokenopts2[x2 + 1] <= L'9') tempchr |= tokenopts2[x2 + 1] - L'0'; 1391 | else if (tokenopts2[x2 + 1] >= L'A' && tokenopts2[x2 + 1] <= L'F') tempchr |= tokenopts2[x2 + 1] - L'A'; 1392 | else if (tokenopts2[x2 + 1] >= L'a' && tokenopts2[x2 + 1] <= L'f') tempchr |= tokenopts2[x2 + 1] - L'a'; 1393 | 1394 | source->SourceName[(x2 - x) / 2] = (CHAR)tempchr; 1395 | } 1396 | 1397 | std::uint64_t tempid = _wtoi64(tokenopts2 + x2 + 1); 1398 | 1399 | source->SourceIdentifier.HighPart = tempid >> 32; 1400 | source->SourceIdentifier.LowPart = tempid & 0xFFFFFFFF; 1401 | } 1402 | } 1403 | } 1404 | 1405 | // Create the token. 1406 | if (valid && user != NULL && groups != NULL && privs != NULL && owner != NULL && primarygroup != NULL && defaultdacl != NULL && source != NULL) 1407 | { 1408 | NTSTATUS status; 1409 | HANDLE temphandle; 1410 | TOKEN_STATISTICS tokenstats; 1411 | DWORD templen; 1412 | DWORD winerror; 1413 | 1414 | if (!::GetTokenInformation(currtoken, TokenStatistics, &tokenstats, sizeof(tokenstats), &templen)) DumpErrorMsg("Unable to get statistics for the current token.", "get_token_info_failed", ::GetLastError()); 1415 | else 1416 | { 1417 | SECURITY_QUALITY_OF_SERVICE tempsqos = { 1418 | sizeof(tempsqos), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING 1419 | }; 1420 | 1421 | OBJECT_ATTRIBUTES tempoa = { 1422 | sizeof(tempoa), 0, 0, 0, 0, &tempsqos 1423 | }; 1424 | 1425 | if (CubicleSoft::SharedLib::Stdcall(GxNtCreateToken, status, &temphandle, desiredaccess, &tempoa, (primary ? TokenPrimary : TokenImpersonation), &tokenstats.AuthenticationId, &tokenstats.ExpirationTime, user, groups, privs, owner, primarygroup, defaultdacl, source) && status >= 0) 1426 | { 1427 | result = temphandle; 1428 | } 1429 | else 1430 | { 1431 | if (CubicleSoft::SharedLib::Stdcall(GxRtlNtStatusToDosError, winerror, status) && winerror != ERROR_MR_MID_NOT_FOUND) DumpErrorMsg("Failed to create a token.", "nt_create_token_failed", winerror); 1432 | else DumpErrorMsg("Failed to create a token and failed to retrieve a Windows error code mapping.", "nt_create_token_failed", status); 1433 | } 1434 | } 1435 | } 1436 | 1437 | ::CloseHandle(currtoken); 1438 | 1439 | // Cleanup. 1440 | if (user != NULL) 1441 | { 1442 | if (freeusersid) ::FreeSid(user->User.Sid); 1443 | 1444 | FreeTokenInformation((LPVOID)user); 1445 | } 1446 | 1447 | if (groups != NULL) 1448 | { 1449 | for (x = 0; x < numsids; x++) ::FreeSid(groups->Groups[x].Sid); 1450 | 1451 | FreeTokenInformation((LPVOID)groups); 1452 | } 1453 | 1454 | if (privs != NULL) FreeTokenInformation((LPVOID)privs); 1455 | 1456 | if (owner != NULL) 1457 | { 1458 | if (freeownersid) ::FreeSid(owner->Owner); 1459 | 1460 | FreeTokenInformation((LPVOID)owner); 1461 | } 1462 | 1463 | if (primarygroup != NULL) 1464 | { 1465 | if (freeprimarygroupsid) ::FreeSid(primarygroup->PrimaryGroup); 1466 | 1467 | FreeTokenInformation((LPVOID)primarygroup); 1468 | } 1469 | 1470 | if (sd != NULL) ::LocalFree(sd); 1471 | if (defaultdacl != NULL) FreeTokenInformation((LPVOID)defaultdacl); 1472 | if (source != NULL) FreeTokenInformation((LPVOID)source); 1473 | 1474 | return result; 1475 | } 1476 | 1477 | 1478 | // Determines whether or not the current process is elevated. 1479 | int IsElevatedProcess() 1480 | { 1481 | HANDLE temptoken; 1482 | PTOKEN_ELEVATION teinfo; 1483 | int result = -1; 1484 | 1485 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &temptoken)) DumpErrorMsg("Unable to open a handle to the process token.", "open_process_token_failed", ::GetLastError()); 1486 | else 1487 | { 1488 | teinfo = (PTOKEN_ELEVATION)AllocateAndGetTokenInformation(temptoken, TokenElevation, sizeof(TOKEN_ELEVATION)); 1489 | 1490 | if (teinfo == NULL) DumpErrorMsg("Unable to get elevation status.", "get_token_information_failed", ::GetLastError()); 1491 | else 1492 | { 1493 | result = (int)(teinfo->TokenIsElevated ? 1 : 0); 1494 | 1495 | FreeTokenInformation((LPVOID)teinfo); 1496 | } 1497 | 1498 | ::CloseHandle(temptoken); 1499 | } 1500 | 1501 | return result; 1502 | } 1503 | 1504 | // Determines whether or not the current process is running as SYSTEM. 1505 | int IsSystemProcess() 1506 | { 1507 | HANDLE temptoken; 1508 | PTOKEN_USER tuinfo = NULL; 1509 | PSID systemsid; 1510 | int result = -1; 1511 | 1512 | // NT AUTHORITY\SYSTEM 1513 | ::ConvertStringSidToSid(_T("S-1-5-18"), &systemsid); 1514 | 1515 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &temptoken)) DumpErrorMsg("Unable to open a handle to the process token.", "open_process_token_failed", ::GetLastError()); 1516 | else 1517 | { 1518 | tuinfo = (PTOKEN_USER)AllocateAndGetTokenInformation(temptoken, TokenUser, 4096); 1519 | 1520 | if (tuinfo == NULL) DumpErrorMsg("Unable to get user token for the process.", "get_token_information_failed", ::GetLastError()); 1521 | else 1522 | { 1523 | result = (::EqualSid(systemsid, tuinfo->User.Sid) ? 1 : 0); 1524 | 1525 | FreeTokenInformation((LPVOID)tuinfo); 1526 | } 1527 | 1528 | ::CloseHandle(temptoken); 1529 | } 1530 | 1531 | ::FreeSid(systemsid); 1532 | 1533 | return result; 1534 | } 1535 | 1536 | 1537 | // Attempts to self-elevate. 1538 | bool ElevateSelf(bool createservice) 1539 | { 1540 | // Retrieve the full EXE path and filename for this process. 1541 | WCHAR procfilename[8192], procdir[8192]; 1542 | CubicleSoft::StaticWCMixedVar TempVar; 1543 | size_t y = (size_t)::GetModuleFileNameW(NULL, procfilename, sizeof(procfilename) / sizeof(WCHAR)); 1544 | if (y >= sizeof(procfilename) / sizeof(WCHAR) - 50) 1545 | { 1546 | DumpErrorMsg("Unable to retrieve EXE path and filename.", "get_module_filename_failed", ::GetLastError()); 1547 | 1548 | return false; 1549 | } 1550 | procfilename[y] = L'\0'; 1551 | 1552 | // Remove Zone.Identifier. 1553 | if (y < TempVar.GetMaxSize() - 18) 1554 | { 1555 | TempVar.SetStr(procfilename); 1556 | TempVar.AppendStr(L":Zone.Identifier"); 1557 | ::DeleteFileW(TempVar.GetStr()); 1558 | } 1559 | 1560 | memcpy(procdir, procfilename, (y + 1) * sizeof(WCHAR)); 1561 | while (y && procdir[y - 1] != L'\\' && procdir[y - 1] != L'/') y--; 1562 | procdir[y] = L'\0'; 1563 | 1564 | // If this process has a console, make the new process connect to the console. 1565 | if (::GetConsoleWindow() == NULL) TempVar.SetStr(L""); 1566 | else 1567 | { 1568 | TempVar.SetStr(L"/attach="); 1569 | TempVar.AppendUInt(::GetCurrentProcessId()); 1570 | TempVar.AppendChar(L' '); 1571 | } 1572 | 1573 | // The new process will either start a system service or run elevated. 1574 | if (createservice) 1575 | { 1576 | TempVar.AppendStr(L"/connectpipe="); 1577 | TempVar.AppendUInt(::GetCurrentProcessId()); 1578 | TempVar.AppendStr(L" /createservice"); 1579 | } 1580 | else 1581 | { 1582 | TempVar.AppendStr(L"/runelevated="); 1583 | TempVar.AppendUInt(::GetCurrentProcessId()); 1584 | } 1585 | 1586 | // Trigger the elevation prompt. 1587 | SHELLEXECUTEINFOW seinfo = {0}; 1588 | 1589 | seinfo.cbSize = sizeof(seinfo); 1590 | seinfo.fMask = 0; 1591 | seinfo.hwnd = NULL; 1592 | seinfo.lpVerb = L"runas"; 1593 | seinfo.lpFile = (LPWSTR)procfilename; 1594 | seinfo.lpParameters = (LPWSTR)TempVar.GetStr(); 1595 | seinfo.lpDirectory = (LPWSTR)procdir; 1596 | seinfo.nShow = SW_NORMAL; 1597 | 1598 | // ShellExecuteExW() does not return until the elevation prompt is handled. 1599 | if (!::ShellExecuteExW(&seinfo)) 1600 | { 1601 | DumpErrorMsg("Unable to create elevated process.", "shell_execute_ex_failed", ::GetLastError()); 1602 | 1603 | return false; 1604 | } 1605 | 1606 | return true; 1607 | } 1608 | 1609 | // Attempts to start a system service. 1610 | bool SystemSelf(DWORD connectpid) 1611 | { 1612 | // Retrieve the full EXE path and filename for this process. 1613 | WCHAR procfilename[8192]; 1614 | size_t y = (size_t)::GetModuleFileNameW(NULL, procfilename, sizeof(procfilename) / sizeof(WCHAR)); 1615 | if (y >= sizeof(procfilename) / sizeof(WCHAR) - 1) 1616 | { 1617 | DumpErrorMsg("Unable to retrieve EXE path and filename.", "get_module_filename_failed", ::GetLastError()); 1618 | 1619 | return false; 1620 | } 1621 | procfilename[y] = L'\0'; 1622 | 1623 | // Prepare an event synchronization object. 1624 | CubicleSoft::StaticWCMixedVar TempVar; 1625 | HANDLE tempevent; 1626 | DWORD pid = ::GetCurrentProcessId(); 1627 | 1628 | TempVar.SetStr(L"Global\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_SrvEv_"); 1629 | TempVar.AppendUInt(pid); 1630 | 1631 | tempevent = ::CreateEventW(NULL, FALSE, FALSE, TempVar.GetStr()); 1632 | if (tempevent == NULL) 1633 | { 1634 | DumpErrorMsg("Unable to create service notification event object.", "create_event_failed", ::GetLastError()); 1635 | 1636 | return false; 1637 | } 1638 | 1639 | // Get a handle to the service control manager. 1640 | SC_HANDLE scmhandle = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SERVICE_START | DELETE); 1641 | if (scmhandle == NULL) 1642 | { 1643 | DumpErrorMsg("Unable to open a handle to the service control manager.", "open_sc_manager_failed", ::GetLastError()); 1644 | 1645 | ::CloseHandle(tempevent); 1646 | 1647 | return false; 1648 | } 1649 | 1650 | // Create the service. 1651 | CubicleSoft::StaticWCMixedVar TempVar2; 1652 | 1653 | TempVar.SetStr(L"z_createprocess-"); 1654 | TempVar.AppendUInt(pid); 1655 | 1656 | TempVar2.SetStr(L"\""); 1657 | TempVar2.AppendStr(procfilename); 1658 | TempVar2.AppendStr(L"\" /connectpipe="); 1659 | TempVar2.AppendUInt(connectpid); 1660 | TempVar2.AppendStr(L" /runservice="); 1661 | TempVar2.AppendUInt(pid); 1662 | 1663 | SC_HANDLE servicehandle = ::CreateServiceW(scmhandle, TempVar.GetStr(), TempVar.GetStr(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, TempVar2.GetStr(), NULL, NULL, NULL, NULL, NULL); 1664 | if (servicehandle == NULL) 1665 | { 1666 | DumpErrorMsg("Unable to create the system service.", "create_service_failed", ::GetLastError()); 1667 | 1668 | ::CloseServiceHandle(scmhandle); 1669 | 1670 | ::CloseHandle(tempevent); 1671 | 1672 | return false; 1673 | } 1674 | 1675 | // Start the system service. 1676 | ::StartService(servicehandle, 0, NULL); 1677 | 1678 | // Wait for up to 15 seconds for the process to fire the event. 1679 | #ifdef _DEBUG 1680 | if (::WaitForSingleObject(tempevent, INFINITE) != WAIT_OBJECT_0) 1681 | #else 1682 | if (::WaitForSingleObject(tempevent, 15000) != WAIT_OBJECT_0) 1683 | #endif 1684 | { 1685 | DumpErrorMsg("System service process did not respond within 15 seconds.", "timeout", ::GetLastError()); 1686 | 1687 | ::DeleteService(servicehandle); 1688 | 1689 | ::CloseServiceHandle(servicehandle); 1690 | ::CloseServiceHandle(scmhandle); 1691 | 1692 | ::CloseHandle(tempevent); 1693 | 1694 | return false; 1695 | } 1696 | 1697 | ::DeleteService(servicehandle); 1698 | 1699 | ::CloseServiceHandle(servicehandle); 1700 | ::CloseServiceHandle(scmhandle); 1701 | 1702 | ::CloseHandle(tempevent); 1703 | 1704 | return true; 1705 | } 1706 | 1707 | 1708 | struct SidInfo { 1709 | PSID MxSid; 1710 | TCHAR MxDomainName[1024]; 1711 | TCHAR MxAccountName[1024]; 1712 | SID_NAME_USE MxSidType; 1713 | }; 1714 | 1715 | // Enable SeBackupPrivilege, SeRestorePrivilege, SeIncreaseQuotaPrivilege, and SeAssignPrimaryTokenPrivilege on the current process token. 1716 | // Only the SYSTEM user has all of these by default. 1717 | bool EnableSystemPrivileges() 1718 | { 1719 | if (!::SetThreadProcessPrivilege(L"SeBackupPrivilege", true)) 1720 | { 1721 | DumpErrorMsg("Failed to enable SeBackupPrivilege.", "enable_privilege_failed", ::GetLastError()); 1722 | 1723 | return false; 1724 | } 1725 | 1726 | if (!::SetThreadProcessPrivilege(L"SeRestorePrivilege", true)) 1727 | { 1728 | DumpErrorMsg("Failed to enable SeRestorePrivilege.", "enable_privilege_failed", ::GetLastError()); 1729 | 1730 | return false; 1731 | } 1732 | 1733 | if (!::SetThreadProcessPrivilege(L"SeIncreaseQuotaPrivilege", true)) 1734 | { 1735 | DumpErrorMsg("Failed to enable SeIncreaseQuotaPrivilege.", "enable_privilege_failed", ::GetLastError()); 1736 | 1737 | return false; 1738 | } 1739 | 1740 | if (!::SetThreadProcessPrivilege(L"SeAssignPrimaryTokenPrivilege", true)) 1741 | { 1742 | DumpErrorMsg("Failed to enable SeAssignPrimaryTokenPrivilege.", "enable_privilege_failed", ::GetLastError()); 1743 | 1744 | return false; 1745 | } 1746 | 1747 | return true; 1748 | } 1749 | 1750 | // Loads the user profile associated with the token. 1751 | // Note that some user profiles are roaming, so call ::NetUserGetInfo() to get the correct profile path. 1752 | bool LoadRealUserProfile(HANDLE maintoken, PROFILEINFO &profile) 1753 | { 1754 | ::ZeroMemory(&profile, sizeof(profile)); 1755 | 1756 | PTOKEN_USER tuinfo = (PTOKEN_USER)AllocateAndGetTokenInformation(maintoken, TokenUser, 4096); 1757 | if (tuinfo == NULL) 1758 | { 1759 | DumpErrorMsg("User token was retrieved/created but retrieving user information failed.", "get_token_information_failed", ::GetLastError()); 1760 | 1761 | return false; 1762 | } 1763 | 1764 | SidInfo TempSidInfo; 1765 | DWORD acctbuffersize, domainbuffersize; 1766 | NET_API_STATUS netstatus; 1767 | LPUSER_INFO_4 userinfo4 = NULL; 1768 | 1769 | TempSidInfo.MxSid = tuinfo->User.Sid; 1770 | 1771 | acctbuffersize = sizeof(TempSidInfo.MxAccountName) / sizeof(TCHAR); 1772 | domainbuffersize = sizeof(TempSidInfo.MxDomainName) / sizeof(TCHAR); 1773 | 1774 | if (!::LookupAccountSid(NULL, TempSidInfo.MxSid, TempSidInfo.MxAccountName, &acctbuffersize, TempSidInfo.MxDomainName, &domainbuffersize, &TempSidInfo.MxSidType)) 1775 | { 1776 | DumpErrorMsg("User token was retrieved/created but retrieving SID information failed.", "lookup_account_sid_failed", ::GetLastError()); 1777 | 1778 | FreeTokenInformation((LPVOID)tuinfo); 1779 | 1780 | return false; 1781 | } 1782 | 1783 | FreeTokenInformation((LPVOID)tuinfo); 1784 | 1785 | profile.dwSize = sizeof(profile); 1786 | profile.lpUserName = TempSidInfo.MxAccountName; 1787 | 1788 | if (TempSidInfo.MxSidType == SidTypeUser || TempSidInfo.MxSidType == SidTypeDeletedAccount) 1789 | { 1790 | netstatus = ::NetUserGetInfo(TempSidInfo.MxDomainName, TempSidInfo.MxAccountName, 4, (LPBYTE *)&userinfo4); 1791 | 1792 | if (netstatus == NERR_Success && userinfo4 != NULL) profile.lpProfilePath = userinfo4->usri4_profile; 1793 | } 1794 | 1795 | // LoadUserProfile() requires SeBackupPrivilege and SeRestorePrivilege and also running as a system service. 1796 | if (!::LoadUserProfile(maintoken, &profile)) 1797 | { 1798 | DumpErrorMsg("Loading the user profile failed.", "load_user_profile_failed", ::GetLastError()); 1799 | 1800 | if (userinfo4 != NULL) ::NetApiBufferFree(userinfo4); 1801 | 1802 | return false; 1803 | } 1804 | 1805 | if (userinfo4 != NULL) ::NetApiBufferFree(userinfo4); 1806 | 1807 | return true; 1808 | } 1809 | 1810 | // Assigns the primary token to the process. 1811 | // The process must have just been created as a suspended process for this to work. 1812 | // The undocumented NtSetInformationProcess() API supposedly requires the SeAssignPrimaryTokenPrivilege (preferably) or possibly SeDebugPrivilege. 1813 | bool AssignPrimaryTokenToProcess(HANDLE prochandle, HANDLE maintoken) 1814 | { 1815 | KERNEL_PROCESS_ACCESS_TOKEN pat; 1816 | NTSTATUS status; 1817 | DWORD winerror; 1818 | 1819 | pat.Token = maintoken; 1820 | pat.Thread = (HANDLE)0; 1821 | 1822 | if (!CubicleSoft::SharedLib::Stdcall(GxNtSetInformationProcess, status, prochandle, NtProcessAccessToken, &pat, sizeof(pat)) || status < 0) 1823 | { 1824 | if (CubicleSoft::SharedLib::Stdcall(GxRtlNtStatusToDosError, winerror, status) && winerror != ERROR_MR_MID_NOT_FOUND) DumpErrorMsg("Failed to assign the primary token to the target process.", "nt_set_information_process_failed", winerror); 1825 | else DumpErrorMsg("Failed to assign the primary token to the target process and failed to retrieve a Windows error code mapping.", "nt_set_information_process_failed", status); 1826 | 1827 | return false; 1828 | } 1829 | 1830 | return true; 1831 | } 1832 | 1833 | 1834 | struct ServiceInitState { 1835 | SERVICE_STATUS MxServiceStatus; 1836 | SERVICE_STATUS_HANDLE MxStatusHandle; 1837 | DWORD MxCreatorPID; 1838 | LPTSTR MxServiceName; 1839 | DWORD MxExitCode; 1840 | }; 1841 | 1842 | ServiceInitState GxService; 1843 | 1844 | // Notify the elevated process that started this service that it can clean up and terminate. 1845 | bool NotifyServiceCreatorProcess() 1846 | { 1847 | if (!GxService.MxCreatorPID) return true; 1848 | 1849 | CubicleSoft::StaticWCMixedVar TempVar; 1850 | HANDLE tempevent; 1851 | 1852 | TempVar.SetStr(L"Global\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_SrvEv_"); 1853 | TempVar.AppendUInt(GxService.MxCreatorPID); 1854 | 1855 | GxService.MxCreatorPID = 0; 1856 | 1857 | tempevent = ::CreateEventW(NULL, FALSE, FALSE, TempVar.GetStr()); 1858 | if (tempevent == NULL) 1859 | { 1860 | DumpErrorMsg("Unable to create service notification event object.", "create_event_failed", ::GetLastError()); 1861 | 1862 | return false; 1863 | } 1864 | 1865 | ::SetEvent(tempevent); 1866 | 1867 | ::CloseHandle(tempevent); 1868 | 1869 | return true; 1870 | } 1871 | 1872 | VOID WINAPI ServiceCtrlHandler(DWORD CtrlType) 1873 | { 1874 | if (CtrlType == SERVICE_CONTROL_STOP && GxService.MxServiceStatus.dwCurrentState == SERVICE_RUNNING) 1875 | { 1876 | // Let the service control manager know that the service will be stopping shortly. 1877 | GxService.MxServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 1878 | GxService.MxServiceStatus.dwControlsAccepted = 0; 1879 | GxService.MxServiceStatus.dwWin32ExitCode = NO_ERROR; 1880 | GxService.MxServiceStatus.dwCheckPoint = 1; 1881 | ::SetServiceStatus(GxService.MxStatusHandle, &GxService.MxServiceStatus); 1882 | } 1883 | } 1884 | 1885 | bool ServiceMainInternal() 1886 | { 1887 | if (!ConnectToMainCommPipe()) 1888 | { 1889 | DumpErrorMsg("Unable to connect to the main communication pipe.", "connect_to_main_comm_pipe_failed", ::GetLastError()); 1890 | 1891 | return false; 1892 | } 1893 | 1894 | // Enable necessary privileges. 1895 | if (!EnableSystemPrivileges()) return false; 1896 | 1897 | // Notify that all is well so far. 1898 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 1899 | 1900 | // Receive the token type. 1901 | char assigntoken; 1902 | if (!ReadFileExact(GxMainCommPipeClient, &assigntoken, 1)) 1903 | { 1904 | DumpErrorMsg("Unable to read token type from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 1905 | 1906 | return false; 1907 | } 1908 | 1909 | // Load the primary token. Requires SeDebugPrivilege. 1910 | // The undocumented NtCreateToken() API requires SeCreateTokenPrivilege. 1911 | HANDLE maintoken; 1912 | if (assigntoken == 0) maintoken = GetTokenFromPID(::GetCurrentProcessId(), TokenPrimary); 1913 | else if (assigntoken == 1) maintoken = GetTokenFromPID(GxService.MxCreatorPID, TokenPrimary); 1914 | else if (assigntoken == 2 || assigntoken == 3) 1915 | { 1916 | // Read token options. 1917 | DWORD tempsize; 1918 | LPWSTR tokenopts = NULL; 1919 | 1920 | if (!ReadCommPipeString(GxMainCommPipeClient, tokenopts, tempsize, 1)) return false; 1921 | 1922 | if (assigntoken == 2) maintoken = FindExistingTokenFromOpts(tokenopts, TokenPrimary); 1923 | else maintoken = CreateTokenFromOpts(tokenopts, true); 1924 | 1925 | FreeCommPipeString(tokenopts); 1926 | } 1927 | else 1928 | { 1929 | DumpErrorMsg("Invalid token type sent on the main communication pipe.", "invalid_token_type", ::GetLastError()); 1930 | 1931 | return false; 1932 | } 1933 | 1934 | if (maintoken == INVALID_HANDLE_VALUE) return false; 1935 | 1936 | // Notify that this process is done with the creator process and can remove the NT service and continue. 1937 | if (!NotifyServiceCreatorProcess()) return false; 1938 | 1939 | // Load the user profile associated with the token. 1940 | PROFILEINFO tempprofile = {0}; 1941 | if (!LoadRealUserProfile(maintoken, tempprofile)) 1942 | { 1943 | ::CloseHandle(maintoken); 1944 | 1945 | return false; 1946 | } 1947 | 1948 | // Generate an environment block for the user. 1949 | LPVOID tempenv = NULL; 1950 | 1951 | if (!::CreateEnvironmentBlock(&tempenv, maintoken, FALSE)) 1952 | { 1953 | DumpErrorMsg("Creating the environment block for the user token failed.", "create_environment_block_failed", ::GetLastError()); 1954 | 1955 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 1956 | 1957 | ::CloseHandle(maintoken); 1958 | 1959 | return false; 1960 | } 1961 | 1962 | // Notify that all is well so far. 1963 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 1964 | 1965 | // Send the environment block. 1966 | WCHAR *envp = (WCHAR *)tempenv; 1967 | CubicleSoft::StaticWCMixedVar TempVar; 1968 | DWORD tempsize = sizeof(WCHAR); 1969 | int x = 0; 1970 | while (envp[x]) 1971 | { 1972 | TempVar.SetStr(envp + x); 1973 | tempsize += (TempVar.GetSize() + 1) * sizeof(WCHAR); 1974 | 1975 | x += TempVar.GetSize() + 1; 1976 | } 1977 | 1978 | ::WriteFile(GxMainCommPipeClient, &tempsize, 4, NULL, NULL); 1979 | x = 0; 1980 | while (envp[x]) 1981 | { 1982 | TempVar.SetStr(envp + x); 1983 | tempsize = (TempVar.GetSize() + 1) * sizeof(WCHAR); 1984 | ::WriteFile(GxMainCommPipeClient, TempVar.GetStr(), tempsize, NULL, NULL); 1985 | 1986 | x += TempVar.GetSize() + 1; 1987 | } 1988 | 1989 | ::WriteFile(GxMainCommPipeClient, L"\0", sizeof(WCHAR), NULL, NULL); 1990 | 1991 | ::DestroyEnvironmentBlock(tempenv); 1992 | 1993 | // Receive the process ID to modify. 1994 | DWORD pid; 1995 | if (!ReadFileExact(GxMainCommPipeClient, &pid, sizeof(DWORD))) 1996 | { 1997 | DumpErrorMsg("Unable to read process ID from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 1998 | 1999 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 2000 | 2001 | ::CloseHandle(maintoken); 2002 | 2003 | return false; 2004 | } 2005 | 2006 | // Open the process specified by the process ID. 2007 | HANDLE tempproc = ::OpenProcess(SYNCHRONIZE | PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION, FALSE, pid); 2008 | if (tempproc == NULL) tempproc = ::OpenProcess(SYNCHRONIZE | PROCESS_SET_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); 2009 | 2010 | if (tempproc == NULL) 2011 | { 2012 | DumpErrorMsg("Unable to open the specified process.", "open_process_failed", ::GetLastError()); 2013 | 2014 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 2015 | 2016 | ::CloseHandle(maintoken); 2017 | 2018 | return false; 2019 | } 2020 | 2021 | // Assign the primary token to the process. 2022 | if (!AssignPrimaryTokenToProcess(tempproc, maintoken)) 2023 | { 2024 | ::CloseHandle(tempproc); 2025 | 2026 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 2027 | 2028 | ::CloseHandle(maintoken); 2029 | 2030 | return false; 2031 | } 2032 | 2033 | // Notify that all is well so far. 2034 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 2035 | 2036 | // Wait for the process to terminate. 2037 | ::WaitForSingleObject(tempproc, INFINITE); 2038 | 2039 | ::CloseHandle(tempproc); 2040 | 2041 | DisconnectMainCommPipe(); 2042 | 2043 | // Unload the user profile. 2044 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 2045 | 2046 | ::CloseHandle(maintoken); 2047 | 2048 | return true; 2049 | } 2050 | 2051 | VOID WINAPI ServiceMain(DWORD argc, TCHAR **argv) 2052 | { 2053 | // Register the service control handler. 2054 | GxService.MxStatusHandle = ::RegisterServiceCtrlHandler(GxService.MxServiceName, ServiceCtrlHandler); 2055 | if (GxService.MxStatusHandle == NULL) return; 2056 | 2057 | // Let the service control manager know that the application is started. 2058 | GxService.MxServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 2059 | GxService.MxServiceStatus.dwCurrentState = SERVICE_RUNNING; 2060 | GxService.MxServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 2061 | GxService.MxServiceStatus.dwWin32ExitCode = NO_ERROR; 2062 | GxService.MxServiceStatus.dwServiceSpecificExitCode = 0; 2063 | GxService.MxServiceStatus.dwCheckPoint = 0; 2064 | if (!::SetServiceStatus(GxService.MxStatusHandle, &GxService.MxServiceStatus)) return; 2065 | 2066 | // A quick debugger hook. 2067 | #ifdef _DEBUG 2068 | while (!::IsDebuggerPresent()) ::Sleep(500); 2069 | ::Sleep(2000); 2070 | #endif 2071 | 2072 | // Get past the event object portion of SystemSelf() if the named pipe and the creator process are the same. 2073 | if (GxConnectPipePID == GxService.MxCreatorPID) NotifyServiceCreatorProcess(); 2074 | 2075 | GxService.MxExitCode = (ServiceMainInternal() ? 0 : 1); 2076 | 2077 | // Notify that this process is done with the creator process and can remove the NT service and continue. 2078 | NotifyServiceCreatorProcess(); 2079 | 2080 | // Stop the service. 2081 | GxService.MxServiceStatus.dwCurrentState = SERVICE_STOPPED; 2082 | GxService.MxServiceStatus.dwControlsAccepted = 0; 2083 | GxService.MxServiceStatus.dwWin32ExitCode = GxService.MxExitCode; 2084 | GxService.MxServiceStatus.dwCheckPoint = 0; 2085 | ::SetServiceStatus(GxService.MxStatusHandle, &GxService.MxServiceStatus); 2086 | } 2087 | 2088 | 2089 | bool GxNetworkStarted = false; 2090 | SOCKET ConnectSocketHandle(TCHAR *socketip, unsigned short socketport, char fd, TCHAR *token, unsigned short tokenlen) 2091 | { 2092 | if (socketip == NULL || socketport == 0 || tokenlen > 512) return INVALID_SOCKET; 2093 | 2094 | // Initialize Winsock. 2095 | if (!GxNetworkStarted) 2096 | { 2097 | WSADATA WSAData; 2098 | if (::WSAStartup(MAKEWORD(2, 2), &WSAData)) return INVALID_SOCKET; 2099 | if (LOBYTE(WSAData.wVersion) != 2 || HIBYTE(WSAData.wVersion) != 2) return INVALID_SOCKET; 2100 | 2101 | GxNetworkStarted = true; 2102 | } 2103 | 2104 | // Determine IPv4 or IPv6. 2105 | SOCKET s; 2106 | if (_tcschr(socketip, _T(':')) != NULL) 2107 | { 2108 | struct sockaddr_in6 si = {0}; 2109 | 2110 | si.sin6_family = AF_INET6; 2111 | si.sin6_port = ::htons(socketport); 2112 | if (::InetPton(AF_INET6, socketip, &si.sin6_addr) != 1) return INVALID_SOCKET; 2113 | 2114 | s = ::WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); 2115 | if (s == INVALID_SOCKET) return INVALID_SOCKET; 2116 | 2117 | if (::connect(s, (sockaddr *)&si, sizeof(si)) != 0) 2118 | { 2119 | ::closesocket(s); 2120 | 2121 | return INVALID_SOCKET; 2122 | } 2123 | } 2124 | else 2125 | { 2126 | struct sockaddr_in si = {0}; 2127 | 2128 | si.sin_family = AF_INET; 2129 | si.sin_port = ::htons(socketport); 2130 | if (::InetPton(AF_INET, socketip, &si.sin_addr) != 1) return INVALID_SOCKET; 2131 | 2132 | s = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); 2133 | if (s == INVALID_SOCKET) return INVALID_SOCKET; 2134 | 2135 | if (::connect(s, (sockaddr *)&si, sizeof(si)) != 0) 2136 | { 2137 | ::closesocket(s); 2138 | 2139 | return INVALID_SOCKET; 2140 | } 2141 | } 2142 | 2143 | // Do authentication first. Send token to the target. 2144 | if (tokenlen) 2145 | { 2146 | char tokenbuf[512]; 2147 | 2148 | // There's no particularly good way to handle errors here without completely rewriting the function. 2149 | if (!ReadFileExact(::GetStdHandle(STD_INPUT_HANDLE), tokenbuf, tokenlen) || ::send(s, tokenbuf, (int)tokenlen, 0) != tokenlen) 2150 | { 2151 | ::closesocket(s); 2152 | 2153 | return INVALID_SOCKET; 2154 | } 2155 | } 2156 | else if (token != NULL) 2157 | { 2158 | char tokenbuf[512]; 2159 | 2160 | for (size_t x = 0; x < 512 && token[x]; x++) 2161 | { 2162 | if (token[x] <= 0xFF) tokenbuf[tokenlen++] = (char)token[x]; 2163 | } 2164 | 2165 | if (!tokenlen || ::send(s, tokenbuf, (int)tokenlen, 0) != tokenlen) 2166 | { 2167 | ::closesocket(s); 2168 | 2169 | return INVALID_SOCKET; 2170 | } 2171 | } 2172 | 2173 | // Send one byte of data to the target so that it knows which file descriptor this socket is associated with. 2174 | if (::send(s, &fd, 1, 0) != 1) 2175 | { 2176 | ::closesocket(s); 2177 | 2178 | return INVALID_SOCKET; 2179 | } 2180 | 2181 | return s; 2182 | } 2183 | 2184 | // Moved these out of the main function into the global namespace so the thread entry functions have relatively clean access to them. 2185 | SOCKET Gx_stdinsocket = INVALID_SOCKET; 2186 | HANDLE Gx_stdinwritehandle = NULL; 2187 | 2188 | SOCKET Gx_stdoutsocket = INVALID_SOCKET; 2189 | HANDLE Gx_stdoutreadhandle = NULL; 2190 | 2191 | SOCKET Gx_stderrsocket = INVALID_SOCKET; 2192 | HANDLE Gx_stderrreadhandle = NULL; 2193 | 2194 | DWORD WINAPI StdinSocketHandler(LPVOID) 2195 | { 2196 | char buffer[65536]; 2197 | int bufferlen; 2198 | 2199 | do 2200 | { 2201 | // Stdin socket -> stdin pipe. 2202 | bufferlen = ::recv(Gx_stdinsocket, buffer, sizeof(buffer), 0); 2203 | if (!bufferlen || bufferlen == SOCKET_ERROR) break; 2204 | 2205 | if (!::WriteFile(Gx_stdinwritehandle, buffer, (DWORD)bufferlen, NULL, NULL)) break; 2206 | 2207 | } while (1); 2208 | 2209 | ::closesocket(Gx_stdinsocket); 2210 | ::CloseHandle(Gx_stdinwritehandle); 2211 | 2212 | Gx_stdinsocket = INVALID_SOCKET; 2213 | Gx_stdinwritehandle = NULL; 2214 | 2215 | return 0; 2216 | } 2217 | 2218 | DWORD WINAPI StdoutSocketHandler(LPVOID) 2219 | { 2220 | char buffer[65536]; 2221 | DWORD bufferlen; 2222 | 2223 | do 2224 | { 2225 | // Stdout pipe -> stdout socket. 2226 | if (!::ReadFile(Gx_stdoutreadhandle, buffer, sizeof(buffer), &bufferlen, NULL)) break; 2227 | 2228 | if (bufferlen && ::send(Gx_stdoutsocket, buffer, (int)bufferlen, 0) != bufferlen) break; 2229 | 2230 | } while (1); 2231 | 2232 | ::shutdown(Gx_stdoutsocket, SD_SEND); 2233 | ::recv(Gx_stdoutsocket, buffer, sizeof(buffer), 0); 2234 | 2235 | ::closesocket(Gx_stdoutsocket); 2236 | ::CloseHandle(Gx_stdoutreadhandle); 2237 | 2238 | Gx_stdoutsocket = INVALID_SOCKET; 2239 | Gx_stdoutreadhandle = NULL; 2240 | 2241 | return 0; 2242 | } 2243 | 2244 | DWORD WINAPI StderrSocketHandler(LPVOID) 2245 | { 2246 | char buffer[65536]; 2247 | DWORD bufferlen; 2248 | 2249 | do 2250 | { 2251 | // Stderr pipe -> stderr socket. 2252 | if (!::ReadFile(Gx_stderrreadhandle, buffer, sizeof(buffer), &bufferlen, NULL)) break; 2253 | 2254 | if (bufferlen && ::send(Gx_stderrsocket, buffer, (int)bufferlen, 0) != bufferlen) break; 2255 | 2256 | } while (1); 2257 | 2258 | ::shutdown(Gx_stderrsocket, SD_SEND); 2259 | ::recv(Gx_stderrsocket, buffer, sizeof(buffer), 0); 2260 | 2261 | ::closesocket(Gx_stderrsocket); 2262 | ::CloseHandle(Gx_stderrreadhandle); 2263 | 2264 | Gx_stderrsocket = INVALID_SOCKET; 2265 | Gx_stderrreadhandle = NULL; 2266 | 2267 | return 0; 2268 | } 2269 | 2270 | 2271 | int MainInternal(int argc, TCHAR **argv, TCHAR **envp) 2272 | { 2273 | bool verbose = false; 2274 | bool wait = false; 2275 | bool terminate = false; 2276 | DWORD waitamount = INFINITE; 2277 | LPTSTR pidfile = NULL; 2278 | char assigntoken = -1; 2279 | LPTSTR tokenopts = NULL; 2280 | bool mergeenv = false; 2281 | HANDLE connectpipe = INVALID_HANDLE_VALUE; 2282 | HANDLE mutexhandle = NULL, semaphorehandle = NULL; 2283 | DWORD priorityflag = 0; 2284 | #ifdef UNICODE 2285 | DWORD createflags = CREATE_UNICODE_ENVIRONMENT; 2286 | #else 2287 | DWORD createflags = 0; 2288 | #endif 2289 | LPTSTR startdir = NULL; 2290 | LPTSTR appname = NULL; 2291 | STARTUPINFO startinfo = {0}; 2292 | SECURITY_ATTRIBUTES secattr = {0}; 2293 | TCHAR *stdinstr = (TCHAR *)_T(":stdin"); 2294 | TCHAR *stdoutstr = (TCHAR *)_T(":stdout"); 2295 | TCHAR *stderrstr = (TCHAR *)_T(":stderr"); 2296 | TCHAR *socketip = NULL; 2297 | unsigned short socketport = 0; 2298 | TCHAR *sockettoken = NULL; 2299 | unsigned short sockettokenlen = 0; 2300 | HANDLE temphandle = NULL, stdinread = NULL, stdoutwrite = NULL, stderrwrite = NULL; 2301 | HANDLE stdinthread = NULL, stdoutthread = NULL, stderrthread = NULL; 2302 | PROFILEINFO tempprofile = {0}; 2303 | PROCESS_INFORMATION procinfo = {0}; 2304 | DWORD exitcode = 0; 2305 | DWORD winerror = 0; 2306 | 2307 | if (argc < 2) 2308 | { 2309 | DumpSyntax(argv[0]); 2310 | 2311 | return 1; 2312 | } 2313 | 2314 | // Initialize structures. 2315 | startinfo.cb = sizeof(startinfo); 2316 | startinfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); 2317 | startinfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); 2318 | startinfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); 2319 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 2320 | 2321 | secattr.nLength = sizeof(secattr); 2322 | secattr.bInheritHandle = TRUE; 2323 | secattr.lpSecurityDescriptor = NULL; 2324 | 2325 | // Process command-line options. 2326 | int x, x2; 2327 | for (x = 1; x < argc; x++) 2328 | { 2329 | if (!_tcsicmp(argv[x], _T("/?"))) 2330 | { 2331 | x = argc; 2332 | 2333 | break; 2334 | } 2335 | else if (!_tcsicmp(argv[x], _T("/v"))) verbose = true; 2336 | else if (!_tcsicmp(argv[x], _T("/w"))) wait = true; 2337 | else if (!_tcsncicmp(argv[x], _T("/w="), 3)) 2338 | { 2339 | wait = true; 2340 | waitamount = _tstoi(argv[x] + 3); 2341 | } 2342 | else if (!_tcsncicmp(argv[x], _T("/pid="), 5)) pidfile = argv[x] + 5; 2343 | else if (!_tcsicmp(argv[x], _T("/term"))) terminate = true; 2344 | else if (!_tcsicmp(argv[x], _T("/runelevated"))) 2345 | { 2346 | // Send this process' command-line and environment variables to an elevated process. 2347 | x2 = IsElevatedProcess(); 2348 | if (x2 < 0) return 1; 2349 | 2350 | if (x2 == 0) 2351 | { 2352 | // Set up an event object to wait for the timeout period. 2353 | CubicleSoft::StaticWCMixedVar TempVar; 2354 | HANDLE tempevent = INVALID_HANDLE_VALUE; 2355 | 2356 | HANDLE commpipeevent = CreateCommPipeConnectEventObj(::GetCurrentProcessId()); 2357 | if (commpipeevent == NULL) 2358 | { 2359 | DumpErrorMsg("Unable to create pipe server event object.", "create_event_failed", ::GetLastError()); 2360 | 2361 | return false; 2362 | } 2363 | 2364 | if (wait) 2365 | { 2366 | TempVar.SetStr(L"Global\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_CommEv2_"); 2367 | TempVar.AppendUInt(::GetCurrentProcessId()); 2368 | 2369 | tempevent = ::CreateEventW(NULL, FALSE, FALSE, TempVar.GetStr()); 2370 | if (tempevent == NULL) 2371 | { 2372 | DumpErrorMsg("Unable to create notification event object.", "create_event_failed", ::GetLastError()); 2373 | 2374 | return false; 2375 | } 2376 | } 2377 | 2378 | // Start the named pipe server. 2379 | HANDLE commpipehandle = StartMainCommPipeServer(); 2380 | if (commpipehandle == INVALID_HANDLE_VALUE) 2381 | { 2382 | DumpErrorMsg("Unable to create the main communication pipe.", "start_main_comm_pipe_failed", ::GetLastError()); 2383 | 2384 | return 1; 2385 | } 2386 | 2387 | // Start an elevated process. 2388 | if (!ElevateSelf(false)) return 1; 2389 | 2390 | // Wait for the client to connect. 2391 | #ifdef _DEBUG 2392 | if (!WaitForValidCommPipeClient(commpipehandle, commpipeevent, INFINITE)) return 1; 2393 | #else 2394 | if (!WaitForValidCommPipeClient(commpipehandle, commpipeevent, 10000)) return 1; 2395 | #endif 2396 | 2397 | // Process the status. 2398 | if (!WaitForCommPipeStatus(commpipehandle)) return 1; 2399 | 2400 | DWORD tempsize; 2401 | 2402 | // Send command-line options. 2403 | tempsize = sizeof(WCHAR); 2404 | for (x2 = 0; x2 < argc; x2++) 2405 | { 2406 | if (x2 != x) 2407 | { 2408 | TempVar.SetStr(argv[x2]); 2409 | tempsize += (TempVar.GetSize() + 1) * sizeof(WCHAR); 2410 | } 2411 | } 2412 | 2413 | ::WriteFile(commpipehandle, &tempsize, 4, NULL, NULL); 2414 | 2415 | for (x2 = 0; x2 < argc; x2++) 2416 | { 2417 | if (x2 != x) 2418 | { 2419 | TempVar.SetStr(argv[x2]); 2420 | tempsize = (TempVar.GetSize() + 1) * sizeof(WCHAR); 2421 | ::WriteFile(commpipehandle, TempVar.GetStr(), tempsize, NULL, NULL); 2422 | } 2423 | } 2424 | 2425 | ::WriteFile(commpipehandle, L"\0", sizeof(WCHAR), NULL, NULL); 2426 | 2427 | // Send environment. 2428 | tempsize = sizeof(WCHAR); 2429 | for (x2 = 0; envp[x2]; x2++) 2430 | { 2431 | TempVar.SetStr(envp[x2]); 2432 | tempsize += (TempVar.GetSize() + 1) * sizeof(WCHAR); 2433 | } 2434 | 2435 | ::WriteFile(commpipehandle, &tempsize, 4, NULL, NULL); 2436 | for (x2 = 0; envp[x2]; x2++) 2437 | { 2438 | TempVar.SetStr(envp[x2]); 2439 | tempsize = (TempVar.GetSize() + 1) * sizeof(WCHAR); 2440 | ::WriteFile(commpipehandle, TempVar.GetStr(), tempsize, NULL, NULL); 2441 | } 2442 | 2443 | ::WriteFile(commpipehandle, L"\0", sizeof(WCHAR), NULL, NULL); 2444 | 2445 | // Get the status/result. 2446 | if (!WaitForCommPipeStatus(commpipehandle)) return 1; 2447 | 2448 | int retval = 0; 2449 | 2450 | // Wait for the event object to fire. 2451 | if (wait) 2452 | { 2453 | if (::WaitForSingleObject(tempevent, waitamount) != WAIT_OBJECT_0) 2454 | { 2455 | DumpErrorMsg("The elevated process did not respond before the timeout expired.", "timeout", ::GetLastError()); 2456 | 2457 | ::CloseHandle(tempevent); 2458 | 2459 | return false; 2460 | } 2461 | 2462 | ::CloseHandle(tempevent); 2463 | 2464 | // Get the status/result. 2465 | if (!WaitForCommPipeStatus(commpipehandle)) return 1; 2466 | 2467 | // Receive the return value. 2468 | if (!ReadFileExact(commpipehandle, &retval, sizeof(retval))) 2469 | { 2470 | DumpErrorMsg("Unable to read return value from the main communication pipe.", "read_main_comm_pipe_failed", ::GetLastError()); 2471 | 2472 | return 1; 2473 | } 2474 | } 2475 | 2476 | ::CloseHandle(commpipehandle); 2477 | 2478 | return retval; 2479 | } 2480 | } 2481 | else if (!_tcsncicmp(argv[x], _T("/runelevated="), 13)) 2482 | { 2483 | // Should only be called by ElevateSelf(false). 2484 | GxConnectPipePID = _tstoi(argv[x] + 13); 2485 | 2486 | // A quick debugger hook. 2487 | #ifdef _DEBUG 2488 | while (!::IsDebuggerPresent()) ::Sleep(500); 2489 | ::Sleep(2000); 2490 | #endif 2491 | 2492 | x2 = IsSystemProcess(); 2493 | if (x2 < 0) return 1; 2494 | 2495 | if (x2 == 1) 2496 | { 2497 | DumpErrorMsg("Process is running as SYSTEM. Expected elevation only.", "elevation_check_failed", ::GetLastError()); 2498 | 2499 | return 1; 2500 | } 2501 | 2502 | x2 = IsElevatedProcess(); 2503 | if (x2 < 0) return 1; 2504 | 2505 | if (x2 == 0) 2506 | { 2507 | DumpErrorMsg("Process is not elevated. Expected elevation.", "elevation_check_failed", ::GetLastError()); 2508 | 2509 | return 1; 2510 | } 2511 | 2512 | if (!ConnectToMainCommPipe()) 2513 | { 2514 | DumpErrorMsg("Unable to connect to the main communication pipe.", "connect_to_main_comm_pipe_failed", ::GetLastError()); 2515 | 2516 | return 1; 2517 | } 2518 | 2519 | CubicleSoft::StaticWCMixedVar TempVar; 2520 | HANDLE tempevent; 2521 | 2522 | TempVar.SetStr(L"Global\\CreateProcess_21F9597D-1A55-41AD-BCE0-0DB5CA53BDF8_CommEv2_"); 2523 | TempVar.AppendUInt(GxConnectPipePID); 2524 | 2525 | tempevent = ::CreateEventW(NULL, FALSE, FALSE, TempVar.GetStr()); 2526 | if (tempevent == NULL) 2527 | { 2528 | DumpErrorMsg("Unable to create notification event object.", "create_event_failed", ::GetLastError()); 2529 | 2530 | return 1; 2531 | } 2532 | 2533 | // Notify that all is well so far. 2534 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 2535 | 2536 | // Get the command-line and environment. 2537 | int argc2 = 0, envc2 = 0; 2538 | WCHAR *argvbuffer, *envpbuffer; 2539 | WCHAR **argv2, **envp2; 2540 | 2541 | if (!ReadCommPipeStringArray(GxMainCommPipeClient, argvbuffer, argv2, argc2)) return 1; 2542 | if (!ReadCommPipeStringArray(GxMainCommPipeClient, envpbuffer, envp2, envc2)) return 1; 2543 | 2544 | // Notify that all is well so far. 2545 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 2546 | 2547 | // Run this function again with the new information. 2548 | int retval = MainInternal(argc2, argv2, envp2); 2549 | 2550 | // Trigger the notification event that this process is done. 2551 | if (!::SetEvent(tempevent)) 2552 | { 2553 | DumpErrorMsg("Unable to trigger the notification event.", "set_event_failed", ::GetLastError()); 2554 | 2555 | ::CloseHandle(tempevent); 2556 | 2557 | return 1; 2558 | } 2559 | 2560 | ::CloseHandle(tempevent); 2561 | 2562 | // Notify that the process is done and send the return value. 2563 | if (GxMainCommPipeClient != INVALID_HANDLE_VALUE) 2564 | { 2565 | ::WriteFile(GxMainCommPipeClient, "\x01", 1, NULL, NULL); 2566 | ::WriteFile(GxMainCommPipeClient, &retval, sizeof(retval), NULL, NULL); 2567 | 2568 | DisconnectMainCommPipe(); 2569 | } 2570 | 2571 | FreeCommPipeStringArray(argvbuffer, argv2); 2572 | FreeCommPipeStringArray(envpbuffer, envp2); 2573 | 2574 | return retval; 2575 | } 2576 | else if (!_tcsicmp(argv[x], _T("/systemtoken"))) assigntoken = 0; 2577 | else if (!_tcsicmp(argv[x], _T("/elevatedtoken"))) assigntoken = 1; 2578 | else if (!_tcsncicmp(argv[x], _T("/usetoken="), 10)) 2579 | { 2580 | assigntoken = 2; 2581 | tokenopts = argv[x] + 10; 2582 | } 2583 | else if (!_tcsncicmp(argv[x], _T("/createtoken="), 13)) 2584 | { 2585 | assigntoken = 3; 2586 | tokenopts = argv[x] + 13; 2587 | } 2588 | else if (!_tcsicmp(argv[x], _T("/mergeenv"))) mergeenv = true; 2589 | else if (!_tcsncicmp(argv[x], _T("/connectpipe="), 13)) GxConnectPipePID = _tstoi(argv[x] + 13); 2590 | else if (!_tcsicmp(argv[x], _T("/createservice"))) 2591 | { 2592 | // Should only be called by ElevateSelf(true). 2593 | if (!GxConnectPipePID) 2594 | { 2595 | DumpErrorMsg("Missing expected /connectpipe.", "pipe_check_failed", ::GetLastError()); 2596 | 2597 | return 1; 2598 | } 2599 | 2600 | x2 = IsSystemProcess(); 2601 | if (x2 < 0) return 1; 2602 | 2603 | if (x2 == 1) 2604 | { 2605 | DumpErrorMsg("Process is running as SYSTEM. Expected elevation only.", "elevation_check_failed", ::GetLastError()); 2606 | 2607 | return 1; 2608 | } 2609 | 2610 | x2 = IsElevatedProcess(); 2611 | if (x2 < 0) return 1; 2612 | 2613 | if (x2 == 0) 2614 | { 2615 | DumpErrorMsg("Process is not elevated. Expected elevation.", "elevation_check_failed", ::GetLastError()); 2616 | 2617 | return 1; 2618 | } 2619 | 2620 | if (!SystemSelf(GxConnectPipePID)) return 1; 2621 | 2622 | return 0; 2623 | } 2624 | else if (!_tcsncicmp(argv[x], _T("/runservice="), 12)) 2625 | { 2626 | // Called by SystemSelf(). 2627 | GxService.MxServiceStatus = {0}; 2628 | GxService.MxStatusHandle = NULL; 2629 | GxService.MxCreatorPID = _tstoi(argv[x] + 12); 2630 | 2631 | CubicleSoft::StaticWCMixedVar TempVar; 2632 | 2633 | TempVar.SetStr(L"z_createprocess-"); 2634 | TempVar.AppendUInt(GxService.MxCreatorPID); 2635 | 2636 | GxService.MxServiceName = TempVar.GetStr(); 2637 | GxService.MxExitCode = 0; 2638 | 2639 | // Start the NT service. 2640 | SERVICE_TABLE_ENTRY servicetable[] = { 2641 | {GxService.MxServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain}, 2642 | {NULL, NULL} 2643 | }; 2644 | 2645 | if (!::StartServiceCtrlDispatcher(servicetable)) 2646 | { 2647 | DumpErrorMsg("Error starting service.", "start_service_ctrl_dispatcher_failed", ::GetLastError()); 2648 | 2649 | GxService.MxExitCode = 1; 2650 | } 2651 | 2652 | return GxService.MxExitCode; 2653 | } 2654 | else if (!_tcsncicmp(argv[x], _T("/mutex="), 7)) 2655 | { 2656 | if (mutexhandle != NULL) ::CloseHandle(mutexhandle); 2657 | 2658 | mutexhandle = ::CreateMutex(NULL, FALSE, argv[x] + 7); 2659 | if (mutexhandle == NULL) 2660 | { 2661 | DumpErrorMsg("Unable to create mutex.", "create_mutex_failed", ::GetLastError()); 2662 | 2663 | return 1; 2664 | } 2665 | } 2666 | else if (!_tcsicmp(argv[x], _T("/singleton"))) 2667 | { 2668 | if (mutexhandle == NULL) 2669 | { 2670 | DumpErrorMsg("The /mutex option was not used.", "mutex_check_failed", ::GetLastError()); 2671 | 2672 | return 1; 2673 | } 2674 | 2675 | if (::WaitForSingleObject(mutexhandle, 0) != WAIT_OBJECT_0) 2676 | { 2677 | DumpErrorMsg("The mutex is in use.", "mutex_in_use", 0); 2678 | 2679 | return 1; 2680 | } 2681 | } 2682 | else if (!_tcsncicmp(argv[x], _T("/singleton="), 11)) 2683 | { 2684 | if (mutexhandle == NULL) 2685 | { 2686 | DumpErrorMsg("The /mutex option was not used.", "mutex_check_failed", ::GetLastError()); 2687 | 2688 | return 1; 2689 | } 2690 | 2691 | DWORD temptimeout = (DWORD)_tstoi(argv[x] + 11); 2692 | 2693 | if (::WaitForSingleObject(mutexhandle, temptimeout) != WAIT_OBJECT_0) 2694 | { 2695 | DumpErrorMsg("The mutex is in use.", "mutex_in_use", 0); 2696 | 2697 | return 1; 2698 | } 2699 | } 2700 | else if (!_tcsncicmp(argv[x], _T("/semaphore="), 11)) 2701 | { 2702 | if (semaphorehandle != NULL) ::CloseHandle(semaphorehandle); 2703 | 2704 | for (x2 = 7; argv[x][x2] && argv[x][x2] != _T(','); x2++); 2705 | if (!argv[x][x2]) 2706 | { 2707 | DumpErrorMsg("Invalid /semaphore option.", "invalid_semaphore_option", 0); 2708 | DumpSyntax(argv[0]); 2709 | 2710 | return 1; 2711 | } 2712 | 2713 | LONG maxcount = (LONG)_tstoi(argv[x] + 11); 2714 | 2715 | semaphorehandle = ::CreateSemaphoreW(NULL, maxcount, maxcount, argv[x] + x2 + 1); 2716 | if (semaphorehandle == NULL) 2717 | { 2718 | DumpErrorMsg("Unable to create semaphore.", "create_semaphore_failed", ::GetLastError()); 2719 | 2720 | return 1; 2721 | } 2722 | } 2723 | else if (!_tcsicmp(argv[x], _T("/multiton"))) 2724 | { 2725 | if (semaphorehandle == NULL) 2726 | { 2727 | DumpErrorMsg("The /semaphore option was not used.", "semaphore_check_failed", ::GetLastError()); 2728 | 2729 | return 1; 2730 | } 2731 | 2732 | if (::WaitForSingleObject(semaphorehandle, 0) != WAIT_OBJECT_0) 2733 | { 2734 | DumpErrorMsg("The semaphore is in use.", "semaphore_in_use", 0); 2735 | 2736 | return 1; 2737 | } 2738 | } 2739 | else if (!_tcsncicmp(argv[x], _T("/multiton="), 10)) 2740 | { 2741 | if (semaphorehandle == NULL) 2742 | { 2743 | DumpErrorMsg("The /semaphore option was not used.", "semaphore_check_failed", ::GetLastError()); 2744 | 2745 | return 1; 2746 | } 2747 | 2748 | DWORD temptimeout = (DWORD)_tstoi(argv[x] + 11); 2749 | 2750 | if (::WaitForSingleObject(semaphorehandle, temptimeout) != WAIT_OBJECT_0) 2751 | { 2752 | DumpErrorMsg("The semaphore is in use.", "semaphore_in_use", 0); 2753 | 2754 | return 1; 2755 | } 2756 | } 2757 | else if (!_tcsicmp(argv[x], _T("/f=ABOVE_NORMAL_PRIORITY_CLASS"))) priorityflag = ABOVE_NORMAL_PRIORITY_CLASS; 2758 | else if (!_tcsicmp(argv[x], _T("/f=BELOW_NORMAL_PRIORITY_CLASS"))) priorityflag = BELOW_NORMAL_PRIORITY_CLASS; 2759 | else if (!_tcsicmp(argv[x], _T("/f=HIGH_PRIORITY_CLASS"))) priorityflag = HIGH_PRIORITY_CLASS; 2760 | else if (!_tcsicmp(argv[x], _T("/f=IDLE_PRIORITY_CLASS"))) priorityflag = IDLE_PRIORITY_CLASS; 2761 | else if (!_tcsicmp(argv[x], _T("/f=NORMAL_PRIORITY_CLASS"))) priorityflag = NORMAL_PRIORITY_CLASS; 2762 | else if (!_tcsicmp(argv[x], _T("/f=REALTIME_PRIORITY_CLASS"))) priorityflag = REALTIME_PRIORITY_CLASS; 2763 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_DEFAULT_ERROR_MODE"))) createflags |= CREATE_DEFAULT_ERROR_MODE; 2764 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_NEW_CONSOLE"))) createflags |= CREATE_NEW_CONSOLE; 2765 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_NEW_PROCESS_GROUP"))) createflags |= CREATE_NEW_PROCESS_GROUP; 2766 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_NO_WINDOW"))) createflags |= CREATE_NO_WINDOW; 2767 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_PROTECTED_PROCESS"))) createflags |= CREATE_PROTECTED_PROCESS; 2768 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_PRESERVE_CODE_AUTHZ_LEVEL"))) createflags |= CREATE_PRESERVE_CODE_AUTHZ_LEVEL; 2769 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_SEPARATE_WOW_VDM"))) createflags |= CREATE_SEPARATE_WOW_VDM; 2770 | else if (!_tcsicmp(argv[x], _T("/f=CREATE_SHARED_WOW_VDM"))) createflags |= CREATE_SHARED_WOW_VDM; 2771 | else if (!_tcsicmp(argv[x], _T("/f=DEBUG_ONLY_THIS_PROCESS"))) createflags |= DEBUG_ONLY_THIS_PROCESS; 2772 | else if (!_tcsicmp(argv[x], _T("/f=DEBUG_PROCESS"))) createflags |= DEBUG_PROCESS; 2773 | else if (!_tcsicmp(argv[x], _T("/f=DETACHED_PROCESS"))) createflags |= DETACHED_PROCESS; 2774 | else if (!_tcsicmp(argv[x], _T("/f=INHERIT_PARENT_AFFINITY"))) createflags |= INHERIT_PARENT_AFFINITY; 2775 | else if (!_tcsncicmp(argv[x], _T("/dir="), 5)) startdir = argv[x] + 5; 2776 | else if (!_tcsncicmp(argv[x], _T("/desktop="), 9)) startinfo.lpDesktop = argv[x] + 9; 2777 | else if (!_tcsncicmp(argv[x], _T("/title="), 7)) startinfo.lpTitle = argv[x] + 7; 2778 | else if (!_tcsncicmp(argv[x], _T("/x="), 3)) 2779 | { 2780 | startinfo.dwX = _tstoi(argv[x] + 3); 2781 | startinfo.dwFlags |= STARTF_USEPOSITION; 2782 | } 2783 | else if (!_tcsncicmp(argv[x], _T("/y="), 3)) 2784 | { 2785 | startinfo.dwY = _tstoi(argv[x] + 3); 2786 | startinfo.dwFlags |= STARTF_USEPOSITION; 2787 | } 2788 | else if (!_tcsncicmp(argv[x], _T("/width="), 7)) 2789 | { 2790 | startinfo.dwXSize = _tstoi(argv[x] + 7); 2791 | startinfo.dwFlags |= STARTF_USESIZE; 2792 | } 2793 | else if (!_tcsncicmp(argv[x], _T("/height="), 8)) 2794 | { 2795 | startinfo.dwYSize = _tstoi(argv[x] + 8); 2796 | startinfo.dwFlags |= STARTF_USESIZE; 2797 | } 2798 | else if (!_tcsncicmp(argv[x], _T("/xchars="), 8)) 2799 | { 2800 | startinfo.dwXCountChars = _tstoi(argv[x] + 8); 2801 | startinfo.dwFlags |= STARTF_USECOUNTCHARS; 2802 | } 2803 | else if (!_tcsncicmp(argv[x], _T("/ychars="), 8)) 2804 | { 2805 | startinfo.dwYCountChars = _tstoi(argv[x] + 8); 2806 | startinfo.dwFlags |= STARTF_USECOUNTCHARS; 2807 | } 2808 | else if (!_tcsicmp(argv[x], _T("/f=FOREGROUND_RED"))) startinfo.dwFillAttribute |= FOREGROUND_RED; 2809 | else if (!_tcsicmp(argv[x], _T("/f=FOREGROUND_GREEN"))) startinfo.dwFillAttribute |= FOREGROUND_GREEN; 2810 | else if (!_tcsicmp(argv[x], _T("/f=FOREGROUND_BLUE"))) startinfo.dwFillAttribute |= FOREGROUND_BLUE; 2811 | else if (!_tcsicmp(argv[x], _T("/f=FOREGROUND_INTENSITY"))) startinfo.dwFillAttribute |= FOREGROUND_INTENSITY; 2812 | else if (!_tcsicmp(argv[x], _T("/f=BACKGROUND_RED"))) startinfo.dwFillAttribute |= BACKGROUND_RED; 2813 | else if (!_tcsicmp(argv[x], _T("/f=BACKGROUND_GREEN"))) startinfo.dwFillAttribute |= BACKGROUND_GREEN; 2814 | else if (!_tcsicmp(argv[x], _T("/f=BACKGROUND_BLUE"))) startinfo.dwFillAttribute |= BACKGROUND_BLUE; 2815 | else if (!_tcsicmp(argv[x], _T("/f=BACKGROUND_INTENSITY"))) startinfo.dwFillAttribute |= BACKGROUND_INTENSITY; 2816 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_FORCEONFEEDBACK"))) startinfo.dwFlags |= STARTF_FORCEONFEEDBACK; 2817 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_FORCEOFFFEEDBACK"))) startinfo.dwFlags |= STARTF_FORCEOFFFEEDBACK; 2818 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_PREVENTPINNING"))) startinfo.dwFlags |= STARTF_PREVENTPINNING; 2819 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_RUNFULLSCREEN"))) startinfo.dwFlags |= STARTF_RUNFULLSCREEN; 2820 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_TITLEISAPPID"))) startinfo.dwFlags |= STARTF_TITLEISAPPID; 2821 | else if (!_tcsicmp(argv[x], _T("/f=STARTF_TITLEISLINKNAME"))) startinfo.dwFlags |= STARTF_TITLEISLINKNAME; 2822 | else if (!_tcsicmp(argv[x], _T("/f=SW_FORCEMINIMIZE"))) startinfo.wShowWindow = SW_FORCEMINIMIZE; 2823 | else if (!_tcsicmp(argv[x], _T("/f=SW_HIDE"))) 2824 | { 2825 | startinfo.dwFlags |= STARTF_USESHOWWINDOW; 2826 | startinfo.wShowWindow = SW_HIDE; 2827 | } 2828 | else if (!_tcsicmp(argv[x], _T("/f=SW_MAXIMIZE"))) startinfo.wShowWindow = SW_MAXIMIZE; 2829 | else if (!_tcsicmp(argv[x], _T("/f=SW_MINIMIZE"))) startinfo.wShowWindow = SW_MINIMIZE; 2830 | else if (!_tcsicmp(argv[x], _T("/f=SW_RESTORE"))) startinfo.wShowWindow = SW_RESTORE; 2831 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOW"))) startinfo.wShowWindow = SW_SHOW; 2832 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWDEFAULT"))) startinfo.wShowWindow = SW_SHOWDEFAULT; 2833 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWMAXIMIZED"))) startinfo.wShowWindow = SW_SHOWMAXIMIZED; 2834 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWMINIMIZED"))) startinfo.wShowWindow = SW_SHOWMINIMIZED; 2835 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWMINNOACTIVE"))) startinfo.wShowWindow = SW_SHOWMINNOACTIVE; 2836 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWNA"))) startinfo.wShowWindow = SW_SHOWNA; 2837 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWNOACTIVATE"))) startinfo.wShowWindow = SW_SHOWNOACTIVATE; 2838 | else if (!_tcsicmp(argv[x], _T("/f=SW_SHOWNORMAL"))) startinfo.wShowWindow = SW_SHOWNORMAL; 2839 | else if (!_tcsncicmp(argv[x], _T("/hotkey="), 8)) 2840 | { 2841 | startinfo.hStdInput = UlongToHandle(_tstoi(argv[x] + 8)); 2842 | stdinstr = (TCHAR *)_T(":hotkey"); 2843 | 2844 | startinfo.dwFlags |= STARTF_USEHOTKEY; 2845 | startinfo.dwFlags &= ~STARTF_USESTDHANDLES; 2846 | } 2847 | else if (!_tcsncicmp(argv[x], _T("/socketip="), 10)) socketip = argv[x] + 10; 2848 | else if (!_tcsncicmp(argv[x], _T("/socketport="), 12)) socketport = (unsigned short)_tstoi(argv[x] + 12); 2849 | else if (!_tcsncicmp(argv[x], _T("/sockettoken="), 13)) sockettoken = argv[x] + 13; 2850 | else if (!_tcsncicmp(argv[x], _T("/sockettokenlen="), 16)) sockettokenlen = (unsigned short)_tstoi(argv[x] + 16); 2851 | else if (!_tcsncicmp(argv[x], _T("/stdin="), 7)) 2852 | { 2853 | if (argv[x][7] == _T('\0')) 2854 | { 2855 | startinfo.hStdInput = NULL; 2856 | stdinstr = (TCHAR *)_T(":null"); 2857 | } 2858 | else if (!_tcsicmp(argv[x] + 7, _T("socket"))) 2859 | { 2860 | Gx_stdinsocket = ConnectSocketHandle(socketip, socketport, '\x00', sockettoken, sockettokenlen); 2861 | temphandle = NULL; 2862 | 2863 | if (Gx_stdinsocket != INVALID_SOCKET && stdinread == NULL && Gx_stdinwritehandle == NULL && ::CreatePipe(&stdinread, &temphandle, &secattr, 0)) 2864 | { 2865 | wait = true; 2866 | 2867 | ::SetHandleInformation(temphandle, HANDLE_FLAG_INHERIT, 0); 2868 | Gx_stdinwritehandle = temphandle; 2869 | 2870 | startinfo.hStdInput = stdinread; 2871 | stdinstr = (TCHAR *)_T(":socket"); 2872 | } 2873 | } 2874 | else 2875 | { 2876 | startinfo.hStdInput = ::CreateFile(argv[x] + 7, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &secattr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 2877 | stdinstr = argv[x] + 7; 2878 | } 2879 | 2880 | startinfo.dwFlags &= ~STARTF_USEHOTKEY; 2881 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 2882 | } 2883 | else if (!_tcsncicmp(argv[x], _T("/stdout="), 8)) 2884 | { 2885 | if (argv[x][8] == _T('\0')) 2886 | { 2887 | startinfo.hStdOutput = NULL; 2888 | stdoutstr = (TCHAR *)_T(":null"); 2889 | } 2890 | else if (!_tcsicmp(argv[x] + 8, _T("socket"))) 2891 | { 2892 | Gx_stdoutsocket = ConnectSocketHandle(socketip, socketport, '\x01', sockettoken, sockettokenlen); 2893 | temphandle = NULL; 2894 | 2895 | if (Gx_stdoutsocket != INVALID_SOCKET && stdoutwrite == NULL && Gx_stdoutreadhandle == NULL && ::CreatePipe(&temphandle, &stdoutwrite, &secattr, 0)) 2896 | { 2897 | wait = true; 2898 | 2899 | ::SetHandleInformation(temphandle, HANDLE_FLAG_INHERIT, 0); 2900 | Gx_stdoutreadhandle = temphandle; 2901 | 2902 | startinfo.hStdOutput = stdoutwrite; 2903 | stdoutstr = (TCHAR *)_T(":socket"); 2904 | } 2905 | } 2906 | else if (!_tcsicmp(argv[x] + 8, _T("stderr"))) 2907 | { 2908 | startinfo.hStdOutput = startinfo.hStdError; 2909 | stdoutstr = stderrstr; 2910 | } 2911 | else 2912 | { 2913 | startinfo.hStdOutput = ::CreateFile(argv[x] + 8, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secattr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 2914 | stdoutstr = argv[x] + 8; 2915 | if (startinfo.hStdOutput != INVALID_HANDLE_VALUE) 2916 | { 2917 | LARGE_INTEGER filepos = {0}; 2918 | ::SetFilePointerEx(startinfo.hStdOutput, filepos, NULL, FILE_END); 2919 | } 2920 | } 2921 | 2922 | startinfo.dwFlags &= ~STARTF_USEHOTKEY; 2923 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 2924 | } 2925 | else if (!_tcsncicmp(argv[x], _T("/stderr="), 8)) 2926 | { 2927 | if (argv[x][8] == _T('\0')) 2928 | { 2929 | startinfo.hStdError = NULL; 2930 | stderrstr = (TCHAR *)_T(":null"); 2931 | } 2932 | else if (!_tcsicmp(argv[x] + 8, _T("socket"))) 2933 | { 2934 | Gx_stderrsocket = ConnectSocketHandle(socketip, socketport, '\x02', sockettoken, sockettokenlen); 2935 | temphandle = NULL; 2936 | 2937 | if (Gx_stderrsocket != INVALID_SOCKET && stderrwrite == NULL && Gx_stderrreadhandle == NULL && ::CreatePipe(&temphandle, &stderrwrite, &secattr, 0)) 2938 | { 2939 | wait = true; 2940 | 2941 | ::SetHandleInformation(temphandle, HANDLE_FLAG_INHERIT, 0); 2942 | Gx_stderrreadhandle = temphandle; 2943 | 2944 | startinfo.hStdError = stderrwrite; 2945 | stderrstr = (TCHAR *)_T(":socket"); 2946 | } 2947 | } 2948 | else if (!_tcsicmp(argv[x] + 8, _T("stdout"))) 2949 | { 2950 | startinfo.hStdError = startinfo.hStdOutput; 2951 | stderrstr = stdoutstr; 2952 | } 2953 | else 2954 | { 2955 | startinfo.hStdError = ::CreateFile(argv[x] + 8, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secattr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 2956 | stderrstr = argv[x] + 8; 2957 | if (startinfo.hStdError != INVALID_HANDLE_VALUE) 2958 | { 2959 | LARGE_INTEGER filepos = {0}; 2960 | ::SetFilePointerEx(startinfo.hStdError, filepos, NULL, FILE_END); 2961 | } 2962 | } 2963 | 2964 | startinfo.dwFlags &= ~STARTF_USEHOTKEY; 2965 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 2966 | } 2967 | else if (!_tcsicmp(argv[x], _T("/attach"))) 2968 | { 2969 | #ifdef SUBSYSTEM_WINDOWS 2970 | // For the Windows subsystem only, attempt to attach to a parent console if it exists. 2971 | InitVerboseMode(); 2972 | #endif 2973 | 2974 | // Reset handles. 2975 | startinfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); 2976 | startinfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); 2977 | startinfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); 2978 | 2979 | stdinstr = (TCHAR *)_T(":stdin"); 2980 | stdoutstr = (TCHAR *)_T(":stdout"); 2981 | stderrstr = (TCHAR *)_T(":stderr"); 2982 | 2983 | startinfo.dwFlags &= ~STARTF_USEHOTKEY; 2984 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 2985 | } 2986 | else if (!_tcsncicmp(argv[x], _T("/attach="), 8)) 2987 | { 2988 | ::CloseHandle(startinfo.hStdInput); 2989 | ::CloseHandle(startinfo.hStdOutput); 2990 | ::CloseHandle(startinfo.hStdError); 2991 | ::FreeConsole(); 2992 | 2993 | DWORD pid = _tstoi(argv[x] + 8); 2994 | 2995 | // A quick debugger hook. 2996 | #ifdef _DEBUG 2997 | while (!::IsDebuggerPresent()) ::Sleep(500); 2998 | ::Sleep(2000); 2999 | #endif 3000 | 3001 | if (::AttachConsole(pid)) 3002 | { 3003 | // Reset handles. 3004 | startinfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); 3005 | startinfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); 3006 | startinfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); 3007 | 3008 | stdinstr = (TCHAR *)_T(":stdin"); 3009 | stdoutstr = (TCHAR *)_T(":stdout"); 3010 | stderrstr = (TCHAR *)_T(":stderr"); 3011 | 3012 | // Reset C library stdin/stdout/stderr. 3013 | if (startinfo.hStdInput != INVALID_HANDLE_VALUE) 3014 | { 3015 | freopen("CONIN$", "r", stdin); 3016 | setvbuf(stdin, NULL, _IONBF, 0); 3017 | } 3018 | 3019 | if (startinfo.hStdOutput != INVALID_HANDLE_VALUE) 3020 | { 3021 | freopen("CONOUT$", "w", stdout); 3022 | setvbuf(stdout, NULL, _IONBF, 0); 3023 | } 3024 | 3025 | if (startinfo.hStdError != INVALID_HANDLE_VALUE) 3026 | { 3027 | freopen("CONOUT$", "w", stderr); 3028 | setvbuf(stderr, NULL, _IONBF, 0); 3029 | } 3030 | 3031 | startinfo.dwFlags &= ~STARTF_USEHOTKEY; 3032 | startinfo.dwFlags |= STARTF_USESTDHANDLES; 3033 | } 3034 | } 3035 | else 3036 | { 3037 | // Probably reached the command to execute portion of the arguments. 3038 | break; 3039 | } 3040 | } 3041 | 3042 | // Failed to find required executable. 3043 | if (x == argc) 3044 | { 3045 | #ifdef SUBSYSTEM_WINDOWS 3046 | InitVerboseMode(); 3047 | #endif 3048 | 3049 | _tprintf(_T("Error: 'EXEToRun' not specified.\n\n")); 3050 | DumpSyntax(argv[0]); 3051 | 3052 | return 1; 3053 | } 3054 | 3055 | // Application name. 3056 | appname = argv[x]; 3057 | 3058 | // Piece together the command-line. 3059 | size_t z = _tcslen(appname) + 2; 3060 | size_t z2, z3; 3061 | for (int y = x + 1; y < argc; y++) 3062 | { 3063 | z += _tcslen(argv[y]) + 3; 3064 | } 3065 | LPTSTR commandline = new TCHAR[z + 1]; 3066 | z = 0; 3067 | commandline[z] = _T('\"'); 3068 | z++; 3069 | z3 = _tcslen(appname); 3070 | for (z2 = 0; z2 < z3; z2++) 3071 | { 3072 | commandline[z] = appname[z2]; 3073 | z++; 3074 | } 3075 | commandline[z] = _T('\"'); 3076 | z++; 3077 | for (int y = x + 1; y < argc; y++) 3078 | { 3079 | commandline[z] = _T(' '); 3080 | z++; 3081 | 3082 | z3 = _tcslen(argv[y]); 3083 | for (z2 = 0; z2 < z3 && argv[y][z2] != _T(' '); z2++); 3084 | if (!z3 || z2 < z3) 3085 | { 3086 | commandline[z] = _T('\"'); 3087 | z++; 3088 | 3089 | for (z2 = 0; z2 < z3; z2++) 3090 | { 3091 | commandline[z] = argv[y][z2]; 3092 | z++; 3093 | } 3094 | 3095 | commandline[z] = _T('\"'); 3096 | z++; 3097 | } 3098 | else 3099 | { 3100 | for (z2 = 0; z2 < z3; z2++) 3101 | { 3102 | commandline[z] = argv[y][z2]; 3103 | z++; 3104 | } 3105 | } 3106 | } 3107 | commandline[z] = _T('\0'); 3108 | 3109 | createflags |= priorityflag; 3110 | if (startinfo.dwFillAttribute) startinfo.dwFlags |= STARTF_USEFILLATTRIBUTE; 3111 | if (startinfo.wShowWindow) startinfo.dwFlags |= STARTF_USESHOWWINDOW; 3112 | if (startinfo.hStdInput == INVALID_HANDLE_VALUE) 3113 | { 3114 | startinfo.hStdInput = NULL; 3115 | stdinstr = (TCHAR *)_T(":null"); 3116 | } 3117 | if (startinfo.hStdOutput == INVALID_HANDLE_VALUE) 3118 | { 3119 | startinfo.hStdOutput = NULL; 3120 | stdoutstr = (TCHAR *)_T(":null"); 3121 | } 3122 | if (startinfo.hStdError == INVALID_HANDLE_VALUE) 3123 | { 3124 | startinfo.hStdError = NULL; 3125 | stderrstr = (TCHAR *)_T(":null"); 3126 | } 3127 | 3128 | // Run verbose output. 3129 | if (verbose) 3130 | { 3131 | #ifdef SUBSYSTEM_WINDOWS 3132 | InitVerboseMode(); 3133 | #endif 3134 | 3135 | _tprintf(_T("Arguments:\n")); 3136 | for (x = 0; x < argc; x++) 3137 | { 3138 | _tprintf(_T("\targv[%d] = %s\n"), x, argv[x]); 3139 | } 3140 | _tprintf(_T("\n")); 3141 | _tprintf(_T("PID File = %s\n"), pidfile); 3142 | _tprintf(_T("\n")); 3143 | if (assigntoken == 0) _tprintf(_T("CreateProcessAsSYSTEM(\n")); 3144 | else if (assigntoken == 1) _tprintf(_T("CreateProcessAsElevated(\n")); 3145 | else if (assigntoken == 2) _tprintf(_T("CreateProcessWithCopiedToken(\n\ttokenopts = %s\n"), tokenopts); 3146 | else if (assigntoken == 3) _tprintf(_T("CreateProcessWithCustomToken(\n\ttokenopts = %s\n"), tokenopts); 3147 | else if (GxConnectPipePID) _tprintf(_T("CreateProcessElevated(\n")); 3148 | else _tprintf(_T("CreateProcess(\n")); 3149 | _tprintf(_T("\tlpApplicationName = %s,\n"), appname); 3150 | _tprintf(_T("\tlpCommandLine = %s,\n"), commandline); 3151 | _tprintf(_T("\tlpProcessAttributes = {\n")); 3152 | _tprintf(_T("\t\tnLength = %d,\n"), secattr.nLength); 3153 | _tprintf(_T("\t\tbInheritHandle = %s,\n"), (secattr.bInheritHandle ? _T("TRUE") : _T("FALSE"))); 3154 | _tprintf(_T("\t\tlpSecurityDescriptor = 0x%p,\n"), secattr.lpSecurityDescriptor); 3155 | _tprintf(_T("\t},\n")); 3156 | _tprintf(_T("\tlpThreadAttributes = {\n")); 3157 | _tprintf(_T("\t\tnLength = %d,\n"), secattr.nLength); 3158 | _tprintf(_T("\t\tbInheritHandle = %s,\n"), (secattr.bInheritHandle ? _T("TRUE") : _T("FALSE"))); 3159 | _tprintf(_T("\t\tlpSecurityDescriptor = 0x%p,\n"), secattr.lpSecurityDescriptor); 3160 | _tprintf(_T("\t},\n")); 3161 | _tprintf(_T("\tbInheritHandles = %s,\n"), (startinfo.dwFlags & STARTF_USESTDHANDLES ? _T("TRUE") : _T("FALSE"))); 3162 | _tprintf(_T("\tdwCreationFlags = 0")); 3163 | if (createflags & ABOVE_NORMAL_PRIORITY_CLASS) _tprintf(_T(" | ABOVE_NORMAL_PRIORITY_CLASS")); 3164 | if (createflags & BELOW_NORMAL_PRIORITY_CLASS) _tprintf(_T(" | BELOW_NORMAL_PRIORITY_CLASS")); 3165 | if (createflags & HIGH_PRIORITY_CLASS) _tprintf(_T(" | HIGH_PRIORITY_CLASS")); 3166 | if (createflags & IDLE_PRIORITY_CLASS) _tprintf(_T(" | IDLE_PRIORITY_CLASS")); 3167 | if (createflags & NORMAL_PRIORITY_CLASS) _tprintf(_T(" | NORMAL_PRIORITY_CLASS")); 3168 | if (createflags & REALTIME_PRIORITY_CLASS) _tprintf(_T(" | REALTIME_PRIORITY_CLASS")); 3169 | if (createflags & CREATE_BREAKAWAY_FROM_JOB) _tprintf(_T(" | CREATE_BREAKAWAY_FROM_JOB")); 3170 | if (createflags & CREATE_DEFAULT_ERROR_MODE) _tprintf(_T(" | CREATE_DEFAULT_ERROR_MODE")); 3171 | if (createflags & CREATE_NEW_CONSOLE) _tprintf(_T(" | CREATE_NEW_CONSOLE")); 3172 | if (createflags & CREATE_NEW_PROCESS_GROUP) _tprintf(_T(" | CREATE_NEW_PROCESS_GROUP")); 3173 | if (createflags & CREATE_NO_WINDOW) _tprintf(_T(" | CREATE_NO_WINDOW")); 3174 | if (createflags & CREATE_PROTECTED_PROCESS) _tprintf(_T(" | CREATE_PROTECTED_PROCESS")); 3175 | if (createflags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL) _tprintf(_T(" | CREATE_PRESERVE_CODE_AUTHZ_LEVEL")); 3176 | if (createflags & CREATE_SEPARATE_WOW_VDM) _tprintf(_T(" | CREATE_SEPARATE_WOW_VDM")); 3177 | if (createflags & CREATE_SHARED_WOW_VDM) _tprintf(_T(" | CREATE_SHARED_WOW_VDM")); 3178 | if (createflags & CREATE_SUSPENDED) _tprintf(_T(" | CREATE_SUSPENDED")); 3179 | if (createflags & CREATE_UNICODE_ENVIRONMENT) _tprintf(_T(" | CREATE_UNICODE_ENVIRONMENT")); 3180 | if (createflags & DEBUG_ONLY_THIS_PROCESS) _tprintf(_T(" | DEBUG_ONLY_THIS_PROCESS")); 3181 | if (createflags & DEBUG_PROCESS) _tprintf(_T(" | DEBUG_PROCESS")); 3182 | if (createflags & DETACHED_PROCESS) _tprintf(_T(" | DETACHED_PROCESS")); 3183 | if (createflags & EXTENDED_STARTUPINFO_PRESENT) _tprintf(_T(" | EXTENDED_STARTUPINFO_PRESENT")); 3184 | if (createflags & INHERIT_PARENT_AFFINITY) _tprintf(_T(" | INHERIT_PARENT_AFFINITY")); 3185 | _tprintf(_T(",\n")); 3186 | _tprintf(_T("\tlpEnvironment = 0x%p,\n"), (void *)NULL); 3187 | _tprintf(_T("\tlpCurrentDirectory = %s,\n"), startdir); 3188 | _tprintf(_T("\tlpStartupInfo = {\n")); 3189 | _tprintf(_T("\t\tcb = %d,\n"), startinfo.cb); 3190 | _tprintf(_T("\t\tlpReserved = %s,\n"), startinfo.lpReserved); 3191 | _tprintf(_T("\t\tlpDesktop = %s,\n"), startinfo.lpDesktop); 3192 | _tprintf(_T("\t\tlpTitle = %s,\n"), startinfo.lpTitle); 3193 | _tprintf(_T("\t\tdwX = %u,\n"), startinfo.dwX); 3194 | _tprintf(_T("\t\tdwY = %u,\n"), startinfo.dwY); 3195 | _tprintf(_T("\t\tdwXSize = %u,\n"), startinfo.dwXSize); 3196 | _tprintf(_T("\t\tdwYSize = %u,\n"), startinfo.dwYSize); 3197 | _tprintf(_T("\t\tdwXCountChars = %u,\n"), startinfo.dwXCountChars); 3198 | _tprintf(_T("\t\tdwYCountChars = %u,\n"), startinfo.dwYCountChars); 3199 | _tprintf(_T("\t\tdwFillAttribute = 0")); 3200 | if (startinfo.dwFillAttribute & FOREGROUND_RED) _tprintf(_T(" | FOREGROUND_RED")); 3201 | if (startinfo.dwFillAttribute & FOREGROUND_GREEN) _tprintf(_T(" | FOREGROUND_GREEN")); 3202 | if (startinfo.dwFillAttribute & FOREGROUND_BLUE) _tprintf(_T(" | FOREGROUND_BLUE")); 3203 | if (startinfo.dwFillAttribute & FOREGROUND_INTENSITY) _tprintf(_T(" | FOREGROUND_INTENSITY")); 3204 | if (startinfo.dwFillAttribute & BACKGROUND_RED) _tprintf(_T(" | BACKGROUND_RED")); 3205 | if (startinfo.dwFillAttribute & BACKGROUND_GREEN) _tprintf(_T(" | BACKGROUND_GREEN")); 3206 | if (startinfo.dwFillAttribute & BACKGROUND_BLUE) _tprintf(_T(" | BACKGROUND_BLUE")); 3207 | if (startinfo.dwFillAttribute & BACKGROUND_INTENSITY) _tprintf(_T(" | BACKGROUND_INTENSITY")); 3208 | _tprintf(_T(",\n")); 3209 | _tprintf(_T("\t\tdwFlags = 0")); 3210 | if (startinfo.dwFlags & STARTF_FORCEONFEEDBACK) _tprintf(_T(" | STARTF_FORCEONFEEDBACK")); 3211 | if (startinfo.dwFlags & STARTF_FORCEOFFFEEDBACK) _tprintf(_T(" | STARTF_FORCEOFFFEEDBACK")); 3212 | if (startinfo.dwFlags & STARTF_PREVENTPINNING) _tprintf(_T(" | STARTF_PREVENTPINNING")); 3213 | if (startinfo.dwFlags & STARTF_RUNFULLSCREEN) _tprintf(_T(" | STARTF_RUNFULLSCREEN")); 3214 | if (startinfo.dwFlags & STARTF_TITLEISAPPID) _tprintf(_T(" | STARTF_TITLEISAPPID")); 3215 | if (startinfo.dwFlags & STARTF_TITLEISLINKNAME) _tprintf(_T(" | STARTF_TITLEISLINKNAME")); 3216 | if (startinfo.dwFlags & STARTF_USECOUNTCHARS) _tprintf(_T(" | STARTF_USECOUNTCHARS")); 3217 | if (startinfo.dwFlags & STARTF_USEFILLATTRIBUTE) _tprintf(_T(" | STARTF_USEFILLATTRIBUTE")); 3218 | if (startinfo.dwFlags & STARTF_USEHOTKEY) _tprintf(_T(" | STARTF_USEHOTKEY")); 3219 | if (startinfo.dwFlags & STARTF_USEPOSITION) _tprintf(_T(" | STARTF_USEPOSITION")); 3220 | if (startinfo.dwFlags & STARTF_USESHOWWINDOW) _tprintf(_T(" | STARTF_USESHOWWINDOW")); 3221 | if (startinfo.dwFlags & STARTF_USESIZE) _tprintf(_T(" | STARTF_USESIZE")); 3222 | if (startinfo.dwFlags & STARTF_USESTDHANDLES) _tprintf(_T(" | STARTF_USESTDHANDLES")); 3223 | _tprintf(_T(",\n")); 3224 | _tprintf(_T("\t\twShowWindow = ")); 3225 | if (!(startinfo.dwFlags & STARTF_USESHOWWINDOW)) _tprintf(_T("(Unused)")); 3226 | else if (startinfo.wShowWindow == SW_FORCEMINIMIZE) _tprintf(_T("SW_FORCEMINIMIZE")); 3227 | else if (startinfo.wShowWindow == SW_HIDE) _tprintf(_T("SW_HIDE")); 3228 | else if (startinfo.wShowWindow == SW_MAXIMIZE) _tprintf(_T("SW_MAXIMIZE")); 3229 | else if (startinfo.wShowWindow == SW_MINIMIZE) _tprintf(_T("SW_MINIMIZE")); 3230 | else if (startinfo.wShowWindow == SW_RESTORE) _tprintf(_T("SW_RESTORE")); 3231 | else if (startinfo.wShowWindow == SW_SHOW) _tprintf(_T("SW_SHOW")); 3232 | else if (startinfo.wShowWindow == SW_SHOWDEFAULT) _tprintf(_T("SW_SHOWDEFAULT")); 3233 | else if (startinfo.wShowWindow == SW_SHOWMAXIMIZED) _tprintf(_T("SW_SHOWMAXIMIZED")); 3234 | else if (startinfo.wShowWindow == SW_SHOWMINIMIZED) _tprintf(_T("SW_SHOWMINIMIZED")); 3235 | else if (startinfo.wShowWindow == SW_SHOWMINNOACTIVE) _tprintf(_T("SW_SHOWMINNOACTIVE")); 3236 | else if (startinfo.wShowWindow == SW_SHOWNA) _tprintf(_T("SW_SHOWNA")); 3237 | else if (startinfo.wShowWindow == SW_SHOWNOACTIVATE) _tprintf(_T("SW_SHOWNOACTIVATE")); 3238 | else if (startinfo.wShowWindow == SW_SHOWNORMAL) _tprintf(_T("SW_SHOWNORMAL")); 3239 | else _tprintf(_T("(Unknown/Other)")); 3240 | _tprintf(_T(",\n")); 3241 | _tprintf(_T("\t\tcbReserved2 = %u,\n"), (DWORD)startinfo.cbReserved2); 3242 | _tprintf(_T("\t\tlpReserved2 = 0x%p,\n"), startinfo.lpReserved2); 3243 | _tprintf(_T("\t\thStdInput = %s,\n"), stdinstr); 3244 | _tprintf(_T("\t\thStdOutput = %s,\n"), stdoutstr); 3245 | _tprintf(_T("\t\thStdError = %s\n"), stderrstr); 3246 | _tprintf(_T("\t}\n")); 3247 | _tprintf(_T(");\n")); 3248 | } 3249 | 3250 | // Prepare to assign a user token to the child process. 3251 | HANDLE commpipehandle = INVALID_HANDLE_VALUE; 3252 | HANDLE maintoken = INVALID_HANDLE_VALUE; 3253 | int envc2 = 0; 3254 | WCHAR *envpbuffer = NULL; 3255 | WCHAR **envp2 = NULL; 3256 | if (assigntoken > -1) 3257 | { 3258 | // The process must be started suspended in order for the specific user token to be assigned. 3259 | // After the process resumes, the process token is locked and can't be changed. 3260 | createflags |= CREATE_SUSPENDED; 3261 | 3262 | // If this is already SYSTEM, then handle directly. 3263 | x2 = IsSystemProcess(); 3264 | if (x2 < 0) return 1; 3265 | 3266 | if (x2 == 1) 3267 | { 3268 | // A quick debugger hook. 3269 | #ifdef _DEBUG 3270 | while (!::IsDebuggerPresent()) ::Sleep(500); 3271 | ::Sleep(2000); 3272 | #endif 3273 | 3274 | // Enable necessary privileges. 3275 | if (!EnableSystemPrivileges()) return 1; 3276 | 3277 | // Load the primary token. Requires SeDebugPrivilege. 3278 | // The undocumented NtCreateToken() API requires SeCreateTokenPrivilege. 3279 | if (assigntoken == 0 || assigntoken == 1) maintoken = GetTokenFromPID(::GetCurrentProcessId(), TokenPrimary); 3280 | else if (assigntoken == 2) maintoken = FindExistingTokenFromOpts(tokenopts, TokenPrimary); 3281 | else if (assigntoken == 3) maintoken = CreateTokenFromOpts(tokenopts, true); 3282 | 3283 | if (maintoken == INVALID_HANDLE_VALUE) return 1; 3284 | 3285 | // Load the user profile associated with the token. 3286 | if (!LoadRealUserProfile(maintoken, tempprofile)) 3287 | { 3288 | ::CloseHandle(maintoken); 3289 | 3290 | return 1; 3291 | } 3292 | 3293 | // Unfortunately, since we are now responsible for refcounting the user profile within this process, 3294 | // we may need to leak the profile handle if the process is being started/run in the background. 3295 | // There are no workarounds. User profile management should be done within the target process. Way to go, Microsoft! 3296 | if (!wait || (waitamount != INFINITE && !terminate)) tempprofile.hProfile = NULL; 3297 | 3298 | // Generate an environment block for the user. 3299 | LPVOID tempenv = NULL; 3300 | 3301 | if (!::CreateEnvironmentBlock(&tempenv, maintoken, FALSE)) 3302 | { 3303 | DumpErrorMsg("Creating the environment block for the user token failed.", "create_environment_block_failed", ::GetLastError()); 3304 | 3305 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 3306 | 3307 | ::CloseHandle(maintoken); 3308 | 3309 | return 1; 3310 | } 3311 | 3312 | // Convert to envp2. 3313 | envpbuffer = (WCHAR *)tempenv; 3314 | for (x2 = 0; envpbuffer[x2]; x2++) 3315 | { 3316 | while (envpbuffer[x2]) x2++; 3317 | } 3318 | 3319 | envpbuffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, (x2 + 1) * sizeof(WCHAR)); 3320 | if (envpbuffer == NULL) 3321 | { 3322 | DumpErrorMsg("Pointer buffer allocation failed.", "heap_alloc_failed", ::GetLastError()); 3323 | 3324 | ::DestroyEnvironmentBlock(tempenv); 3325 | 3326 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 3327 | 3328 | ::CloseHandle(maintoken); 3329 | 3330 | return 1; 3331 | } 3332 | 3333 | memcpy(envpbuffer, tempenv, (x2 + 1) * sizeof(WCHAR)); 3334 | 3335 | ::DestroyEnvironmentBlock(tempenv); 3336 | 3337 | if (!MakeStringArrayFromString(envpbuffer, x2 + 1, envp2, envc2)) return 1; 3338 | } 3339 | else 3340 | { 3341 | // Start the named pipe server. 3342 | commpipehandle = StartMainCommPipeServer(); 3343 | if (commpipehandle == INVALID_HANDLE_VALUE) 3344 | { 3345 | DumpErrorMsg("Unable to create the main communication pipe.", "start_main_comm_pipe_failed", ::GetLastError()); 3346 | 3347 | return 1; 3348 | } 3349 | 3350 | HANDLE commpipeevent = CreateCommPipeConnectEventObj(::GetCurrentProcessId()); 3351 | if (commpipeevent == NULL) 3352 | { 3353 | DumpErrorMsg("Unable to create pipe server event object.", "create_event_failed", ::GetLastError()); 3354 | 3355 | return 1; 3356 | } 3357 | 3358 | // Elevate (if not already) and create the system service. 3359 | x2 = IsElevatedProcess(); 3360 | if (x2 < 0) return 1; 3361 | 3362 | if (x2 == 0) 3363 | { 3364 | if (!ElevateSelf(true)) return 1; 3365 | } 3366 | else 3367 | { 3368 | if (!SystemSelf(::GetCurrentProcessId())) return 1; 3369 | } 3370 | 3371 | // Wait for the client to connect. 3372 | #ifdef _DEBUG 3373 | if (!WaitForValidCommPipeClient(commpipehandle, commpipeevent, INFINITE)) return 1; 3374 | #else 3375 | if (!WaitForValidCommPipeClient(commpipehandle, commpipeevent, 35000)) return 1; 3376 | #endif 3377 | 3378 | // Wait for the system service to initialize. 3379 | if (!WaitForCommPipeStatus(commpipehandle)) return 1; 3380 | 3381 | // Send the token type. 3382 | ::WriteFile(commpipehandle, &assigntoken, 1, NULL, NULL); 3383 | 3384 | // Send token options (if any). 3385 | if (assigntoken == 2 || assigntoken == 3) 3386 | { 3387 | CubicleSoft::StaticWCMixedVar TempVar; 3388 | DWORD tempsize; 3389 | 3390 | TempVar.SetStr(tokenopts); 3391 | tempsize = (TempVar.GetSize() + 1) * sizeof(WCHAR); 3392 | ::WriteFile(commpipehandle, &tempsize, 4, NULL, NULL); 3393 | ::WriteFile(commpipehandle, TempVar.GetStr(), tempsize, NULL, NULL); 3394 | } 3395 | 3396 | // Wait for the system service to select or create the token, load the user profile, and get an environment for the user. 3397 | if (!WaitForCommPipeStatus(commpipehandle)) return 1; 3398 | 3399 | // Get the environment. 3400 | if (!ReadCommPipeStringArray(commpipehandle, envpbuffer, envp2, envc2)) return 1; 3401 | } 3402 | 3403 | // Merge the current environment with the user environment. 3404 | // User environment variables take precedence. 3405 | if (mergeenv) 3406 | { 3407 | CubicleSoft::PackedOrderedHash TempHash(32); 3408 | CubicleSoft::PackedOrderedHashNode *HashNode; 3409 | 3410 | // Copy local process variables. 3411 | for (x = 0; envp[x]; x++) 3412 | { 3413 | for (x2 = 0; envp[x][x2] && envp[x][x2] != L'='; x2++); 3414 | 3415 | // Keep special cmd.exe pseudo-variables (current directory path per drive). 3416 | if (!x2 && envp[x][0] == L'=' && envp[x][1] >= L'A' && envp[x][1] <= L'Z' && envp[x][2] == L':' && envp[x][3] == L'=') x2 = 3; 3417 | 3418 | TempHash.Set((char *)envp[x], x2 * sizeof(WCHAR), envp[x] + x2); 3419 | } 3420 | 3421 | // Copy user variables. Replaces existing values. 3422 | for (x = 0; envp2[x]; x++) 3423 | { 3424 | for (x2 = 0; envp2[x][x2] && envp2[x][x2] != L'='; x2++); 3425 | 3426 | // Keep special cmd.exe pseudo-variables (current directory path per drive). 3427 | if (!x2 && envp2[x][0] == L'=' && envp2[x][1] >= L'A' && envp2[x][1] <= L'Z' && envp2[x][2] == L':' && envp2[x][3] == L'=') x2 = 3; 3428 | 3429 | TempHash.Set((char *)envp2[x], x2 * sizeof(WCHAR), envp2[x] + x2); 3430 | } 3431 | 3432 | // Calculate the size of the buffer needed. 3433 | DWORD tempsize = sizeof(WCHAR); 3434 | size_t hashpos = TempHash.GetNextPos(); 3435 | while ((HashNode = TempHash.Next(hashpos)) != NULL) 3436 | { 3437 | tempsize += HashNode->GetStrLen() + ((wcslen(HashNode->Value) + 1) * sizeof(WCHAR)); 3438 | } 3439 | 3440 | WCHAR *tempenvpbuffer; 3441 | 3442 | tempenvpbuffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, tempsize); 3443 | if (tempenvpbuffer == NULL) 3444 | { 3445 | DumpErrorMsg("Pointer buffer allocation failed.", "heap_alloc_failed", ::GetLastError()); 3446 | 3447 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 3448 | 3449 | ::CloseHandle(maintoken); 3450 | 3451 | return 1; 3452 | } 3453 | 3454 | // Copy the variables to the buffer. 3455 | x = 0; 3456 | hashpos = TempHash.GetNextPos(); 3457 | while ((HashNode = TempHash.Next(hashpos)) != NULL) 3458 | { 3459 | x2 = (int)HashNode->GetStrLen(); 3460 | memcpy(tempenvpbuffer + (x / sizeof(WCHAR)), HashNode->GetStrKey(), x2); 3461 | x += x2; 3462 | 3463 | x2 = (int)((wcslen(HashNode->Value) + 1) * sizeof(WCHAR)); 3464 | memcpy(tempenvpbuffer + (x / sizeof(WCHAR)), HashNode->Value, x2); 3465 | x += x2; 3466 | } 3467 | 3468 | // Replace envp2. 3469 | FreeCommPipeStringArray(envpbuffer, envp2); 3470 | 3471 | envpbuffer = tempenvpbuffer; 3472 | if (!MakeStringArrayFromString(envpbuffer, tempsize / sizeof(WCHAR), envp2, envc2)) return 1; 3473 | } 3474 | } 3475 | 3476 | // Execute CreateProcess(). 3477 | BOOL result = ::CreateProcess(appname, commandline, &secattr, &secattr, (startinfo.dwFlags & STARTF_USESTDHANDLES ? TRUE : FALSE), createflags, (envp2 != NULL ? *envp2 : *envp), startdir, &startinfo, &procinfo); 3478 | 3479 | FreeCommPipeStringArray(envpbuffer, envp2); 3480 | 3481 | if (!result) 3482 | { 3483 | winerror = ::GetLastError(); 3484 | 3485 | if (winerror == ERROR_ELEVATION_REQUIRED) _tprintf(_T("'%s' must be run as Administrator.\n\n"), appname); 3486 | 3487 | _tprintf(_T("lpApplicationName = %s\n"), appname); 3488 | _tprintf(_T("lpCommandLine = %s\n\n"), commandline); 3489 | 3490 | DumpErrorMsg("An error occurred while attempting to start the process.", "create_process_failed", winerror); 3491 | 3492 | exitcode = 1; 3493 | } 3494 | else 3495 | { 3496 | // Assign user token. 3497 | if (assigntoken > -1) 3498 | { 3499 | // This process only has a token if it is running as SYSTEM. 3500 | if (maintoken != INVALID_HANDLE_VALUE) 3501 | { 3502 | // Assign the primary token to the process. 3503 | if (!AssignPrimaryTokenToProcess(procinfo.hProcess, maintoken)) 3504 | { 3505 | ::CloseHandle(procinfo.hThread); 3506 | 3507 | ::TerminateProcess(procinfo.hProcess, 1); 3508 | ::CloseHandle(procinfo.hProcess); 3509 | 3510 | // Unload the user profile. 3511 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 3512 | 3513 | ::CloseHandle(maintoken); 3514 | 3515 | return 1; 3516 | } 3517 | } 3518 | else 3519 | { 3520 | // Send the system service the process ID of the newly created, suspended process. 3521 | ::WriteFile(commpipehandle, &procinfo.dwProcessId, 4, NULL, NULL); 3522 | 3523 | // Wait for the system service to assign the token to the process. 3524 | if (!WaitForCommPipeStatus(commpipehandle)) 3525 | { 3526 | ::CloseHandle(procinfo.hThread); 3527 | 3528 | ::TerminateProcess(procinfo.hProcess, 1); 3529 | ::CloseHandle(procinfo.hProcess); 3530 | 3531 | return 1; 3532 | } 3533 | 3534 | ::CloseHandle(commpipehandle); 3535 | } 3536 | 3537 | // Start the main thread. 3538 | ::ResumeThread(procinfo.hThread); 3539 | } 3540 | 3541 | if (pidfile != NULL) 3542 | { 3543 | char pidbuffer[65]; 3544 | _itoa(procinfo.dwProcessId, pidbuffer, 10); 3545 | HANDLE hpidfile = ::CreateFile(pidfile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secattr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 3546 | if (hpidfile == INVALID_HANDLE_VALUE) 3547 | { 3548 | #ifdef SUBSYSTEM_WINDOWS 3549 | InitVerboseMode(); 3550 | #endif 3551 | 3552 | _tprintf(_T("PID file '%s' was unable to be opened.\n"), pidfile); 3553 | } 3554 | else 3555 | { 3556 | ::WriteFile(hpidfile, pidbuffer, (DWORD)strlen(pidbuffer), NULL, NULL); 3557 | ::CloseHandle(hpidfile); 3558 | } 3559 | } 3560 | 3561 | ::CloseHandle(procinfo.hThread); 3562 | if (wait) 3563 | { 3564 | // Wait for process to complete. 3565 | if (verbose) 3566 | { 3567 | if (waitamount == INFINITE) _tprintf(_T("Waiting for process to complete...\n")); 3568 | else _tprintf(_T("Waiting for process to complete (%ims)...\n"), waitamount); 3569 | } 3570 | 3571 | // If socket handles are used, start relevant threads to pass the data around. 3572 | if (Gx_stdinsocket != INVALID_SOCKET && stdinread != NULL) 3573 | { 3574 | ::CloseHandle(stdinread); 3575 | stdinread = NULL; 3576 | 3577 | stdinthread = ::CreateThread(NULL, 0, StdinSocketHandler, &startinfo, 0, NULL); 3578 | 3579 | if (stdinthread == NULL) 3580 | { 3581 | #ifdef SUBSYSTEM_WINDOWS 3582 | InitVerboseMode(); 3583 | #endif 3584 | 3585 | _tprintf(_T("The 'stdin' socket handler thread failed to start.\n")); 3586 | } 3587 | } 3588 | 3589 | if (Gx_stdoutsocket != INVALID_SOCKET && stdoutwrite != NULL) 3590 | { 3591 | ::CloseHandle(stdoutwrite); 3592 | stdoutwrite = NULL; 3593 | 3594 | stdoutthread = ::CreateThread(NULL, 0, StdoutSocketHandler, &startinfo, 0, NULL); 3595 | 3596 | if (stdoutthread == NULL) 3597 | { 3598 | #ifdef SUBSYSTEM_WINDOWS 3599 | InitVerboseMode(); 3600 | #endif 3601 | 3602 | _tprintf(_T("The 'stdout' socket handler thread failed to start.\n")); 3603 | } 3604 | } 3605 | 3606 | if (Gx_stderrsocket != INVALID_SOCKET && stderrwrite != NULL) 3607 | { 3608 | ::CloseHandle(stderrwrite); 3609 | stderrwrite = NULL; 3610 | 3611 | stderrthread = ::CreateThread(NULL, 0, StderrSocketHandler, &startinfo, 0, NULL); 3612 | 3613 | if (stderrthread == NULL) 3614 | { 3615 | #ifdef SUBSYSTEM_WINDOWS 3616 | InitVerboseMode(); 3617 | #endif 3618 | 3619 | _tprintf(_T("The 'stderr' socket handler thread failed to start.\n")); 3620 | } 3621 | } 3622 | 3623 | if (::WaitForSingleObject(procinfo.hProcess, waitamount) == WAIT_OBJECT_0) 3624 | { 3625 | if (!::GetExitCodeProcess(procinfo.hProcess, &exitcode)) exitcode = 0; 3626 | } 3627 | else if (terminate) 3628 | { 3629 | if (verbose) _tprintf(_T("Timed out. Terminating.\n")); 3630 | ::TerminateProcess(procinfo.hProcess, 1); 3631 | if (!::GetExitCodeProcess(procinfo.hProcess, &exitcode)) exitcode = 0; 3632 | } 3633 | else 3634 | { 3635 | // The child process is still running but this process should exit. Get all threads to terminate. 3636 | if (Gx_stdinsocket != INVALID_SOCKET) ::closesocket(Gx_stdinsocket); 3637 | if (Gx_stdoutsocket != INVALID_SOCKET) ::closesocket(Gx_stdoutsocket); 3638 | if (Gx_stderrsocket != INVALID_SOCKET) ::closesocket(Gx_stderrsocket); 3639 | 3640 | Gx_stdinsocket = INVALID_SOCKET; 3641 | Gx_stdoutsocket = INVALID_SOCKET; 3642 | Gx_stderrsocket = INVALID_SOCKET; 3643 | 3644 | if (Gx_stdinwritehandle != NULL) ::CloseHandle(Gx_stdinwritehandle); 3645 | if (Gx_stdoutreadhandle != NULL) ::CloseHandle(Gx_stdoutreadhandle); 3646 | if (Gx_stderrreadhandle != NULL) ::CloseHandle(Gx_stderrreadhandle); 3647 | 3648 | Gx_stdinwritehandle = NULL; 3649 | Gx_stdoutreadhandle = NULL; 3650 | Gx_stderrreadhandle = NULL; 3651 | } 3652 | } 3653 | 3654 | ::CloseHandle(procinfo.hProcess); 3655 | } 3656 | 3657 | delete[] commandline; 3658 | 3659 | if (GxNetworkStarted) 3660 | { 3661 | // Wait for the threads to finish. 3662 | if (stdinthread != NULL && ::WaitForSingleObject(stdinthread, 0) != WAIT_OBJECT_0) ::TerminateThread(stdinthread, INFINITE); 3663 | if (stdoutthread != NULL) ::WaitForSingleObject(stdoutthread, INFINITE); 3664 | if (stderrthread != NULL) ::WaitForSingleObject(stderrthread, INFINITE); 3665 | 3666 | if (Gx_stdinsocket != INVALID_SOCKET) ::closesocket(Gx_stdinsocket); 3667 | if (Gx_stdoutsocket != INVALID_SOCKET) ::closesocket(Gx_stdoutsocket); 3668 | if (Gx_stderrsocket != INVALID_SOCKET) ::closesocket(Gx_stderrsocket); 3669 | 3670 | ::WSACleanup(); 3671 | } 3672 | 3673 | if (maintoken != INVALID_HANDLE_VALUE) 3674 | { 3675 | // Unload the user profile. 3676 | if (tempprofile.hProfile != NULL) ::UnloadUserProfile(maintoken, tempprofile.hProfile); 3677 | 3678 | ::CloseHandle(maintoken); 3679 | } 3680 | 3681 | if (mutexhandle != NULL) 3682 | { 3683 | ::ReleaseMutex(mutexhandle); 3684 | ::CloseHandle(mutexhandle); 3685 | } 3686 | 3687 | if (semaphorehandle != NULL) 3688 | { 3689 | ::ReleaseSemaphore(semaphorehandle, 1, NULL); 3690 | ::CloseHandle(semaphorehandle); 3691 | } 3692 | 3693 | // Let the OS clean up after this program. It is lazy, but whatever. 3694 | if (verbose) _tprintf(_T("Return code = %i\n"), (int)exitcode); 3695 | 3696 | return (int)exitcode; 3697 | } 3698 | 3699 | // Need to rebuild the environment variables. 3700 | int _tmain(int argc, TCHAR **argv, TCHAR **envp) 3701 | { 3702 | int envc2; 3703 | TCHAR **envp2; 3704 | int result; 3705 | 3706 | LPWCH tempenv = ::GetEnvironmentStrings(); 3707 | int x2; 3708 | 3709 | // Convert to envp. 3710 | WCHAR *envpbuffer = (WCHAR *)tempenv; 3711 | for (x2 = 0; envpbuffer[x2]; x2++) 3712 | { 3713 | while (envpbuffer[x2]) x2++; 3714 | } 3715 | 3716 | envpbuffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, (x2 + 1) * sizeof(WCHAR)); 3717 | if (envpbuffer == NULL) return 1; 3718 | 3719 | memcpy(envpbuffer, tempenv, (x2 + 1) * sizeof(WCHAR)); 3720 | 3721 | ::FreeEnvironmentStrings(tempenv); 3722 | 3723 | if (!MakeStringArrayFromString(envpbuffer, x2 + 1, envp2, envc2)) return 1; 3724 | 3725 | result = MainInternal(argc, argv, envp2); 3726 | 3727 | FreeCommPipeStringArray(envpbuffer, envp2); 3728 | 3729 | return result; 3730 | } 3731 | 3732 | #ifdef SUBSYSTEM_WINDOWS 3733 | #ifndef UNICODE 3734 | // Swiped from: https://stackoverflow.com/questions/291424/canonical-way-to-parse-the-command-line-into-arguments-in-plain-c-windows-api 3735 | LPSTR* CommandLineToArgvA(LPSTR lpCmdLine, INT *pNumArgs) 3736 | { 3737 | int retval; 3738 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, NULL, 0); 3739 | if (!SUCCEEDED(retval)) return NULL; 3740 | 3741 | LPWSTR lpWideCharStr = (LPWSTR)malloc(retval * sizeof(WCHAR)); 3742 | if (lpWideCharStr == NULL) return NULL; 3743 | 3744 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, lpWideCharStr, retval); 3745 | if (!SUCCEEDED(retval)) 3746 | { 3747 | free(lpWideCharStr); 3748 | 3749 | return NULL; 3750 | } 3751 | 3752 | int numArgs; 3753 | LPWSTR *args; 3754 | args = ::CommandLineToArgvW(lpWideCharStr, &numArgs); 3755 | free(lpWideCharStr); 3756 | if (args == NULL) return NULL; 3757 | 3758 | int storage = numArgs * sizeof(LPSTR); 3759 | for (int i = 0; i < numArgs; i++) 3760 | { 3761 | BOOL lpUsedDefaultChar = FALSE; 3762 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, NULL, 0, NULL, &lpUsedDefaultChar); 3763 | if (!SUCCEEDED(retval)) 3764 | { 3765 | ::LocalFree(args); 3766 | 3767 | return NULL; 3768 | } 3769 | 3770 | storage += retval; 3771 | } 3772 | 3773 | LPSTR* result = (LPSTR *)::LocalAlloc(LMEM_FIXED, storage); 3774 | if (result == NULL) 3775 | { 3776 | ::LocalFree(args); 3777 | 3778 | return NULL; 3779 | } 3780 | 3781 | int bufLen = storage - numArgs * sizeof(LPSTR); 3782 | LPSTR buffer = ((LPSTR)result) + numArgs * sizeof(LPSTR); 3783 | for (int i = 0; i < numArgs; ++ i) 3784 | { 3785 | BOOL lpUsedDefaultChar = FALSE; 3786 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, buffer, bufLen, NULL, &lpUsedDefaultChar); 3787 | if (!SUCCEEDED(retval)) 3788 | { 3789 | ::LocalFree(result); 3790 | ::LocalFree(args); 3791 | 3792 | return NULL; 3793 | } 3794 | 3795 | result[i] = buffer; 3796 | buffer += retval; 3797 | bufLen -= retval; 3798 | } 3799 | 3800 | ::LocalFree(args); 3801 | 3802 | *pNumArgs = numArgs; 3803 | 3804 | return result; 3805 | } 3806 | #endif 3807 | 3808 | int CALLBACK WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR lpCmdLine, int /* nCmdShow */) 3809 | { 3810 | int argc, envc; 3811 | TCHAR **argv, **envp; 3812 | int result; 3813 | 3814 | #ifdef UNICODE 3815 | argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 3816 | #else 3817 | argv = CommandLineToArgvA(lpCmdLine, &argc); 3818 | #endif 3819 | 3820 | if (argv == NULL) return 1; 3821 | 3822 | LPWCH tempenv = ::GetEnvironmentStrings(); 3823 | int x2; 3824 | 3825 | // Convert to envp. 3826 | WCHAR *envpbuffer = (WCHAR *)tempenv; 3827 | for (x2 = 0; envpbuffer[x2]; x2++) 3828 | { 3829 | while (envpbuffer[x2]) x2++; 3830 | } 3831 | 3832 | envpbuffer = (WCHAR *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, (x2 + 1) * sizeof(WCHAR)); 3833 | if (envpbuffer == NULL) return 1; 3834 | 3835 | memcpy(envpbuffer, tempenv, (x2 + 1) * sizeof(WCHAR)); 3836 | 3837 | ::FreeEnvironmentStrings(tempenv); 3838 | 3839 | if (!MakeStringArrayFromString(envpbuffer, x2 + 1, envp, envc)) return 1; 3840 | 3841 | result = MainInternal(argc, argv, envp); 3842 | 3843 | ::LocalFree(argv); 3844 | 3845 | FreeCommPipeStringArray(envpbuffer, envp); 3846 | 3847 | return result; 3848 | } 3849 | #endif 3850 | --------------------------------------------------------------------------------