SEHOP는 SEH Chain의 Validation을 런타임에 검사하는 미티게이션으로, SafeSEH와는 달리 컴파일 시 별다른 옵션을 주지 않았더라도 OS단에서 검사하여 공격을 차단하고 응용 프로그램을 보호한다. Windows 7이나 Windows Vista의 경우 SEHOP를 지원하지만 일부 프로그램이 호환되지 않는 문제가 있어 자동으로 적용되지는 않고, 레지스트리를 편집하여 사용할 수 있도록 설정해 주어야 한다.


[regedit으로 레지스트리 수정]

1. 윈도우키 + R 또는 시작->실행을 열어 regedit 실행.

2. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel 로 이동.

3. DisableExceptionChainValidation 항목이 없을 경우 DWORD 값으로 새로 만들고 0으로 설정한다. 거꾸로 끄고 싶을 경우에는 1로 설정해주면 된다.

4. 재부팅. ( 바로 적용이 되는지는 모르겠지만 직접 해봤을 때는 재부팅해야 적용됐다. )


이 레지스트리 설정을 변경할 경우 일부 응용 프로그램이 비정상적으로 동작할 수 있으므로 조심해야 한다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

Tag SEHOP

댓글을 달아 주세요

Kali Linux 설치하기

Knowledge 2018. 2. 20. 10:21

 노트북에 Kali Linux를 설치하려고 평소에 하던대로 ISO 파일을 내려받고 설치하는 도중 'Failed to copy file from CD-ROM. Retry?'라는 메시지가 뜨고 계속 재시도해도 진행되지 않는 에러가 발생하여 여러 가지 방법을 시도해 보다가 힘들게 성공하여 기록으로 남기기 위해 작성한다.


1. Kali ISO 파일 다운로드

https://www.kali.org/downloads/


 위 URL에서 최신 버전을 다운받을 수 있으며, 인터넷 속도에 따라 다르지만 HTTP로 다운로드시 10분 내외로 전부 다운로드 받아지며 대략 3GB정도 되는 크기이다.


2. Rufus를 이용하여 USB에 부팅 디스크 만들기

https://rufus.akeo.ie/


 필자는 포터블이 편해서 포터블로 다운받았다. 부팅 디스크로 사용할 USB를 꽂고 다음과 같이 Rufus를 이용해 부팅 디스크를 만든다.


 '부팅 가능한 디스크 만들기' 옆의 드롭다운 메뉴에서 ISO 이미지를 선택하고, 그 오른쪽 버튼을 클릭해서 다운받은 Kali ISO 파일을 선택한다.


 만약 이런 창이 뜬다면 그냥 '예'를 눌러주자. 얼마 안 걸리는 작업이다.



 마지막에는 ISO가 아닌 DD 방식으로 만들도록 설정을 바꿔 주어야 한다.  다른 컴퓨터에서는 모르겠지만 내 노트북에서는 ISO 이미지 모드로 쓸 경우 'Failed to copy file from CD-ROM. Retry?' 에러가 발생했다.


3. 설치할 PC에 USB를 꽂고, BIOS 세팅을 USB를 최우선으로 변경 후 부팅하여 설치한다.

 이 부분은 여타 다른 리눅스나 윈도우 설치와 크게 다르지 않으므로 따로 작성하지 않는다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

Tag kali

댓글을 달아 주세요

Windows에서 python-magic 사용하기


리눅스 환경에서는 python-magic을 사용하기 위한 Dependency가 이미 충족되어 있기 때문에, 혹은 쉽게 설치가 가능하기 때문에 python-magic을 사용하는 데 큰 문제가 없지만, 윈도우에서는 그런 게 없기 때문에 몇 가지 절차를 거친 후에야 python-magic을 사용할 수 있다.


python-magic Github : https://github.com/ahupp/python-magic


 python-magic의 Github에 씌여 있는 설명대로 진행하면 윈도우에서도 python-magic을 문제없이 사용할 수 있다.

우선 magic의 사용에 필요한 라이브러리를 준비해야 한다.

magic1.dll, regex2.dll, zlib1.dll의 세 가지인데, 이는 각각 다음 주소에서 다운받을 수 있다.


magic1.dll : http://gnuwin32.sourceforge.net/packages/file.htm

regex2.dll : http://gnuwin32.sourceforge.net/packages/regex.htm

zlib1.dll  : http://gnuwin32.sourceforge.net/packages/zlib.htm


 굳이 Complete package를 다운받을 필요 없이 Binaries만 다운받아서 압축을 풀고, bin 폴더 안으로 들어가면 필요한 dll을 확인할 수 있다. 이 세개의 dll 파일을 다운받고, DLL이 들어 있는 폴더의 경로를 환경변수 PATH에 추가하던지 환경변수 PATH에 있는 폴더에 DLL을 복사하던지 둘 중 편한 방식을 사용하면 된다. 필자의 경우 후자가 편하기 때문에 PATH 내에 들어 있는 C:\Python27 내부에 라이브러리를 복사해 두었다.

 다음으로 위에서 magic1.dll을 위해 다운받아서 압축을 푼 폴더 기준으로 share\misc\ 에 있는 magic과 magic.mgc 파일을 원하는 위치에 두고 사용하면 된다.

 python-magic은 magic.Magic 클래스의 인스턴스를 생성하여 이 인스턴스의 메서드를 호출하여 파일의 타입을 판별하도록 해야 한다. 사용법 자체는 Github에도 있고 구글링해도 쉽게 나오므로 따로 작성하지는 않겠다.


** magic 파일을 환경변수 PATH에 지정된 폴더에 넣고 사용할 경우 다음과 같은 에러가 발생할 수 있다.

파이썬에서 import magic을 할 경우 다음과 같은 에러가 발생한다.

에러가 발생한 C:\Python27\lib\site-packages\magic.py를 열고 확인해 보면 다음과 같은 부분이 문제이다.

이 소스대로라면 리눅스의 magic 라이브러리를 먼저 탐색하기 때문에 먼저 'magic'을 찾고, 찾지 못했을 경우에 'magic1'을 찾는다. 우리가 다운로드 받은 magic 라이브러리는 magic1.dll 이기 때문에 magic을 먼저 찾는 과정에서 magic 바이너리가 발견되었고, 이것을 그대로 로드하려다가 문제가 발생한 것이다.

 따라서 ctypes.util.find_library('magic') 부분을 뒤로 보내서 먼저 'magic1'을 찾게 하던지, 소스 자체를 삭제하던지 편할대로 하면 된다. 필자는 확실하게 하기 위해 아예 삭제해버렸고, 그 소스는 다음과 같이 변한다.

이제 이를 저장하고 위에 적힌 대로 사용하면 된다.


** 64비트 파이썬을 사용하는 경우에는 magic1.dll 대신 64비트로 빌드된 libmagic 라이브러리를 사용해야 한다.

이는 다음 링크에서 다운로드 받을 수 있다.

64-bit libmagic: https://github.com/pidydx/libmagicwin64


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

Tag Python

댓글을 달아 주세요

한빛미디어의 '리눅스 커널의 이해'를 읽고 내용을 정리하는 글입니다. **


- 가상 파일 시스템의 역할

 '가상 파일 시스템(VFS, Virtual File System)'은 표준 유닉스 파일 시스템이 제공하는 모든 시스템 콜을 처리하는 커널 소프트웨어 계층이다. VFS의 장점은 여러 종류의 파일 시스템에 대해 공통 인터페이스를 제공한다는 것이다. 예를 들어, cp 명령을 통해 MS-DOS 디스켓의 파일을 일반 Ext2(2차 확장 파일 시스템) 디렉토리에 복사한다고 해도 cp 프로그램은 복사 대상과 복사 목적지의 파일 시스템 유형을 알 필요가 없다. 대신 cp는 일반 시스템 콜(open, read, write 등)을 통해 VFS와 상호 작용한다.

