소프트웨어 보안을 위한 메모리 구조
1. 프로세스와 메모리 레이아웃
•
프로세스: 실행 중인 프로그램.
프로그램 실행을 위해 메모리 내에 코드, 데이터, 스택, 힙, U-영역 등의 영역(segments)이 필요.
•
프로세스 이미지:
프로세스가 메모리에 적재된 형태(아래 구조 참고).
프로세스 메모리 구조
1.
코드 세그먼트(code):
•
실행 명령어(기계어) 저장
2.
데이터 세그먼트(data):
•
전역 변수, static 변수
•
초기화된(int a = 1; static int b = 2;) vs. 초기화되지 않은 데이터(long sum[1000];)
3.
스택(stack):
•
함수 호출 시 지역 변수, 매개 변수, 반환 주소, 프레임 포인터 등 저장
•
함수 호출·종료에 따라 동적으로 push/pop
4.
힙(heap):
•
동적 메모리 할당(malloc, new 등) 영역
•
크기가 런타임 중 변경 가능
2. 메모리 할당 방식에 따른 변수의 저장 위치
•
전역변수, static 변수:
◦
데이터 세그먼트에 저장
•
지역 변수, 매개 변수:
◦
스택에 저장
•
동적 할당 변수:
◦
힙에 저장(예: malloc, calloc, realloc)
3. 동적 메모리 할당
•
함수 및 용도
◦
void* malloc(size_t size); — size 바이트만큼 메모리 할당, 시작 주소 반환
◦
void free(void* ptr); — 할당된 메모리 해제
◦
void* calloc(size_t n, size_t size); — n개, size 바이트씩 0으로 초기화
◦
void* realloc(void* ptr, size_t newsize); — 기존 블록 크기 변경
예시
c
CopyEdit
char *ptr = (char*) malloc(40);
int *arr = (int*) malloc(10 * sizeof(int));
free(ptr);
C
복사
구조체 메모리 할당
c
CopyEdit
struct student { int id; char name[10]; };
struct student *ptr = (struct student*) malloc(sizeof(struct student));
C
복사
•
배열 형태: malloc(n * sizeof(struct student));
4. 연결리스트(Linked List)와 동적할당
•
구조체 내에 자기참조 포인터
c
CopyEdit
struct student {
int id;
char name[20];
struct student *next;
};
C
복사
•
연결리스트 구축
◦
입력받은 데이터마다 새로운 노드 동적할당, next 포인터로 연결
◦
삽입, 삭제, 역순 출력, 큐/스택 등 다양한 자료구조 구현 가능
5. 메모리 관리 함수
•
memset(void s, int c, size_t n):
◦
n 바이트를 c 값으로 초기화
•
*memcmp(const void s1, const void s2, size_t n):
◦
두 메모리 블록의 n바이트 비교, 같으면 0 반환
•
memchr(const void s, int c, size_t n):
◦
n바이트 중 c와 같은 첫 바이트 위치 반환
•
memmove, memcpy:
◦
src→dst로 n바이트 복사 (겹침시 memmove, 아니면 memcpy)
•
strncpy:
◦
문자열 복사, 지정된 n바이트까지만 복사
6. 버퍼 오버플로(Buffer Overflow, BOF) 취약점
•
정의:
◦
할당된 버퍼 크기를 초과하는 데이터를 기록하여, 인접 메모리 영역을 덮어씀
•
원인 함수:
◦
gets(buffer)와 같이 입력 크기 제한 없는 함수 사용
•
스택 오버플로우 시나리오:
◦
예) char overflowme[32]; gets(overflowme);
→ 32바이트 초과 데이터 입력 시, overflowme 다음에 위치한 변수/제어값(예: 리턴주소)까지 덮어쓸 수 있음
•
실전 영향:
◦
리턴주소 덮어쓰기 → 코드 실행 흐름 변경(익스플로잇)
◦
인접 변수, 스택프레임 변조
7. x86 스택 프레임 구조
•
주요 레지스터
◦
EIP(Instruction Pointer), EAX/EBX/ECX/EDX(General), ESP(Stack Pointer), EBP(Frame Pointer)
•
함수 호출 시 스택 변화
◦
main(), vuln() 등 함수 호출 시 각 함수의 로컬 변수, 리턴주소, 저장된 EBP 등이 스택에 쌓임
◦
Stack은 high address → low address로 성장
•
Stack Frame Layout
less
CopyEdit
[High Addr]
| ...
| Saved EBP ← EBP
| Return Address ← EIP
| Local Variables (e.g., buff[40])
| ...
[Low Addr] ← ESP
Plain Text
복사
•
취약점 영향
◦
Buffer overflow 발생 시, return address(EIP)를 공격자가 원하는 값(예: shellcode)으로 덮어쓰기 가능
8. 힙(Heap) 구조 및 취약점
•
Heap chunk:
◦
동적 할당 영역은 여러 "chunk"(메타데이터+실제 데이터)로 분리
•
Heap overflow:
◦
힙에 할당된 버퍼 크기 초과 데이터 기록 시,
인접 chunk의 메타데이터·포인터까지 덮어쓰는 현상
•
공격 벡터
1.
메모리 할당자 공격
•
free chunk의 메타데이터를 변조, 임의 주소에 쓰기
•
glibc allocator: fastbin/smallbin/largebin/unsortedbin 등 구조
•
현대 시스템은 대부분 메타데이터 무결성 검증 도입(공격 난이도 상승)
2.
애플리케이션 데이터 공격
•
프로그램의 실제 데이터(함수 포인터, vtable, 구조체 필드 등) 덮어쓰기
9. 메모리 취약점 예시 코드
c
CopyEdit
char buffer[32];
gets(buffer); // 취약: 입력 제한 없음
char *ptr = malloc(40);
strcpy(ptr, input); // 취약: input 길이 체크 안 함
struct node *p = malloc(sizeof(struct node));
free(p);
strcpy((char*)p, data); // Use-after-free 취약
C
복사
10. 보안 관점의 메모리 관리
•
취약점 예방
◦
안전한 함수 사용(fgets, strncpy 등), 길이 검사
◦
동적할당 후 NULL 검사
◦
사용 후 free, 이중 free 방지, dangling pointer 해소
◦
힙/스택 오버플로 방지: ASLR, Stack Canary, Safe unlinking(힙)
◦
static/동적분석 도구로 자동 검사
11. 참고: glibc malloc/heap chunk 내부 구조
•
할당 chunk:
◦
[size][user data][padding]
•
free chunk:
◦
[size][prev_size][fd][bk][user data/unused]
•
bin:
◦
크기별로 fastbin(<64), smallbin(≤512), largebin(>512), unsortedbin, topchunk 등 관리
Memory Layout #2
1. 프로세스와 메모리 구조
•
프로세스: 실행 중인 프로그램(프로그램+상태)
•
메모리 레이아웃:
◦
코드 세그먼트: 실행 명령어 저장
◦
데이터 세그먼트: 전역·static 변수 저장
▪
초기화 데이터 / 미초기화 데이터로 분리
◦
스택(Stack): 지역 변수, 매개변수, 리턴주소, 프레임 포인터 저장
◦
힙(Heap): 동적 메모리(malloc, new 등)
◦
U-영역: OS 및 시스템 내부 용도
2. 할당 방식별 변수 위치
•
전역·static 변수: 데이터 세그먼트
•
지역 변수·매개변수: 스택 프레임
•
동적할당 변수: 힙 영역
3. 동적 메모리 할당
•
함수:
◦
malloc(size_t size): size 바이트 할당
◦
calloc(n, size): 0으로 초기화하며 n개 할당
◦
realloc(ptr, newsize): 크기 변경
◦
free(ptr): 할당 메모리 해제
•
C 코드 예시:
c
CopyEdit
char *ptr = (char*)malloc(40);
int *arr = (int*)malloc(10 * sizeof(int));
free(ptr);
C
복사
•
구조체 메모리 할당:
c
CopyEdit
struct student { int id; char name[10]; };
struct student *ptr = (struct student*)malloc(sizeof(struct student));
C
복사
•
구조체 배열:
c
CopyEdit
struct student *ptr = (struct student*)malloc(n * sizeof(struct student));
C
복사
4. 연결리스트(Linked List)와 동적할당
•
자기 참조 구조체:
c
CopyEdit
struct student { int id; char name[20]; struct student *next; };
struct student *ptr = (struct student*)malloc(sizeof(struct student));
C
복사
•
입력값마다 동적할당 → next 포인터로 리스트 구축
5. 메모리 관리 함수
•
memset(s, c, n): s부터 n바이트를 c로 설정
•
memcmp(s1, s2, n): s1, s2의 n바이트 비교
•
memchr(s, c, n): s의 n바이트 내 c 위치 반환
•
memcpy(dst, src, n): src→dst로 n바이트 복사(겹침 금지)
•
memmove(dst, src, n): src→dst로 n바이트 복사(겹침 허용)
•
strncpy(dst, src, n): 문자열 최대 n바이트 복사
6. Buffer Overflow(BOF) 취약점 구조
•
정의:
◦
예약된 버퍼 크기를 초과한 데이터 입력 → 인접 메모리(변수, 제어값) 덮어씀
•
원인 함수:
◦
gets(buffer): 입력 길이 제한 없음
•
실제 메모리 영향:
◦
예: char overflowme[32]; gets(overflowme);
→ 32바이트 초과 데이터 입력 시, 인접 변수·리턴주소까지 손상
7. x86 아키텍처와 스택 프레임
•
주요 레지스터
◦
EIP: Instruction Pointer(명령어 주소)
◦
EAX/EBX/ECX/EDX: 범용
◦
EBP: Stack Frame Pointer
◦
ESP: Stack Top Pointer
•
함수 호출 흐름:
◦
인자 push → call 함수 → (이전 EBP, 리턴주소 저장) → 로컬변수 공간 할당
•
스택은 high address→low address 방향 성장
8. 스택 프레임 오버플로우 메커니즘
•
프레임 레이아웃
java
CopyEdit
| High Addr
| Saved EBP
| Return Address (EIP)
| Local Variables (e.g., buffer[40])
| Low Addr
Plain Text
복사
•
BOF 발생 시:
◦
buffer 초과 입력 → return address(EIP)까지 덮어쓰기 가능
◦
공격자는 임의 코드 실행 위치 지정 가능
9. Heap Buffer Overflow와 Chunk 구조
•
Heap 구조:
◦
여러 chunk(메타데이터+유저데이터)
◦
할당·해제(chunk의 free/used 상태) 및 크기/포인터 정보 포함
•
Heap BOF:
◦
할당된 buffer 초과 입력 → 인접 chunk의 메타데이터·어플리케이션 데이터 손상
•
공격 방법:
1.
메모리 할당자(allocator) 공격:
•
free chunk의 메타데이터 변조, 임의 주소 쓰기
•
modern glibc는 무결성 체크 강화
2.
어플리케이션 데이터 공격:
•
구조체 내부 함수포인터, vtable 등 실데이터 손상
10. gdb 실습 및 메모리 분석
•
gdb 명령:
◦
break main
◦
run
◦
disas main
◦
si/ni: step into/next instruction
◦
info reg: 레지스터 값 확인
◦
x/[포맷] [주소]: 메모리 덤프
◦
set 명령으로 메모리 값 수정
•
엔디안(Endianness):
◦
Little endian(하위 바이트 우선), Big endian(상위 바이트 우선)
11. x86 어셈블리어 기본
•
명령어
◦
mov dst, src: src 값을 dst에 저장
◦
lea dst, src: src의 주소를 dst에 저장
◦
push, pop: 스택 입출력
◦
cmp, test: 조건 검사, 플래그 설정
◦
jmp [addr]: 무조건 점프
◦
jcc [addr]: 조건부 분기
•
레지스터명, 표기방식(AT&T/Intel), CPU 종류(x86/x64/ARM 등)