Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1003 lines (861 sloc) 35.6 KB
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.DirectoryServices.Protocols;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Sharphound2.OutputObjects;
using SearchScope = System.DirectoryServices.Protocols.SearchScope;
namespace Sharphound2.Enumeration
{
internal class ApiFailedException : Exception {}
internal class SystemDownException : Exception { }
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
internal static class LocalAdminHelpers
{
private static Cache _cache;
private static Utils _utils;
private static readonly Regex SectionRegex = new Regex(@"^\[(.+)\]", RegexOptions.Compiled);
private static readonly Regex KeyRegex = new Regex(@"(.+?)\s*=(.*)", RegexOptions.Compiled);
private static readonly string[] Props =
{"samaccountname", "samaccounttype", "dnshostname", "serviceprincipalname", "distinguishedname"};
private static readonly string[] GpoProps =
{"samaccounttype", "dnshostname", "distinguishedname", "serviceprincipalname", "samaccountname"};
private static readonly string[] GpLinkProps = {"distinguishedname"};
private static readonly string[] AdminProps = {"samaccountname", "dnshostname", "distinguishedname", "samaccounttype"};
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(20);
private static byte[] _sidbytes;
public static void Init()
{
_cache = Cache.Instance;
_utils = Utils.Instance;
var sid = new SecurityIdentifier("S-1-5-32");
_sidbytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(_sidbytes, 0);
}
private static SamEnumerationObject[] NetLocalGroupGetMembers(ResolvedEntry entry, out string machineSid)
{
Utils.Debug("Starting NetLocalGroupGetMembers");
var server = new UNICODE_STRING(entry.BloodHoundDisplay);
//Connect to the server with the proper access maskes. This gives us our server handle
var obj = default(OBJECT_ATTRIBUTES);
Utils.Debug($"Starting SamConnect");
var status = SamConnect(ref server, out var serverHandle,
SamAccessMasks.SamServerLookupDomain |
SamAccessMasks.SamServerConnect, ref obj);
Utils.Debug($"SamConnect returned {status}");
switch (status)
{
case NtStatus.StatusRpcServerUnavailable:
throw new SystemDownException();
case NtStatus.StatusSuccess:
break;
default:
throw new ApiFailedException();
}
Utils.Debug($"Starting SamLookupDomainInSamServer");
//Use SamLookupDomainInServer with the hostname to find the machine sid if possible
try
{
var san = new UNICODE_STRING(entry.ComputerSamAccountName);
SamLookupDomainInSamServer(serverHandle, ref san, out var temp);
//This will throw an exception if we didn't actually find the alias
machineSid = new SecurityIdentifier(temp).Value;
SamFreeMemory(temp);
}
catch
{
machineSid = "DUMMYSTRINGSHOULDNOTMATCH";
}
Utils.Debug($"Resolved Machine Sid {machineSid}");
Utils.Debug($"Starting SamOpenDomain");
//Open the domain for the S-1-5-32 (BUILTIN) alias
status = SamOpenDomain(serverHandle, DomainAccessMask.Lookup, _sidbytes, out var domainHandle);
Utils.Debug($"SamOpenDomain returned {status}");
if (!status.Equals(NtStatus.StatusSuccess))
{
SamCloseHandle(serverHandle);
throw new ApiFailedException();
}
Utils.Debug($"Starting SamOpenAlias");
//Open the alias for Local Administrators (always RID 544)
status = SamOpenAlias(domainHandle, AliasOpenFlags.ListMembers, 544, out var aliasHandle);
Utils.Debug($"SamOpenAlias returned {status}");
if (!status.Equals(NtStatus.StatusSuccess))
{
SamCloseHandle(domainHandle);
SamCloseHandle(serverHandle);
throw new ApiFailedException();
}
Utils.Debug($"Starting SamGetMembersInAlias");
//Get the members in the alias. This returns a list of SIDs
status = SamGetMembersInAlias(aliasHandle, out var members, out var count);
Utils.Debug($"SamGetMembersInAlias returned {status}");
if (!status.Equals(NtStatus.StatusSuccess))
{
SamCloseHandle(aliasHandle);
SamCloseHandle(domainHandle);
SamCloseHandle(serverHandle);
throw new ApiFailedException();
}
Utils.Debug($"Cleaning up handles");
SamCloseHandle(aliasHandle);
SamCloseHandle(domainHandle);
SamCloseHandle(serverHandle);
if (count == 0)
{
SamFreeMemory(members);
return new SamEnumerationObject[0];
}
Utils.Debug($"Copying data");
//Copy the data of our sids to a new array so it doesn't get eaten
var grabbedSids = new IntPtr[count];
Marshal.Copy(members, grabbedSids, 0, count);
var sids = new string[count];
//Convert the bytes to strings for usage
for (var i = 0; i < count; i++)
{
sids[i] = new SecurityIdentifier(grabbedSids[i]).Value;
}
Utils.Debug($"Starting LsaOpenPolicy");
//Open the LSA policy on the target machine
var obja = default(OBJECT_ATTRIBUTES);
status = LsaOpenPolicy(ref server, ref obja,
LsaOpenMask.ViewLocalInfo | LsaOpenMask.LookupNames, out var policyHandle);
Utils.Debug($"LSAOpenPolicy returned {status}");
if (!status.Equals(NtStatus.StatusSuccess))
{
LsaClose(policyHandle);
SamFreeMemory(members);
throw new ApiFailedException();
}
Utils.Debug($"Starting LSALookupSids");
var nameList = IntPtr.Zero;
var domainList = IntPtr.Zero;
//Call LsaLookupSids using the sids we got from SamGetMembersInAlias
status = LsaLookupSids(policyHandle, count, members, ref domainList,
ref nameList);
Utils.Debug($"LSALookupSids returned {status}");
if (!status.Equals(NtStatus.StatusSuccess) && !status.Equals(NtStatus.StatusSomeMapped))
{
LsaClose(policyHandle);
LsaFreeMemory(domainList);
LsaFreeMemory(nameList);
SamFreeMemory(members);
throw new ApiFailedException();
}
Utils.Debug($"Finished API calls");
//Convert the returned names into structures
var iter = nameList;
var translatedNames = new LsaTranslatedNames[count];
Utils.Debug($"Resolving names");
for (var i = 0; i < count; i++)
{
translatedNames[i] = (LsaTranslatedNames)Marshal.PtrToStructure(iter, typeof(LsaTranslatedNames));
iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf(typeof(LsaTranslatedNames)));
}
Utils.Debug($"Resolving domains");
//Convert the returned domain list to a structure
var lsaDomainList =
(LsaReferencedDomainList)(Marshal.PtrToStructure(domainList, typeof(LsaReferencedDomainList)));
//Convert the domain list to individual structures
var trustInfos = new LsaTrustInformation[lsaDomainList.count];
iter = lsaDomainList.domains;
for (var i = 0; i < lsaDomainList.count; i++)
{
trustInfos[i] = (LsaTrustInformation)Marshal.PtrToStructure(iter, typeof(LsaTrustInformation));
iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf(typeof(LsaTrustInformation)));
}
Utils.Debug($"Matching up data");
var resolvedObjects = new SamEnumerationObject[translatedNames.Length];
//Match up sids, domain names, and account names
for (var i = 0; i < translatedNames.Length; i++)
{
var x = translatedNames[i];
if (x.DomainIndex > trustInfos.Length || x.DomainIndex < 0 || trustInfos.Length == 0)
continue;
resolvedObjects[i] =
new SamEnumerationObject
{
AccountDomain = trustInfos[x.DomainIndex].name.ToString(),
AccountName = x.Name.ToString(),
AccountSid = sids[i],
SidUsage = x.Use
};
}
Utils.Debug($"Cleaning up");
//Cleanup
SamFreeMemory(members);
LsaFreeMemory(domainList);
LsaFreeMemory(nameList);
LsaClose(policyHandle);
Utils.Debug($"Done NetLocalGroupGetMembers");
return resolvedObjects;
}
public static IEnumerable<LocalAdmin> GetSamAdmins(ResolvedEntry entry)
{
Utils.Debug("Starting GetSamAdmins");
string machineSid = null;
Utils.Debug("Starting Task");
var t = Task<SamEnumerationObject[]>.Factory.StartNew(() =>
{
try
{
return NetLocalGroupGetMembers(entry, out machineSid);
}
catch (ApiFailedException)
{
return new SamEnumerationObject[0];
}
catch (SystemDownException)
{
return new SamEnumerationObject[0];
}
});
var success = t.Wait(Timeout);
Utils.Debug("Task Finished");
if (!success)
{
Utils.Debug("SamAdmin Timeout");
throw new TimeoutException();
}
Utils.Debug("SamAdmin success");
var resolvedObjects = t.Result;
if (resolvedObjects.Length == 0)
{
Utils.Debug("SamAdmins returned 0 objects");
yield break;
}
Utils.Debug("Processing data");
//Process our list of stuff now
foreach (var data in resolvedObjects)
{
var sid = data?.AccountSid;
Utils.Debug($"Processing sid: {sid}");
if (sid == null)
{
Utils.Debug("Null sid");
continue;
}
if (data.AccountName.Equals(string.Empty))
{
Utils.Debug("Empty AccountName");
continue;
}
if (sid.StartsWith(machineSid))
{
Utils.Debug("Local Account");
continue;
}
string type;
switch (data.SidUsage)
{
case SidNameUse.SidTypeUser:
type = "user";
break;
case SidNameUse.SidTypeGroup:
type = "group";
break;
case SidNameUse.SidTypeComputer:
type = "computer";
break;
case SidNameUse.SidTypeWellKnownGroup:
type = "wellknown";
break;
default:
type = null;
break;
}
if (type == null)
continue;
if (data.AccountName.EndsWith("$"))
type = "unknown";
string resolvedName;
Utils.Debug($"Object Type: {type}");
if (type.Equals("unknown"))
{
Utils.Debug("Resolving Sid to object");
var mp = _utils.UnknownSidTypeToDisplay(sid, _utils.SidToDomainName(sid),
AdminProps);
if (mp == null)
continue;
Utils.Debug($"Got Object: {mp.PrincipalName}");
resolvedName = mp.PrincipalName;
type = mp.ObjectType;
}
else
{
Utils.Debug("Resolving Sid to Object");
resolvedName = _utils.SidToDisplay(sid, _utils.SidToDomainName(sid), AdminProps, type);
if (resolvedName == null)
continue;
Utils.Debug($"Got Object: {resolvedName}");
}
yield return new LocalAdmin
{
ObjectType = type,
ObjectName = resolvedName,
Server = entry.BloodHoundDisplay
};
}
}
#region hidden
//public static List<LocalAdmin> LocalGroupWinNt(string target, string group)
//{
// var members = new DirectoryEntry($"WinNT://{target}/{group},group");
// var localAdmins = new List<LocalAdmin>();
// try
// {
// foreach (var member in (System.Collections.IEnumerable)members.Invoke("Members"))
// {
// using (var m = new DirectoryEntry(member))
// {
// //Convert sid bytes to a string
// var sidstring = new SecurityIdentifier(m.GetSid(), 0).ToString();
// string type;
// switch (m.SchemaClassName)
// {
// case "Group":
// type = "group";
// break;
// case "User":
// //If its a user but the name ends in $, it's actually a computer (probably)
// type = m.Properties["Name"][0].ToString().EndsWith("$", StringComparison.Ordinal) ? "computer" : "user";
// break;
// default:
// type = "group";
// break;
// }
// //Start by checking the cache
// if (!_cache.GetMapValue(sidstring, type, out var adminName))
// {
// //Get the domain from the SID
// var domainName = _utils.SidToDomainName(sidstring);
// //Search for the object in AD
// var entry = _utils
// .DoSearch($"(objectsid={sidstring})", SearchScope.Subtree, AdminProps, domainName)
// .DefaultIfEmpty(null).FirstOrDefault();
// //If it's not null, we have an object, yay! Otherwise, meh
// if (entry != null)
// {
// adminName = entry.ResolveAdEntry().BloodHoundDisplay;
// _cache.AddMapValue(sidstring, type, adminName);
// }
// else
// {
// adminName = null;
// }
// }
// if (adminName != null)
// {
// localAdmins.Add(new LocalAdmin { ObjectName = adminName, ObjectType = type, Server = target });
// }
// }
// }
// }
// catch (COMException)
// {
// //You can get a COMException, so just return a blank array
// return localAdmins;
// }
// return localAdmins;
//}
//public static List<LocalAdmin> LocalGroupApi(string target, string group, string domainName, string domainSid)
//{
// const int queryLevel = 2;
// var resumeHandle = IntPtr.Zero;
// var machineSid = "DUMMYSTRING";
// var LMI2 = typeof(LOCALGROUP_MEMBERS_INFO_2);
// var returnValue = NetLocalGroupGetMembers(target, group, queryLevel, out IntPtr ptrInfo, -1, out int entriesRead, out int _, resumeHandle);
// //Return value of 1722 indicates the system is down, so no reason to fallback to WinNT
// if (returnValue == 1722)
// {
// throw new SystemDownException();
// }
// //If its not 0, something went wrong, but we can fallback to WinNT provider. Throw an exception
// if (returnValue != 0)
// {
// throw new ApiFailedException();
// }
// var toReturn = new List<LocalAdmin>();
// if (entriesRead <= 0) return toReturn;
// var iter = ptrInfo;
// var list = new List<API_Encapsulator>();
// //Loop through the data and save them into a list for processing
// for (var i = 0; i < entriesRead; i++)
// {
// var data = (LOCALGROUP_MEMBERS_INFO_2)Marshal.PtrToStructure(iter, LMI2);
// ConvertSidToStringSid(data.lgrmi2_sid, out string sid);
// list.Add(new API_Encapsulator
// {
// Lgmi2 = data,
// sid = sid
// });
// iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf(LMI2));
// }
// NetApiBufferFree(ptrInfo);
// //Try and determine the machine sid
// foreach (var data in list)
// {
// if (data.sid == null)
// {
// continue;
// }
// //If the sid ends with -500 and doesn't start with the DomainSID, there's a very good chance we've identified the RID500 account
// //Take the machine sid from there. If we don't find it, we use a dummy string
// if (!data.sid.EndsWith("-500", StringComparison.Ordinal) ||
// data.sid.StartsWith(domainSid, StringComparison.Ordinal)) continue;
// machineSid = new SecurityIdentifier(data.sid).AccountDomainSid.Value;
// break;
// }
// foreach (var data in list)
// {
// if (data.sid == null)
// continue;
// var objectName = data.Lgmi2.lgrmi2_domainandname;
// if (objectName.Split('\\').Last().Equals(""))
// {
// //Sometimes we get weird objects that are just a domain name with no user behind it.
// continue;
// }
// if (data.sid.StartsWith(machineSid, StringComparison.Ordinal))
// {
// //This should filter out local accounts
// continue;
// }
// string type;
// switch (data.Lgmi2.lgrmi2_sidusage)
// {
// case SID_NAME_USE.SidTypeUser:
// type = "user";
// break;
// case SID_NAME_USE.SidTypeGroup:
// type = "group";
// break;
// case SID_NAME_USE.SidTypeComputer:
// type = "computer";
// break;
// case SID_NAME_USE.SidTypeWellKnownGroup:
// type = "wellknown";
// break;
// default:
// type = null;
// break;
// }
// //I have no idea what would cause this condition
// if (type == null)
// {
// continue;
// }
// if (objectName.EndsWith("$", StringComparison.Ordinal))
// {
// type = "computer";
// }
// var resolved = _utils.SidToDisplay(data.sid, _utils.SidToDomainName(data.sid), AdminProps, type);
// if (resolved == null)
// {
// continue;
// }
// toReturn.Add(new LocalAdmin
// {
// ObjectName = resolved,
// ObjectType = type,
// Server = target
// });
// }
// return toReturn;
//}
#endregion
public static IEnumerable<LocalAdmin> GetGpoAdmins(SearchResultEntry entry, string domainName)
{
const string targetSid = "S-1-5-32-544__Members";
var displayName = entry.GetProp("displayname");
var name = entry.GetProp("name");
var path = entry.GetProp("gpcfilesyspath");
if (displayName == null || name == null || path == null)
{
yield break;
}
var template = $"{path}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf";
var currentSection = string.Empty;
var resolvedList = new List<MappedPrincipal>();
if (!File.Exists(template))
yield break;
using (var reader = new StreamReader(template))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var sMatch = SectionRegex.Match(line);
if (sMatch.Success)
{
currentSection = sMatch.Captures[0].Value.Trim();
}
if (!currentSection.Equals("[Group Membership]"))
{
continue;
}
var kMatch = KeyRegex.Match(line);
if (!kMatch.Success)
continue;
var n = kMatch.Groups[1].Value;
var v = kMatch.Groups[2].Value;
if (!n.Contains(targetSid))
continue;
v = v.Trim();
var members = v.Split(',');
foreach (var m in members)
{
var member = m.Trim('*');
string sid;
if (!member.StartsWith("S-1-", StringComparison.CurrentCulture))
{
try
{
sid = new NTAccount(domainName, m).Translate(typeof(SecurityIdentifier)).Value;
}
catch
{
sid = null;
}
}
else
{
sid = member;
}
if (sid == null)
continue;
var domain = _utils.SidToDomainName(sid) ?? domainName;
var resolvedPrincipal = _utils.UnknownSidTypeToDisplay(sid, domain, Props);
if (resolvedPrincipal != null)
resolvedList.Add(resolvedPrincipal);
}
}
}
foreach (var ouObject in _utils.DoSearch($"(gplink=*{name}*)", SearchScope.Subtree, GpLinkProps, domainName))
{
var adspath = ouObject.DistinguishedName;
foreach (var compEntry in _utils.DoSearch("(objectclass=computer)", SearchScope.Subtree, GpoProps,
domainName, adspath))
{
var samAccountType = compEntry.GetProp("samaccounttype");
if (samAccountType == null || samAccountType != "805306369")
continue;
var server = compEntry.ResolveAdEntry()?.BloodHoundDisplay;
if (server == null)
continue;
foreach (var user in resolvedList)
{
yield return new LocalAdmin
{
ObjectName = user.PrincipalName,
ObjectType = user.ObjectType,
Server = server
};
}
}
}
}
private class SamEnumerationObject
{
internal string AccountName { get; set; }
internal string AccountDomain { get; set; }
internal string AccountSid { get; set; }
internal SidNameUse SidUsage { get; set; }
}
#region LSA Imports
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus LsaLookupSids(
IntPtr policyHandle,
int count,
IntPtr enumBuffer,
ref IntPtr domainList,
ref IntPtr nameList
);
[DllImport("advapi32.dll")]
private static extern NtStatus LsaOpenPolicy(
ref UNICODE_STRING server,
ref OBJECT_ATTRIBUTES objectAttributes,
LsaOpenMask desiredAccess,
out IntPtr policyHandle
);
[DllImport("advapi32.dll")]
private static extern NtStatus LsaFreeMemory(
IntPtr buffer
);
[DllImport("advapi32.dll")]
private static extern NtStatus LsaClose(
IntPtr buffer
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct LsaTrustInformation
{
internal LsaUnicodeString name;
private IntPtr sid;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct LsaUnicodeString
{
private ushort length;
private ushort maxLen;
[MarshalAs(UnmanagedType.LPWStr)] private string name;
public override string ToString()
{
return $"{name.Substring(0, length / 2)}";
}
}
#pragma warning disable 649
private struct LsaTranslatedNames
{
internal SidNameUse Use;
internal LsaUnicodeString Name;
internal int DomainIndex;
}
#pragma warning restore 649
private struct LsaReferencedDomainList
{
internal uint count;
internal IntPtr domains;
}
private enum SidNameUse
{
SidTypeUser = 1,
SidTypeGroup,
SidTypeDomain,
SidTypeAlias,
SidTypeWellKnownGroup,
SidTypeDeletedAccount,
SidTypeInvalid,
SidTypeUnknown,
SidTypeComputer
}
#endregion
#region SAMR Imports
[DllImport("samlib.dll")]
private static extern NtStatus SamCloseHandle(
IntPtr handle
);
[DllImport("samlib.dll")]
private static extern NtStatus SamFreeMemory(
IntPtr handle
);
[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus SamLookupDomainInSamServer(
IntPtr serverHandle,
ref UNICODE_STRING name,
out IntPtr sid);
[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus SamGetMembersInAlias(
IntPtr aliasHandle,
out IntPtr members,
out int count);
[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus SamOpenAlias(
IntPtr domainHandle,
AliasOpenFlags desiredAccess,
int aliasId,
out IntPtr aliasHandle
);
[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus SamConnect(
ref UNICODE_STRING serverName,
out IntPtr serverHandle,
SamAccessMasks desiredAccess,
ref OBJECT_ATTRIBUTES objectAttributes
);
//[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
//private static extern NTSTATUS SamEnumerateAliasesInDomain(
// IntPtr domainHandle,
// ref int enumerationContext,
// out IntPtr buffer,
// int preferredMaxLen,
// out int count
// );
//[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
//private static extern NTSTATUS SamOpenAlias(
// IntPtr domainHandle,
// SamAliasFlags desiredAccess,
// int aliasId,
// out IntPtr aliasHandle
//);
[DllImport("samlib.dll", CharSet = CharSet.Unicode)]
private static extern NtStatus SamOpenDomain(
IntPtr serverHandle,
DomainAccessMask desiredAccess,
[MarshalAs(UnmanagedType.LPArray)]byte[] domainSid,
out IntPtr domainHandle
);
[Flags]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum AliasOpenFlags
{
AddMember = 0x1,
RemoveMember = 0x2,
ListMembers = 0x4,
ReadInfo = 0x8,
WriteAccount = 0x10,
AllAccess = 0xf001f,
Read = 0x20004,
Write = 0x20013,
Execute = 0x20008
}
[Flags]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum LsaOpenMask
{
ViewLocalInfo = 0x1,
ViewAuditInfo = 0x2,
GetPrivateInfo = 0x4,
TrustAdmin = 0x8,
CreateAccount = 0x10,
CreateSecret = 0x20,
CreatePrivilege = 0x40,
SetDefaultQuotaLimits = 0x80,
SetAuditRequirements = 0x100,
AuditLogAdmin = 0x200,
ServerAdmin = 0x400,
LookupNames = 0x800,
Notification = 0x1000
}
[Flags]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum DomainAccessMask
{
ReadPasswordParameters = 0x1,
WritePasswordParameters = 0x2,
ReadOtherParameters = 0x4,
WriteOtherParameters = 0x8,
CreateUser = 0x10,
CreateGroup = 0x20,
CreateAlias = 0x40,
GetAliasMembership = 0x80,
ListAccounts = 0x100,
Lookup = 0x200,
AdministerServer = 0x400,
AllAccess = 0xf07ff,
Read = 0x20084,
Write = 0x2047A,
Execute = 0x20301
}
[Flags]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum SamAliasFlags
{
AddMembers = 0x1,
RemoveMembers = 0x2,
ListMembers = 0x4,
ReadInfo = 0x8,
WriteAccount = 0x10,
AllAccess = 0xf001f,
Read = 0x20004,
Write = 0x20013,
Execute = 0x20008
}
[Flags]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum SamAccessMasks
{
SamServerConnect = 0x1,
SamServerShutdown = 0x2,
SamServerInitialize = 0x4,
SamServerCreateDomains = 0x8,
SamServerEnumerateDomains = 0x10,
SamServerLookupDomain = 0x20,
SamServerAllAccess = 0xf003f,
SamServerRead = 0x20010,
SamServerWrite = 0x2000e,
SamServerExecute = 0x20021
}
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING : IDisposable
{
private ushort Length;
private ushort MaximumLength;
private IntPtr Buffer;
public UNICODE_STRING(string s)
: this()
{
if (s == null) return;
Length = (ushort)(s.Length * 2);
MaximumLength = (ushort)(Length + 2);
Buffer = Marshal.StringToHGlobalUni(s);
}
public void Dispose()
{
if (Buffer == IntPtr.Zero) return;
Marshal.FreeHGlobal(Buffer);
Buffer = IntPtr.Zero;
}
public override string ToString()
{
return (Buffer != IntPtr.Zero ? Marshal.PtrToStringUni(Buffer) : null) ?? throw new InvalidOperationException();
}
}
#pragma warning disable 169
[SuppressMessage("ReSharper", "InconsistentNaming")]
private struct OBJECT_ATTRIBUTES : IDisposable
{
public void Dispose()
{
if (objectName == IntPtr.Zero) return;
Marshal.DestroyStructure(objectName, typeof(UNICODE_STRING));
Marshal.FreeHGlobal(objectName);
objectName = IntPtr.Zero;
}
public int len;
public IntPtr rootDirectory;
public uint attribs;
public IntPtr sid;
public IntPtr qos;
private IntPtr objectName;
public UNICODE_STRING ObjectName;
}
#pragma warning restore 169
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum NtStatus
{
StatusSuccess = 0x0,
StatusMoreEntries = 0x105,
StatusSomeMapped = 0x107,
StatusInvalidHandle = unchecked((int)0xC0000008),
StatusInvalidParameter = unchecked((int)0xC000000D),
StatusAccessDenied = unchecked((int)0xC0000022),
StatusObjectTypeMismatch = unchecked((int)0xC0000024),
StatusNoSuchDomain = unchecked((int)0xC00000DF),
StatusRpcServerUnavailable = unchecked((int)0xC0020017)
}
#endregion
//#region pinvoke-imports
//[DllImport("NetAPI32.dll", CharSet = CharSet.Unicode)]
//private static extern int NetLocalGroupGetMembers(
// [MarshalAs(UnmanagedType.LPWStr)] string servername,
// [MarshalAs(UnmanagedType.LPWStr)] string localgroupname,
// int level,
// out IntPtr bufptr,
// int prefmaxlen,
// out int entriesread,
// out int totalentries,
// IntPtr resume_handle);
//[DllImport("Netapi32.dll", SetLastError = true)]
//private static extern int NetApiBufferFree(IntPtr buff);
//[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
//public struct LOCALGROUP_MEMBERS_INFO_2
//{
// public IntPtr lgrmi2_sid;
// public SidNameUse lgrmi2_sidusage;
// [MarshalAs(UnmanagedType.LPWStr)]
// public string lgrmi2_domainandname;
//}
//public class API_Encapsulator
//{
// public LOCALGROUP_MEMBERS_INFO_2 Lgmi2 { get; set; }
// public string sid;
//}
//[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
//private static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);
//#endregion
}
}