# c#提权以SYSTEM运行程序后,如何操作注册表

当需要登录界面截图,和模拟键盘,或其它特殊操作,需要提权到SYSTEM

如果此时程序需要操作注册表,就无法使用HKEY_CURRENT_USER来设置当前用户的配置,此时就需要使用HKEY_USERS来设置当前系统登录用户的配置

又因为用的是SYSTEM用户运行程序,所以需要用winapi来获取sid,而无法直接获取

定义一些内容

C#
[DllImport("kernel32.dll")]
public static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
public static extern bool WTSQueryUserToken(int sessionId, out IntPtr token);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(
        nint TokenHandle,
        TOKEN_INFORMATION_CLASS TokenInformationClass,
        nint TokenInformation,
        int TokenInformationLength,
        out int ReturnLength);
[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurity]
public static extern bool ConvertSidToStringSid(IntPtr sid, out string stringSid);

public enum TOKEN_INFORMATION_CLASS
{
    TokenUser = 1,
    TokenGroups,
    TokenPrivileges,
    TokenOwner,
    TokenPrimaryGroup,
    TokenDefaultDacl,
    TokenSource,
    TokenType,
    TokenImpersonationLevel,
    TokenStatistics,
    TokenRestrictedSids,
    TokenSessionId,
    TokenGroupsAndPrivileges,
    TokenSessionReference,
    TokenSandBoxInert,
    TokenAuditPolicy,
    TokenOrigin,
    TokenElevationType,
    TokenLinkedToken,
    TokenElevation,
    TokenHasRestrictions,
    TokenAccessInformation,
    TokenVirtualizationAllowed,
    TokenVirtualizationEnabled,
    TokenIntegrityLevel,
    TokenUIAccess,
    TokenMandatoryPolicy,
    TokenLogonSid,
    MaxTokenInfoClass
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_USER
{
    public SID_AND_ATTRIBUTES User;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct USER_INFO_0
{
    public string usri0_name;
}
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int NetUserEnum(
StringBuilder servername,
int level,
int filter,
out IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
ref int resume_handle
);
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int NetApiBufferFree(IntPtr Buffer);

[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool LookupAccountName(
    string lpSystemName,
    string lpAccountName,
    IntPtr Sid,
    ref int cbSid,
    StringBuilder ReferencedDomainName,
    ref int cchReferencedDomainName,
    out int peUse
);

获取当前登录用户的sid

C#
private static string currentUsername = string.Empty;
public static string GetCurrentUserSid() 
{
    if (OperatingSystem.IsWindows() == false)
    {
        return string.Empty;
    }
    if (OperatingSystem.IsWindows())
    {
        WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
        currentUsername = currentIdentity.Name;
        //不是SYSTEM用户,直接获取当前用户sid
        if (IsSystemUser() == false)
        {
            return currentIdentity.User.Value;
        }
    }
    //获取系统登录用户sid
    IntPtr hToken;
    int sessionId = (int)Kernel32.WTSGetActiveConsoleSessionId();
    if (WTSAPI32.WTSQueryUserToken(sessionId, out hToken))
    {
        try
        {
            IntPtr tokenInformation;
            int returnLength;
            if (GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out returnLength) || returnLength == 0)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            tokenInformation = Marshal.AllocHGlobal(returnLength);
            try
            {
                if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, tokenInformation, returnLength, out returnLength))
                {
                    return string.Empty;
                }

                var user = (TOKEN_USER)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
                string stringSid;
                if (ConvertSidToStringSid(user.User.Sid, out stringSid))
                {
                    return stringSid;
                }
            }
            finally
            {
                Marshal.FreeHGlobal(tokenInformation);
            }
        }
        finally
        {
            if (hToken != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(hToken);
            }
        }
    }
    return string.Empty;
}
public static bool IsSystemUser()
{
    return currentUsername == "NT AUTHORITY\\SYSTEM";
}

未登录系统,获取默认用户sid

当系统只有一个登录用户时,可以作为默认sid,以操作注册表

C#
public static string GetDefaultUserSid()
{
    if (OperatingSystem.IsWindows() == false)
    {
        return string.Empty;
    }

    List<string> registrySids = Registry.Users.GetSubKeyNames().ToList();
    List<string> sids = new List<string>();
    int resumeHandle = 0;
    int result = NetApi32.NetUserEnum(null, 0, 2, out IntPtr bufPtr, -1, out int entriesRead, out int totalEntries, ref resumeHandle);
    if (result == 0)
    {
        try
        {
            for (int i = 0; i < entriesRead; i++)
            {
                USER_INFO_0 userInfo = (USER_INFO_0)Marshal.PtrToStructure(bufPtr+(Marshal.SizeOf(typeof(USER_INFO_0))*i), typeof(USER_INFO_0));

                int cbSid = 0;
                int cchReferencedDomainName = 0;
                int peUse;
                StringBuilder referencedDomainName = new StringBuilder();
                IntPtr pSid = IntPtr.Zero;

                bool bSuccess = LookupAccountName(null, userInfo.usri0_name, pSid, ref cbSid, referencedDomainName, ref cchReferencedDomainName, out peUse);
                if (!bSuccess && cbSid > 0)
                {
                    pSid = Marshal.AllocHGlobal(cbSid);
                    referencedDomainName.EnsureCapacity(cchReferencedDomainName);
                    bSuccess = LookupAccountName(null, userInfo.usri0_name, pSid, ref cbSid, referencedDomainName, ref cchReferencedDomainName, out peUse);
                }
                
                if (bSuccess)
                {
                    if (ConvertSidToStringSid(pSid, out string stringSid))
                    {
                        if (registrySids.Contains(stringSid))
                        {
                            sids.Add(stringSid);
                        }
                    }
                }

                if (pSid != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pSid);
                }
            }
        }
        catch (Exception)
        {
        }
        finally
        {
            NetApi32.NetApiBufferFree(bufPtr);
        }
    }

    if(sids.Count == 1)
    {
        return sids[0];
    }
    return string.Empty;
}

可以配置注册表了

C#
HKEY_USERS\{sid}\xxx