2011-05-23

Virtual HID Mouse Driver 연구


저자: AmesianX
제작: powerhacker.net
제작년도: 2009년 1월 15일
본 문서는 파워해커에서 제작되었으며 무단배포를 허용함..
[서문]
본 문서의 시작에 앞서 목적에 대해 언급한다. 이 문서는 두가지 목적을 위해서 작성되어졌다.
첫번째는 모르는 부분에대한 새로운 연구나 공부를 시도할때 참고할 수 있는 공부방식(Style)에 대한
설명이고, 두번째는 Virtual HID Mouse Driver 를 제작할때 참고해야할 전초를 다지기위한 목적으로
구성되어 있다. 이렇게 기술문서를 공개하는 이유에는 여러가지가 있지만 무엇보다 이 기술이라는 분야는
언젠가는 자신이 알고있는 오늘의 기술이 내일의 쓰레기가 된다. 일찌감치 알고있는 정보들은 공개하고
더 높은 곳으로 계속 뛰어오를때 진정으로 아무도 가보지못한 고지에 오른 것이다. 그래야 스스로가
발전하는 길이고 바로 모두가 발전하는 길일 것이다. 고지는 점령 당하라고 있는 것이다. 언제까지
정적인 비공개노선으로 점령당하길 기다리고 있을 것인가? 앞으로는 더욱더 가속화될 것이다.
[기술을 습득하는 공부방법]
어떤 새로운 기술을 알고싶다면 최대한 동일기능이 구현된 많은 소스들을 수집하라. 그리고 그 차이점을
소스분석을 통해서 캣치하라. 아마 대부분은 소스분석을 깊게 들어가지 않아도 차이점이 눈에 띄게 될
것이다. 그러나.. 아쉽게도 Virtual HID Mouse 에 대한 소스는 인터넷에서 거의 찾기 힘들 것이다. 물론,
공식적인 샘플들을 찾아낼 수 있지만 필자처럼 커널지식보다 유저레벨 어플리케이션 조작만 파온 사람은
그 유명업체들의 공식샘플을 보고도 감을 잡아내기는 하늘에 별따기처럼 어렵다. 즉, 프로그래밍 = 개념
이라는 공식이 있기 때문에 이 개념만 잡고나면 프로그래밍은 껌씹기나 마찬가지다. 개념을 잡는 요령이
있다면 그것은 바로 앞에서 언급한 동일기능 소스의 차이점을 분석해내는 것이다.
그렇기 때문에 매우 제한된 소스(공식소스들)를 갖고 시작하는 것은 전혀 이해를 이끌어내지 못한다.
최대한 고갈되었다고 생각될 정도로 많은 설명이나 소스들을 검색해서 찾아내야 한다. 이때 사용할 수 있는
방법은 구해낸 소스들의 일부 시그너처(Signature)를 검색키워드로 사용하는 것이다. 이런식으로 최대한
많은 정보들을 뽑다보면 선구자들의 질문들을 찾아낼 수 있다. 바로, 그 질문들에서 핵심개념들을 뽑아낼
수 있다. 자주 언급되는 사항이 바로 그 핵심일 것이다. 어떻게보면 "미네르바" 인지 "미네로하이바" 인지
그 친구처럼 철저한 검색기술로 무장한 채로 짜집기의 달인이 되어야만 할 필요가 있다. 만약 충분히 많이
찾아서 더이상 찾아낼 것 조차 없다고 느끼고 등골이 휘어지는 고통을 여러번 견뎌내었다면 이제 얻어낸
것들을 대상으로 비교분석을 취한다. 그렇게 되면 중요 키포인트가 눈에 보이게 될 것이다. 왜 이렇게 해야
하냐면 답은 간단하다. 스스로 엄청난 고통을 감뇌하면서 충분히 검색을 했는데 다 내용이 거기서 거기인
경우가 99.9% 일 것이다. 왜? 그것은 삽질하는 프로그래머가 택한 프로그래밍 방법론은 달라도 전체적인
흐름은 재밌게도 한가지밖에 없으니까. 이게 답이다.
"어라? 다 똑같은 소리만 짓거리고 있잖아? 뭔가 좀 더 구체적인 것을 달라고!!"
이 소리가 나올때 이미 정답은 구해진 것이나 마찬가지다. 다 똑같은 소리를 짓거린다거나 똑같은 소스에
차이점은 있지만 원하는 것이 아니라고 생각될때 사실상 그것이 원하는 것일 확률이 태반이다. 그 이상의
짜집기(Copy & Paste) 능력은 밥을 숫갈로 퍼서 먹는 것이다. 그런데 필자같은 사람은 퍼서 먹여주는 것을
원한다. 왜? 프로그래밍이 나온 이유가 무엇인가? 로보트를 만들려고 기를 쓰는거보면 모르겠는가? 인간에게
숫갈로 밥을 퍼먹여 주려는게 궁극적인 목적아닌가? 프로그래머는 인간이 아니었나? (하기사 혹자는 로보트
라고도 부릅디다..)
프로그래머는 좀 퍼서 먹여주면 어디가 덧나는가? 짜증나게 약올리는 시스템 프로그래머들이여 퍼먹여주지
않으면 스스로 밥숫갈 놓게되는 날이 온다. 인터넷시대에서 정보라는 것은 물 흐르듯 흐를수 밖에 없기 때문에
빨리 털고 높은 곳으로 뛰지않으면 숫가락 놓을 준비를 해야 할 것이다. 오늘의 신기술이 내일의 쓰레기가 되는
시대에서 서로 돕지않으면 혼자 살아남기 힘들다. 외국애들 보라 오픈소스로 자기의 기술을 다 까발리고
있으면서도 항상 최첨단 기술을 걷고 있는 것을 보면 우리가 얼마나 어리석은지 진정 모르냐 이말이다.
외국애들은 오픈소스를 통해서 시너지라는 것을 이용하고 있다. 물론, 수 많은 개발자들이 다 참가해서
오픈소스가 완성될 것이라고 착각하면 그건 열라 바보이다. 유명 보안 오픈소스인 Nessus 개발자의 고충이
섞인 글을 읽어보았는가? 오픈소스임에도 불구하고 자기혼자만 개발해 왔다고 써있었다. 대부분의 오픈소스들이
마찬가지라고 보면 될 것이다. 다만, 시너지효과로 인해 개발자 1명이 해낼수 있는 능력이 100명 1000명 수준으로
증폭되었기에 프로젝트가 맥을 이어갈 수 있는 것이다. 즉, 게임용어로 말하면 "버프"(버프를 모르진 않겠지..) 를
받은 것이다. 그렇게 되면 소수의 엘리트 파워가 오픈소스에 집중되는 일반유저의 관심만큼 증폭되기 때문이다.
단순히 눈에보이는게 전부라고 생각한다면 큰 것을 놓치게된다. (아니라고? 아님말고.. 허위사실 유포로 감금되어야
하나.. ㅎㅎ)
[현 상황]
현 상황에서는 Virtual HID Mouse 가 Auto Mouse 나 MACRO 라는 시장에서 대안으로 대두된 상태라 x시장에서도
드라이버의 판매가 이루어지고 있는 상황이다. 참고로 이 기술이나 드라이버를 판매자체는 사실 법적인 문제의
소지가 없다. 왜냐면 이 Virtual HID Mouse 기술은 범용기술이기 때문이다. 이 기술이 사용되고있는 쪽은 예를들면
조이스틱을 에뮬레이션하거나(용산에가면 조이스틱 판다..) 블루투스 마우스를 사용하도록 해주거나 혹은 그에
버금가는 마우스 업체들이 주로 보유하고있는 자체기술에서 많이 볼 수 있다. 이건 우리가 늘상 접하는 일반분야에서
말한것 뿐이고 무엇보다 가장 많이 사용되고 있는 곳은 임베디드 산업에서일 것이다.
그러므로 이 기술을 파는 것은 전혀 문제가 될 수 없다. 이 기술자체는 Microsoft 사에서 공식적으로 WinDDK 에
예제와 함께 배포하고 있다는 사실을 알만한 사람들은 이미 다 알고있겠다. 단지 그 이상의 자세한 정보를 찾기가
어렵고 Microsoft 사에서 제공하는 기술정보를 알기위해서 밑받침(선행) 되어야할 정보가 없다는 것이 문제점이다.
그래서 기존에 커널 드라이버를 개발하던 사람들이나 천국이지 필자같은 미천한 어플프로그래머들한테는 짜증만
나게 할 뿐이다. 이 중간에 생략된 정보들이 무엇인지 알아내야 한다. 그것이 유저레벨이라는 미천한 신분에서
커널레벨이라는 귀족신분으로 도약할 수 있는 길이다. 테스트환경 만드는게 짜증나서 공부하지 않았다가 완전
독박 쓴 기분이기에 좀 고약한 말투가 나온다. (기분 상하는 커널레벨의 고급 독자는 당장 이 문서를 접기 바란다.
필자도 커널레벨을 공부해야 윈도우를 진정으로 아는 것이라는 대에 이견이 없다. 다만, 오래전부터 블루스크린과
친구를 맺고싶지 않았을 뿐이다. 이거 참.. 뻘쭘하게 커널과 "절친노트" 라도 찍어야하나..)
===================================================================================================================
현재, 인터넷에는 몇몇의 기술 정보이외에는 관련된 기술적 자료를 찾아볼 수 없다고 해도 과언이 아니다.
진짜 짜증나도록 자료가 없다. 이해를 할 수 있을만한 한글로된 기술자료는 극적으로 한개를 찾을 수 있었다.
먼저, Virtual HID Mouse 를 찾아보면 가장먼저 접하게 되는 사이트가 있다.
까마귀라는 한국사람이 자신이 만들었다고하는 Virtual HID Mouse 가 검색이 된다. 블로그의 글 내용인데 사실
저수준을 다루는 고급개발자가 아니면 볼게없다.. (왜? 필자같은 어플프로그래밍 수준은 못알아 먹으니까.. + 안갈켜주니까
-_-;)
그리고 MSDN 이 검색되고 vhidmini 라는 것이 검색된다. 또한, 짱개 사이트의 vhidmouse 라는 소스와 hidmouse 라는 소스도
검색된다. 근데.. 오래전에 GxxxGuard 라는 소스가 인터넷에 떴다는 그 몹쓸 짱개사이트였다. 구현을 하기 위해서는
vhidmini 라는 소스가 그중에서 제일 유력한 후보가 될 것이고 나머지 vhidmouse 와 hidmouse 는 소스를 구하는데 한참이나
걸릴 것이다. 일단, 대충 둘러서 정보를 캣치해야한다. 다음은 이미 다들 알고있겠지만 SoftICE 라는 걸작을 만들어낸
"Numega Soft" 라는 회사의 Virtual HID Mouse 공식샘플의 일부소스이다. 아쉽게도 이 소스는 Windows 9x 계열에서
작동되는 드라이버이므로 NT 계열에서는 사용할 수 없다. 하지만 캣치할 수 있는 중요한 내용이 포함되어 있다. 위에서
언급한 것중에 vhidmouse 라는 것이다. 다음은 vhidmouse 소스 중의 일부분이다.
--------------------------------------------------------------------------------------------------------------------
// vmoudev.cpp - virtual mouse device for HID example
//=============================================================================
//
// Compuware Corporation
// NuMega Lab
// 9 Townsend West
// Nashua, NH 03060 USA
//
// Copyright (c) 1998 Compuware Corporation. All Rights Reserved.
// Unpublished - rights reserved under the Copyright laws of the
// United States.
//
//=============================================================================
// This module implements the device class of the virtual HID
// mouse minidriver.
#include
#include "vmoudev.h"
#include "hidmouse.h"
KTrace T("",TRACE_MONITOR, TraceAlways, BreakNever, KUstring(L"HidMouse"));
#define SCALEX 3
#define SCALEY 3
// The HID report descriptor for this device
// (taken from USB/HID specification)
HID_REPORT_DEscRIPTOR MouseHidReportDesc[] = {
0x05, 0x01, // Usage Page (Generic Desktop),
0x09, 0x02, // Usage (Mouse),
0xA1, 0x01, // Collection (Application),
0x09, 0x01, // Usage (Pointer),
0xA1, 0x00, // Collection (Physical),
0x05, 0x09, // Usage Page (Buttons),
0x19, 0x01, // Usage Minimum (01),
0x29, 0x03, // Usage Maximun (03),
0x15, 0x00, // Logical Minimum (0),
0x25, 0x01, // Logical Maximum (1),
0x95, 0x03, // Report Count (3),
0x75, 0x01, // Report Size (1),
0x81, 0x02, // Input (Data, Variable, Absolute), ;3 button bits
0x95, 0x01, // Report Count (1),
0x75, 0x05, // Report Size (5),
0x81, 0x01, // Input (Constant), ;5 bit padding
0x05, 0x01, // Usage Page (Generic Desktop),
0x09, 0x30, // Usage (X),
0x09, 0x31, // Usage (Y),
0x15, 0x81, // Logical Minimum (-127),
0x25, 0x7F, // Logical Maximum (127),
0x75, 0x08, // Report Size (8),
0x95, 0x02, // Report Count (2),
0x81, 0x06, // Input (Data, Variable, Relative), ;2 position bytes (X & Y)
0xC0, // End Collection,
0xC0 // End Collection
};
// HardwareID for the virtual mouse.
WCHAR HardwareID[]={L"ROOT\\NUMEGA_VIRTUAL_HID_MOUSE\0"};
WCHAR DeviceID[] ={L"ROOT\\NUMEGA_VIRTUAL_HID_MOUSE\0"};
HID_DEVICE_ATTRIBUTES DeviceAttributes = {
sizeof(HID_DEVICE_ATTRIBUTES),
MY_VENDOR_ID,
MY_PRODUCT_ID,
VERSION_NUMBER
};
-----------------------------------------------------------------------------------------------------
위와 같이 HID_REPORT_DEscRIPTOR 라는 것을 볼 수 있는데, 처음에는 이게 뭔지 모른다. 감도 안온다.
그러므로 그 관점을 그대로 두고 다음으로 넘어가서 다른 소스들을 보자.
(처음엔 원래 모르겠지.. 나만그런가.. -_-; 제길..)
인터넷에서 검색하면 위의 소스와 MSDN (Microsoft 사의 공식샘플과 설명문서) 을 먼저 보게 될 것이다.
그러면 당연히 vhidmini 라는 것도 접하게 된다. 소스는 인터넷에서 파는 놈들도 있으니 그냥 찾기는
좀 짜증이 날 것이다. Microsoft 사의 홈페이지에서 WinDDK 를 받으라. 그 안에 VHidMini 라는 샘플이
들어있다. 그놈을 보면 다음과 같이 생겨먹은 부분에 주목하자.
------------------------------------------------------------------------------------------------------
if (NT_SUCCESS(ntStatus)) {
//
// Use default "HID Descriptor" (hardcoded). We will set the
// wReportLength memeber of HID descriptor when we read the
// the report descriptor either from registry or the hard-coded
// one.
//
deviceInfo->HidDescriptor = DefaultHidDescriptor;
//
// Check to see if we need to read the Report Descriptor from
// registry. If the "ReadFromRegistry" flag in the registry is set
// then we will read the descriptor from registry using routine
// ReadDescriptorFromRegistry(). Otherwise, we will use the
// hard-coded default report descriptor.
//
queryStatus = CheckRegistryForDescriptor(DeviceObject);
if(NT_SUCCESS(queryStatus)){
//
// We need to read read descriptor from registry
//
queryStatus = ReadDescriptorFromRegistry(DeviceObject);
if(!NT_SUCCESS(queryStatus)){
DebugPrint(("Failed to read descriptor from registry\n"));
ntStatus = STATUS_UNSUCCESSFUL;
}
}
------------------------------------------------------------------------------------------------------
Microsoft 에서 MSDN 의 설명을 먼저 읽어본 뒤 위의 소스주석부분을 보자. 다음과 같이
친절하게 설명해주고 있다.
// Check to see if we need to read the Report Descriptor from
// registry. If the "ReadFromRegistry" flag in the registry is set
// then we will read the descriptor from registry using routine
// ReadDescriptorFromRegistry(). Otherwise, we will use the
// hard-coded default report descriptor.
설명에 써있듯이 자기네는 그냥 하드코딩 했으니까 ReadFromRegistry 를 참고하면 다른 디바이스를
등록할 수 있단다. 처음 접할땐 사전지식이 없기에 "이게 뭔 개소리야?" 라고 생각이 들 것이다.
(? 반응이 없으면 나만그런거 같다.. ㅎ)
------------------------------------------------------------------------------------------------------
그렇다면 이제 개소리는 집어치우고 다음을 보자.
------------------------------------------------------------------------------------------------------
Radly
This being my first driver project, and an unusual one at that, there's a lot to get my
head wrapped around. My intent is to produce a virtual HID device (mouse emulation) that
uses a complex non-HID physical device as the input medium. I'm using the VHidMini sample
from the WDK hid folder as a starting point, and I'm now at the point where I need to transform
the sample's report format into a mouse format. I notice with confusion that the report descriptor
in vhidmini.inf is different from that in vhidmini.h, with no explanation in vhidmini.htm as
to why they are different. Does anyone here have any insight on that?
어떤 놈이 필자가 생각하는 것과 동일한 구현을 해볼려고 삽질중에 무시무시한 고급개발자들과
해커들이 몰린다는 osronline 에 질문을 던져놓고 있다. 이 소리는 무엇인가? 필자도 결국 저
문제에 봉착하게 될 날이 온다는 시나리오를 미리 발견한 것이다. 그러므로 주의 깊게 읽어볼
필요가 있다. 여기서 얻어낼 수 있는 정보는 sample's report format 이란 것과 report descriptor
라는 두가지 내용이다. 원래부터 찾기힘든 정보들에는 동문서답 내지는 "내가 니 밥처먹는데
밥숫가락으로 퍼먹여주랴?" 정도의 댓글이 달리는데 그래도 이 글의 답글중에 괜찮은 답글이
있다. 근데.. 여전히 찾기힘든 정보만큼이나 짤막하게 달아놓는다. 이런사람이 있다는 것은
희망이고 곧 빛이다. 다음의 답글을 읽어보자.
-----------------------------------------------------------------------------------------------------
allen zhang
RE: VHidMini report descriptor(s)
see the follow source code and the ReadDescriptorFromRegistry function, You should be understand it.
deviceInfo->HidDescriptor = DefaultHidDescriptor;
queryStatus = CheckRegistryForDescriptor(DeviceObject);
if(NT_SUCCESS(queryStatus)) {
queryStatus = ReadDescriptorFromRegistry(DeviceObject);
...
입에서 욕은 나온다. 왜? 말할라면 다 말하던가 아니면 동문서답이나 하고 가던가 감칠맛나게 소스 네줄
뿌리고 튀다니.. 그래도 고맙다. 지식을 갈구하는 자에게는 이것조차 선물이다. 제길..
현자라면 푸념할때가 아닌거 같다. 여기서 뭔가 개념을 잡을 수 있는 정보를 캣치해야만 한다.
이 글에서 외국아가(짱개류 외국애인지 allen zhang 이구나.. 역시 중국은 AUTO 에 강한가보다..)
말하기를 소스코드를 보란다. 그 안에 ReadDescriptorFromRegistry 를 보라고 권하면서 일부 소스를 긁어서
보여준다. 글은 짧지만 분명 소스를 열어봤기에 긁어서 붙여줬을테니 아쉬워도 고마운 행동이다.
여기서 보라고 한 소스가 바로 위에서 VHidMini 샘플의 일부분이라고 뿌린 부분이다. 즉, 이 부분의 내용은
최초 질문자의 글에서 캣치한 sample's report format 와 report descriptor 라는 부분에대한 응답이다.
즉, 이 함수의 이름으로부터 알 수 있듯이 레지스트리로부터 디스크립터를 읽어들인다는 것에 바로
Virtual HID Mouse 의 핵심구현이 얽혀있다는 것을 시사한다는 점을 알 수 있다.
그런데 필자도 단지 이두개만 가지고 감을 잡지는 못했다. 왜냐면, 커널을 깊이 공부한 적이 없는 사람이
이 두가지의 자료만을 토대로 감을 잡아낼 수 있겠는가? 그렇다면 그건 천재이거나 영어를 모국어처럼
잘하는 사람일게 분명하다. 필자는 남보다 항상 두배로 삽질을 하는데 머리가 남들보다 딸려서 그렇다.
자료도 항상 두배로 찾아야 한다. 결정적으로 힌트가 되었던 것은 역시 국내사이트인데 드라이버 개발정보를
알려주는 곳인 driveronline.org 에서의 힌트와 임베디드 계통의 KELP 사이트에서 였다.
-------------------------------------------------------------------------------------------------------------
Re] Re] Re] USB 가상 키보드, 마우스 드라이버
·작성일 2007.10.02:10.40 (화)
· 작성자 Woof
· 605
pnp 를 이용해서든지 해서 가상적인 usb 장치가 인식이 되면 일반적인 usb 장치를 다루듯이 이용하시면 됩니다.
일반적으로 실제 장치들은 Windows에서 제공하는 기본적인 드라이버로도 동작하기 때문에 필요가 없지만
이와 같은 경우에는 간단하게 자기 드라이버를 열어서 인식된 장치 device와 통신하는 드라이버 정도는 필요하겠지요.
뭐, usb라서 따로 드라이버없이 application으로도 충분히 가능한건데 다들 위와 같은 방법을 이용하더군요.
새로나온 umdf 등을 이용하면 더 간단하고 새로나온 것에 대한 공부도 하면서 재미나게 할 수 있을지도 모르겠네요.
위 에서 말한 대부분의 경우 라고 한 것의 예를 간단히 들어보면 자신의 드라이버를 올리고 해당 드라이버에서
application과 통신 device를 생성한 뒤에 pnp를 이용해서 가상적인 usb 장치를 만들고 그것과 통신하는 길?을 적당히
만들어서 이용하시면 됩니다. sample에서는 가상적인 장치 인식이 바로 usb나 그런 부분이 아니였던 것 같은데 적당히
고치면 되겠지요.
위에도 썻지만, 잘 찾으면 다 만들어진 코드 어디 있을 것 같습니다. 저도 한번 찾으려다가 그냥 sample에 있어서
말았는데. :|
" 해당 usb 드라이버를 이용해서" 라는 부분에 대해서 물어보셔서 이 부분만 따로 답을 달면 해당 usb device를
제어(control)하는 드라이버를 지칭했습니다. 또 처음에 말한 것 처럼 class관련 드라이버에 대해서는 생각할
필요가 없습니다.
역시 위에 쓴 것 처럼 어디에 쓰실지 궁금하네요. 가상 키입력등은 S/W나 그런 자동화 테스트에 이용하기도 하고
꽤 여러군데서 쓰기는 하는데 안좋지는 않지만 뭐 . 그런데도 쓰여서 :|
-------------------------------------------------------------------------------------------------------------
최초 질문자가 어떻게 구현해야 하냐고 질문하자 pnp 를 통해서 가상적인 usb 를 인식시킨 다음에 적당히 device 와
통신시키라고 한다. 자세한 내용은 말해주지 않고 Microsoft 에서 제공하는 샘플로도 구현이 가능할 것이라는 내용과
어딘가에는 이미 다 만들어진 소스가 있을텐데 찾아보라고 한다. 이말 믿고 인터넷에서 찾아헤메다가는 마누라가
집나가도 모를것이다. 답변에서 보듯이 이 사람은 진짜 적당히 답글을 달고 있는 사람이라는 점을 주의해야 한다.
이 사람의 힌트와 통찰력이 Microsoft 사의 샘플에서 존재하는 핵심이라는 점을 알 수 있다. 일단, 정보는 머리속에
꼬깃꼬깃 담아두고 계속 다음 검색으로 넘어가면서 본인의 마음속에 답이 한가지로 수렴되도록 정보들을 계속 얻어
내어보자. 이쯤되면 정상적인 사이트를 뒤지는 것이 힘들어진다. 왜냐면 너무 정보가 부족하기 때문이리라.
고로 컨트롤러를 제어하는 것을 찾아본다. 예를들면 USB HID 조이스틱 드라이버 같은 것을 찾아내는 것이다.
다음과 같은 좋은 예가 있었다.
XBOX 의 6축 조이스틱 패드를 윈도우에서 사용할 수 있도록 해주는 소스였다. 검색을 할때는 기존에 얻었던 소스에서
일부 특이하게 보일만한 함수를 키워드로 검색하면 운좋게 찾을 수 있다. 소스를 보면 너무 길고 난해하고 이해하기
힘들뿐이다. 당연히 커널관련 지식이 깊지않은 이상 어떻게 분석하고싶어도 그럴 도리가 없다. 그러므로 파일구성을
보는 것이 전부이다. 여기서 또한가지 힌트를 얻을 수 있다.
XBCD_control.c
XBCD_driver.c
XBCD_driver.h
XBCD_hid.h
XBCD_report.h
위와 같은 파일구성에서 driver 나 control 소스를 보기전에 report 라는 눈에 띄는 놈이 있다. 이 헤더파일의 내용을
보게되면 다음과 같은 것이 적혀있다.
-------------------------------------------------------------------------------------------------------------
char ReportDescriptor[213] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x09, // USAGE_PAGE (Button)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x01, // USAGE (Button 1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x02, // USAGE (Button 2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x03, // USAGE (Button 3)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x04, // USAGE (Button 4)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x05, // USAGE (Button 5)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x06, // USAGE (Button 6)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x07, // USAGE (Button 7)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x08, // USAGE (Button 8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x09, // USAGE (Button 9)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0a, // USAGE (Button 10)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0b, // USAGE (Button 11)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0c, // USAGE (Button 12)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x04, // REPORT_SIZE (4)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x75, 0x10, // REPORT_SIZE (16)
0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x95, 0x02, // REPORT_COUNT (2)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x02, // USAGE_PAGE (Simulation Controls)
0x95, 0x02, // REPORT_COUNT (2)
0x09, 0xba, // USAGE (Rudder)
0x09, 0xbb, // USAGE (Throttle)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x07, // LOGICAL_MAXIMUM (7)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM (315)
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
0x75, 0x04, // REPORT_SIZE (4)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0d, // USAGE (Button 13)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0e, // USAGE (Button 14)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x0f, // USAGE (Button 15)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x10, // USAGE (Button 16)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x05, 0x00, // USAGE_PAGE (Not Defined)
0x09, 0x00, // USAGE (Undefined)
0x09, 0x01, // USAGE (Undefined)
0x09, 0x02, // USAGE (Undefined)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0xc0 // END_COLLECTION
};
-------------------------------------------------------------------------------------------------------------
그리고 이 홈페이지에서 최대한 얻을 수 있는 것은 다 캣치해야 하는데 http://www.redcl0ud.com/xbcd.html
홈페이지의 마지막쯤에 보면 http://www.redcl0ud.com/files/USBView.cab 라는 것이 있다. 그리고 캡춰사진이
있는데 핵심사항으로 체크를 해둔 부분이 있다. idVendor, idProduct 라는 부분에 체크를 해두고 있다.
그리고 USBView 에서 보여주는 내용이 모냐면 바로 Device Descriptor 였다. 오호라.. 내친김에 이 사이트에서는
USB 드라이버를 제작하는 방법까지 설명하는 링크를 걸어두고 있었다.
이 링크인데 제목은 "Inside XBox Controller" 라고 되어있다. 대충 무슨내용이 있는지 열거하자면..
-------------------------------------------------------------------------------------------------------------
...
...
...
Here are descriptor dumps of the hub, the gamepad and the memory unit.
* Descriptors of the integrated hub
* Descriptors of the gamepad (American)
* Descriptors of the memory unit
The vendor ID is 0x045e (Microsoft). Product IDs are as follows:
ID product
0x001c integrated hub
0x0202 gamepad (American)
0x0280 memory unit
0x0284 DVD remote receiver
0x0285 gamepad (Japanese)
It is not recommended to distinguish Xbox gamepads by vendor/product IDs because third-party controllers may
have their own vendor/product IDs.
...
Input Report
The input report is 20-byte.
헤더그림
Output Report
The output report (rumble control) is 6-byte.
헤더그림
The Xbox gamepad lacks the HID report descriptor that describes the input/output report formats. Based on the
above report formats I have tried writing the report descriptor for your information. This is a mere example;
neither official nor verified. Accuracy is not guaranteed.
* Text format
* Binary format
* HID Descriptor Tool format (to be loaded into HID Descriptor Tool)
---------------------------------------------------------------------------------------------------------------
위와 같은 내용들이 있었다. 상당히 많은 삘을 주고있다. 누가봐도 HID Descriptor 와 HID Report Format
그리고 Input Report 와 Output Report 를 통해서 통신한다는 아주 기본적인 컨셉(개념)은 머리가 아니라(T.T)
가슴에 와닿을 것이다. (젠쟝.. 가슴에만 담아두마.. -_-;)
일단, 이쯤에서 XBCD 소스는 닫아둔다. 언제 이거 분석하고 앉아있는가.. 최종컨셉이 다르기 때문에 힌트만
뽑아먹고 닫아두는 것이다. 애초에 만들려고 한건 Virtual HID Mouse 인데 이건 그 컨셉이 아니기에 대충
훑어보고 넘겨야 한다. 또 다른 소스들을 보자. 마구잡이로 검색하면서 받아둔 소스중에 앞에서 처음에
말했던 hidmouse 라는 소스를 한번 진단해보자.. -_-;
(PIC 를 이용한 마우스 제작소스)
파일을 열어보니 원하는게 아닌듯 보였는데 알고보니 PIC 용이었다.
운 좋게도 필자는 PIC18x 롬라이터를 갖고 있어서 이 소스가 뭔지 알 수 있었다.
오래전에 친구의 부탁으로 PIC18x 칩에 어셈블리를 롬라이팅 하는 프로그램을 만들어준 적이
있어서 무슨 소스인지 금방 알 수 있었다. 요새는 PIC 프로그래밍에도 C 가 쓰이고 PIC 는 PLC 대체용으로
쓰이기도 한다. 그런데 PIC 로 USB 모듈을 부착하고 마우스로 제작이 가능한거 같았다.
어쨌거나 원하는 내용은 아니었지만 생각해보면 가장 중요한 것이 물리적인 장치 아닌가?
물리적인 장치에서는 기존에 찾은 소스들이나 검색내용들과 어떤 차이가 있는지 알아볼 필요도 있다.
hidmouse 라는 소스에서 파일구성을 보면 특이한 놈이 있다.
usb_descriptors.c 라는 소스가 있는데 여태까지 눈여겨왔던 descriptor 라는 단어가 보일 수 밖에 없다.
-------------------------------------------------------------------------------------------------------------
/* Device Descriptor */
ROM USB_DEVICE_DEscRIPTOR device_dsc=
{
0x12, // Size of this descriptor in bytes
USB_DEscRIPTOR_DEVICE, // DEVICE descriptor type
0x0200, // USB Spec Release Number in BCD format
0x00, // Class Code
0x00, // Subclass code
0x00, // Protocol code
USB_EP0_BUFF_SIZE, // Max packet size for EP0, see usb_config.h
MY_VID, // Vendor ID
MY_PID, // Product ID: Mouse in a circle fw demo
0x0003, // Device release number in BCD format
0x01, // Manufacturer string index
0x02, // Product string index
0x00, // Device serial number string index
0x01 // Number of possible configurations
};
... 중략 ...
//Class specific descriptor - HID mouse
ROM struct{BYTE report[HID_RPT01_SIZE];}hid_rpt01={
{0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Buttons) */
0x19, 0x01, /* Usage Minimum (01) */
0x29, 0x03, /* Usage Maximum (03) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (0) */
0x95, 0x03, /* Report Count (3) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Constant) ;5 bit padding */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xC0, 0xC0}
};/* End Collection,End Collection */
-------------------------------------------------------------------------------------------------------------
hidmouse 의 소스는 분석하지 않고 특징적인 몇개의 파일만 열어보고 위의 부분에 주목하고 닫아 버린다.
역시 컨셉은 물리 마우스가 아니라 가상 마우스이기 때문이다. 가상 마우스는 연결되면 오른쪽 아래의
트레이에 연결되었다고 풍선글이 떠야 하는 형태로 진행되어야 하는 것이 머리속의 구상이었다. 이미
driveronline 의 힌트에서 pnp 를 통해서 usb 를 인식시키라는 내용을 보았고 osronline 에서는 vhidmini 의
특정부분을 언급했으며 osronline 의 최초 질문자는 샘플의 포맷과 디스크립터를 언급했다. 다음의 사이트를
보게되면 약 90% 의 감이 오게되는데 임베디드 계통이 역시나 제일 확실하다. 시스템 프로그래머라는
황무지(wild)에서 살아가는 사람들이기 때문이다. 다만, 숫갈로 퍼먹여 주는걸 제일 싫어해서 짜증난다.
다음의 KELP 사이트의 글을 보면(http://kelp.or.kr/korweblog/stories.php?story=07/02/13/3453938)
글쓴이가 usb mouse 구현시 뭔가 이상하다는 식으로 적어놓고 있는 내용을 볼 수 있는데 한번 읽어보자.
-------------------------------------------------------------------------------------------------------------
usb slave 포트를 이용하여 usb mouse를 구현하고 있습니다.
글쓴이 : omayaro (2007년 02월 13일 오후 02:10) 읽은수: 643
<커널 2.6.11에서 gadget api를 이용하여 usb 마우스를 구현해 보고 있습니다.>
처음에 모듈을 등록하기 위하여
static int __init my_module_init(void)
{
int retval;
retval = usb_gadget_register_driver( &g_ugdDriver );
if (retval)
{
printk(KERN_ERR "[ omayaro ] module_init: cannot register gadget driver, ret=%d\n", retval);
return retval;
}
return 0;
}
위에서 처럼 하여 등록을 마쳤습니다.
그리고 pc( window xp 와 fedora core4 를 사용하는 pc 2대를 한번씩 테스트 함 )에
usb cable을 연결하였습니다.
그랬더니 임베디드 보드와 pc에 몇가지 반응이 오더군요..
아래의 내용은 진짜 마우스를 꼽았을때에 windows xp에서 usb descriptor를
분석한 내용입니다.
Device Descriptor:
bcdUSB: 0x0110
bDeviceClass: 0x00
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x08 (8)
idVendor: 0x062A
idProduct: 0x0000
bcdDevice: 0x0000
iManufacturer: 0x00
iProduct: 0x00
iSerialNumber: 0x00
bNumConfigurations: 0x01
ConnectionStatus: DeviceConnected
Current Config Value: 0x01
Device Bus Speed: Low
Device Address: 0x02
Open Pipes: 1
Endpoint Descriptor:
bEndpointAddress: 0x81
Transfer Type: Interrupt
wMaxPacketSize: 0x0004 (4)
bInterval: 0x0A
그리고 아래의 정보는 제가 짠 프로그램이 동작하여 pc에 등록된 정보입니다.
Device Descriptor:
bcdUSB: 0x0110
bDeviceClass: 0x03
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x10 (16)
idVendor: 0x062A
idProduct: 0x0000
bcdDevice: 0x0199
iManufacturer: 0x00
iProduct: 0x00
iSerialNumber: 0x00
bNumConfigurations: 0x01
ConnectionStatus: DeviceConnected
Current Config Value: 0x00
Device Bus Speed: Low
Device Address: 0x00
Open Pipes: 0
보시면 ConnectionStatus에 들어 있는 정보가 좀 틀리고 end point의
정보는 아예 없는 것을 보실수 있습니다.. 이 내용을 분석한 제 생각으로는
일단 Open Pipes의 수가 0인 것으로 보아 우선 정상적으로 연결 실패.. 가
난 것으로 보이고요
end point가 없는 것으로 보아 어떤 설정또는 ep0를 통하여 세팅중에 에러가
난 것으로 보입니다...
이제 질문...( 서론이 좀 길었죠??ㅡㅜ;;;; ) 제가 아직 usb 에 대해 정확히
이해를 못해서 인지는 몰라도 usb ep0를 통하여 setup이 될때 어떤 순서로
setup이 이루어 지는지 잘 모르겠습니다. 제 생각으로는
1. host가 device descriptor를 요청하여 가져감
2. 나머지 descriptor( configure, interface )를 가져감
으로 생각이 되는데요.. 순서가 저렇게 되는 것이 맞나요??
그리고 pipe( 제 생각에는 in/out end point )가 open되는 시점이 이 언제인지
궁금합니다..
조금 정리해서 물어본다면..
usb device가 pc에 꽂힌 후 정상 인식 되기까지 host가 device에게 요청하는
메시지의 순서가 궁금하네요~~
그리고 언제 pipe가 오픈이 되는 지... 도 궁금합니다~~
고수님들의 답변 기다릴게요~~
하루에 refresh만 5천번하는.. omayaro 였습니다...ㅠ0ㅠ
--------------------------------------------------------------------------------------------------
오케이.. 뭔가 삘이오는데.. 바로 앞에서 가슴속에 담아둔 그거네.. -_-; 중요한건 바로
디스크립터(descriptor) 컨피겨(configure) 인터페이스(interface) 라는 내용이 또 나온다.
어쨌거나 저쨌거나 질문자는 실패한 사람이니까 믿을게 못된다. 여기에 달린 댓글이 중요하다.
그런데.. 역시나.. 멋진 시스템 프로그래머들 같으니라구.. ㅎㅎ 직접읽어보라.. 민망하다..
--------------------------------------------------------------------------------------------------
익명 (2007년 02월 13일 오후 10:08)
밥은 직접 떠서 드세요.
device 연결시점에서는 default(첫번째) configuration 으로 동작합니다.
host 쪽에서 사용자의 요구에 의해 다른 configuration 으로 전환합니다.
흔한 경우는 아닙니다.
이런 방식을 사용하는 대표적인 경우는 device 쪽에 end point 가 모자랄 경우입니다.
간혹, host쪽 사용자에게 디버깅 포트등을 숨기기 위해서 사용하기도 합니다.
--------------------------------------------------------------------------------------------------
역시나 댓글한번 멋지게 달려있었다. 밥은 직접 떠먹어야 한다는 말. 누가 그걸 모르나.
이쯤되면 검색에 이골이 나기 일보직전이 되고 서서히 자신감도 사라지고 짜증이 섞이기
마련이다. 이때한번 refresh 가 필요한데 검색은 계속되어야 한다.. 쭈~욱..
점점 검색하다보면 Filter Driver 에 대한 내용이 나오기도 하고 처음부터 검색이 되더라도
알아먹지 못했던 내용이 두번째 검색하다 모르고 똑같은걸 또 보게되면(즉, 본걸 나중에
또 봤을때)
이해가 되기 시작하는 부분이 생겨나기 시작한다. 바로 다음의 부분들이 그러한 부분들이라
하겠다.
-----------------------------------------------------------------------------------------------------------------
bodnar
February 11th, 2007, 05:58 AM
I am trying to fix a part of the report descriptor on an existing USB HID device that woked fine but
has problems on Vista. It installs
and everything is OK with it but DirectX refuses to see it due to some inconsistency in the report descriptor.
I understand that I would need to write a filter driver to fix that.
I have downloaded recent WDK and looked at the samples, specifically HID\Firefly sample but I cannot figure out
how to alter
Report Descriptor data when the driver receives IRP_MN_START_DEVICE in DispatchPnP. I can see how PDEVICE_OBJECT
DeviceObject is passed
to it but mmm.. how do I get access to HID device details so I can alter it?
When I look inside HID\Vhidmini virtual HID minidriver I can see exactly what I would want to use in the
filter driver:
deviceInfo->ReportDescriptor = NewReportDescriptor;
deviceInfo->HidDescriptor.DescriptorList[0].wReportLength = ...;
I am a bit lost... Any help is appreciated.
------------------------------------------------------------------------------------------------------------------
미국말로 뭐라 지껄이는지 전혀 관심없다. 오로지 Report Descriptor data when the driver receives IRP_MN_START_DEVICE
in DispatchPnP 라는 문장과 다음의 소스 두부분이다.
deviceInfo->ReportDescriptor = NewReportDescriptor;
deviceInfo->HidDescriptor.DescriptorList[0].wReportLength = ...;
이 글에서 필자가 어느타임에 어디를 수정해야 할지 방향을 구체적으로 잡아나갈 수 있는 단초가 마련되기 시작한다.
즉, DispatchPnP 에서 IRP_MN_START_DEVICE 를 받았을때 Report Descriptor data 가 관계가 있다는 점이고 ReportDescriptor 를
NewReportDescriptor 로 할당하는 조작을 잡아낼 수 있다. 원래는 vhidmini 라는 Microsoft 사의 공식샘플이 어떻게 생겨먹었는지
잠시 소스 일부분을 보자면
// Store the registry report descriptor in the device extension
//
deviceInfo->ReadReportDescFromRegistry = TRUE;
deviceInfo->ReportDescriptor = RegistryReportDescriptor;
deviceInfo->HidDescriptor.DescriptorList[0].wReportLength =
(USHORT)RegistryReportDescriptorLength;
위의 모습처럼 되어있다. 그런데 저 미국아가 한 짓은 deviceInfo->ReportDescriptor = RegistryReportDescriptor 를
NewReportDescriptor 로 바꿨다는 것이다. 그러니 필자도 역시 이 부분을 건드리게 될 것이란 소리가 된다. 그러므로
수정을 가할 부분을 한 부분 구체적으로 알아먹었다. 이 글의 맨 처음 시작부분의 Numega Soft 의 소스라고 되어있는
부분을 보자.
// The HID report descriptor for this device
// (taken from USB/HID specification)
HID_REPORT_DEscRIPTOR MouseHidReportDesc[] = {
0x05, 0x01, // Usage Page (Generic Desktop),
0x09, 0x02, // Usage (Mouse),
0xA1, 0x01, // Collection (Application),
0x09, 0x01, // Usage (Pointer),
0xA1, 0x00, // Collection (Physical),
0x05, 0x09, // Usage Page (Buttons),
0x19, 0x01, // Usage Minimum (01),
0x29, 0x03, // Usage Maximun (03),
0x15, 0x00, // Logical Minimum (0),
... 생략 ...
해당 부분을 보면 이제서야 뭔가 감이 오기 시작하게 되는 것이다.
---------------------------------------------------------------------------------------------------
드디어;;
·작성일 2007.01.28:15.25 (일)
· 작성자 rechoco
· 회 487
hid minidriver 테스트 살짝 성공~
샘플소스를 구해서 레포트 디스크립터랑 익스텐션 조금 손만 본거지만.
디바이스에서 데이터를 hid포멧에 맞춰서 주는게 아니라서..
강제로 제가 바꿔줘야 했거든요ㅋ
마우스로 테스트 해봤더니 쭉쭉 잘옮겨지더군요
물론 목표한 디지타이져는 아직 안됩니다;;
(와컴것 분석했더니 절대좌표모드일때도, 마우스 플래그를 쓰더군요
xp에서는 디지타이져가 지원이 안되니까 절대좌표 "처럼" 마우스좌표로 작업합니다.
저도 그렇게 하긴했는데. 영 개운하지가 않아서;;
디지타이져가 비스타에서는 지원 된다길래 해봤는데 실패했다는;;)
제가 참고한 샘플은 WDK의 vhidmini 소스입니다.
xp용으로 inf파일하고 소스파일 조금 수정하시면 빌드도 잘되고..
헛짓거리중에 잠깐 들러봤습니다.
드라이버 온라인님들 모두 화이팅하세요!!
---------------------------------------------------------------------------------------------------
아쉽게도 이 글은 가상 마우스인지 실제 마우스의 필터드라이버를 만든것인지 알길이 없어서
단지 말 그대로 희망만 주는 글인데 되긴 되나부다 정도로만 넘겨야 했다. 중요한 개념가닥을
잡아내는 파편과도 같은 내용들은 모두 끝났고(사실 더 많지만..) 다음의 세가지 정보의 검색이
사실상 전체적인 개념(컨셉)을 모두 얻도록 해주었다.
---------------------------------------------------------------------------------------------------
08-May-08 05:16:00
I wrote a bus driver that generated virtual USB PDO for Printer, Scanner, and
SmartCard. It worked fine before. Recently, I was requested to provide a
virtual USB mouse PDO. What I did was as following steps
1. An usermode application access bus driver to add a new PDO
2. bus driver use IoCreateDeviceSecure to create a new device and invalid
bus relation.
3. PNP manager found this new device, it will query the hardwareid, device
instanceid, and compatible id.
4. I provide USB\Class_03&SubClass_01&Prot_02 as compatible ID
5. System find this is a HID device and start to query Device Descriptor,
Configuration Descriptor, and HID descriptor.
6. Since this is a virtual mouse, bus driver gives HID descriptor without
hardware. I copy a standard mouse device's HID descriptor (3button usb
mouse) to caller.
7. From Device Manager, I can see a HID device shows up, but there is not
mouse device shows up.
If I plug in a real usb mouse, I can see system create a HID device first,
then HIDClass driver create a PDO for mouhid driver. Is anyting wrong I did?
I have carefully checked the USB data sent to caller, everything is ok, but
system doesn't like this device as mouse, Just consider it as generic USB
HID device.
Could someone give me help?
Thanks!
---------------------------------------------------------------------------------------------------
유저모드
---------------------------------------------------------------------------------------------------
2. KSP(www.ksyspro.org) 라는 곳에서 작성한 "USB강의자료.PDF" 라는 파일이 인터넷에서 검색되었다.
내용의 제목은 "9차 정기 세미나 강의 자료" USB Device Driver 강의였다. 아쉽지만 원래 이런
사이트는 문이 닫혀있다. (원래 그런거니까 이해를 해야한다.. -_-; 얼마나 힘들겠는가..? )
- 내용이 작살이다. 이건 그냥 인터넷에서 받아서 보라.. 전체적인 개념정립이 이루어진다.
(아마 앞서서 했던 기본적인 검색뻘짓이 없이는 읽을 수 있는 내용이 아니었으리..)
---------------------------------------------------------------------------------------------------
3. MSDN & ReactOS 소스 중의 USB 부분에 있는 Mouse 드라이버
- 항상 마지막은 MSDN 의 승리이다. 전체적인 모든 내용이 다 들어있다. 빌어먹을 일정수준이상이
되지 않으면 처음에 백날봐도 못알아 처먹는다는 것이 문제다. 커널이던 응용프로그래밍이던
이건 차이가 없다. 지금도 응용프로그래밍도 못알아 먹는게 태반이니까. 어쩔수 없이 이 고생을
치르는건 MSDN 을 보기위해서가 아닐까. (COM 프로그래밍도 MSDN 이 제일 많은 정보가 있다.)
이 말을 증명해보겠음!!
다음은 MSDN 의 vhidmini.h 라는 파일에 능구렁이처럼 맨 마지막 부분에 주석으로 처리되어있다.
[vhidmini.h 파일]
... 생략 ...
/*
//
// Here is sample descriptor that has two top level collection - mouse
// collection and vendor defined collection with a custom feature item. If
// you want to provide sideband communication with your hidmini
// driver, you can add a custom collection with the collection provided
// by the hardware and open the custom collection from an app to
// communicate with the driver.
// 여기 두개의 탑레벨 모음인 샘플 디스크립터가 있다.
// 커스텀 피처아이템을 가진 마우스 모음과 벤더 정의 모음이다.
// 니가 만약 너의 hidmini 드라이버를 가지고 사이드 밴드 통신을 제공하길
// 원한다면, 너는 하드웨어에서 제공되는 모음과 응용프로그램으로부터 드라이버
// 통신하는 것까지 개인모음을 추가할 수 있다.
// (역주: 즉, 몬소리냐면 이거 앞에서 선언한 DefaultReportDescriptor 대신에
// 이걸 그냥 가져다가 써라. 마우스 예제다. 이 말이나 마찬가지임.
// 여기에 니가 원하는거 추가해서 쓰라는 소리임. 이미 소스에 다 있었음.)
HID_REPORT_DEscRIPTOR DefaultReportDescriptor[] = {
0x05, 0x01, //Usage Page (Generic Desktop),
0x09, 0x02, //Usage (Mouse),
0xA1, 0x01, //Collection (Application),
0x85, 0x01, //REPORT_ID (1)
0x09, 0x01, //Usage (Pointer),
0xA1, 0x00, //Collection (Physical),
0x05, 0x09, //Usage Page (Buttons),
0x19, 0x01, //Usage Minimum (01),
0x29, 0x03, //Usage Maximun (03),
0x15, 0x00, //Logical Minimum (0),
0x25, 0x01, //Logical Maximum (1),
0x95, 0x03, //Report Count (3),
0x75, 0x01, //Report Size (1),
0x81, 0x02, //Input (Data, Variable, Absolute), ;3 button bits
0x95, 0x01, //Report Count (1),
0x75, 0x05, //Report Size (5),
0x81, 0x01, //Input (Constant), ;5 bit padding
0x05, 0x01, //Usage Page (Generic Desktop),
0x09, 0x30, //Usage (X),
0x09, 0x31, //Usage (Y),
0x15, 0x81, //Logical Minimum (-127),
0x25, 0x7F, //Logical Maximum (127),
0x75, 0x08, //Report Size (8),
0x95, 0x02, //Report Count (2),
0x81, 0x06, //input (Data, Variable, Relative), ;2 position bytes (X & Y)
0xC0, //End Collection,
0xC0, //End Collection,
0x06,0x00, 0xFF, // USAGE_PAGE (Vender Defined Usage Page)
0x09,0x01, // USAGE (Vendor Usage 0x01)
0xA1,0x01, // COLLECTION (Application)
0x85,0x02, // REPORT_ID (2)
0x09,0x01, // USAGE (Vendor Usage 0x01)
0x15,0x00, // LOGICAL_MINIMUM(0)
0x26,0xff, 0x00, // LOGICAL_MAXIMUM(255)
0x75,0x08, // REPORT_SIZE (0x08)
0x95,0x01, // REPORT_COUNT (0x01)
0xB1,0x00, // FEATURE (Data,Ary,Abs)
0xC0 // END_COLLECTION
};
*/
자.. 이제 끝났다~ 라고?
한숨을 쉬기에는 너무 이르다. 대부분의 혼선과 문제점은 여기서부터 시작되기 때문이다.
Microsoft 사의 WinDDK 라는 개발킷에 있는 vhidmini 샘플은 그저 샘플일 뿐이기에 정상작동이 되는지
확인을 해야하기 때문이다. 필자는 위에서 주석처리 되어있는 마우스 예제 DefaultReportDescriptor 의
주석을 풀고 기존에 있던 샘플 DefaultReportDescriptor 와 교체했다.
드라이버를 컴파일하는 방법은 여기서 설명하지 않는다. 그냥 WinDDK 설치후 프로그램 메뉴에서 XP 용
콘솔창을 열고 vhidmini 디렉토리로 이동후 nmake 명령을 내리면 컴파일이 가능한 것을 여기서 구차하게
다 설명을 할순없다. (그러면서도 벌써 설명까지 다 해주는 친절한 금자씨.. ㅎ)
이제 vhidmini 드라이버를 컴파일하고 장치를 인스톨 시키면 오른쪽 화면아래 트레이에 드라이버가
인식되었다고 뜰 것을 기대했다. 우리가 흔히 새 마우스를 USB 포트에 꽂으면 장치가 검색되었다고 뜨는걸
볼 수 있지 않은가? Human Interface Device(휴먼인터페이스장치) 어쩌구라는 메시지와 함께 잠시뒤에
마우스가 발견되었다는 식의 그런 메시지를 기대했다. 그러나 결과는 참담했고 미궁속으로 계속해서
빠져들고 말았다. 왜 그랬을까..?
비단 이 문제는 필자만의 문제가 아니었다. vhidmini 샘플 드라이버를 처음접하는 모든 프로그래머들이
모두 이같은 삽질의 미궁속에 빠져든다는 점을 검색을 통해서 알 수 있었다. 바로, MS 의 함정을 말이다.
여기서 다시 위와 같은 오류를 범해나가는 설명을 할 것이다. 어떤식으로 접근할 것인가와 얼마나 많은 뻘짓이
필요했는가에 대해서 설명할 필요가 있다고 본다. 그로인해서 얻은 것들은 상당히 많이 있다. 바로 단거리
스피드로 달리는 사람들은 놓칠수 있는 정보를 마라토너들은 두루두루 보고 달릴수 있는 것처럼 주변지식들을
충분히 얻을수 있다는 장점이 있다. 이 문서를 쓰는것 자체가 사실 기술적인 것에 너무 치우치는 쪽 보다는
학습방법을 알리는 병행효과를 얻기위한 것이기 때문에 무엇을 보았는지 지금부터 과정을 설명할 것이다.
앞서서 우리는 가상마우스를 만들기 위해서는 Virtual HID Device 를 만들수 있어야 한다는 점만 인식하고
출발했다. 완전히 지식이 전무한 상태에서 기본 골격코드마져 없는 허당상태로 시작할 수 있는 프로그래머는
아무도 없다. 이미 가상마우스 프로그램을 만들어본 경험이 있는 프로그래머라도 기본적인 코드의 골격없이
모든걸 직접 작성하는걸 기대하는건 어려운일이란 얘기이다. 그래서 우리가 앞에서 선행작업을 한 것이 바로
그 뼈대를 찾기위한 작업이었고, 숱한 오류과정을 거치며 우리가 만들 가상장치의 핵심뼈대를 발굴하고 비교
분석하여 선정하는 작업까지 마쳤다. 그리고 우리가 만들 장치에서 가장 중요한 핵심키포인트를 잡아내는
학습방법까지 소개하였다. 결론적으로 앞에서 습득한 사항들을 요약해 보자면..
1. 우리가 만드는 Virtual HID Device 는 mouse 또는 keyboard 와 혼합형태(혹은 단독일수도.. 그건 선택사항)이며
가상 USB 를 통해서 장치가 인식되어야 한다. 이 점은 프로그래밍적으로 정보를 수집하기 전에 머리속으로
구상한 내용에 속한다.(나중에 언급하겠지만 실제 설치/작동은 프로그래머의 상상과 약간 다르다.)
2. 여러 정보들을 수집하여 비교분석 한 뒤 그 중에서 vhidmini 라는 Microsoft 사의 WinDDK 개발킷 공식샘플을
채용하기로 최종결론을 내렸다.
3. vhidmini 라는 샘플을 운용하기위해 요구되는 스킬은 USB 의 HID 라는 인터페이스이며 이 인터페이스의 핵심
키포인트는 바로 Report Descriptor 라는 Descriptor 를 어떻게 기술할 것인가에 달려있다는 점을 알아낼 수
있다. 이 점은 이미 학습방법으로 어떻게 그 특징을 캣치하는지 보여주었다.
4. 우리는 최종적으로 Virtual HID Device 를 마우스로 인식시키기위해 Report Descriptor 라는 기술자(descriptor)를
찾아내야 했으며 적당한 기술자를 vhidmini.h 에서 발견하였다. 이 기술자를 Default 로 맞추고 컴파일 한 뒤
VMware 에 설치된 윈도우에서 설치하면 실제장치로 인식된다는 것까지 모두 정립하였다.
다음의 명령어를 이용해서 장치를 설치할 수 있다. 즉, 윈도우가 설치된 VMware 에는 vhidmini.sys 와 vhidmini.inf
그리고 devcon.exe 라는 총 세개의 파일이 복사되어야 한다. devcon 이라는 툴은 윈도우 장치관리자가 할 수 있는
모든 기능+ 를 콘솔에서 명령내릴수 있도록 해주는 커맨드유틸이다.
[설치명령]
devcon install vhidmini.inf "{D49F883C-6486-400a-8C22-1A9EF48577E4}\HID_DEVICE"
위와 같이 VMware 에 컴파일된 vhidmini 드라이버 파일들을 모두 복사한 뒤에 설치명령을 내린다.
VMware 의 화면에는 신뢰를 받지못한 장치 드라이버 설치시에 뜨는 경고문구가 뜨게될 것이다.
<계속> 이라는 버튼을 클릭하게되면 sys 파일을 찾지못해 디렉토리 지정창이 한번 더 뜰 것이다.
그 이유는 현 설치위치(devcon 명령어 실행디렉토리 위치) 밑에 i386 이라는 디렉토리에 sys 파일이
존재한다는 가정을하기 때문이다. 즉, INF 파일이 존재하는 위치를 기준으로 그 하위 i386 디렉토리에서
드라이버파일을 찾는다. 그래서 드라이버를 못찾는다는 창이 뜨게된다. 이건 그냥 적당히 vhidmini.sys
파일이 있는 디렉토리를 지정하면 알아서 설치가된다. 그런데.. 우리가 예상했던 설치모습이 아니었다.
그건 필자만의 착각이었을런지도 모르겠지만, 일단 이렇게 장치의 설치과정은 밍밍하게 끝나버린다.
이제 장치가 제대로 인식되었는지 확인을 하기위해 "장치관리자" 를 오픈한다. 그러면 휴먼인터페이스
장치쪽에 두가지 장치가 새롭게 추가되어있는 것을 보게될 것이다. 그런데, 뭔가 생각하던것과는 다르게
인식된 듯한 생각을 가지게 될 것이다. 그 장치는 그저 Generic HID 장치일 뿐 마우스가 아니다. 이게
도대체 어찌된 영문인가? 뭔가 잘못된 것이 있는지 확인해보고 수도없이 DefaultReportDescriptor 를 수정
해봐도 역시나 마찬가지로 마우스로 인식되질 않았다. Revert to snapshot 을 수도없이 반복하며 디스크립터
(Report Descriptor) 를 수정해도 역시나 반응은 일반장치(Generic HID) 일 뿐이었다. 정확히 말하면 VMware
기준으로 XP 에서 장치를 설치했을때 설치되는 이름은 두가지였다.
"HID 준수장치"
"Root Enumerated HID Device (sample)"
위의 두가지 장치가 설치된다. 우리가 원하는 것은 "HID 준수장치" 가 "HID 규격 마우스" 로 인식되어야만
한다. 그런데, 이런현상이 계속 지속되어 혼란이 가중될 뿐이었다. 어딘가 필자가 모르는 키포인트가 또다시
존재할 것이리라 생각하고 이 현상을 겪는 어딘가에 있을 동지에게 SOS 를 날려야 했다. 우리의 구글형님이
그러한 고충을 겪는 사람들을 모두 한자리로 집합시켜주었다. 그런데.. 구글이 불러모은 검색정보들은 하나
같이 모두 헛소리들 뿐이었다. 근접은 했어도 정답이 하나도 없는게 아닌가.. 제길..
FireFox 에서 탭을 약 20개 가까이 띄워놓고 검색에 검색을 반복하며 필자의 Report Descriptor 정보중 어디가
잘못되었는지를 찾아내기 위해서 안간힘을 쓰고 있었다. Report Descriptor 라는 것은 무엇인가? USB 라는
장치가 자기의 정보를 상위장치에 넘겨서 인식되도록 하기위한 마치 신분증과도 같은 것이다. 어디서 태어났고
어디서 자랐으며 나이는 몇살이고 남성인지 여성인지 기타등등.. 마치 이런정보처럼 인식정보를 쏘기전에
셋팅하는 값이다. 이 값이 하나라도 잘못될 경우에 장치는 절대로 제대로 인식되지 않는다. 항상 사람이 고생을
하려면 깨닫는 과정에서 착각을 일으키게된다. 필자는 vhidmini.h 파일에 있는 공식적인 마우스(예제) 디스크립터
주석을 풀어서 대체시켰다. 필자는 그 디스크립터를 믿지 못했다. 어딘가 오류가 있거나 한가지를 수정함으로
인해서 다른것까지 수정해야하는 문제점이 걸렸다거나 그런류로 생각할 수 밖에 없었다.
다음은 필자와 같은 문제점을 겪는 사람들에 대한 이야기다. 그다지 위안도 되지 않았고 결국 구글형님을 통해
문제의 정확한 해결점을 찾아낼 수 없었지만.. 그 과정에서 문득 떠오르는 영감을 주었기에 그 과정을 그려보고자
한다.
-----------------------------------------------------------------------------------------------------------------
thank for your advice.
i forget to assign REPORT_ID for each report desc.
It works now.
However, i migrated the corrected report to "hidfake". (Walter Oney 's sample)
The system pop up 3 "Found New Device Wizard" window and identfy it as "Unknow Device".
Any difference detween these 2 drivers' enumeration?
Appreciated.
(필자요약: REPORT_ID 를 빼먹었다. 작동된다. 그런데 hidfake 꺼를 배꼈다. 그랬더니 "알려지지않은 장치" 라는
새로운 장치로 "하드웨어 찾기" 가 세개나 뜬다. 나머지는 해석할 필요 없겠다.. 왜 그런가? 라는 질문
이라고 생각하고 넘어간다..)
-----------------------------------------------------------------------------------------------------------------
"Doron Holan [MS]" wrote:
you only need one HID minidriver. from it, a keyboard and a mouse can
be
enumerated. you just have to put each device into its own top level
collection. If you are having trouble, i would find a USB HID that already
does this and look at its HID descriptor
(필자요약: 이놈이 다른 게시판에도 있는걸보면 좀 하는거 같다. 대충 번역하면 너는 오직 한개의 HID 미니드라이버만
필요할 뿐이다. 그리고 키보드나 마우스가 열거될수 있는 것으로 부터, 그리고 각각의 그 자신의 탑레벨
모음속에 각장치들을 넣어야만 한다. 만약 문제가 있다면, 니가 만든 USB HID 를 어쩌구 저쩌구..
뭔가 HID Descriptor 와 연관이 있겠거니하고 그냥 넘어감..)
-----------------------------------------------------------------------------------------------------------------
I'm pretty sure you will need to break up hte device into a Mouse device and Keyboard device. (i.e two drivers,
one with a report descriptor for a keyboard and one for a mouse)
The inf files should not refer to HID\MyVirtualHidDevice - these id's need to be picked up from keyboard.inf or
msmouse.inf - idealy reporting the compatible id of HID_SYSTEM_KEYBOARD or HID_SYSTEM_MOUSE (i think)
(필자요약: 마우스와 키보드장치속에 hte 장치를 깨야할 필요가 있다라고 해석해야 하나.. 이놈이 주장하는 내용은
일단, report descriptor 에 키보드와 마우스가 하나로 일치되어있는가를 확인하라는 내용과 HID\MyVirtual
HidDevice 라는 장치명으로 되어있는 vhidmini.sys 샘플이 이름이 잘못되어서 그런게 아니냐는 속임수에
빠지기 쉬운 의견을 제시해 놓고 있다. 마치.. 네이버 지식인인가? 하지만 여기서 얻을 수 있는 점이
있는데 HID_SYSTEM_KEYBOARD, HID_SYSTEM_MOUSE 라는 지시어이다. 어설픈건 혼란을 가중시키는데 원래는
HID_DEVICE_SYSTEM_KEYBOARD 이고 HID_DEVICE_SYSTEM_MOUSE 가 맞는거니까 속지말자. 뒤에서 설명하겠지만
이걸 알아 듣기 위해서 새로운 개념을 습득하게 된다.)
-----------------------------------------------------------------------------------------------------------------
Hi,
I write a HID minidriver with standard Mouse and Keyboard report
descriptors.
It is based on vhidmini in Windows Server 2003 DDK.
The driver work fine and I can read/write the report from the
device.
The Device Manager shows it is HID-compliant device in HID class,
but
the
Mouse Class and Keyboard Class have none.
How should I do so that it can be a mouse device and keyboard
device?
Should I modify the INF file? My driver's INF is almost same as the
vhidmini's.
(필자요약: Windows Server 2003 DDK 로 vhidmini 를 만들었다는 식인데 장치명이 HID-compliant(일반 복합HID 장치)로
인식되는데 마우스 클래스와 키보드 클래스가 없다고 말한다. 어떻게 마우스와 키보드 장치로 인식시키는
것이냐고 물어본다. 자기가 INF 파일을 수정해야 하는지 물어본다. 그리고 INF 파일은 vhidmini 와 거의
같다고 말한다. 이 사람이 겪는 증상이 필자가 겪는 증상과 100% 일치한다. 그런데 불행히도 이 글에는
더이상의 답변이 달려있지 않았다. 눈물의 고배를 마시고 돌아서야 하는 이 저린마음.. T.T 어쩔수 없이
또다시 구글형님의 도움을 받아야한다.)
-----------------------------------------------------------------------------------------------------------------
이 질/답들에서 배운 것은 검증해봐야하는 대상들이다. 일단, HID Report Descriptor 가 잘못되었는지 검증해야하며
INF 파일이 잘못되었는지 검증해봐야하고 "한참을 헤메게 만든 요인이었지만" REPORT ID 에 대해서도 검증해봐야
한다는 몇가지 결론을 얻은채 새로운 검색활로를 모색해보게 된다. 이 검색은 첫 발을 내딘수준에 불과하다. 거의
48시간을 오로지 이 문제를 해결하기 위해서 정보들을 모아야 했다.
-----------------------------------------------------------------------------------------------------------------
Loading a driver on HID class
Hi all,
I have a USB device that exposes a HID interface. It is not a mouse or
a keyboard, just a general HID device. Device Manager displays it as a
"HID-compliant device".
Now I would like to install a device driver on it and use the HID
interface to communicate with.
My INF refers the HID\VID_xxxx&PID_xxxx string and my driver gets
loaded.
So I get the PDO from AddDevice and I need the FileObject to
communicate using IOCTL_HID_SET_FEATURE and IOCTL_HID_GET_FEATURE. But
I cannot retrieve it; I use the FireFly sample and the function
FireflyOpenStack to retrieve the FileObject form the PDO but it fails
in my driver with error 0xC000000E (STATUS_NO_SUCH_DEVICE) when
calling ZwOpenFile.
What's wrong? The pdoName of the file open by ZwOpenFile is something
like "\Device\00000096". Is it possible to get the SymbolicLinkName
instead?
If someone could help...
Thanks, Roger
(필자요약: HID 인터페이스를 노출하는 USB 장치를 만들었다. 그런데 그게 마우스나 키보드가 아니네? 단지
일반 HID 장치인거야. 디바이스 디스플레이에 보면 "HID-compliant device" 라고 표시되네.
횽님들.. 알려주십쇼.. 뭐 이런 내용식으로 글을 써놨다. 그래도 아는게 많은 사람이라서 그런지
정보들을 많이 뿌려놨는데 독이되는 요소들이 있지만, 덤으로 알게되는 요소들도 그 못지않게
많다. IOCTL_HID_SET_FEATURE 과 IOCTL_HID_GET_FEATURE 에대한 얘기는(추후 구현되어야 하는 내용)
좋은 정보임에 틀림없다. 그런데 우리가 원하는 답을 얻지는 못하고 단지 INF 파일에 VID 와
PID 스트링이 문제의 시발점이 될수도 있지는 않은가 하는 의심을 해볼수 있다. 물론, 그게 해답은
아니지만 추가정보를 검색해야할 키워드로써 대상물망에 넣어두자. VID 란 벤더아이디를 의미하고
제작사의 고유식별번호이며, PID 란 프로덕트 아이디 즉, 제품번호이겠다. 이게 하드웨어는 모두
고유하다고 하는데 과연 이 때문에 마우스나 키보드로 인식이 안되고 일반장치로 인식되는 것일까?)
-----------------------------------------------------------------------------------------------------------------
when are you trying to open a handle? during AddDevice or
IRP_MN_START_DEVICE? neither will work b/c a file create can only occur
once the start irp has come back to the pnp manager. so to make this work i
would
a) register a custom device interface GUID
b) register for device interface arrivals on your custom guid. when you
get called, open your stack like FireFly does
you will also need to register for handle notifications on the file handle
that you open so that you can gracefully disable the device. If you use the
KMDF firefly sample, the WDFIOTARGET object does this for you
(필자요약: 답변중에 하나인데 이 답변은 질문자의 추측보다 더 가관이다. IRP_NM_START_DEVICE 나 AddDevice 등록시
(필자는 커널을 몰라서 몬소린지 모르지만 이건 아니다정도의 감은 있었다.. -_-;) 하라는 식으로
답변이 달려있는데 KMDF 까지 들먹거리는걸로 봐서는 논점에서 많이 빗나갔다. KMDF 는 새로운 드라이버
개발 프레임워크인데 질문자가 그걸 물어본게 아니다. 기존의 방법으로 설명해줘야하는 것이 옳은데
그렇지도 않았고, 너무 많은 작업을 추가하라고 주문하는거보니 필자의 생각과 달랐다. 필자의 감은
Report Descriptor 만으로도 해결될 문제라고 속삭이고 있었기 때문에 이 답은 해결책이 아니었다. 다른
답변중에는 URB 를 보내라는 말도 있었다. 모두 무시한다.)
-----------------------------------------------------------------------------------------------------------------
지금 거론하는 필자의 문제해결 방법은 링크를 보여주면서 찾아나가는 방법을 설명하고 있다. 그렇기에 링크역시
필자가 검색해서 누른 순서대로 임을 밝힌다. 다음으로 찾은 것은 MSDN 의 설명이다. 그런데 MSDN 의 설명은 항상
깨달음을 얻은뒤에나 값진 보물이 되지 깨달음을 얻기전까지는 그저 잘 만들어진 명세서에 불과하다고 느낄때가
많다. 마치 선생님이 "공부하라"고 그렇게 들볶던 말들이 성인이 된 뒤에 "정답" 이라고 느끼는 것처럼 느낀뒤에만
알 수 있는 것들이 기록되어 있는게 MSDN 과도 같다. 왜 그땐 몰랐지? 왜 그땐 안봤지? 나중에 이런말 자주하게
될거다. ㅎㅎ 그런데.. 역시.. -_-; 이 링크를 보던 시점(현 글을 쓰는 시점기준으로 하루전)만 해도 이 링크는
그저 도움이 안되었다.
(필자요약: 설명이 잘 되있다. 읽어보라.. 해결책도 들어있다. 그런데 그냥보면 절대 모른다. 개고생하면 그때는
답이 보이지만 그냥보면 모른다.)
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
(필자요약: 이 사이트는 중국사이트인데 읽을수가 없다. 몇가지 좋은 키워드와 코드가 있는데 해결책은 아니고
나중에 마우스나 키보드를 구현할때 참고할 만한 코드가 아주 조금 있을 뿐이었다.)
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
(필자요약: 필자가 겪는 문제를 어느정도 일부분은 해결한 것 같기도하고 그렇지 않은거 같기도 한데 희한한
삽질을 하고 있었다. Descriptor 를 계속해서 바꿔가면서 테스트하는 것을 물어보고 있었다.
마우스와 키보드를 인식시키기 위해서 vhidmini 를 기본베이스로 디스크립터를 조작하는데 설치가
안된다는 그런 질문이었다. 질문양이 많아서 짤라붙이기는 못하겠다. 답글을 보자.)
Adrian Schlesinger
Join Date: 24 Sep 2008
Posts To This List: 7
RE: Simulate keystrokes
I have detected what was wrong:
1. When adding the report ID item to the keyboard top-level collection, an additional byte should be returned to
the system at the beginning of the data, specifying that report ID.
2. Communication from user mode did not work because when cycling through the HID devices,
in addition to vendor ID, product ID and version, the usage specified for the additional end point should also
be matched (with Vendor Usage 1), otherwise you can end up trying to call WriteFile for a handle corresponding
to the keyboard end point.
(필자요약: 질문자의 답글인데 키보드 top-level collection(최상위 모음) 에 REPORT ID 를 추가할때 어쩌구 저쩌구
얘기가 나온다. 그리고 usage 라는 말도 나온다. 필자가 원하는 답이 여기에 있을 것이라고 생각하여
엄청난 검색을 통해 알게되었는데 원하는 답은 아니었다. 단지, top-level 의 개념은 중요했다.
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
(필자요약: 앞에서 어떤사람이 한 질문과 똑같은 질문인듯 싶다. 왜 도대체 마우스로 인식이 안되냐.. 이 질문이다.)
-----------------------------------------------------------------------------------------------------------------
다음은 중요한 개념중에 하나인 Top-Level Collection 이다.
-----------------------------------------------------------------------------------------------------------------
Special Top-Level Collections (Reserved for OS use)
Certain HID top-level collections generate a special HID device string. In Windows 2000, Windows XP, and Windows
Server 2003, the top-level collections listed in Table 6 are special cased and each has an additional hardware ID.
Table 6 identifies these collections. The last column identifies the additional string that is added to the hardware
ID list.
Table 6: Special-Cased Top-Level Collections
Device Type Usage Page Usage ID Additional Hardware ID
Pointer 0x01 0x01 HID_DEVICE_SYSTEM_MOUSE exclusive
Mouse 0x01 0x02 HID_DEVICE_SYSTEM_MOUSE exclusive
Joystick 0x01 0x04 HID_DEVICE_SYSTEM_GAME
Game pad 0x01 0x05 HID_DEVICE_SYSTEM_GAME
Keyboard 0x01 0x06 HID_DEVICE_SYSTEM_KEYBOARD exclusive
Keypad 0x01 0x07 HID_DEVICE_SYSTEM_KEYBOARD exclusive
System Control 0x01 0x80 HID_DEVICE_SYSTEM_CONTROL
Consumer Audio Control 0x0C 0x01 HID_DEVICE_SYSTEM_CONSUMER
(필자요약: 이게 모냐면 Top-Level Collection 이라 불리우는 입력장치유형이다. 위에서 Usage Page 와 Usage ID 라는
것이 있는데 이게 바로 Report Descriptor 에 있는 항목에 적혀있다. 이걸 어떻게 바꾸느냐에 따라서
가상장치가 마우스가 되느냐, 키보드가 되느냐 아니면 조이스틱이 되느냐를 결정한다. 멋지지 않은가?
물론, 원하는 해답은 아니다. 그러나 필수적으로 개념을 갖고가야 한다. 여기서 중요한게 있다면 입력
장치의 형태가 공유(share)모델이냐 아니면 베타적모델(독점)이냐 이다. 마우스와 키보드는 독점모델
이다. 즉, 마우스와 키보드는 장치가 열려있으면 다른 프로그램이 장치를 열어서 쓰고읽는 것이 불가능
하도록 secure 모델로 처리되어있단다. 이때, 이걸 피해가려면 새로운 장치를 동일하게 하나더 만들어서
그 장치와 통신하면 이런 독점모델을 피해갈 수 있단다. MSDN 에 다 나와있다.. 전부 다~
링크가 있었는데 FireFox 가 깨져서 다 날아가 버리는 바람에 찾을수 없지만 베타적오픈과 공유오픈이
가능한 표시가 위의 Top-Level Collection 에 일일이 나열되어 있는 정보도 찾을 수 있었다. 어쨌거나
중요한건 짚고 넘어가자.. 참고로 잊지는 않았겠지? 가상장치가 일반장치로 인식되서 마우스 장치로
인식시키려고 뻘짓하다가 이런곳까지 당도하게 된거란 점.. 이렇게 정보들을 다양한 방법으로 얻어서
공부하는데도 웹서핑한다고 눈치주는 경우도 있다. x같은 경우지.. 이게 단순히 노는걸로 보여? 입에서
욕나오네.. 갑자기 머리에 히터가 작동되서 한번 지껄여봤습니다. 다시 집중모드로 돌아가봅시다.. ㅋㅋ)
Table 13-1 HIDCLASS-Compatible ID for Each Supported Usage
지원되는 HIDCLASS 호환 아이디.. HIDCLASS 는 각 장치로 분배를 해주는 역할을 한다고 합니다.
더 자세한건 묻지마삼 다칩니다요.. 전 아는것만 얘기할 뿐임..
Usage Page Usage Compatible ID
Generic desktop Pointer or mouse HID_DEVICE_SYSTEM_MOUSE
Keyboard or keypad HID_DEVICE_SYSTEM_KEYBOARD
Joystick or game pad HID_DEVICE_SYSTEM_GAME
System control HID_DEVICE_SYSTEM_CONTROL
Consumer (Any) HID_DEVICE_SYSTEM_CONSUMER
앞서서 XBCD 라고 XBOX 조이스틱 에뮬레이션 드라이버에 대해서 말한적이 있습니다.
헤더의 USAGE_PAGE 를 잘 봅시다. 그리고 USAGE 를 봅니다.
// XBCD 예
char ReportDescriptor[213] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop) <-- 요건 뭐시냐? Table 13-1 입니다욤..
0x09, 0x04, // USAGE (Joystick) <-- 위의 Table 6 에 Joystick 의 Usage ID 보입니껑?
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x09, // USAGE_PAGE (Button)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
// vhidmini.h 의 맨마지막 주석이 되어있었던 마우스예제 헤더
HID_REPORT_DEscRIPTOR DefaultReportDescriptor[] = {
0x05, 0x01, //Usage Page (Generic Desktop), <-- Table 13-1 일반 데스크탑
0x09, 0x02, //Usage (Mouse), <-- Mouse 임
0xA1, 0x01, //Collection (Application),
0x85, 0x01, //REPORT_ID (1) <-- REPORT_ID 꼭 이게 값이 있는지 확인해야함.
0x09, 0x01, //Usage (Pointer), HID Tool 이라는 놈은 이 값을 빼고 보여줌.(주의!)
0xA1, 0x00, //Collection (Physical),
0x05, 0x09, //Usage Page (Buttons),
0x19, 0x01, //Usage Minimum (01),
0x29, 0x03, //Usage Maximun (03),
0x15, 0x00, //Logical Minimum (0),
여기서 Top-Level collection 이 지칭하는 의미는 상위장치입니다. 이런식으로 다중 인터페이스를 구현할 수 있는데
예를들면 마우스와 키보드를 가상장치 하나로 일타쌍피도 만들어낼 수 있습니다. 시중에 파는 키보드 중에 마우스도
있고 USB 포트도 달려있는 키보드들은 이런 다중인터페이스를 만들기위해서는 multiple top-level collection 지정을
해줘야 한다는 얘기죠. 어쨌거나.. 이러한 정보들은 얻었고 Report Descriptor 가 잘못된 것인가라고 판단해보니
전혀 아니었습니다. 오히려 첫번째 XBCD 의 헤더모습에서는 REPORT ID 가 보이지 않는데 두번째 vhidmini.h 에서는
REPORT ID 항목도 빼먹지않고 넣었고 완벽합니다. 그런데 왜.. 대체 왜!! 인식이 마우스로 되지않고 일반장치라고
잡히는 거냐 이말이죠.. 심지어는 공식사이트에서 다운로드 받은 마우스와 키보드 Report Descriptor 를 가져다가도
해봤고 직접 하드웨어에서 뽑아낸 값을 통해서도 해봤지만 모두 인식이 안되었답니다.
-----------------------------------------------------------------------------------------------------------------
사실.. 위에서 언급한 내용은 탐색과정에서 추가로 얻어내는 개념들에 대해서 비중이 있었기에 설명을 하였습니다.
아마, 계속해서 같은 방식으로 설명하면 이 문서를 보면서 쌍욕을하게 될지도 모르기에 후다닥 빨리 접겠습니다.
다음은 제가 문제해결(일반장치를 마우스로 인식하게 만드는)을 하기 위해서 찾아다녔던 링크입니다. 더 많지만
추려서 올려봅니다.
(결정적인 영감을 주게된 링크는 바로 다음의 링크이고 사실, 앞에서 한번 언급했었던 링크입니다.. 정확히 말하자면
해결방법을 직설적으로 말해준게 아니라, 넌 이걸 이해해야된다라고만 코드의 일부분을 언급하고 숫가락으로 떠먹여
주지는 않겠다는 인상을 풍기는 답글이지요.. 처음에는 이걸 보고도 잘 이해를 못합니다. 저만 그런건 아닐겁니다.
어떤 질문자 중에서는 프린터, 스캐너, 스마트카드를 비롯해 각종 디바이스 드라이버를 만들었다는 이력을 밝히며
저와 같은 증상때문에 고민을 하고 있는 사람도 있었으니까요.. 제가 볼때 이건 MS 의 함정입니다. 의도하지 않은
함정말입니다.)
-----------------------------------------------------------------------------------------------------------------
바로 이 답글이 눈을 뜬 사람에게만 통하는 핵심인 셈입니다.
allen zhang
Join Date: 22 Jul 2008
Posts To This List: 38
RE: VHidMini report descriptor(s)
see the follow source code and the ReadDescriptorFromRegistry function, You should be understand it.
deviceInfo->HidDescriptor = DefaultHidDescriptor;
queryStatus = CheckRegistryForDescriptor(DeviceObject);
if(NT_SUCCESS(queryStatus)) {
queryStatus = ReadDescriptorFromRegistry(DeviceObject);
.......
(필자요약: 다음의 소스코드와 ReadDescriptorFromRegistry 함수를 봐라.. 너는 이걸 이해해야할 필요가 있다.
라고 말하며 일부 소스를 첨부했습니다. 그리고는 아무런 추가의 말도 없습니다. 장난하는것도
아니고.. -_-; 이때 아차하고 생각을 떠올리면 이 문제는 해결됩니다. 위에서 숱하게 질문하던
외국 개발자들도 결국에는 알아냈겠지요?)
-----------------------------------------------------------------------------------------------------------------
vhidmini 드라이버를 구동시키고 일반 HID 장치로 인식된 드라이버를 마우스 장치로 둔갑시키기 위해서는
소스를 수정해야 합니다. 바로 vhidmini.c 드라이버 소스를 수정하는 일입니다. 그것도 필자가 생각했었던
Report Descriptor 의 오류를 수정하는 것이 아니라 코드를 수정해야 합니다. 앞에서도 한번 언급했었지만
Microsoft 사의 공식 DDK 샘플소스에 오류가 있을리는 없으니까요. vhidmini.h 에 주석으로 정의되어있던
마우스 Report Descriptor 의 예는 오류가 없었습니다. 오류에 빠지도록 만든것은 바로 ReadDescriptorFromRegistry
함수에 있었습니다. vhidmini 소스를 보면 주석에 써있기를 우리는 하드코딩을 하였다라고 적혀 있습니다.
Report Descriptor 를 하드코딩 하였다는 것이지요. 그런데, 단순히 그냥 하드코딩으로 놔두지.. 더 많은걸
보여주고 싶었던지 if-else 로 두가지 처리루틴으로 나눠놓은것이 문제의 핵심이었던 것입니다. 다음의
소스를 보십시오.
-----------------------------------------------------------------------------------------------------------------
[vhidmini.c 소스에서 수정해야 할 부분]
HID Report Descriptor 를 레지스트리에서 읽어들이는 코드를
하드코딩쪽으만 처리하도록 수정함.
deviceInfo->HidDescriptor = DefaultHidDescriptor;
//
// Check to see if we need to read the Report Descriptor from
// registry. If the "ReadFromRegistry" flag in the registry is set
// then we will read the descriptor from registry using routine
// ReadDescriptorFromRegistry(). Otherwise, we will use the
// hard-coded default report descriptor.
//
queryStatus = CheckRegistryForDescriptor(DeviceObject); // 함정이 발생하는 지역
if(NT_SUCCESS(queryStatus)){
//
// We need to read read descriptor from registry
//
// 장치를 설치할때 INF 파일에 있는 Report Descriptor 가 레지스트리에 기록되고
// 이 부분에서 레지스트리에 기록된 Report Descriptor 를 읽어들임.
// 그러므로 백날 헤더의 Report Descriptor 를 바꿔봤자 INF 파일에 있었던 것을
// 읽어들이는 꼴이 되므로 항상 "일반 HID 장치"(Generic HID Device) 가 드라이버로
// 등록될 수 밖에 없었던 것이다. 이런 제길.. 이 문제로 이틀을 꼬박 다 날렸다니..
// Microsoft 는 반성하라~!! Microsoft 는 함정을 제거하라~!!
// 고로 이 부분으로 빠져들지 않도록 주석처리 하라..
queryStatus = ReadDescriptorFromRegistry(DeviceObject);
if(!NT_SUCCESS(queryStatus)){
DebugPrint(("Failed to read descriptor from registry\n"));
ntStatus = STATUS_UNSUCCESSFUL;
}
}
else{
//
// We will use hard-coded report descriptor.
//
deviceInfo->ReportDescriptor = DefaultReportDescriptor;
DebugPrint(("Using Hard-coded Report descriptor\n"));
}
... 중략 ...
case IRP_MN_REMOVE_DEVICE:
//
// free memory if allocated for report descriptor
//
// 이 부분도 주석처리해야 한다. 왜냐면 레지스트리에서부터 읽어들이지 않았기 때문에
// 메모리 할당이 안되어 있기 때문이다. 그냥 두어도 상관없을거 같지만 메모리 해제부분이니
// 꺼림칙하므로 되도록이면 제거하길 바란다.
if(deviceInfo->ReadReportDescFromRegistry)
ExFreePool(deviceInfo->ReportDescriptor);
SET_NEW_PNP_STATE(deviceInfo, Deleted);
ntStatus = STATUS_SUCCESS;
break;
-----------------------------------------------------------------------------------------------------------------
이제 vhidmini 샘플을 컴파일하고 VMware 에 vhidmini.sys 와 vhidmini.inf 파일을 복사하자. 그리고 devcon 명령으로
앞서했던 것처럼 install 을 해보자. 설치를 하고나면 장치관리자의 기존에 있던 "HID 준수장치" 라는 것은 없어지고
마우스항목에 "HID 규격 마우스" 가 추가되는 것을 볼 수 있을 것이다.(ㅎㅎ 이맛을 보려고.. 밤을새고 삽질을..)
이제.. 장치관리자에서 새로 추가된 "HID 규격 마우스" 를 선택한 뒤에 속성을 보자. 속성을 선택한 뒤 "자세히"
라는 것을 클릭한다. 그리고 장치인스턴스 ID 라는 것을 보라. HID\MyVirtualHidDevice&Col01 라고 되어있는 것을
볼 수 있다. 또한, 일치하는 장치 ID 를 선택하면 hid_device_system_mouse 라고 되어있다. 즉, 마우스로 인식이
되었다는 것이다. 가상 HID 디바이스 위에서 마우스 장치가 인식되어 있는 것이다. 이제 가상 HID 디바이스와
어플리케이션 간에 통신루틴을 프로그래밍하고 어플리케이션의 명령에따라 상위 클래스로 IRP/URB 같은 요청을
(이거참.. 이 레이어에서는 어떤 요청을 사용해야하는 건지 또 공부해야하는군.. -_-; USB 강의 문서에 보면 상위
클래스는 URB 통신을 한다고 나와있었다..)
날려주면 키보드나 마우스 같은 장치들을 에뮬레이션 시킬수 있다. 곧, 가상장치를 조종할 수 있다.
하지만? 그리 쉽지는 않을 것이다. vhidmini 샘플에는 testvhid 라는 어플리케이션 소스도 같이 들어있는데 이
testvhid 소스는 어플리케이션단에서 vhid miniport 드라이버와 통신하는 예제이다. 참고로 아무런 수정없이 샘플만
컴파일해도 어플리케이션과 드라이버 사이에 제대로 통신이 이루어지지않는다. 역시나 이 문제는 HID Report
Descriptor 의 데이타의 구성문제에 속한다. 한번 고생한 것이 또다시 반복되는 시점이기도 하다. 이 부분을 해결하면
어플리케이션은 2 또는 3 가지의 top-level collection 중에서 사용자정의(User-Defined) 콜렉션을 통해서
miniport 드라이버와 통신을 할 수 있게되고 이 통신에 따라서 마우스 IRP 를 상위 클래스로 때려주면 된다.
더 자세한 내용은 스스로 연구하길 바란다. 이상으로 이번문서를 마무리하려고 한다. 왜냐면 이제 테스트 기반이
마련되었으니 나머지 추가구현은 스스로가 해야할 몫이기 때문이다. 어떠한 목적으로 개발하던간에 그 이상은 이제
필자가 관여할 부분이 아닌것 같다. 어차피 이 문서의 목적은 가상 마우스를 제작하는 것을 중점으로 삶는 것보다도
모르는 분야를 독학하며 개척해 나갈때 자신의 공부스타일의 한 예로써 제시하는 것이었다. 여기서 거론된 공부스타일을
종합해 본다면 이렇게 말할 수 있을것 같다. "관례를 찾아냄으로써 DIFF 를 추출해내는 공부법" 이라고 할 수 있다.
즉, 최대한 많은 자료들을 검색으로 긁어모은 뒤에 분리분석 작업을 통하여 동일한 부분의 반복을 찾아낸다.
찾아낸 반복내용이 있다면 결국 그 반복은 해당 프로그래밍의 관례에 속한다. 즉, 해당 관례는 몸으로 빨아들이고 차이가
나는 지점에서 기술을 흡수한다. 이런식의 반복학습을 통하여 더이상 다른 코드나 정보들을 찾아낼 수 없다고 판단이
들때 그 기술은 자기것이 된다. 필자는 유저레벨을 공부할때 항상 이방식을 택해왔다. 정보가 고갈되고 더이상 찾아낼수
없을때까지 긴시간을 할애해서 리서치를 먼저 한다. 그 뒤에 코딩으로 들어간다. 그리고 관례코드를 따른다. 그렇게되면
생전 처음보는 프로그래밍에서도 어느지점은 건드려도 되고 어느지점은 절대 건드려서는 안되는지 쉽게(? 정확히가 맞겠다)
익힐수 있다. 이제는 커널레벨 공부를시작하면서 이 고통스럽고도 지루한 공부방식을 다시 사용하고자하며 다른 사람에게
조그마한 도움이 되고자 이런 문서를 작성하였다. (같이 게임에 미쳐서 광랩하던 친구가 갑자기 URL 을 뿌려줘서 읽어
보았더니 국가에서 인증한 기술이라는 자랑스런 인증서와 함께 뭔가를 팔고있었다. 보란듯이 대놓고 말이다. 디자인도
쌈빡해서 사고싶게 만드는 그것.. 그게 뭔지는 대충 말안해도 알겠지만.. 이 문서를 빨리 공개해야하겠다는 생각이
들었다. 원래는 문서를 다 작성해놓고도 공개하지않는 방향으로 생각을 굳혔다가 아무래도 생각이 바뀌기 시작하였다.
그 이유는 뒷부분의 부록을 보라..)
아마 이 방식으로 공부하는 사람들이 많겠지만 그렇지 않을수도 있을 것이다. 적어도 이렇게 공부할땐 뼈를 깎듯이
힘들지만 원하고자 하는 지식을 얻으면 그 지식의 깊이가 뼈속으로 스며들 것이다. 필자처럼 어떤 새로운 분야에
공부를 시작하거나 공부하는 방법을 몰라서 물어보려는 사람들이 있다면 이 문서를 읽어보라고 말하고 싶다.
<잡설>
필자가 만약에 커널디버깅을 잘 다뤘다면 아마도 쉽게 문제를 해결했을지도 모르겠다. 하지만 필자는 귀차니즘
때문에 커널디버깅을 좋아하지 않았고 결국, 공부도하지 않았다. (요즘들어 먹고 살라니.. 공부중이다.. -_-;)
그런데 필자는 오히려 디버거를 쓰지 않으면 않을수록 프로그램에 대한 이해도는 높아진다고 생각한다. 왜냐면
사람의 머리는 충분히 상황을 시뮬레이션 할 수 있다고 생각하기 때문이다. 에뮬레이션은 불가능하겠지만
시뮬레이션은 가능하다. 그리고 이 작업을 통해서 문제가 발생되는 지점을 캣치할 수 있다고 생각한다. 그렇게
되었을때 남보다 더 느릴지 몰라도 이해도는 훨씬더 깊이있는 굴곡을 생성해낸다고 생각한다. 어쩌면 그렇게
믿고싶은 것일지도 모르겠다. 적어도 자신은 그렇게 소신을 갖고있다. 그렇다고 커널디버깅을 배우는 것을 말리거나
도외시하라는 것은 절대 아니다. 필수적으로 갖고가야할 기술이라는 점에는 변함이 없지만 두가지를 모두 안배하는
것이 스스로에게 좀 더 풍요로운 지식을 가져다 줄 것이라는 점을 인지했으면 좋겠다는 의미이다.
"Art of Hooking" 이라는 문서처럼 스스로의 깨달음을 전달하려고 쓴 문서는 아니지만 이 시간에도 같은 문제로
삽질하고있을 그들에게(아직도 정확한 답변을 보지 못한 질문자들이 인터넷에 깔려있음을.. 이미 보여줬다..)
이틀이라는 시간을 좀 더 귀중한 곳에 쓸 수 있도록 해줄 수 있다는 것은 행복한 일이라고 생각한다. 만약
누군가에게 이런 도움을 지속적으로 받을수 있었다면 본인은 솔로여야할 이유가 없었을 것이리라.. (아쉽게도
영어를 못하니 국내만이라도 도움을 받으면 좋을듯 싶다.)
참고로.. 이 문서는 주제를 인지하는 시점부터 해결과정을 도출해내는 모든 전 과정이 거의 실시간으로 쓰여졌다..
그렇게 해야만이 이 문서의 목적인 "가상마우스" 와 "공부방법론" 두가지를 모두 전달할 수 있다고 판단했기
때문이다. 필자는 누군가 이 문서를 읽고 얻은게 있었다면 그것만으로 행복할 것이다. 하드에 처박혀 쓰레기가
된채 어느날 자신도 모르게 삭제되는 그런 정보가 아니라는 점만으로도 충분히 가치있는 것이니까..
[부록1: 필자가 생각하는 에뮬레이션과 시뮬레이션의 차이점]
에뮬레이션: 기계의 톱니바뀌처럼 구성요소들의 기능자체들이 모두 동일하게 작동해야 한다. (기능/작동의 동일화)
시뮬레이션: 기능자체를 동일하게 할 필요없이 입/출력되는 수치/값만 동일하면 된다. (수치/데이타의 동일화)
(필자주: 틀렸을 수도 있다. 정확하지않다. 다만, 여태까지 몸으로 와닿는 느낌자체를 말로 풀어써본 것 뿐이다.)
[부록2: 시중에 떠도는 물리형 Auto Mouse 탐지법]
먼저, 가상장치에 대한 것부터 언급해보고 물리형으로 넘어가겠다.
<1. 가상장치 탐지방법>
재밌는 사실이 있는데 아는 사람은 알 것이고 모르는 사람은 모를 것이다.
MSPRESS 에 보면 짜가장치(FakeDevice) 탐색법이 있다.
-------------------------------------------------------------------------------------
HANDLE CtestDlg::FindFakeDevice()
{
GUID hidguid;
HidD_GetHidGuid(&hidguid);
CDeviceList devlist(hidguid);
int ndevices = devlist.Initialize();
for(int i = 0; i < ndevices; ++i)
{
HANDLE h = CreateFile(devlist.m_list[i].m_linkname, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE)
continue;
HIDD_ATTRIBUTES attr = {sizeof(HIDD_ATTRIBUTES)};
BOOLEAN okay = HidD_GetAttributes(h, &attr);
CloseHandle(h);
if(!okay)
continue;
if(attr.VendorID != HIDFAKE_VID ||
attr.ProductID != HIDFAKE_PID)
continue;
return CreateFile(devlist.m_list[i].m_linkname,
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
}
return INVALID_HANDLE_VALUE;
}
-----------------------------------------------------------------------------------------
위의 짜가장치 탐색법은 골때리게 단순하다. 결국에는 HIDFAKE_VID 와 HIDFAKE_PID 를
체크하는 개념이다. 즉, 제작사(Vendor ID)와 제품번호(Product ID)를 가지고 짜가인지
판별하겠다는 개념이다. 그렇다면 제작사와 제품번호는 속일수 없다고 생각하는가?
커널을 뒤짚어 엎는(Subvert) 판국에 저렇게 단순하게 비교해서는 가상장치를 막을수
없다. 위의코드를 보고 판단을 하길 가상장치는 다 막겠다고 판단한다면 정말 큰일이다.
어처구니 없는 싸움이 키보드보안이래 또 발생할 것이기 때문이다. 위의 코드가 의미하는
바를 잘못해석하면 안된다. 위의 코드는 이렇게 해석해야 한다. 그 어떠한 가상장치를
막을수 있다는 컨셉하에 위의 코드를 만들길 시도한것이 아니라 고정장치를 알아내기위해
만들어진 컨셉이라는 점을 말이다. 단지 정형화 되어있는 즉, 고정되어있는 상태에서는
가능할 수 있겠다. 이해를 돕기위해 예를한가지 들어보자.
VMware 같은 가상머신 안에서 작동하는 것인지 정도는 파악할 수 있지 않을까 싶다.
왜냐면 VMware 가 동적으로 제작사와 제품번호를 바꿔야할 까닭이 없지않은가? 다르게 말하면
VMware 가 굳이 자기 제품정보를 굳이 속이거나 변덕스럽게 수시로 바꿔야할 필요가 전혀
없다는 얘기다. 이런경우에는 마치 하드웨어처럼 고정장치로 봐도 무방하다. 고로 VMware 에는
항상 고정된 장치가 있다고 판단해도 될 것이며 VMware 안에 구현된 장치들 중에는 가상장치들이
있으므로 항상 디텍트 할 수 있단 얘기가 되므로 위의 짜가장치 탐색법은 그러한 경우에
사용할 수 있는 컨셉이라는 소리다.
아직 더 깊이 연구해보지는 않았지만 가상장치와 전쟁을 선포할 경우에 또 하나의 "키보드보안"
같은 파국으로 치닫기에 좋은 스토리가 탄생할 것이다. 차라리 이것을 좀 더 유용한 곳에
사용해보면 어떨까? 바로!! 물리형 Auto Mouse 를 탐지하는 것이다. 기술은 적합한 곳에 쓰라고
있는 것이지 말도안되는 헛다리 싸움에 쓰라고 존재하지 않는다. 기술을 적용시킬때는 단순히
눈과 머리로만 판단하지말고 포괄적인 정황을 바탕으로 자신의 가슴속에 확신이 설때 비로소
본격적인 전쟁에 들어가야 한다. 그저 돈이된다고 키보드보안처럼 너도나도 발담궈놓고 아무도
책임지지않고 아무도 발도 못빼는 승자없는 싸움에 휘말리기 싫다면 필히 이 말을 웃어넘기지
말아야 할 것이다.(아마, 이젠 더이상 발을 빼지도 못할것이다.. 담구지 말라고 뜯어말려도
담궜으니 해야할 일은 그저 땜빵밖에 뭐가 더 있겠는가.. ㅉㅉ)
-----------------------------------------------------------------------------------------
<2. 물리형 Auto Mouse 탐지>
물리형 Auto Mouse 를 탐지하는 것의 기본바탕은 앞서 짜가장치 탐색에 사용된 코드를
그대로 채용하면 되겠다. 단, 제작사와 제품번호만이 아닌 추가정보를 이용할 필요가
있겠다. 그런데, 왜 효용성이 물리형 Auto Mouse 를 탐지하는 것에 효력이 있을까?
그 이유는 간단하다. 다음과 같은 정보를 보자. 필자는 최근에 오픈된 MMORPG 온라인
게임의 Auto Mouse 를 구매해보았다. 과연 어떻게 돌아가는지 궁금증도 있었고 진짜
완벽하게 구동이 되는것인지 두눈으로 확인해보고 싶었기 때문이었다. 그런데 일단,
한군데의 제품은 구동이 제대로 안되는 것을 확인하였다. 다만, 마우스 입력이나
키보드 입력은 그대로 게임속으로 전달되고 있다는 점에 주목하였다. 과연 그러한
것을 어떻게 탐지해낼 수 있을까? 다음은 USB View 라는 프로그램으로 Auto Mouse
장치의 Descriptor 를 본 화면이다.
"USB Composite 장치"
-----------------------------------------------
Device Descriptor:
bcdUSB: 0x0200
bDeviceClass: 0x00
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x20 (32)
idVendor: 0x03EB (Atmel Corporation)
idProduct: 0x4743
bcdDevice: 0x1000
iManufacturer: 0x01
0x0409: "ATPLAY"
iProduct: 0x02
0x0409: "ATPLAY PRO8"
iSerialNumber: 0x03
0x0409: "1.0.0"
bNumConfigurations: 0x01
... 생략 ...
-----------------------------------------------
그렇다. idVendor 가 제작사이고 idProduct 가 제품번호이겠다. 이 정보는 필자가
예상컨대 고정이라 할 수 있다. 일단, 텍스트를 주의깊게 보자. "Atmel Corporation" 이
보이는가? 해커들이 가장 많이 사용한다는 그 유명한 "Atmel" 칩이 사용되었다.
일명 FPGA 칩이라고 불리운다. 이 FPGA 칩은 프로그래밍 가능한 이점이 있다.
(사견: 언제부터 마우스가 Atmel 칩으로 제작되었던가? 그래서 오토마우스가 그리도
비싼거였나보다.. 싸구려 PIC 칩만으로도 마우스를 만들수 있다. 전자업계는
원래 원가전쟁을 벌이는 계통이라서 단 1원이라도 원가를 절감해 보려고
깎고깎고 전쟁을 벌인다. 필자의 친구가 전자회사 CEO 라서 이점은 매우 잘
알고있다. 그 친구의 말을 빌어보자면 진짜 1원갖고 쪼잔하게 굴 수 밖에
없다고 한다. 살아남아야 하니까 말이다.. 그런데 FPGA 칩을 쓰는 것을 보면
좀 수상하다. 왜 재프로그래밍이 가능한 칩을 쓰는 것인가.. 재수없으면 기타장치
메모리스틱 리더기같은 것들이 연결되어 있을수도 있으므로 함부로 판단하기는
이르지만 아마도 대량유통되는 칩이 아닌 DIY(사제품)칩을 쓴다는 것은 기정
사실이라고 추측할만 하지않은가? 필자가 대에충 찾아보니 Auto Mouse 제작사
두군데가 Atmel 칩을 사용하고 있었다.)
만약 이 Atmel 칩이 필자가 아는 것과 달리 재프로그래밍이 가능한 기능이 없다면
게임업계에서는 대박의 찬스를 잡은 것이리라.. 왜? 고정이잖은가? 고정!!
또, 재미난 정보를 볼 수 있는데 바로 그 유명하다고 하는 "ATPLAY" 라는 마크가
찍혀있다. 아주 그냥 상호를 갖다가 박아놓은거보니 장사할 생가이 없다하겠다.
일단, 칩이 Atmel 칩으로 제작되어있는 USB 형 물리장치들을 사용하는 사용자는 모두
감시대상순위로 집어넣어도 십중팔구는 맞아떨어질 것이다. 특히나 Atmel 칩에 상관없이
상호명 ATPLAY 가 박혀있다면 그 사용자는 거의 Auto Mouse 를 사용하고 있을 확률이
99.9% 라고 할 수 있겠다. 그리고 USB View 같은 프로그램이 사용하는 루틴들은 위에서
언급된 루틴들에서 크게 벗어나지는 않을테니 쉽게탐지할 수 있다고 감히 추측해본다.
딱한가지 우려되는 사항이 있다면 프로그래밍 가능한 칩이기 때문에 업데이트 기능으로
펌웨어를 갈아치울 경우에 난감하게 될 것이다. 하지만 방금 언급한 방식을 사용한다면
Auto Mouse 제작사의 미래고객이 아닌 현재 고객들은 99.9% 가 탐지된다고 봐도 무방할
것이다. 물론, Auto Mouse 의 종류별로 이짓을 해야겠지만.. 프로그래머들의 습성상
모두 식별자를 기록해놓았을테니(아마, Atmel 칩에 코딩을 의뢰하거나 혹은 Atmel 칩에
프로그램이 가능한 사람을 영입했겠지만 프로그래머들이 그런거 생각안한다. 왜냐면
원리원칙을 따르려는 프로그래머의 심리상 바보같이 위에처럼 ATPLAY 를 박는게 미덕으로
생각할 것이기 때문이다.)
행동만 빨리 취한다면 현재 Auto Mouse 사용자들의 태반은 모두 다 탐지해낼 수 있을
것이라고 감히 판단해본다. 즉, 물리형은 고정이라는 변수를 잘 활용할 수 있기에 탐지도
비교적 수월한 편에 속한다는 것이다. 한가지 더 재미난 상상을 해보자.. 과연 Auto Mouse
업체에서 펌웨어 업데이트 기능을 추가로 만들어내는데 얼마나 시간이 걸릴까?
누가 더 대응이 빠를까..?
과연 이 전쟁에서 Auto Mouse 업체의 대응이 빠를까? 아니면 막는자의 대응이 더 빠를까?
아마도 덩치가 작은쪽이 더 빠르겠지만 두고볼 일일 것이다. 이 전쟁은 어느한쪽의
시간전쟁일 뿐이다.. 정확하고 빠른판단이 내려지면 그대로 바로 행하는 쪽이 이기는 것이다.
현재로써 본 필자의 생각으로는 물리형 Auto Mouse 는 막을 수 있다. 이 문서가 나온 시점
앞으로가 문제이다.. 점차 Auto Mouse 는 가상마우스로 진화를 꿈꾸고 있기 때문이다.
그것이 바로 기술의 대세인 것이다. 오늘의 기술이 내일의 쓰레기가 되어버리는 시대에서
속도전의 중요함이다. 승자는 과연 누가 될까? 아무도 모른다.. 기술은 끝이 없으니까..
마이 골치아픈 것이다.. -_-;
어디까지나.. 필자 개인이 현 상태를 진단해본 사견일 뿐이다. 이 이상 어케 더 판단하랴..
[HID Descriptor Tool]
USB.ORG 공식 사이트에서 받을수 있음
(위에서 언급한 사이트 이외 참고한 사이트)
이 긴 문서를 다 읽었다면 혹시 당신도 백수? 백조?
에구구.. 오늘은 무슨해킹을 연구하는지 한수 배우러 용산에 용팔이나
만나러 가야겠군.. -_-;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
파워해커 사이트에 관련 내용이 더 있었는데 사라졌네요 ..
AmesianX님이 실제 구현한 파일 첨부.