Skip to content

Commit

Permalink
Added /Log & Enhanced Logging For /CopyDomain
Browse files Browse the repository at this point in the history
- Added /Log parameter that will record all INFO, WARNING, and ERROR messages to a designated file.
- Changed /CopyDomain to output warnings when no matching account is found in the target domain.
- Changed /CopyDomain to not alter inherited ACEs.
- Various refactoring to utilize modern compiler syntax.
  • Loading branch information
NoMoreFood committed Dec 15, 2018
1 parent ee7d1bb commit fa1c183
Show file tree
Hide file tree
Showing 33 changed files with 506 additions and 155 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*.userprefs

# Build results
Temp/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]eleases/
Expand Down Expand Up @@ -64,6 +65,9 @@ artifacts/
*.pidb
*.svclog
*.scc
*.ipdb
*.iobj
*.lastcodeanalysissucceeded

# Chutzpah Test files
_Chutzpah*
Expand Down
Binary file modified Build/Release/x64/repacls.exe
Binary file not shown.
Binary file modified Build/Release/x86/repacls.exe
Binary file not shown.
Binary file modified Build/Repacls.zip
Binary file not shown.
3 changes: 3 additions & 0 deletions Build/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ set LIBNAME=Repacls
set LIBURL=https://github.com/NoMoreFood/Repacls

:: do cleanup
DEL "%~dp0*.iobj" /F /S /Q >NUL 2>&1
DEL "%~dp0*.ipdb" /F /S /Q >NUL 2>&1
DEL "%~dp0lastcodeanalysissucceeded" /F /S /Q >NUL 2>&1
RD /S /Q "%~dp0..\.vs" >NUL 2>&1
RD /S /Q "%~dp0..\Temp" >NUL 2>&1
RD /S /Q "%~dp0Debug" >NUL 2>&1
Expand Down
2 changes: 1 addition & 1 deletion Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// helper functions
VOID EnablePrivs();
const PSID GetSidFromName(std::wstring & sAccountName);
PSID GetSidFromName(std::wstring & sAccountName);
std::wstring GetNameFromSid(const PSID tSid, bool * bMarkAsOrphan = nullptr);
std::wstring GetNameFromSidEx(const PSID tSid, bool * bMarkAsOrphan = nullptr);
std::wstring GetDomainNameFromSid(const PSID tSid);
Expand Down
54 changes: 27 additions & 27 deletions Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <windows.h>
#include <sddl.h>
#include <lmcons.h>
#include <stdio.h>
#include <cstdio>
#include <ntsecapi.h>
#include <atlbase.h>
#include <atlstr.h>
Expand All @@ -18,7 +18,7 @@
#include "Operation.h"
#include "Functions.h"

