x0FANSA-hk 님의 블로그

MJSEC) 신입 부원 과제 본문

MJSEC

MJSEC) 신입 부원 과제

x0fansa-hk 2026. 4. 30. 10:33

part 웹


미리 말씀드리지만 저는 웹은 컴퓨터활용능력 2급에 언급되는 정도만 알고 있습니다. 모르는 부분이 많기에 가급적 다른 지식의 힘을 빌렸습니다.


Q1. proxy가 무엇이며 Forward Proxy와 Reverse Proxy의 차이점을 설명해주세요.
A1. 프록시는 저는 심부름꾼이라고 이해를 합니다. 보통 저희가 공유기를 통해서 웹에 접속을 한다면 저의 주소로 데이터가 들어오게 되지만 이것은 저는 특정 지점에서 명령을 하고 프록시서버에서 나머지를 처리하게 되는 것이죠. 이중에서 포워드프록시는 클라이언트측에서, 리버스프록시는 호스트측에서 사용을 하는데 포워드프록시는 자신이 누군지를 숨기고 우회할 목적으로, 리버스프록시는 원본을 참조하여 클라이언트에게 알려주기 위해(원본보호) 사용됩니다. 가끔 딥웹을 들어갈때 토르가 사용되기도 하는데요, 토르의 경우 이 프록시가 여러개에 암호화를 거친것으로 볼 수 있습니다.(물론 실제로는 브라우저의 최소한의 보안도 무시해야하기에 일반적으로는 어려움)

Q2. 응답코드정리
A2. 표로 (Gemini가)정리하였습니다.

200 301 400 401 403
성공. 정상적인 동작입니다. 요청한 페이지가 아예 다른 주소로 이동함을 의미합니다. 데이터가 손상되거나 서버가 받을 수 없는 형태로 받았을때의 출력입니다. 인증이 필요하지만 토큰이 잘못되었을때의 출력입니다. 접근 불가능한 사람이 접근을 하려는 상황에서의 출력입니다. 보통은 링크로 원격 접속시 토큰여부로 걸리게 됩니다.
404 500 502 503  
게스트가 요청한 명령이 없다는 출력입니다. 서버오류에서의 출력입니다. 서버와 사용자 사이의 오류가 있을때의 출력입니다. 서버가 모종의 사유로 게스트의 요청을 거부한 상태입니다.  

 

Q3. 네이버 검색을 할 때는 주소창에 내가 검색한 단어가 보이지만 로그인을 할 때는 비밀번호가 주소창에 보이지 않습니다. 데이터를 전달하는 두 가지 방식인 GET과 POST의 차이는 무엇이가요?
A3. 이것은 주소창에 정보가 남냐 아니냐의 차이입니다. GET은 데이터를 말 그대로 가져오기 위해서 사용되는 방식입니다. 그렇기 때문에 공개적으로 정직하게(쿼리 스트링) 목적이 노출되고 그 데이터를 받아옵니다. POST는 숨길 내용을 숨겨서 변경해서 전송하는 방법입니다. 그렇기 때문에 주소창에 드러나지 않으며 전송과정에서도 HTML문서(의 <form>태그)와는 별개의 패킷으로 전송이 됩니다.

Q4. URL Encoding, HTML Entity Encoding, Base64 Encoding의 사용 목적을 각각 설명, 공격자가 필터를 우회하기 위해 이것들을 어케 활용하는지
A4. 인코딩이라는 용어 자체는 보여주는 방식입니다. 우리가 파일을 열때 익숙하게 아스키코드내의 문자라면 ANSI, 한글이라면 보통 UTF8, 기계어라면 BIN으로 보는것과 같은 개념입니다. 우리가 파일 경로에서 쓰지 못하는 문자가 있는 경우등이 있습니다. URL도 마찬가지로 일부 특수문자(공백이라던가)를 원칙상으로는 사용할 수 없습니다. 그러나 GET형식으로 공백이 검색이 된다면 이를 표현해야하 하는데 이때 대체문자를 사용해서 URL규격에 맞게 보여주는 것이 URL인코딩입니다. HTML인코딩 역시 브라우저가 코드를 오해하지 않게 적절히 다른 문자들로 치환하는 것인데 파일입출력시 이스케이프문자 \과 겹치는걸 대비해서 \\을 쓰는것과 같은 사례입니다. 결국은 컴파일러가 처리해주기 때문이죠. Base64 Encoding은 데이터를 64개의 지정된 아스키문자로 바꾸는 인코딩입니다. 이것들을 잘 사용하면 AES-256같은 암호화 알고리즘없이도 사람의 눈으로 보기에는 알아볼 수 없게 만들 수 있습니다만 요즘 트랜드는 암호화없는 난독화의 경우 AI에게 쉽게 격파당하는 단점이 있습니다. 반대로 알아보기 힘들다는 점으로 공격자는 이것을 악성코드로 숨기고 데이터덩어리처럼 포장해서 보낼수도 있습니다.(그러면 안돼)

