DKOM?
- Direct Kernel Object Manipulation
- 직접 커널 객체 조작
- 커널 오브젝트 - 프로세스, 드라이버, 파일등에 대한 정보를 조작하는 기법
- 함수 테이블이나 Native API 등에 대한 hooking등을 거치지 않고 커널 오브젝트를 직접적으로 조작한다.
- 프로세스 리스트를 받아오는 원리가 EPROCESS 구조체에 들어있는 멤버들에 의존적임
- 따라서 객체 멤버를 조작하는 방식으로 다른 부분에 영향을 미치지 않고 조작가능
- 오브젝트의 구조와 그 구조체 속 멤버등의 전체 구조, 역할 등을 완벽하게 파악하고 이해해야함.
- 업데이트시 커널 버전에 따른 변화와 그 영향을 인지하고 있어야함.
PEB - Process Environment Block
- User Mode 에서 프로세스 정보를 가짐
- 각 프로세스마다 PEB가 존재하고, 각 쓰레드별로 TEB를 가짐
- 커널에 있는 정보를 가져와 유저모드 메모리에 프로세스 구조체를 저장한다. → 간접적인 접근
- 유저모드에서 프로세스 정보를 조회할 때 마다 커널에 접근해야 한다면 상대적으로 시간이 오래 걸리게 되니, 유저모드 메모리에 해당 데이터를 복사해와 저장한 개념.
EPROCESS
- Kernel Mode의 프로세스 정보를 가지고 있는 구조체
- 원본 데이터에 직접적으로 접근하여 정보 가져옴
원리
- EPROCESS 구조체 내의 이중 연결리스트로 프로세스 리스트가 구현됨
- LIST_ENTRY 구조체
- FLINK : 앞 구조체를 가리킴
- BLINK : 뒤 구조체를 가리킴
- 변조하고자 하는 프로세스 앞 프로세스의 BLINK와 뒤 프로세스의 FLINK를 조작하여 서로 연결해주면 가운데 프로세스가 없는것 처럼 여겨지게 된다.
디버깅
- windbg에서 프로세스 이름으로 정보 찾고 EPROCESS 구조체 내용물 확인
- !process 1774 로 notepad.exe 정보 확인
- dt_EPROCESS ffff9b8f9b6da0c0로 EPROCESS 구조 확인
- ActiveProcessLinks 멤버에 LIST_ENTRY가 있고, FLINK와 BLINK 정보가 확인된다.
- notepad.exe 전후 프로세스인 msedge.exe 와 TextInputHost.exe 정보 확인
- msedge.exe : ffff9b8f9b37a080
- PID : 0xcfc ( → notepad.exe)
- 주소 + 0x448 = FFFF9B8F'9B37A4C8
- TextInputHost.exe :
- PID : 0x1c74 ( ← notepad.exe)
- 주소 + 0x448 = FFFF9B8F`9B6BB4C8
- notepad.exe 의 ActiveProcessLinks 속 멤버에서 앞 뒤 프로세스에 유효하게 접근할 수 있음.
- 두 프로세스 모두 ActiveProcessLinks 멤버로 0xffff9b8f`9b6da508을 가지고 있는데, 이는 notepad.exe 의 ActiveProcesssLinks(+0x448) 주소값임.
- msedge.exe 가 두번째 멤버가 됨. (BLINK)
- TextInputHost.exe 가 첫 번째 멤버가 됨.(FLINK)
- msedge.exe : ffff9b8f9b37a080
목표
- 작업관리자에서 유저모드 어플리케이션 notepad.exe 를 숨기자.
- 숨기고자 하는 PID 입력해서 넘기고 숨겨보기
- EProcess의 ActiveProcesssLinks를 고정 오프셋이 아닌, 다른 함수의 실행 결과로 가져올 수 있으면 제일 좋음
- EProcess.UniqueProcessId + 8로 추정
- EProcess.UniqueProcessId는 SYSTEM_PROCESS_INFORMATION의 멤버이기도 함.
- SYSTEM_PROCESS_INFORMATION.UniqueProcessId + 8 위치 값을 가져와 출력해서 ActiveProcessLinks 와 동일한지 비교해보자.
- 프로세스별 ActiveProcessLinks 오프셋에 접근
- +0이 FLINK로, 해당 위치를 따라가 대상 프로세스의 BLINK(+0x8)를 BLINK에 있는 값으로 바꿔줘야함
- +8이 BLINK로 , 해당 위치를 따라가 대상 프로세스의 FLINK(+0x8)을 FLINK에 있는 값으로 바꿔줘야함.
- 원본 process의 flink와 blink는 커널 오류 방지를 위해 서로가 서로를 가리키게 해줌.
실습
- PID 입력하여 ActiveProcessLinks 오프셋에 접근하도록 드라이버에 요청하는 유저모드 프로그램 작성
- SYSTEM_PROCESS_INFORMATION→UniqueProcessId 로는 제대로 접근 불가
-
+0x440이면 FFFF9B8F9B6DA500 가 나와야함.
- EPROCESS 찾아오는 함수 필요
- PsLookupProcessByProcessId() 함수
- pid를 인자로 EPROCESS 객체 반환
- MmGetSystemRoutineAddress() 함수 사용해 ntoskrnl에서 가져오자.
- PsLookupProcessByProcessId() 함수
- 찾아온 EPROCESS 객체에서 UniqueProcessId의 오프셋을 찾아야함.
- PsGetProcessId() 함수
- EPROCESS를 인자로 받아 0x440 위치에 있는 UniqueProcessId를 반환하는 함수
- 어셈블리에 오프셋 바이트가 박혀있음 → 함수 주소 직접 참조해서 오프셋 바이트 읽어올 수 있어보임.
- 함수 시작주소 + 3바이트부터 2바이트 읽어오면 오프셋 나옴
- MmGetSystemRoutineAddress() 함수 사용해 ntoskrnl에서 시작주소 가져오자.
- 1088 = 0x440
- 오프셋 성공적으로 가져왔다.
- 가져온 오프셋과 EPROCESS 구조체 시작주소 더하면 PID 위치에 접근할 수 있다.
- 3bc = 956 으로 PID 제대로 갖고온게 확인된다.
- 구해온 오프셋에 8바이트 더하면 ActiveProcessLinks 에 접근할 수 있다.
- 근데 왜 이렇게 했었지..? 그냥 EPROCESS→ActiveProcessLinks로 갖고올수 있었을텐데
- 아무튼 진행, FLINK와 BLINK에 제대로 접근 되는것 확인됨.
- Flink를 따라가 BLINK(+0x8)를 BLINK에 있는 값으로 변경
- Blink를 따라가 FLINK(+0x0)을 FLINK에 있는 값으로 변경
- 드라이버 DKOM 코드 실행
- 전 후 프로세스들의 FLINK,BLINK가 가리키는 주소 체크
- svchost.exe
- TextInputHost.exe
- ~F144C8이 notepad.exe의 ActiveProcessLinks 주소
- ffff9b8f9af14080+448 = FFFF9B8F9AF144C8
- 실행 후
- svchost.exe
- ActiveProcessLinks : FFFF9B8F9AE664C8
- TextInputHost.exe
- ActiveProcessLinks : FFFF9B8F99C444C8
- FFFF9B8F9AF144C8 이 아닌 각기 서로의 ActiveProcessLinks를 가리키는게 확인됨 .
- svchost.exe
- 전 후 프로세스들의 FLINK,BLINK가 가리키는 주소 체크
ProcessHide 완료
Github
https://github.com/synod2/Kernel_Hooking/tree/main/Window Device Driver/MyDriver5
참고 문서
https://ko.wikipedia.org/wiki/직접_커널_객체_조작 - 위키백과 DKOM
https://1828.tistory.com/entry/Rootkit-DKOM-Direct-Kernel-Object-Manipulation을-이용한-프로세스-은닉 - DKOM
https://ghdwn0217.tistory.com/64 - PEB,EPROCESS,KPROCESS
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ps/eprocess/index.htm EPROCESS 구조체