Security - Real World/Reversing

Windows Kernel Hooking - Part 3

PS리버싱마크해커박종휘TV 2024. 12. 6. 21:50

오랜만에 올리는 Tech blog이다. 많은 기술적 내용은 나의 개인 웹사이트에 올려져 있고, 티스토리의 용도를 V-log 용도로 사용하려고 했다. 하지만 그렇게 하자니 나의 개인 생활 내에서 언급할 부분이 너무 없고 이에 따라 블로그가 허전하다는 느낌이 들어서 기술적 내용을 담은 포스트를 게시하기로 하였다.

 

신기한 후킹을 몇 개 많이 배웠다. 이를 응용하기로 하자.

Hypervisor

Windows 내 NT 함수를 인라인 후킹하거나, 전역 구조체, 특정 MSR 레지스터 및 커널 스택을 변조하는 것은 Windows 자체에서 Kernel Patch Protector (KPP, a.k.a. PatchGuard, PG)를 동원하여 금지하고 있다. 이 KPP를 우회하는 매커니즘을 만드는 행위는 위법에 준하므로, 안티 치트에서 이를 행하지 않는다. 이는 물론 우리에게도 마찬가지이지만, 법적 문제에 엮일 일이 없으므로 이 KPP를 기술적으로 우회해도 별 일이 거의 없다. 하지만 KPP 자체의 보안 매커니즘은 상상을 초래할 만큼 강력하기 때문에, 이를 부적절한 방법으로 우회하는 방법은 생각하지 않는 것이 좋다.

 

대신, 합법적으로 우회하는 방법인 Hypervisor를 소개한다. Hypervisor를 이용한 EPT Hooking으로 NT 함수에 후킹을 걸 수 있고, 이 외 다양하게 HV에서 지원하는 매커니즘을 이용해서 기존에 PG에서 제한하는 행위를 할 수 있다.

 

물론, 몇 가지 DKOM은 작동하지 않는다. 이를 알아보도록 하자.

 

여기서 소개하는 것은 모두 Intel HV, VT-x이다! AMD HV에 대한 내용은 나중에 글을 하나 더 올릴게요.

EPT Hooking - Basic

EPT Hooking으로 NT 함수를 후킹하는 방법을 소개하겠다. 기본 아이디어는 기존 페이지 (PML4 Entry)를 FakePageRealPage로 분할하는 것이다. 우리는 RealPage를 수정할 (후킹할) 것이다. 이 때 FakePage에 R/W 권한을 주고, RealPage에 X 권한을 주게 되면, 해당 주소를 읽었을 때 RealPage는 R/W 권한이 없으므로 EPT Violation VM Exit이 일어나게 된다. 이때 이를 적절하게 핸들링하여 해당 페이지를 FakePage로 교체한다. 그렇게 되면 PG는 물론 모든 드라이버는 Read할 때 FakePage를 참조하지만 막상 실행하면 RealPage를 실행하는 셈이 된다. 이를 통해서 NT 함수, 나아가 어떤 함수를 스텔스-하게 후킹할 수 있다.

 

* FakePage RealPage
권한 Read, Write Execute
수정 여부 None Inline Hooking 됨
비고 해당 페이지에서 실행할 때 EPT Violation 발생 해당 페이지를 읽거나 쓸 때 EPT Violation 발생

 

한계는, 어떤 코드를 '실행'하는 것을 훔치는 '후킹'이기 때문에, 그냥 Raw하게 DKOM할 때는 먹히지 않는다.

SSDT Hooking - EPT (Not recommended)

엄청 무식한 방법으로 SSDT를 EPT를 후킹할 수 있다. Windows syscall table의 모든 함수에 EPT Hooking을 걸어 감지해보자. 단, 오버헤드가 매우 클 것이다. CPU가 쓰레드리퍼 정도면 한번씩 시도해 보자.

SSDT Hooking

다른 오버헤드가 큰 방법으로 EFER를 이용하는 방법이 있다. Intel SDM에 따르면 다음과 같이 SYSCALL이 진행된다.

IF (CS.L ≠ 1 ) or (IA32_EFER.LMA ≠ 1) or (IA32_EFER.SCE ≠ 1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
  THEN #UD;
FI;
RCX ← RIP; (* Will contain address of next instruction *)
RIP ← IA32_LSTAR;
R11 ← RFLAGS;
RFLAGS ← RFLAGS AND NOT(IA32_FMASK);
CS.Selector ← IA32_STAR[47:32] AND FFFCH (* Operating system provides CS; RPL forced to 0 *)
(* Set rest of CS to a fixed value *)
CS.Base ← 0; (* Flat segment *)
CS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *)
CS.Type ← 11; (* Execute/read code, accessed *)
CS.S ← 1;
CS.DPL ← 0;
CS.P ← 1;
CS.L ← 1; (* Entry is to 64-bit mode *)
CS.D ← 0; (* Required if CS.L = 1 *)
CS.G ← 1; (* 4-KByte granularity *)
CPL ← 0;
SS.Selector ← IA32_STAR[47:32] + 8; (* SS just above CS *)
(* Set rest of SS to a fixed value *)
SS.Base ← 0; (* Flat segment *)
SS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *)
SS.Type ← 3; (* Read/write data, accessed *)
SS.S ← 1;
SS.DPL ← 0;
SS.P ← 1;
SS.B ← 1; (* 32-bit stack segment *)
SS.G ← 1; (* 4-KByte granularity *)

 

