성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - gpedit.msc의 "User Rights Assignment" 특권을 코드로 설정/해제하는 방법</h1> <p> 특권에 대한 제어는 gpedit.msc를 이용해 "Local Computer Policy" / "Computer Configuration" / "Windows Settings" / "Security Settigns" / "Local Policies" / "User Rights Assignment" 패널에서 할 수 있습니다. 가령, 아래의 화면은 "Lock pages in memory" 특권에 원하는 계정 또는 그룹을 설정하는 방법을 보여주고 있습니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='enable_large_page_1.png' src='/SysWebRes/bbs/enable_large_page_1.png' /><br /> <br /> 경우에 따라서는 이런 특권 설정을 GUI를 통해 하는 것이 번거로울 수 있는데요, 당연히 코드를 통해 제어하는 방법도 제공하고 있습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Assigning Privileges to an Account ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/secbp/assigning-privileges-to-an-account'>https://learn.microsoft.com/en-us/windows/win32/secbp/assigning-privileges-to-an-account</a> </pre> <br /> 위의 설명에 간략하게 나오지만 LsaAddAccountRights API를 통해 가능합니다. (제거는 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaremoveaccountrights'>LsaRemoveAccountRights</a>)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LsaAddAccountRights function ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights'>https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights</a> </pre> <br /> 구체적인 사용법은 아래의 Q&A에서 찾을 수 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Enable large pages in Windows programmatically ; <a target='tab' href='https://stackoverflow.com/questions/42354504/enable-large-pages-in-windows-programmatically'>https://stackoverflow.com/questions/42354504/enable-large-pages-in-windows-programmatically</a> </pre> <br /> 위의 글에서는 C++로 제공하고 있으니 ^^ 이 글에서는 C#으로 포팅해 다음과 같이 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Principal; using System.Text; [assembly: SupportedOSPlatform("windows")] internal class Program { [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] static unsafe extern uint LsaOpenPolicy(LSA_UNICODE_STRING systemName, LSA_OBJECT_ATTRIBUTES* objectAttributes, POLICY_ACCESS desiredAccess, out IntPtr PolicyHandle); [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] static unsafe extern uint LsaAddAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, LSA_UNICODE_STRING* UserRights, uint CountOfRights); [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] static unsafe extern uint LsaRemoveAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, bool allRights, LSA_UNICODE_STRING* UserRights, uint CountOfRights); [DllImport("advapi32.dll")] private static extern long LsaClose(IntPtr ObjectHandle); public const string SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege"; public const int STATUS_SUCCESS = 0; static void Main(string[] args) { IntPtr hToken = WindowsIdentity.GetCurrent().Token; using (<a target='tab' href='https://www.sysnet.pe.kr/2/0/13206#Win32UserToken'>Win32UserToken</a> tokenUser = new Win32UserToken(hToken)) { Console.WriteLine($"SID Found: {tokenUser.Sid}"); // 특권에 사용자 추가 if (<span style='color: blue; font-weight: bold'>ChangePrivileges(tokenUser, SE_LOCK_MEMORY_NAME, true)</span> == true) { Console.WriteLine($"{SE_LOCK_MEMORY_NAME}: added"); } // 특권에 사용자 제거 // if (ChangePrivileges(tokenUser, SE_LOCK_MEMORY_NAME, false) == true) // { // Console.WriteLine($"{SE_LOCK_MEMORY_NAME}: removed"); // } } } private static unsafe bool ChangePrivileges(TOKEN_USER tokenUser, string privilegeName, bool addRights) { LSA_OBJECT_ATTRIBUTES attr = new LSA_OBJECT_ATTRIBUTES(); IntPtr policyHandle; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13203'>LSA_UNICODE_STRING</a> serverName = new LSA_UNICODE_STRING(); uint result = LsaOpenPolicy(serverName, &attr, POLICY_ACCESS.POLICY_CREATE_ACCOUNT | POLICY_ACCESS.POLICY_LOOKUP_NAMES, out policyHandle); if (result != 0) { #if DEBUG Console.WriteLine($"LsaOpenPolicy failed: {Marshal.GetLastWin32Error()}"); #endif return false; } <a target='tab' href='https://www.sysnet.pe.kr/2/0/13203'>LSA_UNICODE_STRING</a> userRightsLSAString = new LSA_UNICODE_STRING(privilegeName); try { if (addRights) { result = LsaAddAccountRights(policyHandle, tokenUser.User.Sid, &userRightsLSAString, 1); #if DEBUG if (result != STATUS_SUCCESS) { Console.WriteLine($"ChangePrivileges failed: {result:x}, {Marshal.GetLastWin32Error()}"); } #endif } else { result = LsaRemoveAccountRights(policyHandle, tokenUser.User.Sid, false, &userRightsLSAString, 1); } } finally { userRightsLSAString.Dispose(); LsaClose(policyHandle); } #if DEBUG if (result != STATUS_SUCCESS) { Console.WriteLine($"ChangePrivileges failed: {result:x}, {Marshal.GetLastWin32Error()}"); } #endif return result == STATUS_SUCCESS; } } [StructLayout(LayoutKind.Sequential)] internal struct LSA_UNICODE_STRING { public UInt16 Length; public UInt16 MaximumLength; public IntPtr Buffer; public LSA_UNICODE_STRING(string text) { Buffer = Marshal.StringToHGlobalUni(text); Length = (UInt16)(text.Length * UnicodeEncoding.CharSize); MaximumLength = (UInt16)(Length + UnicodeEncoding.CharSize); } public override string ToString() { return Marshal.PtrToStringUni(Buffer, Length / UnicodeEncoding.CharSize); } public void Dispose() { if (Buffer != IntPtr.Zero) { Marshal.FreeHGlobal(Buffer); } Buffer = IntPtr.Zero; Length = 0; MaximumLength = 0; } } [StructLayout(LayoutKind.Sequential)] internal unsafe struct LSA_OBJECT_ATTRIBUTES { public uint Length; public IntPtr RootDirectory; public LSA_UNICODE_STRING* ObjectName; public uint Attributes; public IntPtr SecurityDescriptor; public IntPtr SecurityQualityOfService; } [Flags] internal enum POLICY_ACCESS { POLICY_CREATE_ACCOUNT = 0x00000010, POLICY_LOOKUP_NAMES = 0x00000800, } </pre> <br /> 위의 코드는 특권을 변경하는 중요한 보안 이슈인만큼 반드시 "관리자 권한"으로 실행해야 합니다. 또한, "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/secbp/assigning-privileges-to-an-account'>Assigning Privileges to an Account</a>" 문서에서도 언급하고 있지만,<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Assigning a privilege to an account does <span style='color: blue; font-weight: bold'>not affect existing user tokens</span>. <span style='color: blue; font-weight: bold'>A user must log off and then log back on</span> to get an access token with the newly assigned privilege. </div><br /> <br /> 반드시 로그온/로그오프를 해야만 적용됩니다. (가령 위의 코드에서 예를 든 "Lock pages in memory" 특권의 경우, 기본적으로는 시스템에 어떠한 사용자 계정도 등록돼 있지 않습니다.)<br /> <br /> 게다가 변경 사항은 현재 실행 중인 gpedit.msc 화면에는 반영되지 않으므로 위의 코드 실행 후 사용자가 특권을 가지고 있는지 확인하려면 실행 중인 gpedit.msc를 종료하고 다시 실행해야만 합니다. 테스트하기가 여간 귀찮은 작업이 아닐 수 없습니다. ^^;<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2003&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 만약 LsaAddAccountRights가 c000000d를 반환한다면? 제 경우에 GetTokenInformation으로 반환받은 2번째 인자의 Sid가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LsaAddAccountRights(policyHandle, <span style='color: blue; font-weight: bold'>tokenUser.User.Sid</span>, &userRightsLSAString, 1); </pre> <br /> 메모리 해제되었기 때문에 발생한 문제였습니다. <a target='tab' href='https://www.sysnet.pe.kr/2/0/13206#free_token'>지난번에 설명</a>한 것처럼, TokenUser 내부에 SID 정보가 함께 포함돼 있기 때문에 LsaAddAccountRights API를 호출하기 전까지는 GetTokenInformation으로 반환한 구조체 메모리를 해제해서는 안 됩니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2804
(왼쪽의 숫자를 입력해야 합니다.)