VFS가 지원하는 파일 시스템은 크게 다음과 같이 세 부류로 나눌 수 있다.

1. 디스크 기반 파일 시스템

로컬 디스크 파티션의 기억 장소 또는 디스크를 흉내내는 몇 가지 다른 장치(USB 등)를 관리한다. VFS가 지원하는 잘 알려진 디스크 기반 파일 시스템은 다음과 같다.

- 널리 사용되는 Ext2, 최신 Ext3, 또한 ReiserFS같은 리눅스의 독자적인 파일 시스템

- SYSV(시스템 V, Coherent, XENIX)와 UFS(BSD, Solaris, NEXTSTEP), MINIX 파일 시스템, 베리타스 VxFS(SCO 유닉스웨어) 등 다른 유닉스를 위한 파일 시스템

- MS-DOS, VFAT, NTFS 같은 마이크로소프트 파일 시스템

- ISO9660 CD-ROM 파일 시스템, UDF DVD 파일 시스템

- IBM OS/2(HPFS), 애플 매킨토시(HFS), 아미가 패스트 파일 시스템(AFFS), 아콘 디스크 파일 시스템(ADFS) 등 기타 전용 파일 시스템

- IBM의 JFS, SGI의 XFS 등 리눅스 이외의 시스템에서 유래한 추가적인 저널링 파일 시스템


2. 네트워크 파일 시스템

 네트워크로 연결된 다른 컴퓨터의 파일에 쉽게 접근하게 해준다. VFS가 지원하는 잘 알려진 네트워크 파일 시스템으로는 NFS, Coda, AFS, CIFS, SMB, NCP 등이 있다. (책에는 없었지만 GlusterFS도 있다)


3. 특수 파일 시스템(가상 파일 시스템이라고도 함)

 이 파일 시스템은 자신의 컴퓨터나 다른 컴퓨터의 실제 디스크 공간을 관리하지 않는다. /proc이 대표적인 특수 파일 시스템이다.


- 공통 파일 모델 

 VFS의 핵심 개념은 지원하는 모든 파일 시스템을 표현할 수 있는 '공통 파일 모델(Common File Model)'을 도입하는 것이다. 이 모델은 전통적인 유닉스 파일 시스템이 제공하는 파일 모델을 충실히 따른다. 

 공통 파일 모델에서는 각 디렉토리를 파일의 목록과 다른 디렉토리들을 포함하는 파일처럼 생각한다. 하지만 몇몇 비 유닉스 계열의 디스크 기반 파일 시스템은 디렉토리 트리에서 각 파일의 위치를 저장한 파일 할당 테이블(FAT)을 사용한다. 이런 파일 시스템에서는 디렉토리를 파일로 간주하지 않는다. FAT 파일 시스템을 리눅스에서 구현하기 위해서는 VFS의 공통 파일 모델을 따르도록 하기 위해 실행 중에 디렉토리에 대응하는 파일을 생성할 수 있어야 한다. 이 파일은 커널 메모리 객체로만 존재한다.

 본래 리눅스 커널은 read()나 ioctl() 작업을 처리하는 특정 함수를 직접 구현할 수 없고, 대신 각 작업에 대해 포인터를 사용해야 한다. 포인터는 접근할 특정 파일 시스템을 위한 적절한 함수를 가리키게 한다. 파일은 커널 메모리에서 file 자료 구조로 표현되는데, 이 자료 구조에는 f_op라는 필드가 있어서 각 파일 시스템을 처리하는 함수에 대한 포인터를 포함한다. 함수 중에는 파일을 읽는 함수도 있으며, read()함수를 실행했을 때 커널 내부에서 sys_read()는 이 함수에 대한 포인터를 찾아서 호출한다. 따라서 다음과 같이 간접적으로 호출하게 되는 것이다.

file->f_op->read(...);

 이와 비슷하게 write()연산은 출력 파일에 대응하는 적당한 쓰기 함수를 실행하도록 한다. 간단히 말해서 커널은 각 열린 파일에 대해 file 변수에 알맞은 함수 포인터를 할당하고, f_op 필드가 가리키는 각 파일 시스템별 호출을 실행해야 한다.


공통 파일 모델은 다음과 같은 객체 유형으로 이루어진다.

- 슈퍼 블록 객체

 마운트 된 파일 시스템에 대한 정보를 저장한다. 디스크 기반 파일 시스템의 경우, 이 객체는 일반적으로 디스크에 저장한 파일 시스템 제어 블록(File System Control Block)에 대응한다.


- 아이노드 객체

 특정 파일에 대한 일반 정보를 저장한다. 디스크 기반 파일 시스템의 경우, 이 객체는 일반적으로 디스크에 저장한 파일 제어 블록(File Control Block)에 대응한다.


- 디엔트리 객체

 디렉토리 항목(즉 특정 파일 이름)과 이에 대응하는 파일의 연결에 대한 정보를 제공한다.


 프로세스 3개가 같은 파일 하나에 접근하며, 이 중 두 프로세스는 동일한 하드 링크를 사용하여 접근한다고 하면 세 프로세스 모두 각각의 파일 객체를 소유하지만 디엔트리 객체는 하드 링크 하나당 하나만 필요하기 때문에 두 프로세스는 같은 디엔트리 객체에 접근하게 된다. VFS는 모든 파일 시스템에 대한 공통 인터페이스를 제공하는 일 외에도 가장 최근에 사용한 디엔트리 객체를 디엔트리 캐시(Dentry Cache)라는 디스크 캐시에 저장함으로 파일 경로명을 경로의 마지막 구성 요소인 파일 아이노드로 변환하는 속도를 높인다.


( 실제 파일 블록당 아이노드 객체는 하나씩 존재함. 디엔트리 객체는 하드링크 하나당 하나씩 존재하며 같은 파일을 가리키는 하드링크는 같은 아이노드 객체를 참조하여 실제 파일에 접근함. )

(디엔트리 객체는 ext 구조에서는 디스크에 저장된다고 하는데 다른 파일 시스템에서는 커널 메모리 영역에 저장된다고 함. 하드 링크는 같은 파일 시스템에 대해서만 만들 수 있다는게 이것때문인 것 같은데, 만약에 NTFS 파일 시스템에서 같은 NTFS 파일 시스템에 하드 링크를 만들 경우 어떻게 저장되는지는 좀 더 찾아봐야 할 듯 하다.)

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

** 한빛미디어의 '리눅스 커널의 이해'를 읽고 내용을 정리하는 글입니다. **

- 시그널과 프로세스 간 통신

 유닉스 '시그널(Signal)'은 프로세스에 시스템 이벤트를 알려주는 메커니즘을 제공한다. 각 이벤트는 SIGTERM과 같이 기호로 된 상수로 참조하는 자신만의 시그널 번호를 가지고 있으며, 이에는 두 종류가 있다.


- 비동기적 알림(Asynchronous Notification)

 예를 들어, 사용자가 터미널에서 인터럽트 키를 눌러(Ctrl + C) Foreground Process에 SIGINT 인터럽트 시그널을 보낼 수 있다.