Q5. 내 컴퓨터 브라우저에서 개발자도구를 이용해 이벤트 페이지의 '탈락'이라는 글자를 '당첨'으로 바꿨습니다. 그런데 왜 실제로 경품을 받을 수는 없을까요? 클라이언트(내컴퓨터)와 서버의 역할 차이를 중심으로 설명해주세요
A5. 클라이언트는 단순히 받은 html문서를 보는것이 전부일 뿐 통신규약(신뢰경계 등)에 따라 서버의 데이터를 건들 수 없습니다. 마찬가지로 같은 컴퓨터로 해당 사이트를 단순히 재접속하기만 하면 다시 탈락으로 나오게 됩니다. 즉 실제로 저장된 탈락 데이터를 당첨으로 바꾸는 것이 아닌 말 그대로 보이는 상태만 바꾼 것입니다. 저도 예전에 esp32로 서버를 만드는 프로젝트를 시도한 적이 있었는데 이때 데이터를 CSV형태로 관리하고 이것을 클라이언트가 읽어오는 코드를 짜다가 이러한 어려움을 알게 되었습니다.

Q6. 로그인을 한 번 하면 페이지를 이동해도 로그아웃되지 않는 이유는?
A6. 기본적으로 웹브라우저는 데이터를 주고받으면서 상태를 기억하지 않습니다. 그러나 모든 경우에 해당하지는 않지만 로그인을 한 번 하면 페이지를 이동해도 로그아웃되지 않는 이유는 로그인을 하면서 별도의 기록이 되기 때문입니다. 보통은 이것을 토큰이나 장기적으로는 쿠키로 저장을 합니다. 또한 개발자도구에서 이에 관련된 값들을 얻어와서 API처럼 활용할 수도 있습니다.

 


part 포렌식



Q1. 16진수 VS 2진수 VS 10진수
A1. 10진수는 현재 이 글에도 사용되는 범용적인 사람들이 사용하는 수의 체계입니다. 숫자가 10을 넘으면 그 위로 올림(carry)하여 한 자리에 0~9까지를 표현합니다. 2진수의 경우 디지털 세계에서 고전컴퓨터가 수를 처리하는 방식입니다. 10진수와 다르게  이 수 체계는 2를 넘으면 그 다음 자리로 올림을 하게 됩니다. 16진수도 마찬가지로 16을  넘으면 그 다음자리로 올림을 하는데 16의 경우 power(2,4)이기 때문에 16진수 한자리로 2진수를 4개 표현할 수 있어서 1바이트를 16진수 2개로 주로 표현하곤 합니다. 기계어를 보면  나타나는 0x00등이 그 예시이지요.

Q2. 기초적인 리눅스 명령어 정리(ls, pwd, cd 등등)
A2. 일단 제가 리눅스를 전혀 모르기에 몇가지만 찾아서 적어보았습니다.

  • pwd: 현재 내가 위치한 폴더의 전체 경로를 보여줌
  • ls: 현재 폴더에 어떤 파일이나 폴더가 있는지를 보여줌
  • cd: 폴더이동(DOS와 동일)
  • mkdir: 새 폴더 만들기
  • cp: 파일복
  • mv: 파일옮기기
  • rm: 파일삭제
  • cat: 파일내용 출력
  • sudo: 관리자권한 실행
  • clear: 터미널 클리어

 

