안경잡이개발자

728x90
반응형

윈도우에서 도커 툴 박스(Docker Toolbox)를 설치해서 도커 구동시키기

나동빈


※ 도커의 동작 과정 살펴보기 ※


  지난 시간에는 간단히 도커(Docker)에 대한 소개를 하는 시간을 가졌습니다. 이번 시간에는 직접 윈도우(Windows) 운영체제에 도커를 설치해서 구동시켜보는 시간을 가져보도록 하겠습니다. 먼저 간단하게 도커를 설치하기 이전에 이해도를 높이기 위해서 도커를 설치한 뒤의 실행 화면을 먼저 살펴보도록 하겠습니다. 이전 시간에도 잠깐 소개했던 솔로몬(Solomon) Hykes의 도커에 대한 발표 내용입니다.



  '리눅스 컨테이너의 미래'라는 거창한 이름의 발표입니다. 일단 바로 중반부에 등장하는 내용을 발췌해보겠습니다.


docker ps

=> 현재 돌아가고 있는 컨테이너를 확인하는 명령어입니다.


docker images

=> 현재 도커 머신에 설치된 도커 이미지를 확인하는 명령어입니다.


docker run -a busybox echo 'Hello World'

=> 먼저 로컬 서버에서 busybox라는 이미지를 찾습니다.

=> 로컬 서버에 해당 이미지가 없으면 도커 허브(Docker Hub)에서 자동으로 다운로드 받습니다.

=> 해당 이미지를 이용해 컨테이너를 실행합니다.

=> 컨테이너 실행 과정에서 디스크 및 메모리를 할당해서 순식간에 리눅스 서버를 구축합니다.

=> 새롭게 생성된 리눅스 서버에서 'Hello World'를 출력합니다.

=> 출력된 결과를 유닉스 소켓을 이용해 클라이언트로 전송하여 화면에 출력합니다.


  발표에서는 정말 쉽게 쉽게 명령어를 입력하고 있는 것처럼 보이지만, 내부적으로는 엄청난 일들이 일어나고 있는 겁니다!


※ 도커 소프트웨어의 개요 ※


  도커는 크게 ① 오픈소스 무료 버전(Community Edition), ② 유료 버전(Enterprise Edition)으로 나뉩니다.


  기본적으로 CE 버전으로도 도커의 모든 기능을 이용하는데 큰 제약이 없지만 안정적으로 실 서버에 도커를 운용하려면 EE를 사용하는 것이 훨씬 유리합니다. 기본적으로 도커 EE를 쓰면 각 서버별로 연간 1,000 달러 이내의 라이선스 비용이 소요됩니다. 다만 전반적인 배포 과정을 훨씬 편리하게 해주어 효용이 높습니다. 또한 현재는 기본적으로 도커 엔진 자체가 사실상 스웜(Swarm)이나 쿠버네티스(Kubernetes) 기술을 지원하고 있어서 유료 서비스만 잘 엮어서 쓰면 매우 강력한 기능을 쉽게 이용할 수 있습니다.


  도커 엔진(Docker Engine): 특정한 소프트웨어를 도커 컨테이너로 만들고 실행하게 해주는 데몬(Daemon)을 의미합니다. 도커 엔진과 도커 클라이언트 사이에는 REST API가 사용됩니다. REST API 서버에 요청을 보내는 것이 도커 클라이언트입니다.


  도커 클라이언트(Docker Client): 도커 엔진과 통신하는 소프트웨어로 개발자가 직접 이용할 수 있습니다. 윈도우(Window)/맥(Mac)/리눅스(Linux)를 지원합니다. 물론 윈도우는 도커를 사용하기에 최악의 조건이고, 리눅스가 제일 최상의 조건이지만 도커는 공식적으로 윈도우를 제대로 지원하고 있습니다.


  도커 호스트 운영체제(Docker Host OS): 도커 엔진을 설치할 수 있는 운영체제 환경을 의미합니다. 64비트 리눅스 커널 버전 3.10 이상 환경을 의미하고, 32비트 환경에서는 도커 엔진이 돌아가지 않는답니다. 애초에 초기의 도커 이미지는 심지어 오직 우분투(Ubuntu) 운영체제 전용이었어요. 현재는 우분투, CentOS, Debian, Fedora 등에서 사용할 수 있게 되었지만요.


  추가적으로 언급하자면, 사실 윈도우는 Docker Host OS가 될 수 없습니다. 당연한 거죠. 도커 엔진은 기본적으로 리눅스 OS 위에서 돌아가는거니까 가상화 기능이 필요합니다. 하지만 다행히도 마이크로소프트(Microsoft)는 기본적으로 도커(Docker)에 많은 관심을 가지고 있는 상황입니다. 그 결과 마이크로소프트는 도커의 초기 버전부터 많은 모듈을 덧붙여서 윈도우 파일 시스템과 도커의 파일 시스템이 연동될 수 있도록 모듈을 개발하여, 현재는 윈도우 컨테이너가 도커 명령어로 정상적으로 작동하고 리눅스 컨테이너와 상호 통신까지 가능합니다.


  도커 머신(Docker Machine): 로컬 및 원격지 서버에 도커 엔진을 설치하고, 다양한 환경 설정을 자동으로 수행해주는 클라이언트를 의미합니다.


  우리가 이번 시간에 설치하게 될 도커 툴박스(Docker Toolbox)는 도커 CE의 이전 버전인데, 윈도우 10 Enterprise/Professional 이전 버전의 윈도우 사용자는 필수적으로 이러한 도커 툴박스를 이용해야 합니다. 물론 기능상으로 큰 문제는 없기 때문에 그냥 이용하시면 됩니다.


▶ 도커 툴 박스 설치 사이트: https://docs.docker.com/toolbox/toolbox_install_windows/


  위 사이트에 접속하면 기본적으로 윈도우 10 Enterprise/Professional은 툴박스가 아니라 윈도우 도커(Docker For Windows) 애플리케이션을 설치할 것을 권장하는 것을 알 수 있습니다. 하지만 우리는 최신 버전의 도커 애플리케이션을 이용하지 않고, 툴박스를 이용할 겁니다. 툴박스를 이용해도 기능적으로는 문제가 없습니다. 이용상의 장단점에 대해서는 차차 알아가시면 됩니다.

  

  우리가 설치할 도커 툴박스는 기본적으로 오라클 VM VirtualBox를 기반으로 동작합니다. 쉽게 말하면 윈도우 위에 리눅스 가상 머신을 돌리고, 그 위에서 도커 엔진을 돌리는 방식이라고 이해할 수 있습니다. 그래서 일단 기본적인 VM 가상화 기능을 사용할 수 있어야 합니다. 따라서 윈도우 운영체제에서 '작업 관리자'의 '성능' 탭에 들어갔을 때 가상화 기능이 활성화 되어있어야 합니다.



  위 사진은 제 컴퓨터의 '작업 관리자'입니다. 저는 윈도우 10 홈(Home) 버전을 쓰고 있어서 이렇게 나오는 건데요, 그냥 '가상화'가 '사용'이라고 되어 있는 것을 체크하시면 됩니다. VM 기능을 이용할 수 있는 상태이므로 바로 도커 툴박스 버전을 설치하면 되는 겁니다. 만약에 '가상화'가 비활성화 되어있으신 분은 노트북 혹은 데스크탑을 재부팅하셔서 바이오스(Bios)에 진입하여 '가상화' 기능을 활성화 해주시면 됩니다.



  이제 바로 위와 같이 'Get Docker Toolbox for Windows'를 눌러서 툴박스 설치 프로그램을 다운로드 받아주세요!


※ 툴박스를 설치하면 자동으로 설치되는 것들 ※


- 도커 엔진이 이미지 및 컨테이너를 생성할 수 있도록 해주는 도커 클라이언트(Docker Client)

- 윈도우 터미널에서 사용하는 도커 엔진 명령어를 이용하게 해주는 도커 머신(Docker Machine)

- 도커 컴포즈(Docker Compose) 기능

- 도커 GUI 도구인 카이트메틱(Kitematic)

- 도커 명령어 입력이 가능한 도커 빠른시작 터미널(Docker QuickStart Shell)

- 오라클 VM VirtualBox


  말했듯이 윈도우에서는 기본적으로 도커 엔진을 구동시킬 수 없습니다. 그래서 작은 크기의 VM 리눅스 위에서 도커 머신 명령어를 입력하는 식으로 도커 엔진에 접근할 수 있습니다. 즉, VM이 도커 엔진의 호스트 역할을 수행하게 되는 겁니다. 그렇기 때문에 당연히 도커 툴박스 관리 도구 및 ISO 파일이 함께 설치됩니다. 이 ISO 파일이 오라클 VM VirtualBox 위에서 동작하는 것이라고 볼 수 있습니다.



  도커 툴박스 설치 프로그램을 실행하면 위와 같습니다.



  설치 경로를 물어보는데 기본 경로로 넣으시면 됩니다. 나중에 이 경로가 도커 기본 터미널의 경로로 설정됩니다.



  이제 설치 구성 목록을 물어보는데, 전체 설치(Full Installation)으로 설정해주세요. 보시면 도커 컴포즈(Docker Compose) 및 VirtualBox와 Kitematic이 설치되는 것을 알 수 있습니다. 전부 위에서 언급했던 것들이에요. 추가적으로 추후에 도커 이미지 생성을 위한 각종 파일들은 모두 깃(Git)을 이용해서 관리하는 것이 일반적이므로 혹여나 깃이 설치되어있지 않으신 분들은 설치해주시는 것을 강력히 추천합니다.



  이후에 위와 같이 기본적인 환경설정 요소를 체크해주시고 넘어가시면 됩니다.



  이제 설치를 진행합니다.



  설치 완료 이후에는 다음과 같이 세 개의 아이콘이 바탕화면에 나타날 겁니다.