- 동기적 에러(Synchronous Error)나 예외(Exception)

 예를 들어, 프로세스가 잘못된 주소에 있는 메모리 위치에 접근하려고 하면 커널은 프로세스에 SIGSEGV 시그널을 보낸다. (BOF 등에서 자주 등장한다.)


 POSIX 표준에서는 20개 가량의 시그널을 정의하고 있으며 이중 2개는 사용자가 정의할 수 있다. 프로세스는 시그널을 받을 때 보통 시그널을 무시하거나, 비동기적으로 특별한 절차(시그널 핸들러)를 실행하는 두 가지 반응을 보인다.

(POSIX 시그널 : http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html)


 프로세스가 이 중 하나를 지정하지 않으면 커널은 시그널 번호에 따라 정해진 기본 동작을 수행한다. 기본 동작은 다음의 다섯 가지이다.

- 프로세스를 종료한다

- 실행 컨텍스트와 주소 공간의 내용을 파일에 기록하고(Core dump) 프로세스를 종료한다. (BOF시 자주 본다)

- 시그널을 무시한다

- 프로세스를 보류한다

- 프로세스가 중단된 상태라면 프로세스 실행을 재개한다

SIGKILL과 SIGSTOP 시그널은 프로세스가 직접 처리할 수 없으며, 무시할 수도 없다.


- 프로세스 관리

 유닉스에서는 프로세스와 프로세스가 실행하는 프로그램을 명확하게 구별한다. 새로운 프로세스를 생성하고 종료하는 데는 각각 fork()와 _exit() 시스템 콜을 사용하지만, 새로운 프로그램을 로드할 때는 exec()계열의 시스템 콜을 사용한다.


 fork()를 호출하는 프로세스는 '부모(Parent)', 새로운 프로세스는 '자식(Child)'이 된다. 프로세스를 기술하는 자료 구조에는 친부모와 모든 친자식들을 가리키는 포인터가 있어 부모와 자식은 서로를 알 수 있다.  fork() 함수를 단순하게 구현하면 부모의 코드 및 데이터를 전부 자식에게 복사하면 되지만 이럴 경우 시간이 많이 걸린다. 그래서 최신 커널은 페이징 유닛(Paging Unit)기능을 활용하여 'Copy On Write'한다. 이는 페이지 복사를 최후의 순간까지 미루는 것이다.

 _exit() 시스템 콜은 프로세스를 종료한다. 커널은 프로세스가 점유하고 있는 자원을 반납하고 부모 프로세스에 SIGCHLD 시그널을 보내는데, 기본적으로 부모는 이 시그널을 무시한다.


- 좀비 프로세스

 부모 프로세스는 wait4() 시스템 콜을 호출하여 자식 프로세스 중 하나가 종료할 때까지 기다릴 수 있다. 이 시스템 콜은 종료한 자식의 PID(Process ID)를 돌려준다. 이 시스템 콜을 호출하면 커널은 이미 종료한 자식이 있는지 검사하는데, wait4() 시스템 콜을 호출하기 전까지 종료한 프로세스는 '좀비 프로세스(Zombie Process)' 상태로 남는다. wait4() 시스템 콜을 호출할 때, 그 전에 종료한 자식 프로세스가 없으면 커널은 보통 자식 프로세스가 종료할 때까지 부모 프로세스를 대기 상태로 만든다.

 만약 부모 프로세스가 wait4() 시스템 콜을 호출하지 않고 종료한다면, 커널은 좀비 프로세스로 남아 있는 자식 프로세스를 'init'이라는 시스템 프로세스의 자식으로 만든다. init 프로세스는 모든 자식의 실행 상태를 지켜보고 정기적으로 wait4() 시스템 콜을 호출하여 모든 좀비 프로세스를 제거하는 효과를 갖는다.


- 가상 메모리

 모든 최신 유닉스 시스템은 '가상 메모리(Virtual Memory)'라는 유용한 추상화 개념을 제공한다. 가상 메모리를 사용하는 데는 다양한 목적과 장점이 있다.

- 여러 프로세스를 동시에 실행할 수 있다.

- 사용할 수 있는 물리 메모리보다 많은 메모리를 필요로 하는 애플리케이션을 실행할 수 있다.

- 프로그램 코드 중 일부만 메모리에 로드해도 프로세스를 실행할 수 있다.

- 각 프로세스는 사용 가능한 물리 메모리의 일부에만 접근할 수 있다.

- 라이브러리나 프로그램의 메모리 이미지 하나를 프로세스 사이에서 공유할 수 있다.

- 프로그램을 재배치(Relocation)할 수 있다.

- 프로그래머는 물리 메모리의 구조에 신경 쓸 필요가 없으므로 편하게 코드를 작성할 수 있다.

 프로세스가 가상 주소를 사용하면 커널과 MMU(Memory Management Unit)가 서로 협력하여 요구한 메모리 주소의 실제 물리적 위치를 찾는다. 현재의 CPU에는 자동으로 가상 주소를 물리 주소로 변환하는 하드웨어 회로가 있다. 이에 따라 사용 가능한 램을 일반적으로 4KB나 8KB단위의 '페이지 프레임(Page Frame)'으로 쪼개고 페이지 테이블(Page Table)을 통해 가상 주소와 물리 주소 사이의 대응 관계를 지정한다. 이 회로를 사용하면 연속된 가상 주소에 메모리 블록을 할당하려는 요청을 실제 물리 주소에서는 연속되지 않는 페이지 프레임 그룹에 할당하여 처리할 수 있어 메모리 할당이 간단해진다.


- 램 사용

 모든 유닉스 운영체제는 RAM(Random Access Memory)을 두 부분으로 나누어 구분한다.

 몇 메가바이트는 커널 이미지(커널 코드 및 커널의 정적 자료 구조)를 저장하는 데  사용한다. 나머지는 보통 가상 메모리 시스템이 다루는 부분으로, 다음의 세 가지 용도로 사용한다.

- 커널에서 필요로 하는 버퍼와 디스크립터, 다른 동적으로 만들어지는 커널 자료 구조용

- 프로세스가 필요로 하는 일반적인 메모리 영역과 파일 메모리 매핑용

- 디스크나 버퍼를 통하는 다른 장치로부터 더 나은 성능을 얻기 위한 캐시용

 사용 가능한 램은 제한되어 있기 때문에 위의 세 가지 요구 사이에 균형을 맞추는 것은 매우 중요하다. 


- 커널 메모리 할당자

 '커널 메모리 할당자(KMA, Kernel Memory Allocator)'는 시스템의 모든 부분에서 오는 메모리 영역의 관련 요청을 처리하는 서브시스템이다. 이 요청 중 일부는 커널에서 사용할 메모리를 위한 것이고, 다른 일부는 사용자 프로그램이 자신의 프로세스 주소 공간을 늘리기 위해 시스템 콜을 통해 호출한 것이다. 좋은 커널 메모리 할당자는 다음과 같은 특징을 갖추어야 한다.

- 빨라야 한다.

- 낭비되는 메모리 양을 최소화해야 한다.

- 메모리 단편화 문제를 줄여야 한다.

- 다른 메모리 관리 서브시스템과 협력하여 이들로부터 메모리를 빌려오거나 이들의 메모리를 해지할 수 있어야 한다.


  프로세스 주소 공간에는 프로세스가 접근할 수 있는 모든 가상 메모리 주소가 들어 있다. 커널은 프로세스의 가상 주소 공간을 '메모리 영역 디스크립터(Memory Area Descriptor)'의 리스트로 저장한다. 예를 들어, 프로세스가 exec() 계열의 시스템 콜을 통해 어떤 프로그램을 실행하면 커널은 프로세스에 다음과 같은 메모리 영역으로 구성된 가상 주소 공간을 할당한다.

