Win32와 드라이버간에 통신을 할 때 Win32에서 드라이버에게 파일경로를 넘겨주어야 할 때가 있었는데 Win32에서
C 드라이브를 경로에 경우 "C:\\"와 같이 접근하면 되지만 드라이버에서는 "\\??\\C:\\" 와 같이 접근을 해야 합니다.
간단하게 위와 같은 경우에는 앞에 "\\??\\" 를 붙여주면 되지만 네트워크 공유 폴더에 경우에는
또 다른 방식으로 이름을 지정해 주어야 합니다.
즉, 상황에 따라 Win32에서 파일 이름을 파싱하여 드라이버에서 인식 할 수 있는 이름으로 변환 후 드라이버에 넘겨주곤 하였는데
좀 더 깔끔한 방법을 소개하려고 합니다.
사실 조금만 생각해보면 Kernel32.CreateFileA는 Kernel32.CreateFileW를 호출하게 되고
( Win32에서 유니코드와 그렇지 않은 API로 나뉘어지는데 유니코드가 아닌 함수는 내부적으로 유니코드로 변환하여
유니코드용 함수를 호출하게 됩니다. 그래서 유니코드로 만들어진 프로그램이 빠르다는 말이 있죠.. )
Kernel32.CreateFileW는 내부적으로 NtDll.NtCreateFile 를 호출할 것입니다.
그리고 여기서 NtCreateFile은 파일이름을 인자로 받지 않습니다.
즉, 그 전에 파일 이름이 NtPathName으로 변환된다는 말입니다.
IDA로 Kernel32.DLL를 열어본 후 Export 된 CreateFileW에서 호출하는 함수들을 보면 다음과 같이 바로 눈에 띄는 함수가 있습니다.
( 해당 Kernel32.DLL은 Windows XP SP2 입니다. 버전이 다른 운영체제에 경우에는 다른 함수를 사용하는 경우도 있습니다. )
함수 이름에서 이미 무엇을 하는 함수인지 설명이 다 되어 있습니다.
구글링을 조합한 해당 함수를 사용하는 예제코드는 다음과 같습니다.
#include <Windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef BOOLEAN (__stdcall *RtlDosPathNameToNtPathName_U)(PCWSTR DosName,
PUNICODE_STRING NtName,
PCWSTR *DosFilePath OPTIONAL,
PUNICODE_STRING NtFilePath );
typedef void (__stdcall *RtlFreeUnicodeString)(
PUNICODE_STRING UnicodeString
);
int _tmain(int argc, _TCHAR* argv[])
{
RtlDosPathNameToNtPathName_U pRtlDosPathNameToNtPathName_U;
RtlFreeUnicodeString pRtlFreeUnicodeString;
HMODULE hModule;
UNICODE_STRING NtName;
hModule = ::LoadLibrary(_T("NtDLL.DLL"));
if (hModule == NULL)
{
printf("NTDLL을 로드 할 수 없습니다.\n");
return (-1);
}
pRtlDosPathNameToNtPathName_U = (RtlDosPathNameToNtPathName_U)::GetProcAddress(hModule, "RtlDosPathNameToNtPathName_U");
if (pRtlDosPathNameToNtPathName_U == NULL)
{
printf("RtlDosPathNameToNtPathName_U API를 찾을 수 없습니다.\n");
return (-1);
}
pRtlFreeUnicodeString = (RtlFreeUnicodeString)::GetProcAddress(hModule, "RtlFreeUnicodeString");
if (pRtlFreeUnicodeString == NULL)
{
printf("RtlFreeUnicodeString API를 찾을 수 없습니다.\n");
return (-1);
}
if (pRtlDosPathNameToNtPathName_U(_T("\\\\10.1.26.15"), &NtName, NULL, NULL) == FALSE)
{
printf("pRtlDosPathNameToNtPathName_U 실패\n");
return 0;
}
// 가져온 이름을 출력
::MessageBoxW(0, NtName.Buffer, _T("확인"), 0);
pRtlFreeUnicodeString(&NtName);
return 0;
}
위에 코드를 통해서 DosPathName을 NtPathName으로 변경할 수 있습니다.
예제 코드에서는 _T("\\\\10.1.26.15") 에 NtPathName를 얻으려고 하였습니다.
실행결과는 다음과 같습니다.
참고로 이 함수는 유저레벨에서만 사용 할 수 있고 인자로 넘겨주는 경로가 존재하지 않아도 상관이 없습니다.
즉, 경로를 드라이버에서 사용가능한 경로로 변환할 뿐 경로가 올바른지는 확인하지 않습니다.
해당 함수가 Undocumented 된 API이긴 하지만 제가 테스트해봤을 때는 거의 모든 운영체제에서 돌아갔습니다.
테스트한 운영체제는..
Windows XP SP2, Windows 2003 Server, Windows XP 64Bit, Windows Vista 64Bit, Windows 7 64Bit 입니다.
( 그냥 다 된다고 보시면 됩니다.-_- )