Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 3개 있습니다.)
한 대의 PC에서 여러 개의 키입력 매크로 프로그램이 가능할까?


저는 SDK 레벨의 개발자입니다. 그래서 때로 DDK 레벨의 개발자들이 부럽습니다. ^^

마침, 이번 마소 3월호의 248 페이지에 "리버싱으로 분석하는 온라인 게임 해킹툴"이라는 재미있는 ^^ 기사를 읽게 되었는데요. 역시 DDK하는 사람들은 리버싱 실력 자체가 다릅니다. 너무 너무 멋있다는! ^^

이야기를 좀 벗어나서.

한때 저도 "모" 게임에 푹 빠졌을 때가 있었다는 사실... 아셨나요? ^^
그 게임에서 구현한 3차원 세계는 정말이지 온라인 게임을 처음 접한 저에게는 신세계와 같았습니다. (결과적으로 지금 생각해 보면, 이때 온라인 게임의 폐해를 알았기에 나중에 WOW가 나왔어도 심드렁할 수 있었던 것 같습니다. ^^;)

제가 그렇게 게임에 빠져있을 때, 나름대로 ^^ 만든 매크로 프로그램을 만들어서 아주 잘 활용했었습니다. 그렇다고 무슨 자동 사냥하는 프로그램은 아니었고, 컴퓨터 2대에서 게임 클라이언트를 각각 실행하고, 한 대에서 제가 게임을 하면서 다른 한대에 실행된 캐릭터를 같이 조정할 수 있는 프로그램이었습니다. 예를 들어, 제 컴퓨터의 "a" 키를 누르면 다른 PC의 캐릭터가 같이 공격해 주고, "h" 키를 누르면 제 캐릭터에 에너지를 보충 할 수 있도록 한 것이었습니다.

"리버싱으로 분석하는 온라인 게임 해킹툴" 기사를 보면서, 그때 제가 만든 그 프로그램이 생각났습니다. 기사에서는 "고전적인 매크로 구현 방법"이라고 하면서 "keybd_event", "mouse_event"를 예로 들었는데 실제로 제가 만든 그 프로그램에서도 "keybd_event" API를 사용해서 구현했었습니다.

기사에서 언급되고 있지만, "keybd_event, mouse_event" API는 게임 보안 솔루션에서 사용을 막았기 때문에 이젠 구식이 되어버린 기술이라고 합니다.

그때 기억도 되새길 겸. 정말로 모 업체의 3-D 게임 하나를 다운로드 받아서 테스트를 해봤습니다. 오호... 정말 안됩니다. ^^; 재미있게도 게임 프로그램이 전면에 뜰 때만 keybd_event 함수가 동작 안하고 다시 다른 응용 프로그램이 전면으로 오면 동작을 합니다. keybd_event 유의 함수가 다른 프로그램에서는 "유용한" 매크로로 쓰인다는 점에 대한 세심한 배려가 돋보입니다.




해보는 김에. 기사에서 언급된 0x64, 0x60 포트를 이용해서 제어를 해보고 싶었습니다. 말씀드린 데로 저는 SDK 개발자이기 때문에 드라이버 개발은 해본적이 없습니다. (사실 Hello World 수준의 dummy 드라이버는 실습해봤지만, 그 이상은 해본 적이 없습니다.)

그래서 검색을 해봤더니. 아하... ^^ 공개된 프로그램이 하나 눈에 띄입니다.

NTPort Library
; http://www.zealsoftstudio.com/ntport/

평가판을 다운로드 할 수 있는데, 그렇다고 기능 제약이 있는 것은 아니고 최초 구동시에 라이선스 알림 대화창이 뜬다는 제약만 있기 때문에 테스트 용으로는 전혀 손색이 없습니다.

그럼. 일단 포트를 읽고 쓰는 드라이버는 구했고. 다음 문제는 어떻게 사용해야 키 입력을 흉내낼 수 있느냐는 것인데.