- 프로그램의 실행 코드

- 프로그램의 초기화된 데이터

- 프로그램의 초기화되지 않은 데이터

- 초기 프로그램 스택(즉 유저 모드 스택)

- 프로그램이 필요로 하는 공유 라이브러리의 실행 코드와 데이터

- 힙(Heap)


- 장치 드라이버

 커널은 '장치 드라이버(Device Driver)'를 통해 입출력 장치와 상호 작용한다. 장치 드라이버는 커널에 들어 있으며 하드 디스크나 키보드, 마우스, 모니터, SCSI 버스에 연결된 장치, 네트워크 카드 같은 장치를 하나 이상 제어하는 자료 구조와 함수로 이루어진다. 각 드라이버는 정해진 인터페이스에 따라 커널의 다른 부분과 상호 작용한다. 이런 접근 방법에는 다음과 같은 이점이 있다.

- 장치에 따라 고유한 코드를 특정 모듈 속으로 넣을 수 있다.

- 제품을 파는 사람은 커널 소스 코드를 모르더라도 인터페이스 명세(Interface Specification)만 알면 새로운 장치를 추가할 수 있다.

- 커널은 모든 장치를 동일한 방법으로 다루고, 동일한 인터페이스로 접근한다.

- 시스템을 재부팅하지 않고도 커널에 동적으로 로드할 수 있는 모듈 형태로 장치 드라이버를 만들 수 있으며 더 이상 필요하지 않은 모듈을 동적으로 언로드하여 커널 이미지가 램에서 차지하는 크기를 줄일 수 있다.


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

** 한빛미디어의 '리눅스 커널의 이해'를 읽고 내용을 정리하는 글입니다. **


- 파일 디스크립터와 아이노드

 유닉스에서 장치 파일과 특수 파일을 제외하면 각 파일은 일련의 문자로 이루어진다. 파일에는 파일의 길이나 파일의 끝(EOF)을 나타내는 구분자와 같은 제어 정보는 들어있지 않다.

 파일 시스템이 파일을 다루는 데 필요한 정보는 모두 '아이노드(Inode)'라는 자료구조에 들어있으며, 각 파일은 각자 자신만의 아이노드를 가진다. 파일 시스템은 유닉스 시스템마다 크게 차이가 있을 수 있지만 적어도 POSIX 표준에서 규정하는 다음과 같은 속성을 항상 제공해야 한다.

- 파일 유형

- 파일과 연결된 하드 링크의 수

- 파일 길이 (바이트 단위)

- 장치 ID

- 아이노드 번호

- 파일 소유자의 UID

- 파일 소유 그룹의 GID

- 마지막 접근 및 수정 시간 등을 나타내는 여러가지 시간 기록

- 접근 권한과 파일 모드


- 접근 권한과 파일 모드

 0x01의 '사용자와 그룹' 절에서 정리했다시피 파일을 사용하는 사용자는 '파일 소유자', '파일 소유 그룹', '그 외의 사용자'의 세 부류가 있다. 이 세 부류 각각에 읽기, 쓰기, 실행이라는 세 가지 접근 권한 유형을 지정할 수 있으므로 파일과 관련된 접근 권한은 서로 다른 이진 플래그 9개로 이루어진다. 이밖에 suid(Set User ID), sgid(Set Group ID), sticky라는 세가지 추가 플래그가 파일 모드를 정의한다. 어떤 파일을 실행하는 프로세스는 보통 프로세스 소유자의 UID와 GID를 가진다. 하지만 suid나 sgid 플래그가 설정된 파일을 실행하면 프로세스는 각각 파일 소유자의 UID나 GID를 갖게 된다. sticky 플래그의 경우 실행이 종료된 후에도 프로그램을 메모리에 유지하도록 커널에 요청하는 플래그이나 더 이상 이는 쓰이지 않는다.


- 프로세스와 커널 모델

 0x01의 '프로세스' 절에서 프로세스는 User mode나 Kernel mode 둘 중 하나에서 동작한다고 정리하였다. User mode에서 동작 중일 때 프로세스는 커널 자료 구조나 커널 프로그램에 직접 접근할 수 없다. 그러나 커널 모드에서 동작 중일 때는 이러한 제한이 적용되지 않는다.

 커널은 '프로세스'의 개념이 아니라 '프로세스 매니저'의 개념으로 프로세스를 생성하고 제거하고 동기화하는 작업을 한다. '프로세스/커널' 모델에서는 프로세스가 커널 서비스를 필요로 할 때 '시스템 콜(System Call)'이라는 구조를 활용하도록 한다. 각 시스템 콜은 매개 변수 그룹을 설정한 후, 사용자 모드에서 커널 모드로 전환하는 하드웨어 고유의 CPU 명령어를 실행한다.


 유닉스 커널은 시스템 콜을 다루는 작업 외에도 많은 일을 수행하는데, 커널 루틴이 호출되는 경우는 다음과 같다.

- 프로세스가 시스템 콜을 호출한 경우

- 프로세스를 실행하던 CPU가 '예외(Exception)'을 발생시킨 경우 (커널이 대신하여 예외를 처리한다)

- 주변 장치가 특정 사건을 알려주기 위해 CPU에 '인터럽트 시그널(Interrupt Signal)'을 보내는 경우 (인터럽트 핸들러라는 커널 프로그램이 처리한다)

- 커널 스레드를 실행한 경우



- 프로세스 구현

 커널은 프로세스를 다루기 위해 각 프로세스를 '프로세스 디스크립터(Process Descriptor'로 나타낸다. 커널은 프로세스의 실행을 중단할 때 다음과 같은 여러 프로세서 레지스터의 현재 내용을 프로세스 디스크립터에 저장한다.

- 프로그램 카운터(PC)와 스택 포인터(SP) 레지스터

- 범용 레지스터

- 부동 소수점 레지스터

- CPU 상태 정보를 담고 있는 레지스터 (Processor Status Word)

- 메모리 관리 레지스터

 커널이 어떤 프로세스를 다시 실행하기로 결정하면 적절한 프로세스 디스크립터 필드를 CPU 레지스터로 읽어들인다.

 (이렇게 프로세스 디스크립터에 쓰고 읽는 과정이 곧 Context Switching이다)


- 재진입 가능한 커널

 모든 유닉스 커널은 '재진입(Reentrant)'이 가능하다. 이것은 여러 프로세스를 동시에 커널 모드에서 실행할 수 있다는 의미이다. CPU가 커널 제어 경로를 실행하고 있을 때 인터럽트가 발생할 경우는 첫 번째 커널 제어가 종료되지 않은 상태에서 CPU는 인터럽트를 처리하기 위해 또 다른 커널 제어 경로를 시작한다. 이 커널 제어가 완료된 후 첫번째 커널 제어 경로를 처리하며 이때 두 커널 제어 경로는 같은 프로세스의 실행 컨텍스트에서 실행되어 이 과정에서 소요된 시스템 시간은 프로세스가 사용한 것으로 계산된다.