※ 도커의 기본 컨셉 ※


  도커는 기본적으로 개발, 배포, 실행 단계를 지원하는 목적으로 사용됩니다. 애플리케이션을 배포할 때는 컨테이너화(Containerization)하여 배포하게 되는데, 말 그대로 특정한 애플리케이션을 컨테이너에 담아서 구동시키는 것을 의미합니다. 컨테이너가 가지는 다양한 특징들 때문에 '컨테이너화' 기술은 매우 각광받게 되었습니다.


- 유동성(Flexible): 매우 복잡한 소프트웨어들이 컨테이너화 될 수 있습니다.

- 가벼움(Lightweight): 컨테이너는 호스트 커널의 기능을 극대화하고 공유할 수 있습니다.

- 상호변경성(Interchangable): 업데이트 및 업그레이드를 즉시 수행할 수 있습니다.

- 이식성(Portable): 로컬에서 빌드하고, 클라우드에 배포하고, 어디에서나 실행할 수 있습니다.

- 확장성(Scalable): 컨테이너의 복제품을 자동적으로 증가시키고 분배할 수 있습니다.

- 수직성(Stackable): 다양한 서비스를 쌓아 올려 훌륭한 서비스를 만들 수 있습니다.


※ 도커 기본 사용법 ※


  위 사진은 도커 실행 화면입니다.



docker --version 명령어를 입력해서 설치된 도커의 버전을 확인할 수 있습니다.

docker info 명령어를 입력해서 설치된 도커의 버전을 보다 상세하게 확인할 수 있습니다.



docker run hello-world: 도커에서 헬로우 월드(Hello World)를 띄우는 명령어입니다. 우리 눈에는 보이지 않지만 다음의 과정이 포함되는 겁니다.


1) hello-world라는 이미지를 검색한다.

2) 로컬에 이미지가 있으면 쓰고, 없으면 도커 허브(Docker Hub)에서 내려받는다.

3) 해당 이미지를 컨테이너로 동작시킨다.


  결과적으로 위 화면과 같이 Hello World가 성공적으로 출력되었습니다.



docker images: 현재 도커 머신에 존재하는 이미지 목록을 출력합니다.



docker ps -a: 현재 도커 머신에 존재하는 컨테이너를 출력합니다.

728x90
반응형

Comment +0

728x90
반응형

도커(Docker)의 개요

나동빈


※ 도커의 개요 ※


  이번 시간에는 최근까지 소프트웨어 개발자들에게 계속 핫한 주제였던 도커(Docker)에 대해서 알아보도록 하겠습니다. 도커(Docker)리눅스 컨테이너를 기반으로 하여 특정한 서비스를 패키징하고 배포하는데 유용한 오픈소스 프로그램입니다.



  도커 공식 서비스는 위 사진과 같이 귀여운 고래 모양의 캐릭터를 메인 로고로서 사용하고 있습니다.


※ 도커를 사용해야 하는 이유 ※


  이번 글에서는 도커를 사용해야 하는 이유에 대해서만 제대로 이해하셔도 충분합니다. 기존에 다양한 형태로 개발을 하셨던 분들이라면 Maven, Gradle, FTP 등의 다양한 패키징 및 배포 도구를 사용해보신 적 있으실 거에요. 이러한 배포 도구들이 사용되었던 이유는, 특정한 소프트웨어를 개발한 이후에 배포하고자 할 때 해당 프로그램이 어디에선가 손쉽게 구동이 되도록 하기 위해서에요. 예를 들어 열심히 웹 개발을 했는데, 실제로 접속이 가능한 웹 서버에 배포를 하지 못한다면 누구도 그 서비스를 이용할 수 없겠죠?


  예를 들어 저는 이전에 JSP를 이용해서 웹 사이트를 개발하는 방법에 대해 소개한 적이 있었어요. 웹 사이트를 개발한 이후 마지막 단계는 배포 및 운영 단계입니다. 그럴 때는 카페 24나 AWS 등의 클라우드 서비스를 이용해 웹 서비스를 배포하는 것이 일반적이에요. 그런데, 일반적으로 이러한 '호스팅 서비스'는 우리의 로컬 컴퓨터와는 차이점이 존재해요. 특정한 서버 환경에 우리가 만든 웹 서비스를 맞추어서 배포해야 하므로 갖가지 설정이 필요하죠. 초기 상태에서 시작한다면 JVM을 설치해야 되고, 톰캣(Tomcat)도 필요하고, 등 할 게 많아요. 또 같은 리눅스라고 해서 괜찮은 것도 아니고, 리눅스 버전에 따라서도 설치 방법이 다르답니다.



  그래서 등장한 것이 바로 '컨테이너(Container)'의 개념이에요. 도커를 설치하면, 컨테이너에 '이미지(Image)'를 담아서 구동시키는 방식으로 손쉽게 배포할 수 있어요. 이러한 컨테이너를 어떻게 만들고, 활용할 지에 대해서는 미리 도커 소프트웨어가 정의를 해 놓고 있어서 도커를 사용하는 우리들은 단순히 이미 배포된 소프트웨어 이미지를 도커를 통해 컨테이너에 올리면 되요. 다시 말해서 우리는 도커만 설치를 하면 나머지는 알아서 설정이 이루어지는 겁니다. JVM, 톰캣 등을 따로따로 설치할 필요가 없으며 도커 하나만 있으면 이 친구가 알아서 해결해줘요.



  나중에 자세히 알아보겠지만, 위 그림과 같이 도커 파일(Docker File)이라는 것을 만들어서 '나는 어떠한 소프트웨어를 컨테이너에 담아서 구동시킬 것이다.'라는 것만 정확히 명시해주고 빌드(Build)를 해주면, 알아서 도커 이미지가 그에 맞게 생성이 됩니다. 그래서 그 이미지를 구동시키면 순식간에(웹 서버도 몇 초만에 뚝딱!) 도커 컨테이너 위에서 실행이 됩니다.


  물론 도커 이전에도 이러한 컨테이너(Container) 기반의 기술이 다수 존재했었어요. 하지만 도커의 고유한 창의적인 기능들과 편리함 때문에 현재 도커가 패키징, 배포 시장을 잠식하고 있는 상태랍니다. 도커는 말씀 드렸듯이 컨테이너(Container) 형태로 동작하는데, 기존에 VM(Virtual Machine) 서비스를 이용해 본 적이 있으신 분들이라면 다소 헷갈릴 수 있어요. 그래서 VM과 도커가 어떻게 다른 기술인지 확실히 이해할 필요가 있답니다.


※ VM(Virtual Machine)과 컨테이너(Container)는 무엇이 다를까 ※


  기본적으로 기존에 우리가 많이 사용하던 VM(가상 머신)과 컨테이너는 큰 차이가 있습니다. 가상 머신은 가상화 기능을 사용해 Guest OS라는 것을 만들어냅니다. 예를 들어 윈도우(Windows) 운영체제를 사용하는 사람이 칼리 리눅스를 새롭게 가상 머신 위에서 구동시킨다면, 윈도우는 Host OS이고 칼리 리눅스는 Guest OS입니다. 다시 말해 Guest OS와 Host OS는 사실상 완전히 별개로 존재할 수 있다는 점에서 서로 의존적이지 않습니다. 대신에 Guest OS의 I/O 기능이 사실상 Host OS를 거쳐서 이루어지기 때문에 속도가 느리다는 단점이 있습니다. 뿐만 아니라 Guest OS 자체가 완전히 새로운 운영체제로서 존재해야되기 때문에 아무리 가벼운 OS를 설치하려고 해도, 용량이 큽니다.



  반면에 컨테이너(Container)는 가상 머신 대신에 도커 엔진(Docker Engine) 위에서 동작한다는 특징이 있어요. 이렇게 해주면 별도의 Guest OS가 사용되지 않아서 성능적으로 매우 개선됩니다. 메모리 용량도 적게 차지하고요. 다만 기본적으로 사용하고 있는 운영체제와 도커의 컨테이너(Container)에 의존성이 존재합니다. 이로 인해 각종 보안 문제로 공격을 받은 적이 있었구요. 기본적으로 윈도우(Windows) 운영체제를 사용하고 있는 컴퓨터는, 도커 엔진 위에 올라가는 컨테이너도 윈도우 컨테이너(Windows Container)여야 합니다. (다행히도 최근에는 도커와 윈도우의 호환성이 매우 높아지고 있어서 사실상 리눅스 컨테이너와 윈도우 컨테이너의 구분이 모호해지고 있습니다.)


  도커는 Container라서 VM에 비해 이미지 파일의 크기가 작아 빠르게 이미지를 만들고 실행할 수 있다는 특징이 있습니다. 예를 들어 대표적인 리눅스 운영체제 배포판 중에서 알파인(Alpine)은 최소한의 기능만 들어가 있어서 5MB 가량으로 작게 구성되어 있습니다. 가상 머신 기술을 이용하는 경우 아무리 운영체제를 가볍게 설치하려고 해도 최소한 기가 바이트 단위인데, 이렇게 Container를 이용하면 그 크기가 200MB 전후로 줄어들 수 있어서 가히 초경량 배포 도구라고 할 수 있답니다.


