'Perflib'에 해당되는 글 1건

  1. 2011.08.30 [윈도우 NT] HKEY_PERFORMANCE_TEXT 핸들 얻기 8
Windows OS2011. 8. 30. 23:00
반응형
개발중 아래와 같은 레지스트리 키를 열고 닫았더니 BSOD가 발생하는 황당한 일이 있었다.

\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009


원인을 추적하다가 재미있는 사실을 몇 가지 알게 되어 기록해 놓을까 한다.

소스코드는 대략 다음과 같다.

RtlInitUnicodeString(&usKeyName,
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\Perflib\\009");
 
InitializeObjectAttributes(&oa, &usKeyName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);

status = ZwOpenKey(&handle, KEY_ALL_ACCESS, &oa);
if (NT_SUCCESS(status))
{
     ZwClose(handle);
}

ZwClose()를 호출하면 BSOD가 발생하고 만다.


1. status와 handle


status는 분명히 STATUS_SUCCESS다.

handle도 분명히 0x80000050 값이 들어 있었다.

하지만 ZwClose()를 호출하면 BugCheck 0x93이 발생한다.
BugCheck 0x93은 INVALID_KERNEL_HANDLE 이다.

분명히 ZwOpenKey() 함수가 성공했고 핸들도 리턴했는데 Invalid Handle 이라니 기가 막힐 노릇이다.

일단 STATUS_SUCCESS는 더 볼게 없으니 handle 값으로 부터 정리하기 시작했다.
정말 지푸라기를 잡는 심정으로 0x80000050를 검색해 봤는데 여기서 의외의 수확을 올리게 되었다. 

0x80000050은 실제 핸들이 아니라 HKEY_LOCAL_MACHINE 같은 predefined 핸들이었다. 

#define HKEY_PERFORMANCE_TEXT          (0x80000050)
#define HKEY_PERFORMANCE_NLSTEXT    (0x80000060)

HKEY_PERFORMANCE_TEXT로 검색 해보니 더 가관이다.
MS KB에 설명이 되어 있는 것이었다.

http://support.microsoft.com/kb/890648

마지막 멘트에 다시 한번 기가 찬다. -_-
"이것은 의도적으로 설계된 동작입니다."


2.  실제 키 핸들 얻기

Zw* 시리즈 함수로 별짓을 다 해 봤지만 항상 0x80000050이 튀어 나와 원하는 개발을 할 수 없었다.
OBJ_KERNEL_HANDLE 플래그를 빼도 항상 0x80000050이 튀어 나온다.
이것이 핸들 테이블에서 나온 것이 아니라 일종의 상수라는 것을 명확히 보여주는 것이다.

실제 레지스트리 오브젝트에 대한 핸들 테이블의 핸들값을 얻어야 내가 원하는 작업을 할 수 있기 때문에...
삽질 끝에 레지스트리도 일종의 오브젝트가 아니겠냐는 생각에 Ob* 시리즈 함수로 가 보기로 했다.

NTSTATUS

ObOpenObjectByName(
                  POBJECT_ATTRIBUTES ObjectAttributes,

  POBJECT_TYPE ObjectType,

  PVOID ParseContext,

  KPROCESSOR_MODE AccessMode,

  ACCESS_MASK DesiredAccess,

  PACCESS_STATE PassedAccessState,

  PHANDLE Handle);

 
이것을 사용하려면 ObjectType을 알아야 하는데 알려진 ObjectType은 IoDriverObjectType, PsProcessObjectType, PsThreadObjectType 이런거 밖에 없어서 레지스트리를 열지 못했다.

결국 찾아낸 레지스트리를 위한 ObjectType은 CmpKeyObjectType 이었다.
하지만 이것은 Undocumented라서 코딩을 할 때 직접 사용할 수가 없다.
커널 내부를 뒤져서 값을 얻어내야 하나 어쩌나 고민을 많이 했는데 결국 이것도 구글사마가 해결해 주었다.

다음은 CmpKeyObjectType을 구하는 코드 예제다.

http://read.pudn.com/downloads159/sourcecode/windows/vxd/716440/RegMon_Checked/RegMon.c__.htm

GetCmpKeyObjectTypeByInstance() 함수에서 우리가 원하는 작업을 해주고 있다.


3. 코드 수정

다음과 같이 수정해 작업을 완료했다. 만세!!!

RtlInitUnicodeString(&usKeyName,
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\Perflib\\009");
 
InitializeObjectAttributes(&oa, &usKeyName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);

status = ObOpenObjectByName(&oa, CmpKeyObjectType, NULL, KernelMode, DesiredAccess, NULL, &handle);
if (NT_SUCCESS(status))
{
     ZwClose(handle);
}

주의할 점은 status는 STATUS_PREDEFINED_HANDLE 로 나온다는 점이다.
#define 
STATUS_PREDEFINED_HANDLE 0x40000016
으로 정의되어 있어 NT_SUCCESS()에서 성공으로 처리된다.

반응형
Posted by GreeMate