Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 4개 있습니다.)
(시리즈 글이 4개 있습니다.)
Windows: 158. 컴퓨터와 사용자의 SID(security identifier) 확인 방법
; https://www.sysnet.pe.kr/2/0/11819

.NET Framework: 2084. C# - GetTokenInformation으로 사용자 SID(Security identifiers) 구하는 방법
; https://www.sysnet.pe.kr/2/0/13206

.NET Framework: 2085. C# - gpedit.msc의 "User Rights Assignment" 특권을 코드로 설정/해제하는 방법
; https://www.sysnet.pe.kr/2/0/13207

.NET Framework: 2130. C# - Win32 API를 이용한 윈도우 계정 정보 (예: 마지막 로그온 시간)
; https://www.sysnet.pe.kr/2/0/13382




C# - GetTokenInformation으로 사용자 SID(Security identifiers) 구하는 방법

사용자의 SID는 이미 BCL을 이용해 간단하게 구할 수 있습니다.

Console.WriteLine(WindowsIdentity.GetCurrent().User?.Value);

// 출력 결과: S-1-5-21-1510216573-2196513108-161129836-1001
// 2.4.2.4 Well-Known SID Structures
// ; https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab

하지만 이 글에서는 (그냥 재미 삼아) Win32 API를 통한 방법을 설명할 텐데요,

GetTokenInformation
; https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation

C++로는 많이 알려져 있으니 이번 글에서는 C#으로 ^^ 구현해 보겠습니다.

using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Principal;

[assembly: SupportedOSPlatform("windows")]

internal class Program
{
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);

    private static uint ERROR_INSUFFICIENT_BUFFER = 122;

    static void Main(string[] args)
    {
        string sidText;
        TOKEN_USER? tokenUser = GeTokenUser(out sidText);

        if (tokenUser == null)
        {
            return;
        }

        Console.WriteLine($"SID Found: {sidText}");
    }

    static unsafe TOKEN_USER? GeTokenUser(out string sid)
    {
        sid = "";
        IntPtr hToken = WindowsIdentity.GetCurrent().Token;

        uint dwBufferSize;
        if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out dwBufferSize))
        {
            int win32Result = Marshal.GetLastWin32Error();
            if (win32Result != ERROR_INSUFFICIENT_BUFFER)
            {
                Console.WriteLine($"GetTokenInformation failed. GetLastError returned: {win32Result}");
                return null;
            }
        }

        IntPtr tokenInformation = Marshal.AllocHGlobal((int)dwBufferSize);
        if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, tokenInformation, dwBufferSize, out dwBufferSize))
        {
            Console.WriteLine($"GetTokenInformation failed. GetLastError returned: {Marshal.GetLastWin32Error()}");
            return null;
        }

        try
        {
            TOKEN_USER? tokenUser = (TOKEN_USER?)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
            if (tokenUser == null)
            {
                return null;
            }

            IntPtr pstr = IntPtr.Zero;
            if (ConvertSidToStringSid(tokenUser.Value.User.Sid, out pstr) == true)
            {
                sid = Marshal.PtrToStringAuto(pstr) ?? "";
                Marshal.FreeHGlobal(pstr);
            }

            return tokenUser;
        }
        finally
        {
            Marshal.FreeHGlobal(tokenInformation);
        }
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
    public IntPtr Sid;
    public int Attributes;
}

