├── .gitignore ├── README.txt ├── deploy ├── README.md ├── config_acl.ps1 ├── ctf │ ├── banner.txt │ └── wctf.exe ├── deploy.bat └── wctf.reg ├── distrib ├── config_acl.ps1 ├── deploy.bat ├── kernel32.dll ├── ntdll.dll ├── wctf.exe └── wctf.reg ├── solution ├── README.txt ├── cleanup.exe └── x.py ├── wctf.sln └── wctf ├── BinaryStream.cpp ├── BinaryStream.h ├── Common.cpp ├── Common.h ├── Crypto.cpp ├── Crypto.h ├── Game.cpp ├── Game.h ├── IPC.cpp ├── IPC.h ├── LogFile.cpp ├── LogFile.h ├── Sandbox.cpp ├── Sandbox.h ├── Service.cpp ├── Service.h ├── SocketServer.cpp ├── SocketServer.h ├── ntdll.lib ├── stdafx.cpp ├── stdafx.h ├── targetver.h ├── wctf.cpp ├── wctf.vcxproj └── wctf.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | deploy\ - everything used to deploy the server, see deploy\README.md for details 2 | distrib\ - contestant accessible attachments 3 | solution\ - exploit, tested under Windows 10 x64 1803 (17134.112) -------------------------------------------------------------------------------- /deploy/README.md: -------------------------------------------------------------------------------- 1 | ## Deployment guide 2 | 3 | * Copy the files under `ctf\` to `C:\ctf\` 4 | * Spawn a terminal with administrator rights, run `deploy.bat` (make sure everything is under current directory) 5 | * Open `regedit.exe`, goto `HKLM\Software\WCTF\`, inspect the ACL of the key. You should find all access from `AppContainer` sandboxed application to the key are denied. 6 | * To change the flag: edit the `flag` value in `wctf.reg`, you can find a placeholder there. -------------------------------------------------------------------------------- /deploy/config_acl.ps1: -------------------------------------------------------------------------------- 1 | # Secure access so attackers can't extract the secret from registry even if they have remote code execution :p 2 | $acl = Get-Acl HKLM:\SOFTWARE\WCTF\ 3 | $sddl = "O:BAG:S-1-5-21-4269827912-1333813219-4110619441-513D:PAI(D;CI;RP;;;AC)(D;CI;RP;;;S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681)(A;CIIO;KA;;;CO)(A;CI;KA;;;SY)(A;CI;KA;;;BA)(A;CI;KR;;;BU)" 4 | $acl.SetSecurityDescriptorSddlForm($sddl) 5 | Set-Acl -Path HKLM:\SOFTWARE\WCTF\ -AclObject $acl 6 | -------------------------------------------------------------------------------- /deploy/ctf/banner.txt: -------------------------------------------------------------------------------- 1 | # # ##### ####### ####### ##### 2 | # # # # # # # # # ## # # ###### 3 | # # # # # # # # # ## ## # 4 | # # # # # ##### # #### # # # ## # ##### 5 | # # # # # # # # ###### # # # 6 | # # # # # # # # # # # # # # 7 | ## ## ##### # # ##### # # # # ###### 8 | -------------------------------------------------------------------------------- /deploy/ctf/wctf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/deploy/ctf/wctf.exe -------------------------------------------------------------------------------- /deploy/deploy.bat: -------------------------------------------------------------------------------- 1 | REM Run this batch file as administrator 2 | 3 | REM Add ctf user 4 | net user test "this_is_a_useless_user_lulwat!" /add 5 | 6 | REM Configure firewall policy 7 | netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound 8 | netsh advfirewall firewall add rule name="chalservice" dir=in action=allow protocol=TCP localport=13337 9 | 10 | REM Configure registry 11 | reg import wctf.reg 12 | powershell .\config_acl.ps1 13 | 14 | REM Install & run the service 15 | C:\ctf\wctf.exe install 16 | net start wctf 17 | -------------------------------------------------------------------------------- /deploy/wctf.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/deploy/wctf.reg -------------------------------------------------------------------------------- /distrib/config_acl.ps1: -------------------------------------------------------------------------------- 1 | # Secure access so attackers can't extract the secret from registry even if they have remote code execution :p 2 | $acl = Get-Acl HKLM:\SOFTWARE\WCTF\ 3 | $sddl = "O:BAG:S-1-5-21-4269827912-1333813219-4110619441-513D:PAI(D;CI;RP;;;AC)(D;CI;RP;;;S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681)(A;CIIO;KA;;;CO)(A;CI;KA;;;SY)(A;CI;KA;;;BA)(A;CI;KR;;;BU)" 4 | $acl.SetSecurityDescriptorSddlForm($sddl) 5 | Set-Acl -Path HKLM:\SOFTWARE\WCTF\ -AclObject $acl 6 | -------------------------------------------------------------------------------- /distrib/deploy.bat: -------------------------------------------------------------------------------- 1 | REM Run this batch file as administrator 2 | 3 | REM Add ctf user 4 | net user test "" /add 5 | 6 | REM Configure firewall policy 7 | netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound 8 | netsh advfirewall firewall add rule name="chalservice" dir=in action=allow protocol=TCP localport=13337 9 | 10 | REM Configure registry 11 | reg import wctf.reg 12 | powershell .\config_acl.ps1 13 | 14 | REM Install & run the service 15 | C:\ctf\wctf.exe install 16 | net start wctf -------------------------------------------------------------------------------- /distrib/kernel32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/distrib/kernel32.dll -------------------------------------------------------------------------------- /distrib/ntdll.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/distrib/ntdll.dll -------------------------------------------------------------------------------- /distrib/wctf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/distrib/wctf.exe -------------------------------------------------------------------------------- /distrib/wctf.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/distrib/wctf.reg -------------------------------------------------------------------------------- /solution/README.txt: -------------------------------------------------------------------------------- 1 | If you want to re-test the exploit after any exploitation trial, run cleanup.exe on the server-side with admin privileges to cleanup the side effects of the exploit. -------------------------------------------------------------------------------- /solution/cleanup.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/solution/cleanup.exe -------------------------------------------------------------------------------- /solution/x.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import telnetlib 3 | import struct 4 | import keystone 5 | import sys 6 | #from hexdump import hexdump 7 | 8 | ADDR = ('localhost', 13337) 9 | s = socket.create_connection(ADDR) 10 | 11 | f = s.makefile('rw', 0) 12 | 13 | def read_until(delim='\n'): 14 | buf = '' 15 | while not buf.endswith(delim): 16 | buf += f.read(1) 17 | return buf 18 | 19 | def p32(x): 20 | return struct.pack(" m_capacity) { 29 | m_offset = m_capacity; 30 | } 31 | else { 32 | m_offset += offset; 33 | } 34 | } 35 | 36 | inline void BinaryStream::rewind(uintptr_t offset) { 37 | if (m_offset < offset) { 38 | m_offset = 0; 39 | } 40 | else { 41 | m_offset -= offset; 42 | } 43 | } 44 | 45 | uintptr_t BinaryStream::remaining() { 46 | return m_capacity - m_offset; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /wctf/BinaryStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | 5 | namespace common { 6 | class BinaryStream { 7 | public: 8 | BinaryStream(uintptr_t length); 9 | BinaryStream(void* ptr, uintptr_t length); 10 | ~BinaryStream(); 11 | void skip(uintptr_t offset); 12 | void rewind(uintptr_t offset); 13 | 14 | template T read() { 15 | if (sizeof(T) > remaining()) { 16 | throw std::out_of_range("Not enough size for read"); 17 | } 18 | 19 | T* ptr = reinterpret_cast((uintptr_t)m_pointer + m_offset); 20 | m_offset += sizeof(T); 21 | return (*ptr); 22 | } 23 | 24 | template void write(T val) { 25 | if (sizeof(T) > remaining()) { 26 | throw std::out_of_range("Not enough size for write"); 27 | } 28 | 29 | T* ptr = reinterpret_cast((uintptr_t)m_pointer + m_offset); 30 | m_offset += sizeof(T); 31 | (*ptr) = val; 32 | } 33 | 34 | void* pointer() { 35 | return m_pointer; 36 | } 37 | 38 | uintptr_t length() { 39 | return m_capacity; 40 | } 41 | 42 | private: 43 | uintptr_t remaining(); 44 | 45 | void* m_pointer; 46 | uintptr_t m_capacity; 47 | uintptr_t m_offset; 48 | bool m_alloc; 49 | }; 50 | } -------------------------------------------------------------------------------- /wctf/Common.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | namespace common { 4 | DWORD Win32FromHResult(HRESULT hr) { 5 | /* https://stackoverflow.com/questions/22233527/how-to-convert-hresult-into-an-error-description */ 6 | if ((hr & 0xFFFF0000) == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0)) { 7 | return HRESULT_CODE(hr); 8 | } 9 | if (hr == S_OK) { 10 | return ERROR_SUCCESS; 11 | } 12 | // Not a Win32 HRESULT so return a generic error code. 13 | return ERROR_CAN_NOT_COMPLETE; 14 | } 15 | 16 | //std::unique_ptr RelativePathToAbsolute(const char* path) { 17 | // if (::PathIsRelativeA(path)) { 18 | // char pathBuffer[MAX_PATH]; 19 | // CHECK_NONZERO(::GetCurrentDirectoryA(sizeof(pathBuffer), pathBuffer)); 20 | // CHECK_HRESULT(::StringCbCatA(pathBuffer, sizeof(pathBuffer), path)); 21 | // return std::move(std::make_unique(pathBuffer)); 22 | // } 23 | // return std::move(std::make_unique(path)); 24 | //} 25 | 26 | std::unique_ptr Win32ErrToString(DWORD err) { 27 | char* msgBuf = nullptr; 28 | 29 | CHECK_NONZERO(::FormatMessageA( 30 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 31 | FORMAT_MESSAGE_FROM_SYSTEM | 32 | FORMAT_MESSAGE_IGNORE_INSERTS, 33 | NULL, 34 | err, 35 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 36 | (LPTSTR)&msgBuf, 37 | 0, NULL 38 | )); 39 | std::unique_ptr result = std::make_unique(msgBuf); 40 | LocalFree(msgBuf); 41 | 42 | return std::move(result); 43 | } 44 | 45 | DWORD fork(void) { 46 | RTL_USER_PROCESS_INFORMATION proc_info; 47 | NTSTATUS s; 48 | DWORD ret; 49 | 50 | s = ::RtlCloneUserProcess(RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED | RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES, NULL, NULL, NULL, &proc_info); 51 | if (!NT_SUCCESS(s)) { 52 | return -1; 53 | } else if (s == STATUS_PROCESS_CLONED) { // child 54 | return 0; 55 | } 56 | ret = ::GetProcessId(proc_info.Process); 57 | ::ResumeThread(proc_info.Thread); 58 | ::CloseHandle(proc_info.Process); 59 | ::CloseHandle(proc_info.Thread); 60 | return ret; 61 | } 62 | 63 | std::unique_ptr GetProcessPath(void) { 64 | char buffer[MAX_PATH]; 65 | CHECK_NONZERO(::GetModuleFileNameA(NULL, buffer, MAX_PATH)); 66 | return std::move(std::make_unique(buffer)); 67 | } 68 | 69 | // https://github.com/aurel26/s-4-u-for-windows 70 | BOOL 71 | GetLogonSID( 72 | _In_ HANDLE hToken, 73 | _Out_ PSID *pLogonSid 74 | ) 75 | { 76 | BOOL bSuccess = FALSE; 77 | DWORD dwIndex; 78 | DWORD dwLength = 0; 79 | PTOKEN_GROUPS pTokenGroups = NULL; 80 | 81 | // 82 | // Get required buffer size and allocate the TOKEN_GROUPS buffer. 83 | // 84 | if (!GetTokenInformation( 85 | hToken, 86 | TokenGroups, 87 | (LPVOID)pTokenGroups, 88 | 0, 89 | &dwLength 90 | )) 91 | { 92 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 93 | { 94 | goto Error; 95 | } 96 | 97 | pTokenGroups = (PTOKEN_GROUPS)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); 98 | if (pTokenGroups == NULL) 99 | goto Error; 100 | } 101 | 102 | // 103 | // Get the token group information from the access token. 104 | // 105 | if (!GetTokenInformation( 106 | hToken, 107 | TokenGroups, 108 | (LPVOID)pTokenGroups, 109 | dwLength, 110 | &dwLength 111 | )) 112 | { 113 | fprintf(stderr, "GetTokenInformation failed (error: %u).\n", GetLastError()); 114 | goto Error; 115 | } 116 | 117 | // 118 | // Loop through the groups to find the logon SID. 119 | // 120 | for (dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++) 121 | if ((pTokenGroups->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) 122 | { 123 | // 124 | // Found the logon SID: make a copy of it. 125 | // 126 | dwLength = GetLengthSid(pTokenGroups->Groups[dwIndex].Sid); 127 | *pLogonSid = (PSID)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); 128 | if (*pLogonSid == NULL) 129 | goto Error; 130 | if (!CopySid(dwLength, *pLogonSid, pTokenGroups->Groups[dwIndex].Sid)) 131 | { 132 | goto Error; 133 | } 134 | break; 135 | } 136 | 137 | bSuccess = TRUE; 138 | 139 | Error: 140 | if (bSuccess == FALSE) 141 | { 142 | if (*pLogonSid != NULL) 143 | HeapFree(::GetProcessHeap(), 0, *pLogonSid); 144 | } 145 | 146 | if (pTokenGroups != NULL) 147 | HeapFree(::GetProcessHeap(), 0, pTokenGroups); 148 | 149 | return bSuccess; 150 | } 151 | 152 | VOID 153 | InitLsaString( 154 | _Out_ PLSA_STRING DestinationString, 155 | _In_z_ LPSTR szSourceString 156 | ) 157 | { 158 | USHORT StringSize; 159 | 160 | StringSize = (USHORT)strlen(szSourceString); 161 | 162 | DestinationString->Length = StringSize; 163 | DestinationString->MaximumLength = StringSize + sizeof(CHAR); 164 | DestinationString->Buffer = (PCHAR)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, DestinationString->MaximumLength); 165 | 166 | if (DestinationString->Buffer) 167 | { 168 | memcpy(DestinationString->Buffer, szSourceString, DestinationString->Length); 169 | } 170 | else 171 | { 172 | memset(DestinationString, 0, sizeof(LSA_STRING)); 173 | } 174 | } 175 | 176 | PBYTE 177 | InitUnicodeString( 178 | _Out_ PUNICODE_STRING DestinationString, 179 | _In_z_ LPWSTR szSourceString, 180 | _In_ PBYTE pbDestinationBuffer 181 | ) 182 | { 183 | USHORT StringSize; 184 | 185 | StringSize = (USHORT)wcslen(szSourceString) * sizeof(WCHAR); 186 | memcpy(pbDestinationBuffer, szSourceString, StringSize); 187 | 188 | DestinationString->Length = StringSize; 189 | DestinationString->MaximumLength = StringSize + sizeof(WCHAR); 190 | DestinationString->Buffer = (PWSTR)pbDestinationBuffer; 191 | 192 | return (PBYTE)pbDestinationBuffer + StringSize + sizeof(WCHAR); 193 | } 194 | 195 | HANDLE LogonS4U(const wchar_t* user, const wchar_t* realm, NTSTATUS* rets) { 196 | LSA_HANDLE lsa = nullptr; 197 | NTSTATUS status = 0xc0000001, SubStatus; 198 | HANDLE hToken = nullptr, current = nullptr; 199 | LSA_STRING Msv1_0Name = { 0 }; 200 | LSA_STRING OriginName = { 0 }; 201 | PMSV1_0_S4U_LOGON pS4uLogon = NULL; 202 | TOKEN_SOURCE TokenSource; 203 | ULONG ulAuthenticationPackage; 204 | DWORD dwMessageLength; 205 | PSID pLogonSid = NULL; 206 | PVOID pvProfile = NULL; 207 | DWORD dwProfile = 0; 208 | LUID logonId = { 0 }; 209 | QUOTA_LIMITS quotaLimits; 210 | PTOKEN_GROUPS pGroups = NULL; 211 | PBYTE pbPosition; 212 | 213 | if (rets) *rets = 0; 214 | 215 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, ¤t)) { 216 | goto bailout; 217 | } 218 | 219 | if (!GetLogonSID(current, &pLogonSid)) { 220 | goto bailout; 221 | } 222 | 223 | status = ::LsaConnectUntrusted(&lsa); 224 | if (!NT_SUCCESS(status)) { 225 | goto bailout; 226 | } 227 | 228 | InitLsaString(&Msv1_0Name, const_cast(MSV1_0_PACKAGE_NAME)); 229 | status = LsaLookupAuthenticationPackage(lsa, &Msv1_0Name, &ulAuthenticationPackage); 230 | if (!NT_SUCCESS(status)) { 231 | goto bailout; 232 | } 233 | 234 | #define EXTRA_SID_COUNT 2 235 | dwMessageLength = (DWORD)sizeof(MSV1_0_S4U_LOGON) + (EXTRA_SID_COUNT + (DWORD)wcslen(realm) + (DWORD)wcslen(user)) * sizeof(WCHAR); 236 | pS4uLogon = (PMSV1_0_S4U_LOGON)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwMessageLength); 237 | if (pS4uLogon == NULL) { 238 | goto bailout; 239 | } 240 | pS4uLogon->MessageType = MsV1_0S4ULogon; 241 | pbPosition = (PBYTE)pS4uLogon + sizeof(MSV1_0_S4U_LOGON); 242 | pbPosition = InitUnicodeString(&pS4uLogon->UserPrincipalName, const_cast(user), pbPosition); 243 | pbPosition = InitUnicodeString(&pS4uLogon->DomainName, const_cast(realm), pbPosition); 244 | 245 | strcpy_s(TokenSource.SourceName, TOKEN_SOURCE_LENGTH, "S4UWin"); 246 | InitLsaString(&OriginName, const_cast("S4U for Windows")); 247 | AllocateLocallyUniqueId(&TokenSource.SourceIdentifier); 248 | 249 | pGroups = (PTOKEN_GROUPS)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOKEN_GROUPS) + 2 * sizeof(SID_AND_ATTRIBUTES)); 250 | if (pLogonSid) { 251 | pGroups->Groups[pGroups->GroupCount].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY; 252 | pGroups->Groups[pGroups->GroupCount].Sid = pLogonSid; 253 | pGroups->GroupCount++; 254 | } 255 | 256 | status = LsaLogonUser( 257 | lsa, 258 | &OriginName, 259 | Network, // Or Batch 260 | ulAuthenticationPackage, 261 | pS4uLogon, 262 | dwMessageLength, 263 | pGroups, // this requires privilege 264 | &TokenSource, // SourceContext 265 | &pvProfile, 266 | &dwProfile, 267 | &logonId, 268 | &hToken, 269 | "aLimits, 270 | &SubStatus 271 | ); 272 | 273 | bailout: 274 | 275 | if (current) ::CloseHandle(current); 276 | if (pLogonSid) ::HeapFree(::GetProcessHeap(), 0, pLogonSid); 277 | if (pGroups) ::HeapFree(::GetProcessHeap(), 0, pGroups); 278 | if (pS4uLogon) ::HeapFree(::GetProcessHeap(), 0, pS4uLogon); 279 | if (rets) *rets = status; 280 | if (lsa) ::LsaClose(lsa); 281 | return hToken; 282 | } 283 | 284 | bool EnablePrivilege(const char* priv) { 285 | HANDLE hCurrent = nullptr; 286 | bool ret = false; 287 | PTOKEN_PRIVILEGES privs = nullptr; 288 | uintptr_t length = sizeof(TOKEN_PRIVILEGES) + (1 - 1) * sizeof(LUID_AND_ATTRIBUTES); 289 | 290 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hCurrent)) { 291 | goto bailout; 292 | } 293 | 294 | privs = reinterpret_cast(halloc(length)); 295 | privs->PrivilegeCount = 1; 296 | if (!::LookupPrivilegeValueA(nullptr, priv, &privs->Privileges[0].Luid)) { 297 | goto bailout; 298 | } 299 | privs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 300 | #pragma warning(disable:4244) 301 | if (!::AdjustTokenPrivileges(hCurrent, FALSE, privs, length, nullptr, 0)) { 302 | goto bailout; 303 | } 304 | #pragma warning(default:4244) 305 | ret = true; 306 | 307 | bailout: 308 | if (hCurrent) ::CloseHandle(hCurrent); 309 | if (privs) hfree(privs); 310 | return ret; 311 | } 312 | 313 | #pragma warning(disable:4267) 314 | bool SetMountPoint(HANDLE hDirectory, std::string& reparse) { 315 | std::string r = "\\??\\" + reparse; 316 | std::wstring redir(r.begin(), r.end()); 317 | PREPARSE_DATA_BUFFER buffer = nullptr; 318 | DWORD retlen; 319 | DWORD size = 16 + sizeof(wchar_t) * redir.size() + 4; 320 | 321 | buffer = reinterpret_cast(halloc(size)); 322 | buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 323 | buffer->ReparseDataLength = 8 + sizeof(wchar_t) * redir.size() + 4; 324 | buffer->MountPointReparseBuffer.SubstituteNameOffset = 0; 325 | buffer->MountPointReparseBuffer.SubstituteNameLength = redir.size() * 2; 326 | buffer->MountPointReparseBuffer.PrintNameOffset = redir.size() * 2 + 2; 327 | buffer->MountPointReparseBuffer.PrintNameLength = 0; 328 | ::RtlCopyMemory(buffer->MountPointReparseBuffer.PathBuffer, redir.c_str(), redir.size() * 2); 329 | 330 | if (!::DeviceIoControl(hDirectory, FSCTL_SET_REPARSE_POINT, buffer, size, nullptr, 0, &retlen, nullptr)) { 331 | return false; 332 | } 333 | return true; 334 | } 335 | #pragma warning(default:4267) 336 | 337 | bool QueryRegValueDword(HKEY hKey, const char* valueName, uint32_t* value) { 338 | DWORD type; 339 | bool ret = false; 340 | DWORD v = 0; 341 | DWORD size = sizeof(v); 342 | 343 | if (::RegQueryValueExA(hKey, valueName, nullptr, &type, reinterpret_cast(&v), &size) != ERROR_SUCCESS) { 344 | goto bailout; 345 | } 346 | 347 | *value = v; 348 | ret = true; 349 | 350 | bailout: 351 | return ret; 352 | } 353 | } -------------------------------------------------------------------------------- /wctf/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define STRSAFE_NO_CCH_FUNCTIONS // I don't like them 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #pragma comment(lib, "Secur32.lib") 22 | //#pragma comment(lib, "Shlwapi.lib") 23 | #pragma comment(lib, "Bcrypt.lib") 24 | 25 | #ifdef _WIN64 26 | typedef uint64_t uintptr_t; 27 | #else 28 | typedef uint32_t uintptr_t; 29 | #endif 30 | 31 | #ifndef PAGE_SIZE 32 | #define PAGE_SIZE 0x1000 33 | #endif 34 | #define PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1))) 35 | 36 | static_assert(sizeof(int32_t) == 4, "Wrong size for int32_t"); 37 | static_assert(sizeof(uint32_t) == 4, "Wrong size for uint32_t"); 38 | static_assert(sizeof(int64_t) == 8, "Wrong size for int64_t"); 39 | static_assert(sizeof(uint64_t) == 8, "Wrong size for uint64_t"); 40 | #ifdef _WIN64 41 | static_assert(sizeof(uintptr_t) == 8, "Wrong size for uintptr_t"); 42 | #else 43 | static_assert(sizeof(uintptr_t) == 4, "Wrong size for uintptr_t"); 44 | #endif 45 | 46 | #define HALLOC_PTR(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x)) 47 | #define HALLOC_TYPE(x, t) (reinterpret_cast(HALLOC_PTR(x))) 48 | #define HFREE(p) (HeapFree(GetProcessHeap(), 0, reinterpret_cast(p))) 49 | #define FATAL(s) do { printf("!! Fatal error at (%s:%d), Message: %s, Win32 Last error %d\n", __FILE__, __LINE__, s, GetLastError()); ::ExitProcess(-1); } while (0) 50 | #define CHECK_NONZERO(x) if(!(x)) { FATAL(#x); } 51 | #define CHECK_ZERO(x) if((x)) { FATAL(#x); } 52 | #define CHECK_ERROR(x) do { DWORD ____tmperr = (x); if(____tmperr != ERROR_SUCCESS) { ::SetLastError(____tmperr); FATAL(#x); } } while (0) 53 | #define CHECK_HRESULT(x) do { HRESULT ____tmphr = (x); if(!SUCCEEDED(____tmphr)) { ::SetLastError(common::Win32FromHResult(____tmphr)); FATAL(#x); } } while(0) 54 | #define CHECK_NTSTATUS(x) do { NTSTATUS ____tmpstatus = (x); if(!NT_SUCCESS(____tmpstatus)) { ::SetLastError(::RtlNtStatusToDosError(____tmpstatus)); FATAL(#x); } } while(0) 55 | #define MAKEQWORD(lo, hi) ((lo) | (uint64_t)((hi) << 32)) 56 | 57 | #if defined(_DEBUG) 58 | #define DCHECK(x) if(!(x)) { FATAL(#x); } 59 | #else 60 | #define DCHECK(x) x 61 | #endif 62 | 63 | #define EXTERN_C extern "C" 64 | 65 | #ifdef _MSC_VER 66 | #define INLINE __forceinline 67 | #define IMPORT __declspec(dllimport) 68 | #elif defined(__GNUC__) 69 | #define INLINE __attribute__((always_inline)) 70 | #define IMPORT __attribute__((dllimport)) 71 | #else 72 | #error "Unsupported compiler." 73 | #endif 74 | 75 | /* NTDLL DEF START */ 76 | 77 | #define STATUS_PROCESS_CLONED (0x297) 78 | 79 | typedef LONG NTSTATUS; 80 | 81 | #ifndef NT_SUCCESS 82 | #define NT_SUCCESS(x) (((LONG)(x)) >= 0) 83 | #endif 84 | 85 | #pragma comment(lib, "ntdll.lib") 86 | EXTERN_C IMPORT ULONG RtlNtStatusToDosError(NTSTATUS Status); 87 | 88 | #define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 89 | #define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 90 | #define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 91 | 92 | typedef struct _CLIENT_ID { 93 | PVOID UniqueProcess; 94 | PVOID UniqueThread; 95 | } CLIENT_ID, *PCLIENT_ID; 96 | 97 | typedef struct _SECTION_IMAGE_INFORMATION { 98 | PVOID EntryPoint; 99 | ULONG StackZeroBits; 100 | ULONG StackReserved; 101 | ULONG StackCommit; 102 | ULONG ImageSubsystem; 103 | WORD SubSystemVersionLow; 104 | WORD SubSystemVersionHigh; 105 | ULONG Unknown1; 106 | ULONG ImageCharacteristics; 107 | ULONG ImageMachineType; 108 | ULONG Unknown2[3]; 109 | } SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; 110 | 111 | typedef struct _RTL_USER_PROCESS_INFORMATION { 112 | ULONG Size; 113 | HANDLE Process; 114 | HANDLE Thread; 115 | CLIENT_ID ClientId; 116 | SECTION_IMAGE_INFORMATION ImageInformation; 117 | } RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; 118 | 119 | EXTERN_C NTSTATUS RtlCloneUserProcess( 120 | ULONG ProcessFlags, 121 | PSECURITY_DESCRIPTOR ProcessSecurityDescriptor /* optional */, 122 | PSECURITY_DESCRIPTOR ThreadSecurityDescriptor /* optional */, 123 | HANDLE DebugPort /* optional */, 124 | PRTL_USER_PROCESS_INFORMATION ProcessInformation 125 | ); 126 | 127 | //typedef struct _UNICODE_STRING 128 | //{ 129 | // USHORT Length; 130 | // USHORT MaximumLength; 131 | // _Field_size_bytes_part_(MaximumLength, Length) PWCH Buffer; 132 | //} UNICODE_STRING, *PUNICODE_STRING; 133 | 134 | EXTERN_C 135 | VOID 136 | NTAPI 137 | RtlInitUnicodeString( 138 | _Out_ PUNICODE_STRING DestinationString, 139 | _In_opt_ PWSTR SourceString 140 | ); 141 | 142 | // Object attributes 143 | 144 | #define OBJ_INHERIT 0x00000002 145 | #define OBJ_PERMANENT 0x00000010 146 | #define OBJ_EXCLUSIVE 0x00000020 147 | #define OBJ_CASE_INSENSITIVE 0x00000040 148 | #define OBJ_OPENIF 0x00000080 149 | #define OBJ_OPENLINK 0x00000100 150 | #define OBJ_KERNEL_HANDLE 0x00000200 151 | #define OBJ_FORCE_ACCESS_CHECK 0x00000400 152 | #define OBJ_IGNORE_IMPERSONATED_DEVICEMAP 0x00000800 153 | #define OBJ_DONT_REPARSE 0x00001000 154 | #define OBJ_VALID_ATTRIBUTES 0x00001ff2 155 | 156 | typedef struct _OBJECT_ATTRIBUTES 157 | { 158 | ULONG Length; 159 | HANDLE RootDirectory; 160 | PUNICODE_STRING ObjectName; 161 | ULONG Attributes; 162 | PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; 163 | PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE 164 | } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; 165 | 166 | #define InitializeObjectAttributes(p, n, a, r, s) { \ 167 | (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ 168 | (p)->RootDirectory = r; \ 169 | (p)->Attributes = a; \ 170 | (p)->ObjectName = n; \ 171 | (p)->SecurityDescriptor = s; \ 172 | (p)->SecurityQualityOfService = NULL; \ 173 | } 174 | 175 | EXTERN_C 176 | NTSTATUS 177 | NTAPI 178 | NtCreateLowBoxToken( 179 | _Out_ PHANDLE TokenHandle, 180 | _In_ HANDLE ExistingTokenHandle, 181 | _In_ ACCESS_MASK DesiredAccess, 182 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 183 | _In_ PSID PackageSid, 184 | _In_ ULONG CapabilityCount, 185 | _In_reads_opt_(CapabilityCount) PSID_AND_ATTRIBUTES Capabilities, 186 | _In_ ULONG HandleCount, 187 | _In_reads_opt_(HandleCount) HANDLE *Handles 188 | ); 189 | 190 | typedef struct _PROCESS_MITIGATION_POLICY_INFORMATION 191 | { 192 | PROCESS_MITIGATION_POLICY Policy; 193 | union 194 | { 195 | PROCESS_MITIGATION_ASLR_POLICY ASLRPolicy; 196 | PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY StrictHandleCheckPolicy; 197 | PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY SystemCallDisablePolicy; 198 | PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ExtensionPointDisablePolicy; 199 | PROCESS_MITIGATION_DYNAMIC_CODE_POLICY DynamicCodePolicy; 200 | PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY ControlFlowGuardPolicy; 201 | PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy; 202 | PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy; 203 | PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy; 204 | PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY SystemCallFilterPolicy; 205 | PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY PayloadRestrictionPolicy; 206 | PROCESS_MITIGATION_CHILD_PROCESS_POLICY ChildProcessPolicy; 207 | }; 208 | } PROCESS_MITIGATION_POLICY_INFORMATION, *PPROCESS_MITIGATION_POLICY_INFORMATION; 209 | 210 | typedef enum _PROCESSINFOCLASS 211 | { 212 | ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION 213 | ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX 214 | ProcessIoCounters, // q: IO_COUNTERS 215 | ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX, VM_COUNTERS_EX2 216 | ProcessTimes, // q: KERNEL_USER_TIMES 217 | ProcessBasePriority, // s: KPRIORITY 218 | ProcessRaisePriority, // s: ULONG 219 | ProcessDebugPort, // q: HANDLE 220 | ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT 221 | ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN 222 | ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10 223 | ProcessLdtSize, // s: PROCESS_LDT_SIZE 224 | ProcessDefaultHardErrorMode, // qs: ULONG 225 | ProcessIoPortHandlers, // (kernel-mode only) 226 | ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS 227 | ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void 228 | ProcessUserModeIOPL, 229 | ProcessEnableAlignmentFaultFixup, // s: BOOLEAN 230 | ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS 231 | ProcessWx86Information, 232 | ProcessHandleCount, // q: ULONG, PROCESS_HANDLE_INFORMATION // 20 233 | ProcessAffinityMask, // s: KAFFINITY 234 | ProcessPriorityBoost, // qs: ULONG 235 | ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX 236 | ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION 237 | ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND 238 | ProcessWow64Information, // q: ULONG_PTR 239 | ProcessImageFileName, // q: UNICODE_STRING 240 | ProcessLUIDDeviceMapsEnabled, // q: ULONG 241 | ProcessBreakOnTermination, // qs: ULONG 242 | ProcessDebugObjectHandle, // q: HANDLE // 30 243 | ProcessDebugFlags, // qs: ULONG 244 | ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables 245 | ProcessIoPriority, // qs: IO_PRIORITY_HINT 246 | ProcessExecuteFlags, // qs: ULONG 247 | ProcessResourceManagement, // ProcessTlsInformation // PROCESS_TLS_INFORMATION 248 | ProcessCookie, // q: ULONG 249 | ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION 250 | ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA 251 | ProcessPagePriority, // q: PAGE_PRIORITY_INFORMATION 252 | ProcessInstrumentationCallback, // qs: PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40 253 | ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX 254 | ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[] 255 | ProcessImageFileNameWin32, // q: UNICODE_STRING 256 | ProcessImageFileMapping, // q: HANDLE (input) 257 | ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE 258 | ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE 259 | ProcessGroupInformation, // q: USHORT[] 260 | ProcessTokenVirtualizationEnabled, // s: ULONG 261 | ProcessConsoleHostProcess, // q: ULONG_PTR // ProcessOwnerInformation 262 | ProcessWindowInformation, // q: PROCESS_WINDOW_INFORMATION // 50 263 | ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8 264 | ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION 265 | ProcessDynamicFunctionTableInformation, 266 | ProcessHandleCheckingMode, 267 | ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION 268 | ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION 269 | ProcessWorkingSetControl, // s: PROCESS_WORKING_SET_CONTROL 270 | ProcessHandleTable, // since WINBLUE 271 | ProcessCheckStackExtentsMode, 272 | ProcessCommandLineInformation, // q: UNICODE_STRING // 60 273 | ProcessProtectionInformation, // q: PS_PROTECTION 274 | ProcessMemoryExhaustion, // PROCESS_MEMORY_EXHAUSTION_INFO // since THRESHOLD 275 | ProcessFaultInformation, // PROCESS_FAULT_INFORMATION 276 | ProcessTelemetryIdInformation, // PROCESS_TELEMETRY_ID_INFORMATION 277 | ProcessCommitReleaseInformation, // PROCESS_COMMIT_RELEASE_INFORMATION 278 | ProcessDefaultCpuSetsInformation, 279 | ProcessAllowedCpuSetsInformation, 280 | ProcessSubsystemProcess, 281 | ProcessJobMemoryInformation, // PROCESS_JOB_MEMORY_INFO 282 | ProcessInPrivate, // since THRESHOLD2 // 70 283 | ProcessRaiseUMExceptionOnInvalidHandleClose, 284 | ProcessIumChallengeResponse, 285 | ProcessChildProcessInformation, // PROCESS_CHILD_PROCESS_INFORMATION 286 | ProcessHighGraphicsPriorityInformation, 287 | ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 288 | ProcessEnergyValues, // PROCESS_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES 289 | ProcessActivityThrottleState, // PROCESS_ACTIVITY_THROTTLE_STATE 290 | ProcessActivityThrottlePolicy, // PROCESS_ACTIVITY_THROTTLE_POLICY 291 | ProcessWin32kSyscallFilterInformation, 292 | ProcessDisableSystemAllowedCpuSets, 293 | ProcessWakeInformation, // PROCESS_WAKE_INFORMATION 294 | ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE 295 | ProcessManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 296 | ProcessCaptureTrustletLiveDump, 297 | ProcessTelemetryCoverage, 298 | ProcessEnclaveInformation, 299 | ProcessEnableReadWriteVmLogging, // PROCESS_READWRITEVM_LOGGING_INFORMATION 300 | ProcessUptimeInformation, // PROCESS_UPTIME_INFORMATION 301 | ProcessImageSection, 302 | ProcessDebugAuthInformation, // since REDSTONE4 303 | ProcessSystemResourceManagement, // PROCESS_SYSTEM_RESOURCE_MANAGEMENT 304 | ProcessSequenceNumber, // q: ULONGLONG 305 | MaxProcessInfoClass 306 | } PROCESSINFOCLASS; 307 | 308 | EXTERN_C 309 | NTSTATUS 310 | NTAPI 311 | NtSetInformationProcess( 312 | _In_ HANDLE ProcessHandle, 313 | _In_ PROCESSINFOCLASS ProcessInformationClass, 314 | _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, 315 | _In_ ULONG ProcessInformationLength 316 | ); 317 | 318 | EXTERN_C 319 | NTSTATUS 320 | NTAPI 321 | NtGetNlsSectionPtr( 322 | _In_ ULONG SectionType, 323 | _In_ ULONG SectionData, 324 | _In_ PVOID ContextData, 325 | _Out_ PVOID *SectionPointer, 326 | _Out_ PULONG SectionSize 327 | ); 328 | 329 | typedef struct _REPARSE_DATA_BUFFER { 330 | ULONG ReparseTag; 331 | USHORT ReparseDataLength; 332 | USHORT Reserved; 333 | union { 334 | struct { 335 | USHORT SubstituteNameOffset; 336 | USHORT SubstituteNameLength; 337 | USHORT PrintNameOffset; 338 | USHORT PrintNameLength; 339 | ULONG Flags; 340 | WCHAR PathBuffer[1]; 341 | } SymbolicLinkReparseBuffer; 342 | struct { 343 | USHORT SubstituteNameOffset; 344 | USHORT SubstituteNameLength; 345 | USHORT PrintNameOffset; 346 | USHORT PrintNameLength; 347 | WCHAR PathBuffer[1]; 348 | } MountPointReparseBuffer; 349 | struct { 350 | UCHAR DataBuffer[1]; 351 | } GenericReparseBuffer; 352 | }; 353 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 354 | 355 | /* NTDLL DEF END */ 356 | 357 | namespace common { 358 | DWORD Win32FromHResult(HRESULT hr); 359 | //std::unique_ptr RelativePathToAbsolute(const char* path); 360 | std::unique_ptr Win32ErrToString(DWORD err); 361 | DWORD fork(void); 362 | std::unique_ptr GetProcessPath(void); 363 | HANDLE LogonS4U(const wchar_t* user, const wchar_t* realm, NTSTATUS* rets = nullptr); 364 | bool EnablePrivilege(const char* priv); 365 | bool SetMountPoint(HANDLE hDirectory, std::string& reparse); 366 | bool QueryRegValueDword(HKEY hKey, const char* valueName, uint32_t* value); 367 | 368 | template T getRandom(void) { 369 | T val = 0; 370 | CHECK_NTSTATUS(::BCryptGenRandom(NULL, reinterpret_cast(&val), sizeof(T), BCRYPT_USE_SYSTEM_PREFERRED_RNG)); 371 | return val; 372 | } 373 | 374 | INLINE void* halloc(uintptr_t size) { 375 | return ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size); 376 | } 377 | 378 | INLINE void hfree(void* ptr) { 379 | ::HeapFree(::GetProcessHeap(), 0, ptr); 380 | } 381 | } -------------------------------------------------------------------------------- /wctf/Crypto.cpp: -------------------------------------------------------------------------------- 1 | #include "Crypto.h" 2 | #include 3 | #include 4 | 5 | namespace common { 6 | uint32_t crctable[256] = { 7 | 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 8 | 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 9 | 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 10 | 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 11 | 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 12 | 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 13 | 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 14 | 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 15 | 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 16 | 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 17 | 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 18 | 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 19 | 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 20 | 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 21 | 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 22 | 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 23 | 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 24 | 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 25 | 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 26 | 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 27 | 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 28 | 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 29 | 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 30 | 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 31 | 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 32 | 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 33 | 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 34 | 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 35 | 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 36 | 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 37 | 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 38 | 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 39 | 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 40 | 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 41 | 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 42 | 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 43 | 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 44 | 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 45 | 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 46 | 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 47 | 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 48 | 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 49 | 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 50 | 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 51 | 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 52 | 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 53 | 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 54 | 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 55 | 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 56 | 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 57 | 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 58 | 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 59 | 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 60 | 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 61 | 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 62 | 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 63 | 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 64 | 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 65 | 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 66 | 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 67 | 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 68 | 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 69 | 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 70 | 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L 71 | }; 72 | 73 | uint32_t crc32c(uint8_t *buf, int len) { 74 | uint32_t crc = 0xffffffff; 75 | while (len-- > 0) { 76 | crc = (crc >> 8) ^ crctable[(crc ^ (*buf++)) & 0xFF]; 77 | } 78 | return crc ^ 0xffffffff; 79 | } 80 | } -------------------------------------------------------------------------------- /wctf/Crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | 5 | namespace common { 6 | uint32_t crc32c(uint8_t *buf, int len); 7 | } -------------------------------------------------------------------------------- /wctf/Game.cpp: -------------------------------------------------------------------------------- 1 | #include "Game.h" 2 | 3 | using namespace common; 4 | 5 | namespace wctf { 6 | uint64_t Game::random() { 7 | m_client->issue_call(3); 8 | return m_client->read_int64(); 9 | } 10 | 11 | void Game::notify_server(const char* s) { 12 | m_client->issue_call(7); 13 | m_client->write_string(s); 14 | return; 15 | } 16 | 17 | void Game::notify_server(std::string& s) { 18 | m_client->issue_call(7); 19 | m_client->write_string(s); 20 | return; 21 | } 22 | 23 | HANDLE Game::open_file(const char* s) { 24 | m_client->issue_call(1); 25 | m_client->write_string(s); 26 | return m_client->read_handle(); 27 | } 28 | 29 | HKEY Game::open_key(const char* s, uint32_t access, uint32_t option) { 30 | m_client->issue_call(2); 31 | m_client->write_string(s); 32 | m_client->write_int(access); 33 | m_client->write_int(option); 34 | return reinterpret_cast(m_client->read_handle()); 35 | } 36 | 37 | void Game::print_banner() { 38 | char buffer[0x100]; 39 | try { 40 | auto handle = this->open_file("banner.txt"); 41 | auto outhandle = ::GetStdHandle(STD_OUTPUT_HANDLE); 42 | while (true) { 43 | DWORD retlen; 44 | if (!::ReadFile(handle, buffer, sizeof(buffer), &retlen, nullptr)) { 45 | break; 46 | } 47 | if (retlen == 0) break; 48 | ::WriteFile(outhandle, buffer, retlen, &retlen, nullptr); 49 | } 50 | ::CloseHandle(handle); 51 | } 52 | catch (std::exception&) { 53 | std::cerr << "Cannot open banner file." << std::endl; 54 | } 55 | return; 56 | } 57 | 58 | void Game::read_config() { 59 | auto key = this->open_key("Config"); 60 | 61 | if (!QueryRegValueDword(key, "MaximumGuessTrials", &m_threshold)) { 62 | throw std::exception("Cannot read registry value"); 63 | } 64 | 65 | ::RegCloseKey(key); 66 | } 67 | 68 | void Game::menu() { 69 | std::cout << "Menu: " << std::endl; 70 | std::cout << "1. Start a new round" << std::endl; 71 | std::cout << "2. Show highscores" << std::endl; 72 | std::cout << "0. Exit" << std::endl; 73 | std::cout << "Choice: "; 74 | return; 75 | } 76 | 77 | void Game::store_result(uint8_t* scores, int rounds) { 78 | auto result = std::make_unique>(); 79 | std::for_each(scores, scores + rounds, [&result](uint8_t& val) { 80 | result->push_back(val); 81 | }); 82 | m_scores.push_back(std::move(result)); 83 | } 84 | 85 | void Game::new_round() { 86 | uint8_t previous[0x40]; 87 | int round = 0; 88 | uint8_t score; 89 | 90 | auto seed = this->random(); 91 | std::srand(static_cast(seed)); 92 | while (round < 0x40) { 93 | std::cout << "Round #" << round << std::endl; 94 | score = 0xff; 95 | auto number = static_cast(std::rand() & 0xff); 96 | auto trial_left = m_threshold; 97 | 98 | while (true) { 99 | std::cout << "1. Ask" << std::endl; 100 | std::cout << "2. Guess" << std::endl; 101 | std::cout << "3. Check previous score" << std::endl; 102 | std::cout << "4. Load previous scores" << std::endl; 103 | std::cout << "0. Go back" << std::endl; 104 | std::cout << "Choice: "; 105 | 106 | int choice; std::cin >> choice; 107 | if (choice == 0) { 108 | goto game_over; 109 | } 110 | else if (choice == 1) { 111 | if (trial_left != 0) { 112 | std::cout << "Ask me, I'll show you the way: "; 113 | int question; std::cin >> question; 114 | if (static_cast(question) == number) { 115 | std::cout << "That's it!" << std::endl; 116 | } 117 | else if (static_cast(question) > number) { 118 | std::cout << "Too big, try something smaller." << std::endl; 119 | } 120 | else { 121 | std::cout << "Too small." << std::endl; 122 | } 123 | trial_left--; 124 | } 125 | else { 126 | std::cout << "No more questions, try harder!" << std::endl; 127 | } 128 | } 129 | else if (choice == 2) { 130 | std::cout << "Show me your answer: "; 131 | int answer; std::cin >> answer; 132 | if (answer == static_cast(number)) { 133 | std::cout << "Correct!" << std::endl; 134 | break; 135 | } 136 | else { 137 | std::cout << "Wrong!" << std::endl; 138 | score--; 139 | } 140 | } 141 | else if (choice == 3) { 142 | std::cout << "Round number: "; 143 | int index; std::cin >> index; 144 | std::cout << "Round #" << index << ": " << static_cast(previous[index]) << std::endl; // leak 145 | } 146 | else if (choice == 4) { 147 | std::cout << "Round number: "; 148 | int index; std::cin >> index; 149 | if (static_cast(index) >= m_scores.size()) { 150 | std::cout << "Invalid index." << std::endl; 151 | } 152 | std::for_each(m_scores[index]->begin(), m_scores[index]->end(), [&previous, &round](uint8_t& val) { 153 | previous[round++] = val; 154 | }); 155 | std::cout << "Done!" << std::endl; 156 | } 157 | } 158 | previous[round++] = score; 159 | } 160 | 161 | game_over: 162 | std::cout << "Game over! Thanks for playing!" << std::endl; 163 | this->store_result(previous, round); 164 | return; 165 | } 166 | 167 | void Game::play() { 168 | int choice; 169 | 170 | this->read_config(); 171 | this->print_banner(); 172 | while (true) { 173 | this->menu(); 174 | std::cin >> choice; 175 | if (choice == 0) { 176 | break; 177 | } 178 | else if (choice == 1) { 179 | this->new_round(); 180 | } 181 | else if (choice == 2) { 182 | uint32_t high_score = 0; 183 | std::for_each(m_scores.begin(), m_scores.end(), [&high_score](std::unique_ptr>& v) { 184 | auto result = std::accumulate(v->begin(), v->end(), static_cast(0)); 185 | if (result > high_score) { 186 | high_score = result; 187 | } 188 | }); 189 | std::cout << "Highest: " << high_score << std::endl; 190 | } 191 | else { 192 | std::cout << "Invalid choice" << std::endl; 193 | } 194 | } 195 | 196 | return; 197 | } 198 | } -------------------------------------------------------------------------------- /wctf/Game.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | 5 | #include "IPC.h" 6 | 7 | namespace wctf { 8 | class Game { 9 | public: 10 | Game(std::unique_ptr& client) { 11 | m_client = std::move(client); 12 | m_threshold = 0; 13 | } 14 | 15 | ~Game() { 16 | 17 | } 18 | 19 | void play(); 20 | 21 | private: 22 | 23 | HANDLE open_file(const char* s); 24 | HKEY open_key(const char* s, uint32_t access = KEY_QUERY_VALUE, uint32_t option = 0); 25 | uint64_t random(); 26 | void notify_server(std::string& s); 27 | void notify_server(const char* s); 28 | 29 | void read_config(); 30 | void print_banner(); 31 | void menu(); 32 | 33 | void new_round(); 34 | 35 | void store_result(uint8_t* scores, int rounds); 36 | 37 | uint32_t m_threshold; 38 | std::unique_ptr m_client; 39 | std::vector>> m_scores; 40 | }; 41 | } -------------------------------------------------------------------------------- /wctf/IPC.cpp: -------------------------------------------------------------------------------- 1 | #include "IPC.h" 2 | 3 | namespace wctf { 4 | uint32_t IPCBase::read_int() { 5 | this->check_type(IPC_INT); 6 | return this->read(); 7 | } 8 | 9 | void IPCBase::write_int(uint32_t val) { 10 | this->write(IPC_INT); 11 | this->write(val); 12 | return; 13 | } 14 | 15 | HANDLE IPCBase::read_handle() { 16 | this->check_type(IPC_HANDLE); 17 | return this->read(); 18 | } 19 | 20 | void IPCBase::write_handle(HANDLE h) { 21 | HANDLE result = nullptr; 22 | 23 | if (m_process == nullptr) { 24 | throw std::exception("write_handle can only be called by server-side application"); 25 | } 26 | 27 | if (!::DuplicateHandle( 28 | ::GetCurrentProcess(), 29 | h, 30 | m_process, 31 | &result, 32 | 0, 33 | FALSE, 34 | DUPLICATE_SAME_ACCESS 35 | )) { 36 | throw std::exception("Failed duplicating handle"); 37 | } 38 | 39 | this->write(IPC_HANDLE); 40 | this->write(result); 41 | return; 42 | } 43 | 44 | std::string IPCBase::read_string() { 45 | std::string result = ""; 46 | this->check_type(IPC_STRING); 47 | auto length = this->read(); 48 | for (auto i = 0; i < length; i++) { 49 | result += this->read(); 50 | } 51 | return result; 52 | } 53 | 54 | void IPCBase::write_string(std::string& s) { 55 | this->write(IPC_STRING); 56 | this->write(static_cast(s.size())); 57 | for (auto &c : s) { 58 | this->write(c); 59 | } 60 | return; 61 | } 62 | 63 | std::unique_ptr IPCBase::read_binary() { 64 | this->check_type(IPC_BINARY); 65 | auto length = this->read(); 66 | auto result = std::make_unique(length); 67 | for (auto i = 0; i < length; i++) { 68 | result->write(this->read()); 69 | } 70 | return std::move(result); 71 | } 72 | 73 | void IPCBase::write_binary(std::unique_ptr& val) { 74 | this->write(IPC_BINARY); 75 | this->write(val->length()); 76 | auto ptr = reinterpret_cast(val->pointer()); 77 | for (auto i = 0; i < val->length(); i++) { 78 | this->write(ptr[i]); 79 | } 80 | return; 81 | } 82 | 83 | uint64_t IPCBase::read_int64() { 84 | this->check_type(IPC_INT64); 85 | return this->read(); 86 | } 87 | 88 | void IPCBase::write_int64(uint64_t val) { 89 | this->write(IPC_INT64); 90 | this->write(val); 91 | } 92 | 93 | void IPCClient::issue_call(uint32_t call) { 94 | this->write(call); 95 | } 96 | 97 | void IPCServer::serve_forever(IPCHandler handler, void* context) { 98 | while (true) { 99 | auto call_id = this->read(); 100 | handler(this, call_id, context); 101 | } 102 | return; 103 | } 104 | } -------------------------------------------------------------------------------- /wctf/IPC.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | #include "BinaryStream.h" 5 | 6 | namespace wctf { 7 | 8 | #define IPC_INT 1 9 | #define IPC_HANDLE 2 10 | #define IPC_STRING 3 11 | #define IPC_INT64 4 12 | #define IPC_BINARY 5 13 | #define IPC_FAIL 0xff 14 | 15 | class IPCBase { 16 | public: 17 | IPCBase(HANDLE hRead, HANDLE hWrite, HANDLE hProcess = nullptr) { 18 | m_read = hRead; 19 | m_write = hWrite; 20 | m_process = hProcess; 21 | } 22 | 23 | ~IPCBase() { 24 | 25 | } 26 | 27 | virtual uint32_t read_int(); 28 | virtual HANDLE read_handle(); 29 | virtual std::string read_string(); 30 | virtual uint64_t read_int64(); 31 | virtual std::unique_ptr read_binary(); 32 | 33 | virtual void write_int(uint32_t val); 34 | virtual void write_handle(HANDLE h); 35 | virtual void write_string(std::string& s); 36 | inline void write_string(const char* s) { 37 | std::string v(s); 38 | this->write_string(v); 39 | } 40 | virtual void write_int64(uint64_t val); 41 | virtual void write_binary(std::unique_ptr& stream); 42 | inline void write_binary(void* buffer, uintptr_t size) { 43 | auto stream = std::make_unique(buffer, size); 44 | this->write_binary(stream); 45 | } 46 | 47 | inline void inform_failure() { 48 | this->write(IPC_FAIL); 49 | } 50 | 51 | template T read() { 52 | T ret; 53 | DWORD bytes_read; 54 | if (::ReadFile(m_read, reinterpret_cast(&ret), sizeof(T), &bytes_read, nullptr)) { 55 | if (bytes_read == sizeof(T)) { 56 | return ret; 57 | } 58 | } 59 | 60 | if (::GetLastError() == ERROR_BROKEN_PIPE) { 61 | throw true; 62 | } 63 | throw std::exception("Failed read IPC element"); 64 | } 65 | 66 | template void write(T val) { 67 | DWORD bytes_written; 68 | if (::WriteFile(m_write, reinterpret_cast(&val), sizeof(T), &bytes_written, nullptr)) { 69 | if (bytes_written == sizeof(T)) { 70 | return; 71 | } 72 | } 73 | throw std::exception("Failed write IPC element"); 74 | } 75 | 76 | private: 77 | 78 | inline void check_type(uint8_t type) { 79 | auto typ = this->read(); 80 | if (type != typ) { 81 | throw std::exception("Incorrect element type"); 82 | } 83 | return; 84 | } 85 | 86 | HANDLE m_read, m_write, m_process; 87 | }; 88 | 89 | class IPCClient : public IPCBase { 90 | public: 91 | IPCClient(HANDLE hRead, HANDLE hWrite) : IPCBase(hRead, hWrite, nullptr){ 92 | 93 | } 94 | 95 | void issue_call(uint32_t call); 96 | }; 97 | 98 | using IPCHandler = void(IPCBase*, uint32_t, void*); 99 | 100 | class IPCServer : public IPCBase { 101 | public: 102 | IPCServer(HANDLE hRead, HANDLE hWrite, HANDLE hProcess) : IPCBase(hRead, hWrite, hProcess) { 103 | 104 | } 105 | 106 | void serve_forever(IPCHandler handler, void* context = nullptr); 107 | }; 108 | } -------------------------------------------------------------------------------- /wctf/LogFile.cpp: -------------------------------------------------------------------------------- 1 | #include "LogFile.h" 2 | 3 | #define BUFFER_SIZE 512 4 | 5 | namespace common { 6 | std::unique_ptr CommonLog = nullptr; 7 | 8 | LogFile::LogFile(const char* file_name, int log_level, bool tempfile) { 9 | m_logLevel = log_level; 10 | //if (::PathIsRelativeA(file_name)) { // Shlwapi uses gdi32 & user32.... 11 | if(tempfile) { 12 | char temp_path[MAX_PATH]; 13 | 14 | ::ZeroMemory(temp_path, sizeof(temp_path)); 15 | CHECK_NONZERO(::GetTempPathA(sizeof(temp_path), temp_path)); 16 | CHECK_HRESULT(::StringCbCatA(temp_path, sizeof(temp_path), file_name)); 17 | m_filePath = std::make_unique(temp_path); 18 | } 19 | else { 20 | m_filePath = std::make_unique(file_name); 21 | } 22 | 23 | m_fileHandle = ::CreateFileA( 24 | m_filePath->c_str(), 25 | GENERIC_READ | GENERIC_WRITE, 26 | FILE_SHARE_READ, 27 | NULL, 28 | OPEN_ALWAYS, 29 | FILE_ATTRIBUTE_NORMAL, 30 | NULL 31 | ); 32 | CHECK_NONZERO(m_fileHandle != INVALID_HANDLE_VALUE); 33 | CHECK_NONZERO(::SetFilePointer(m_fileHandle, 0, NULL, FILE_END) != INVALID_SET_FILE_POINTER) 34 | } 35 | 36 | LogFile::LogFile(const char* filePath) : LogFile(filePath, LogLevelWarn) { } 37 | LogFile::LogFile() : LogFile("program.log") { } 38 | 39 | LogFile::~LogFile() { 40 | ::CloseHandle(m_fileHandle); 41 | } 42 | 43 | void LogFile::vprintf(char* format, va_list args) { 44 | char temp_buf[BUFFER_SIZE]; 45 | size_t write_len; 46 | DWORD retlen; 47 | 48 | #pragma warning(disable:4267) 49 | m_logMutex.lock(); 50 | CHECK_HRESULT(::StringCbVPrintfA(temp_buf, BUFFER_SIZE, format, args)); 51 | CHECK_HRESULT(::StringCbLengthA(temp_buf, BUFFER_SIZE, &write_len)); 52 | CHECK_NONZERO(::WriteFile(m_fileHandle, temp_buf, write_len, &retlen, NULL)); 53 | m_logMutex.unlock(); 54 | #pragma warning(default:4267) 55 | return; 56 | } 57 | 58 | void LogFile::do_log(const char* tag, const char *format, va_list args) { 59 | char temp_buf[BUFFER_SIZE]; 60 | char format_buf[BUFFER_SIZE]; 61 | struct tm t; 62 | auto current = ::time(nullptr); 63 | CHECK_ZERO(::localtime_s(&t, ¤t)); 64 | ::strftime(temp_buf, BUFFER_SIZE, "%F %T [%%s]: %%s\n", &t); 65 | CHECK_HRESULT(::StringCbPrintfA(format_buf, BUFFER_SIZE, temp_buf, tag, format)); 66 | this->vprintf(format_buf, args); 67 | return; 68 | } 69 | 70 | void LogFile::debug(const char* format, ...) { 71 | va_list args; 72 | 73 | if (m_logLevel > LogLevelDebug) return; 74 | va_start(args, format); 75 | this->do_log("DEBUG", format, args); 76 | va_end(args); 77 | } 78 | 79 | void LogFile::log(const char* format, ...) { 80 | va_list args; 81 | 82 | if (m_logLevel > LogLevelLog) return; 83 | va_start(args, format); 84 | this->do_log("INFO", format, args); 85 | va_end(args); 86 | } 87 | 88 | void LogFile::warn(const char* format, ...) { 89 | va_list args; 90 | 91 | if (m_logLevel > LogLevelWarn) return; 92 | va_start(args, format); 93 | this->do_log("WARN", format, args); 94 | va_end(args); 95 | } 96 | 97 | void LogFile::error(const char* format, ...) { 98 | va_list args; 99 | 100 | if (m_logLevel > LogLevelError) return; 101 | va_start(args, format); 102 | this->do_log("ERROR", format, args); 103 | va_end(args); 104 | } 105 | } -------------------------------------------------------------------------------- /wctf/LogFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define LOG CommonLog->log 12 | 13 | namespace common { 14 | const int LogLevelDebug = 0; 15 | const int LogLevelLog = 1; 16 | const int LogLevelWarn = 2; 17 | const int LogLevelError = 3; 18 | 19 | class LogFile { 20 | public: 21 | LogFile(); 22 | LogFile(const char* file_name); 23 | LogFile(const char* file_name, int log_level, bool tempfile = true); 24 | ~LogFile(); 25 | 26 | virtual void log(const char* format, ...); 27 | virtual void debug(const char* format, ...); 28 | virtual void error(const char* format, ...); 29 | virtual void warn(const char* format, ...); 30 | 31 | private: 32 | std::unique_ptr m_filePath; 33 | std::mutex m_logMutex; 34 | int m_logLevel; 35 | HANDLE m_fileHandle; 36 | 37 | void vprintf(char* format, va_list args); 38 | void do_log(const char* tag, const char* fmt, va_list args); 39 | }; 40 | 41 | extern std::unique_ptr CommonLog; 42 | } -------------------------------------------------------------------------------- /wctf/Sandbox.cpp: -------------------------------------------------------------------------------- 1 | #include "Sandbox.h" 2 | 3 | namespace common { 4 | void Sid::from_pointer(void * ptr) { 5 | auto stream = new BinaryStream(ptr, 0xffff); 6 | 7 | auto revision = stream->read(); 8 | if (revision != 1) { 9 | throw std::exception("Invalid SID pointer"); 10 | } 11 | auto subauth_cnt = stream->read(); 12 | for (auto i = 0; i < 6; i++) { 13 | m_sia.Value[i] = stream->read(); 14 | } 15 | for (auto i = 0; i < subauth_cnt; i++) { 16 | m_subauth.push_back(stream->read()); 17 | } 18 | } 19 | 20 | #pragma warning(disable:4267) 21 | std::unique_ptr Sid::as_stream() { 22 | auto stream = std::make_unique(1032); 23 | stream->write(1); 24 | stream->write(m_subauth.size()); 25 | for (auto i = 0; i < 6; i++) { 26 | stream->write(m_sia.Value[i]); 27 | } 28 | for (auto i = 0; i < m_subauth.size(); i++) { 29 | stream->write(m_subauth[i]); 30 | } 31 | return std::move(stream); 32 | } 33 | #pragma warning(default:4267) 34 | 35 | std::unique_ptr Sid::repr() { 36 | auto stream = as_stream(); 37 | char* stringsid = nullptr; 38 | if (!::ConvertSidToStringSidA(stream->pointer(), &stringsid)) { 39 | throw std::exception("Invalid SID"); 40 | } 41 | auto result = std::make_unique(stringsid); 42 | ::LocalFree(stringsid); 43 | return std::move(result); 44 | } 45 | 46 | HANDLE FilteredToken::make() { 47 | #pragma warning(disable:4267) 48 | HANDLE result = nullptr, 49 | current; 50 | PSID_AND_ATTRIBUTES disabled = nullptr, restricted = nullptr; 51 | PLUID_AND_ATTRIBUTES privileges = nullptr; 52 | DWORD priv_cnt = 0, disabled_cnt = 0, restricted_cnt = 0; 53 | 54 | if(!m_token) { 55 | if (!::OpenProcessToken( 56 | ::GetCurrentProcess(), 57 | TOKEN_ALL_ACCESS, 58 | ¤t 59 | )) { 60 | throw std::exception("OpenProcessToken failed"); 61 | } 62 | } 63 | else { 64 | current = m_token; 65 | } 66 | SandboxLauncher::disable_inherit(result); 67 | 68 | if (m_privilege.size()) { 69 | privileges = new LUID_AND_ATTRIBUTES[m_privilege.size()]; 70 | auto index = 0; 71 | std::for_each(m_privilege.begin(), m_privilege.end(), [&index, privileges](std::unique_ptr& priv) { 72 | if (!::LookupPrivilegeValueA(nullptr, priv->c_str(), &privileges[index].Luid)) { 73 | throw std::exception("Failed lookup privilege"); 74 | } 75 | privileges[index].Attributes = 0; 76 | index++; 77 | }); 78 | priv_cnt = m_privilege.size(); 79 | } 80 | 81 | std::vector> disabled_ptr; 82 | if (m_disabled.size()) { 83 | disabled = new SID_AND_ATTRIBUTES[m_disabled.size()]; 84 | std::for_each(m_disabled.begin(), m_disabled.end(), [&disabled_ptr](std::unique_ptr& sid) { 85 | disabled_ptr.push_back(std::move(sid->as_stream())); 86 | }); 87 | for (auto i = 0; i < m_disabled.size(); i++) { 88 | disabled[i].Sid = disabled_ptr[i]->pointer(); 89 | disabled[i].Attributes = 0; 90 | } 91 | disabled_cnt = m_disabled.size(); 92 | } 93 | 94 | std::vector> restricted_ptr; 95 | if (m_restricted.size()) { 96 | restricted = new SID_AND_ATTRIBUTES[m_restricted.size()]; 97 | std::for_each(m_restricted.begin(), m_restricted.end(), [&restricted_ptr](std::unique_ptr& sid) { 98 | restricted_ptr.push_back(std::move(sid->as_stream())); 99 | }); 100 | for (auto i = 0; i < m_restricted.size(); i++) { 101 | restricted[i].Sid = restricted_ptr[i]->pointer(); 102 | restricted[i].Attributes = 0; 103 | } 104 | restricted_cnt = m_restricted.size(); 105 | } 106 | 107 | if (!::CreateRestrictedToken( 108 | current, 109 | m_flags, 110 | disabled_cnt, 111 | disabled, 112 | priv_cnt, 113 | privileges, 114 | restricted_cnt, 115 | restricted, 116 | &result 117 | )) { 118 | throw std::exception("Failed create filtered token"); 119 | } 120 | 121 | if (privileges) delete[] privileges; 122 | if (disabled) delete[] disabled; 123 | if (restricted) delete[] restricted; 124 | SandboxLauncher::disable_inherit(result); 125 | 126 | return result; 127 | #pragma warning(default:4267) 128 | } 129 | 130 | HANDLE LowBoxToken::make() { 131 | HANDLE result = nullptr, 132 | current; 133 | 134 | if (!m_token) { 135 | if (!::OpenProcessToken( 136 | ::GetCurrentProcess(), 137 | TOKEN_ALL_ACCESS, 138 | ¤t 139 | )) { 140 | throw std::exception("OpenProcessToken failed"); 141 | } 142 | m_token = current; 143 | } 144 | else { 145 | current = m_token; 146 | } 147 | SandboxLauncher::disable_inherit(m_token); 148 | 149 | auto package = m_package->as_stream(); 150 | 151 | PSID_AND_ATTRIBUTES capabilities = nullptr; 152 | std::vector> capability_sids; 153 | if (!m_capabilities.empty()) { 154 | capabilities = new SID_AND_ATTRIBUTES[m_capabilities.size()]; 155 | std::for_each(m_capabilities.begin(), m_capabilities.end(), [&capability_sids](const std::unique_ptr& val) { 156 | capability_sids.push_back(std::move(val->as_stream())); 157 | }); 158 | for (auto i = 0; i < capability_sids.size(); i++) { 159 | capabilities[i].Sid = capability_sids[i]->pointer(); 160 | capabilities[i].Attributes = SE_GROUP_ENABLED; 161 | } 162 | } 163 | 164 | PHANDLE handles = nullptr; 165 | if (!m_handles.empty()) { 166 | handles = new HANDLE[m_handles.size()]; 167 | for (auto i = 0; i < m_handles.size(); i++) { 168 | handles[i] = m_handles[i]; 169 | } 170 | } 171 | 172 | #pragma warning(disable:4267) 173 | auto status = ::NtCreateLowBoxToken( 174 | &result, 175 | current, 176 | TOKEN_ALL_ACCESS, 177 | nullptr, 178 | package->pointer(), 179 | m_capabilities.size(), 180 | capabilities, 181 | m_handles.size(), 182 | handles 183 | ); 184 | #pragma warning(default:4267) 185 | 186 | if (capabilities != nullptr) delete[] capabilities; 187 | if (handles != nullptr) delete[] handles; 188 | 189 | if (!NT_SUCCESS(status)) { 190 | throw std::exception("NtCreateLowBoxToken failed"); 191 | } 192 | 193 | SandboxLauncher::disable_inherit(result); 194 | return result; 195 | } 196 | 197 | bool SandboxLauncher::disable_inherit(HANDLE h, bool disable) { 198 | if (::SetHandleInformation(h, HANDLE_FLAG_INHERIT, disable ? 0 : 1)) { 199 | return true; 200 | } 201 | return false; 202 | } 203 | 204 | bool SandboxLauncher::enable_mitigation() { 205 | // looks like ProcessMitigationPolicy can be changed only by the calling process 206 | // PROCESS_MITIGATION_POLICY_INFORMATION pmpi; 207 | // 208 | //#define SET_MITIGATION(v,f) \ 209 | // pmpi.Policy = Process##v; \ 210 | // pmpi.v.Flags = f; \ 211 | // do { NTSTATUS s; if(!NT_SUCCESS((s = ::NtSetInformationProcess(::GetCurrentProcess(), ProcessMitigationPolicy, &pmpi, sizeof(pmpi))))) { std::cout << #v << ' ' << s << std::endl; return false; } } while(0); 212 | // 213 | // SET_MITIGATION(ASLRPolicy, 7); 214 | // SET_MITIGATION(StrictHandleCheckPolicy, 1); 215 | // SET_MITIGATION(SystemCallDisablePolicy, 1); 216 | // SET_MITIGATION(ExtensionPointDisablePolicy, 1); 217 | // SET_MITIGATION(FontDisablePolicy, 1); 218 | // SET_MITIGATION(ImageLoadPolicy, 7); 219 | // 220 | //#undef SET_MITIGATION 221 | 222 | //do { 223 | // PROCESS_MITIGATION_DEP_POLICY dep; 224 | // ::RtlZeroMemory(&dep, sizeof(dep)); 225 | // dep.Enable = 1; 226 | // dep.DisableAtlThunkEmulation = 1; 227 | // dep.Permanent = 1; 228 | // if (!::SetProcessMitigationPolicy(ProcessDEPPolicy, &dep, sizeof(dep))) { 229 | // printf("dep %d\n", ::GetLastError()); 230 | // return false; 231 | // } 232 | //} while (0); 233 | 234 | //do { 235 | // PROCESS_MITIGATION_ASLR_POLICY aslr; 236 | // ::RtlZeroMemory(&aslr, sizeof(aslr)); 237 | // aslr.EnableHighEntropy = 1; 238 | // aslr.EnableForceRelocateImages = 1; 239 | // aslr.EnableBottomUpRandomization = 1; 240 | // if (!::SetProcessMitigationPolicy(ProcessASLRPolicy, &aslr, sizeof(aslr))) { 241 | // printf("aslr %d\n", ::GetLastError()); 242 | // return false; 243 | // } 244 | //} while (0); 245 | 246 | do { 247 | PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY sh; 248 | ::RtlZeroMemory(&sh, sizeof(sh)); 249 | sh.RaiseExceptionOnInvalidHandleReference = 1; 250 | sh.HandleExceptionsPermanentlyEnabled = 1; 251 | if (!::SetProcessMitigationPolicy(ProcessStrictHandleCheckPolicy, &sh, sizeof(sh))) { 252 | return false; 253 | } 254 | } while (0); 255 | 256 | do { 257 | PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY win32k; 258 | ::RtlZeroMemory(&win32k, sizeof(win32k)); 259 | win32k.DisallowWin32kSystemCalls = 1; 260 | if (!::SetProcessMitigationPolicy(ProcessSystemCallDisablePolicy, &win32k, sizeof(win32k))) { 261 | return false; 262 | } 263 | } while (0); 264 | 265 | do { 266 | PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ep; 267 | ::RtlZeroMemory(&ep, sizeof(ep)); 268 | ep.DisableExtensionPoints = 1; 269 | if (!::SetProcessMitigationPolicy(ProcessExtensionPointDisablePolicy, &ep, sizeof(ep))) { 270 | return false; 271 | } 272 | } while (0); 273 | 274 | do { 275 | PROCESS_MITIGATION_FONT_DISABLE_POLICY font; 276 | ::RtlZeroMemory(&font, sizeof(font)); 277 | font.DisableNonSystemFonts = 1; 278 | if (!::SetProcessMitigationPolicy(ProcessFontDisablePolicy, &font, sizeof(font))) { 279 | return false; 280 | } 281 | } while (0); 282 | 283 | do { 284 | PROCESS_MITIGATION_IMAGE_LOAD_POLICY image; 285 | ::RtlZeroMemory(&image, sizeof(image)); 286 | image.NoLowMandatoryLabelImages = image.NoRemoteImages = image.PreferSystem32Images = 1; 287 | if (!::SetProcessMitigationPolicy(ProcessImageLoadPolicy, &image, sizeof(image))) { 288 | return false; 289 | } 290 | } while (0); 291 | 292 | return true; 293 | } 294 | 295 | bool SandboxLauncher::lockdown() { 296 | TOKEN_MANDATORY_LABEL tml; 297 | PSID sid = nullptr; 298 | HANDLE hCurrent = nullptr; 299 | bool ret = false; 300 | 301 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hCurrent)) { 302 | goto bailout; 303 | } 304 | 305 | if (!::ConvertStringSidToSidA("S-1-16-0", &sid)) { 306 | goto bailout; 307 | } 308 | tml.Label.Sid = sid; 309 | tml.Label.Attributes = SE_GROUP_INTEGRITY; 310 | 311 | if (!::SetTokenInformation(hCurrent, TokenIntegrityLevel, &tml, sizeof(tml))) { 312 | goto bailout; 313 | } 314 | 315 | ret = true; 316 | 317 | bailout: 318 | 319 | if (hCurrent) ::CloseHandle(hCurrent); 320 | if (sid) ::LocalFree(sid); 321 | 322 | return ret; 323 | } 324 | 325 | PROCESS_INFORMATION SandboxLauncher::launch(PreLaunchCallback prelaunch, PostLaunchCallback postlaunch) { 326 | LPPROC_THREAD_ATTRIBUTE_LIST pptal = nullptr; 327 | SIZE_T dwSize = 0; 328 | HANDLE hProcess = nullptr; 329 | HANDLE hJob = nullptr; 330 | HANDLE hToken = nullptr; 331 | STARTUPINFOEXA si; 332 | PROCESS_INFORMATION pi; 333 | 334 | ::RtlZeroMemory(&pi, sizeof(pi)); 335 | 336 | /* Craft ps attributes */ 337 | if (::InitializeProcThreadAttributeList(pptal, 2, 0, &dwSize) != 0 || ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 338 | goto bailout; 339 | } 340 | pptal = reinterpret_cast(halloc(dwSize)); 341 | if (!::InitializeProcThreadAttributeList(pptal, 2, 0, &dwSize)) { 342 | goto bailout; 343 | } 344 | 345 | if(!::UpdateProcThreadAttribute(pptal, 0, PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY, &m_childpolicy, sizeof(m_childpolicy), NULL, NULL)) { 346 | goto bailout; 347 | } 348 | 349 | if (m_mitigation && !::UpdateProcThreadAttribute(pptal, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &m_mitigation, sizeof(m_mitigation), NULL, NULL)) { 350 | goto bailout; 351 | } 352 | 353 | /* Craft job object */ 354 | hJob = ::CreateJobObjectA(nullptr, nullptr); 355 | disable_inherit(hJob); 356 | if (!hJob) { 357 | goto bailout; 358 | } 359 | 360 | if (m_restrictui) { 361 | JOBOBJECT_BASIC_UI_RESTRICTIONS jbur; 362 | ::RtlZeroMemory(&jbur, sizeof(jbur)); 363 | jbur.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP; 364 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS; 365 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; 366 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS; 367 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; 368 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD; 369 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS; 370 | jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD; 371 | if (!::SetInformationJobObject(hJob, JobObjectBasicUIRestrictions, &jbur, sizeof(jbur))) { 372 | goto bailout; 373 | } 374 | } 375 | do { 376 | JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; 377 | ZeroMemory(&jeli, sizeof(jeli)); 378 | jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME; 379 | jeli.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = m_timeout; 380 | jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; 381 | jeli.ProcessMemoryLimit = m_memlimit; 382 | jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; 383 | jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 384 | jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; 385 | jeli.BasicLimitInformation.ActiveProcessLimit = 1; 386 | if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { 387 | goto bailout; 388 | } 389 | } while (0); 390 | 391 | /* Craft token */ 392 | if (m_token) { 393 | hToken = m_token; 394 | } 395 | else { 396 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { 397 | goto bailout; 398 | } 399 | m_token = hToken; 400 | } 401 | disable_inherit(m_token); 402 | 403 | /* Craft parameters */ 404 | ::RtlZeroMemory(&si, sizeof(si)); 405 | si.StartupInfo.cb = sizeof(si); 406 | si.lpAttributeList = pptal; 407 | if (m_stdin && m_stdout && m_stderr) { 408 | si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; 409 | si.StartupInfo.hStdInput = m_stdin; 410 | si.StartupInfo.hStdOutput = m_stdout; 411 | si.StartupInfo.hStdError = m_stderr; 412 | } 413 | 414 | prelaunch(si); 415 | 416 | if (!::CreateProcessAsUserA( 417 | hToken, 418 | m_executable->c_str(), 419 | const_cast(m_application->c_str()), 420 | nullptr, 421 | nullptr, 422 | TRUE, 423 | CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT, 424 | nullptr, 425 | nullptr, 426 | &si.StartupInfo, 427 | &pi 428 | )) { 429 | //printf("%s %d\n", __FUNCTION__, ::GetLastError()); 430 | goto bailout; 431 | } 432 | 433 | if (!::AssignProcessToJobObject(hJob, pi.hProcess)) { 434 | ::TerminateProcess(pi.hProcess, 255); 435 | goto bailout; 436 | } 437 | 438 | postlaunch(pi, hJob); 439 | 440 | bailout: 441 | if (pptal) hfree(pptal); 442 | if (hToken) ::CloseHandle(hToken); 443 | return pi; 444 | } 445 | } -------------------------------------------------------------------------------- /wctf/Sandbox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | #include 5 | #include "BinaryStream.h" 6 | 7 | #define MITIGATION_ON(x) PROCESS_CREATION_MITIGATION_POLICY_##x##_ALWAYS_ON 8 | #define MITIGATION_OFF(x) PROCESS_CREATION_MITIGATION_POLICY_##x##_ALWAYS_OFF 9 | 10 | namespace common { 11 | class Sid { 12 | public: 13 | // Initialize from pointer 14 | Sid(void* ptr) { 15 | from_pointer(ptr); 16 | } 17 | 18 | // Initialize from String SID 19 | Sid(const char* s) { 20 | PSID pSid = nullptr; 21 | if (!::ConvertStringSidToSidA(s, &pSid)) { 22 | throw std::exception("Invalid String SID"); 23 | } 24 | from_pointer(pSid); 25 | ::LocalFree(pSid); 26 | } 27 | 28 | std::unique_ptr as_stream(); 29 | std::unique_ptr repr(); 30 | 31 | private: 32 | void from_pointer(void* ptr); 33 | 34 | SID_IDENTIFIER_AUTHORITY m_sia; 35 | std::vector m_subauth; 36 | }; 37 | 38 | class IToken { 39 | public: 40 | virtual HANDLE make() = 0; 41 | }; 42 | 43 | class FilteredToken : public IToken { 44 | public: 45 | FilteredToken() { 46 | m_flags = 0; 47 | m_token = nullptr; 48 | } 49 | 50 | ~FilteredToken() { 51 | if (m_token) ::CloseHandle(m_token); 52 | } 53 | 54 | inline void with_flags(uint32_t flags) { m_flags = flags; } 55 | inline void disable_all_privileges(bool doit) { 56 | if (doit) m_flags |= DISABLE_MAX_PRIVILEGE; 57 | else m_flags &= ~(DISABLE_MAX_PRIVILEGE); 58 | } 59 | inline void add_removed_privilege(const char* priv) { m_privilege.push_back(std::make_unique(priv)); } 60 | inline void add_disabled_sid(const char* stringsid) { m_disabled.push_back(std::make_unique(stringsid)); } 61 | inline void add_restricted_sid(const char* stringsid) { m_restricted.push_back(std::make_unique(stringsid)); } 62 | inline void add_disabled_sid(void* ptr) { m_disabled.push_back(std::make_unique(ptr)); } 63 | inline void add_restricted_sid(void* ptr) { m_restricted.push_back(std::make_unique(ptr)); } 64 | inline void with_token(HANDLE h) { clear_token(); m_token = h; } 65 | inline void with_token(IToken* ptr) { clear_token(); m_token = ptr->make(); } 66 | inline void clear_token() { if (m_token) { ::CloseHandle(m_token); } m_token = nullptr; } 67 | 68 | virtual HANDLE make(); 69 | 70 | private: 71 | 72 | uint32_t m_flags; 73 | std::vector> m_privilege; 74 | std::vector> m_disabled; 75 | std::vector> m_restricted; 76 | HANDLE m_token; 77 | }; 78 | 79 | class LowBoxToken : public IToken { 80 | public: 81 | LowBoxToken() { 82 | m_package = nullptr; 83 | m_token = nullptr; 84 | } 85 | 86 | ~LowBoxToken() { 87 | if (m_token) ::CloseHandle(m_token); 88 | } 89 | 90 | inline void package_sid(const char* string_sid) { m_package = std::make_unique(string_sid); } 91 | inline void package_sid(void* ptr) { m_package = std::make_unique(ptr); } 92 | inline void add_capability(const char* string_sid) { m_capabilities.push_back(std::make_unique(string_sid)); } 93 | inline void add_capability(void* ptr) { m_capabilities.push_back(std::make_unique(ptr)); } 94 | inline void add_handle(HANDLE h) { m_handles.push_back(h); } 95 | inline void with_token(HANDLE token) { clear_token(); m_token = token; } 96 | inline void with_token(IToken* token) { clear_token(); m_token = token->make(); } 97 | inline void clear_token() { if (m_token) { ::CloseHandle(m_token); } m_token = nullptr; } 98 | 99 | virtual HANDLE make(); 100 | 101 | private: 102 | std::unique_ptr m_package; 103 | std::vector> m_capabilities; 104 | std::vector m_handles; 105 | HANDLE m_token; 106 | }; 107 | 108 | using PreLaunchCallback = void(STARTUPINFOEXA&); 109 | using PostLaunchCallback = void(PROCESS_INFORMATION&, HANDLE); 110 | 111 | class SandboxLauncher { 112 | public: 113 | SandboxLauncher() { 114 | m_restrictui = false; 115 | m_mitigation = 0; 116 | m_childpolicy = 0; 117 | m_memlimit = 0x10000000; 118 | m_timeout = 1200000000; 119 | m_stdin = m_stdout = m_stderr = nullptr; 120 | m_token = nullptr; 121 | } 122 | 123 | ~SandboxLauncher() { 124 | if (m_token) ::CloseHandle(m_token); 125 | } 126 | 127 | inline void with_mitigation(uint64_t mitigation) { m_mitigation = mitigation; } 128 | inline void restrict_child(bool restricted) { m_childpolicy = restricted ? PROCESS_CREATION_CHILD_PROCESS_RESTRICTED : 0; } 129 | inline void restrict_ui_access(bool restricted) { m_restrictui = restricted; } 130 | inline void with_memory_limit(uintptr_t value) { m_memlimit = value; } 131 | inline void with_timeout(uintptr_t value) { m_timeout = value; } 132 | inline void command_line(const char* ptr) { m_application = std::make_unique(ptr); } 133 | inline void command_line(std::unique_ptr &ptr) { m_application = std::move(ptr); } 134 | inline void executable(const char* ptr) { m_executable = std::make_unique(ptr); } 135 | inline void executable(std::unique_ptr &ptr) { m_executable = std::move(ptr); } 136 | inline void with_stdio(HANDLE in, HANDLE out, HANDLE err) { m_stdin = in; m_stdout = out; m_stderr = err; }; 137 | inline void with_token(IToken* token) { clear_token(); m_token = token->make(); } 138 | inline void clear_token() { if (m_token) { ::CloseHandle(m_token); } m_token = nullptr; } 139 | 140 | PROCESS_INFORMATION launch(PreLaunchCallback prelaunch, PostLaunchCallback postlaunch); 141 | 142 | static bool disable_inherit(HANDLE h, bool disable = true); 143 | static bool enable_mitigation(); 144 | static bool lockdown(); 145 | 146 | private: 147 | 148 | bool m_restrictui; 149 | uint64_t m_mitigation; 150 | uint32_t m_childpolicy; 151 | uintptr_t m_memlimit; 152 | uint64_t m_timeout; 153 | std::unique_ptr m_application; 154 | std::unique_ptr m_executable; 155 | HANDLE m_stdin, m_stdout, m_stderr; 156 | HANDLE m_token; 157 | }; 158 | } -------------------------------------------------------------------------------- /wctf/Service.cpp: -------------------------------------------------------------------------------- 1 | #include "Service.h" 2 | #include "LogFile.h" 3 | 4 | namespace common { 5 | Service *Service::instance_; 6 | 7 | Service::Service(const std::string &name, 8 | bool canStop, 9 | bool canShutdown, 10 | bool canPauseContinue 11 | ) : 12 | name_(name), statusHandle_(NULL) 13 | { 14 | 15 | // The service runs in its own process. 16 | status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 17 | 18 | // The service is starting. 19 | status_.dwCurrentState = SERVICE_START_PENDING; 20 | 21 | // The accepted commands of the service. 22 | status_.dwControlsAccepted = 0; 23 | if (canStop) 24 | status_.dwControlsAccepted |= SERVICE_ACCEPT_STOP; 25 | if (canShutdown) 26 | status_.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; 27 | if (canPauseContinue) 28 | status_.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; 29 | 30 | status_.dwWin32ExitCode = NO_ERROR; 31 | status_.dwServiceSpecificExitCode = 0; 32 | status_.dwCheckPoint = 0; 33 | status_.dwWaitHint = 0; 34 | } 35 | 36 | Service::~Service() 37 | { } 38 | 39 | void Service::run() 40 | { 41 | instance_ = this; 42 | 43 | SERVICE_TABLE_ENTRYA serviceTable[] = 44 | { 45 | { const_cast(name_.c_str()), serviceMain }, 46 | { NULL, NULL } 47 | }; 48 | 49 | if (!::StartServiceCtrlDispatcherA(serviceTable)) { 50 | throw std::exception("Failed starting the service"); 51 | } 52 | } 53 | 54 | void WINAPI Service::serviceMain( 55 | __in DWORD argc, 56 | __in_ecount(argc) LPSTR *argv) 57 | { 58 | CHECK_NONZERO(instance_ != NULL); 59 | 60 | 61 | 62 | // Register the handler function for the service 63 | instance_->statusHandle_ = ::RegisterServiceCtrlHandlerA( 64 | instance_->name_.c_str(), serviceCtrlHandler); 65 | if (instance_->statusHandle_ == NULL) 66 | { 67 | instance_->setStateStopped(255); 68 | return; 69 | } 70 | 71 | // Start the service. 72 | instance_->setState(SERVICE_START_PENDING); 73 | instance_->onStart(argc, argv); 74 | } 75 | 76 | void WINAPI Service::serviceCtrlHandler(DWORD ctrl) 77 | { 78 | switch (ctrl) 79 | { 80 | case SERVICE_CONTROL_STOP: 81 | if (instance_->status_.dwControlsAccepted & SERVICE_ACCEPT_STOP) { 82 | instance_->setState(SERVICE_STOP_PENDING); 83 | instance_->onStop(); 84 | } 85 | break; 86 | case SERVICE_CONTROL_PAUSE: 87 | if (instance_->status_.dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) { 88 | instance_->setState(SERVICE_PAUSE_PENDING); 89 | instance_->onPause(); 90 | } 91 | break; 92 | case SERVICE_CONTROL_CONTINUE: 93 | if (instance_->status_.dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) { 94 | instance_->setState(SERVICE_CONTINUE_PENDING); 95 | instance_->onContinue(); 96 | } 97 | break; 98 | case SERVICE_CONTROL_SHUTDOWN: 99 | if (instance_->status_.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) { 100 | instance_->setState(SERVICE_STOP_PENDING); 101 | instance_->onShutdown(); 102 | } 103 | break; 104 | case SERVICE_CONTROL_INTERROGATE: 105 | ::SetServiceStatus(instance_->statusHandle_, &instance_->status_); 106 | break; 107 | default: 108 | break; 109 | } 110 | } 111 | 112 | void Service::setState(DWORD state) 113 | { 114 | setStateL(state); 115 | } 116 | 117 | void Service::setStateL(DWORD state) 118 | { 119 | status_.dwCurrentState = state; 120 | status_.dwCheckPoint = 0; 121 | status_.dwWaitHint = 0; 122 | SetServiceStatus(statusHandle_, &status_); 123 | } 124 | 125 | void Service::setStateStopped(DWORD exitCode) 126 | { 127 | status_.dwWin32ExitCode = exitCode; 128 | setStateL(SERVICE_STOPPED); 129 | } 130 | 131 | void Service::setStateStoppedSpecific(DWORD exitCode) 132 | { 133 | status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; 134 | status_.dwServiceSpecificExitCode = exitCode; 135 | setStateL(SERVICE_STOPPED); 136 | } 137 | 138 | void Service::bump() 139 | { 140 | ++status_.dwCheckPoint; 141 | ::SetServiceStatus(statusHandle_, &status_); 142 | } 143 | 144 | void Service::hintTime(DWORD msec) 145 | { 146 | ++status_.dwCheckPoint; 147 | status_.dwWaitHint = msec; 148 | ::SetServiceStatus(statusHandle_, &status_); 149 | status_.dwWaitHint = 0; // won't apply after the next update 150 | } 151 | 152 | void Service::onStart( 153 | __in DWORD argc, 154 | __in_ecount(argc) LPSTR *argv) 155 | { 156 | setState(SERVICE_RUNNING); 157 | } 158 | void Service::onStop() 159 | { 160 | setStateStopped(NO_ERROR); 161 | } 162 | void Service::onPause() 163 | { 164 | setState(SERVICE_PAUSED); 165 | } 166 | void Service::onContinue() 167 | { 168 | setState(SERVICE_RUNNING); 169 | } 170 | void Service::onShutdown() 171 | { 172 | onStop(); 173 | } 174 | 175 | bool Service::Install() { 176 | SC_HANDLE scMan = NULL, scSvc = NULL; 177 | char szSelfPath[MAX_PATH]; 178 | bool result = false; 179 | 180 | CHECK_NONZERO(GetModuleFileNameA(GetModuleHandleA(NULL), szSelfPath, MAX_PATH)); 181 | 182 | scMan = ::OpenSCManagerA( 183 | NULL, 184 | NULL, 185 | SC_MANAGER_ALL_ACCESS 186 | ); 187 | if (!scMan) { 188 | goto bailout; 189 | } 190 | 191 | if ((scSvc = ::CreateServiceA( 192 | scMan, 193 | name_.c_str(), 194 | name_.c_str(), 195 | SERVICE_ALL_ACCESS, 196 | SERVICE_WIN32_OWN_PROCESS, 197 | SERVICE_AUTO_START, 198 | SERVICE_ERROR_IGNORE, 199 | szSelfPath, 200 | NULL, 201 | NULL, 202 | NULL, 203 | NULL, 204 | NULL 205 | )) == NULL) { 206 | goto bailout; 207 | } 208 | 209 | result = true; 210 | 211 | bailout: 212 | if (scMan) ::CloseServiceHandle(scMan); 213 | if (scSvc) ::CloseServiceHandle(scSvc); 214 | return result; 215 | } 216 | 217 | bool Service::Uninstall() { 218 | SC_HANDLE scMan = NULL, scSvc = NULL; 219 | char szSelfPath[MAX_PATH]; 220 | bool result = false; 221 | 222 | CHECK_NONZERO(GetModuleFileNameA(GetModuleHandleA(NULL), szSelfPath, MAX_PATH)); 223 | 224 | scMan = ::OpenSCManagerA( 225 | NULL, 226 | NULL, 227 | SC_MANAGER_ALL_ACCESS 228 | ); 229 | if (!scMan) { 230 | goto bailout; 231 | } 232 | 233 | if (!(scSvc = ::OpenServiceA(scMan, name_.c_str(), SERVICE_ALL_ACCESS))) { 234 | goto bailout; 235 | } 236 | 237 | if (!::DeleteService(scSvc)) { 238 | goto bailout; 239 | } 240 | 241 | result = true; 242 | 243 | bailout: 244 | if (scMan) ::CloseServiceHandle(scMan); 245 | if (scSvc) ::CloseServiceHandle(scSvc); 246 | return result; 247 | } 248 | } -------------------------------------------------------------------------------- /wctf/Service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | #include 5 | 6 | namespace common { 7 | // https://blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/windows-service-wrapper-in-c/ 8 | class Service 9 | { 10 | public: 11 | // The way the services work, there can be only one Service object 12 | // in the process. 13 | Service(const std::string &name, 14 | bool canStop, 15 | bool canShutdown, 16 | bool canPauseContinue); 17 | 18 | virtual ~Service(); 19 | 20 | // Run the service. Returns after the service gets stopped. 21 | // When the Service object gets started, 22 | // it will remember the instance pointer in the instance_ static 23 | // member, and use it in the callbacks. 24 | // The errors are reported back in err. 25 | void run(); 26 | 27 | // Change the service state. Don't use it for SERVICE_STOPPED, 28 | // do that through the special versions. 29 | // Can be called only while run() is running. 30 | void setState(DWORD state); 31 | // The convenience versions. 32 | void setStateRunning() 33 | { 34 | setState(SERVICE_RUNNING); 35 | } 36 | void setStatePaused() 37 | { 38 | setState(SERVICE_PAUSED); 39 | } 40 | // The stopping is more compilcated: it also sets the exit code. 41 | // Which can be either general or a service-specific error code. 42 | // The success indication is the general code NO_ERROR. 43 | // Can be called only while run() is running. 44 | void setStateStopped(DWORD exitCode); 45 | void setStateStoppedSpecific(DWORD exitCode); 46 | 47 | // On the lengthy operations, periodically call this to tell the 48 | // controller that the service is not dead. 49 | // Can be called only while run() is running. 50 | void bump(); 51 | 52 | // Can be used to set the expected length of long operations. 53 | // Also does the bump. 54 | // Can be called only while run() is running. 55 | void hintTime(DWORD msec); 56 | 57 | bool Install(); 58 | bool Uninstall(); 59 | 60 | // Methods for the subclasses to override. 61 | // The base class defaults set the completion state, so the subclasses must 62 | // either call them at the end of processing (maybe after some wait, maybe 63 | // from another thread) or do it themselves. 64 | // The pending states (where applicable) will be set before these methods 65 | // are called. 66 | // onStart() is responsible for actually starting the application 67 | virtual void onStart( 68 | __in DWORD argc, 69 | __in_ecount(argc) LPSTR *argv); 70 | virtual void onStop(); // sets the success exit code 71 | virtual void onPause(); 72 | virtual void onContinue(); 73 | virtual void onShutdown(); // calls onStop() 74 | 75 | protected: 76 | // The callback for the service start. 77 | static void WINAPI serviceMain( 78 | __in DWORD argc, 79 | __in_ecount(argc) LPSTR *argv); 80 | // The callback for the requests. 81 | static void WINAPI serviceCtrlHandler(DWORD ctrl); 82 | 83 | // the internal version that expects the caller to already hold statusCr_ 84 | void setStateL(DWORD state); 85 | 86 | protected: 87 | static Service *instance_; 88 | 89 | std::string name_; // service name 90 | 91 | SERVICE_STATUS_HANDLE statusHandle_; // handle used to report the status 92 | SERVICE_STATUS status_; // the current status 93 | 94 | private: 95 | Service(); 96 | Service(const Service &); 97 | void operator=(const Service &); 98 | }; 99 | } -------------------------------------------------------------------------------- /wctf/SocketServer.cpp: -------------------------------------------------------------------------------- 1 | #include "SocketServer.h" 2 | 3 | namespace common { 4 | SocketServer::SocketServer(const char* address, uint16_t port) { 5 | m_socket = ::WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, 0); 6 | if (m_socket == INVALID_SOCKET) { 7 | throw std::exception("WSASocket failed"); 8 | } 9 | 10 | const int option = 1; 11 | if (::setsockopt( 12 | m_socket, 13 | SOL_SOCKET, 14 | SO_REUSEADDR, 15 | reinterpret_cast(&option), 16 | sizeof(option) 17 | ) != 0) { 18 | throw std::exception("setsockopt failed"); 19 | } 20 | 21 | struct sockaddr_in addr; 22 | ::RtlZeroMemory(&addr, sizeof(addr)); 23 | addr.sin_port = ::htons(port); 24 | addr.sin_addr.S_un.S_addr = ::inet_addr(address); 25 | addr.sin_family = AF_INET; 26 | 27 | if (::bind(m_socket, reinterpret_cast(&addr), sizeof(addr))) { 28 | throw std::exception("bind failed"); 29 | } 30 | 31 | if (::listen(m_socket, DEFAULT_BACKLOG) != 0) { 32 | throw std::exception("listen failed"); 33 | } 34 | 35 | #ifdef SECURE 36 | if (::SetHandleInformation( 37 | reinterpret_cast(m_socket), 38 | HANDLE_FLAG_INHERIT, 39 | 0 40 | ) != TRUE) { 41 | throw std::exception("SetHandleInformation failed"); 42 | } 43 | #endif 44 | 45 | m_serving = false; 46 | m_sockEvent = ::CreateEventA(nullptr, FALSE, FALSE, nullptr); 47 | m_exitEvent = ::WSACreateEvent(); 48 | 49 | if (::WSAEventSelect(m_socket, m_sockEvent, FD_ACCEPT) != 0) { 50 | throw std::exception("WSAEventSelect failed"); 51 | } 52 | } 53 | 54 | SocketServer::~SocketServer() { 55 | ::WSACloseEvent(m_sockEvent); 56 | ::WSACloseEvent(m_exitEvent); 57 | ::closesocket(m_socket); 58 | } 59 | 60 | void SocketServer::kill() { 61 | if (m_serving) { 62 | ::WSASetEvent(m_exitEvent); 63 | } 64 | } 65 | 66 | void SocketServer::serve_forever(NewConnectionCallback callback) { 67 | WSAEVENT events[] = { m_sockEvent, m_exitEvent }; 68 | struct sockaddr_in addr; 69 | int length = sizeof(addr); 70 | 71 | m_serving = true; 72 | while (true) { 73 | auto retcode = ::WSAWaitForMultipleEvents(2, events, FALSE, INFINITE, FALSE); 74 | if (retcode == WSA_WAIT_EVENT_0) { 75 | auto client = ::accept( 76 | m_socket, 77 | reinterpret_cast(&addr), 78 | &length 79 | ); 80 | callback(client, &addr); 81 | } 82 | else if (retcode == WSA_WAIT_EVENT_0 + 1) { 83 | break; 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /wctf/SocketServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 5 | #include 6 | #pragma comment(lib, "ws2_32.lib") 7 | 8 | #define DEFAULT_BACKLOG 5 9 | #define SECURE 1 10 | 11 | namespace common { 12 | using NewConnectionCallback = void (SOCKET, struct sockaddr_in*); 13 | 14 | class SocketServer { 15 | public: 16 | SocketServer(const char* address, uint16_t port); 17 | ~SocketServer(); 18 | 19 | void serve_forever(NewConnectionCallback callback); 20 | void kill(); 21 | 22 | private: 23 | 24 | bool m_serving; 25 | WSAEVENT m_sockEvent, m_exitEvent; 26 | SOCKET m_socket; 27 | }; 28 | } -------------------------------------------------------------------------------- /wctf/ntdll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/wctf/ntdll.lib -------------------------------------------------------------------------------- /wctf/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/wctf/stdafx.cpp -------------------------------------------------------------------------------- /wctf/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/wctf/stdafx.h -------------------------------------------------------------------------------- /wctf/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/wctf/targetver.h -------------------------------------------------------------------------------- /wctf/wctf.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marche147/wctf-game/005be7e0d73163c3037a19ba8bb7fb1678e1d664/wctf/wctf.cpp -------------------------------------------------------------------------------- /wctf/wctf.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {B25FF758-3B44-4195-892D-FF63929A5C28} 24 | Win32Proj 25 | wctf 26 | 10.0.16299.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | NotSet 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Use 116 | Level3 117 | MaxSpeed 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | NotUsing 134 | Level3 135 | MaxSpeed 136 | true 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | MultiThreaded 142 | Guard 143 | 144 | 145 | Windows 146 | true 147 | true 148 | true 149 | /entry:mainCRTStartup %(AdditionalOptions) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Create 177 | Create 178 | Create 179 | Create 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /wctf/wctf.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 头文件 35 | 36 | 37 | 头文件 38 | 39 | 40 | 头文件 41 | 42 | 43 | 头文件 44 | 45 | 46 | 头文件 47 | 48 | 49 | 头文件 50 | 51 | 52 | 53 | 54 | 源文件 55 | 56 | 57 | 源文件 58 | 59 | 60 | 源文件 61 | 62 | 63 | 源文件 64 | 65 | 66 | 源文件 67 | 68 | 69 | 源文件 70 | 71 | 72 | 源文件 73 | 74 | 75 | 源文件 76 | 77 | 78 | 源文件 79 | 80 | 81 | 源文件 82 | 83 | 84 | 源文件 85 | 86 | 87 | --------------------------------------------------------------------------------