Q3. CLI 와 GUI의 개념 및 CLI 사용 방법
A3. 저는 이거의 경우 windows3.1이전과 windows95를 예시로 설명을하고 싶습니다. 일단 CLI는 Command Line Interface로 소위 말하는 검은창 혹은 도스의 IO방식입니다. 글과 문자가 대부분으로 이루어져있습니다. GUI는 Graphical User Interface로 그래픽을 통해서 커맨드없이(혹은 커맨드를 다른 방식으로 치환해서) 사용자와 상호작용하는 방식입니다. 대표적인 예시로 든 windows3.1까지의 경우 컴퓨터를 사용하기 위해서는 명령어를 외어야 했고 그렇기에 컴퓨터난이도가 높아 학원까지 있었습니다만 windows95가 만들어진 이후에는 컴퓨터의 대중화에 힘입어서 대부분의 사람들이 어렵지 않게 컴퓨터를 사용할 수 있게한 전환점으로 현재 windows도 95와 그 이전과 비교한다면 95에 가장 가깝다고 말할 수 있습니다. (실제로 저도 마찬가지로 DIY로 가상머신으로 고전 운영체제의 컴퓨터를 많이 돌리지만 대부분의 마지노선이 다루기 편한 windows95입니다.) 다른 예시로는 옛날 PC통신 시절의 CLI(정확히는 TUI지만)와 www이후의 html(gui)가 있습니다. 역시 인터넷 사용을 편하게 해주는(물론 PC통신은 엄밀히 말해서 인터넷은 아닙니다.) 전환점이 되었습니다.
 그리고 CLI사용방법? 이거는 답변드리기가 어렵습니다. CLI라고 해도 GUI랑 단순할뿐 정형화되어있지는 않습니다. 다만 공통분모로 작성을 한다면 엔트리에 명령어를 직접 입력해서 동작하는 방식으로(이 명령어는 사용자가 직접 작성하는 구조) 진행됩니다.

Q4. 기본적인 컴퓨터 파일 구조 체계에 대한 정리(C드라이브, D드라이브, 사용자 폴더 구조 등)
A4. 일단 예시에 C,D드라이브가 있기 때문에 운영체제적으로도 추상화되어있는 체계말고 windows처럼 추상화된 드라이브를 잡는 경우를 예시로 들자면 외부 보조저장장치의 경우 누군가가 호출을 해서 잡아서 그 경로를 기반으로 파일이나 폴더가 주소체계에 맞게 되어있습니다. 이건 모든 파일관리시스템에서 대부분 동일합니다. 이중에서 windows의 경우 플로피디스크의 잔재로 요즘의 경우 주 운영체제가 설치되는 환경을 C드라이브로 기본적으로 설정을 하였습니다. 이 외에 다른 물리적 저장매체가 잡히거나 같은 디스크를 파티션을 나눈다면 이때 다른 드라이브 할당 문자로 A,B,D를 사용자가 지정해서 등록할 수 있습니다.
 컴퓨터 파일의 경우 물리적으로는 완전히 다른 위치에 있지만 디스크의 맨 처음 헤더에서 해당 파일의 리스트를 보여주고 우리는 이것을 기반으로 파일을 탐색합니다. 그리고 파일을 실제로 열 경우 그 주소를 참조해서 해당 위치의 바이너리값을 읽어오는 방식을 사용합니다. 


part 리버싱



Q1. 컴퓨터 구조란?
A1. 컴퓨터의 구성요소를 크게 4가지로 나눈다면 저는 CPU, RAM, 보조기억장치, I/O 로 나눌 것입니다.(폰 노이만 구조) 현대 컴퓨터는 대부분 메모리에 저장된 명령어를 CPU가 순차적으로 처리하며, 이 과정에서 효율적인 데이터 흐름을 만드는 것이 컴퓨터 구조의 본질이라고 생각합니다.