※ Solomon Hykes의 도커(Docker) ※


  도커의 시작을 알린 사람은 Solomon Hykes로 2013년 PyCon에서 '리눅스 컨테이너의 미래'라는 제목으로 도커에 대해서 발표했습니다. 발표 내용은 유튜브(YouTube)에도 올라와 있어서 손쉽게 확인할 수 있습니다. (https://www.youtube.com/watch?v=wW9CAH9nSLs)



  보시면 솔로몬 씨가 'Hello World'라는 문장을 띄운 것을 알 수 있습니다. 도대체 Hello World를 띄운 것이 뭐가 그렇게 대단하다고 사람들이 이렇게 도커에 열광하는 걸까요? 그것은 'Hello World'가 새로운 운영체제 위에서 출력이 된 것이기 때문입니다. 이것은 엄청난 발전입니다. 아니, 몇 초 만에 별도의 운영체제를 생성한 뒤에 'Hello World'를 출력한 것이니까요. VM을 사용해보신 분들이라면 이게 얼마나 편리한 것인지 감이 오실 겁니다.


  리눅스 컨테이너(Linux Container)는 오래 전부터 개발되던 기술입니다. 그 역사만 10년이 넘었습니다. 하지만 리눅스 컨테이너를 개발하거나 활용하는 기술은 복잡하고 까다로웠습니다. 그래서 다양한 회사가 컨테이너 기술을 사용하고 있음에도 불구하고 일반 개발자들 사이까지 널리 퍼지지는 못했습니다. 그러한 배경에서 도커가 등장함에 따라 그 방향이 바뀌게 됩니다. 리눅스 컨테이너 기술을 기반으로 하되, 매우 편리한 인터페이스 및 명령어를 가지고 있어서 컨테이너 기술을 잘 모르는 개발자들도 손쉽게 도커를 이용해 컨테이너를 띄워볼 수 있게 된 것입니다.


※ 도커는 어떻게 만들어졌을까? ※


  도커는 기본적으로 도커 엔진(Docker Engine)에 의해 동작합니다. 도커 엔진의 핵심 로직은 Google의 Go 언어로 개발이 되어 있습니다. Go로 개발된 도커 엔진은 기존의 C언어로 개발된 모듈보다 더욱 빠르게 동작합니다. Go 프로그래밍 언어는 기본적으로 포인터 등의 기능을 지원하며 Garbage Collector나 클래스 등의 기능까지 지원하는 언어입니다. 최근에는 깃(Git) 서버도 Go 언어로 개발이 되고 있습니다. 실제로 그렇게 Go로 개발한 서버 모듈이 빠르게 동작하죠. Go로 개발된 소프트웨어 중에서 가장 잘 나가고 있는 것이 바로 도커(Docker)인 것입니다.


※ 도커 패러다임 ※


  도커는 변경 불가능한 인프라(Immutable Infrastructure)를 주요 패러다임으로 상정하고 있습니다. 기본적으로 특정한 소프트웨어가 자주 수정되는 경우 서비스 안정성에 여러 가지 문제가 발생할 수 있습니다. 특히 언제 어떠한 부분이 변경되었는지 등의 이력 관리를 제대로 하는 것이 매우 중요합니다. 그러한 배경에서 아예 변경 자체를 못하도록 하자고 해서 '변경 불가능한 인프라'의 개념이 나타나게 되었는데, 이것은 서버를 구축한 이후에는 변경이나 업데이트를 할 수 없도록 하는 것입니다.


  그렇다면 서버에 문제가 생겨서 업데이트를 해야 되는 경우는 어떻게 할까요? 그냥 삭제해버리는 겁니다!


  서버 패치 등은 지속적으로 이루어져야 하는데 '어떻게 한 번 서버를 구축하고 나서 업데이트를 안 할 수 있을까요?' 사실 이는 처음 들었을 때 이해하기 힘들 수도 있는데요. 도커를 쓰다보면 서버 자체를 들었다 놨다 하는 것이 익숙해지실 겁니다. 다시 말해, 서버를 업데이트를 해야 할 때는 새로운 도커 이미지 자체를 업데이트 해서 그냥 컨테이너에 올리는 겁니다. 기존에 돌아가던 컨테이너는 완전히 '제거' 해버리구요.


  도커에서는 그냥 이미지를 도커 위에 올리는 것 만으로도 배포가 끝나버립니다. '변경 불가능한(Immutable)' 이라는 의미는 그 자체로 '상태가 없는(Stateless)', '확장 가능한(Scalable)' 형태입니다. 도커로 돌아가는 소프트웨어 자체는 어떠한 데이터도 가지고 있으면 안 되고, 임시 파일 정도만 가지고 있는 상태에서 돌아가야 합니다. 사용자 계정 정보와 같은 실질적인 데이터들은 외부 스토리지로 빼 놓아야 하는 겁니다. 최근에 자주 언급되고 있는 마이크로 서비스(Micro Service)의 특징과 매우 닮아있습니다.


※ 리눅스 컨테이너 ※


  도커는 말했듯이 대표적인 컨테이너 기술입니다. 일반적으로 리눅스 컨테이너(LXC)리눅스상에서 여러 개의 격리된 리눅스 시스템을 동작시키기 위한 운영체제 단의 가상화 기술을 의미합니다. Warden, Libvirt가 그 대표적인 예시입니다. 리눅스 커널(Kernel) 위에 Libvirt가 돌아가며 그 위에 컨테이너가 올라가는 방식이지요. 그래서 이러한 LXC 기술에 대한 이해가 높으신 분은 사실 도커와 같은 기술을 자체적으로 개발할 수도 있는 겁니다. 구글이 발표한 자료에 따르면 매주 수십억 개의 컨테이너가 전 세계적으로 구동된다고 합니다. 또한 구글이 운영하는 모든 서비스는 컨테이너를 기반으로 돌아갑니다. 왜냐구요? 성능이 좋으니까요. 구글은 도커 이전에도 이미 이러한 리눅스 컨테이너 기술을 사용하고 있었고, 최근에는 도커 기반의 기술을 많이 활용하고 있답니다.


  앞에서도 언급했지만 도커 이전의 LXC 기술들은 기본적으로 오래 전부터 사용되고 있었으나 설정이 매우 복잡하고, 사용하기 어려워 널리 알려지지 않았던 것입니다. 도커가 출시되면서 10분도 채 소요되지 않는 시간에 대부분의 서비스를 구동시킬 수 있어서 실질적으로 대부분의 개발자가 이용할 수 있는 레벨까지 편리성을 제공하게 된 것입니다.

728x90
반응형

Comment +0

728x90
반응형

  Pwn Adventure 3게임 해킹(Game Hacking) 공부를 목표로 하여 만들어진 게임입니다. 일종의 MMORPG 게임인데, 이 게임에서는 모든 것을 해 볼 수 있습니다. 이 게임은 간단한 형태의 매우 많은 취약점을 내장하고 있다는 점에서 하늘 날기(Flying), 돈 버그(Endless Cash), 이외에도 클라이언트 데이터 변조 혹은 네트워크 해킹이 가능합니다.


  물론 이러한 게임 해킹 과정을 통해서 단순히 게임을 불법적으로 즐기는 것이 아니라, 취약점을 찾아서 게임을 개발한 사람에게 알려주겠다는 착한 마인드로 공부를 하셔야 됩니다. 이 게임은 개발자가 놓치기 쉬운 부분들을 누락시켜서, 고의적으로 문제가 있도록 개발이 되어있습니다. 그래서 엔진 자체를 해킹하려는 시도보다는 이 게임을 면밀히 분석해서 해킹하는게 좋습니다.


  ▶ 공식 웹 주소: http://www.pwnadventure.com



  사이트에 접속하면 바로 게임을 다운로드 해볼 수 있습니다.



  자신의 운영체제에 맞게 다운로드를 진행해주세요.



  압축 파일을 압축 해제 해보시면 위와 같이 설치 프로그램이 존재합니다. 이를 실행해서 설치를 진행해봅시다.



  설치를 위해서 외국 서버로부터 데이터를 받아오는데, 외국 서버가 생각보다 많이 느려서 기다리실 필요가 있습니다.



  설치가 완료되면 'Play' 버튼을 눌러서 게임을 시작할 수 있습니다.



  아직 우리는 서버를 설정하지 않았기 때문에 오프라인 플레이(Offline Play)를 눌러봅시다.



  그러면 위와 같이 캐릭터를 선택하여 생성할 수 있습니다.



  간단하게 게임을 실행해보면서 대략적인 게임 방법에 대해 알아보는 시간을 가지시면 됩니다.

728x90
반응형

Comment +0

728x90
반응형

pwnable.kr - lotto 문제풀이(Write Up)

문제 분류: 공격(Attack)


  이번 시간에 풀어 볼 문제는 pwnable.kr의 lotto입니다. 기존에 기초 프로그래밍 위주로 학습하고 보안 분야로 넘어오신 분들에게 적합한 문제라고 생각되어 이 문제 또한 앞 부분에서 다루게 되었습니다. 더불어 난이도 또한 낮은 기초 문제라서 기초 C언어 지식으로도 문제를 해결하실 수 있답니다.


  ▶ 문제풀이 사이트 주소: http://pwnable.kr/play.php



  이 문제는 SSH를 이용해 통신해야 하는 문제입니다. SSH는 원격지에 있는 서버에 접속하여 서버를 관리할 수 있도록 해주는 프로토콜입니다. 윈도우 10은 기본적으로 OpenSSH를 내장하고 있어 쉽게 SSH를 사용할 수 있지만 사용이 어려운 경우 Putty와 같은 별도의 소프트웨어가 필요할 수 있습니다. Putty는 https://www.putty.org에 접속하여 설치가 가능합니다.



  저는 위와 같이 윈도우에 내장된 OpenSSH를 이용해 서버에 접속해보았습니다.



  접속 이후에는 ls 명령어를 이용해 위와 같이 현재 루트 디렉토리에 어떠한 파일이 존재하는지 확인할 수 있습니다. lotto 실행 파일과 lotto.c 소스코드 파일이 존재합니다. 일반적으로 이러한 분석 및 공격 분류의 문제는 소스코드를 보면서 취약점을 파악해 실행 파일에 적용하여 Flag 값을 얻을 수 있는 방식입니다. 일단 간단히 ./lotto 명령어로 로또 프로그램을 테스트해보겠습니다.



  6자리의 로또 코드를 입력하라고 하는 군요. 다만, 제가 알기로 로또는 1부터 45까지의 숫자를 넣는 것인데 6자리를 문자열로 넣는 것이 조금 이상하다는 생각이 들었습니다. 일단 아래와 같이 cat lotto.c 명령어를 입력해 lotto.c 소스코드를 분석할 수 있습니다.



  소스코드를 확인한 결과 랜덤한 6개의 숫자가 lotto[] 배열에 저장되고, 숫자는 아스키 코드로 1부터 45까지의 랜덤한 숫자로 추출됩니다. 다만 아래쪽을 보면 이중 For문을 이용해 우리가 입력한 문자열과 배정된 당첨 lotto[] 번호가 일치하는지 확인하는 구문이 나옵니다. 저의 문자열 매칭 알고리즘 강의(https://blog.naver.com/ndb796/221240660061)를 들으신 분이라면 위 알고리즘이 잘못되었다는 것을 쉽게 파악할 수 있을 겁니다. 위 알고리즘은 단 하나의 로또 번호라도 일치한다면, match 변수의 값이 6이 됩니다. 즉, 하나만 번호가 일치해도 당첨이 되어버리는 잘못 작성된 소스코드라고 할 수 있습니다.


  우리는 이렇게 잘못 작성된 알고리즘을 이용하여 로또에 쉽게 당첨되는 공격(Attack) 시도를 할 것입니다.



  먼저 아스키 코드표를 봅시다. 이제 위에서 보이는 1부터 45까지의 아스키 코드 중에서 아무거나 6개를 넣으면 됩니다. 그러면 우연히 1개의 문자라도 로또 당첨 번호와 일치하는 경우 플래그(Flag) 값을 내뱉을 것입니다. 통계적으로 생각했을 때 대략 3~4번만 시도해도 답이 도출될 것이라 판단할 수 있습니다.



  위와 같이 문자열 "######"으로 몇 번 입력을 시도하니 플래그 값이 나왔습니다.

728x90
반응형

Comment +2

728x90
반응형

pwnable.kr - coin1 문제풀이(Write Up)

문제 분류: 코딩(Coding)


  이번 시간에 풀어 볼 문제는 대표적인 CTF 문제풀이 사이트인 pwnable.kr의 기초 문제 중 'coin1' 문제입니다. CTF 공부 카테고리에서 실질적으로 풀어보는 첫 번째 문제가 될 겁니다. pwnable.kr 공식 웹 사이트에서 coin1 문제를 확인하실 수 있습니다.


  ▶ 문제풀이 사이트 주소: http://pwnable.kr/play.php



  문제를 확인해보면 pwnable.kr에 9007번 포트로 접속해보라고 나오는 것을 알 수 있습니다.



  문제를 요약하면 다음과 같습니다.

 

- N은 코인의 개수, C는 시도 횟수

- 서버로 코인의 번호들을 보내면 서버는 해당 코인들의 무게 합을 반환합니다.

- 가짜 코인이 하나 숨어있는데, 가짜 코인의 무게는 9이고 정상 코인의 무게는 10입니다.

 

  따라서 이 문제를 해결하기 위해서는 이분 탐색을 수행하면 됩니다. 예를 들어 전체 코인이 4개가 있고, 각 코인의 무게가 다음과 같다고 해봅시다.

 

코인: 0 1 2 3

무게: 10 10 9 10

 

  이 때 맨 처음에 서버로 “0 1”을 보낼 수 있습니다. 그러면 서버에서는 두 코인의 무게 합인 20을 반환할 겁니다. 우리는 이 20이라는 결과를 통해서 , 01 중에는 가짜 코인이 없구나.’ 라는 것을 알 수 있습니다. 그러면 01은 더 이상 볼 필요 없이 23을 보면 됩니다.

 

  23 중에서 반드시 하나는 가짜 코인이 섞여있다는 것을 알고 있기 때문에 “2”만 보내면 됩니다. 만약에 서버로부터 돌아오는 결과가 9라면 2가 가짜 코인이고, 서버로부터 돌아오는 결과가 10이라면 3이 가짜 코인이 되는 것입니다.

 

  이와 같이 현재 가짜 코인이 섞여있는 코인의 집합 중에서 계속해서 절반(Half)만 확인하는 식으로 매 번 검사할 때마다 검사하는 집합의 크기를 1/2로 줄일 수 있습니다. 이것은 이분 탐색의 대표적인 예라고 할 수 있으며 통상적으로 코인의 개수가 N개일 때 O(logN)의 시간 복잡도를 가집니다.

 

  이제 문제를 푸는 방법을 알았으므로 Java를 이용해 서버 통신 모듈을 개발합니다.

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


private static Socket sock = null;

private static OutputStream out = null;

private static InputStream in = null;

private static BufferedWriter bw = null;

private static BufferedReader br = null;


public static void init() {

try {

sock = new Socket("pwnable.kr", 9007);

out = sock.getOutputStream();

in = sock.getInputStream();

bw = new BufferedWriter(new OutputStreamWriter(out));

br = new BufferedReader(new InputStreamReader(in));

} catch (Exception e) {

e.printStackTrace();

}

}


public static void close() {

try {

bw.close();

br.close();

sock.close();

} catch (IOException e) {

e.printStackTrace();

}

}


public static int findAnswer() {

    try {

    // 서버로부터 메시지를 전달 받습니다.

    String line = null;

        while((line = br.readLine()) != null){

        // 전달 받은 메시지를 출력합니다.

        System.out.println(line);

        // 첫 번째 문자가 'N'인 경우 문제 풀이가 시작된 것입니다.

        if(line.length() != 0 && line.charAt(0) == 'N') break;

        }

        // N과 C를 파싱하여 각각 숫자로 변환합니다.

        int N = Integer.parseInt(line.split("N=")[1].split(" C")[0]);

        int C = Integer.parseInt(line.split(" C=")[1]);

        System.out.println("N의 값은 " + N + ", C의 값은 " + C);

    } catch (Exception e) {

    e.printStackTrace();

    }

return -1;

    }


public static void main(String[] args) {

try {

init();

findAnswer();

close();

} catch (Exception e) {

System.out.println(e);

}

}

}

 

  전체 소스코드는 소켓 통신 관련 객체를 초기화하는 init() 함수, 소켓 통신 관련 객체를 파기하는 close() 함수, 서버와 직접 통신해 답을 찾아내는 findAnswer() 함수로 구성됩니다. 일단은 간단하게 findAnswer() 함수에서 서버로부터 메시지를 계속 전달 받되, 첫 번째 문자가 “N"인 라인을 만나면 NC의 값을 파싱하여 변수에 담는 것까지 작업해보았습니다.



  실행 결과는 위와 같습니다. 서버로부터 계속 데이터를 전달 받다가, “N=숫자 C=숫자의 형태를 만나면 이를 Javasplit() 함수를 이용해 숫자 정보만 파싱하는 것입니다. 이제 NC를 받아오는 모듈까지 작성했으므로 이를 확장시켜서 이분 탐색을 활용해 정답을 도출하도록 확장시키면 됩니다.


import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


private static Socket sock = null;

private static OutputStream out = null;

private static InputStream in = null;

private static BufferedWriter bw = null;

private static BufferedReader br = null;


public static void init() {

try {

sock = new Socket("pwnable.kr", 9007);

out = sock.getOutputStream();

in = sock.getInputStream();

bw = new BufferedWriter(new OutputStreamWriter(out));

br = new BufferedReader(new InputStreamReader(in));

} catch (Exception e) {

e.printStackTrace();

}

}


public static void close() {

try {

bw.close();

br.close();

sock.close();

} catch (IOException e) {

e.printStackTrace();

}

}


public static int findAnswer() {

    try {

    // 서버로부터 메시지를 전달 받습니다.

    String line = null;

        while((line = br.readLine()) != null){

        // 전달 받은 메시지를 출력합니다.

        System.out.println(line);

        // 첫 번째 문자가 'N'인 경우 문제 풀이가 시작된 것입니다.

        if(line.length() != 0 && line.charAt(0) == 'N') break;

        }

        // N과 C를 파싱하여 각각 숫자로 변환합니다.

        int N = Integer.parseInt(line.split("N=")[1].split(" C")[0]);

        int C = Integer.parseInt(line.split(" C=")[1]);

        int up = N - 1;

        int down = 0;

        

        while(down <= up && C > 0) {

            int middle = (up + down) / 2;

            String message = "";

            for(int i = down; i <= middle; i++) {

            message += i + " ";

            }

            // 서버로 확인하고자 하는 코인들의 번호를 전달합니다.

            System.out.println("보낸 값: " + message);

            bw.write(message);

            bw.newLine();

            bw.flush();

            C--;

            // 서버로부터 결과 정보를 전달 받습니다.

            line = br.readLine();

            System.out.println("받은 값: " + line);

            int next = Integer.parseInt(line);

            // 10으로 나누어진다면 해당 범위에 가짜 코인이 없습니다.

            if(next % 10 == 0) {

            down = middle + 1;

            }

            // 그렇지 않다면 해당 범위에 가짜 코인이 있습니다.

            else {

            up = middle - 1;

            }

        }

        // 아직 확인할 수 있는 횟수가 더 남았다면 다 쓸 때까지 전송합니다.

        if(C > 0) {

            bw.write(down + "");

            bw.newLine();

            bw.flush();

        }

        // 결과적으로 찾은 가짜 코인의 번호는 down에 담겨있습니다.

        return down;

    } catch (Exception e) {

    e.printStackTrace();

    }

return -1;

    }


public static void main(String[] args) {

try {

init();

// 게임이 여러 차례 존재하므로 무한정 실행시킵니다.

while(true) {

int bottom = findAnswer();

String message = bottom + "";

System.out.println("가짜 코인 발견: " + message);

// 한 차시 게임에서 찾은 답을 전송합니다.

            bw.write(message);

            bw.newLine();

            bw.flush();

}

} catch (Exception e) {

System.out.println(e);

}

}

}



  결과적으로 총 100차례의 게임이 빠르게 수행되고 플래그 값으로 b1NaRy_S34rch1nG_1s_3asy_p3asy를 찾았습니다. 이를 입력하면 정답 처리를 받게 됩니다.