- 동기화와 임계 영역

 재진입 가능한 커널을 구현하려면 동기화를 이용해야 한다. 커널 제어 경로가 어떤 커널의 자료 구조를 가지고 작업하던 도중에 멈춘 경우, 이 자료 구조가 올바른 상태로 다시 설정되지 않는 한 다른 커널 제어 경로가 동일한 자료 구조를 사용할 수 없도록 해야 한다. 둘 이상의 프로세스를 어떻게 스케줄 하느냐에 따라 계산 결과가 달라질 수 있다면 해당 코드는 잘못된 것이며 이를 '경쟁 조건(Race Condition)'이 있다고 한다.

 모든 작업이 '원자적인 연산(Atomic Operation)'을 이용한다면 전역 변수에 안전하게 접근할 수 있다. 하지만 커널에는 이런 단일 연산으로 다룰 수 없는 많은 자료 구조가 있다. 예를 들어, 연결 리스트의 항목을 제거하는 경우 적어도 두 개의 포인터에 동시에 접근해야 하므로 단일 연산으로는 이를 수행할 수 없다. 어떤 코드 영역이든 일단 시작했다면 다른 프로세스가 해당 영역에 진입하기 전에 끝내야만 하는 영역을 '임계 영역(Critical Section)'이라고 한다.

 이런 문제를 해결하기 위해 몇 가지 동기화 기법을 채택하였는데, 이들은 각각 다음과 같다.

- 커널 선점 기능 제거

고전 유닉스는 커널을 비선점형으로 만드는 것으로 동기화 문제를 해결하였다. 비선점형 커널에서는 프로세스가 커널 모드에서 실행 중이면 이를 임의로 보류하거나 다른 프로세스로 교체할 수 없다. 하지만 멀티프로세서 시스템에서는 서로 다른 CPU에서 실행되는 두 커널 제어 경로가 동시에 같은 자료구조에 접근할 수도 있으므로 멀티프로세서 시스템에서 비선점성은 적합하지 못하다.

- 인터럽트 금지

유니프로세서 시스템을 위한 동기화 매커니즘으로 임계 영역에 들어가기 직전에 모든 하드웨어 인터럽트를 금지하고 임계 영역을 빠져나오는 즉시 다시 허용하는 것이다. 매우 간단한 매커니즘이지만 임계 영역이 크면 긴 시간동안 하드웨어의 동작을 멈추게 할 수도 있다는 문제가 있다.

- 세마포어

세마포어는 어떤 자료구조와 연계된 간단한 카운터로 모든 커널 스레드는 자료 구조에 접근하기 전에 해당 세마포어를 검사한다. 세마포어는 정수 변수와 대기 중인 프로세스 목록, 원자적으로 작동하는 up()과 down() 두 개의 메서드를 갖는다. 각 자료 변수는 모두 자신만의 세마포어를 가지며, 정수 변수는 초기값 1을 가진다. 커널 제어 경로가 자료구조에 접근하려고 할 때 먼저 해당 세마포어의 down()메서드를 실행한다. 이후 세마포어 정수 변수의 값이 음수가 아니면 해당 자료구조에 접근할 수 있고, 음수이면 커널 제어 경로를 실행 중이던 프로세스를 대기 프로세스 목록에 추가하고 대기한다. 먼저 자료구조에 접근해 사용 중이던 프로세스가 작업을 마치며 up() 메서드를 실행하면 대기 중인 프로세스중 하나는 작업을 계속할 수 있다.

- 스핀 락

스핀 락은 세마포어와 매우 유사하지만 프로세스 리스트가 없다는 점이 다르다. 프로세스가 사용하려는 자료구조를 이미 다른 프로세스가 사용하고 있다면 프로세스는 락이 풀릴 때까지 반복해서 명령어를 실행하며 회전(spin)한다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

** 한빛미디어의 '리눅스 커널의 이해'를 읽고 내용을 정리하는 글입니다. **


http://www.kernel.org 에서 리눅스 커널 소스를 내려받을 수 있다.


- 운영체제의 기초 개념

 모든 컴퓨터 시스템에는 '운영체제'라는 기본적인 프로그램 집합이 들어있고, 이 중에서 가장 중요한 프로그램을 '커널'이라고 한다. 커널은 시스템을 부팅할 때 램으로 로딩되며, 시스템이 동작하는데 필요한 여러 가지 중요한 절차를 수행한다.


운영체제는 다음의 주요 목적을 달성해야 한다.

- 하드웨어의 구성 요소와 상호 작용하면서 하드웨어 플랫폼에 들어 있는 저수준의 프로그래밍 가능한 모든 요소를 서비스한다.

- 유저 애플리케이션을 위한 실행 환경을 제공한다.


 MS-DOS의 경우 모든 유저 애플리케이션이 하드웨어 구성 요소를 직접 실행할 수 있지만, 이와 대조적으로 유닉스 계열 운영체제는 프로그램이 하드웨어의 자원을 이용하기 위해 반드시 운영체제에 요청을 해야 한다. 요청을 받은 커널은 이를 평가하고 사용자 프로그램을 대신해 하드웨어 구성 요소와 상호 작용한다. 이런 권한 제어를 위해 최근 운영체제는 특수한 하드웨어 기능을 활용한다. CPU는 유저 애플리케이션을 위해 User mode, 커널을 위해 Kernel mode라는 서로 다른 두 실행 모드를 제공한다.


- 사용자와 그룹

유닉스에서 모든 사용자는 '사용자 ID', 즉 UID라고 하는 고유한 번호로 구별된다. 다른 사용자와 선택적으로 어떤 것을 공유할 수 있도록 각 사용자는 하나 이상의 사용자 그룹(Group)의 일원이 될 수 있는데, 이 그룹은 GID라는 고유한 숫자로 구별한다.

 각 파일은 단 하나의 그룹과 연결되기 때문에 한 파일에 대해 '파일 소유자', '파일 소유 그룹', '그 외의 사용자' 셋에 대해 파일에 대한 접근 권한을 다르게 설정할 수 있다.

(흔히 리눅스에서 사용하는 chmod 등으로 권한을 변경하는 것이 이에 해당한다)


 모든 유닉스 계열 운영체제에는 루트(Root)나 슈퍼유저(SuperUser)라는 특별한 사용자가 있다. 운영체제는 루트 사용자에게 일반적인 보호 매커니즘을 적용하지 않으므로 루트 사용자는 거의 모든 일을 할 수 있다. 이는 시스템의 모든 파일에 접근하는 것과 실행 중인 모든 사용자 프로그램의 동작을 조정하는 것을 포함한다.


- 프로세스

 멀티유저 시스템은 여러 프로세스가 동시에 동작하면서 CPU를 비롯한 시스템 자원을 서로 차지하려고 경쟁하는 실행 환경을 제공해야 한다. 동시에 여러 프로세스를 동작할 수 있는 시스템을 '멀티프로그래밍' 또는 '멀티프로세싱'이라고 한다. 여러 개의 프로세스가 동시에 실행 중일 때 CPU를 사용할 프로세스를 선택하는 것은 스케줄러라는 운영체제 구성 요소의 역할이다.

 유닉스 계열 운영체제는 '프로세스/커널 모델'을 채택한다. 프로세스가 커널에 어떤 요청을 하기 위해 시스템 콜(System Call)을 호출할 때마다 하드웨어는 실행 모드를 User mode에서 Kernel mode로 전환한 뒤 엄격하게 제한된 목적으로만 커널 코드를 실행한다. 이렇게 운영체제가 프로세스의 실행 컨텍스트 내에서 프로세스의 요청을 처리한 뒤, 다시 실행 모드를 Kernel mode에서 User mode로 전환하고, User mode에서 시스템 콜 이후의 명령을 수행하기 시작한다.



- 커널 구조