[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_USER
{
    public SID_AND_ATTRIBUTES User;
    public SecurityIdentifier Sid;
}

[StructLayout(LayoutKind.Sequential)]
public unsafe struct SecurityIdentifier
{
    public byte Revision;
    public byte SubAuthorityCount;
    public fixed byte IdentifierAuthority[6];
    public fixed int SubAuthority[1];
}

internal enum TOKEN_INFORMATION_CLASS
{
    TokenUser = 1,
}

위의 코드를 보면 다음의 순서로 sid를 구하는데요,

  1. GetTokenInformation을 호출해 사용자 보안 토큰의 크기를 구하고,
  2. 그 크기만큼을 메모리에 할당한 다음,
  3. 다시 GetTokenInformation을 호출해 보안 토큰 정보를 반환
  4. 그 보안 토큰으로부터 SID 문자열 얻기

여기서 재미있는 것은 TOKEN_USER 타입의 구조입니다.

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_user
typedef struct _TOKEN_USER {
  SID_AND_ATTRIBUTES User;
} TOKEN_USER, *PTOKEN_USER;

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_and_attributes
typedef struct _SID_AND_ATTRIBUTES {
    PSID Sid;
    DWORD Attributes;
} SID_AND_ATTRIBUTES, * PSID_AND_ATTRIBUTES;

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
typedef struct _SID {
   BYTE  Revision;
   BYTE  SubAuthorityCount;
   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
   DWORD SubAuthority[1]; // SubAuthorityCount 수만큼 배열이 동적으로 결정됨
} SID, *PISID;

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority
typedef struct _SID_IDENTIFIER_AUTHORITY {
  BYTE Value[6];
} SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY;

보는 바와 같이 TOKEN_USER는 내부에 SID를 향한 포인터를 담고 있으며 다시 그 _SID의 마지막 멤버인 SubAuthority는 그 크기가 정해지지 않은, 즉, 사용자에 따라 동적으로 달라지는 배열을 포함하고 있습니다.

바로 이러한 동적 크기의 성격 때문에 GetTokenInformation API는 크기를 먼저 얻게 한 다음, 사용자 측에서 그 크기만큼 메모리를 할당하게 만들고, 그것을 다시 GetTokenInformation에 전달해 TOKEN_USER 구조체의 모든 내용을 반환받는 식으로 동작하는 것입니다.

그리고 여기서 또 한 가지 재미있는 점은, SID_AND_ATTRIBUTES 구조체의 멤버인 Sid가 포인터이긴 하지만, 그리 멀리 있지 않은, 즉 GetTokenInformation이 반환한 크기 이내에 있는 위치를 가리킨다는 점입니다. 다시 말해, GetTokenInformation이 44를 반환했다면, 이 구조체는 다음과 같은 유형으로 정의가 됩니다.

// x64인 경우, GetTokenInformation이 요구한 크기가 44바이트로 가정

offset: 0x0 - TOKEN_USER의 시작, 즉 SID_AND_ATTRIBUTES의 시작, 결국 Sid 8바이트 포인터 멤버
              이 포인터는 현재로부터 0x10 이후의 위치를 가리킴
offset: 0x8 - Attributes 4바이트
offset: 0xc - 4바이트 패딩
offset: 0x10 - 이하 (44 - 16) 28바이트까지 SID 구조체 내용 포함
               BYTE  Revision 필드 (현재는 1이지만 향후 개정판이 나온다면 변경)
offset: 0x11 - BYTE  SubAuthorityCount 필드
offset: 0x12 - BYTE Value[6] == SID_IDENTIFIER_AUTHORITY IdentifierAuthority
offset: 0x18 - DWORD SubAuthority[SubAuthorityCount]

보는 바와 같이 포인터가 구조체 내부의 영역을 가리키는 식입니다. 따라서 만약 "Marshal.AllocHGlobal((int)dwBufferSize);"로 할당한 메모리의 주소가 0x1000이라면, Sid 포인터의 값은 0x1010입니다. (TOKEN_USER와 SID_AND_ATTRIBUTES 구조체가 향후 달라질 가능성은 거의 없긴 해도 하드 코딩하는 것은 끊임없는 주의를 요합니다. ^^)




어쨌든, 이번 글의 주제에 따라 SID는 ConvertSidToStringSid API를 호출하는 시점에 안전하게 구할 수 있었지만, 혹시 기왕에 구한 tokenUser 인스턴스를 재사용하는 것이 가능할까요?

바로 전에 설명한 TOKEN_USER 인스턴스의 메모리 구조를 염두에 두고, 아래의 코드를 자세하게 다시 살펴보겠습니다. ^^

IntPtr tokenInformation = Marshal.AllocHGlobal((int)dwBufferSize);
GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, tokenInformation, dwBufferSize, out dwBufferSize);

try
{
    TOKEN_USER? tokenUser = (TOKEN_USER?)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
    return tokenUser;
}
finally
{
    Marshal.FreeHGlobal(tokenInformation);
}

얼핏 보면 Marshal.PtrToStructure를 통해 값 복사를 했으므로 이후 독자적으로 tokenUser 인스턴스를 사용해도 될 것 같은데요, 하지만, 이렇게 반환한 tokenUser 인스턴스는 finally에 의해 해제되는 tokenInformation으로 인해 향후 사용 시 오류가 발생하게 됩니다. 즉, 반환한 tokenUser 인스턴스는 더 이상 유효하지 않은 값이 된 것입니다.