Q2-1. 어셈블리어란?
A2-1. 어셈블리어를 저는 기계어라고 생각을 합니다. 사실 실제로도 거의 같습니다. 말 그대로 기계가 알아듣는 언어이기에 하드웨어를 직접 다루는 언어입니다. 모든 CPU는 그 고유의 하드웨어를 가지고 있고 이에 맞는 기계어를 가지고 있습니다. 이때 사용되는 하드웨어는 저의 주 무대일 수도 있는 전자회로적 ALU이지요. 74HC283같은 IC로 덧셈을 수행하거나, 7404같은 IC로 NOT을 구현하거나 하는 과정이 수없이 일어나는 것이 CPU이고 이것을 인간이 비교적 보고 직접적으로 다루기 쉽게 해주는 것이 어셈블리어입니다. 이 때문에 어떤 컴퓨터의 어셈블리어가 다른 곳에서 적용이 되는것은 운영체제의 도움(시스템 콜 등. 물론 이것도 100%보장은 못함)을 받지 않는다면 어렵다고 볼 수 있습니다. 여기서 이 명령어들의 모음이 이 질문의 중단원인 명령어 집합입니다.

Q2-2. RISC vs. CISC
A2-2. 이 단어들 둘다 아키텍쳐입니만 이 둘의 지향점이 많이 다릅니다. RISC의 R은 Reduced, 즉 명령어들을 간단하게 만들고 빠르지만 간단하게 처리하자 입니다. 명령어의 규격이 통일되어있지만 하나의 작업도 여러개의 명령어를 써야하는 것이죠. 또한 클럭이 확 늘어나는 특징이  있습니다. 반면 CISC의 C는 Complex, 즉 여러개의 복잡한 명령어를 가지자 입니다. 어떤 프로그램을 하나  만들더라도 명령어 하나가 많은 작업을  할수 있기에 코드의 길이가 짧아지지만 대신 직접적인 회로의 양이 증가하고 클럭 맞추기가 어렵습니다. 이 둘의 관계를 잘 보여주는 예시로는 닌텐도사에서 만든 두 게임기인 슈퍼패미컴(1990년)과 닌텐도64(1996년)가 있습니다. 슈퍼패미컴의 경우 16비트, 3.58 MHz라는 CPU를 가진것에 반해 고작 6년후에 출시가 된 닌텐도64의 경우 64비트, 93.75 MHz라는 CPU를 지녔지만 정직하게 실 성능도 한세대 차이에 가격차이도 거의 없었습니다. 이것은 슈퍼패미컴은 CISC를 채택하여 크고 느리게 처리하였기에 표기상은 낮아보였지만 반대로 N64는 RISC를 채택하여  작고 빠르게 처리하여 실성능과는 다르게 표기 수치가 약 30배 차이가 나게 된 사유가 있었습니다.

Q2-3. x86-64
A2-3.  x86이라는 용어는 옛날 80시리즈 32비트 아키텍쳐를 의미하고 x64라는 용어는 intel의 64비트 아키텍쳐를 의미합니다. 여기서 특별히 64비트로 넘어오지만 x86시절의 하위호환성을 대부분 유지하기에 x64를 x86-64라고 명명하기도 합니다.

Q3. 마이크로아키텍쳐
A3. 아키텍쳐는 특정 명령어체계입니다. 그렇기에 같은 아키텍쳐라면 확장기능이 있지않다면 기계어는 같습니다. 마치 옛날자동차나 신형자동차나 엑셀을 밟으면 가는것처럼요. 그러나 앞에 마이크로가  붙으면 지향점을 의미합니다. 마치 현대자동차와 기아자동차처럼요. 아무튼 마이크로아키텍쳐는 CPU가 동작을 하는 과정을 위한(CPU아키텍쳐) 하드웨어를 배치하는 것을 포괄적으로 의미합니다. 물론 같은 마이크로아키텍쳐 내부에서도 공정에 따른 라인배치나 처리방식(중간과정), 반도체공정이 미묘하게 다르다는 특징이 있지만 하나의 family로 부릅니다.

