Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
Device Driver 응용 프로그램의 빌드 스크립트 - 두 번째 이야기

예전에, Device Driver 응용 프로그램을 빌드하는 것에 대한 글을 썼는데요.

Device Driver 응용 프로그램의 빌드 스크립트
; https://www.sysnet.pe.kr/2/0/921

역시, 초보이다 보니 ^^ 현실에 안 맞는 부분이 몇 개 있어서 수정합니다.




우선, 그 예제 솔루션에 있던 ntifs.h 파일은 삭제합니다. 왜냐하면 "http://www.acc.umu.se/~bosse/ntifs.h" 사이트에서 구한 것인데, XP/2003 버전까지만 되어 있고, Windows Vista 급 이상부터는 전혀 업데이트가 안되어 있기 때문에 사용할 만한 가치가 없어보였습니다. 어차피 지원은 Windows 7까지를 고려해야 하는데, 차라리 WDK에서 제공되는 ntifs.h 파일이 더욱 현실적이기 때문입니다.

따라서, 예제에 있던 다음의 코드는 Offset 값으로 치환되어야 합니다.

{
    PEPROCESS pEprocess = PsGetCurrentProcess();
    char subsystemVersion[1024];
    StringCchPrintfA(subsystemVersion, 1024, "Subsystem Version: %d.%d\n", pEprocess->SubSystemMajorVersion, 
        pEprocess->SubSystemMinorVersion);
    DbgPrint(subsystemVersion);
}

자, 옵셋값으로 치환되려면 이제 운영체제에 대한 고려를 해야 합니다. 이전에 설명한 글에서는 이를 지정하기 위해서 별도의 PLATFORM_PREPROCESSOR 전처리기를 연결하려고 애썼는데 이럴 필요가 없습니다. WDK 내부의 헤더파일들은 모두 이미 NTDDI_VERSION라는 전처리기를 이용하고 있기 때문에 이것을 맞춰주면 되는 것이었습니다.

운영체제별로 어떤 값을 지정할 지는 WDK의 sdkddkver.h 파일에서 확인할 수 있는데요.

#define NTDDI_WIN2K                         0x05000000
#define NTDDI_WIN2KSP1                      0x05000100

...[생략]...

#define NTDDI_LONGHORN  NTDDI_VISTA
                        
#define NTDDI_WS08                          NTDDI_WIN6SP1
#define NTDDI_WS08SP2                       NTDDI_WIN6SP2
#define NTDDI_WS08SP3                       NTDDI_WIN6SP3
#define NTDDI_WS08SP4                       NTDDI_WIN6SP4

#define NTDDI_WIN7                          0x06010000

아하, 그렇다면 빌드 스크립트에 다음과 같이 지정해 주면 되겠군요.

msbuild ".\DeviceDriverSample\DeviceDriverSample.vcxproj" /p:PLATFORM_PREPROCESSOR="NTDDI_VERSION=0x05020000" /p:Platform=Win32;Configuration=%BUILDCONFIG% /p:TargetName=%SERVICENAME32% /p:OutDir=%CURRENTDIR%bin\%TARGETOS%\

한 가지 더 정의해 주어야 합니다. NTDDI_VERSION와 함께 _WIN32_WINNT도 지정해 주어야 하는데 역시 다음과 같이 이미 정의되어 있는 값들을 적절하게 사용해 주어야 합니다.

#define _WIN32_WINNT_NT4                    0x0400
#define _WIN32_WINNT_WIN2K                  0x0500
#define _WIN32_WINNT_WINXP                  0x0501
#define _WIN32_WINNT_WS03                   0x0502
#define _WIN32_WINNT_WIN6                   0x0600
#define _WIN32_WINNT_VISTA                  0x0600
#define _WIN32_WINNT_WS08                   0x0600
#define _WIN32_WINNT_LONGHORN               0x0600
#define _WIN32_WINNT_WIN7                   0x0601

그래서, msbuild 실행 시 전처리기 정의는 다음과 같이 운영체제마다 달리 해주시면 됩니다.

msbuild "...\DeviceDriverSample.vcxproj" /p:PLATFORM_PREPROCESSOR="NTDDI_VERSION=0x05020000;_WIN32_WINNT=0x0502" /p:Platform=Win32;Configuration=%BUILDCONFIG% /p:TargetName=%SERVICENAME32% /p:OutDir=%CURRENTDIR%bin\%TARGETOS%\

이제 소스 코드에서 예제로 사용된 SubSystemMajorVersion과 SubSystemMinorVersion 필드는 옵셋 값으로 정의해 주는 것만 남았는데요. 예제를 간략히 하기 위해 ImageFileName으로 대신하겠습니다.

#if NTDDI_VERSION == NTDDI_WS03

#if defined(_AMD64_)
#define PSOFFSET_IMAGEFILENAME 0x268   // windows 2003 x64
#else
#define PSOFFSET_IMAGEFILENAME 0x164   // windows 2003 x86
#endif

