[C]변수들이 스텍에 쌓이는 순서


변수 선언의 순서에 따라 스텍에 쌓이는 순서가 달라집니다.

strcpy()함수와 같이 메모리를 신경써야되는 함수들은 실수할 가능성이 큽니다. 조금이나마 피해를 줄이기 위해서는 실수할 가능성이 큰 변수의 주소를 뒤쪽에 위치시키는 것이 안전할 것입니다. 이런 것들을 자세히 알아보기 위해서는 어셈블리어를 보는 것이 좋겠지만 오늘은 간단히 코딩만으로 알아볼 계획입니다. 먼저 다음과 같은 조건에서 하였습니다.

  1. 64비트 운영체제 Vscode
  2. ASLR기능을 껐습니다.(ASLR기능이란 보안목적으로 매번호출할때마다 주소값을 바뀌게하는 기능입니다.)

#include <stdio.h>
#include <string.h>

void sss(void)
{
    char ss[4];
}

int main(void)
{   
    char word[4];
    char word1[4];
    char *address;    
    const char num[] = "123456789";

    strcpy(word, num);                        // 고의로 메모리초과
    sss();                                    // sss()함수 호출
    /* 출력함수 생략 */
    address = word;                           // word의 주소 address주소로 지정
}
변수명주소
address38373635(모름)
포인터임시주소0x0061FECC
word[4]0x0061FEC8
word1[4]0x0061FEC4
num[]0x0061FEBA
ss[4]0x0061FE8C

배열의 경우 선언순서에 따라 위쪽부터 차례로 스텍에 쌓였습니다. 그러나 포인터 같은 경우 스텍의 가장 위쪽에 임시로 4바이트씩 쌓였습니다(포인터의 크기:4바이트 기준). 단, 포인터를 선언만하고 사용하지않으면 스텍에 쌓이지 않았습니다.
프로그램실행시 가장먼저 main함수의 지역변수들이 스택 위쪽에 쌓였으며 외부함수의 변수는 아래쪽에 자리함을 알 수 있습니다.
여기서 포인터 주소값을 명시적으로 해주면 포인터변수 또한 선언순서에 따라 스텍에 쌓이게 할 수 있습니다.

1. "&"를 사용하여 포인터 주소를 사용하여 포인터의 주소를 명시적으로 바꿔보기

printf("address포인터의 주소는 %s입니다.\n", &address); // &address를 사용해서 포인터의 주소를 확인

위와 같은 코드를 추가해서 *address의 주소를 확인하는 것만으로 다음과같이 스텍에 쌓이게 됬습니다.
하지만 이러한 방법을 의도적으로 사용하기 보다는 포인터의 주소가 명확하지 않을때는 가장위에 임시스텍으로 쌓인다는 것 정도로만 아는 것이 좋을 것 같습니다.

변수명주소
word[4]0x0061FECC
word1[4]0x0061FEC8
*address0x0061FEC4
num[]0x0061FE8C
ss[4]0x0061FE8C

2. 포인터배열로 선언

char *address[2];       //char*형 주소를 2개 담을 수 있는 배열

이 처럼 포인터배열로 선언하여 사용하면 됩니다. 위의 포인터배열은 크기가 8바이트(포인터원소 2개)로 잡히게 됩니다.


고찰

  • 바뀔가능성이 큰 변수, 포인터변수, const 변수 순으로 선언하면 실수하더라도 스텍충돌을 줄일 수 도 있을 것 입니다.
  • 가장중요한 것은 정해진 메모리만 사용하는 것 입니다.
  • 안전한 상황이 아닐때는 strcpy, memcpy와 같은 함수보다 strlcpy, memmove와 같은 함수를 사용하는 것이 좋을 것 입니다.
  • 다른운영체제 다른콘솔프로그램에서는 다른방식으로 스텍에 쌓일 수 있습니다. (c가 크로스 플랫폼이라는 주장은 컴파일 전 단계까지! 어셈블리어로 만드는 과정은 컴파일러 마음..)
  • 하지만 요즘 컴파일러들의 ASLR기능이 잘 발달되어 있어서 크게 신경을 쓰지않아도 될 것 같습니다.




© 2021.02. by kirim

Powered by kkrim