이때 보면 가장 먼저 EFER MSR과 비교하여 UD를 발생시키는 것을 볼 수 있는데, EFER MSR를 끄고 UD를 핸들링하여 syscall을 에뮬레이팅을 하는 방법이다. 최적화 시킬 경우 오버헤드가 비교적 적다는 장점이 있다.

Stealth Page

몇몇 안티치트는 페이지를 Walking하면서 만약 수상한 곳에 MZ 헤더나, Opcode들이 있다면 (메뉴얼 매핑된 파일이 있다면) 밴을 시킨다. 이를 방지하기 위해 .text 섹션 내 R/W 권한을 삭제하는 조치를 취할 수 있다. 단, 이렇게 할 경우 .text 섹션 내 데이터를 수정하기 힘들다는 점이 있다. (가능하긴 하다)

 

이때 돌아가는 쓰레드까지 숨길 수는 없으므로 PsLookupThreadByThreadId 와 같은 함수를 후킹해서 막을 수 있다. 사실 이렇게 해도 NMI (BUG) 로 CPU 클럭을 정지시켜 순간의 덤프를 뜰 수 있는데 이 과정에서 모든 CPU의 RIP를 대조 비교하는 방법에 막힌다. 물론 이때 NMI Handler를 오버라이딩하는 방법으로 파훼 가능하다. (이 또한 EPT Hooking)

고찰

사실 이렇게 HV를 사용할 수만 있으면 손쉽게 Bypass가 가능하다! 하지만 HV는 그 자체로 감지 벡터가 너~무나 많다는 점이 문제이다. 따라서 HV를 사용하는 해커들 사이에서는 두 가지 파(派)가 존재한다.

  • HV를 리버싱의 툴로써 쓰지만, 실제 Bypass에는 이용하지 않고 Bypass를 할 때는 일반 커널 모듈을 쓰는 파 (커널파)
    • 이유는 간단하다. 오버헤드가 너무 크기 때문이다. HV는 그 자체로 오버헤드가 너무 크다. 차라리 HV를 이용한 고급 수준의 리버싱을 행한 다음 그것을 바탕으로 Bypass 모듈을 작성하는 것이다.
    • 이에 바탕이 되는 정리는, '상대가 나를 모르고 나는 상대를 알 때' 우위를 점한다는 것이다.
    • 대표적으로 iPower, Rake 등이 여기에 속한다.
    • 또한 이들의 말로는 HV를 사용해서 Bypass를 하는 건 Elite Hacker가 아니라고 한다.
  • HV를 Bypass의 용도로 쓰는 파 (HV파)
    • 이 또한 이유가 간단하다. HV Detection Vector를 모두 제거한다면 그 자체로 아주 강력한 툴이 되기 때문이다. 몸이 편하다.
    • 이에 바탕이 되는 정리는, '내가 상대가 못하는 것을 할 수 있을 때' 우위를 점한다는 것이다.
    • 많은 사람들이 여기에 속한다.
    • 이들은 솔직히 말하면 리버싱 실력이 부족한 것이다. 그래서 편해지려고 하는 것 같다.

이 두 가지 파의 대립은 Internal Cheat vs. External Cheat 급으로 생각하면 편하다.

 

나는 커널파에 속한다. 이유는 간단하다. 나는 리버싱을 할 수 있기 때문이다. 그리고 더 재밌고, 더 안정적이다. 하지만 HV파 또한 존중한다. 나는 한다면 HV를 써서 리버싱을 할 것이고 HV를 써서 Bypass를 할 것 같다. 동등한 권한에서 안티 치트의 동작을 막는 건 Windows 플랫폼이라는 제약 때문에 살짝 힘들기 때문이다.

 

하지만 그래도 나 또한 결국에는 커널 레벨에서 한 번 안티 치트를 Bypass해보고 싶긴 하다 ^_^

결론

이를 행하기 위해 요즘에 나를 위한 Rootkit을 제작 중에 있다. 나만을 위한 루트킷을 제작하면 내가 원하는 동작을 할 때 더 편할 것 같아서이다. 배포할 예정은 없다.

'Security - Real World > Reversing' 카테고리의 다른 글

Windows Kernel Hooking - II-1  (1) 2024.01.10
Windows Kernel Hooking - II  (1) 2024.01.08
Windows Kernel Hooking - I-1  (1) 2024.01.06
Windows Kernel Hooking - I  (2) 2024.01.04