왜냐하면, TOKEN_USER의 값 자체는 복사되었지만, PSID 포인터가 가리키는 영역이 Marshal.FreeHGlobal에 의해 해제가 되었으므로 더 이상 유효하지 않는 포인터가 됐기 때문입니다. 이로 인해, 아쉽지만 TOKEN_USER 인스턴스가 필요하다면 저런 식으로 메서드 한 개에 추상화시키는 것은 좀 무리가 있고, 차라리 그냥 클래스 수준으로 추상화하는 것이 더 좋습니다.

using System.Runtime.InteropServices;

public class Win32UserToken : IDisposable
{
    IntPtr _tokenInformation;
    TOKEN_USER? _tokenUser;
    string _sid = "";
    public string Sid => _sid;

    const uint ERROR_INSUFFICIENT_BUFFER = 122;

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);

    public static implicit operator TOKEN_USER(Win32UserToken token)
    {
        if (token._tokenUser == null)
        {
            throw new NullReferenceException(nameof(token._tokenUser));
        }

        return token._tokenUser.Value;
    }

    public Win32UserToken(IntPtr hToken)
    {
        uint dwBufferSize;
        if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out dwBufferSize))
        {
            int win32Result = Marshal.GetLastWin32Error();
            if (win32Result != ERROR_INSUFFICIENT_BUFFER)
            {
                return;
            }
        }

        _tokenInformation = Marshal.AllocHGlobal((int)dwBufferSize);
        if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, _tokenInformation, dwBufferSize, out dwBufferSize))
        {
            return;
        }

        _tokenUser = (TOKEN_USER?)Marshal.PtrToStructure(_tokenInformation, typeof(TOKEN_USER));
        if (_tokenUser == null)
        {
            return;
        }

        IntPtr pstr = IntPtr.Zero;
        if (ConvertSidToStringSid(_tokenUser.Value.User.Sid, out pstr) == true)
        {
            _sid = Marshal.PtrToStringAuto(pstr) ?? "";
            Marshal.FreeHGlobal(pstr);
        }
    }

    public void Dispose()
    {
        if (_tokenInformation != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(_tokenInformation);
            _tokenInformation = IntPtr.Zero;
        }
    }
}

// ...[생략]...

그래서 이런 식으로 사용하면 안전하게 TokenUser를 보호할 수 있습니다.

using System.Runtime.Versioning;
using System.Security.Principal;

[assembly: SupportedOSPlatform("windows")]

internal class Program
{
    static void Main(string[] args)
    {
        IntPtr hToken = WindowsIdentity.GetCurrent().Token;
        using (Win32UserToken tokenUser = new Win32UserToken(hToken))
        {
            Console.WriteLine(tokenUser.Sid); // S-1-5-21-1510216573-2196513108-161129836-1001

            // 이 범위 내에서 tokenUser._tokenUser 인스턴스를 안전하게 사용
        }
    }
}




참고로, 저렇게 출력한 Sid 문자열(S-1-5-21-1510216573-2196513108-161129836-1001)은 TOKEN_USER로부터 그대로 구하는 것이 가능합니다.

S: 접두사
1: SecurityIdentifier의 Revision 필드 값
5: SecurityIdentifier의 6바이트 IdentifierAuthority 값
21: DWORD SubAuthority[0]
1510216573: DWORD SubAuthority[0]
2196513108: DWORD SubAuthority[2]
161129836: DWORD SubAuthority[3]
1001: DWORD SubAuthority[4]

따라서 Win32UserToken 타입에 다음과 같은 방법으로 SID 문자열을 구할 수도 있습니다.

public override string ToString()
{
    if (this._tokenUser == null)
    {
        return "";
    }

    TOKEN_USER tokenUser = this._tokenUser.Value;
    return $"S-{tokenUser.Sid.Revision}-{tokenUser.Sid.IdentifierAuthorityAsValue}-{this.SubAuthority}";
}

