Buffer over flow를 공부하기에 앞서 배우고 있는 것들을 계속해서 기술해 나갈 예정입니다.
buffer over flow란?
버퍼에 메모리 상에서 할당된 값보다 큰 값을 넣어서 인접한 버퍼에 영향을 주어 데이터를 변형시키는 해킹 기법.
오늘 배울 것은 버퍼의 역할과, 메모리의 구조입니다.
제일 처음부터 배운 버퍼에 대해서 알아보도록 하겠습니다!
Buffer란, 데이터가 어느 한 공간에서 다른 공간으로 이동될 때 임시적으로 값이 저장되는 임시 저장소를 뜻합니다.
조금 헷갈리시나요?
쉬운 예를 들어보죠.
사이다가 든 컵 A와, 콜라가 든 컵 B가 있다고 가정해보겠습니다.
A와 B컵사이의 내용물을 바꾸고 싶을 때 보통 어떻게 하죠?
- 새로운 컵 C를 가져와서 A컵에 있는 사이다를 C컵에 옮긴 뒤
- B컵에 있는 콜라를 A컵으로 옮기고
- 마지막으로 C컵에 있는 사이다를 B컵으로 옮깁니다.
여기서 내용물에 해당하는 콜라와 사이다는 데이터이구요
해당 내용물을 담고 있는 컵 A, B, C는 모두 버퍼에 해당합니다.
이제 이해가 좀 되시나요?
그럼 이젠 소스를 보고 알아보도록 하겠습니다.
C언어에서 작성한 소스입니다.
C언어에서 버퍼를 확인하는 가장 빠른 방법은 바로 변수를 선언하는 것입니다.
char 형 문자열 배열인 str을 선언해주고, int형 변수인 num을 선언해주었습니다.
여기서 str과 num은 모두 버퍼에 해당합니다.
출력 값을 확인해보면 str에 넣어준 hello와 num에 넣어준 5가 출력되는 것을 확인하실 수 있습니다.
여기서 str의 크기를 6을 준 이유는 문자열 배열은 배열 맨 마지막에 마지막임을 알리는 NULL 값이 들어갈 공간을 줘야 하기 때문입니다.
실제로 들어갈 수 있는 크기는 맨 마지막 칸을 제외한 5개입니다.
버퍼의 크기는 변수가 어떤 형식으로 선언이 되었냐에 따라서 다르게 주어집니다.
char = 1byte
int = 4byte
운영체제의 bit에 따라 다르지만 위 크기는 32bit 운영체제에서 할당받는 버퍼의 크기입니다.
그리고 해당 변수들, 그러니까 버퍼들은 선언이 됨과동시에 메모리에 공간을 각각의 byte만큼 할당받게 됩니다.
즉, 버퍼는 메모리 주소의 참조를 통하여 접근을 할 수 있습니다.
그럼 위 코드에서 해당 변수들의 메모리 주소를 확인해보도록 하겠습니다.
제가 3번 정도 실행을 해보았는데요, 보이시나요?
실행을 할 때마다 메모리 상의 주소가 달라집니다.
그리고 해당 메모리 주소가 가리키는 쪽에 str과 num의 값이 저장돼있다는 뜻입니다.
위 그림처럼 각각의 주소는 메모리상에서 해당 버퍼가 저장된 곳을 가리키고 있습니다.
그리고 눈치가 빠르신 분들이라면 알아채셨겠지만, 변수가 메모리상에 저장된 위치가 서로 다릅니다.
str이 num보다 위에 있어도 되는 거 아닐까요? 그게 아니라면 어떠한 규칙이 있는 걸까요?
그에 대한 답은 바로 스택에 있습니다.
스택(Stack) 이란??
LIFO(Last In First Out)의 구조를 가지고 있으며, 먼저 들어온 데이터는 맨 마지막에 나가는 형식입니다.
메모리 구조상에서 맨 밑부분에 위치하며, C언어에서 지역변수 및 매개변수들은 모두 스택에 공간을 할당받아서 저장되게 됩니다.
위 그림도 메모리라고 적었지만 사실 엄밀히 따지자면 스택에 저장된 버퍼 값들입니다. (ㅎㅎ;)
먼저 들어온 데이터는 스택 밑에서부터 저장되며, 그 뒤로 들어온 데이터들은 위로 차곡차곡 쌓입니다.
이제 알아내셨나요?
위로 올라가셔서 main.c 소스코드를 다시 보고 오세요!
네, str이 num보다 먼저 선언되어서 그렇습니다.
스택에서는 밑에서부터 쌓인다고 했는데요, char 형인 str이 먼저 선언되고, 그다음 줄에 바로 int 형인 num이 선언되어서 스택상에서는 str주소가 num보다 밑에 있는 것입니다.
그럼 여기서 궁금한 점이 생기는데요
과연 서로 얼마나 떨어져 있을까..?
...저만 생기나요?
뭐, 바로 확인해보면 되죠
아까 전에 구한 메모리 주소를 10진수로 바꿔서 차이를 비교해보도록 하겠습니다.
가장 최근에 실행했을 때 나온 메모리 주소 값입니다.
여기서 끝 두 자리를 빼고는 모두 동일한 값들이기에 끝 두 자리만 비교해보죠
둘의 차이가 6입니다. 정확히는 6 Byte라고 해야겠죠
Char 형은 문자 '1'개당 1 Byte를 할당받고,
Int 형은 범위 -2,147,483,648 ~ 2,147,483,647 사이의 값이면 4byte를 할당받습니다.
저는 str을 6의 크기의 배열로 선언하였기 때문에 str의 크기는 6 Byte를 할당받게 된 것이죠.
그래서 6 Byte의 크기를 뺀 주소가 Num의 주소가 되는 것입니다.
그려놓고 보니까 심각하네요..
해당 방식대로 Stack에 쌓인다고 생각하시면 될 거 같습니다.
그리고 주소 값은 해당 변수 전체를 가리키는 것이 아닌 맨 첫 번째 Byte의 주소 값을 가리킵니다.
이해가 되시나요??
아직도 배워야 할게 많네요