커널모드가 왜 안전한지를 모르겠습니다

coleea의 이미지

커널모드라는게, 유저모드에서는 실행불가능한 명령어를 전부 다 언락해서 모든 명령어가 실행가능하다고 알고있는데요, 도대체 어떻게 시스템 이상으로부터 컴퓨터를 보호하는지를 모르겠습니다. 예를 들어, 어떤 중요한 메모리 주소의 값을 악의적으로 조작해서 블루스크린을 띄우게 하는 명령어가 있다고 하면 이건 절대로 실행되서는 안되는 것일 텐데, 이런 명령어를 실행하기 위해 커널모드로 스위치해서 이 명령어가 execute된다면 어떻게 시스템을 보호한다는 건지 모르겠습니다. 이 명령어가 안전한지를 검증하고, 만일 안전하다면 실행하는 방식인가요? 만일 그런 거라면 구체적으로 어떻게 검증하나요? 그리고 유저모드에서도 그렇게 안전을 검증한 후에 괜찮다고 판단하면 그 때 실행하면 될 텐데 왜 굳이 커널모드로 전환해야 하는건가요??

익명 사용자의 이미지

가상 주소 공간으로 프로세스마다 메모리 공간을 완전히 독립시키는 건 일단 둘째치고,
유저모드에서의 어떤 코드 실행만으로 블루스크린(커널 패닉)은 절대로 뜨지 않습니다.

여기서 "절대로" 라는 건 정말 완전히 불가능한 것이라기 보다, 커널 취약점 등이 아닌 이상은
불가능하다는 의미입니다. 즉 시스템이 죽을 수 있는 중요한 처리는 모두 시스템 콜을 통해
인자만 전달하고 결과만 받는 방식으로 되어있지, 직접 처리할 수 없게 되어 있다는 얘기입니다.

단순히 한 프로세스에서 자기 메모리 주소 값 어떤 것을 조작한다고 해서 커널 패닉이 뜨던가요?
궁금하시면 직접 리눅스나 윈도를 설치하시고 유저모드에서 특정 명령어 실행으로 커널 패닉을 띄워보시면 됩니다.
커널 익스플로잇이 아닌 이상 그런 것은 원칙적으로 불가능합니다.

물론 추후 문제가 될 수 있는 부분들에 대해서는 OS 자체적으로 Privilege 시스템을 두고 보호합니다.
가령 아무나 커널 모드에서 동작하는 드라이버를 마구 올릴 수 있게 한다면 당연히 개나소나 시스템을 죽일 수 있을 것입니다.
따라서 이런 부분은 특수한 관리자 권한을 가졌거나 할 때만 가능하도록 하는 등의 처리가 OS 레벨에서 보통 되어 있습니다.
어차피 여기서 얘기하는 이런 것은 유저모드/커널모드 에서의 보호를 의미하는 것은 아닙니다.

커널 모드로 스위치한다고 해서 유저 프로세스가 원하는 코드를 실행시킬 수 있는 것은 아니죠?
스위칭 후의 처리는 커널 코드에 있지 유저 코드에 있는 것이 아닙니다. 정해진 코드만 수행할 뿐이죠.

coleea의 이미지

그러면 커널모드와 유저모드의 차이가 (1) 접근가능한 메모리 영역에 차이가 있는것이고 (2) 커널모드라고 하더라도 커널이 상주된 메모리 어드레스의 값을 임의로 조작할 수 있는 게 아니라 시스템 콜을 호출할 때 인자를 주고 리턴값을 주는 방식으로만 간접적으로 이용이 가능하다는 거죠? 그리고 'Privilege 시스템?'은 뭔지 잘 모르겠지만 시스템 콜을 실행할 때 인자를 잘못된 값을 건내줘서 커널이 문제가 생길 수 있는 상황에 대비해서 커널 내부에 보호 기능을 구현한 알고리즘 같은 거로 봐도 되나요?

익명 사용자의 이미지

접근 가능한 메모리 영역도 물론 차이가 있습니다. 커널 모드에선 일단 모든 메모리에 액세스가 가능하니까요.
물론, 그렇다고 일반적인 상황에서 커널이 물리 메모리 주소에 바로 액세스한다는 얘기는 아닙니다.
보통 운영체제는 대부분 페이징을 하고, 리눅스라면 pgd pte 등의 항목이 있는데 한 마디로 현재 태스크의
페이지 테이블은 모두 따로 존재하고 유저모드에서 시스템 콜 호출로 커널 모드로 스위칭이 되었다고 해도
현재 태스크에 대한 정보는 그대로 가지고 있습니다. 즉 어느 프로세스에서 시스템 콜을 호출했냐에 따라서 다르다는 얘기입니다.