728x90
반응형

Comment +0

728x90
반응형

자바(Java)를 이용해 문제풀이 서버와 통신하기

나동빈

 

  일반적으로 시스템 해킹을 공부하기 위한 각종 포너블(Pwnable) 사이트나 실제 CTF 대회에서는 Netcat 모듈을 이용해 문제풀이 서버와 통신하는 경우가 많습니다. 또한 실시간으로 서버와 상호작용을 해야 하는 경우가 많기 때문에 실제로 문제를 풀 때는 파이썬(Python)을 이용해서 서버와 통신하는 모듈을 구현하곤 합니다. 하지만 저와 같이 국내 개발자 출신의 해커들은 자바(Java)가 더 익숙한 경우가 많기 때문에 자바를 이용해 문제를 푸는 방법에 대해 소개하고자 합니다.

 

  먼저 기본적인 Netcat(nc) 명령어를 이용해 서버와 통신하는 화면은 다음과 같습니다. 저는 윈도우 운영체제를 이용하고 있어서 별도의 Netcat 프로그램을 환경변수로 설정했습니다.



  pwnable.krcoin1 문제를 풀기 위해 접속한 모습은 위와 같습니다. 어차피 nc 모듈도 서버와 소켓 통신을 이용해 메시지를 주고받는 프로그램에 불과하기 때문에 어떠한 프로그래밍 언어를 이용하던지 간에 똑같은 기능을 구현할 수 있습니다.

 

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

 

