윈도우 드라이버를 개발하다 보면 UNICODE_STRING을 사용하는 경우가 많습니다. 유저모드에서 커널모드로 문자열을 보내왔을 때나, 드라이버 내에서 문자열을 주고 받고 조작하는 경우 보통 NULL로 끝나는 char * 문자열을 이용하는 경우가 많습니다.

이 char * 문자열은 윈도우 커널모드 함수에서 받아주지 않기 때문에 UNICODE_STRING으로 변환해서 사용합니다. 보통 char *에서 ANSI_STRING으로 만들고 다시 RtlAnsiStringToUnicodeString()을 사용해서 UNICODE_STRING으로 변환합니다.

RtlAnsiStringToUnicodeString()의 매개변수는 대상 UNICODE_STRING과 원본 ANSI_STRING, 그리고 대상 UNICODE_STRING의 버퍼를 메모리에 자동 할당 할 것인지 아닌지를 지정하는 것입니다.

그런데 이 RtlAnsiStringToUnicodeString()의 세번째 매개 변수와 대상 UNICODE_STRING 상태에 따라 RtlFreeUnicodeString()의 사용 여부가 결정됩니다. RtlAnsiStringToUnicodeString()에서 TRUE로 설정하여 버퍼 메모리를 자동으로 할당한 경우.

VOID
SampleFunction(char *Str)
{
    ANSI_STRING     ansiStr;
    UNICODE_STRING  uniStr;

    RtlInitAnsiString(&ansiStr, Str);
    RtlAnsiStringToUnicodeString(&uniStr, &ansiStr, TRUE);
    (...)
    RtlFreeUnicodeString(&uniStr);
}

당연한 것이겠지만 uniStr의 버퍼를 자동으로 할당했으므로 RtlFreeUnicodeString()을 사용하여 버퍼를 해제해줘야 합니다.

RtlAnsiStringToUnicodeString()에서 FALSE로 설정하여 버퍼 메모리를 자동으로 할당하지 않고 지역 변수 배열을 사용하는 경우.

VOID
SampleFunction(char *Str)
{
    ANSI_STRING     ansiStr;
    UNICODE_STRING  uniStr;
    WCHAR           buffer[100];

    RtlInitAnsiString(&ansiStr, Str);
    uniStr.Buffer = &buffer[0];
    uniStr.Length = 0;
    uniStr.MaximumLength = sizeof(buffer);
    RtlAnsiStringToUnicodeString(&uniStr, &ansiStr, FALSE);
    (...)
}

이 경우에서는 지역 변수 배열을 사용하였기 때문에 당연히 RtlFreeUnicodeString()을 사용하면 안되겠죠.

RtlAnsiStringToUnicodeString()에서 FALSE로 설정하여 버퍼 메모리를 자동으로 할당하지 않았지만 버퍼는 동적으로 할당한 경우.

VOID
SampleFunction(char *Str)
{
    ANSI_STRING     ansiStr;
    UNICODE_STRING  uniStr;
    PWCHAR          buffer = ExAllocatePool(PagedPool, 100);

    RtlInitAnsiString(&ansiStr, Str);
    uniStr.Buffer = buffer;
    uniStr.Length = 0;
    uniStr.MaximumLength = 100;
    RtlAnsiStringToUnicodeString(&uniStr, &ansiStr, FALSE);
    (...)
    RtlFreeUnicodeString(&uniStr);
}

이미 ExAllocatePool()로 uniStr의 버퍼에 메모리를 할당했습니다. 그래서 RtlAnsiStringToUnicodeString()에서는 FALSE를 줘서 자동으로 메모리를 할당하지 않도록 했습니다. ExAllocatePool()로 메모리를 할당했기 때문에 RtlFreeUnicodeString()로 해제를 해줘야 합니다. ExFreePool()로 해제를 해줘도 상관은 없습니다.

RtlAnsiStringToUnicodeString()과 마찬가지로 RtlUnicodeStringToAnsiString()도 대상 ANSI_STRING을 동적으로 할당할 것인지 아닌지를 지정할 수 있습니다. 물론 위의 예제에 나온 상황에 맞게 RtlFreeAnsiString()을 사용해 주면 됩니다.

이번에는 RtlInitUnicodeString()으로 만든 유니코드 문자열에 대해서는 어떤지 알아보겠습니다.

VOID
SampleFunction()
{
    UNICODE_STRING  uniStr;

    RtlInitUnicodeString(&uniStr, L"Hello World");
    (...)
}

코드상에서 직접 문자열 상수로 지정해줬기 때문에 L"Hello World" 부분은 데이터 영역에 들어가 있습니다. 그러므로 RtlFreeUnicodeString()로 해제할 필요가 없습니다.

VOID
SampleFunction()
{
    UNICODE_STRING  uniStr;
    PWCHAR          wStr = ExAllocatePool(PagedPool, 100);

    swprintf(wStr, L"Hello %s", L"World");
    RtlInitUnicodeString(&uniStr, wStr);
    (...)
    RtlFreeUnicodeString(&uniStr);
}

동적으로 메모리를 할당한 뒤 UNICODE_STRING으로 만들었습니다. 그래서 RtlFreeUnicodeString()으로 메모리를 해제해줘야 합니다(예제에서는 간단하게 보이기 위해서 swprintf()를 사용하였지만 실제로 이런 함수를 써서는 안되겠죠).

마찬가지로 RtlInitUnicodeString()과 같이 RtlInitAnsiString()도 위의 예제와 같은 방식으로 RtlFreeAnsiString()을 사용하면 됩니다.

이런 저런 코드를 예로 들었지만 핵심은 동적 메모리를 할당하였는가 아닌가 입니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

08 September 2007