안 쓰던 블로그

[리버싱] 메모리 구조, 함수 호출 과정, Stack Frame 본문

CTF/Reversing

[리버싱] 메모리 구조, 함수 호출 과정, Stack Frame

proqk 2020. 8. 30. 21:01
반응형

메모리 구조

프로그램이 실행되면 로더에 의해서 디스크에서 주기억장치로 프로그램이 적재된다

프로그램은 메모리 상에서 4가지 세그먼트로 나뉘어 스택 형태로 구현된다

 

일반적으로 컴파일러들은 이런 식으로 실제 코드 부분/데이터 부분을 개념적으로 구분해서 사용한다

물론 이게 물리적으로 딱 정해진 것이 아니라, 사용자가 어디서부터 어디는 코드 영역, 어디는 데이터 영역이라고 임의로 구분해서 사용하는 것일 뿐이다

CPU나 메모리 같은 헤드웨어 입장에서는 값이 데이터인지 코드인지 구분하지 않는다

 

1. 코드 세그먼트

프로그램 소스코드 저장

 

2. 데이터 세그먼트

전역변수 global, 정적변수 static, 배열 array, 구조체 structure 등이 저장된다

초기화 된 데이터는 이곳에 저장된다

 

2-1. BSS

데이터 세그먼트 중 초기화 되지 않은 데이터는 이곳에 저장된다

 

3.  HEAP 세그먼트

가변 크기로 프로그래머 필요에 따라 할당하여 사용한다

위에서부터 채워진다

 

4. STACK 세그먼트

가변 크기로 지역 변수가 저장되는 곳이다

여러 개의 스택 프레임이 존재한다

밑에서부터 채워진다

 

 

정리하면

1. 전역 변수는 데이터 세그먼트에 맵핑되어 실행 이미지 안에 그 정보가 포함된다

 

2. 함수 안에서 사용되는 지역 변수들은 스택 메모리 공간에 동적 할당된다

 

3. 전역 변수는 프로그램이 종료되지 않는 이상 항상 같은 메모리 공간에 맵핑되므로 직접 그 주소값으르 사용해 엑세스가 가능하다

3-1. 그래서 이 글(https://foxtrotin.tistory.com/264)에서 보면 전역변수는 메모리 주소로 접근하고 지역변수는 ebp레지스터로 접근하는데, 지역 변수는 동적 할당이라 스택 포인터를 의미하는 ebp레지스터를 매개로 지정되었기 때문이다

 

4. HEAP과 STACK이 서로 반대로 채워지기 때문에 서로의 영역을 침범하기도 한다. 이를 악용한 공격(HEAP, STACK overflow)도 있다

 


스택 프레임

메모리 구조 중 STACK 세그먼트에서 존재하는 스택 프레임은 중요하다

스택 프레임은 어떤 함수가 호출되었을 때 그 함수 호출을 위해 필요한 값들을 저장하는 메모리 블록

스택 프레임은 인자, 리턴값 등을 가진다

함수가 끝나고 복귀 주소로 돌아갈 때 스택 프레임은 소멸한다

 

스택 프레임이 왜 등장했나?

어셈블리 프로그래밍을 할 때 서브루틴을 구현하려면 보통 레지스터를 쓴다

메모리를 복사하는 루틴을 만들고, 복사할 시작 주소, 타겟 주소, 복사할 양으르 서브루틴에 전달한 뒤에, 루틴이 끝나면 다시 호출된 지점으로 돌아와 작업을 계속하는 식이다

서브루틴이 실행되는 동안 RX 레지스터(값 하나만 저장 가능)에 복귀 주소를 저장하고, 그 값으로 돌아오게 된다

(메모리 구조 참고: https://foxtrotin.tistory.com/167서브루틴의 개념 참고: foxtrotin.tistory.com/170)

 

그런데 문제는, 함수를 한 번만 호출하지는 않는다는 점이다

함수마다 레지스터를 쓰는데 함수가 중첩되어 실행되기 시작하면 이 함수가 어떤 레지스터를 건들면 안 되는지도 알 수가 없어진다

 

그래서 이걸 해결하기 위해, 복귀 주소를 특정 레지스터나 특정 메모리 주소에 저장하지 않고, 스택 공간에 차례대로 쌓는 개념이 등장했다

그리고 이렇게 복귀 주소를 쌓아둔 스택 메모리 중 가장 윗 부분 주소만 특정 레지스터에 저장하기로 약속했다

이 특정 레지스터가 바로 SP=스택 포인터고, SP는 스택 프레임을 가리킨다

 

복귀 주소를 그렇게 저장하게 되면서 한 번 함수를 호출할 때마다 필요한 메모리 블록도 여러 개가 필요하게 되었다

그래서 나온 게 스택 프레임이고, 함수 호출 시 할당되는 메모리 블록을 의미한다

 


함수 호출 과정

1. 함수가 사용할 인자값들을 스택에 넣고 함수의 시작 지점으로 점프해서 함수를 시작한다. 해당 함수의 스택 프레임이 할당된다

2. 함수 내에서 사용할 스택 프레임 값들을 할당한다

3. 함수 내용을 수행한다

4. 함수가 끝나면 함수가 호출된 지점으로 복귀하기 위해 스택을 pop한다

5. 호출한 지점의 다음 라인으로 점프하여 프로그램이 계속 실행된다

 

 

함수가 끝나고 복귀할 때 스택을 정리하는 방법에 여러 가지가 있는데, 함수 호출 규약이라고 한다

함수 호출 규약 참고: https://phaphaya.tistory.com/24

반응형

'CTF > Reversing' 카테고리의 다른 글

Register  (0) 2020.09.01
리버싱-전역/지역 변수 초기화  (0) 2020.09.01
abex crackme 3번-keyfile채우기  (0) 2020.08.26
리버싱-분기문 우회  (0) 2020.08.26
리버싱-실행 프로그램 변경  (0) 2020.08.26
Comments