생각보다, 이 방법을 언급하고 있는 자료를 찾기가 쉽지 않았습니다. 겨우 찾은 방법이 아래 문서의 11페이지에 나온 정도입니다.

Keylogging Analysis
; http://codeengn.com/?module=file&act=procFileDownload&file_srl=233&sid=7f06fd348c558e70720143533138db07

위의 내용을 NTPortLibrary 코드로 다시 쓰면.

bool is_buffer_empty(void)
{
    int timeout;
    
    for(timeout = 200; timeout && (Inport(0x64) & 0x02); timeout--) 
    {
      Sleep(1);
    }

    retrn timeout != 0;
}

if (is_buffer_empty() == false)
{
    return;
}
Outport(0x64, 0xD2);

if (is_buffer_empty() == false)
{
    return;
}
Outport(0x60, scanCode);

위의 코드에서 scanCode 값은 아래 문서의 16페이지에서 찾아볼 수 있습니다.

Keyboard Scan Code Specification
; http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc

keyboard_port_io_scancode_1.png

즉, scanCode 값에 10을 넣으면 키보드로 "9" 숫자키를 입력한 것과 동일한 효과를 발생합니다.

자, 이 정도 하고 다시 게임 프로그램을 실행시키고 테스트 해봅니다. 우와~~~ 이번에는 정상적으로 문자가 전달됩니다. ^^ 아직 여기까지는 보안 프로그램이 막지 않았나 봅니다. (이 프로그램에 쓰인 보안 프로그램만 이것을 막지 않은 것인지, 아니면 이것을 원천적으로 막을 수 없는 것인지는 잘 모르겠습니다.)




그런데. 재미있는 방법이 하나 더 있습니다.
"Remote Desktop" 아시죠? mstsc.exe 프로그램으로 원격 접속하는.

예전에는 RDS 환경이 그다지 좋지 않아서 RDS 세션 환경에서는 3-D 프로그램이 실행되지 않았었는데, 어느새 시간은 흘러 ^^ 그것도 이젠 옛날 이야기가 되어버렸습니다. 그러니까, Windows Server 2008 R2에서 제공되는 RDS 환경에서는 3-D 프로그램도 정상적으로 실행이 됩니다. (제가 테스트 해본 바에 의하면 Vista 원격 접속 지원에서도 3-D 프로그램이 실행되었기 때문에 아마도 Windows Server 2008에서도 되지 않을까... 예상합니다.)

그렇다면 RDS 환경에서 게임 프로그램을 실행한 경우에는 키 입력을 위한 매크로 프로그램이 어떻게 작동할 수 있을까요? 우선, RDS 환경 내에서 포트 제어를 통한 키입력 방법은 효과가 없습니다. 짐작으로는, RDP 클라이언트 측에서 마우스/키 입력이 RDS 세션으로 전달되어 처리되는 방식이기 때문에 물리적인 하드웨어의 포트를 제어하는 방식으로는 RDS 세션 내의 프로그램에 영향을 주지는 않는 것 같습니다.

또한, "keybd_event, mouse_event" API도 RDP 환경 내에서 동작은 하지만 여전히 보안 프로그램이 설치된 게임 클라이언트에는 동작하지 않습니다.

그런데! ... 이쯤 되면 눈치채신 분도 계시겠지만.

어차피 게임 클라이언트는 그것이 실행된 윈도우 세션 내에서의 "keybd_event, mouse_event"에 대해서만 막을 수 있기 때문에, "RDP 클라이언트가 실행된 환경"에서 "keybd_event, mouse_event" API를 실행시켜 전달하는 것은 가능하다는 것입니다. 말로 설명하는 것보다 아래의 화면을 보면 이해가 되시겠죠. ^^

keyboard_port_io_scancode_2.png