대부분의 유닉스 커널은 모놀리식(Monolithic)이다. (모놀리식/마이크로 커널)

 마이크로 커널은 여러 계층 사이에 메시지를 전달하는 비용이 들기 때문에 일반적으로 모놀리식 커널보다 느리다. 하지만 마이크로 커널은 비교적 독립된 프로그램이기 때문에 시스템 프로그래머들이 모듈화 된 접근을 하도록 만들고, 다른 아키텍처로 이식하기 매우 쉽다. 또한 마이크로 커널은 불필요한 기능을 구현하는 시스템 프로세스를 스왑 아웃할 수 있어 램을 훨씬 잘 활용하는 경향이 있다.

 성능을 떨어뜨리지 않고 마이크로 커널의 여러 장점을 효율적으로 달성하기 위해 리눅스 커널은 '모듈(Module)'을 제공한다. 모듈은 실행 중 커널에 링크 및 언링크 가능한 오브젝트 파일이며, 이는 대개 파일 시스템, 장치 드라이버, 커널의 상위 계층에 있는 다른 기능을 구현하는 함수의 집합으로 이루어진다.


- 하드 링크와 소프트 링크

 디렉토리에 있는 파일명을 '파일 하드 링크', 간단히 '링크'라고 한다.

'$ ln f1 f2' 

명령으로 f1이 나타내는 파일에 대해 f2라는 새로운 하드 링크를 만들 수 있다.

하드 링크에는 두 가지 제약이 있다.

1. 디렉토리에 대하여 하드 링크를 만들 수 없다. 이는 트리 구조인 디렉토리 계층 구조가 순환 그래프로 바뀌기 때문에 파일명으로 파일을 찾지 못하게 될 수 있기 때문이다.

2. 동일한 파일 시스템에 있는 파일 사이에서만 하드 링크를 만들 수 있다.


 '소프트 링크' 또는 '심볼릭 링크'는 이런 제약을 극복하기 위해 도입되었다.

 심볼릭 링크는 다른 파일의 경로명을 포함하는 작은 파일로, 어떤 파일 시스템에 들어 있는 특정 파일이나 디렉토리도 가리킬 수 있으며 실제로 존재하지 않는 파일도 가리킬 수 있다. (윈도우의 '바로 가기'와 비슷하다고 생각하면 된다)

다음 유닉스 명령은 경로명 f2가 경로명 f1을 가리키도록 소프트 링크를 만든다.

'$ ln -s f1 f2'


'Knowledge' 카테고리의 다른 글

리눅스 커널 공부 정리 0x03  (0) 2017.06.27
리눅스 커널 공부 정리 0x02  (1) 2017.06.21
리눅스 커널 공부 정리 0x01  (0) 2017.06.20
Memory Segmentation  (0) 2016.12.27
NAT와 NAPT의 개념과 원리  (10) 2016.12.01
멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

Memory Segmentation

Knowledge 2016. 12. 27. 17:27


 Memory Segmentation이란 메모리를 '세그먼트'라는 영역으로 나눠 관리하는 것을 의미한다.




 먼저 어떤 프로세스에 대해 '액세스해도 좋은 메모리 범위(세그먼트)'를 정한다. 이는 해당 메모리의 시작 주소(Base Address), 크기(Limit), 권한을 포함하고 있다. 이런 각 세그먼트에 관한 정보를 Segment Descriptor라고 하며, 이를 모아서 테이블로 만든 것을 Descriptor Table 이라고 한다.


 16비트 리얼 모드에서는 ( 세그먼트 레지스터 * 16 + 오프셋 주소 )로 실제 주소를 구했지만, 보호 모드에서는 세그먼트 레지스터에 씌여진 값이 바로 주소로 연결되는 것이 아닌 Descriptor Table에서의 오프셋을 의미한다.


 세그먼트 레지스터에는 CS, DS, SS, ES, FS, GS 등이 있다.




 세그먼트 레지스터는 상위 13비트는 Index, 그 다음 1비트는 TI(Table Indicator) 플래그, 그다음 2비트는 RPL(Requested Privilege Level)이라는 값으로 사용된다. 프로그램이 메모리 속의 데이터를 읽어 들일 때 DS 레지스터의 내용을 암시적으로 참조하게 된다.


 세그먼트 레지스터로 Segment Descriptor를 참조하여 Base Address를 구한 뒤, 오프셋 주소를 더해서 액세스할 번지를 구하는 것이다. 식으로 나타내면 ( DescriptorTable[DS_Register].BaseAddress + Offset ) 이라고 할 수 있다.


 이 때문에 보호 모드에서는 세그먼트 레지스터를 세그먼트 셀렉터(Segment Selector)라고 한다. Descriptor table을 각 프로세스마다 준비해두면 메모리를 보호할 수 있지만, 이는 다시 말해서 Descriptor Table을 프로세스 수만큼 생성해야 한다는 뜻이다. 따라서 여러 프로세스가 동시에 동작하면서 메모리에는 한 개 이상의 LDT가 존재하게 되는데, 이를 관리하기 위해 CPU는 시스템에 딱 하나만 존재하는 GDT(Global Descriptor Table)이라는 테이블을 이용한다. GDT는 LDT의 시작 주소와 그 크기 등이 작성된 테이블이다.


 CPU 내에는 GDT의 시작 주소와 그 크기를 담고 있는 GDTR이라는 레지스터가 존재한다.


 CPU는 현재 작동중인 프로세스의 Descriptor Table의 주소를 참조하기 위해 LDTR이라는 것을 사용한다. LDTR은 Local Descriptor Table Register의 약자로, 각 프로세스에 대해 준비된 Descriptor의 위치를 나타내기 위한 레지스터이다. OS에서 애플리케이션으로 제어가 넘어갈 때 OS는 LDTR에 해당 프로세스의 LDT를 가리키는 GDT의 Offset을 설정한다. 당연하게도 프로세스는 이 레지스터에 참조 또는 수정을 할 수 없는데, 수정할 수 있게 되면 프로세스 마음대로 Descriptor Table을 만들고 이를 참조하여 메모리 보호가 깨져버리기 때문이다. 


"세그먼트 레지스터 (셀렉터) 내부에는 TI라는 1비트짜리 플래그가 있는데, TI가 0일 경우 GDT를 참조하며 1인 경우 LDT를 참조한다."


이 부분이 의문이다.

LDTR을 이용하여 LDT를 참조한 후 Index를 이용해 Descriptor를 찾고 여기서 Base Address를 구하는 일반적인 방식을 사용하지 않고 어째서 GDT를 참조하는가 하는 것이다.

GDT를 참조한다고 해도 얻을 수 있는 것은 LDT의 주소 뿐이고 LDT 내부로 한번 더 참조해 들어가야 Base Address를 구할 수 있는데 셀렉터의 Index는 GDT를 구하는데 사용했으므로 더이상 LDT를 참조할 때 사용할 Offset이 없다.





...미완성된 글입니다. 혹시 의문을 해소해주실 수 있는 분이나 잘못된 부분을 발견하시면 댓글로 알려주시면 감사하겠습니다.

'Knowledge' 카테고리의 다른 글

리눅스 커널 공부 정리 0x02  (1) 2017.06.21
리눅스 커널 공부 정리 0x01  (0) 2017.06.20
Memory Segmentation  (0) 2016.12.27
NAT와 NAPT의 개념과 원리  (10) 2016.12.01
멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
PCB (Process Control Block)  (0) 2016.11.14
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요


 네트워크를 공부하려면 필수적으로 거쳐야 하는 것이 바로 사설망이다.

NAT가 무엇인지 알고는 있었지만 새로운 사실을 공부한 김에 다시 개념을 정리하도록 하겠다.