#elif NTDDI_VERSION == NTDDI_WIN7

#if defined(_AMD64_)
#define PSOFFSET_IMAGEFILENAME 0x2e0   // windows 7 x64
#else
#define PSOFFSET_IMAGEFILENAME 0x16c   // windows 7 x86   
#endif

#endif

마지막으로 수정해야 할 것 하나 더! 이전 글에서 참고했던 DDK 빌드 설정에서는 LIB 경로를 모두 Visual Studio 프로젝트 설정 내에서 했는데요.

Visual Studio 2010 - Device Driver 제작- Hello World 예제
; https://www.sysnet.pe.kr/2/0/919

빌드 스크립트에도 역시 이 설정을 반영해 주어야 합니다. 이를 위해서 PropertyGroup을 다음과 같은 식으로 변경해주어야 하는데,

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental></LinkIncremental>
    ...[생략]...
 
    <LibraryPath>$(DDK_LIB_PATH)</LibraryPath>
    <ReferencePath />
</PropertyGroup>

개인적으로 이 부분이 가장 난감한 부분인데요. 만약, 모든 빌드(Win32/x64, Debug/Release)에 대해서 위와 같이 DDK_LIB_PATH를 설정해 주면 Visual Studio IDE 내에서 빌드할 때 실패해 버립니다. 왜냐하면, IDE 내에서는 $(DDK_LIB_PATH)가 정의되어 있지 않기 때문입니다. 가능한 전역 환경 변수 설정을 피하려고 했는데, 여기서만큼은 방법이 없군요. ^^ 어쩔 수 없이, 전역 환경 변수 DDK_LIB_PATH를 자신의 로컬 테스트 환경에 맞게 설정한 다음 Visual Studio를 재시작해주어야 합니다. (그래서, 제 컴퓨터에는 DDK_LIB_PATH가 "C:\WinDDK\7600.16385.1\lib\win7\amd64"로 설정되어 있습니다.)

자, 그럼 이제 다시 빌드 스크립트로 돌아와서 다음과 같이 DDK_LIB_PATH를 각 빌드마다 변경해 주는 것이 가능합니다.

SET DDK_LIB_PATH=C:\WinDDK\7600.16385.1\lib\wnet\i386
msbuild ".\DeviceDriverSample\DeviceDriverSample.vcxproj" ... /p:OutDir=%CURRENTDIR%bin\%TARGETOS%\ 

SET DDK_LIB_PATH=C:\WinDDK\7600.16385.1\lib\wnet\amd64
msbuild ".\DeviceDriverSample\DeviceDriverSample.vcxproj" ... /p:OutDir=%CURRENTDIR%bin\%TARGETOS%\ 

이 정도면 제법 보완이 된 것 같지요. ^^

첨부한 파일은 위의 작업이 반영된 예제 솔루션입니다.




그 외에, 시행착오한 것을 살펴보면.

DriverMain.c가 "C" 언어 구문으로 컴파일이 되어 변수 선언을 함수 초기 또는 블록 바로 다음에만 해야하는 제약이 있습니다. 특히나, DbgPrint 같은 진단 코드를 별 생각 없이 아무데나 끼워넣을 때 다음과 같은 식으로 오류가 발생합니다.

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
    DbgPrint("DriverEntry\n");
    char imageFileName[100];

    return STATUS_SUCCESS;
}

error C2143: syntax error : missing ';' before 'type'

순수 "C" 언어 해본지 하도 오래되어서 처음엔 왜 이런 오류가 발생하는지 몰라서 헤맸는데요. ^^;

C++ 구문의 이점을 누리지 않겠다고 작정했으면, "char imageFileName[]" 선언을 함수 초기로 올려주거나, 별도의 "{", "}" 블럭으로 감싸주면 됩니다. 만약, C++ 컴파일로 바꾸겠다면? 일관성을 위해 DriverMain.c의 확장자를 DriverMain.cpp로 바꿔주고 해당 파일의 속성 창을 열어 다음과 같이 "Advanced" / "Compile As" 값을 "Compile as C++ Code (/TP)"로 바꿔주어야 합니다. (만약, 프로젝트에 파일 추가 시 확장자가 cpp였다면 자동으로 /TP 옵션으로 지정됩니다.)

how_to_build_driver_app_1.png

두 번째로, windbg의 "dt" 명령어를 통해서 알아내는 구조체가 "커널 모드"냐 "사용자 모드"냐에 따라 달라지는 경우가 있다는 것!

windbg의 커널 모드 진입에 대해서는 다음에서 설명드렸고,

Windbg - Local Kernel Debug 모드
; https://www.sysnet.pe.kr/2/0/934

어떻게 달라지는지 예를 들어 보면,