Q4. 메모리 계층이란?
 A4. 컴퓨터는 자료를 다루는 도구이기에 자료가 곧 식량입니다. 그렇기에 이 자료들을 다루는데 있어서 물리적으로 여러 형태의 방법으로 자료를 저장합니다. 이때 자료를 저장하는 방법은 크게 레지스터, 캐시메모리, 메인메모리(RAM), 보조기억장치로 나뉩니다. 제가 나열한 순서중 레지스터로 갈수록 CPU가 빠르게 접근하여 빠른 연산이 가능하고(사실 아예 레지스터와 캐시메모리는 CPU안에 있음), 반대로 보조기억장치로 갈수록 비휘발적이며 저장공간대비 가격이 저렴해집니다. 또한 레지스터에 가까울수록 프로그래머의 손을 덜 탄다는 특징이 있는데 레지스터의 경우 상승엣지등을 감지하여 클럭으로 입출력을 제어하는 반면 램이나 보조기억장치는 운영체제나 혹은 사람이 직접 제어한다는 특징이 있습니다.
 제 컴퓨터를 기준으로 한다면 일단 저는 i9-13900H를 쓰기 때문에 cpu를 통해 레지스터와 캐시메모리를 알 수 있는데 인텔에 따르면 레지스터는 64비트짜리 여러개(정확한 수치 확인불가), 3개의 캐시메모리(1.2,11.5,24MB, 작업관리자에서 확인가능. 이것도 숫자에 따라서 속도가 다르지만 자세히 적지는 않겠습니다.)가 있으며 이제 cpu랑 다른 파트인 메인메모리는 15.6GB짜리(이것도 표기는 16GB)를 가지고 있고, 마지막으로 자료를 저장하고 전원이 끊겨도 보존되는 SSD는 1TB입니다.
 사실 최신 트렌드는 요즘 SoC라고 해서 CPU안에 사실상 램까지 집어넣는 시도가 많이 보이고 있습니다. 이 경우 CPU와 메인메모리 사이의 공간이 짧아져서 마치 캐시메모리처럼 쓰는 것도 실험적으로 진행중입니다. 예시를 들자면 intel의 Intel® Core™ Ultra Processors (Series 2)라인이 있습니다.

Q4-1. 캐시
A4-1. 캐시메모리는(캐시파일 아님)CPU안에 있는 작은 메모리 입니다. 주로 자주쓰는 데이터를 저장하거나 다른 메모리계층에서의 속도차이를 완충하는 버퍼같은 역할을 합니다. 레지스터처럼 빠르고 실시간으로 계산되지는 않지만 엄연히 CPU안에서 좀 큰 정보들을 처리하는데 쓰기도 합니다. 덕분에 CPU가 레지스터가 아니어도 큰 정보들(사실 레지스터에게는 이러한 글 자체도 담기는 벅참)을 비교적 빠르게 접근할 수 있게 해주는 것이죠. 물론 이것이 그러다보니 빨리 계산되고 사라지는 레지스터나, 외부에서 침범하기에는 비교적 보안이 잘 되어있는 다른 저장장치랑은 다르게 자주쓰는 비밀번호나 개인정보가 캐시에 있다가 공격자에 의해 탈취당하는(멜트다운 등) 문제도 있었습니다. 또한 고급언어로는 대부분 캐시나 레지스터를 직접적으로 다룰 수 없다는 문제도 있습니다.(이때문에 CPU게이트에서 여러 회사가 고생을 많이 했다는 말이 있습니다.)
 하드웨어적으로는 캐시메모리는 기본적으로 레지스터와 같은 D플립플롭(74HC374와 같음)과 비슷한 구조로 사용합니다만 이를 제어하는것은 RAM의 CMOS(옛날에는 TTL)입니다. 이를 독자적으로 SRAM으로 부릅니다. 그렇기에 신기하게도 RAM과 레지스터의 중간의 특성을 보여주게 되는 것입니다. 물론 플립플롭을 사용하기에 가격과 부피 사유로 몇백MB급으로 키우지 못하고 있는것이 현실입니다.