const PSID GetSidFromName(std::wstring & sAccountName)
PSID GetSidFromName(std::wstring & sAccountName)
{
// for caching
static std::shared_mutex oMutex;
Expand All @@ -27,7 +27,7 @@ const PSID GetSidFromName(std::wstring & sAccountName)
// scope lock for thread safety
{
std::shared_lock<std::shared_mutex> oLock(oMutex);
std::map<std::wstring, PSID>::iterator oInteractor = oNameToSidLookup.find(sAccountName);
const auto oInteractor = oNameToSidLookup.find(sAccountName);
if (oInteractor != oNameToSidLookup.end())
{
return oInteractor->second;
Expand Down Expand Up @@ -59,7 +59,7 @@ const PSID GetSidFromName(std::wstring & sAccountName)

// reallocate memory and copy sid to a smaller part of memory and
// then add the sid to the cache map
PSID tSid = (PSID)memcpy(malloc(iSidSize), tSidFromName, iSidSize);
auto tSid = (PSID) memcpy(malloc(iSidSize), tSidFromName, iSidSize);

// scope lock for thread safety
{
Expand All @@ -83,12 +83,12 @@ std::wstring GetNameFromSid(const PSID tSid, bool * bMarkAsOrphan)
// scope lock for thread safety
{
std::shared_lock<std::shared_mutex> oLock(oMutex);
std::map<PSID, std::wstring, SidCompare>::iterator oInteractor = oSidToNameLookup.find(tSid);
auto oInteractor = oSidToNameLookup.find(tSid);
if (oInteractor != oSidToNameLookup.end())
{
// if blank that means the account has no associated same
// and likely is an orphan
if (oInteractor->second == L"" &&
if (oInteractor->second.empty() &&
bMarkAsOrphan != NULL) *bMarkAsOrphan = true;

// return the found full name
Expand Down Expand Up @@ -126,7 +126,7 @@ std::wstring GetNameFromSid(const PSID tSid, bool * bMarkAsOrphan)

// copy the sid for storage in our cache table
const DWORD iSidLength = GetLengthSid(tSid);
PSID tSidCopy = (PSID)memcpy(malloc(iSidLength), tSid, iSidLength);
auto tSidCopy = (PSID)memcpy(malloc(iSidLength), tSid, iSidLength);

// scope lock for thread safety
{
Expand All @@ -142,7 +142,7 @@ std::wstring GetNameFromSidEx(const PSID tSid, bool * bMarkAsOrphan)
{
// if sid is resolvable then return the account name
std::wstring sName = GetNameFromSid(tSid, bMarkAsOrphan);
if (sName != L"") return sName;
if (!sName.empty()) return sName;

// if sid is unresolvable then return sid in string form
WCHAR * sSidBuf;
Expand All @@ -159,7 +159,7 @@ std::wstring GetDomainNameFromSid(const PSID tSid)

// sometimes the domain will be returned as DOMAIN\DOMAIN instead
// of just DOMAIN\ so lets trim off any excess characters
std::wstring::size_type nSlash = sDomainName.find(L"\\");
const std::wstring::size_type nSlash = sDomainName.find(L'\\');
if (nSlash != std::wstring::npos) sDomainName.erase(nSlash + 1);
return sDomainName;
}
Expand All @@ -173,7 +173,7 @@ std::wstring GenerateInheritanceFlags(DWORD iCurrentFlags)
if (INHERIT_ONLY_ACE & iCurrentFlags) sFlags += L"Inherit Only;";

// handle the empty case or trim off the trailing semicolon
if (sFlags.size() == 0) sFlags = L"None";
if (sFlags.empty()) sFlags = L"None";
else sFlags.pop_back();

// return the calculated string
Expand Down Expand Up @@ -219,7 +219,7 @@ std::wstring GenerateAccessMask(DWORD iCurrentMask)

// loop through the mask and construct of string of the names
std::wstring sMaskList;
for (int iMaskEntry = 0; iMaskEntry < _countof(MaskDefinitions) && iCurrentMask > 0; iMaskEntry++)
for (unsigned int iMaskEntry = 0; iMaskEntry < _countof(MaskDefinitions) && iCurrentMask > 0; ++iMaskEntry)
{
if ((MaskDefinitions[iMaskEntry].Mask & iCurrentMask) == MaskDefinitions[iMaskEntry].Mask)
{
Expand All @@ -235,7 +235,7 @@ std::wstring GenerateAccessMask(DWORD iCurrentMask)
}

// handle the empty case or trim off the trailing semicolon
if (sMaskList.size() == 0) sMaskList = L"None";
if (sMaskList.empty()) sMaskList = L"None";
else sMaskList.pop_back();

// return the calculated string
Expand All @@ -255,7 +255,7 @@ VOID EnablePrivs()

// get the current user sid out of the token
BYTE aBuffer[sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE];
PTOKEN_USER tTokenUser = (PTOKEN_USER)(aBuffer);
auto tTokenUser = (PTOKEN_USER)(aBuffer);
DWORD iBytesFilled = 0;
if (GetTokenInformation(hToken, TokenUser, tTokenUser, sizeof(aBuffer), &iBytesFilled) == 0)
{
Expand All @@ -266,18 +266,18 @@ VOID EnablePrivs()

WCHAR * sPrivsToSet[] = { SE_RESTORE_NAME, SE_BACKUP_NAME,
SE_TAKE_OWNERSHIP_NAME, SE_CHANGE_NOTIFY_NAME };
for (int i = 0; i < sizeof(sPrivsToSet) / sizeof(WCHAR *); i++)
for (auto& i : sPrivsToSet)
{
// populate the privilege adjustment structure
TOKEN_PRIVILEGES tPrivEntry;
tPrivEntry.PrivilegeCount = 1;
tPrivEntry.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

// translate the privilege name into the binary representation
if (LookupPrivilegeValue(NULL, sPrivsToSet[i],
if (LookupPrivilegeValue(NULL, i,
&tPrivEntry.Privileges[0].Luid) == 0)
{
wprintf(L"ERROR: Could not lookup privilege: %s\n", sPrivsToSet[i]);
wprintf(L"ERROR: Could not lookup privilege: %s\n", i);
continue;
}

Expand Down Expand Up @@ -306,17 +306,17 @@ VOID EnablePrivs()

// convert the privilege name to a unicode string format
LSA_UNICODE_STRING sPrivilege;
sPrivilege.Buffer = sPrivsToSet[i];
sPrivilege.Length = (USHORT)(wcslen(sPrivsToSet[i]) * sizeof(WCHAR));
sPrivilege.MaximumLength = (USHORT)((wcslen(sPrivsToSet[i]) + 1) * sizeof(WCHAR));
sPrivilege.Buffer = i;
sPrivilege.Length = (USHORT)(wcslen(i) * sizeof(WCHAR));
sPrivilege.MaximumLength = (USHORT)((wcslen(i) + 1) * sizeof(WCHAR));

// attempt to add the account to policy
if ((iResult = LsaAddAccountRights(hPolicyHandle,
tTokenUser->User.Sid, &sPrivilege, 1)) != STATUS_SUCCESS)
{
LsaClose(hPolicyHandle);
wprintf(L"ERROR: Privilege '%s' was not able to be added with error '%lu'\n",
sPrivsToSet[i], LsaNtStatusToWinError(iResult));
i, LsaNtStatusToWinError(iResult));
continue;
}

Expand All @@ -326,14 +326,14 @@ VOID EnablePrivs()
if (AdjustTokenPrivileges(hToken, FALSE, &tPrivEntry,
sizeof(TOKEN_PRIVILEGES), NULL, NULL) == 0 || GetLastError() != ERROR_NOT_ALL_ASSIGNED)
{
wprintf(L"ERROR: Could not adjust privilege: %s\n", sPrivsToSet[i]);
wprintf(L"ERROR: Could not adjust privilege: %s\n", i);
continue;
}

// error if not all items were assigned
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
wprintf(L"ERROR: Could not enable privilege: %s\n", sPrivsToSet[i]);
wprintf(L"ERROR: Could not enable privilege: %s\n", i);
}
}

Expand All @@ -347,7 +347,7 @@ HANDLE RegisterFileHandle(HANDLE hFile, std::wstring sOperation)
static std::map<std::wstring, std::pair<HANDLE,std::wstring>> oFileLookup;

// do a reverse lookup on the file name
DWORD iSize = GetFinalPathNameByHandle(hFile, NULL, 0, VOLUME_NAME_NT);
auto iSize = (size_t) GetFinalPathNameByHandle(hFile, NULL, 0, VOLUME_NAME_NT);

// create a string that can accommodate that size (plus null terminating character)
std::wstring sPath;
Expand All @@ -365,7 +365,7 @@ HANDLE RegisterFileHandle(HANDLE hFile, std::wstring sOperation)
sPath.resize(iSize);

// if the handle already exists, then use that one if the parameters match
std::map<std::wstring, std::pair<HANDLE,std::wstring>>::iterator oFile = oFileLookup.find(sPath);
auto oFile = oFileLookup.find(sPath);
if (oFile != oFileLookup.end())
{
if (oFileLookup[sPath].second == sOperation)
Expand All @@ -389,10 +389,10 @@ HANDLE RegisterFileHandle(HANDLE hFile, std::wstring sOperation)
std::wstring GetAntivirusStateDescription()
{
// initialize COM for checking the antivirus status
HRESULT hResult = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
const HRESULT hResult = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (hResult != S_OK && hResult != S_FALSE)
{
return false;
return L"Unknown";
}

// assume not installed by default
Expand Down Expand Up @@ -477,7 +477,7 @@ BOOL WriteToFile(std::wstring & sStringToWrite, HANDLE hFile)
}

// allocate and do the conversion
BYTE * sString = (BYTE *) malloc(iChars);
auto* sString = (BYTE *) malloc(iChars);
iChars = WideCharToMultiByte(CP_UTF8, 0,
sStringToWrite.c_str(), (int) sStringToWrite.length(),
(LPSTR) sString, iChars, NULL, NULL);
Expand Down
39 changes: 31 additions & 8 deletions InputOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <string>
#include <iostream>

#include "OperationLog.h"

class InputOutput
{
private:
Expand Down Expand Up @@ -45,6 +47,12 @@ class InputOutput
return iMaxThreads;
}

static bool & Log()
{
static bool bLog = false;
return bLog;
}

static std::vector<std::wstring> & ScanPaths()
{
static std::vector<std::wstring> vScanPaths;
Expand All @@ -54,13 +62,13 @@ class InputOutput
static void AddFile(const std::wstring & sLine)
{
// discover the long file name prefix so we can subtract it from the display path
static std::wstring sPrefix = L"FILE: ";
static std::wstring sPrefix;
static size_t iPrefix = (size_t) -1;
if (iPrefix == (size_t) -1)
{
const std::wstring sUnc = L"\\??\\UNC\\";
const std::wstring sLocal = L"\\??\\";
if (sLine.compare(0, sUnc.size(), sUnc.c_str()) == 0) { iPrefix = sUnc.size(); sPrefix += L"\\\\"; }
if (sLine.compare(0, sUnc.size(), sUnc.c_str()) == 0) { iPrefix = sUnc.size(); sPrefix = L"\\\\"; }
else if (sLine.compare(0, sLocal.size(), sLocal.c_str()) == 0) iPrefix = sLocal.size();
else iPrefix = 0;
}
Expand All @@ -69,31 +77,46 @@ class InputOutput
GetDetail() = L"";
}

static void AddInfo(const std::wstring & sLine, std::wstring sPart, bool bMandatory = false)
static void AddInfo(const std::wstring & sLine, const std::wstring & sPart, bool bMandatory = false)
{
if (Log())
{
OperationLog::LogFileItem(L"INFO", GetFileName(), sLine + ((sPart.empty()) ? L"" : L" in " + sPart));
}

if (!InQuietMode() || bMandatory)
{
GetDetail() += L" INFO: " + sLine + ((sPart == L"") ? L"" : L" in " + sPart) + L"\n";
GetDetail() += L" INFO: " + sLine + ((sPart.empty()) ? L"" : L" in " + sPart) + L"\n";
}
}

static void AddWarning(const std::wstring & sLine)
{
if (Log())
{
OperationLog::LogFileItem(L"WARNING", GetFileName(), sLine);
}

GetDetail() += L" WARNING: " + sLine + L"\n";
}

static void AddError(const std::wstring & sLine, const std::wstring & sExtended = L"")
{
if (Log())
{
OperationLog::LogFileItem(L"ERROR", GetFileName(), sLine);
}

GetDetail() += L" ERROR: " + sLine + L"\n";
if (sExtended != L"") GetDetail() += L" ERROR DETAIL: " + sExtended + L"\n";
if (!sExtended.empty()) GetDetail() += L" ERROR DETAIL: " + sExtended + L"\n";
}

static void WriteToScreen()
{
// to to screen if there is anything to write
if (GetFileName().size() > 0 && GetDetail().size() > 0)
// output to screen if there is anything to write
if (!GetFileName().empty() && !GetDetail().empty())
{
wprintf(L"%s", (GetFileName() + GetDetail()).c_str());
wprintf(L"FILE: %s", (GetFileName() + GetDetail()).c_str());
}

// clear out buffer now that it's printed
Expand Down
Loading

0 comments on commit fa1c183

Please sign in to comment.