참고※참고 자료는 드림핵을 이용하였습니다.
※드림핵의 로드맵대로 공부할 예정입니다.
※스스로를 위해 정리한 참고 자료입니다.
컴퓨터 과학(Computer Science)은 컴퓨터를 이용한 모든 작업과 그 기반 이론을 연구하는 학문입니다.
이는 코딩뿐만 아니라 계산 이론, 알고리즘, 소프트웨어 설계, 네트워크 등 광범위한 영역을 포함합니다.
해킹은 이러한 컴퓨터 과학 지식을 응용하여 프로그램이나 시스템의 취약점을 발견하고 공격하는 행위를 가리킵니다. 따라서 컴퓨터 시스템, 네트워크, 프로그래밍 언어, 운영 체제 등의 컴퓨터 과학 지식이 요구됩니다.
진법은 임의의 숫자 혹은 문자를 사용하여 수를 표현하는 체계를 말합니다.
- n진법은 한 자릿수를 n가지(0~n-1)의 숫자 혹은 문자로 나타낼 수 있습니다.
- n진법으로 나타낸 수는 각각 10진수(decimal), 2진수(binary), 16진수(hexadecimal)라고 합니다.
비트와 바이트는 컴퓨터의 데이터를 다루는데 가장 기본이 되는 개념이다.
- 컴퓨터는 0과 1만으로 데이터를 표현하고 처리한다.
- 컴퓨터에서 사용하는 데이터의 최소 단위를 1비트(bit, binary digit)라고 한다.
- 8개의 비트로 구성된 더 큰 단위는 1바이트(byte)라고 하고, 이는 메모리에 저장되는 최소단위다.
- 1바이트는 가지의 수를 표현할 수 있습니다.
- 10진수로 0~255, 2진수로 00000000~11111111, 16진수로 00~FF 까지 나타냅니다.
10진수 60을 2진수와 16진수로 나타낸 예시
- 16진수 한 자리는 2진수 네 자리로 변환된다.
- 따라서 16진수 0x3C는 각 자리를 2진수로 나타내면 0011과 1100이 됩니다. 이를 이어 붙이면 00111100이 되며, 이는 10진수 60을 2진수로 표현한 것과 같다.
여러 개의 비트로 구성된 이진 데이터에서 가장 왼쪽에 있는 비트를 Most Significant Bit (MSB, 최상위 비트)라고 하고, 가장 오른쪽에 있는 비트를 Least Significant Bit (LSB, 최하위 비트)라고 부른다.
- “중요하다(Significant)”는 표현을 붙이는 이유는 가장 왼쪽에 있는 비트가 숫자의 크기에 가장 큰 영향을 미치기 때문이고, 가장 오른쪽에 있는 비트는 숫자의 크기에 가장 작은 영향을 미치기 때문에 그러한 명명이 붙은 것으로 알려져 있다.
부호가 있는 데이터의 경우, MSB는 부호의 의미를 가지게 됩니다.
- MSB가 0이면 양수, 1이면 음수를 나타냅니다.
- 프로그래밍 언어에서 부호(+,-)를 가지는 데이터는 Signed 데이터 또는 부호가 있는 데이터라 부르고, 부호없이 양수(+)만 나타내는 데이터는 unsigned데이터 또는 부호가 없는 데이터라 부릅니다.
- 예를 들어 앞서 살펴본 0b10010100가 부호가 있는 데이터라면, MSB가 1이기 때문에 10진수로 표현하면 음수인 -108이 됩니다.
- 반면에 부호가 없는 데이터라면, MSB인 1은 부호를 의미하지 않고, 값을 의미하므로 양수인 148이 됩니다.
2바이트 이상의 데이터는 메모리에 연속적으로 저장됩니다.
- 이때 각 바이트가 메모리에 정렬되는 방식을 바이트 오더링이라고 부릅니다.
- 바이트 오더링의 두 가지 방식으로 빅 엔디안(Big Endian)과 리틀 엔디안(Little Endian)이 있습니다.
- 어떤 바이트부터 낮은 주소에 저장되는지에 따라 두 방식을 구분합니다.
- 빅 엔디안은 큰 바이트부터 낮은 주소에 저장되고, 반대로 리틀 엔디안은 작은 바이트부터 낮은 주소에 저장됩니다.
💡 바이트 오더링과 MSB,LSB
- MSB,LSB를 이용하여 빅 엔디안, 리틀 엔디안을 설명하는 것을 종종 볼 수 있습니다.
- 이떄의 MSB,LSB는 앞서 배운 Bit를 의미하는 것이 아니라, Byte를 의미합니다.
- 바이트 오더링은 비트의 순서가 아니라 바이트의 순서를 고려하는 것으로, 바이트 내 비트의 순서는 동일하고 바이트의 순서만 달라집니다.
가장 왼쪽에 있는(큰) 바이트부터 메모리의 낮은 주소에 저장됩니다.
- 네트워크 상에서 데이터를 전송할 때는 빅 엔디안 방식을 따릅니다.
- 대표적으로 SPARC CPU에서 빅 엔디안을 사용합니다
가장 오른쪽에 있는(작은) 바이트부터 메모리의 낮은 주소에 저장됩니다.
- 대표적으로 Intel의 x86, x86-64 CPU에서 리틀 엔디안을 사용합니다. 대다수의 개인용 컴퓨터 및 서버 환경에서 x86-64 CPU 아키텍처를 사용하고 있는 만큼, 리틀 엔디안 방식을 잘 알고 있어야 합니다.
정교함과 정확함이 필수인 해킹 분야에서 데이터가 어떤 바이트 오더링으로 메모리에 저장되었는지 고려하는 것은 기본 중에 기본입니다.
- 예를 들어, 리버스 엔지니어링 분야에서 메모리에 저장된 값을 이용해 역연산을 수행하여 어떤 정답이 되는 값을 찾아내는 작업은 매우 흔합니다.
- 이때 메모리에 저장된 값이 어떤 엔디안으로 저장되어 있는지를 고려하지 않으면, 전혀 다른 데이터로 혼동하여 올바른 역연산 결과를 얻을 수 없게 됩니다. 앞서 살펴본 예시와 같이 리틀 엔디안 방식으로 저장된 0x01234567을 빅 엔디안으로 생각하고 0x67452301이라는 숫자로 다뤄버리면, 결국 전혀 다른 데이터를 가지고 역연산을 수행하는 꼴이 되므로 올바른 결과를 얻을 수 없게 됩니다.
- 시스템 해킹을 예로 들어 보겠습니다. 공격을 위해 특정 메모리 주소에 원하는 값을 덮어씌워야 한다고 가정합니다. 메모리의 낮은 주소부터 값을 쓰는 경우라면, 리틀 엔디안을 고려하여 저장하려는 값의 가장 오른쪽 바이트부터 써야 원하는 값이 제대로 저장됩니다.
- 또한 어떤 변수의 출력값을 알아내려고 한다면, 메모리의 변수 주소 영역에서 높은 주소부터 값을 읽어온다는 것을 고려해야 합니다.
- 현실에서 많은 개인용 컴퓨터와 서버 환경이 x86 아키텍처를 사용하고 있고, 이에 따라 드림핵 로드맵 역시 대부분 x86 아키텍처 환경을 기준으로 설명하고 있습니다.
- 그만큼 리틀 엔디안에 친숙해지는 편이 좋으므로 예시를 통해 자세히 살펴보겠습니다.
- 하단의 코드는 문자열과 16진수 정수를 메모리에 저장하는 C 프로그램의 소스 코드입니다.
- 각각 메모리에 저장된 값과 출력값을 직접 확인하여 리틀 엔디안 방식이 어떻게 적용되었는지 알아보겠습니다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char * str = "ABCD"; // 16진수: 41424344
puts(str); // 문자열 출력 결과: ABCD
unsigned int num = 0;
num = 0x41424344;
printf("%x\n", num); // 16진수 출력 결과: 41424344
return 0;
}
str
변수 주소의 메모리를 1 바이트씩 4 바이트 출력한 결과입니다. 1 0x555555556004: 0x41 0x42 0x43 0x44
num
변수 주소의 메모리를 1 바이트씩 4 바이트 출력한 결과입니다.num
변수 값을 출력하면 메모리에 저장된 4 바이트에서 높은 주소부터 읽어 와서 원래 값 그대로 출력됩니다.1 0x7fffffffe314: 0x44 0x43 0x42 0x41
피연산자를 2진수로 표현하여 비트(bit) 단위로 연산하는 것을 비트 연산이라고 합니다.
- 비트 단위로 논리 연산을 수행하거나, 비트를 특정 값만큼 이동하는 시프트(shift) 연산을 수행합니다.
참 또는 거짓 값으로 연산을 수행하고 결과로 참(1,true) 또는 거짓(0,false)을 반환합니다.
비트 단위로 논리 연산을 수행합니다. 1은 참, 0은 거짓을 나타냅니다.
비트를 특정 값만큼 왼쪽 혹은 오른쪽으로 이동합니다. n만큼 시프트한 결과는 으로 곱하거나 나눈 값과 같습니다.
인코딩(Encoding)은 데이터를 특정한 형식으로 변환하는 것을 말합니다.
- 데이터의 크기를 줄이거나, 컴퓨터가 이해하기 쉽게 변환할 때 사용합니다.
- 인코딩된 데이터는 디코딩(Decoding)하여 원래의 값을 구할 수 있습니다.
- 인코딩은 암호화와 유사하지만, 암호화는 데이터의 기밀성을 목적으로 남이 데이터를 알아보지 못하도록 변환하기 때문에 비밀키가 있어야 원문을 복구할 수 있지만, 인코딩은 누구나 표준화된 방식을 사용해서 디코딩하여 원문을 구할 수 있다는 점이 다릅니다.
- 인코딩은 컴퓨터가 데이터를 효율적으로 다루도록 중요한 역할을 수행합니다.
- 그만큼 해킹을 공부하다 보면 웹, 시스템, 네트워크 등 모든 분야에서 인코딩이 중요하다는 것을 알 수 있습니다.
- 어떤 인코딩된 데이터의 생김새를 보고 인코딩 방식을 유추하여 직접 디코딩해서 원문으로부터 단서를 얻어야 하는 경우도 있습니다.
컴퓨터는 0과 1만을 이용하여 정보를 처리하고 표현합니다.
우리가 입력하는 모든 글자가 0과 1로 변환되려면 숫자로 표현되어야 합니다.(이를 위한 것이 아스키 코드)
아스키 코드(American Standard Code for Information Interchange, ASCII)는 정보 교환을 위한 미국 표준 코드로, 문자를 숫자로 변환하는 문자 인코딩(character encoding)의 표준입니다.
- 아스키 코드를 사용하여 문자를 숫자로 인코딩하면 서로 다른 장치 간 데이터 전송을 더 쉽게 수행할 수 있습니다.
- 아스키 문자 1개는 1 바이트 크기로, 7 비트로 문자를 표현하고 1 비트는 오류 체크를 위해 사용합니다.
- 따라서 27=128가지의 문자 표현이 가능하며, 각 문자는 0~127까지의 10진수 값을 가집니다.
- 이 값을 문자의 아스키 값(ASCII value)이라고 합니다.
아스키테이블
🐧 아스키 테이블을 참조하여 다음 일련의 비트를 해석해보자!
01001000 01100101 01101100 01101100 01101111 00101100 00100000 01000100 01110010 01100101 01100001 01101101 01101000 01100001 01100011 01101011 00100001
정답: Hello, Dreamhack!
풀이 : 8비트씩 분리 -> 8비트 이진수를 십진수로 변환 -> 십진수에 대응하는 아스키 문자 찾기
유니코드(Unicode)는 영어 뿐만 아니라, 전세계 모든 언어의 문자에 고유한 번호를 부여하는 국제 표준 코드입니다.
- 서로 다른 언어를 사용할 때 문자가 호환되지 않는 문제를 해결하기 위해 만들어졌습니다.
- 아스키 코드보다 용량을 크게 확장하여 최대 32 비트로 문자 1개를 표현하여, 현재 143000개 이상의 문자를 표현할 수 있습니다.
- 유니코드의 처음 128개 문자는 아스키 코드의 문자와 정확히 일치합니다.
- 즉, 유니코드 안에 아스키 코드가 포함된다고 볼 수 있습니다.
- 유니코드 값은 U+ 뒤에 16진수를 붙여 나타냅니다.
- UTF-8, UTF-16, UTF-32 등 유니코드를 사용하는 다양한 인코딩 형식이 존재합니다.
- 이는 컴퓨터가 어떤 문자를 어떻게 읽어야 하는지 미리 정해줍니다.
- UTF 뒤의 숫자는 비트를 의미합니다.
- 가장 일반적으로 사용되는 UTF-8은 1~4 바이트의 가변적인 크기로 문자 1개를 표현하는 방식으로, 아스키 코드와 유사합니다.
- 현재 컴퓨터로 볼 수 있는 글자는 대부분 UTF-8로 인코딩된 값입니다.
웹에서 사용되는 URL은 특정한 형식의 문자열만 허용합니다.
- 알파벳 대소문자, 숫자, 그리고 일부 특수 문자만을 포함할 수 있습니다.
- 웹 브라우저로부터 받은 URL 문자열을 유효한 형식으로 변환하는 것을 URL 인코딩이라고 합니다.
URL 인코딩을 통해 문자열을 인터넷으로 전송 가능한 형식으로 변환합니다.
- 이를 통해 전송 중에 문자가 수정되거나 의도와 다르게 해석되는 것을 막을 수 있습니다.
- 허용되지 않는 문자, 즉 인코딩이 필요한 특수문자는
:/?#[]@!$&'()*+,;=%공백
입니다.
URL 인코딩은 % 기호 뒤에 해 문자의 아스키 코드 16진수 값을 붙여 나타냅니다.
- 공백 문자를 예로 들어 보겠습니다.
- URL에 공백이 포함되는 경우 + 기호 혹은 %20으로 변환됩니다.
- 여기서 %20이 URL 인코딩 결과입니다.
- 예를 들어, 문자열
Welcome, Dreamhack Beginners! :)
를 URL 인코딩한 결과는Welcome%2C%20Dreamhack%20Beginners%21%20%3A%29
입니다.
Base64 인코딩은 이진 데이터를 아스키 문자로 구성된 텍스트로 변환하는 인코딩 방식입니다.
- 총 64개의 아스키 문자가 인코딩에 사용되기 때문에 64진법(Base 64)라는 의미에서 이러한 이름이 붙여졌습니다.
- 64개의 아스키 문자는 알파벳 대소문자(52자), 숫자(10자), +, / 입니다.
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
- Base64 인코딩은 이진 데이터를 그대로 포함할 수 없이 텍스트만 허용되는 환경에서 이진 데이터를 텍스트 형식으로 나타내기 위해 사용합니다.
- 예를 들어, 이진 데이터인 이미지를 HTML 파일에 넣는 경우 base64로 인코딩해 넣을 수 있습니다.
Base64 인코딩 방식은 다음과 같습니다.
- 원본 이진 데이터를 비트 나열로 표현하고, 이를 6 비트씩 끊어서 묶습니다. 만약 비트의 개수가 6의 배수가 아닐 경우, 0을 뒤에 추가하여 6의 배수로 만듭니다.
- 각 6 비트 묶음을 수로 변환한 뒤, base64 테이블에서 해당하는 문자를 찾아 이로 치환합니다.
- 이렇게 치환 과정을 거친 뒤, 글자 수가 4의 배수가 되도록 문자 '='를 반복해 뒤에 추가합니다. 이를 패딩(Padding)이라고 합니다.
패딩을 왜 넣어야 하나요?
예를 들어 여러분이 ZA 라는 두 글자를 디코딩하게 되면, 011001 000000 이라는 12개의 비트 나열로 바뀌게 되고, 이를 앞에서 8개씩 끊어 읽으면 아스키 문자 ‘d' (01100100) 이후 0000 이 남게 됩니다.
이 경우 디코딩을 하는 입장에서 뒤에 추가적인 내용이 있는데 오지 않은 것인지, 아니면 여기서 디코딩을 끝내는 것이 맞는지 알 수 없게 됩니다.
그러므로 총 비트의 개수가 8의 배수가 되게끔 패딩 문자 '='를 뒤에 붙여 이를 명확하게 하는 것입니다.
dream
을 base64 인코딩한 예시입니다. 인코딩 결과는 ZHJlYW0=
입니다.🐧 온라인 base64 디코더를 사용해서 다음의 데이터를 직접 디코딩해보자!
SGFwcHkgRHJlYW1oYWNrLCBIYXBweSBIYWNraW5nIQ==
- 정답 : Happy Dreamhack, Happy Hacking!
풀이 : Dreamhack Tools
사용자는 웹 브라우저, 엑셀, 메모장 등의 프로그램으로 원하는 작업을 수행하곤 합니다.
사용자를 위해 특정한 기능을 수행하는 이러한 프로그램을 응용 프로그램이라고 합니다.
이때 응용 프로그램의 동작을 수행하고, 응용 프로그램에게 시스템 자원을 할당하는 등의 복잡한 관리 작업은 사용자가 아니라 운영체제(Operating System, OS)라는 소프트웨어에 의해 이루어집니다.
사용자 및 응용 프로그램은 컴퓨터 하드웨어(CPU, 메모리, 입출력 장치 등)에 직접 접근하지 않습니다.
대신 운영체제가 하드웨어와 사용자/응용 프로그램 사이에서 중재자 역할을 합니다.
운영체제는 CPU, 메모리, 입출력 장치(키보드, 마우스, 디스크) 등의 하드웨어 자원을 효율적으로 사용할 수 있도록 분배, 할당하여 성능을 높입니다.
만약 운영체제가 없다면 사용자가 하드웨어를 직접 관리해야 합니다.
운영체제 덕분에 사용자는 컴퓨터를 보다 편리하게 사용할 수 있습니다.
운영체제는 하드웨어 자원들을 적절히 분배하고 각 기능을 수행합니다. 수행하는 대표적인 기능들은 다음과 같습니다.
운영체제는 사용자와 컴퓨터 사이 인터페이스 역할도 합니다. 사용자가 컴퓨터에 명령을 내릴 수 있도록 하는데, 이는 다음으로 소개할 셸의 기능입니다.
운영체제는 크게 커널과 셸로 나눌 수 있습니다.
먼저 커널(Kernel, 알맹이)은 운영체제의 핵심 기능인 하드웨어 관리를 실제로 수행하는 프로그램입니다.
커널은 소프트웨어와 하드웨어 간의 커뮤니케이션을 관리하며, 시스템이 부팅될 때 메모리에 올라가서 꺼질 때까지 실행됩니다.
셸(Shell, 껍질)은 사용자와 운영체제의 커널 사이에서 사용자가 운영체제에 명령을 내릴 수 있도록 인터페이스 역할을 합니다.
사용자가 셸에 명령을 입력하면, 셸이 명령어를 해석하여 커널에 요청합니다.
커널은 명령을 수행하며 하드웨어를 조작하고, 수행 결과를 셸에 전송합니다.
셸은 이 결과를 해석하여 사용자에게 출력해 줍니다. 즉, 셸은 명령어를 해석하는 역할을 하여 사용자와 운영체제가 소통할 수 있도록 합니다.
셸을 획득하면 명령어를 통해 원하는 작업을 수행하고 시스템을 제어할 수 있게 됩니다.
따라서 일반적으로 셸을 획득하는 것을 시스템 해킹의 성공으로 여깁니다.
이번 강의에서는 컴퓨터 과학이 무엇이며 왜 공부해야 하는지 알아보고, 강의 수강을 위해 필요한 기초 지식을 공부했습니다. 이번 강의를 통해 컴퓨터에서 값을 표현하고 메모리에 저장하는 방법, 비트 연산을 수행하는 방법, 인코딩 방식, 그리고 리눅스(Linux) 운영체제가 무엇인지 충분히 숙지하셨다면 앞으로 강의를 수강할 때 더 깊은 이해가 가능할 것입니다.
해킹은 컴퓨터에 대한 이해와 기술적 지식이 필요한 분야인 만큼 깊게 파고들면 파고들수록 컴퓨터 과학에 대해 더 깊게 이해해야 합니다. 하지만 이번 강의에서 다룬 내용은 컴퓨터 과학에서 극히 기초적인 지식에 불과합니다. 따라서 드림핵 강의를 수강하시면서 컴퓨터 과학 공부도 함께 차근차근 병행해 나가시기를 추천해 드립니다. 드림핵이든 컴퓨터 과학이든 뒤로 갈수록 지식이 결합되면서 시너지 효과가 발생할 것은 분명합니다. 🚩
출처 : https://learn.dreamhack.io/440#16
진법: 임의의 숫자 혹은 문자를 사용하여 수를 표현하는 체계. 10진법, 2진법, 16진법, 8진법 등이 있고 컴퓨터에서는 주로 2진법과 16진법을 사용한다.
바이트 오더링: 바이트가 메모리에 저장되는 방식. 빅 엔디안은 가장 왼쪽에 있는 바이트부터 낮은 주소에 저장되고, 리틀 엔디안은 가장 오른쪽에 있는 바이트부터 낮은 주소에 저장한다.
비트 연산: 비트 단위로 수행하는 연산. |(or), &(and), ^(xor), ~(not), >>(right shift), <<(left shift) 연산자가 있다.
인코딩: 데이터를 특정한 형식으로 변환하는 것. 인코딩 방식으로는 아스키 코드와 유니 코드, url 인코딩과 base64 인코딩 등이 있다.
운영 체제: 사용자 및 응용 프로그램과 하드웨어 사이에서 중재자 역할을 하는 소프트웨어. 하드웨어를 관리하고 사용자에게 편리함을 제공한다.