2013년 9월 11일 수요일

커널모드 유닛테스팅 프레임웤 활용하기

간단하게 커널모드 코드를 테스트 할 수 있는 방법에 대해서 설명할까 합니다.

응용프로그램 레벨에서는 사용할 수 없는 인스트럭션 (예. mov eax, cr0 )들에 대한 테스트가 필요한 경우도 있고, 간단한 커널모드 코드 조각을 테스트해야 하는 경우도 있고, 커널모드 안티디버깅 같은거 테스트 할 때... 필요한 상황이야 많습니다.

대부분의 응용프로그램 개발자들은 커널모드 프로그램 개발에 대해서 알지 못하고, 간단한 테스트 하나 하자고, 두꺼운 책들과 씨름하기엔 너무 귀찮고, 좀 어렵죠?

좀 쉽게 할 수 있는 방법이 없을까요? (있으니까 이런 글을 쓰고 있겠죠.)
유닛테스트 프레임웤 중에 cfix 라는 녀석은 윈도우즈 커널모드 코드에 대한 유닛테스팅을 지원합니다. 예전에도 몇번인가 제 블로그에서 다뤘던거 같은데요.

cfix 를 이용해서 테스트할 커널 코드를 작성하고, 프레임웤을 이용해서 실행해보면 되는거죠.  드라이버를 실행하기 위한 서비스 등록/시작/중지/제거기능, 드라이버 구현을 위한 기본 코드따위는 프레임웤이 알아서 다 해줍니다.

우린 그냥 프레임웤에 테스트할 코드만 넣어주면 땡입니다. 아싸~

우선 여기에서 cfix 프레임웤을 받아서 설치합니다.
홈페이지에 보면 Visual Assert 와 cfix 두 가지가 있는데 읽어보시면 알겠지만 visual assert 는 cfix 를 기반으로 만들어진 visual studio 용 플러그인입니다.

둘다 사용법은 같으니 아무거나 설치해도 됩니다만 나중에 visual studio 에서 유닛테스트를 사용할 때도 쓰려면 visual assert 를, 드라이버 테스트만을 위한거라면 cfix 를 설치하시면 됩니다.

설치할때 주의할 점은 설치 경로에 공백문자나 한글이 있는 경우 커널모드 유닛테스트를 사용할때 문제가 발생 할 수 있습니다. 그냥 C:\VisualAssert 에 설치하는게 가장 좋습니다.


C:\VisualAssert 아래 Doc, example 에 보면 필요한 내용은 다 있습니다. 그거 참고해서 알아서들 하세요... 라고 하고 싶... :-)

C:\VisualAssert\examples\KernelMode 에 보면 우리에게 필요한 모든게 다 있습니다.



suite.c 파일에 있는 주석들 대충 읽어보면 뭐가 뭔지 알거에요. 나중에 읽어보시고, 아래처럼 간단하게 고쳐봅시다.

#include 

static void __stdcall Test1()
{
    // CR4 레지스터에 접근해 볼까요?
    unsigned long _cr4=0x00000000;
    __asm 
    {
        mov eax, cr4
        mov _cr4, eax
    }
    
    CFIX_LOG(L"cr4 = 0x%08x", _cr4);
}

CFIX_BEGIN_FIXTURE( MyFixture )
 CFIX_FIXTURE_ENTRY( Test1 )
CFIX_END_FIXTURE()

이제 드라이버 코드를 빌드해야 겠죠? 당연히 드라이버 빌드를 위해서는 DDK가 설치되어있어야 합니다. 우선 32 비트 xp 에서 테스트 할 것이므로 아래 그림처럼 WindowsXP \ x86 checked Build Environment 를 선택합니다.
당연히 디버깅을 할 상황도 생길 수 있기때문에 checked build 를 선택하는게 좋겠죠?



MakeFile, Sources 파일이 위치한 경로로 이동해서 "build -ceZ" 명령을 내려주면 됩니다.

