Windows 에서 시스템콜테이블(SSDT) 후킹을 통해 파일정보를 숨기는 루트킷 드라이버를 만들었다.
먼저 WDK 를 설치해서 Windows7 x86 checked Build Environment 를 만들고 작업 폴더에
DriverEntry 가 정의된 C 소스파일, SOURCES 파일, makefile 을 아래와 같이 준비한다.
파일들은 첨부되어있다.
c 소스파일에는 DriverEntry 가 아래와같이 정의되어있다
처음에 CR0 레지스터 값을 출력해주고, PDRIVER_OBJECT 객체의 DriverUnload 함수포인터를
설정해준다. 그리고 CR0 의 WP 비트를 클리어한다음 SSDT Hooking 함수를 실행하고 다시 WP 비트를
세트 해준다. 그런데 사실 WP 비트가 셋 되있는 상태로도 SSDT 후킹이 잘 됬었다. Windows7 에서 SSDT 가
쓰기방지 안되어있을리 없을거같은데 원인은 모르겠다. 설마 쓰기 활성화가 되어있나...
아무튼 SSDT 후킹은 SSDT 시작주소를 찾아서 원하는 엔트리의 함수포인터를 수정하는 것인데
일단 SSDT 의 시작주소는 커널심볼이 존재하기때문에 특별히 찾기위한 스킬은 필요없고,
KeServiceDescriptorTable 이라는 심볼만 dllimport 로 임포트 해주면 된다
실제 후킹은 SYSTEMSERVICE(_name) 이라는 매크로를 통해서 이루어지는데
이 매크로의 _name 부분에 원하는 시스템 함수 (ZwQueryDirectoryFile 등과 같은) 의 이름을 주면
해당 함수의 시작주소+1 의 메모리주소에서 4바이트를 가져오고 그것을 SSDT index 번호로
이용해서 그 위치에 원하는 함수 포인터의 주소를 덮어쓴다.
Zw... 함수는 시스템 서비스 래퍼함수이기 때문에 가장 첫 명령이 SSDT 의 index 번호를 스택이나
레지스터에 로드하는 것이다. 그래서 기계어 명령어상에 이 index 번호가 하드코딩되있고, 이것을
SSDT 엔트리 번호로 이용한다. 물론 번호를 직접 알면 SSDT 엔트리 주소를 다르게 계산해도 된다.
아무튼, 후킹할 함수를 다음과 같이 정의한다.
여기서 함수의 프로토타입을 맞춰줘야 하는데 Microsoft MSDN 을 참조하면 된다.
WDK 콘솔을 아래와같이 실행시키고 작업 디렉토리에 가서 build 명령으로 컴파일 하면 sys 파일이 생성된다.
생성된 sys 파일을 InstDriver 유틸로 커널에 로드한다.
DbgView 유틸로 시스템 디버그 메시지를 보면 다음과 같은 내용들이 출력된다.
여기서 File Information number 라는것은 ZwQueryDirectoryFile 함수가 호출되었을때
FileInformationClass 멤버변수의 Enum 상수를 출력한 것인데, 2, 3번은 파일의 Access Time이나
디스크 용량에 대한 정보를 쿼리할때의 상수값이다. 이때 이 정보를 secret 디렉토리에 대해서
쿼리할때만 주지 않게 하기 위해서 FakeZwQueryDirectoryFile 의 첫 부분에서는
다음과 같이 전달된 파일 핸들로부터 파일 이름을 확인한다.
// find file name
ZwQueryInformationFile( FileHandle, &IoStatusBlock2, buffer, sizeof(buffer), FileNameInformation );
pfni = (FILE_NAME_INFORMATION*)buffer;
if( !CompareUni2Ansi(pfni->FileName, "\\secret") ){
DbgPrint("secret found!!\n");
//DbgPrint("FileInformation number : %d\n", FileInformationClass);
return STATUS_ACCESS_VIOLATION;
}
그리고 secret 이라는 파일이름이 확인되는 경우 STATUS_ACCESS_VIOLATION 을 리턴한다.
그렇지 않은 경우는 정상 함수를 다시 호출해준다.
또한 ZwCreateFile 을 후킹해서 다음과 같은 코드를 삽입하여 secret 디렉토리에 대한 CreateFile API 를 실패하게 한다
UNICODE_STRING FileName;
RtlInitUnicodeString(&FileName, L"\\??\\D:\\secret");
if( !RtlCompareUnicodeString(ObjectAttributes->ObjectName,&FileName, TRUE) ){
DbgPrint("Rootkit Blocked D:\\secret \n");
return (STATUS_OBJECT_NAME_INVALID);
}
결과적으로 secret 디렉토리를 윈도우 쉘 상에서 열려고 하면 CreateFile 이 실패하기 때문에 폴더를 열 수가 없으며
원래는 아래와 같이 내부에 PDF 파일이 들어있고
폴더 속성이 아래와같이 나타나지만
루트킷 드라이버가 로드된 이후로는 secret 파일의 파일 핸들이 전달될때 QueryDirectoryFile API가
실패하기 때문에 다음과 같이 폴더 속성도 나타나지 않는다.
'Programming' 카테고리의 다른 글
argv_input_test.c (0) | 2013.03.19 |
---|---|
shellcodetest.c (0) | 2013.03.19 |
x86 privilege level (0) | 2013.02.21 |
OpenSSL Server Example (0) | 2013.02.05 |
linux/windows SSL client example (0) | 2013.02.04 |