본문 바로가기

My Study/Programming&Theory

Driver Load, Unload

처음에 디바이스 책에서 보면 sys파일을 커널에 로드 시키기 위해서 장치관리자에 들어가 새 하드웨어를 추가시키고 그 하드웨어에 드라이버를 등록시키고... 쫌 복잡합니다. 그래서 소스코드로 간단하게 등록시키는 방법이 있습니다.

현재 루트킷 책을 보면서 공부 중이라 책에 나와있는데로 2가지 방법을 설명하겠습니다.
각 코드는 전부 테스트 해보고 정상실행 된 것까지 확인한 코드들 입니다.

1. 비정상적인 방법 ( 문서화 되지 않은 함수를 사용 )
보통은 드라이버를 커널에 로드시키면 서비스에 등록을 시켜야합니다. 하지만 이 방법은 문서화되지 않은 함수를 사용하면서 서비스에 등록하는 루틴없이 바로 파일을 커널에 로드시켜버립니다. 그러므로 이 방법을 사용하면 같은 드라이버를 여러번 커널에 로드시킬 수 있습니다.

이 방법의 위험한 점은 서비스에 등록시키지 않고 커널에 로드시키기 때문에 해당 메모리가 페이징 될 수 있다는 것입니다.
즉, 물리메모리에 올라왔던 내용이 하드디스크로 이동이 될 수 있다는 것입니다. 이 때 해당 물리메모리에 접근을 하게되면 BSOD가 발생하겠지요. 그래서 이 방법을 사용해 로드시킬 때는 해당 페이지 영역을 NonPaged영역으로 만들어놓고 로드시켜야합니다. 이에 대한 코드는 생략하고 일단 sys파일을 커널에 로드시키는 부분만 봐보도록 하겠습니다.

간단한 샘플 코드 같은 경우는 해당 페이지영역을 NonPaged로 하지 않아도 BSOD는 뜨지 않습니다.
해당 소스코드입니다. 예제코드에는 RtlDosPathNameToNtPathName_U함수를 안썼던데 전 이 함수까지 써서 코드를 완성시켜보았습니다. RtlDosPathNameToNtPathName_U함수는 일반적으로 유저모드에서 사용하는 문자열을 커널 레벨에서 정확하게 처리될 수 있는 형태의 문자열로 변환하는 함수입니다.

코드를 보시면 daPath에 "C:\simple.sys"라고 문자열을 지정시켜줬는데 ( \ <- 한번만 쓰겠습니다. )
RtlDosPathNameToNtPathName_U함수를 거치게 되면 
"\??\C:\simple.sys"
위와 같이 변하게 됩니다.

그리고 그 문자열을 가지고 ZwSetSystemInformation에 적당한 인자를 설정하고 호출하면 
해당 드라이버는 커널에 로드되게 됩니다.

위 방법이 문서화되지 않은 함수를 사용한 비정상적인 방법입니다.

2. Service Control Manager(SCM)을 사용한 정상적인 방법
이 방법은 문서화 된 함수를 사용해 커널에 드라이버를 로드시키는 정상적인 방법입니다.

바로 코드를 보시겠습니다. 보통은 해당 함수에 대해 에러처리를 해줘야하는데 그냥 간단하게
에러처리 같은건 다 빼버리고 엑기스만 적어봤습니다.
함수들만 쭉 사용해서 보면 대충 어떻게 작동되는지 알 수 있을 것입니다. 주석도 나름 달아봤습니다.
먼저 CreateService를 사용해 서비스를 등록시킵니다. 이 때 5번째 인자로 SERVICE_DEMAND_START이 값을 사용했는데
저 값을 사용하면 StartService함수를 사용해야만 드라이버 시작됩니다. 

그리고 StartService함수를 사용하면 DriverEntry함수부터 시작이 되게 됩니다.
그리고 ControlService함수 인자로 SERVICE_CONTROL_STOP를 주게되면 해당 드라이버의 Unload함수가 호출됩니다.
이 값은 DriverEntry에서 등록을 시켜주었겠지요.

그리고 드라이버를 제거해주는 것 까지 보여주는 소스코드입니다.

실제로 사용할 땐 적절한 에러처리코드 또한 넣어서 사용해야겠지요.