Q5. 컴파일이란?
A5. 사실 아까 어셈블리어랑은 정확히 반대되는 개념입니다. 어셈블리어는 기계어랑 바로 매칭이 된다는 특징이 있기에 표를 통해 직접적으로 기계어의 번역이 가능합니다.(저급언어) 반대로 포트란을 필두로 하나의 문법체계를 공유하면서 대신 하드웨어에 따라서는 같은내용을 다른 기계어로(CPU마다 기계어가 다르기에) 처리한다는 개념이 컴파일입니다. 예시로는 인사를 하는것을 국적에 따라 "Hello", "안녕하세요" 하는것과 비슷한 것입니다. 덕분에 같은 언어로 다른 CPU에 (컴파일러가 있다면)적용이 가능하다는 장점이 있습니다. 다른 특징으로는 추상화라는 개념이 있습니다. 예를들어 저희가 컴퓨터 화면에 "Hello World"를 출력하려면 기계어로는 일단 메모리를 확보를 하고, 레지스터로 CPU를 통해 필요한 연산을 수행한 후 IO를 찾아서 그 IO에 맞게 출력을 하는(이것도 많이 생략한거고 생각보다는 더 복잡함) 절차를 거쳐야 하는데 이걸 어셈블리어로 코딩을 하기에는 위 모든 과정을 전부 코드를 짜야하기에 생산성이 떨어집니다. 대신에 고급언어를 쓰면 컴파일 과정에서 위의 과정중 상당수를 생략해주기 때문에 생산성이 높아집니다. 물론 그만큼 컴파일 과정에서 우리가 지정할 수 없는 명령도(대표적으로 레지스터) 있기 때문에 현대에는 비교적 추상화 수준이 낮은 언어는 힌트라는 개념을 추가하는 추세입니다.

Q5-1. 소스코드와 목적코드
A5-1. 쉽게말해서 보통의 경우는 고급언어와 저급언어입니다. 저는 개인적으로 인간이 읽기 쉽다와 읽기 어렵다 라는것으로 이해를 하는데 소스코드를 컴파일하면 (전처리나 링킹등을 포함해서)목적코드가 출력됩니다.

Q5-2. 컴파일 (과정까지)
A5-2. 사실 이걸 포괄적으로 알기는 어렵습니다. 그러므로 저의 주 언어인 C언어를 기반으로 적었습니다. 우선 전처리입니다. 전처리의 경우 사실 단순히 치환에 가깝습니다. 필요한 헤더를 (일단은) 통째로 넣고, 메크로를 말 그대로 그대로 바꿔쓰는 과정을 합니다. 물론 요즘은 컴파일러가 똑똑해져서 컴파일 과정에서 약간의 연산을 하며 치환하기도 합니다.

#define ID 60241796
#define ID_C ID%10000
/*여기서 어차피 코드에서 ID_C는 1796임이 변하지 않기에 처음부터 1796으로 치환을 진행함*/

그 다음은 소스파일을 본격적으로 기계어로 바꾸는 컴파일 과정을 진행합니다. 컴파일이 완료되면 어셈블리어파일이나 바이너리 파일로 출력이 나옵니다. 여기까지가 순수 컴파일이지만 보통은 이 이후 실제로 기능과 연결을 시켜서 바로 실행가능한 링킹도 이 범주에 들어간다고 볼 수 있습니다.(어셈블리어 코딩시에는 링킹을 수동으로 해야함)
 참고로 컴파일은 같은 언어에 같은 CPU의 경우에도 굉장히 많은 컴파일러가 존재하고 같은 언어지만 많은 문법이 있습니다. 제 예시로는 저는 C언어를 보통 C89스타일(대표적으로는 for(int i=4;;)를 미지원. 이걸 하려면 {}를 만들어서 간접적으로 변수를 버리는 루프를 만들어야 합니다.)로 만드는 편이고, 경우에 따라서는 컴파일러 중에서는 특별히 아래의 코드가 지원되는 기능(이라고는 하지만 요즘은 대부분의 컴파일러가 지원을 하더라고요. 또한 보통들 이러한 문법은 잘 안씁니다.)을 쓰곤 합니다.

const void *labelarr[4]={&&ADD,&&SUB,&&MUL,&&DIV}; /*C표준 컴파일러는 미지원하는 문법*/
	returnlabel:
	...
	goto *labelarr[C]; /*C표준 컴파일러는 미지원하는 문법*/
	PORTC=C;
	ADD:;
	...
	goto returnlabel;

 