public unsafe int IdentifierAuthority
{
    get
    {
        if (this._tokenUser == null)
        {
            return 0;
        }

        TOKEN_USER tokenUser = this._tokenUser.Value;

        int result = 0;

        for (int i = 0; i < 6; i++)
        {
            byte value = tokenUser.Sid.IdentifierAuthority[i];
            result |= (value << (8 * (6 - (i + 1))));
        }

        return result;
    }
}

public unsafe string SubAuthority
{
    get
    {
        if (this._tokenUser == null)
        {
            return "";
        }

        TOKEN_USER tokenUser = this._tokenUser.Value;

        IntPtr ptr = IntPtr.Add(this._tokenInformation, 0x18);
        string[] texts = new string[tokenUser.Sid.SubAuthorityCount];
        for (int i = 0; i < tokenUser.Sid.SubAuthorityCount; i++)
        {
            int auth = *(int *)ptr.ToPointer();
            texts[i] = auth.ToString();
            ptr = IntPtr.Add(ptr, 4);
        }

        return string.Join('-', texts);
    }
}


[StructLayout(LayoutKind.Sequential)]
public unsafe struct SecurityIdentifier
{
    public byte Revision;
    public byte SubAuthorityCount;
    public fixed byte IdentifierAuthority[6];
    public fixed int SubAuthority[1];

    public unsafe int IdentifierAuthorityAsValue
    {
        get
        {
            int result = 0;

            for (int i = 0; i < 6; i++)
            {
                byte value = IdentifierAuthority[i];
                result |= (value << (8 * (6 - (i + 1))));
            }

            return result;
        }

        set
        {
            int idAuthority = value & 0x3F;

            for (int i = 0; i < 6; i++)
            {
                long mask = 0xFF << (8 * i);
                long maskedValue = (idAuthority & mask);
                long idValue = maskedValue >> (8 * i);

                IdentifierAuthority[5 - i] = (byte)idValue;
            }
        }
    }
}

보는 바와 같이 대부분의 필드 값이 SID 문자열로 직렬화되는데요, 단지 여기서 SID_AND_ATTRIBUTES의 Attributes 값은 누락돼 있습니다.

Attributes

Specifies attributes of the SID. This value contains up to 32 one-bit flags. Its meaning depends on the definition and use of the SID.

32Bit Flags 형식으로 값을 나타낸다고 하는데, 일단 제 사용자 계정으로 테스트했을 때는 모든 값이 0이었습니다. 문서에 따르면 "아마도" 그룹 성격의 TOKEN_GROUPS 형식에서 사용되는 듯한데, 만약 그런 경우라면 일반 사용자 계정을 대상으로는 SID 문자열로부터 TOKEN_USER 구조체를 온전히 복원하는 것이 가능합니다.

(첨부 파일은 이 글의 C# 예제 코드와 C++ 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/11/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2023-01-09 10시37분
What are these SIDs of the form S-1-15-2-xxx?
; https://devblogs.microsoft.com/oldnewthing/20220502-00/?p=106550

What are these SIDs of the form S-1-15-3-xxx?
; https://devblogs.microsoft.com/oldnewthing/20220503-00/?p=106557

Management of SIDs in Active Directory
; https://social.technet.microsoft.com/wiki/contents/articles/20590.management-of-sids-in-active-directory.aspx

Dubious security vulnerability: Granting access to SIDs that don’t exist yet
; https://devblogs.microsoft.com/oldnewthing/20230106-00/?p=107680

SIDs are really just another a fancy way of creating unique IDs in a decentralized way
; https://devblogs.microsoft.com/oldnewthing/20230613-00/?p=108335
정성태
2023-05-18 11시12분
How do I free the pointers returned by functions like Get­Token­Information?
; https://devblogs.microsoft.com/oldnewthing/20230517-00/?p=108207

GetTokenInformation과 같은 식으로 첫 번째 호출에서 크기를 구하고, 할당한 다음 두 번째 호출에서 실제 정보를 구하는 식으로 동작하는 API들이 있는데요, 재미있는 건 그런 API가 반환한 구조체 내에는 포인터도 있다는 점입니다. 그렇다면 그 포인터도 명시적으로 해제해야 할까요?

그에 대한 답이 위의 링크에 있습니다.

(답은, 부가적인 해제 과정을 필요하진 않고, 우리가 할당해 넘긴 그 메모리만 삭제하면 됩니다.)
정성태
2024-02-15 08시26분
Windows API 중에는 본문에서 다룬 GetTokenInformation처럼 1) 버퍼 크기를 먼저 구하고, 2) 해당 버퍼 크기만큼 사용자가 할당 후, 3) 다시 그 버퍼로 API를 호출하는 유형들이 있습니다.

