성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
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'>커널 객체를 위한 null DACL 생성 방법</h1> <p> (비스타 운영체제 이상의 경우 세션 0번에 활성화되는) NT 서비스에서 생성한 커널 객체를 사용자 로그인 환경에서 실행한 응용 프로그램으로부터 접근하려는 경우 보안 오류가 발생합니다.<br /> <br /> 간단하게 재현을 해볼까요? ^^<br /> <br /> NT 서비스로 테스트하면 번거로우니 psexec.exe를 이용해 Local SYSTEM 계정으로 실행해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Local SYSTEM 권한으로 코드를 실행하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1436'>http://www.sysnet.pe.kr/2/0/1436</a> </pre> <br /> 그래서 다음의 코드는,<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; using System.Threading; class Program { static void Main(string[] args) { EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset, "my.event"); Console.WriteLine("Wait for 'my.event'..."); ewh.WaitOne(); } } </pre> <br /> (관리자 권한으로 실행한 cmd.exe 창에서) psexec의 도움을 받아 SYSTEM 계정으로 실행되고 이벤트 객체는 그 권한에 영향을 받습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp><span style='color: blue; font-weight: bold'>psexec -s c:\temp\ConsoleApplication1.exe</span> PsExec v1.98 - Execute processes remotely Copyright (C) 2001-2010 Mark Russinovich Sysinternals - www.sysinternals.com Wait for 'my.event'... </pre> <br /> 위와 같이 실행하면 ConsoleApplication1.exe 창은 보이지 않지만 작업관리자에서 Session 0번에 SYSTEM 권한으로 실행된 것을 확인할 수 있습니다.<br /> <br /> 이 상태에서 현재 윈도우에 로그온 한 사용자가 다음의 프로그램을 실행하면,<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; using System.Threading; class Program { static void Main(string[] args) { EventWaitHandle ewh = EventWaitHandle.OpenExisting("my.event"); ewh.Set(); } } </pre> <br /> OpenExisting에서 예외가 발생합니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.Threading.WaitHandleCannotBeOpenedException: <span style='color: blue; font-weight: bold'>No handle of the given name exists.</span> at System.Threading.EventWaitHandle.OpenExisting(String name) at Program.Main(String[] args) in c:\...\ConsoleApplication2\Program.cs:line 8 </pre> <br /> 그런데, 이상하군요. 접근 권한 에러가 아니라 아예 named kernel object 자체를 찾을 수 없다고 나옵니다. 왜냐하면 이름 공간이 다르기 때문인데요. 이럴 때는 Global 접두사를 붙여줘야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 처음 여는 쪽 EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset, <span style='color: blue; font-weight: bold'>@"Global\my.event"</span>); // 기존의 객체를 여는 쪽 EventWaitHandle ewh = EventWaitHandle.OpenExisting(<span style='color: blue; font-weight: bold'>@"Global\my.event"</span>); </pre> <br /> 그럼 해당 핸들을 찾을 수 있게 되지만, 예견했던대로 이제는 보안 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>Unhandled Exception: System.UnauthorizedAccessException: Access to the path is denied.</span> at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.Threading.EventWaitHandle.OpenExistingWorker(String name, EventWaitHandleRights rights, EventWaitHandle& result) at System.Threading.EventWaitHandle.OpenExisting(String name) at Program.Main(String[] args) in c:\...\ConsoleApplication2\Program.cs:line 9 </pre> <br /> 자, 이 오류를 다시 벗어나기 위해서는 예전에 소개한 WorldSid를 부여해 권한을 열어주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Named 동기화 개체 생성 시 System.UnauthorizedAccessException 예외 발생하는 경우 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1170'>http://www.sysnet.pe.kr/2/0/1170</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>EventWaitHandleSecurity</span> mSec = new EventWaitHandleSecurity(); var everyoneSid = new SecurityIdentifier(<span style='color: blue; font-weight: bold'>WellKnownSidType.WorldSid</span>, null); <span style='color: blue; font-weight: bold'>EventWaitHandleAccessRule</span> rule = new EventWaitHandleAccessRule(everyoneSid, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify, <span style='color: blue; font-weight: bold'>AccessControlType.Allow</span>); mSec.AddAccessRule(rule); bool createdNew; string eventName = @"Global\my.event"; EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset, eventName, out createdNew, mSec); Console.WriteLine("Wait for 'my.event'..."); ewh.WaitOne(); </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 이렇게 이름 있는 커널 객체에 AccessRule/Security 관련 객체들이 제공되는 것은 .NET 2.0부터입니다. 물론 요즘에는 1.x 버전을 사용할 일이 거의 없기 때문에 상관없지만 가끔은 .NET 2.0에서도 AccessRule/Security 객체들이 제공하지 않는 경우가 있어서 문제입니다.<br /> <br /> 바로 NamedPipe를 사용할 때가 그 한 예인데요. .NET에서 NamedPipe를 사용할 수 있는 NamedPipeServerStream / NamedPipeClientStream 객체는 .NET 3.5부터 제공되기 때문에 .NET 2.0 전체를 지원해야 하는 3rd-party 라이브러리 제작자들은 이 클래스를 사용하기가 살짝 난감합니다.<br /> <br /> 이 때문에 CreateNamedPipe Pinvoke 호출을 이용해 파이프를 생성해야 하는데, 그렇게 되면 보안 설정도 CreateNamedPipe의 마지막 인자에 전달되는 SECURITY_ATTRIBUTES를 이용해 null DACL을 만들 수밖에 없습니다. 다행히 ^^ 이에 대해서는 검색해 보니 친절하게 다음과 같이 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Creating a Null DACL in Managed Code ; <a target='tab' href='http://codemortem.blogspot.kr/2006/01/creating-null-dacl-in-managed-code.html'>http://codemortem.blogspot.kr/2006/01/creating-null-dacl-in-managed-code.html</a> </pre> <br /> 예를 들어, CreateFile에 (참고로 .NET 2.0부터는 FileStream에 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesecurity'>FileSecurity</a>가 제공되므로 그것을 쓰는 것이 좋습니다.) SECURITY_ATTRIBUTES를 null dacl로 채우고 직접 설정하는 코드는 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Microsoft.Win32.SafeHandles; using System; using System.IO; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using System.Threading; class Program { [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public uint nLength; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } [DllImport("kernel32.dll", SetLastError = true)] public static extern SafeFileHandle CreateFile( String pipeName, uint dwDesiredAccess, uint dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplate); static void Main(string[] args) { CreateFileWithLogonSecurity(); CreateFileWithNullDacl(); } private static void CreateFileWithLogonSecurity() { File.WriteAllText(@"c:\temp\current_system.txt", "test"); } // http://codemortem.blogspot.kr/2006/01/creating-null-dacl-in-managed-code.html public static SECURITY_ATTRIBUTES GetNullDacl() { // Build NULL DACL (Allow everyone full access) RawSecurityDescriptor gsd = new RawSecurityDescriptor(ControlFlags.DiscretionaryAclPresent, null, null, null, null); // Construct SECURITY_ATTRIBUTES structure SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); sa.nLength = (uint)Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES)); sa.bInheritHandle = false; // Get binary form of the security descriptor and copy it into place byte[] desc = new byte[gsd.BinaryLength]; gsd.GetBinaryForm(desc, 0); sa.lpSecurityDescriptor = Marshal.AllocHGlobal(desc.Length); // This Alloc is Freed by the Disposer or Finalizer Marshal.Copy(desc, 0, sa.lpSecurityDescriptor, desc.Length); return sa; } private static void CreateFileWithNullDacl() { SECURITY_ATTRIBUTES sa = GetNullDacl(); SafeFileHandle pHandle = CreateFile(@"c:\temp\null_dacl.txt", (uint)FileAccess.ReadWrite, 0, ref sa, (uint)2 , 0, IntPtr.Zero); int lastError = Marshal.GetLastWin32Error(); if (pHandle.IsInvalid == true) { Console.WriteLine("Invalid: " + lastError); } pHandle.Close(); } } </pre> <br /> "psexec -s" 옵션을 통해 위의 프로그램을 실행시킨 후 CreateFileWithNullDacl 메서드로 생성된 null_dacl.txt 파일을 탐색기에서 보안 설정을 확인하면 다음과 같이 특이하게 나옵니다.<br /> <br /> <img alt='null_dacl_1.png' src='/SysWebRes/bbs/null_dacl_1.png' /> <br /><br /> 정리하자면, 보안을 풀어 세션 경계를 넘어서까지 공유하고 싶은 커널 객체가 있다면 각각의 커널 객체에 대한 ...Security/...AccessRule을 사용하고, 그것이 여의치 않을 때는 null dacl을 사용하면 됩니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=882&boardid=331301885'>첨부 파일은 위의 코드를 포함</a>합니다.)<br /> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1333
(왼쪽의 숫자를 입력해야 합니다.)