public class Main {

 

public static void main(String[] args) {

    try {

      Socket sock = new Socket("pwnable.kr", 9007);

      InputStream in = sock.getInputStream();

      BufferedReader br = new BufferedReader(new InputStreamReader(in));

      String line = null;

      while((line = br.readLine()) != null){

        System.out.println(line);

      }

      br.close();

      sock.close();

    } catch (Exception e) {

    System.out.println(e);

    }

  }

}

 

  따라서 위와 같이 Java에서 Socket 통신 기본 라이브러리를 활용해서 동일한 기능을 구현할 수 있습니다. 프로그램의 실행 결과는 다음과 같습니다. 저는 이클립스(Eclipse) 개발환경에서 실행해보았습니다.



  요약하여 설명하자면 서버로부터 어떠한 명령어를 기다리고자 하는 경우에는 두 개의 라이브러리 InputStreamBufferedReader를 이용해 서버로부터 특정한 메시지를 전달받을 수 있습니다. 구체적 동작 과정을 살펴보면 InputStream을 이용해 서버로부터 전달 받은 메시지 스트림(Stream)을 일정 크기의 버퍼(Buffer)에 지속적으로 담아서 문자(Character) 형태로 읽어들여 String 객체에 문자열 데이터를 담은 것입니다. 이 때 이렇게 버퍼를 쓰는 이유는 매 번 CPU 자원을 활용하지 않고 일정 주기로 CPU 자원을 활용하도록 해서 프로그램의 효율성을 높이는 것입니다.

 

그렇다면 서버로 어떠한 데이터를 전달하고자 할 때는 어떻게 할 수 있을지 알아봅시다. 직관적으로 생각했을 때 InputStreamBufferedReader의 반대인 OutputStreamBufferedWriter를 쓰면 될 것 같다는 생각이 드실 겁니다. 결과적으로 다음과 같이 소스를 작성할 수 있습니다.


import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.net.Socket;


public class Main {


public static void main(String[] args) {

        try {

            Socket sock = new Socket(특정한 서버, 포트 번호);

            InputStream in = sock.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(in));

            OutputStream out = sock.getOutputStream();

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));

            String line = null;

            while((line = br.readLine()) != null){

            System.out.println(line);

            if(line.contains("Enter your name:")) break;

            }

            bw.write("Dongbin, Na");

            bw.newLine();

            bw.flush();

            bw.close();

            br.close();

            sock.close();

        } catch (Exception e) {

            System.out.println(e);

        }

}

}


  위 소스코드는 특정한 서버에 접속해서 메시지를 계속해서 전달 받다가 “Enter your name:"이 포함된 문장을 만나면 더 이상 서버로부터 데이터를 전달받지 않고 "Dongbin, Na"라는 메시지를 서버로 전송하는 것입니다. 실제로 이러한 방식으로 서버와 클라이언트 통신을 진행할 수 있습니다. 물론 일반적으로 서버로부터 들어오는 데이터가 정형화되어있지 않다면 split(), contains(), substring() 등의 함수를 이용해서 메시지에서 필요한 내용만 파싱하여 사용할 필요도 있습니다.


728x90
반응형

Comment +0

728x90
반응형

  네트워크 상에서 특정한 컴퓨터와 통신하기 위한 유틸리티(Utility)넷캣(Netcat)을 사용할 수 있습니다. TCP/UDP 프로토콜을 이용하는 환경에서는 범용적으로 적용이 가능하다는 점에서 간단한 네트워크 통신을 위한 목적으로 간단히 사용할 수 있습니다. 줄여서 NC(Network Connection)라고도 부르며 해킹(Hacking)에 있어서도 이용범위가 넓다는 특징이 있습니다. 리눅스 운영체제에서는 Netcat을 사용하기 어렵지 않지만 윈도우(Windows) 운영체제를 사용하는 사람들은 별도의 소프트웨어를 설치해서 사용할 수 있답니다.


  ▶ Netcat 다운로드 사이트: https://eternallybored.org/misc/netcat/



  사이트 접속 이후에는 위와 같이 netcat 1.12 버전을 다운로드하여 윈도우에서 사용할 수 있습니다.



  다운로드 이후에는 압축을 풀어줍니다. 저는 위와 같이 C 드라이브에 압축을 풀어주었습니다.



  이후에 제어판 -> 시스템 -> 고급 시스템 설정 -> '환경 변수'로 들어가서 우리가 방금 다운로드 해 준 Netcat 유틸리티를 커맨드 라인에서 사용할 수 있도록 변수 설정을 해주도록 하겠습니다.



  시스템 변수 -> 'Path'에서 사용할 유틸리티의 경로를 설정해 줄 수 있습니다. 여기에서 새롭게 자신이 압축을 푼 Netcat 폴더의 위치를 넣어주시면 됩니다. 이후에 명령 프롬프트(CMD)에서 Netcat 유틸리티를 사용하실 수 있습니다.


1. 서버 열기 및 접속하기


  서버를 열 때는 nc -l 명령어를 사용합니다. l(Listen)은 단순한 채팅 서버와 같은 역할을 수행합니다. 클라이언트에서 접속할 때는 호스트 주소와 포트 번호를 공백으로 구분하여 이어서 작성하면 됩니다.



  일반적으로 다음과 같이 해킹 문제풀이 서버에 접속하면 서버에서 미리 준비된 메시지를 출력하고, 사용자와 상호작용을 하는 식입니다.



  Netcat은 말 그대로 네트워크 통신 도구이므로 다음과 같이 웹 사이트에 접속할 수도 있습니다. 다음 사진은 제 개인 웹 사이트에 접속하여 요청(Request) 패킷을 Netcat 유틸리티 상에서 전송해 본 것입니다. 서버에서 성공적으로 응답(Response) 패킷이 날아온 것을 알 수 있습니다.



728x90
반응형

Comment +0