다음은, 윈도우 7 x64 시스템에서 notepad.exe에 대해 "Attach to a Process..."로 "사용자 모드" 디버깅 상태에 들어갔을 때 "_EPROCESS" 구조의 일부입니다.

+0x2c0 FreeTebHint      : Ptr64 Void
+0x2c8 PageDirectoryPte : _HARDWARE_PTE
+0x2c8 Filler           : Uint8B
+0x2d0 Session          : Ptr64 Void
+0x2d8 ImageFileName    : [15] UChar
+0x2e7 PriorityClass    : UChar

아래는 "커널 모드" 디버깅 상태입니다.

+0x2c0 FreeTebHint      : Ptr64 Void
+0x2c8 FreeUmsTebHint   : Ptr64 Void
+0x2d0 PageDirectoryPte : _HARDWARE_PTE
+0x2d0 Filler           : Uint8B
+0x2d8 Session          : Ptr64 Void
+0x2e0 ImageFileName    : [15] UChar

이름은 같은 구조체인데 "사용자 모드" / "커널 모드"에 따라 달라지기 때문에 구조체 옵셋을 알아볼 때 주의하셔야 합니다.




마지막으로, 저야 뭐 재미삼아서 DDK 분야를 기웃거리고 있지만, 사용자 응용 프로그램 분야에 비해 너무나도 자료가 없다는 것이 실감납니다. 아래의 글이, 아마도 그에 대한 증명이 아닐까 싶은데요. ^^

Virtual HID Mouse & Keyboard Driver (가상키보드 마우스 드라이버)
; http://www.hanistory.com/89631

정말 눈물겨운 스토리입니다. ^^; 저만 해도, 벌써 windbg에 "커널 모드"라는 것 자체가 있었는지도 몰랐으니, 가상 HID 드라이버를 만든다는 것은 그야말로 무에서 유를 창조하는 것과 다를 바가 없습니다.

사실, DDK라는 분야가 너무나 방대하고 서로 엮어져 있다는 것도 문제라면 문제겠지요. 게다가 포럼을 통해 답을 얻는 것도 마찬가지입니다. 시간이 금쪽같이 귀해진 시대에 살면서, 밑바닥 지식부터 하나하나 친절하게 설명할 수 있는 답변자를 기대하는 것은 어쩌면 욕심이라고 볼 수도 있습니다.

어쨌든... 방법이라면... 다들 차근차근히 문서화를 하자고요. ^^

결정적으로 위의 글에서 마음에 공감가는 부분을 소개하자면,

이렇게 기술문서를 공개하는 이유에는 여러가지가 있지만 무엇보다 이 기술이라는 분야는 언젠가는 자신이 알고있는 오늘의 기술이 내일의 쓰레기가 된다. 일찌감치 알고있는 정보들은 공개하고 더 높은 곳으로 계속 뛰어오를때 진정으로 아무도 가보지못한 고지에 오른 것이다. 그래야 스스로가 발전하는 길이고 바로 모두가 발전하는 길일 것이다.


저 역시, 제 블로그에 공개하는 정보들이 위의 취지에서와 같습니다. "오늘의 기술"이라는 것이 사실 조금만 시간이 지나도 쓰레기가 되어 버립니다. 차라리, 그것들을 공개해서 많은 사람들이 나눠쓰고 좀 더 빨리 "내일의 기술"을 앞당기는 것이 더 바람직합니다.





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

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/27/2021]

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

비밀번호

댓글 작성자
 



2010-10-22 01시12분
[ryujh] 매번 댓글 달지는 못하지만 자주 방문하고 있습니다.

인용한 글 내용 중 내일의 쓰레기라고 할 수 있다는 것이 대단합니다. 최소한 어제든 오늘이든 기술로 알게된 것이니 말입니다.

예전에는 기술로 알기전에 쓰레기라고 멀리했거나 관심이 없었는데 갈수록 예전의 '쓰레기(?)' 들을 기술로 알아야겠다고 느낍니다.

좀 감동해서 글 남깁니다.
[guest]
2010-10-22 01시45분
에이~~~ 표현이 좀 극단적이기는 하지만 ryujh 님도 글쓴이가 어떤 의미로 언급한 것인지 아시잖아요. ^^

이번엔 ryujh 님의 댓글에 동의하는 말을 한다면,,, 개인적으로는 어셈블리를 현란하게 구사해야만(!) 했던 시절에 태어나지 못한 것을 아쉽게 생각하고 있습니다. 그때부터 정립된 기술체계라면 지금보다 더 바탕이 탄탄하지 않았을까 싶은 마음이거든요. ^^
kevin25

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13607정성태4/25/2024191닷넷: 2248.C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024203닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024398닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024441오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024711닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024801닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024851닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/2024885닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024871닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024894닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024881닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241072닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241052닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241069닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241084닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241219C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241200닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241079Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241154닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241270닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241170오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241337Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241132Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241251개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
13583정성태3/25/20241472Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...