위의 그림에서는 Windows 7 운영체제에 키 입력을 keybd_event 함수를 이용해서 발생시키는 "MainWindow"라는 제목을 가진 프로그램과 mstsc.exe 프로그램이 실행되어 있습니다. 보는 것처럼, MainWindow 프로그램에서 발생하는 keybd_event에 의해서 mstsc.exe에 숫자 키 "9"가 전달되는 것을 확인할 수 있습니다. 일단 메모장을 실행시켜서 테스트하고 있지만 게임 클라이언트라고 하더라도 정상적으로 키 입력을 "받아들일 수밖에" 없습니다. 왜냐하면 그 입력이 keybd_event에 의한 것인지 사용자에 의한 것인지 "RDS 세션에서 작동 중인 게임 프로그램에 설치된 보안 프로그램 입장"에서는 구별할 수 있는 방법이 없기 때문입니다.

그렇다면!
손쉬운 자동 사냥 프로그램을 못하게 하려고 keybd_event 함수를 막았던 보안 업체는, 위와 같은 방식의 자동 사냥 프로그램은 어떻게 막을 수 있을까요? 아마도, 아예 RDS 환경에서는 게임이 실행되지 못하도록 만들어야 하지 않을까 싶은데요. 정말... 그렇게 막아버리게 될까요? ^^

한 가지 더 재미있는 사실이 있습니다.

그동안, "전면에 띄워진 윈도우"에만 키 입력이 전달되었기 때문에 "하나의 PC"에서 하나의 매크로 프로그램만을 동작시킬 수 있었지만 RDS 세션을 활용하게 되면 "하나의 PC"에서 다중 RDS 세션을 열어서 여러 개의 매크로 프로그램을 돌리는 것이 가능합니다.

이런 거 보면, 정말이지 ^^ 컴퓨터는 다양한 기술의 조합에 따른 운영의 묘미가 있는 것 같습니다.

이게 끝일까요? ... 알 수 없습니다. 언제나 기술은 변화하기 때문에.
2003년 PDC에서 언급된 "Multiple Input Queues"가 구현된다면... 위와 같이 다중 세션을 열어놓을 필요도 없을 테니까요.




마지막으로 한 가지 더 재미있는 사실.
Windows Server 2008 R2의 RDS 연결 제한은 기본적으로 "Administration" 모드로 제공되는 라이선스로 인해 2개이고, 라이선스 서버를 제공하는 경우에만 2개의 제한을 없앨 수가 있습니다.

그런데, 예전에 제가 Windows 7의 RDP 연결에서 1개의 연결 제한을 해제하는 (정식이 아닌) 패치를 알려드렸었지요.

Windows 7 - 다중 원격 접속(Remote Desktop) 허용
; https://www.sysnet.pe.kr/2/0/782

재미있게도 그 패치를 적용하면 RDP 연결 수에 제한이 없습니다.

이번 글을 쓰면서, VPC에 설치해 둔 Windows Server 2008 R2에도 해당 termsrv.dll을 테스트해 보았습니다. 오호... 2개의 연결 제한을 갖는 "Administration Mode"가 해제되었다는!




첨부한 파일은 본문에서 설명한 2개의 문서와 소스 코드입니다.

polling.pdf: 위에서 설명한 Keylogging Analysis PPT 문서
scancode.doc: 위에서 설명한 Keyboard Scan Code Specification 문서
Console.zip: NTPortLibrary를 이용한 키입력 테스트 프로젝트 (VS2010)



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2010-12-16 12시22분
The AT keyboard controller
; http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html

Hardware Manipulation (Keyboard LED)
; http://ezbeat.tistory.com/294
정성태
2011-02-10 11시03분
[지나가다] 포트를 이용한 키입력 전달을 보안 프로그램에서 막고 있다고 하셨는데요. 키보드 후킹 후 SendMessage는
상관 없을런지..? 이와 관련해서는 말씀이 없으셔서 관련 주제라 한번 댓글을 달아봅니다. ^^;

스크립트를 지원하는 매크로 프로그램에서 키입력 전달 스크립트를 만들어서 사용하면 키입력이 전달되더군요.
(보안 프로그램이 해당 매크로 프로그램을 막지는 않았음. 막힐경우 불탐으로 클라이언트 종료.)