728x90
반응형

  해킹 방어 대회(CTF)에서는 다양한 문제들이 가리지 않고 출제됩니다. 이번 시간에는 해킹 방어 대회를 위해 사용할 수 있는 기본적인 소프트웨어 및 프로그래밍 언어에 대해 알아보겠습니다. 사실상 기본적인 도구가 없으면 해킹 자체를 할 수 없는 경우가 많기 때문에 아래에서 언급된 소프트웨어는 필수적으로 설치를 할 수 있으면 좋습니다. 또한 아래 언급되는 대다수의 소프트웨어가 기초적인 프로그래밍 능력이 없으면 활용하기 어렵습니다. 하지만 당장 잘 모르는 소프트웨어라고 하더라도, 필요할 때마다 설치하며 공부하시면 효율적인 학습이 가능합니다.


  어떠한 CTF라도 출제되는 문제 유형에서 요구하는 모든 도구(Tool)를 정확히 파악하기는 매우 어려우며 출제자의 의중을 파악하기도 어렵습니다. 특히 특정 분야에 대한 매우 전문적인 지식을 요구하는 고난이도의 문제가 자주 출제됩니다. 따라서 처음 CTF를 출전할 때 다양한 고충을 겪으시는 것은 당연하니 처음에 너무 모르는 도구가 많다고 하시더라도 겁먹지 말고, 천천히 하나씩 접해보시는 걸 추천합니다.


  ▶ 파이썬(Python) 및 IDE: 파이썬은 해킹을 위한 매우 강력한 기능을 제공합니다. 특히 pwntools와 같은 라이브러리를 활용할 수 있습니다. 프로그래밍 언어는 도구에 불과하다고는 하지만 사실상 오늘날의 CTF는 파이썬을 다루지 못하면 상당한 제약을 느낄 수 있습니다.


  ▶ C/C++/C# 및 Visual Studio: 비주얼 스튜디오 개발 환경에서 C언어 계열을 활용하는 문제는 매우 많이 출제되고 있습니다. 최근 F#까지 간혹 출제되는 추세입니다. 사실상 매우 기본적인 C언어 개발환경이므로 설치가 필수적입니다.


  ▶ 가상 머신(Virtual Machine): 상당수 문제는 리눅스(Linux) 환경이 아니면 접근조차 할 수 없는 경우가 많으므로 다른 운영체제 시스템 환경을 다루기 위해 가상 머신을 사용할 필요가 있습니다.


  ▶ SSH/Netcat: 원격지에 존재하는 서버(Server)에 접속하기 위한 목적으로 사용할 수 있는 유틸리티(Utility)입니다. 일반적으로 CTF 대회 문제 서버에 접속하기 위해 자주 사용됩니다. 또한 이러한 도구는 비단 해킹이 아니라 특정 서버를 관리하기 위한 목적으로 사용됩니다.


  ▶ IDA Debugger: 디버깅(Debugging) 도구 중에서 가장 강력한 소프트웨어 중 하나입니다. 다양한 플러그 인을 직접 개발하거나 사용할 수 있는데, 대표적인 플러그 인으로는 기존 기계어를 C언어 문법과 흡사하게 재구성해서 보여주는 기능을 가진 Hex-Rays가 있습니다.


  ▶ Cygwin/MinGW: 유닉스 계열의 명령어나 기능을 윈도우 운영체제에서 소스코드로부터 컴파일할 수 있도록 해주는 소프트웨어입니다. 물론 이러한 프로그램을 설치한다고 해서 리눅스 전용 바이너리 파일을 실행할 수 있는 것은 아니므로 가상 머신을 따로 설치하는 것이 좋습니다.


  ▶ 와이어샤크(Wireshark): 대표적인 네트워크 패킷(Packet) 분석 소프트웨어입니다. 사실 CTF 뿐만 아니라 네트워크를 통한 대부분의 소프트웨어를 분석하거나 개발할 때 사용할 수 있습니다.


  ▶ 칼리 리눅스(Kali Linux): 침투 테스트 목적으로 가장 인기있는 운영체제(OS)입니다. 기본적으로 해킹에 필요한 대부분의 기능 및 도구가 베이직하게 설치가 되어 있다는 점에서 매우 편리하게 사용 가능합니다. 보안을 시작하는 사람들이 가장 먼저 설치하는 것 중 하나입니다.


  ▶ 반디집: 윈도우 운영체제를 베이스로 사용하는 유저들이 리눅스나 맥(MAC) 개발 환경에서 압축된 다양한 파일을 쉽게 해제할 수 있도록 도와줍니다. 다양한 압축 기능을 사용하기 편리하게 해놓았다는 점에서 활용도가 높습니다.


  ▶ HxD: 바이너리 코드(2진수)보다 가독성이 높아 컴퓨터 분야에서 널리 쓰이는 16진수 형태의 데이터를 헥사 코드라고 말합니다. 특정한 파일이 있을 때 이를 16진수 코드로 분석하고자 할 때 사용할 수 있는 소프트웨어입니다. 이를 활용해 JPG, PNG 등의 이미지 파일 등을 16진수 데이터 형태로 분석할 수도 있으며 적절하게 변경하여 사용할 수 있습니다.


  ▶ Pwn Debugger: 리눅스 환경에서 손쉽게 사용할 수 있는 디버깅 도구입니다. 기본적인 gdb보다 더 가시적이며 동적인 환경에서 


  ▶ 버프 수트(Burp Suite): 웹 취약점 분석 도구 중에서 가장 유명한 도구 중 하나입니다. 자체적인 프록시(Proxy) 기능을 제공하며 다양한 파라미터 변조 및 반복적인 패킷 전송 공격 등 다양한 공격이 가능합니다.


  ▶ 크롬 & EditThisCookie: 크롬(Chrome) 개발자 도구만 잘 이용해도 다양한 웹 취약점 테스트를 별도의 프로그래밍 도구 없이 할 수 있는 시대가 왔습니다. 특히 EditThisCookie와 같은 별도의 확장 프로그램을 잘 활용하시면 매우 편리한 웹 취약점 분석이 가능합니다.


  ▶ 볼라빌리티(Volatility): 파이썬으로 개발된 메모리 분석 및 포렌식 전용 소프트웨어입니다. 기본적으로 칼리 리눅스 운영체제에 포함되어 있는 도구 중 하나입니다.


  ▶ FTK Imager: 포렌식 및 메모리 이미징을 위한 전문 소프트웨어입니다. 다양하고 강력한 부가적인 기능을 제공합니다.


  ▶ dex2jar: 대표적인 안드로이드 어플리케이션 디컴파일러입니다. 이를 활용하면 APK 파일을 뜯어 분석한 뒤에 smali 파일을 수정해 다시 패키징하고, 어플리케이션으로 제작하여 해적판까지 배포할 수 있습니다.


  ▶ jd-gui: 자바(Java)로 개발된 JAR 파일을 손 쉽게 분석할 수 있도록 도와주는 GUI 소프트웨어입니다.


  ▶ PHP/JSP 개발환경: 웹 해킹 문제로 가장 많이 출제되는 PHP 및 JSP 언어에 대한 개발환경을 구축해놓고, 기초적인 프로그래밍까지 할 수 있도록 공부하시면 많은 도움이 됩니다.

728x90
반응형

Comment +2

  • 연두아빠 2018.08.21 22:14

    gdb도 추가해줘!

  • 처음 이글을 봤을때 뭐이래 많나 생각했는데,
    지금보니 저거 이미 다있고 추가로 툴들 훨씬많이 갖고있네요 ㅋㅋㅋㅋㅋㅋㅋㅋ

728x90
반응형

Facebook Hacker Cup 2018 Qualification Round

나동빈

 

  페이스북 해커컵(Facebook Hacker Cup)은 특이한 방법으로 진행됩니다. 프로그램을 만들 때의 논리력을 중요시하기 때문에 언어의 제약이 없습니다. 그래서 문제를 푼 뒤에 소스코드를 제출할 때는 페이스북에서 제공하는 입력 파일(Input)을 받아서, 그것을 프로그램에 넣고 나온 결과(Output)을 그대로 제출하면 됩니다. 소스코드도 별도로 제출하긴 하는데 부정행위 검사용에 불과한 것 같습니다.



  실제로 문제를 푼 뒤에 답안을 제출할 때는 6분의 시간을 줍니다. 입력 데이터에 대해 6분 안에 올바른 결과 값을 도출해서 제출하면 되는 방식입니다.



  출력 파일 및 소스코드를 제출하면 다음과 같이 제출이 이루어집니다. 결과는 대회가 끝나야 제대로 나오는 것으로 판단됩니다.



1. Tourist

 

문제 설명

(페이스북 해커컵의 문제는 영어로 제공됩니다. 제가 임의로 번역한 겁니다.)

 

- N개의 장소가 있고 장소의 이름은 문자열로 주어집니다.

- 1부터 N까지 인기가 많은 순서대로 주어집니다.

- 한 번 방문할 때 K개의 장소를 방문합니다.

- 구체적으로 어떤 장소를 방문할지 결정하기 위해서, 이미 몇 번 방문한 적이 있는지를 기준으로 N개의 장소를 오름차순 정렬합니다. 그리고 인기도가 많은 순으로 정렬하여 K개를 선택합니다.

- 즉 모든 장소에 대해서 방문한 횟수를 기준으로 먼저 선택하고, 방문한 횟수가 동일하다면 인기도가 높은 장소를 선택하면 됩니다.

- Alex는 이미 V-1번 방문한 적이 있습니다. 이제 V번째 방문을 하고자 합니다. AlexV번째 방문할 때 방문하게 될 K개의 장소를 출력하면 됩니다.

- T번의 테스트가 제공되고 매 테스트마다 N, K, V가 주어지고 N개의 장소명이 주어집니다.

- 1 <= T <= 80, 1 <= K <= N <= 50, 1 <= V 10^12


예제 입력

6

4 1 3

LikeSign

Arcade

SweetStop

SwagStore

4 4 100

FoxGazebo

MPK20Roof

WoodenSculpture

Biryani

4 3 1

LikeSign

Arcade

SweetStop

SwagStore

4 3 3

LikeSign

Arcade

SweetStop

SwagStore

4 3 10

LikeSign

Arcade

SweetStop

SwagStore

2 1 1000000000000

RainbowStairs

WallOfPhones

 

예제 출력

Case #1: SweetStop

Case #2: FoxGazebo MPK20Roof WoodenSculpture Biryani

Case #3: LikeSign Arcade SweetStop

Case #4: LikeSign SweetStop SwagStore

Case #5: LikeSign Arcade SwagStore

Case #6: WallOfPhones


문제 풀이

  문제의 조건을 보면 V가 최대 1,000,000,000,000까지 들어올 수 있습니다. 그래서 단순히 문제에서 요구하는 대로 매 번 정렬을 수행해서 최종적인 답을 도출하려고 하면 정해진 시간 안에 문제를 해결할 수 없을 겁니다. 그래서 최대 1,000,000,000,000 번을 실제로 다 방문하지 말고 어떠한 규칙을 발견할 수 있도록 애초에 문제에서 유도하고 있는 것입니다. 따라서 문제를 풀 때는 정확히 이러한 N, K, V의 범위. 즉 정의역에 대해서 가장 먼저 파악해야 합니다.

 

  만약에 장소의 개수가 5개라고 할 때, 차례대로 장소를 방문한다고 하면 i번째 장소를 방문하고자 할 때 인덱스는 어떻게 될까요?



  위와 같이 장소가 5개 있다고 해봅시다. 이 때 3번째 장소는 다음과 같이 인덱스 2에 해당합니다.



  그렇다면 9번째 장소는 어떻게 될까요? 다음과 같이 한 바퀴 돌아서 인덱스 3에 해당합니다.



  즉, i번째 장소의 인덱스를 구하려면 (i - 1) % N을 하면 됩니다. 이걸 왜 언급하는 걸까요? 그 이유는 이 문제는 슬라이딩 윈도우와 흡사한 알고리즘이 사용되기 때문입니다. 전체 배열의 크기가 N일 때 한 번 방문할 때 K개만큼 방문하면서 계속해서 원을 그리며 이동하면 됩니다.

 

  다만 방문 횟수가 동일한 경우에는 더 인기가 많은 장소부터 출력해야 하므로 슬라이딩 윈도우를 수행하며, 출력하기 전에 인덱스를 기준으로 장소의 이름에 대해 오름차순 정렬을 수행해주시면 됩니다.

 

  결과적으로 이미 V - 1번 방문을 했다고 가정하므로 V번 째 방문을 할 때는 (K * (V - 1)) % N + 1번째 장소부터 (K * (V - 1)) % N + K번째 장소까지 방문하여 출력하면 정답 처리를 받을 수 있습니다. (출력하기 직전에 인덱스를 기준으로 정렬을 해주시면 됩니다.)