이 말은 리눅스에서 커널 모드라고 해도 결국 페이지 테이블을 통한 가상 주소 -> 물리 주소 변환 후 액세스를 한다는 얘기입니다.
이는 경우에 따라 오해가 있을 수 있는 얘기이지만 보통의 상황에선 그렇습니다. 또한 커널 모드에서는 모든 메모리에 접근이 가능하단 얘기와
유저 모드에서는 커널 메모리에 접근을 못하도록 보호한다는 얘기는 여러 가지 의미가 있을 수 있는데, 당연한 얘기지만 일단 유저 모드에서
만약 c0000000 부터가 커널 영역 가상 주소라고 치면 *(char *)0xc0000000 = 'a'; 와 같은 명령을 실행했다간 바로 Segmentation fault 입니다.
왜냐면 일단 현재 유저 모드 태스크의 페이지 테이블에 c0000000 에 대한 정보는 없기 때문입니다.

이 때, 커널 모드로 스위칭해서 커널 코드 중 0xc0000000 에 액세스한다고 해서 paging error 가 나진 않습니다. 커널 모드에서는
이에 대한 페이지 정보를 글로벌하게 갖고 있기 때문입니다. 물론 커널 모드에서 현재 태스크(프로세스)의 가상 주소에 접근해서 값을 바꿀 수도 있습니다.
보통 리눅스 커널 코드를 보시면 copy_from_user 나 copy_to_user 등이 있는데 이게 어떻게 되어 있는지 잘 보시면 됩니다.

그리고, 이런 생각을 할 수 있습니다. "그럼 시스템 콜 중 만약 write/read 같은 시스템 콜의 두 번째 인자(주소)에 0xc0000000 이상의 주소를 넘겨서
커널이 직접 그곳에 쓰게 하면 유저 모드에서도 그 주소에 값을 간접적으로 읽고 쓸 수 있는 것 아니냐" 라고 할 수 있습니다.
당연히 이론적으로는 그렇게 할 떄 특별히 문제되는 것은 없습니다.

그래서 리눅스에서는 최신 리눅스 커널로 치면 thread_info 인가 task_struct 구조체의 addr_limit 같은 항목을 두고,
모든 시스템 콜 핸들러에서 유저 모드에서 받는 주소에 대해선 전부 addr_limit 주소보다 큰지 작은지에 대해서 체크를 합니다.
그리고 만약 크다면 바로 에러를 내뱉고 리턴하는 것이 직접 커널 모드로 짜여 있습니다.

이는 일종의 소프트웨어 레벨에서의 보호라고 할 수 있습니다. 앞에서 얘기한 것과는 조금 성격이 다르다고 할 수 있겠지요.

커널 모드에 대해 어떤 생각을 하시는지 모르겠지만, 기본적으로 리눅스로 치면 모듈과 같은 것을 직접 작성해서 올리는 경우가 아니라면
커널이 수행하는 코드는 당연히 처음부터 정해져 있습니다.(설치하신 리눅스 커널 코드 그대로 행동하겠지요) 유저가 커널 모드에서 임의로 어떤
코드를 실행하고 말고의 문제가 아니고 그런 게 가능하지도 않습니다.

Privilege 시스템은 별 게 아니라(그냥 대충 제가 쓴 용어입니다), 리눅스로 치면 권한 시스템들입니다. root 와 같은 특별한 관리자 계정이 따로 있는 이유가
일반적인 유저가 할 수 있는 일과 관리자가 할 수 있는 일을 나누기 위해서이고, 이것이 예로 들어서 위에서 언급한 모듈과 같은 것을 올리는 게 있을 수 있습니다.
아무 유저나 커널 모듈을 동적으로 막 올릴 수 있다면 어떻게 될지는 아실 테니 굳이 언급하지 않겠습니다.
이런 부분 역시 일종의 소프트웨어(커널) 레벨에서의 보호 기법 중 하나라고 할 수 있는 것입니다.