Q5-3. 인터프리팅
A5-3. JavaScript, Python, Ruby등 많은언어가 인터프리터 지원으로 있다고 하지만 제가 아는 언어가 파이썬밖에 없기에 이를 예시로 든다면 C언어의 경우 소스를 완성하고 이를 한번에 컴파일을 해서 기계어로 만들어냅니다. 그러나 인터프리팅이 지원되는 언어의 경우 라인 하나를 바로 읽어서 컴퓨터가 실행을 하는 방식입니다. 덕분에 코드를 만들면서 바로 실행해서결과를 볼 수도 있고 인터프리터가 설치되있다면 exe등으로 컴파일 없이 소스코드만 가지고 동작을 시킬 수 있는 이점이 있지만 기본적으로 속도가 느리다(라고 하기에는 컴파일 언어가 상대적으로 저수준인 면도 있습니다만)라는 단점이 있습니다.

Q5-4. 컴파일 vs. 인터프리팅 둘의 차이점
A5-4. 컴파일과 인터프리팅 둘다 결국 기계어로 바꾼다 라는 점에서는 공통점을  가지지만 언제 바꾸는지로 구별이 됩니다. 영어를 한국어로 번역하는 것을 예시로 들자면 같은 영어를 기록해 두고 원서를 번역본으로 만드는것이 컴파일, 영어를 듣자마자 한국어로 동시통역하는것을 인터프리팅으로 볼수 있겠지요. 이러한 상반된 특징때문에 위에서 설명한 것처럼 고급언어도 컴파일언어(C,RUST,GO)와 인터프리팅언어(지만 소스를 통으로 컴파일하는 것도 가능은 합니다.)로 나뉩니다.

Q6. 프로그램이란? (왜 바이너리라고도 부를까?)
A6. (특히 요즘의 경우 운영체제에서)기기가 특정 기능을 수행하게 하는 절차입니다. 사용자의 입력에 따라 그 입력된 값을 일정한 처리 방법과 순서에 따라 처리하여 결과를 산출하게 합니다.
 그리고 솔직히 말씀드려서 저는 strcmp("프로그램","바이너리")=!0 이라고 생각합니다. 둘은 대부분의 경우 지칭하는 개념은 같지만 전자공학과인 저의 시선에서 본다면 프로그램은 회로를 돌아가게 하는 알고리즘이라면 바이너리는 그 체계이기 때문입니다. 그래서 왜 바이너리라고 부르는지 저 개인적으로는 사실 잘 모르겠지만 컴퓨터의 프로그램은 대부분 바이너리를 쓰기에 라는 결론을 내리겠습니다.(사실 이게 또 양자컴퓨터 영역으로 넘어가면 혼란스러워지지만 이건 너무 갔으니 제외하겠습니다.)
 그리고 중요한 것이 ASIC은 컴퓨터지만 프로그램이라는 개념을 사용하지 않습니다. 물론 ASIC은 그러한 특징으로 (ASIP이상이 아닌이상)고급언어 개념이 현재는 전무한 상태입니다.

Q7. 디버깅이란?
A7. 디버깅의 범주가 너무 넓고 질문이 추상적이기에 개념적인것만 기록하였습니다. 디버깅의 경우 프로그램의 문제가 생길경우 그 원인을 찾아내고 해결을 하는 과정입니다. 만약 신텍스 에러가 났다면 보통은 컴파일러수준에서 오류를 알려주기 때문에 오류가 난 지점에서 문제를  해결 하면 되지만 런타임에러라면 그 난이도가 올라갑니다. 특히 이미 컴파일이 되어 소스를 알수없는 프로그램의 경우 디컴파일이후 난독화수준으로 어려운 언어를 디버깅해야하기에 그냥 필요한 기능만 하는 별도의 미러 프로그램을 만드는것이 편한 경우도 있습니다. 논리오류의 경우 프로그램까지 잘 돌아가지만 결과가 틀린 경우입니다. 이 경우도 변수를  일일히 확인해야하기 때문에 역시 소스를 따로 빼놓는것이 유리합니다.