#include <iostream>

#include <fstream>

#include <algorithm>

#include <vector>

 

using namespace std;

 

long long int n, k, v;

string a[50];

 

class Node {

  public:

    int rate;

    string name;

    Node(int rate, string name) {

      this->rate = rate;

      this->name = name;

    }

    bool operator <(Node other) {

      return this->rate < other.rate;

    }

};

 

int getIndex(int number) {

  return (number - 1) % n;

}

 

int main(void) {

  ifstream inFile("Tourist.txt");

  ofstream outFile("Tourist Answer.txt");

  int T;

  inFile >> T;

  for(int testCase = 1; testCase <= T; testCase++) {

    vector<Node> nodes;

    inFile >> n >> k >> v;

    for(int i = 0; i < n; i++) {

      inFile >> a[i];

    }

    for(int i = (k * (v - 1) % n + 1); i <= (k * (v - 1) % n + k); i++) {

      int index = getIndex(i);

      nodes.push_back(Node(index, a[index]));

    }

    sort(nodes.begin(), nodes.end());

    outFile << "Case #" << testCase << ": ";

    for(int i = 0; i < nodes.size(); i++) {

      outFile << nodes[i].name << ' ';

    }

    outFile << '\n';

  }

  return 0;

}


2. Interception

 

문제 설명

(페이스북 해커컵의 문제는 영어로 제공됩니다. 제가 임의로 번역한 겁니다.)

 

-

- 위와 같은 형태로 표현되는 다항식이 있습니다.

- 이러한 다항식을 실제로 계산할 때는 연산자 우선순위가 덧셈(+), 곱셈(*), 지수 승(^) 순서입니다.

- 예를 들어 는 로 계산됩니다.

- 음수(-) 부호는 우선순위가 제일 높습니다.

- 0^01입니다.

- T번의 다항식(Polynomial)이 주어집니다.

- 다항식마다 최고 차수 N이 주어지고, 그 이후에 차수 N부터 0까지 차례대로 계수가 주어집니다.

- 계산하기 위한 다항식을 f(x)라고 했을 때 f(x) = 0이 되는 X의 개수를 출력하고, 각 라인에서는 각 X의 값을 오름차순으로 출력합니다.


예제 입력

2

1

1

1

4

9

0

-6

2

-2

 

예제 출력

Case #1: 1

0.0

Case #2: 0

 

부가 설명

주어진 예제 입력을 설명해드리자면 다음과 같습니다.

 

2: 테스트 케이스가 2

1: 첫 번째 테스트 케이스는 최고 차수가 1

1: 차수 1의 계수

1: 차수 0의 계수

4: 두 번째 테스트 케이스는 최고 차수가 4

9: 차수 4의 계수

0: 차수 3의 계수

-6: 차수 2의 계수

2: 차수 1의 계수

-2: 차수 0의 계수

 

첫 번째 테스트 케이스에서 들어온 입력은 다음과 같은 식입니다.



이제 이걸 f(x)로 표현하면 다음과 같습니다.



  그러므로 X0일 때 f(x)0이 될 수 있는 것입니다.


문제 풀이

  일단 문제에서 요구하는 연산자의 우선순위를 따랐을 때 모든 다항식은 지수함수으로 표현된다는 것을 이해하셔야 합니다. 왜냐하면 지수 연산(^)의 우선순위가 제일 낮기 때문입니다. 어떠한 입력이 들어와도 결과적으로 f(x)는 지수함수 형태에요. 이 때 일반적인 지수함수는 절대로 f(x) = 0이 될 수 없다는 것을 생각해보세요. 지수함수는 다음과 같이 생겼습니다.



다만, 문제의 형태를 보면 항상 최종적으로는 X와 어떠한 식을 곱하도록 되어있습니다. 예를 들어 다음의 식을 고려해보세요 이 식은 다음과 같이 바뀝니다.



  즉 어떠한 다항식이 들어왔을 때 무조건 (어떠한 숫자 * X)의 지수 승 형태로 표현되는 것을 알 수 있습니다. 이게 힌트인데요. 일반적인 지수함수는 어떤 일이 있어도 그 결과 f(x) = 0이 될 수 없기 때문에 (어떠한 숫자 * X)를 이용하셔야 합니다. X = 0인 경우에는 f(x) = 0이 될 가능성이 존재하기 때문입니다. 다시 말해서 어떠한 입력 값이 들어오더라도 근이 한 개 존재하는 경우(X = 0), 근이 아예 존재하지 않는 경우 두 가지 경우 밖에 존재하지 않는다는 것입니다.

 

  이 때 실제로 특정한 f(x)에 대해 X = 0을 넣는 방식으로 답을 계산할 수도 있을 것입니다. 다만 저는 조금 더 문제를 분석해보았어요. 그랬더니 재미있는 규칙을 찾았습니다. 바로 항의 개수가 홀수일 때는 근이 없고, 항의 개수가 짝수일 때는 근이 있다는 겁니다. 종이로 그려보니까 다음과 같았어요.



  따라서 테스트 케이스마다 N을 입력 받아서 N이 홀수일 때와 짝수일 때를 구분해서 정답을 출력하면 문제를 쉽게 해결할 수 있습니다.


#include <iostream>

#include <fstream>

 

using namespace std;

 