그리고 말씀하신 것 처럼 시스템 콜을 실행할 때 잘못된 인자를 건네줘서 문제가 생길 수 있는 상황에 대비한 보호 기능은 말 하자면
커널 익스플로잇 등을 방어하기 위한 기법을 말씀하시는 것인데, 아쉽게도 특별히 그러한 것이 그렇게 많지 않습니다.
일단 커널 취약점은 아무래도 유저 레벨에서의 취약점에 비해 방어하기가 어렵습니다. 커널 모드 자체가 더 로우 레벨이 없기 때문에
이를 보호해 줄 상단 영역 코드 같은 것을 만들 수 없습니다. 따라서 대부분 특별히 보호 기법이 있지는 않고, 많은 방어 기능들은 모두
유저 레벨에서의 취약점 방어에 치중해 있습니다.(최근으로 치면 seccomp 같은 샌드박스 용도의 시스템 콜 등이 있겠습니다.)

그러나 간단히 그나마 역사적으로 커널 취약점에 대해 뭔가 방어하기 위한 수정을 한 적이 있기는 있는데,(물론 여기서 말하는건 다 리눅스 얘기입니다.)
과거 Null pointer dereference 라는 취약점 형태가 한창 유행했을 때가 있습니다. 이 형태의 공격법이 공개된 이후 이러한 형태를 갖고 있던
취약점들이 전부 익스플로잇에 성공하게 되었습니다. 간단히 말해서 커널 상에서 어떠한 이유로(물론 보통 근본 원인은 시스템 콜 인자 조작) 널 포인터를
액세스하거나, 또는 아예 그냥 노골적으로 널 포인터를 함수로 호출하는 형태가 만들어졌다고 할 때, 이전에 유저 모드 프로세스에서 미리 mmap 등을
통해 0번 주소에 메모리를 할당해놓고 아예 바로 코드를 넣던지, 아니면 단순 널 포인터 액세스라면 어떤 값을 넣어서 이후 트리거가 가능하도록 하는 방식입니다.

추후 이런 형태의 취약점을 방어하기 위해 현재는 유저 모드에서 mmap 을 통해 0번 주소에 메모리를 할당하는 것은 금지되었습니다.(0x0 부터 1페이지 단위까지)
이 형태의 취약점은 리눅스 뿐 아니라 다른 운영체제에도 흔히 발견되었으며 Mac OS X 에서도 아주 자주 볼 수 있습니다.

이러한 것이 어떻게 보면 커널 익스플로잇을 막기 위한 일종의 보호 기법 적용의 한 예라고 볼 수 있습니다. 물론 단순히 회피하기 위해 커널을 조금 수정한 것 뿐이지
어떤 "보호 기능" 을 넣었다고 하기엔 좀 무리가 있긴 합니다만 일단 이런 것이 굳이 말하자면 커널 익스플로잇 보호의 예 입니다.
사실 대부분의 커널 모드 익스플로잇은 커널의 시스템 콜 쪽 보다는 드라이버 쪽이 훨씬 더 많습니다만.

coleea의 이미지

먼저 매우 길고 성의있는 답변을 해 주신 데 감사드립니다. 이 질문은 작년에 OS 학부수업을 들으면서 커널모드 SWITCHING을 왜 하는지 납득할 한 이유를 찾지 못해서 올린 질문인데 제 예상을 뛰어넘는 전문적인 답변글에 이해를 하지 못하고 결국 이해를 미뤄두었던 기억이 납니다. 1년이 지난 지금 읽어보니 작성해 주신 글의 반 정도는 이해가 되어 그동안 쌓은 내공이 헛되진 않았다는 안도가 듭니다(하지만 여전히 어려운 글이군요). 커널에 대한 이해가 깊으신 것 같은데 리눅스 커널관련 시스템 프로그래머 일을 하실 거란 생각이 듭니다. 저도 리눅스 커널을 다루는 시스템 프로그래머로 진로를 염두하고 있는데 커널 소스코드 이해 및 디버깅 관련하여 대단한 스트레스를 받을 거라고 생각되어 주저하고 있습니다(일을 하면서 많은 스트레스를 받은 업종은 되도록 피하고 싶었습니다). 혹시 이 글을 보신다면 현직에서 필요로 하는 리눅스 커널 관련 지식이 무엇인지, 어떤 식으로 공부를 하셨는지, 디버깅 작업은 순조로운지 등을 여쭙고 싶습니다

다시 한번 정성어린 답변에 깊은 감사드립니다

hxploit의 이미지

오랫동안 kldp 안들어오다가 우연히 들어와봤는데, 제가 썼던 답글이
위로 올라와있어서 놀랐네요. 그 사이에 익명 글쓰기도 없어진..듯 하네요.