개인적으로, 저 1~3번의 연속 작업이 원자적 단위가 아니기 때문에 3번 단계에서 달라진 상황이 발생할 수 있고, 그렇다면 ERROR_INSUFFICIENT_BUFFER 체크를 while 루프로 처리해 반복해야 하나... 라는 생각을 한 적이 있습니다.

이에 대한 걱정을 아래의 글에서 해결해 주고 있습니다. ^^

Functions that return the size of a required buffer generally return upper bounds, not tight bounds
; https://devblogs.microsoft.com/oldnewthing/20240214-00/?p=109400

----------------------------------------------------

How can I tell whether two programs will share drive letter mappings?
; https://devblogs.microsoft.com/oldnewthing/20240912-00/?p=110251

----------------------------------------------------

A function for creating an absolute security descriptor from a self-relative one
; https://devblogs.microsoft.com/oldnewthing/20241002-00/?p=110333
정성태
2024-12-04 07시46분
커널 디버깅에서 특정 프로세스의 Token 정보 조회

// PID == 4에 해당하는 System 프로세스 정보 조회

kd> !process 4
Searching for Process with Cid == 4
PROCESS ffffb4035d09a1c0
    SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
    DirBase: 001aa000 ObjectTable: ffff980090e08180 HandleCount: 2125.
    Image: System
    VadRoot ffffb4035d0b15b0 Vads 6 Clone 0 Private 21. Modified 27955. Locked 0.
    DeviceMap ffff980090e35180
    Token ffff980090e04850
    ElapsedTime 22:04:11.229
    UserTime 00:00:00.000
    KernelTime 00:09:24.875
    QuotaPoolUsage[PagedPool] 0
    QuotaPoolUsage[NonPagedPool] 272
    Working Set Sizes (now,min,max) (23, 50, 450) (92KB, 200KB, 1800KB)
    PeakWorkingSetSize 215
    VirtualSize 3 Mb
    PeakVirtualSize 14 Mb
    PageFaultCount 2508
    MemoryPriority BACKGROUND
    BasePriority 8
    CommitCharge 48

// 위의 정보에서 "Token ffff980090e04850"로 출력된 정보를 보다 상세하게 조회

kd> !token ffff980090e04850
_TOKEN 0xffff980090e04850
TS Session ID: 0
User: S-1-5-18
User Groups:
 00 S-1-5-32-544
    Attributes - Default Enabled Owner
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-11
    Attributes - Mandatory Default Enabled
 03 S-1-16-16384
    Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
 02 0x000000002 SeCreateTokenPrivilege Attributes -
 03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
 04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
 05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
 07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
 08 0x000000008 SeSecurityPrivilege Attributes -
 09 0x000000009 SeTakeOwnershipPrivilege Attributes -
 10 0x00000000a SeLoadDriverPrivilege Attributes -
 11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
 12 0x00000000c SeSystemtimePrivilege Attributes -
 13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
 14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
 15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
 16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
 17 0x000000011 SeBackupPrivilege Attributes -
 18 0x000000012 SeRestorePrivilege Attributes -
 19 0x000000013 SeShutdownPrivilege Attributes -
 20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
 21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
 22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
 23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
 25 0x000000019 SeUndockPrivilege Attributes -
 28 0x00000001c SeManageVolumePrivilege Attributes -
 29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
 30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
 31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
 32 0x000000020 SeRelabelPrivilege Attributes -
 33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
 34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
 35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
 36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3eb ParentToken ID: 0
Modified ID: (0, 3ec)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:
Invalid AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION with no claims
Process Token TrustLevelSid: S-1-19-1024-8192
정성태

... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12211정성태4/27/202019239개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202020686.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/202017404오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/202015963Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/202015799스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/202018422오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/202015092스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/202015089스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/202017935오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/202021196개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/202018434오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token". [1]
12200정성태3/18/202018523VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/202016167오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/202019528오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/202018810VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/202015942오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202018262.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202020992오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/202017903개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202019923개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202021040개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/202015620오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202021204개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202026020Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/202015575오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/202017398오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...