NAT란 무엇인가


 NAT는 Network Address Translation의 약자이다.

현재 사용하는 XXX.XXX.XXX.XXX 형태의 IP 주소는 IPv4 형식으로 4 옥텟으로 표현하도록 되어 있다.

여기서 옥텟이란 8개의 연속적인 비트의 집합을 의미하는 것으로 바이트와 같은 단위라고 봐도 무방하다.

즉, IPv4 에서의 주소는 4바이트로 표현할 수 있고 따라서 최대 개수는 2^32 = 4,294,967,296 이다.

이는 약 43억 개이므로 충분하다고 생각할 수 있지만 사실 IP 주소는 그렇게 여유롭지 않다.

그래서 IP 주소의 절약을 위해 NAT 시스템을 도입하였다.


[NAT 개념도]


 NAT는 하나의 공인 IP를 여러개의 사설 IP로 변환하는 시스템이다.

랜카드를 두 개 이상 가진 공유기(라우터)가 사설망과 외부망의 사이에서 중개자 역할을 하는 것이다.

주변에서 많이 쓰는 공유기가 바로 대표적인 NAT 시스템이다.



NAT의 장점


NAT를 사용하는 이유는 크게 두 가지가 있다.


  1. IP 주소 절약


위에서 언급했다시피 IPv4는 최대 43억개 까지밖에 사용할 수 없다.

하지만 사설망을 사용하게 되면 그 활용도가 훨씬 늘어남을 알 수 있다.


[NAT 사용 시 공인IP 주소를 효율적으로 사용할 수 있다]


 한 공인IP당 A클래스 사설망의 경우 16,777,216개, B클래스 사설망의 경우 1,048,576개, C클래스 사설망의 경우 65,536개의 내부 주소 사용이 가능하다. 물론 네트워크 주소와 브로드캐스트 주소를 빼고 서브넷 마스크까지 계산한다면 훨씬 적어지겠지만 원래라면 한 IP만이 사용 가능한 주소를 저렇게 많은 수의 기기가 연결하여 사용 가능하다는 점은 분명히 큰 이점이다.


  2. 보안


[NAT를 사용하면 외부에서 접근이 불가능해져 보안이 강하다]


 밑에서 설명할 내용이지만 사설망은 특별한 설정을 해 주지 않는 이상 외부망에서 사설망 내부 PC로의 접근이 불가능한 구조로 되어 있다. 따라서 네트워크를 통한 공격에 매우 안전해진다.


  3. IP 주소 관리의 편의성


 이건 개인적으로 생각하고 있던 것이지만 서술해두겠다. IP 주소는 통신을 위한 것이므로 충돌이 일어나서는 안된다. 다시 말해, 한 IP 주소는 무조건 한 PC만이 사용해야 한다. 하지만 43억 개나 되는 IP 주소를 개개인의 PC나 휴대폰 등에 전부 등록하는 것은 당연하게도 무리이다. 사설망을 사용하게 되면 공유기가 자신에게 접속하는 기기의 IP만 관리하면 되므로 훨씬 간편하게 IP를 관리하는 것이 가능하다. 실제로 지금 대부분의 공유기는 DHCP를 이용해 사설IP를 동적으로 할당한다.



NAT의 통신 원리


 사설망에서 외부망으로 통신을 시도하면 해당 패킷은 무조건 공유기를 거치게 되어 있다. 여기서는 의미상 게이트웨이라고 하겠다. 당연하게도 처음 발신자가 게이트웨이에게 패킷을 전달했을 때 '보내는 사람의 IP'를 나타내는 SRC_IP는 사설망 내부의 발신자 주소로 채워져 있다. 하지만 만약에 이것이 그대로 전달될 경우, 수신자가 응답하는 상대의 주소는 사설망 내부의 IP가 되므로 전 세계의 수많은 사설망중 어디로 보내야 할지 알 수가 없게 된다.이런 이유 때문에 외부에서 사설망으로의 접근이 불가능하여 NAT가 보안이 뛰어나다는 장점도 있다.


[사설망의 통신 과정. Gateway->PC1은 어떻게 할까?]


 그래서 통신을 위해 게이트웨이는 패킷을 받아서 패킷 안의 IP주소를 자신의 공인IP로 임의수정하여 목적지로 발신한다.

패킷을 받은 수신자는 목적지 IP 주소에 게이트웨이가 집어넣은 공인IP를 넣어서 패킷을 발신할 것이고, 해당 패킷은 게이트웨이에게 도달하게 될 것이다. 이전에 이렇게 거기까지 공부한 이후 어떻게 다시 사설망의 특정 사용자에게 도달이 가능했는지 궁금했었다.


[NAT가 패킷을 전달하는 과정]


 게이트웨이가 발신자의 IP를 수정하여 발신할 때 게이트웨이는 내부에 갖고 있는 테이블에 이를 기록해 둔다. 어떤 주소에서 어떤 주소로, 무슨 프로토콜을 사용하여 발신했는지를 기록한 후, 응답 패킷이 되돌아오면 그 값을 찾아서 해당하는 사설망 기기에게 발신하는 것이다. 이렇게 테이블을 만들어서 저장해놓고 찾아가는 방법으로 이 문제를 해결할 수는 있지만 문제가 한 가지 더 생길 수 있다. 이는 다음에 설명할 NAPT에서 해결된다.



NAPT란 무엇인가


[NAT의 통신 문제. 만약 두 기기가 동시에 접속을 요청한다면?]


 NAT의 통신 방식은 큰 문제가 하나 있다. 바로 여러 개의 사설망 내의 기기가 동시에 같은 외부망 주소로 접속을 요청하는 경우이다. 원래라면 요청한 곳의 주소와 포트를 보고 해당하는 사설망 주소를 찾아서 보내주어야 하는데, 해당하는 주소가 여러 개일 경우 중복되므로 이를 처리할 방법이 없는 것이다.


이를 해결하기 위해 만들어진 것이 NAPT이다.

[NAPT의 통신 과정. 발신자 포트번호까지 사용하므로 문제 없이 통신할 수 있다]


 NAPT는 Network Address Port Translation의 약자로, NAT에서는 발신자의 사설망-외부망 IP를 바꿔서 보내주는 역할만을 수행했다면 이제는 포트까지 바꿔서 보내는 역할을 한다. 이러면서 NAPT의 테이블은 NAT의 테이블에서 Port에 해당하는 컬럼이 추가된다. 이렇게 변경한 결과 게이트웨이는 발신자의 포트 번호를 보고도 구별할 수 있게 되었다. 만약 발신자의 포트 번호까지 같게 패킷이 보내지면 어떻게 되느냐고도 할 수 있는데, 그럴 경우에는 위 그림처럼 Private Port를 임의의 SRC 포트로 바꿔서 보내면 되므로 아무런 문제가 없다.


'Knowledge' 카테고리의 다른 글

