성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
[정성태] 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...
글쓰기
제목
이름
암호
전자우편
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# - DirectX 게임 클라이언트 실행 중 키보드 입력을 감지하는 방법</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;' > Low level keyboard input from Windows ; <a target='tab' href='https://stackoverflow.com/questions/310576/low-level-keyboard-input-from-windows'>https://stackoverflow.com/questions/310576/low-level-keyboard-input-from-windows</a> </pre> <br /> DirectX 게임에 대해서는 통하지 않습니다. 예전에 소개한 RawInput 조차도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > RawInput을 이용한 키보드/마우스 입력 모니터링 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11615'>https://www.sysnet.pe.kr/2/0/11615</a> </pre> <br /> 마찬가지인데요, 이것이 가능하려면 보다 하위 기술로 내려가야 하는데 일례로 커널 레벨에서 키보드 장치에 대한 driver를 직접 필터링하는 식이어야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Keyboard Hooking In Kernel ; <a target='tab' href='https://www.codeproject.com/Articles/194729/Keyboard-Hooking-In-Kernel'>https://www.codeproject.com/Articles/194729/Keyboard-Hooking-In-Kernel</a> </pre> <br /> 문제는, 저런 DDK 소스 코드를 빌드해 정상적인 인증서로 서명하지 않으면 윈도우 환경을 <a target='tab' href='https://www.sysnet.pe.kr/2/0/2914'>"testsigning" 모드</a>로 두어야 하는데 이것 또한 은근 보안 위협에 노출될 수 있고 게다가 특정 게임 보안 프로그램에서는 아예 testsigning 환경에서는 게임을 실행하지 못하게 하는 경우도 있습니다.<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;' > C# - (Wireshark의) USBPcap을 이용한 USB 패킷 모니터링 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12215'>https://www.sysnet.pe.kr/2/0/12215</a> </pre> <br /> <a target='tab' href='https://desowin.org/usbpcap/'>USBPcap</a>이 답이 될 수 있습니다. 실제로 그 글에서 소개한 소스 코드로 빌드한 ConsoleApp1.exe는 키보드 입력에 대해 DirectX 게임 실행 중에도 잘 동작하는 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > "C:\Program Files\Wireshark\extcap\USBPcapCMD.exe" -d \\.\USBPcap1 --devices 1 -o - | ConsoleApp1.exe </pre> <br /> 게다가 USBPcap 자체에서 이미 정식 인증서로 서명한 드라이버 파일을 제공하므로 현실적으로 사용하는 데에도 아무런 불편함이 없습니다. 한 가지 아쉬운 점이 있다면, USBPcapCMD.exe의 동작 방식으로 인해 표준 입출력에 대한 redirection을 처리해야 한다는 것인데 개발하면서 디버깅하는 중에는 이 과정이 꽤나 귀찮다는 점입니다.<br /> <br /> 이런 불편함을 없애려면 USBPcapCMD.exe처럼 직접 USBPcap device driver에 접근해 필터링하는 코드를 작성하는데, 음... C/C++로 하려니 역시나 귀찮고 아무도 C#으로 컨버팅하지도 않은 듯하니... 어쩔 수 없이 그냥 제가 만들었습니다. ^^; 소스 코드는 기존 usbcap을 fork한,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > stjeong / usbpcap ; <a target='tab' href='https://github.com/stjeong/usbpcap'>https://github.com/stjeong/usbpcap</a> </pre> <br /> windows 하위 폴더에 csUSBPcapCMD C# 프로젝트로 만들어 두었고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > \usbpcap\windows\csUSBPcapCMD ; <a target='tab' href='https://github.com/stjeong/usbpcap/tree/master/windows/csUSBPcapCMD'>https://github.com/stjeong/usbpcap/tree/master/windows/csUSBPcapCMD</a> </pre> <br /> 장치 선택을 쉽게 할 수 있도록 Wireshark에서 보여주는 번호와 일치하는 "device id"를 함께 출력하도록 했습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > D:\usbpcap\windows\csUSBPcapCMD\bin\Debug\netcoreapp3.1> <span style='color: blue; font-weight: bold'>csUSBPcapCMD.exe</span> Following filter control devices are available: 1 \\.\USBPcap1 \??\USB#ROOT_HUB30#4&1148bc98&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8} [Port 7] (<span style='color: blue; font-weight: bold'>device id: 1)</span> USB Composite Device USB Input Device HID Keyboard Device USB Input Device HID-compliant consumer control device HID-compliant system controller [Port 8] (<span style='color: blue; font-weight: bold'>device id: 2</span>) Generic USB Hub [Port 1] (<span style='color: blue; font-weight: bold'>device id: 6</span>) USB Input Device HID-compliant mouse [Port 9] (<span style='color: blue; font-weight: bold'>device id: 3</span>) USB Mass Storage Device ST1000LM 024 HN-M101MBB USB Device [Port 12] (<span style='color: blue; font-weight: bold'>device id: 4</span>) Intel(R) Wireless Bluetooth(R) Microsoft Bluetooth LE Enumerator Bluetooth Device (RFCOMM Protocol TDI) Microsoft Bluetooth Enumerator ...[생략]... Bluetooth Device (Personal Area Network) [Port 13] (<span style='color: blue; font-weight: bold'>device id: 5</span>) Wacom Tablet HID-Compliant Mouse HID-compliant vendor-defined device HID-compliant pen HID-compliant digitizer </pre> <br /> 따라서, 위와 같은 상황에서 "HID Keyboard Device"를 모니터링하고 싶다면 "csUSBPcapCMD.exe 0 1"과 같이 실행하면 됩니다.<br /> <br /> 참고로, 대부분의 기능을 별도로 USBPcapClient 라이브러리로 만들어 두었고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > usbpcap/windows/USBPcapLib/ ; <a target='tab' href='https://github.com/stjeong/usbpcap/tree/master/windows/USBPcapLib'>https://github.com/stjeong/usbpcap/tree/master/windows/USBPcapLib</a> </pre> <br /> NuGet에도 올렸으니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > USBPcapLib ; <a target='tab' href='https://www.nuget.org/packages/USBPcapLib/'>https://www.nuget.org/packages/USBPcapLib/</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 USBPcapLib -Version 1.0.3 </pre> <br /> 다음과 같은 식으로 코딩하기만 하면 됩니다. ^^ (물론, 사전에 USBPcap을 시스템에 설치해 두어야 하고, 프로그램은 반드시 관리자 권한으로 실행해야 합니다.)<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.Collections.Generic; using USBPcapLib; namespace ConsoleApp1 { // Install-Package USBPcapLib -Version 1.0.3 // <a target='tab' href='https://github.com/stjeong/usbpcap/tree/master/windows/csUSBPcapCMD'>https://github.com/stjeong/usbpcap/tree/master/windows/csUSBPcapCMD</a> class Program { static void Main(string[] args) { List<string> filters = USBPcapClient.find_usbpcap_filters(); if (filters.Count == 0) { Console.WriteLine("No filter control devices are available."); if (USBPcapClient.is_usbpcap_upper_filter_installed() == true) { Console.WriteLine("Please reinstall USBPcapDriver."); } Console.WriteLine($@"USBPcap UpperFilter entry appears to be present. Most likely you have not restarted your computer after installation. It is possible to restart all USB devices to get USBPcap working without reboot. {Environment.NewLine}WARNING:{Environment.NewLine} Restarting all USB devices can result in data loss. If you are unsure please answer 'n' and reboot in order to use USBPcap."); return; } // how can I precisely specify a USB device to capture with tshark? // <a target='tab' href='https://osqa-ask.wireshark.org/questions/53919/how-can-i-precisely-specify-a-usb-device-to-capture-with-tshark'>https://osqa-ask.wireshark.org/questions/53919/how-can-i-precisely-specify-a-usb-device-to-capture-with-tshark</a> // Tracking only one USB Port in Filter using USBPcap // <a target='tab' href='https://osqa-ask.wireshark.org/questions/63837/tracking-only-one-usb-port-in-filter-using-usbpcap'>https://osqa-ask.wireshark.org/questions/63837/tracking-only-one-usb-port-in-filter-using-usbpcap</a> int targetDeviceId = 0; int filterDeviceId = 1; if (args.Length >= 1) { targetDeviceId = int.Parse(args[0]); } if (args.Length >= 2) { filterDeviceId = int.Parse(args[1]); } Console.WriteLine("Following filter control devices are available:"); string target = null; int i = 0; foreach (string filter in filters) { Console.WriteLine($"{i + 1} {filter}"); string deviceList = USBPcapClient.enumerate_print_usbpcap_interactive(filter); Console.WriteLine(deviceList); if (targetDeviceId == i) { target = filter; break; } i++; } if (string.IsNullOrEmpty(target) == false) { using (USBPcapClient usbCapture = new USBPcapClient(target, filterDeviceId)) { usbCapture.HeaderRead += UsbCapture_HeaderRead; usbCapture.DataRead += UsbCapture_DataRead; usbCapture.start_capture(); usbCapture.wait_for_exit_signal(); } } } private static void UsbCapture_HeaderRead(object sender, HeaderEventArgs e) { Console.WriteLine(e.Header.magic_number); Console.WriteLine(e.Header.version_major); Console.WriteLine(e.Header.version_minor); Console.WriteLine(e.Header.thiszone); Console.WriteLine(e.Header.sigfigs); Console.WriteLine(e.Header.snaplen); Console.WriteLine(e.Header.network); } static int _index = 1; private static void UsbCapture_DataRead(object sender, DataEventArgs e) { int pcapRecordDataSize = (int)e.Record.incl_len; //Console.WriteLine($"[{index}]"); //Console.WriteLine(e.Record.ts_sec); //Console.WriteLine(e.Record.ts_usec); //Console.WriteLine(e.Record.incl_len); //Console.WriteLine(e.Record.orig_len); byte[] buf = e.Data; switch (e.Header.function) { case URB_FUNCTION.URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: if (e.Header.IrpDirection == IRPDierction.PDO_TO_FDO) { Console.Write($"[{_index}] {e.Header.bus}.{e.Header.device}.{e.Header.EndpointNumber}, {e.Header.transfer}, {e.Header.IrpDirection} : {buf.Length} - "); if (e.Header.status != USBD_STATUS.USBD_STATUS_SUCCESS) { break; } if (e.Header.transfer != USBPCAP_TRANSFER_TYPE.INTERRUPT) { break; } if (buf.Length != 8) { break; } byte code = buf[2]; if (code == 0) { Console.WriteLine("key up"); } else { // <a target='tab' href='https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2'>https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2</a> if (code >= 0x4 && code <= 0x1d) { char ch = (char)(code - 4 + (short)'a'); Console.WriteLine(ch + " pressed"); } else { Console.WriteLine("Key pressed"); } } } break; default: break; } _index++; } } } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8817
(왼쪽의 숫자를 입력해야 합니다.)