제가 썼던 답변 뒤에 보안 관련 내용이 들어간 건 우연이 아니라 보안 분야를 공부하고,
그쪽으로 일하고 있기 때문입니다. 그래서 시스템 프로그래머라고 하긴 좀 어렵겠네요.
어차피 커널 분석도 해야 하고, 알아야 해서 공부는 지속적으로 하고 있습니다.
정말로 제대로 커널을 만지는 쪽에서 일하지는 않기 때문에 물어보신 부분에 대한 답변을
드리기는 조금 어려울 것 같습니다.

저도 그쪽은 잘 모르지만, 어차피 커널 관련으로 국한시킨다면 하는 일은 대체로
한정되어 있을 것입니다. 업체에서 개발한 특정 하드웨어를 타깃으로 해서, 거기서 동작할
커널을 직접 커스터마이징하고 필요하면 드라이버 개발을 하는 등의 일을 할 것입니다.

분야는 약간 상이할지 모르겠지만, 조금 현실감 있게 실제 예를 들어서 말씀드리면
삼성에서 (어쩔 수 없이) 공개하고 있는 자사 기기의 안드로이드 소스 코드를 받아서 보시면
drivers 디렉토리에서 잘(..) 뒤져보시다보면 해당 기기에서 사용하는 모니터라든가 기타
몇몇 하드웨어에 대한 드라이버 소스 코드가 추가되어 있는 것을 보실 수 있습니다.
제 기억에는 주석으로 해당 코드의 개발자명도 적혀 있던 걸로 기억합니다.

펌웨어 개발에 가까운 쪽이면 이런 일을 하게 될 것입니다. 원하시는 분야가 이 쪽인진 모르겠습니다만
이런 쪽은 단순히 특정 운영체제의 커널에 대해서만 안다고 되는 건 아니고, 어떤 OS 에서든
일단 드라이버 개발에 있어서 필요한 개념들을 알고 있어야 합니다. 필요에 따라서는 직접 데이터시트 읽고
혼자서 다 구현해야 할 수도 있겠죠. 뭐 업체들이 분업을 어떻게 하고 있는진 모르겠습니다.

저는 어디까지나 제가 필요하다고 생각하는 부분까지만 커널을 공부하기 때문에, 원하시는 것과는
상이할 수 있습니다. 그러니 너무 귀담아 듣지는 마시고, 단순히 물어보신 것에 답변하자면
공부는 기본적으로 OS 의 이론적인 부분은 어느 정도 먼저 선행되있다는 가정 하에 몇몇
커널의 코드 일부분을 직접적으로 잘 설명해주는 책이나 사이트 등을 참조하고, Cross Reference 사이트를
통해서 직접 분석합니다. 디버깅은 보통 원하는 커널을 컴파일해서 qemu 로 올리고, cgdb 를 이용해서
반(半) 소스레벨 디버깅을 하고 있습니다. 더 편한 방법도 있겠지만 저 개인적으로는 이것만으로도
분석에는 별로 지장은 없다고 생각하고 있습니다.

익명 사용자의 이미지

유저모드에서 커널모드의 메모리로 접근할 수 없습니다. 해서, 일반 유저모드에서 실행되는 모든 어플리케이션이 커널의 메모리 영역을 수정할 수가 없지요.
만약 프로세서의 보호모드가 없어서 커널메모리의 접근이 가능하다면, 커널의 데이터를 조작해서 특정파일을 삭제 할 수 없도록 조작하거나, 프로세스를 은닉하거나 각종 악성 행위를 할 수 있는겁니다.

모든 Win32 API는 마지막에 native API를 호출하여 커널에 진입합니다.

전달된 유저 어플리케이션의 요청이 드라이버에게 전달되고 드라이버는 다시 커널에게 작업을 요청합니다.
(사실 드라이버는 커널 레벨에서 동작하는 프로그램이라 요청이고 자시고 자기 멋대로 조작이 가능합니다. 이런 커널조작 목적의 드라이버 프로그램을 루트킷이라고 부릅니다.)

간단하게 정리하면 일반 유저가 개발한 어플이 OS에 치명적인 공격이나 악성 행위를 할 수 없도록 보호하는 기술입니다.

물론, 우회하는 방법도 있으니 그다지 효과적이지는 않습니다.
(디바이스 드라이버는 커널 레벨에서 동작하니까요.)

coleea의 이미지

리눅스 커널 관련해서 공부를 하고 있었는데 윈도우즈의 커널 작동방식에 대해서도 이해하게 되었습니다.
시간 내 주셔서 답변해 주신데 대해 깊은 감사드립니다

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.