리눅스 커널 공부 정리 0x01  (0) 2017.06.20
Memory Segmentation  (0) 2016.12.27
NAT와 NAPT의 개념과 원리  (10) 2016.12.01
멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
PCB (Process Control Block)  (0) 2016.11.14
[Assembly] Intel x86, Local JMP [0xE9]  (0) 2016.11.07
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

  • 김춘돌 2017.07.03 15:40  댓글주소  수정/삭제  댓글쓰기

    이해가 잘 되네요!

    잘보구 갑니다~~~

    감사합니다~~

  • Asd 2017.08.10 23:20  댓글주소  수정/삭제  댓글쓰기

    그렇다면 랜상에서 192.168.0.2와 192.168.0.3 컴퓨터가 동시에 넥슨 서버에 접속 했을 때 접속이 되는 이유는 저희 집 공유기가 별도의 설정 없이 napt를 해주었기 때문인가요? 아님 다른 이유인가요??

    • __미니__ 2017.08.12 11:46 신고  댓글주소  수정/삭제

      말씀하신 이유가 맞을 거라고 생각합니다.
      넥슨 서버가 항상 동일한 IP에 동일한 Port를 사용한다고 가정했을 때, 동일한 사설망 내의 여러 대의 컴퓨터가 NAT의 환경에서 넥슨 서버에 동시에 접속하는 것은 불가능합니다.

  • 2018.12.23 23:43  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  • 조성민 2019.03.10 20:54  댓글주소  수정/삭제  댓글쓰기

    안녕하세요^^ 제가 궁금한 부분을 알고 작성하신것처럼 포스팅해주셨어요 ㅎㅎ 감사합니다!!
    예시의 경우는 사설망과 서버(비사설) 간의 통신이고 만약 서버중계없이 사설망과 사설망 간의 통신을 할 때는 포트포워딩이 필요하다 라고 이해를 했는데 맞을까요???

    • __미니__ 2019.03.11 08:46 신고  댓글주소  수정/삭제

      안녕하세요~
      그렇게 이해하셔도 틀리지는 않지만 정확히는 '접근하려는 주소가 다른 사설망 내부에 있느냐 아니냐'에 따라 다릅니다.

      A라는 사설망에서 B라는 다른 사설망 내부에 있는 서버에 접근하려고 한다면 당연히 포트포워딩이 필요하구요, 공인 IP를 가진 서버(비사설)에서 다른 사설망 내부에 있는 서버에 접근하려고 해도 마찬가지로 포트포워딩이 필요합니다.

  • 익명 2020.11.05 20:25  댓글주소  수정/삭제  댓글쓰기

    포트 번호를 통해 서버가 사설망 내의 컴퓨터와 1:1로 통신할 수 있음에도 보안성이 좋다고 하는 것은
    SRC 포트를 게이트웨이가 그때그때 얼마든지 임의로 설정할 수 있기 때문인가요?

    • __미니__ 2020.11.06 11:08 신고  댓글주소  수정/삭제

      보안성이 좋다고 하는 이유는 사설망 외부에서 사설망 내부의 임의의 서버나 포트로 접근이 불가능하기 때문입니다. 본문에도 작성해 두었지만, 포트 포워딩이나 DMZ 설정 등을 따로 하지 않는 이상 사설망에서 외부망으로의 접속은 가능하지만 외부망에서 먼저 사설망 내부로의 접속 시도가 불가능해집니다.

  • 익명 2021.03.24 16:18  댓글주소  수정/삭제  댓글쓰기

    좋은글 감사합니다. 정리를 정말 잘하시네요~!



 기본적으로 CPU는 하나당 한 가지의 일밖에 동시에 처리할 수 없다.

때문에 메모리에 두 개 이상의 프로그램을 배치했다고 하더라도 두 개의 프로그램이 동시에 실행될 수는 없다.


 결국 멀티 태스킹을 구현하기 위해서는 아주 짧은 시간동안 한 프로세스를 실행하다가 다른 프로세스로 CPU를 사용할 수 있도록 넘겨주는 것을 반복해야 한다. 이를 간단하게 구현하기 위해서는 각 어플리케이션마다 중간에 '전환점'을 만들어 두면 된다. 전환점은 '내 처리를 잠시 멈추고 CPU를 다른 프로세스에게 배정할 적당한 때가 왔다'라는 것을 알리는 것으로, OS에게 특정 System Call을 호출하여 다른 프로세스에게 CPU를 배정하는 일을 하게 만든다. 이렇게 OS가 각 애플리케이션의 처리를 나눠서 조금씩 실행시키는 기능을 '디스패치(Dispatch)'라고 부른다.


 하지만 이런 방식으로 CPU를 나눠 쓰며 멀티태스킹을 구현하게 되면 문제가 생긴다.

'전환점'은 어플리케이션이 직접 호출하는 것이므로 어플리케이션 설계 단계에서 어디가 전환점인지를 알아야 한다. 다시 말해서 CPU 배분이라는 OS 고유의 권한을 어플리케이션 설계자에게 전가할 수밖에 없다는 것이다. 또한 '전환점'을 사용하게 되면 이 '전환점'이 호출되기 전에는 절대로 다른 프로세스에게 CPU가 배분되지 않는다는 문제도 발생한다. 따라서 다른 방법을 사용해서 멀티태스킹을 구현할 필요가 있게 되는데, 이후에는 따로 '전환점'의 System Call을 사용하지 않고 어떤 것이든 System Call을 사용한다면 즉시 Dispatch가 일어나도록 했다.


 하지만 이 방식에도 문제가 많다.

시스템 콜을 잘 사용하지 않고 연산만을 많이 수행하는 프로그램이나 소프트웨어상의 결함으로 무한 루프에 빠져버려 시스템 콜을 호출하지 않는 프로세스가 있다고 가정하면 다른 프로세스는 CPU를 할당받을 수가 없게 된다. 이를 방지하기 위해 최종적으로 CPU는 타이머와 끼어들기(interrupt)를 이용한 멀티 태스킹을 구현했다. 끼어들기는 CPU가 어떤 작업을 진행하고 있어도 이것을 강제로 중단시키고 OS로 돌아와 다른 프로세스에게 CPU를 할당할 수 있도록 하는 신호를 의미한다.


OS는 어플리케이션으로 CPU 제어를 넘기기 전에 항상 타이머를 설정하여 일정 시간 이후 끼어들기가 발생하도록 한다.

그러면 어플리케이션으로 제어가 넘어간 후에 무한 루프나 긴 연산으로 System Call이 발생하지 않더라도 일정 시간 후에는 강제적으로 OS로 제어를 되돌릴 수 있다.


 이렇게 타이머 끼어들기를 사용하는 프로그램 전환 방법을 시간을 짧게 구분해서 애플리케이션끼리 서로 나눈다고 하여 '시분할 시스템'이라고 한다. 앞에서 언급한 시스템 호출을 계기로 CPU를 재배정하는 방식의 멀티 태스킹은 다른 타 어플리케이션의 존재를 의식하고 계속 System Call을 호출해주지 않으면 작동할 수 없기 때문에 '협조적 멀티태스킹'이라고 부르며, 시분할 시스템처럼 타 어플리케이션의 중단을 기다릴 필요가 없는 멀티태스킹 방식을 '비협조적 멀티태스킹'이라고 부른다. 또한 이런 '비협조적 멀티태스킹'은 다른 어플리케이션의 중단을 기다리지 않고도 처리를 가로챌 수 있다는 점에서, 즉 '먼저 취할 수 있다'는 점에서 '선점 멀티태스킹(Preemptive multitasking)'이라고도 하며, 그 반대로 다른 어플리케이션의 처리를 먼저 취할 수 없는 멀티태스킹을 '비선점 멀티태스킹'이라고 한다.

'Knowledge' 카테고리의 다른 글

Memory Segmentation  (0) 2016.12.27
NAT와 NAPT의 개념과 원리  (10) 2016.12.01
멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
PCB (Process Control Block)  (0) 2016.11.14
[Assembly] Intel x86, Local JMP [0xE9]  (0) 2016.11.07
[BigData] Spark 공부  (0) 2016.10.19
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요