diff --git a/Build/Release/x64/repacls.exe b/Build/Release/x64/repacls.exe index 79a0f58..3cd4474 100644 Binary files a/Build/Release/x64/repacls.exe and b/Build/Release/x64/repacls.exe differ diff --git a/Build/Release/x86/repacls.exe b/Build/Release/x86/repacls.exe index 972efb7..0aaeb3f 100644 Binary files a/Build/Release/x86/repacls.exe and b/Build/Release/x86/repacls.exe differ diff --git a/Build/Repacls.zip b/Build/Repacls.zip index 070e4f6..509a4ff 100644 Binary files a/Build/Repacls.zip and b/Build/Repacls.zip differ diff --git a/Build/build.cmd b/Build/build.cmd index c5545d1..41ef6a6 100644 --- a/Build/build.cmd +++ b/Build/build.cmd @@ -4,7 +4,7 @@ CLS SET PATH=%WINDIR%\system32;%WINDIR%\system32\WindowsPowerShell\v1.0 :: cert info to use for signing -SET CERT=193A6FACBFBFC43ADB74ABB669543FCBC1C4F26C +SET CERT=BC4F81C0B3B32755A8CC9A6B91713958294788F0 set TSAURL=http://time.certum.pl/ set LIBNAME=Repacls set LIBURL=https://github.com/NoMoreFood/Repacls diff --git a/OperationBackupSecurity.cpp b/OperationBackupSecurity.cpp index f29d5e3..83f88b8 100644 --- a/OperationBackupSecurity.cpp +++ b/OperationBackupSecurity.cpp @@ -46,7 +46,7 @@ bool OperationBackupSecurity::ProcessSdAction(std::wstring & sFileName, ObjectEn DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &sInfo, NULL) == 0) { - InputOutput::AddError(L"ERROR: Unable to generate string security descriptor."); + InputOutput::AddError(L"Unable to generate string security descriptor."); return false; } @@ -55,7 +55,7 @@ bool OperationBackupSecurity::ProcessSdAction(std::wstring & sFileName, ObjectEn if (WriteToFile(sToWrite, hFile) == 0) { LocalFree(sInfo); - InputOutput::AddError(L"ERROR: Unable to write security descriptor to file."); + InputOutput::AddError(L"Unable to write security descriptor to file."); return false; } diff --git a/OperationHelp.cpp b/OperationHelp.cpp index 7cc8c1e..fd53384 100644 --- a/OperationHelp.cpp +++ b/OperationHelp.cpp @@ -101,7 +101,7 @@ this way is preferable to multiple commands because the security descriptor is only read and written once for the entire command which is especially helpful for large volumes. -Commands That Do Not Alter Security +Commands That Do Not Alter Settings ----------------------------------- /PrintDescriptor Prints out the security descriptor to the screen. This is somewhat useful @@ -156,7 +156,7 @@ Commands That Do Not Alter Security regular expression to refine what part of the security descriptor to scan. See Other Notes & Limitations section for more information. -Commands That Can Alter Security (When /WhatIf Is Not Present) +Commands That Can Alter Settings (When /WhatIf Is Not Present) -------------------------------- /GrantPerms : /DenyPerms : @@ -228,6 +228,9 @@ Commands That Can Alter Security (When /WhatIf Is Not Present) have been littered from the old cacls.exe command that didn't understand how to set up inheritance. +/RemoveStreams + Removes any alternate data streams on targeted files. + /ReplaceAccount : Search for an account and replace it with another account. diff --git a/OperationLocate.cpp b/OperationLocate.cpp index b8c182c..c2e6df8 100644 --- a/OperationLocate.cpp +++ b/OperationLocate.cpp @@ -83,6 +83,6 @@ void OperationLocate::ProcessObjectAction(ObjectEntry & tObjectEntry) L"," + Q(sSize) + L"," + Q(sAttributes) + L"\r\n"; if (WriteToFile(sToWrite, hReportFile) == 0) { - InputOutput::AddError(L"ERROR: Unable to write security information to report file."); + InputOutput::AddError(L"Unable to write security information to report file."); } } \ No newline at end of file diff --git a/OperationLocateHash.cpp b/OperationLocateHash.cpp index f4730bc..98c9808 100644 --- a/OperationLocateHash.cpp +++ b/OperationLocateHash.cpp @@ -106,10 +106,10 @@ void OperationLocateHash::ProcessObjectAction(ObjectEntry & tObjectEntry) // initialize hash for this thread static constexpr size_t iFileBuffer = 2 * 1024 * 1024; - __declspec(thread) static BCRYPT_HASH_HANDLE HashHandle = NULL; - __declspec(thread) static PBYTE Hash = nullptr; - __declspec(thread) static PBYTE FileBuffer = nullptr; - __declspec(thread) static DWORD HashLength = 0; + thread_local static BCRYPT_HASH_HANDLE HashHandle = NULL; + thread_local static PBYTE Hash = nullptr; + thread_local static PBYTE FileBuffer = nullptr; + thread_local static DWORD HashLength = 0; if (Hash == nullptr) { BCRYPT_ALG_HANDLE AlgHandle = NULL; diff --git a/OperationLocateShortcut.cpp b/OperationLocateShortcut.cpp index e881844..ca65bdc 100644 --- a/OperationLocateShortcut.cpp +++ b/OperationLocateShortcut.cpp @@ -82,14 +82,14 @@ void OperationLocateShortcut::ProcessObjectAction(ObjectEntry & tObjectEntry) if (!std::regex_match(sFileName, tRegexLink)) return; // initialize com for this thread - __declspec(thread) static bool bComInitialized = false; + thread_local static bool bComInitialized = false; if (!bComInitialized) { bComInitialized = true; const HRESULT hComInit = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (hComInit != S_OK && hComInit != S_FALSE) { - wprintf(L"ERROR: Could not initialize COM.\n"); + wprintf(L"Could not initialize COM.\n"); exit(-1); } } @@ -98,7 +98,7 @@ void OperationLocateShortcut::ProcessObjectAction(ObjectEntry & tObjectEntry) WIN32_FILE_ATTRIBUTE_DATA tData; if (GetFileAttributesExW(tObjectEntry.Name.c_str(), GetFileExInfoStandard, &tData) == 0) { - InputOutput::AddError(L"ERROR: Unable to read file attributes."); + InputOutput::AddError(L"Unable to read file attributes."); } // get common file attributes @@ -137,7 +137,7 @@ void OperationLocateShortcut::ProcessObjectAction(ObjectEntry & tObjectEntry) Q(sAttributes) + L"," + Q(sTargetPath) + L"," + Q(sWorkingDirectory) + L"\r\n"; if (WriteToFile(sToWrite, hReportFile) == 0) { - InputOutput::AddError(L"ERROR: Unable to write security information to report file."); + InputOutput::AddError(L"Unable to write security information to report file."); } } diff --git a/OperationRemoveStreams.cpp b/OperationRemoveStreams.cpp new file mode 100644 index 0000000..14f724c --- /dev/null +++ b/OperationRemoveStreams.cpp @@ -0,0 +1,76 @@ +#include "OperationRemoveStreams.h" +#include "InputOutput.h" +#include "Functions.h" + +ClassFactory OperationRemoveStreams::RegisteredFactory(GetCommand()); + +OperationRemoveStreams::OperationRemoveStreams(std::queue& oArgList, const std::wstring& sCommand) : Operation(oArgList) +{ + + // load function pointer to query file information + HMODULE hModule = GetModuleHandle(L"ntdll.dll"); + if (hModule == NULL || (NtQueryInformationFile = (decltype(NtQueryInformationFile)) + GetProcAddress(hModule, "NtQueryInformationFile")) == NULL) + { + wprintf(L"ERROR: Unable to obtain function pointer in parameter '%s'.\n", GetCommand().c_str()); + exit(-1); + } + + // only flag this to apply to the core object with the file name + AppliesToObject = true; +} + +void OperationRemoveStreams::ProcessObjectAction(ObjectEntry& tObjectEntry) +{ + HANDLE hFile = CreateFile(tObjectEntry.Name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + InputOutput::AddError(L"Unable open file for stream deletion."); + return; + } + + // loop until we can fill the stream into a buffer + NTSTATUS iStatus; + thread_local std::vector sInfoBuffer(16 * 1024, 0); + for (iStatus = STATUS_BUFFER_OVERFLOW; iStatus == STATUS_BUFFER_OVERFLOW; + sInfoBuffer.resize(sInfoBuffer.size() * 2, 0)) + { + IO_STATUS_BLOCK tIOStatus = {}; + iStatus = NtQueryInformationFile(hFile, &tIOStatus, sInfoBuffer.data(), (ULONG) sInfoBuffer.size(), FileStreamInformation); + if (iStatus == STATUS_SUCCESS) break; + } + + // cleanup and verify we got the data we needed + CloseHandle(hFile); + if (iStatus != STATUS_SUCCESS) return; + + // Loop for all streams + for (PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)sInfoBuffer.data(); pStreamInfo->StreamNameLength != 0; + pStreamInfo = (PFILE_STREAM_INFORMATION)((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset)) + { + // skip main data stream + const WCHAR sData[] = L"::$DATA"; + if (_countof(sData) - 1 == pStreamInfo->StreamNameLength / sizeof(WCHAR) && + _wcsnicmp(pStreamInfo->StreamName, sData, _countof(sData) - 1) == 0) + { + if (pStreamInfo->NextEntryOffset == 0) break; + continue; + } + + // remove the stream + std::wstring sStream((const wchar_t *) pStreamInfo->StreamName, (size_t) (pStreamInfo->StreamNameLength / sizeof(WCHAR))); + std::wstring sFullStreamName = (tObjectEntry.Name + sStream); + if (InputOutput::InWhatIfMode() || (SetFileAttributes(sFullStreamName.c_str(), FILE_ATTRIBUTE_NORMAL) != 0 && DeleteFile(sFullStreamName.c_str()) != 0)) + { + InputOutput::AddInfo(L"Removed stream: " + sStream, L""); + } + else + { + InputOutput::AddError(L"Unable delete stream: " + sStream); + } + + // break if no next stream + if (pStreamInfo->NextEntryOffset == 0) break; + } +} \ No newline at end of file diff --git a/OperationRemoveStreams.h b/OperationRemoveStreams.h new file mode 100644 index 0000000..c7e7a8f --- /dev/null +++ b/OperationRemoveStreams.h @@ -0,0 +1,60 @@ +#pragma once + +#define UMDF_USING_NTSTATUS +#include + +#include + +#include "Operation.h" + +class OperationRemoveStreams : public Operation +{ +private: + + // statics used by command registration utility + static std::wstring GetCommand() { return L"RemoveStreams"; } + static ClassFactory RegisteredFactory; + + // + // Definitions below avoid need to install Windows Driver Development Kit + // + + typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; + } IO_STATUS_BLOCK, * PIO_STATUS_BLOCK; + + typedef enum _FILE_INFORMATION_CLASS { + FileStreamInformation = 22 + } FILE_INFORMATION_CLASS, * PFILE_INFORMATION_CLASS; + + #pragma pack(push, 4) + typedef struct _FILE_STREAM_INFORMATION { // Information Class 22 + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER EndOfStream; + LARGE_INTEGER AllocationSize; + WCHAR StreamName[1]; + } FILE_STREAM_INFORMATION, * PFILE_STREAM_INFORMATION; + #pragma pack(pop) + + typedef NTSTATUS(NTAPI* NTQUERYINFORMATIONFILE)( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass); + + NTQUERYINFORMATIONFILE NtQueryInformationFile; + +public: + + // overrides + void ProcessObjectAction(ObjectEntry & tObjectEntry) override; + + // constructors + OperationRemoveStreams(std::queue & oArgList, const std::wstring & sCommand); +}; \ No newline at end of file diff --git a/OperationReport.cpp b/OperationReport.cpp index 192c48a..c235f37 100644 --- a/OperationReport.cpp +++ b/OperationReport.cpp @@ -85,7 +85,7 @@ SidActionResult OperationReport::DetermineSid(const WCHAR * const sSdPart, Objec std::wstring sToWrite = Q(tObjectEntry.Name) + L"," + Q(sSdPart) + L"," + Q(sAccount) + L"\r\n"; if (WriteToFile(sToWrite, hReportFile) == 0) { - InputOutput::AddError(L"ERROR: Unable to write security information to report file."); + InputOutput::AddError(L"Unable to write security information to report file."); } return SidActionResult::Nothing; @@ -117,7 +117,7 @@ bool OperationReport::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry Q(sAccount) + L"," + Q(sMask) + L"," + Q(sFlags) + L"\r\n"; if (WriteToFile(sToWrite, hReportFile) == 0) { - InputOutput::AddError(L"ERROR: Unable to write security information to report file."); + InputOutput::AddError(L"Unable to write security information to report file."); } } diff --git a/Resource.rc b/Resource.rc index 3893fb6..faacd93 100644 Binary files a/Resource.rc and b/Resource.rc differ diff --git a/repacls.vcxproj b/repacls.vcxproj index 8c1b316..4692b3b 100644 --- a/repacls.vcxproj +++ b/repacls.vcxproj @@ -214,6 +214,7 @@ + @@ -249,6 +250,7 @@ + diff --git a/repacls.vcxproj.filters b/repacls.vcxproj.filters index c656c57..f36780f 100644 --- a/repacls.vcxproj.filters +++ b/repacls.vcxproj.filters @@ -121,6 +121,9 @@ Source\Operations + + Source\Operations + @@ -258,6 +261,9 @@ Includes\Operations + + Includes\Operations +