int main(void) {

  ifstream inFile("Interception.txt");

  ofstream outFile("Interception Answer.txt");

  int T;

  inFile >> T;

  for(int testCase = 1; testCase <= T; testCase++) {

    int n;

    inFile >> n;

    for(int i = 0; i <= n; i++) {

      int x;

      inFile >> x;

    }

    outFile << "Case #" << testCase << ": ";

    if(n % 2 == 1) {

      outFile << 1 << '\n';

      outFile << "0.0" << '\n';

    else {

      outFile << 0 << '\n';

    }

  }

  return 0;

}


3. Ethan Searches for a String

 

문제 설명

(페이스북 해커컵의 문제는 영어로 제공됩니다. 제가 임의로 번역한 겁니다.)

 

- 문자열 매칭 알고리즘이란 문자열 A가 문자열 B 안에 포함이 되어있는지를 판단하는 알고리즘입니다.

- 다음의 Ethan이 작성한 문자열 매칭 알고리즘이 있습니다. 이 알고리즘은 잘못된 알고리즘입니다.

 

(1) ij1로 설정합니다.

(2) i > |A|일 때, TRUE를 반환합니다.

(3) j > |B|일 때, FALSE를 반환합니다.

(4) Ai = Bj일 때, ij1 증가시키고 (2) 단계로 이동합니다.

(5) i = 1일 때, j1 증가시키고 (2) 단계로 이동합니다.

(6) i1로 설정하고 (2) 단계로 이동합니다.

 

- A라는 문자열이 주어졌을 때, 우리는 문자열 AB라는 문자열 안에 포함이 되는데도 불구하고 Ethan의 알고리즘이 FALSE를 뱉는 문자열 B’를 찾아내고자 합니다.

- 문자열 B를 찾을 수 없을 때는 'Impossible'을 출력합니다.

- 테스트 케이스 T100개까지 주어질 수 있습니다.

- 1 <= A의 길이 <= 2,000

- 1 <= B의 길이 <= 10,000

 

예제 입력

4

ABACUS

FACEBOOK

XYZXZYX

FBFBF

 

예제 출력

Case #1: ASUCABABACUSA

Case #2: Impossible

Case #3: XZYXYZXYZXZYXYZXYZYX

Case #4: Impossible

 

문제 풀이

 

  먼저 문제를 접하기 전에 KMP 알고리즘에 대한 제 강의 노트를 한 번 보시고 오시기 바랍니다.

 

  KMP 알고리즘: https://blog.naver.com/ndb796/221240660061

 

  일단 Ethan의 알고리즘을 직접 C++ 소스코드로 구현해보겠습니다.

 

#include <iostream>

 

using namespace std;

 

bool contains(string a, string b) {

  int i = 1, j = 1;

  while(1) {

    if(i > a.length()) return true;

    if(j > b.length()) return false;

    if(a[i - 1] == b[j - 1]) {

      i++;

      j++;

      continue;

    }

    if(i == 1) {

      j++;

      continue;

    }

    i = 1;

  }

}

 

int main(void) {

  string a = "ABACUS";

  string b = "ASUCABABACUSA";

  cout << contains(a, b) << '\n';

  int result = b.find(a);

  cout << result;

  return 0;

}


  위 소스코드를 보시면 “ABACUS”는 분명히 “ASUCABABACUSA”에 포함이 되는데도 불구하고 Ethan의 알고리즘은 FALSE(0)라는 값을 반환하는 것을 알 수 있습니다. find() 함수는 C++에서 기본적으로 제공하는 문자열 매칭 함수입니다. find() 함수는 "ABACUS"가 포함되는 시작 인덱스인 6이라는 값을 내뱉는 것을 알 수 있습니다. 그렇다면 이 문제를 풀기 위해서는 어떻게 해야 할까요? Ethan의 알고리즘을 직접 손으로 차근차근 해보면 쉽게 이해하실 수 있습니다. 문자열이 일치하는지 하나씩 검사하는 알고리즘인데요, 접미사와 동일한 접두사가 존재하는 경우를 캐치하지 못한답니다.

 

  따라서 하나씩 검사하다가 불일치 할 때 위치에서의 접미사와 동일한 접두사가 존재하는지 확인해야합니다. Ethan의 소스코드는 그러한 경우를 캐치하지 못하고 넘어가는 불완전한 소스코드니까요.

 

  예를 들어 FBFBFKEthan의 소스코드의 결함을 보여주는 예시입니다. FBFBFBFK와 비교했을 때 하나씩 검사하다가 FBFBFKFBFBFBFK가 불일치가 발생합니다. 이 때 접미사와 접두사는 FBFB로 동일하게 구성됩니다.

 

A: FBFBFK

B: FBFBFBFK

 

  이러한 경우 실제로는 문자열이 포함됨에도 불구하고 Ethan의 함수는 이를 감지할 수 없습니다. 따라서 Ethan 함수의 결함을 지적하기 위해서는 하나씩 검사하다가 불일치 할 때 위치에서의 접미사와 동일한 접두사가 존재하는 문자열 B를 만들면 됩니다.

 

  다만 불일치하기 직전까지는 일단 AB가 일치했다는 것을 이해하셔야 합니다.

 

A: FBFBFK

B: FBFBFBFK

 

  위 경우를 보시면 일단 FBFBF까지는 일치했습니다. 그 이후에 KB가 서로 다른 겁니다. 따라서 불일치 할 때 위치에서의 접두사와 접미사가 일치하려면, 불일치하기 직전까지 검사했던 일련의 문자열들도 당연히 접두사와 같아야 합니다. 예를 들어 FBFF 두 개가 존재할 겁니다.

 

  다시 말해서 하나씩 검사하다가 불일치 할 때 위치에서의 접미사와 동일한 접두사가 존재하는 문자열 B’를 만들기 위해서는 문자열 A에서 이미 접두사와 접미사가 일치하는 문자열 쌍이 존재해야만 합니다. 예를 들어 FACEBOOK은 접두사와 접미사가 일치하는 문자열 쌍이 하나도 존재하지 않습니다. 그렇기 때문에 Ethan의 함수에서의 결함을 찾을 수 없는 예시가 되는 겁니다.

 

  이제 문자열 ‘FBFBF’를 고려해봅시다. 이것도 Ethan의 함수의 결함을 찾을 수 없습니다.

 

A: FBFBF

B: FBFB?

 

  위 경우를 보시면 물음표 위치에 F를 넣는다고 하면 문자열 일치가 발생해버리고, K와 같이 다른 문자를 넣는다고 하면 불일치 할 때 위치에서의 접미사와 동일한 접두사가 존재할 수 없습니다. 모순이 발생하는 거죠. 분명히 접두사와 접미사가 일치하는 경우는 'F', 'FB', 'FBF' 세 개가 존재함에도 불구하고 이러한 문제가 발생했습니다. 여기에서 하나의 사실을 유추할 수 있습니다. 바로 접두사와 접미사가 일치하는 연속된 경우의 뒤에 하나 이상의 여분 문자가 필요하다는 것입니다.

 

  예를 들어 FBFBK'F', 'FB'라는 두 개의 접미사와 동일한 접두사가 존재할 수 있습니다. 이제 'F'‘FB'는 연속되어 있으므로 ’FB'만 고려하겠습니다. 이 때 잘 살펴보시면 'FB'의 뒤에 K라는 하나 이상의 여분 문자가 존재합니다. 그래서 이 KF로 바꾸어주고, 뒤에 BK를 붙여줌으로써 다음과 같은 문자열을 만들 수 있어요.

 

B: FBFBFBK


  이렇게 만들게 되면 Ethan의 함수에 존재하는 결함을 밝힐 수 있게 되는 것입니다. 따라서 문제를 해결하기 위한 방법은 다음과 같습니다.

 

< Ethan의 함수에 존재하는 결함을 밝히는 방법 >

 

1. 접두사 및 접미사 쌍 테이블을 만들기

2. 접두사와 접미사가 일치하는 연속된 경우의 뒤에 하나 이상의 여분 문자가 존재하는지 확인하기

 

  예를 들어 ‘XYZXZYX’의 접두사 및 접미사 쌍 테이블은 다음과 같습니다.



  보면 ‘XYZXZYX’는 인덱스 3의 값이 1이라서 접두사와 접미사가 일치하는 부분 문자열이 존재하는 겁니다. 이 때 인덱스 4가 여분 문자로 존재합니다. 따라서 다음과 같은 문자열을 만들 수 있어요.

 

XYZXYZXZYX

 

  다시 말해 XYZX에서 그 뒤로 YZXZYX를 넣어준 겁니다. 이것은 접두사와 접미사가 일치할 수 있는 부분이 인덱스 3‘X’이므로 그 뒤로 접미사를 제외한 YZXZYX를 넣어준 것이라고 할 수 있습니다. 이런식으로 만들게 되면 ‘XYZXYZXZYX' 안에 'XYZXZYX’가 포함되어 있음에도 불구하고 Ethan의 함수는 이를 찾아내지 못합니다. 이제 이 내용을 소스코드로 옮기면 됩니다.


#include <iostream>

#include <fstream>

#include <vector>

 

using namespace std;

 

vector<int> makeTable(string pattern) {

  int patternSize = pattern.size();

  vector<int> table(patternSize, 0);

  int j = 0;

  for(int i = 1; i < patternSize; i++) {

    while(j > 0 && pattern[i] != pattern[j]) {

      j = table[j - 1];

    }

    if(pattern[i] == pattern[j]) {

      table[i] = ++j;

    }

  }

  return table;

}

 

int main(void) {

  ifstream inFile("Ethan.txt");

  ofstream outFile("Ethan Answer.txt");

  int T;

  inFile >> T;

  for(int testCase = 1; testCase <= T; testCase++) {

    outFile << "Case #" << testCase << ": ";

    string pattern;

    inFile >> pattern;

    vector<int> table = makeTable(pattern);

    bool find = false;

    for(int i = 1; i < table.size(); i++) {

      // 접두사와 접미사가 일치하는 '연속된 경우'의 뒤에 '하나 이상의 여분 문자가 존재'하는지 확인

      if(table[i] <= table[i - 1] && table[i - 1] != 0) {

        // 찾았을 경우 문자열 생성

        find = true;

        // 앞 부분 출력

        for(int j = 0; j < i; j++) {

          outFile << pattern[j];

        }

        // 뒷 부분 출력

        for(int j = table[i - 1]; j < pattern.size(); j++) {

          outFile << pattern[j];

        }

        outFile << '\n';

        break;

      }

    }

    if(!find) {

      outFile << "Impossible\n";

    }

  }

  return 0;

}

728x90
반응형

Comment +0

728x90
반응형

사 혹은 미래교사의 인터넷 교육(방송) 수익 창출에 대해서

나동빈


  저는 최근에 유튜브(YouTube) 활동을 하면서 공무원이 유튜브 활동으로 광고 수익을 얻는다면 어떠한 문제가 있을 지에 대해서 고민해보았습니다. 기본적으로 교사와 같은 공무원은 겸업 금지 항목에 의해서 자신의 본업을 제외하고 추가적인 직업을 가질 수 없습니다. 특히나 개인사업자를 내고 다른 사업을 하는 경우에는 더욱 그러한 제약이 심합니다.


  Q. 그렇다면 현직 교사가 유튜브와 같은 플랫폼에서 개인 방송으로 수익을 창출하는 건 괜찮나요?


  ▶ 이와 관련해서는 현재 국립 교사로 활동하고 계신 제 지인 분들께 여쭈어보니, 가장 중요한 건 학교장의 승인이라고 하더군요. 교육지원청에 전화를 해서 물어보았을 때도 겸업 관련 권한은 담당 학교장이 크게 관여하는 것이라고 말씀해주셨습니다. 구체적으로 하려는 활동이 공무원의 본질을 흐트리지 않으면서 정당하게 수익을 창출할 수 있다는 것을 증명해야 하는 것 같아요. 또한 아직까지 유튜브와 같은 플랫폼에 대한 사례가 많지 않은 것 같아요. 대부분 잘 모르시는 것 같구요.


  조금 다른 이야기이지만 회사로 나가는 외부 강의(현장 강의)의 경우에는 강의 수당으로 학교장의 승인이 있으면 된다고 합니다. 사실 그래서 가장 중요한 거는 학교장에게 보고하고, 승인을 받는 과정이 중요한 것 같아요. 물론 이런 강의 활동이 아니라 뻔하게 다른 사업(음식점, PC방 등)을 한다거나 하는 것들은 당연히 안 되는 거구요.


  Q. 아직 교사가 되기 전에 찍어 놓은 동영상이 교사가 된 이후에 부가적인 수입을 내는 경우는 어떤가요?


  ▶ 만약에 임용고시를 준비하는 대학생이 본인의 재량껏 다양한 유료 인터넷 교육을 진행해보았다고 해봅시다. 본인의 이름으로 개인 사업자를 내고 활동할 수도 있겠지요. 당연히 교사가 되기 전이니까 어떠한 제약조건도 없을 겁니다. 그런데, 대학생 때 찍은 유료 강의들이 교사가 된 이후에도 수익을 창출한다면? 이것도 겸업으로 판단되어 제재를 받을 수 있을지 궁금합니다.


  이와 관련해서는 모 지역의 교육지원청에 제가 직접 전화를 해보았어요. 그 답변으로는 교사가 되기 전에 기존에 찍었던 인터넷 강의는 '지속적이고 일정한 수익 창출'도 아니며 교사로 일하기 전에 일을 한 것이기 때문에 교사가 된 이후에 계속해서 인터넷 강의를 찍는게 아니라면, 애초에 과거의 일이므로 문제가 없다고 합니다.


  그래서 교사로 붙은 뒤에는 혹시 사업자 등록을 한 상태라면 인터넷 강사로 활동하던 관련 사업자를 폐지하고, 그 때부터는 계약 내용을 바꾸어서 6.6%로 기타소득으로 세금을 떼고 받는 식으로 진행하면 큰 어려우이 없는 것 같습니다. 물론 이는 교사가 되기 전에 찍은 강의라서 가능한 거고, 교사가 된 이후에는 새로 전문 사기업에서 인터넷 강의를 찍는 것은 쉽지 않은 것 같습니다.

728x90
반응형

Comment +1