Q8. 윈도우에서의 프로세스 동작과정
A8. 우선 Windows의 경우 벌써 40년이 넘어가는 운영체제이기 때문에 현대에 사용되는 NT를 기반으로 작성하였습니다. Windows는 운영체제입니다. 그렇기때문에 우선 컴퓨터에 설치를 하는것이 우선입니다. 하드웨어에 맞게 적절히 windows가 설치가 되면 windows는 해당 기기의 대부분의(펌웨어 등 기기 독자적인 부분은 제외) 권한을 가지게 됩니다. 이후 사용자가 윈도우즈를 키면 windows는 간략한 POST과정을 거쳐 부팅이 됩니다. 이후 사용자가 실행하는 프로그램을 실행시켜 IO를 진행하는것이 기본 아이디어 입니다.
 그러나 컴퓨터마다 기계어가 다르기 때문에 동일한 IO여도 이를 실행하는 언어는 완전히 다릅니다. 이때문에 windows는 시스템콜이라는 것을 사용합니다.  프로그램 파일이 특정 기능을 수행할 경우 widnows의  기본 API중 이와 유사하거나 경우에 따라서 CPU가 직접 지원하는 방식을 찾아서 진행을 하게 됩니다. 이것으로 IO를 수행하는것이 프로세스 동작과정입니다.
 이 과정에서 프로세스는 메모리를 필요로 하는데 메모리의 경우 windows가 일단 모든 메모리를 선점한 이후 windows가 프로세서가 필요로 하는 양만큼 능동적으로 주는 체계(가상메모리체계. 램플러스랑 다릅니다.)를 사용합니다. 그렇기에 NT이후의 경우 특유의 메모리문제가 줄어들었습니다.
 마지막으로 windows의 경우 위험한 동작을  막기 위해 Ring구조를 취해서 대부분 사무적인 임무는 사용자모드(Ring3)에서 수행시키고 관리자모드, PE모드, 커널모드로 갈수록 직접적인 동작을 수행합니다.

 

 


part 시스템 해킹


#include <stdio.h>

int main(){
	char arr[10];
	scanf("%s",arr);
}

 컴퓨터 오류는 크게 신텍스오류와 런타임오류로 나뉩니다. 이건 문법적으로 틀린것이 전혀 없습니다. 그러나 런타임에서 문제될 것이 있습니다. 현재 arr라는 배열은 스택에 배치가 됩니다. 스택은 그 안의 데이터는 자유롭게 수정가능하지만 그 크기는 자유롭지 않습니다. 정수 등의  경우는 오버플로만 조심하면 큰 문제될 것이 없지만 현재 arr은 배열입니다. 고정된사이즈 10을 가지고 있고요. 만약 scanf가 10 이상의 데이터(실제로는 문자열이므로 '\0'제외 9)가 들어오면 이 배열의 너머의 데이터를  침범하게 되어 문제가 됩니다. 그러나 이건 어떤 값을 넣냐에 달려있기에 런타임에서 오류가 나게 되는 것입니다.
 문제는 이게 단순히 런타임에러로 끝나서 return -1;상황이라면 양호하지만 누군가 이것을 의도적으로 메모리를 침범한다면 그것이 공격자에게 유리하게 사용될 가능성이 있습니다. 예를들어 스택에 저장되는 변수는 그 이후에 복귀주소(RET)를 가지고 있는 경우가 있습니다만 10자리 이후 공격주소를 scanf에 넣는다면 복귀주소는 메모리에서  공격주소로 바뀌고 호출이후 다른 동작을 하게 됩니다. 물론 이건 요즘 컴파일러에 따라서 불가능하기도 하다고는 합니다. 그 왜에도 처음부터 크러시를 목적으로 공격하는것에 취약합니다.
 해결법으로는 처음부터 갯수를 %9s로 제한을 두거나, fgets로 지정된크기까지만 잘라서 받는 방법이 있습니다. 혹은 안전하게 동적메모리를 사용하는 것도 방법입니다.

 


긴글읽어주셔서 감사합니다.