성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
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# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 프로세스의 모든 핸들을 열람 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12080'>http://www.sysnet.pe.kr/2/0/12080</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;' > // Install-Package KernelStructOffset using (WindowsHandleInfo whi = new WindowsHandleInfo()) { for (int i = 0; i < whi.HandleCount; i++) { SYSTEM_HANDLE_ENTRY she = whi[i]; if (she.UniqueProcessId != processId) { continue; } string objName = she.GetName(out string handleTypeName); Console.WriteLine($"{handleTypeName}: {objName}"); } } </pre> <br /> 재미있는 것은, 특정 프로세스가 권한도 충분한데 열람이 안 되는 문제가 있었습니다. 왜냐하면 SYSTEM_HANDLE_ENTRY의,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_HANDLE_ENTRY { <span style='color: blue; font-weight: bold'>public int UniqueProcessId;</span> public byte ObjectType; public byte HandleFlags; public short HandleValue; public IntPtr ObjectPointer; public int AccessMask; } </pre> <br /> UniqueProcessId가 실은 int 형이 아닌,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_HANDLE_ENTRY { <span style='color: blue; font-weight: bold'>public short UniqueProcessId;</span> <span style='color: blue; font-weight: bold'>public short CreatorBackTraceIndex;</span> public byte ObjectType; public byte HandleFlags; public short HandleValue; public IntPtr ObjectPointer; public int AccessMask; } </pre> <br /> 2개의 필드를 하나로 합친 것으로써 원래는 short형이었기 때문입니다. 그래서 PID가 65535보다 크면 overflow가 발생해 정상적인 조회가 안 된 것입니다. 다행히 이에 대해서 검색해 보면 해법이 나오는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Why can't get process id that more than 65535 by 'ntQuerySystemInformation' in Win7 64bit? ; <a target='tab' href='https://stackoverflow.com/questions/23951043/why-cant-get-process-id-that-more-than-65535-by-ntquerysysteminformation-in-w'>https://stackoverflow.com/questions/23951043/why-cant-get-process-id-that-more-than-65535-by-ntquerysysteminformation-in-w</a> </pre> <br /> 따라서 NtQuerySystemInformation에 SYSTEM_EXTENDED_HANDLE_INFORMATION == 64 값을 전달하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > NativeMethods.NtQuerySystemInformation(<span style='color: blue; font-weight: bold'>SYSTEM_INFORMATION_CLASS.SystemExtendedHandleInformation</span>, ptr, guessSize, out requiredSize); </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;' > [StructLayout(LayoutKind.Sequential)] public struct _SYSTEM_HANDLE_INFORMATION_EX { public IntPtr HandleCount; public IntPtr Reserved; public _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles; /* Handles[0] */ public int NumberOfHandles { get { return HandleCount.ToInt32(); } } } </pre> <br /> IntPtr.Size * 2만큼의 위치부터 새로운 _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX 구조체로 받아오면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential)] public struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { public IntPtr ObjectPointer; <span style='color: blue; font-weight: bold'>public IntPtr UniqueProcessId;</span> public IntPtr HandleValue; public uint GrantedAccess; public ushort CreatorBackTraceIndex; public ushort ObjectTypeIndex; public uint HandleAttributes; public uint Reserved; public int OwnerPid { get { return UniqueProcessId.ToInt32(); } } } </pre> <br /> (<a target='tab' href='https://github.com/stjeong/DotNetSamples/blob/master/WinConsole/Debugger/KernelStructOffset/WindowsHandleInfo.cs'>DotNetSamples/WinConsole/Debugger/KernelStructOffset/WindowsHandleInfo.cs</a>의 코드는 위의 변경 사항이 적용된 것입니다.)<br /> <br /> <hr style='width: 50%' /><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;' > { IntPtr targetHandle = ...[다른 프로세스의 Handle]...; int targetPid = ...[targetHandle 핸들을 소유하고 있는 ProcessId]...; IntPtr targetProcessHandle = <span style='color: blue; font-weight: bold'>OpenProcess</span>(ProcessAccessRights.<span style='color: blue; font-weight: bold'>PROCESS_DUP_HANDLE</span>, false, targetPid); IntPtr currentProcess = NativeMethods.GetCurrentProcess(); IntPtr duplicatedHandle; bool dupResult = <span style='color: blue; font-weight: bold'>DuplicateHandle</span>( targetProcessHandle, targetHandle, currentProcess, out duplicatedHandle , 0, false, DuplicateHandleOptions.<span style='color: blue; font-weight: bold'>DUPLICATE_SAME_ACCESS</span>); } </pre> <br /> 대개의 경우, 원본 핸들과 동일한 권한(DUPLICATE_SAME_ACCESS)으로 복사하면 문제가 없는데 간혹 Process 타입의 핸들인 경우 GetModuleFileNameEx로 호출했을 때,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > StringBuilder sb = new StringBuilder(4096); int length = GetModuleFileNameEx(duplicatedHandle, IntPtr.Zero, sb, sb.Capacity); </pre> <br /> length == 0이 나오면서 StringBuilder 버퍼에는 3개의 문자 정도가 다음과 같은 쓰레기 값으로 채워져 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [0] 0x0050 'P' char [1] 0x0001 '\u0001' char [2] 0xfffd '' char </pre> <br /> 이런 문제가 발생하는 Process 타입 핸들은 권한을 조회해 보면 SYNCHRONIZE(0x00100000)만 가지고 있는 것을 볼 수 있습니다. 따라서 위의 결과를 반환하는 핸들은 DuplicateHandle 시 다음과 같은 옵션으로 복제해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // PROCESS_VM_READ = 0x10, // PROCESS_QUERY_INFORMATION = 0x0400, int addAccessRights = (int)(ProcessAccessRights.<span style='color: blue; font-weight: bold'>PROCESS_VM_READ</span> | ProcessAccessRights.<span style='color: blue; font-weight: bold'>PROCESS_QUERY_INFORMATION</span>); bool dupResult = DuplicateHandle(targetProcessHandle, targetHandle, currentProcess, out duplicatedHandle , addAccessRights, false, <span style='color: blue; font-weight: bold'>0</span>); </pre> <br /> 다른 타입의 핸들도 이런 식으로 그 타입에 맞는 권한을 조정해야 하는데 이게 좀 애매합니다. 왜냐하면, 해당 핸들의 타입을 알기 위해서는 DuplicateHandle을 해야 하는데 그때 미리 access rights를 부여하는 것이 올바른 동작인지에 대해서는 보장할 수 없기 때문입니다. 예를 들어 프로세스인 경우 PROCESS_QUERY_INFORMATION = 0x0400이지만 스레드인 경우에는 THREAD_QUERY_INFORMATION = 0x0040이 되므로 어떤 핸들을 복사해야 하느냐에 따라 권한을 위한 상수도 달라집니다.<br /> <br /> 결국 한 번은 핸들의 타입을 알기 위해 복제를 해야 하고, 이후 다시 한번 타입에 따른 권한 설정을 위해 복제를 해야만 하는 식입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9117
(왼쪽의 숫자를 입력해야 합니다.)