해당 매크로는 키보드 후킹 후 SendMessage를 하는것 같던데, 해당 매크로의 특정 키워드를 사용하는 거라서
내부에서 어떤 식으로 키입력을 게임(활성창)에 전달하는지 자세히 모르겠네요.

매크로의 원본 C소스까지 공개되어 있던데.. 저는 뭐 소스 분석할 실력이 아니라서^^
[guest]
2011-02-10 11시43분
DirectX 게임 유는 직접 키보드 하드웨어로부터 입력을 받아들이기 때문에 SendMessage 같은 윈도우 메시지 API 전달 함수로는 키 입력을 할 수 없습니다. 말씀하신 스크립트 지원 매크로 프로그램이 뭔지는 모르겠지만, 정확히 SendMessage를 사용하는 것이 맞나요? 맞다면, 그리고 그것으로 DirectX 게임 유에 테스트 하신 건가요?
정성태
2011-02-10 03시24분
[지나가다] 글을 삭제하려고 했는데 댓글을 주셨네요. 감사드립니다. :-)

매크로 프로그램의 소스를 자세히 확인해 보니 SetWindowsHookEx()로 후킹 후 활성화된 윈도우에 keybd_event()를 사용하는 형태 였습니다.
그리고 위의 방식 말고, 직접적으로 SendMessage()를 지원했지만 제가 테스트 해볼 당시에는 작동하지 않았던것으로 기억합니다.

말씀하신 내용이 맞는듯.. 해당 게임은, 유명 온라인 게임으로 DirectX 류가 맞는것 같습니다. 엉뚱한 내용으로 귀찮게 해드려서 죄송합니다. ^^;;
[guest]
2017-06-02 12시59분
첨부 파일 - Operating Systems Development Series.mht

Operating Systems Development - Scan Codes
; http://www.brokenthorn.com/Resources/OSDevScanCodes.html
정성태

... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13151정성태10/31/20226130C/C++: 161. Windows 11 환경에서 raw socket 테스트하는 방법파일 다운로드1
13150정성태10/30/20226047C/C++: 160. Visual Studio 2022로 빌드한 C++ 프로그램을 위한 다른 PC에서 실행하는 방법
13149정성태10/27/20226035오류 유형: 825. C# - CLR ETW 이벤트 수신이 GCHeapStats_V1/V2에 대해 안 되는 문제파일 다운로드1
13148정성태10/26/20225956오류 유형: 824. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for 'net5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFramew
13147정성태10/25/20225029오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/20225879.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
13145정성태10/21/20226198오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/20226083.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/20226589오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/20225915도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/20227203.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/20226550C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/20226223.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/20227627.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/20225913.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
13136정성태10/7/20226494.NET Framework: 2054. .NET Core/5+ SDK 설치 없이 dotnet-dump 사용하는 방법
13135정성태10/5/20226777.NET Framework: 2053. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프를 분석하는 방법 - 두 번째 이야기
13134정성태10/4/20225457오류 유형: 820. There is a problem with AMD Radeon RX 5600 XT device. For more information, search for 'graphics device driver error code 31'
13133정성태10/4/20225839Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법 [1]
13132정성태10/3/20225734스크립트: 42. 파이썬 - latexify-py 패키지 소개 - 함수를 mathjax 식으로 표현
13131정성태10/3/20228559.NET Framework: 2052. C# - Windows Forms의 데이터 바인딩 지원(DataBinding, DataSource) [2]파일 다운로드1
13130정성태9/28/20225426.NET Framework: 2051. .NET Core/5+ - 에러 로깅을 위한 Middleware가 동작하지 않는 경우파일 다운로드1
13129정성태9/27/20225708.NET Framework: 2050. .NET Core를 IIS에서 호스팅하는 경우 .NET Framework CLR이 함께 로드되는 환경
13128정성태9/23/20228400C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import파일 다운로드1
13127정성태9/22/20226870Windows: 210. WSL에 systemd 도입
13126정성태9/15/20227476.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...