성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] The Windows Registry Adventure #1: ...
[정성태] systemd for Developers I ; https:/...
[정성태] 엄밀히 object 타입의 인스턴스가 다른 타입으로 형변환 가능...
[정성태] 아래의 글에서 나오는 "Windows Application Pa...
[정성태] The history of calling conventions,...
[정성태] Secure and Deploy .NET Windows Form...
[정성태] Get Started with Milvus Vector DB i...
[정성태] cyberark/PipeViewer - A tool that...
[정성태] WinForms in a 64-Bit world – our st...
[정성태] 예제에서 SELECT_SQL도 내부적으로는 SqlCommand/...
글쓰기
제목
이름
암호
전자우편
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'>Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약</h1> <p> <a target='tab' href='https://www.facebook.com/rkttu'>남정현</a> 님이 nuget에 공개한 Wslhub.Sdk 패키지는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Wslhub.Sdk ; <a target='tab' href='https://www.nuget.org/packages/Wslhub.Sdk/'>https://www.nuget.org/packages/Wslhub.Sdk/</a> </pre> <br /> Windows 10에 설치된 WSL의 다양한 기능을 래핑해 아래와 같이 사용법을 제공합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Wslhub.Sdk; // Place the code Wsl.InitializeSecurityModel() at the top of your application's Main() method. Wsl.InitializeSecurity(); // Assert WSL installation status Wsl.AssertWslSupported(); // Enumerate distro list var distros = Wsl.GetDistroListFromRegistry(); // Query distro informations var queryResults = Wsl.GetDistroQueryResult(); // Run a command var result = Wsl.RunWslCommand(distroName, "cat /etc/passwd"); // Run a command with default distro var defaultDistro = Wsl.GetDefaultDistro(); var result = defaultDistro.RunWslCommand("cat /etc/passwd"); Stream Redirection using var outputStream = new MemoryStream(); var defaultDistro = Wsl.GetDefaultDistro(); defaultDistro.RunWslCommand("ls /dev | gzip -", outputStream); outputStream.Seek(0L, SeekOrigin.Begin); using var gzStream = new GZipStream(outputStream, CompressionMode.Decompress, true); using var streamReader = new StreamReader(gzStream, new UTF8Encoding(false), false); var content = streamReader.ReadToEnd(); Console.Out.WriteLine(content); </pre> <br /> 이 중에서 Wsl.InitializeSecurity() 메서드는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > wsl-sdk-dotnet/src/Wslhub.Sdk/Wsl.cs / ; <a target='tab' href='https://github.com/wslhub/wsl-sdk-dotnet/blob/main/src/Wslhub.Sdk/Wsl.cs#L19'>https://github.com/wslhub/wsl-sdk-dotnet/blob/main/src/Wslhub.Sdk/Wsl.cs#L19</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static void InitializeSecurityModel() { var result = NativeMethods.<span style='color: blue; font-weight: bold'>CoInitializeSecurity</span>( IntPtr.Zero, (-1), IntPtr.Zero, IntPtr.Zero, NativeMethods.RpcAuthnLevel.Default, NativeMethods.RpcImpLevel.Impersonate, IntPtr.Zero, NativeMethods.EoAuthnCap.StaticCloaking, IntPtr.Zero); if (result != 0) throw new COMException("Cannot complete CoInitializeSecurity.", result); } </pre> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity'>CoInitializeSecurity</a> Win32 API 함수를 호출하는데요, 엄밀히는 Console App 프로젝트에서 저걸 호출하지 않아도 WslHub 패키지는 잘 동작합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var defaultDistro = Wsl.GetDefaultDistro(); var result = defaultDistro.RunWslCommand("cat /etc/passwd"); // 정상 동작 </pre> <br /> 그런데 굳이 CoInitializeSecurity를 명시적으로 호출하는 이유는, 이것의 적용이 Process(EXE) 전역적으로 단 한 번의 호출만 허용된다는 특징을 가지기 때문입니다. 이에 대해서는 이미 아래의 논의에서 나왔는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CoInitializeSecurity를 제대로 부르고 싶으면 결국은 네이티브의 힘을 빌릴 수밖에 없는 걸까요? ; <a target='tab' href='https://forum.dotnetdev.kr/t/coinitializesecurity/1002'>https://forum.dotnetdev.kr/t/coinitializesecurity/1002</a> </pre> <br /> 간단하게 예를 들어, Console App 프로젝트를 만들어 2번 연속 호출해 보면,<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 WslSdk.Interop; // Install-Package Wslhub.Sdk class Program { static void Main(string[] args) { var result = NativeMethods.CoInitializeSecurity( IntPtr.Zero, (-1), IntPtr.Zero, IntPtr.Zero, NativeMethods.RpcAuthnLevel.Default, NativeMethods.RpcImpLevel.Impersonate, IntPtr.Zero, NativeMethods.EoAuthnCap.StaticCloaking, IntPtr.Zero); Console.WriteLine(result); result = NativeMethods.CoInitializeSecurity( IntPtr.Zero, (-1), IntPtr.Zero, IntPtr.Zero, NativeMethods.RpcAuthnLevel.Default, NativeMethods.RpcImpLevel.Impersonate, IntPtr.Zero, NativeMethods.EoAuthnCap.StaticCloaking, IntPtr.Zero); Console.WriteLine(result); } } /* 출력 결과 0 -2147417831 */ </pre> <a name='rpc_e_too_late'></a> <br /> 처음 호출은 0(S_OK)을 반환하지만, 두 번째 호출은 0x80010119(-2147417831) 값으로 RPC_E_TOO_LATE - "Security must be initialized before any interfaces are marshalled or unmarshalled. It cannot be changed once initialized." 오류를 의미합니다.<br /> <br /> 따라서, 응용 프로그램 사용 중에 누군가 CoInitializeSecurity를 다른 옵션을 적용해 초기화하게 되면 이후의 WslHub 메서드들이 정상 동작하지 않을 수 있으므로 미리 WslHub의 요구 사항에 맞게 호출해 두는 것뿐입니다. (그렇다면, 반대로 다른 응용 프로그램들이 CoInitializeSecurity를 호출했을 때 의도했던 목적으로의 사용은 실패하게 될 것입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, WSL Win32 API 호출과 CoInitializeSecurity가 무슨 관계가 있을까요? <br /> <br /> 이에 대해서도 "<a target='tab' href='https://forum.dotnetdev.kr/t/coinitializesecurity/1002'>CoInitializeSecurity를 제대로 부르고 싶으면 결국은 네이티브의 힘을 빌릴 수밖에 없는 걸까요?</a>" 질문에서 설명이 나옵니다.<br /> <br /> 즉, WSL의 기능들은 내부적으로 (svchost.exe로 호스팅 중인) lxssmanager.dll에서 제공하는 CLSID_LxssUserSession/IID_ILxssSession COM 개체를 통해 노출이 되는데, WSL Win32 API 함수들은 그 COM 개체 사용을 래퍼하는 층에 불과합니다. (확인하지는 않았습니다. ^^)<br /> <br /> 결국 여러분의 프로세스에서 호출하는 Win32 API는 svchost.exe로의 RPC 호출이 되고 이 과정에서 원격 호출을 위한 자격 전달의 기본 모드를 CoInitializeSecurity로 설정할 수 있었던 것입니다. 여기서 WSL API 사용과 관계해 중요한 것은 Impersonate 인자인데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var result = NativeMethods.CoInitializeSecurity( IntPtr.Zero, (-1), IntPtr.Zero, IntPtr.Zero, NativeMethods.RpcAuthnLevel.Default, <span style='color: blue; font-weight: bold'>NativeMethods.RpcImpLevel.Impersonate,</span> IntPtr.Zero, NativeMethods.EoAuthnCap.StaticCloaking, IntPtr.Zero); </pre> <br /> 만약 이 값을 Identify나 Anonymous로 바꾸면 실행 시 다음과 같은 식의 오류가 발생합니다.<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.Exception: Ubuntu20.04 is not registered distro. at Wslhub.Sdk.Wsl.RunWslCommand(String distroName, String commandLine, Int32 bufferLength) at WinFormInit.Program.Main(String[] args) </pre> <br /> 왜냐하면, 호출 측의 자격 증명(예를 들어 로컬 컴퓨터의 testusr 계정)이 서비스 측(svchost.exe)으로 전달되지 않기 때문에 서버는 현재 호출의 사용자 계정 문맥을 확인할 수 없고, 이는 "Ubuntu20.04"가 설치된 testusr 계정에 대한 정보를 알 수 없는 결과를 낳기에 저런 오류가 발생하게 됩니다.<br /> <br /> 정리하면, svchost.exe에서 실행하는 코드를 현재 우리가 실행 중인 프로그램(ConsoleApp1.exe)을 구동한 사용자 계정의 문맥으로 실행할 수 있어야 정상적인 WSL Win32 API들이 동작할 수 있습니다. 그리고, 이를 위해서는 (기본값으로 Impersonate가 가능하기 때문에) CoInitializeSecurity 호출을 아예 하지 않거나, Impersonate가 가능한 모드로 CoInitializeSecurity를 호출해야 한다는 전제 조건이 필요합니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1792&boardid=331301885'>첨부 파일은 이 글의 예제 프로젝트를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> (업데이트: <a target='tab' href='https://www.sysnet.pe.kr/2/0/12666'>별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출</a>)<br /> <br /> <del>참고로, "<a target='tab' href='https://forum.dotnetdev.kr/t/coinitializesecurity/1002'>CoInitializeSecurity를 제대로 부르고 싶으면 결국은 네이티브의 힘을 빌릴 수밖에 없는 걸까요?</a>" 글에서는 STAThread가 붙은 경우 CoInitializeSecurity가 모두 성공했다고 하지만 제가 테스트한 바에 따르면 달랐습니다. 즉, 다음의 코드를,<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 WslSdk.Interop; class Program { <span style='color: blue; font-weight: bold'>[STAThread]</span> static void Main(string[] args) { var result = NativeMethods.CoInitializeSecurity( IntPtr.Zero, (-1), IntPtr.Zero, IntPtr.Zero, NativeMethods.RpcAuthnLevel.None, NativeMethods.RpcImpLevel.Impersonate, IntPtr.Zero, NativeMethods.EoAuthnCap.StaticCloaking, IntPtr.Zero); Console.WriteLine(result); } } </pre> <br /> .NET Framework에서 실행하면 -2147417831(RPC_E_TOO_LATE) 값이 반환되고 .NET Core에서 실행하면 0이 반환됩니다.<br /></del> <br /> <hr style='width: 50%' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - CoCreateInstance 관련 Inteop 오류 정리 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12678'>https://www.sysnet.pe.kr/2/0/12678</a> Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약 ; https://www.sysnet.pe.kr/2/0/12665 별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12666'>https://www.sysnet.pe.kr/2/0/12666</a> COM+ 서버 응용 프로그램을 이용해 CoInitializeSecurity 제약 해결 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12667'>https://www.sysnet.pe.kr/2/0/12667</a> ionescu007/lxss github repo에 공개된 lxssmanager.dll의 CLSID_LxssUserSession/IID_ILxssSession 사용법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12676'>https://www.sysnet.pe.kr/2/0/12676</a> 역공학을 통한 lxssmanager.dll의 ILxssSession 사용법 분석 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12677'>https://www.sysnet.pe.kr/2/0/12677</a> C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12668'>https://www.sysnet.pe.kr/2/0/12668</a> DLL Surrogate를 이용한 Out-of-process COM 개체에서의 CoInitializeSecurity 문제 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12670'>https://www.sysnet.pe.kr/2/0/12670</a> CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12679'>https://www.sysnet.pe.kr/2/0/12679</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1784
(왼쪽의 숫자를 입력해야 합니다.)