빨간 부분으로 표시한 곳을 보면 에러가 발생했네요.
잘 읽어보면 에러를 워닝으로 간주하도록 설정이 되어있어서 워닝 -> 에러가 된 상황이죠. ( 간혹 워닝은 워닝이니까 하고, 무시하는 님들이 계신데, 워닝은 에러... 라고 생각하고, 미주알 고주알 따져서 반드시 다 없애주는 버릇을 들여줘야 합니다. 무시한 워닝 하나로 인해 정말 찾기 힘든 버그 만들어서 개고생 하는 사람들 많이 봤습니다 )

워닝 메세지를 보니 cr4 라는 레이블을 인식못한다는 군요. 이것은 DDK 에 내장된 어셈블러가 'cr4' 문자열을 레지스터 이름으로 인식하지 못하고, 일반 문자열로 인식하기 때문이겠죠.

mov eax, cr4 명령을 기계어로 넣어주거나, __readcr4( ) 함수를 이용하면 됩니다. 자세한건 인터넷 찾아보시고요. 아래 처럼 코드를 수정해 주시고, 다시 build -ceZ 명령을 해봅시다.

static void __stdcall Test1()
{
    // CR4 레지스터에 접근해 볼까요?
    unsigned long _cr4=0x00000000;
    __asm
    {
        _emit 0x0f   ; mov eax, cr4
        _emit 0x20 
        _emit 0xe0   
        
        mov _cr4, eax
    } 
    
    CFIX_LOG(L"cr4 = 0x%08x", _cr4);
}


아 ㅅㅂ 또 에러인데, 이번도 warning 때문에 발생한 문제네요. 그런데 워닝 메세지도 없습니다. ㅠ.ㅠ

build 명령을 내리면 현재 MakeFile 이 있는 디렉토리에 buildXXX_XXX_XXX.wrn/err/log 파일이 생성되는데 각 파일들은 워님, 에러, 로그를 담고있습니다.
wrn 파일을 열어보면 아래와 같은 내용이...

1>c:\visualassert\examples\kernelmode\cl : warning D9035 : option 'Wp64' has been deprecated and will be removed in a future release
1>c:\visualassert\examples\kernelmode\suite.c : warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss

대충 요약해 보면 첫 번째 라인은 64 비트 호환모드 옵션(/Wp64)때문인데 아마 vs 2003 이후 컴파일러부터는 필요없는 옵션일겁니다.
두 번째는 소스 파일 인코딩이 cp949 가 아니라는 거죠. 외국에서 만든거니 코드페이지가 달라서 발생한 문제입니다.

다 귀찮으니 SOURCES 파일의 컴파일 옵션을 아래처럼 수정하고, 다시 빌드합니다.
( 64 비트 호환모드 체크 안함 / 워닝을 에러로 처리 안함 )

# MSC_WARNING_LEVEL=/W4 /WX /Wp64
MSC_WARNING_LEVEL=/W4


그대로 따라했다면 " C:\VisualAssert\examples\bin\chk\i386\kern.sys/pdb/obj/lib " 같은 파일들이 생겼을겁니다. 젠장, 이제 겨우 빌드에 성공했군요. -_-;

이제 빌드 한 커널드라이버를 실행하기 위해서 가상머신에 cfix 프레임웤을 설치해야 합니다. 사실 개발 머신에는 cfix 프레임웤을 설치할 필요는 없으나 예제 코드를 그대로 사용하려고 했던거고요.

정말 중요한건 드라이버를 실행할 PC 에 cifx 프레임웤을 설치해야 하는거죠.
가상머신에 visual studio 가 설치되어있지 않은 경우 visual assert 를 설치하려하면 오류가 나고, 설치가 중단됩니다. 당연히 visual assert 는 visual studio 플러그인이니까요. 홈페이지에서 cfix 를 다운로드 해서 설치하거나 " c:\VisualAssert\bin\i386 " 폴더 아래의 파일들을 가상머신의 적당한 경로에 복사합니다. 편한대로 하세요~

저는 가상머신의 c:\dbg 폴더 아래에 복사했고, 빌드 할 실행파일 kern.sys 도 같은 경로에 복사했습니다. c:\dbg\cfix32.exe -kern kern.sys 명령을 실행하면 아래처럼 우리가 작성했던 코드의 결과를 확인할 수 있습니다.


참~ 쉽죠~?! ^__^