[C언어] 메모리의 동적 할당
오늘은 UAF 취약점을 공부하기에 앞서 C언어에서의 동적할당에 대해서 공부해보겠습니다.
동적 할당이란 프로그램이 실행중(runtime)에 필요한 만큼의 메모리를 힙 영역에서 빌려 쓰는 것을 말합니다.
동적 할당은 실행 전 크기가 전해지지 않은 데이터를 다룰 때 사용합니다.
정적 할당에 비해 유연성도 뛰어나고, 메모리 효율 또한 좋기 때문에 최적화에도 좋습니다.
C언어에서는 malloc과 free함수를 통해 메모리를 할당하고, 해제합니다.
#include <stdlib.n>
void *malloc(size_t size); // 힙 영역에서 메모리 할당
void free(void *ptr); // 힙 영역에 할당된 메모리 해제
malloc함수는 성공 시 할당된 메모리의 주소값을 반환하고 실패시 NULL을 반환합니다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int* ptr1 = (int *)malloc(100);
*ptr1 = 123456;
printf("할당된 주소: %p\n", ptr1);
printf("%d \n", ptr1[0]);
free(ptr1);
printf("%d \n", ptr1[0]);
printf("해제 후 포인터: %p\n", (void*)ptr1);
return 0;
}
이렇게 코드를 적어주고 출력결과를 보겠습니다.

할당된 주소의 값과 해제 후 포인터를 보면 주소값이 그대로 남아있는 걸 확인할 수 있습니다.
또한 malloc 함수는 할당 실패 시 NULL을 반환하기 떄문에 예외처리 코드를 작성해주면 좋습니다.
int *ptr = (int*)malloc(sizeof(int));
if (ptr == NULL)
{
// 예외처리
}
free함수를 사용해서 메모리를 할당해제하지 않으면 메모리 누수가 나기 때문에 사용하고 나서 꼭 free로 할당 해제해주셔야 합니다.
free함수를 사용한 예제 코드만 한번 작성해보겠습니다.
사용자의 이름을 입력받아 출력하는 함수 char* ReadUsername이 있다고 해보겠습니다.
#include <stdio.h>
char* ReadUsername(void)
{
char name[30];
printf("이름을 입력하세요: ");
scanf_s("%s", name);
return name;
} //소멸되는 지역변수의 return은 오류를 발생함
int main(void) {
char* name1;
char* name2;
name1 = ReadUsername();
printf("name1: %s \n", name1);
name2 = ReadUsername();
printf("name2: %s \n", name2);
return 0;
}
위와 같은 코드는 소멸되는 변수의 return으로 스택 메모리를 잘못 반환한 오류입니다.
또 변수를 전역변수로 설정해버리면
#include <stdio.h>
char name [30];
char* ReadUsername(void)
{
printf("이름을 입력하세요: ");
scanf_s("%s", name);
return name;
}
int main(void) {
char* name1;
char* name2;
name1 = ReadUsername();
printf("name1: %s \n", name1);
name2 = ReadUsername();
printf("name2: %s \n", name2);
return 0;
}
두번째 name2 입력때 첫번째 입력했던 name1의 결과가 바껴서 꼬여버리는 상황이 발생하기 때문에 올바른 코딩이 아닙니다.
올바른 코딩 예제를 보겠습니다.
#include <stdio.h>
#include <stdlib.h>
char* ReadUsername(void) {
char* name = (char*)malloc(sizeof(char) * 30);
printf("이름을 입력하세요 : ");
scanf_s("%s", name, 30);
return name;
}
int main(void) {
char* name1;
char* name2;
name1 = ReadUsername();
printf("name1: %s \n", name1);
name2 = ReadUsername();
printf("name2: %s \n", name2);
free(name1);
free(name2);
return 0;
}
위와같은 코드로 작성하면 정적할당으로 구현하기 힘든 기능들을 효율적으로 구현할 수 있습니다.
이번 포스팅에서는 동적할당하는 malloc과 free에 대해 간단하게 알아보았습니다.
다음 포스팅에서는 이러한 동적할당에서 발생하는 보안 위험인 Use After Free에 대해 살펴보겠습니다.
읽어주셔서 감사합니다~