증상
커널 콜백 등록(프로세스·레지스트리·모듈·스레드 알림)이 실패해도 드라이버는 정상 로드로 끝났다. 결과는 최악의 중간 상태다 — 드라이버는 메모리에 올라와 있는데 아무것도 감시하지 않고, 에이전트는 등록이 실패했다는 사실조차 모른다. 겉보기에 멀쩡하니 탐지가 통째로 비는데도 아무 신호가 없다.
이것이 이론상의 이야기가 아닌 이유는 커널 알림 콜백에 종류마다 64개라는 상한이 있기 때문이다. 보안 제품이 여럿 설치된 PC에서는 이 상한이 실제로 차서 등록이 실패할 수 있다. 여러 보안 솔루션이 공존하는 고객사 환경일수록 밟힐 가능성이 높다. 이 결함은 OR-2304 정밀 리뷰 중에 찾았다.
원인
DriverEntry가 StartDriver 실패 시 status를 바꾸지 않고 __leave만 했다. 그리고 StartDriver 자신도 Start*Filter의 결과와 무관하게 STATUS_SUCCESS를 돌려줬다. 그래서 하위 등록이 실패해도 그 실패가 로드 결과까지 올라오지 못하고, 드라이버는 성공으로 로드됐다.
수정
Start*Filter의 실패를 StartDriver → DriverEntry로 전파해, 콜백 등록에 실패하면 로드 자체를 실패(STATUS_INSUFFICIENT_RESOURCES)로 끝내도록 바꿨다. 감시하지 못하는 드라이버는 올라와 있느니 차라리 로드에 실패하는 편이, 이상을 바로 드러내므로 낫다.
같은 리뷰에서 함께 고친 것
정밀 리뷰 과정에서 초기화 경로의 결함 둘을 더 찾아 같이 고쳤다.
-
flush 스레드가 시작 즉시 끝나던 문제.
CThread::Thread가 스레드 안에서KWAIT_BLOCK2개를 할당했는데, 이 할당이 실패하면 flush 스레드가 시작하자마자 끝나 레지스트리 flush가 돌지 않았다. 대기 객체가 2개로THREAD_WAIT_OBJECTS(3) 이하이므로,WaitBlockArray를 NULL로 넘겨 커널이 스레드 내장 블록을 쓰게 하면 이 할당 자체가 필요 없다. 할당을 없애 실패 가능성을 지웠다. -
테이블 초기화가 다른 클래스 크기를 쓰던 문제.
CFileTable·CRegTable의Initialize가RtlZeroMemory(this, sizeof(CProcessTable))로 자기 크기가 아닌 다른 클래스 크기를 썼다. 지금 레이아웃에서는 우연히 안전했지만, 클래스가 커지면 깨질 수 있어 자기 크기로 바로잡았다.
Orange Platform 에이전트 커널 미니필터(orange.sys)의 안정성 분석 리포트입니다.