리눅스 커널, `/proc/self/mem`을 통해 메모리 보호를 우회한다!

by DD
3개월 전
조회수 8

`/proc/self/mem`을 사용하면 쓰기 불가 메모리 영역(Unwritable Memory)에 접근 가능하며, 이는 JIT 컴파일러 및 디버거에서 활용됨

x86-64 아키텍처(x86-64 Architecture)의 Write Protect (CR0.WP) 및 SMAP (CR4.SMAP) 설정을 통해 메모리 접근을 제어함

커널은 `get_user_pages_remote()`를 통해 물리적 메모리 프레임(Physical Memory Frame)을 찾고, `kmap()`으로 커널 공간에 매핑하여 접근 권한을 우회함

FOLL_FORCE 플래그(FOLL_FORCE Flag)를 사용하여 쓰기 불가 페이지에 대한 접근을 허용하며, `copy_to_user_page()`를 통해 실제 쓰기 연산을 수행함

/proc/self/mem의 '펀치 스루(Punch Through)' 동작 원리

본문은 `/proc/self/mem`을 통한 메모리 쓰기 동작이 쓰기 불가 영역(Unwritable Memory)에도 성공하는 '펀치 스루(Punch Through)' 특성을 설명한다. 특히, 커널은 `get_user_pages_remote()`를 통해 물리적 메모리 주소(Physical Address)를 획득하고, `kmap()`을 사용하여 커널 공간에 매핑함으로써 메모리 접근 권한(Memory Access Permissions)을 우회한다. 이러한 방식은 JIT 컴파일러 및 디버거에서 활용되며, 하드웨어의 메모리 보호 기법을 우회하는 핵심 원리로 작용한다.

x86-64 아키텍처의 메모리 보호 기법

x86-64 아키텍처는 Write Protect (CR0.WP)Supervisor Mode Access Prevention (SMAP) 설정을 통해 커널의 메모리 접근을 제어한다. 하지만, `/proc/*/mem` 구현은 이러한 제약을 우회한다. 커널은 `FOLL_FORCE` 플래그를 사용하여 페이지 테이블(Page Table) 검사를 무시하고, 물리적 메모리에 직접 접근한다. 이는 커널이 가상 메모리 시스템(Virtual Memory System)을 완벽하게 제어할 수 있기 때문이다.

get_user_pages_remote()와 FOLL_FORCE 플래그

커널은 `get_user_pages_remote()` 함수를 사용하여 가상 주소(Virtual Address)물리적 프레임(Physical Frame)으로 변환한다. 이 과정에서 `FOLL_FORCE` 플래그는 메모리 접근 권한 검사(Memory Access Permission Check)를 무시하도록 지시한다. 즉, 쓰기 불가 페이지에 대한 접근을 허용하여 '펀치 스루' 동작을 가능하게 한다. 이러한 메커니즘은 커널이 하드웨어 제약(Hardware Constraints)을 우회하는 핵심적인 방법 중 하나이다.

커널의 메모리 접근 과정: kmap()과 copy_to_user_page()

커널은 `kmap()` 함수를 통해 물리적 메모리 프레임을 커널의 가상 주소 공간(Virtual Address Space)에 매핑한다. 이 매핑은 쓰기 가능한 권한으로 설정되어, 커널이 메모리에 자유롭게 접근할 수 있게 한다. 마지막으로 `copy_to_user_page()` 함수를 사용하여 실제 쓰기 연산을 수행한다. 이러한 일련의 과정은 커널이 MMU(Memory Management Unit)의 제약을 우회하고, 메모리 보호 메커니즘(Memory Protection Mechanisms)을 무시하는 방식으로 작동함을 보여준다.

Linux Internals: How /proc/self/mem writes to unw