윤성우의 열혈 C++ 책을 보면서 공부하던 도중, 오류가 있는 것 같아서 직접 해보려고한다
이 필자는 아래 코드에서 정수를 반환하며 메모리 공간이 할당되면서 초기화가 된다고 했다.
int SimpleFunc(int n)
{
return n;
}
int main()
{
int num=10;
cout << SimpleFunc(num)<<endl;
}
그러나 아무리 보아도, 우리가 알던 지식으로는 64bit에서 rdi에 매개변수를 담아주고 바로 rax로 값을 가져오는 루틴이 수행될 것 같았다. 일단 직접 컴파일 해서 ida로 까보았다.
사실 너무나도 당연한 결과였다. 아무래도 책의 필자가 레지스터에 관한 개념까지는 소개하는 것이 힘들어서 이렇게 말한게 아닐까 생각한다. 그런데, 그 다음부분은 문제가 있어 보였다. 저자는 결론을 다음과 같이 내렸는데,
"함수가 값을 반환하면, 별도의 메모리 공간이 할당되고, 이 공간에 반환 값이 저장된다(반환값으로 초기화된다)."
여기에는 객체도 포함된다고 했는데, 객체의 초기화는 어떻게 될까? 별도의 메모리 공간이 할당되게 된다고 가정을 하면 스택프레임이 끝나는 시점에도 그 부분이 사용가능해야 한다는 말인데, 이는 말이 안된다.
예를들어서 함수를 연속적으로 호출하게 된다면? 위의 예로 따지자면 SimpleFunc
함수의 반환값을 바로 다른 함수의 매개변수로 사용한다고 가정하면 SimpleFunc
의 (이미사용이 끝난) 스택프레임을 다시 사용하게 될 것이고 이 반환값이 저장된 곳은 덮어질 수 밖에 없다. 그렇다면 동적할당 되는 것일까? 이 또한 말이 안된다. C++은 가비지컬렉터가 없고 동적할당을 하는 것 자체는 너무 비용이 크다. 이 결론은 컴파일을 해보면 쉽게 알 수 있다. 이번에는 클래스로 정의를 해보았다.
#include <iostream>
using namespace std;
class MyClass
{
public:
int my_num;
char buf[0x80];
MyClass(int num) : my_num(num)
{
cout<<"생성!"<<endl;
}
MyClass MyFunc()
{
MyClass temp=*this;
return temp;
}
};
int main()
{
MyClass *obj=new MyClass(0x80);
MyClass temp=obj->MyFunc();
printf("%d\n",temp.my_num);
}
buf를 준 이유는 int 만 줘버리게 되면, MyClass의 크기가 4byte가 되어버려서 그냥 eax에 넣어서 반환해버리기 때문이다. 구조체도 동일하다.
v5가 temp이다. 알아서 g++이 MyFunc의 첫번째 인자에 temp의 주소를 넣어주었다. 즉 컴파일 과정에서 함수의 인자 변경이 일어난 것이다.
그리고 C++이 ida에서 어떻게 분석될지 궁금했었는데, 상당히 알아볼만하게 변환되는 것 같다. 가상함수만 좀 다를것 같긴한데 계속 공부해 보아야겠다.
결론적으로는 이 책의 저자가 틀린설명을 했다고 보는게 맞지 않을까? 기본적인 int , char 같은 자료형들은 rax 레지스터에 반환될 수 있고 , 레지스터는 메모리라고 부르지는 않으니까.. 또한 8byte가 넘어가는 자료형의 경우에는 반환하는 함수의 인자를 컴파일러가 바꾸어 버려서 Call by Address 형식으로 받아버리기 때문이다.
'Security > 개인 연구' 카테고리의 다른 글
free 함수 분석(glibc 2.31) (0) | 2021.01.13 |
---|---|
malloc 함수 분석 (0) | 2021.01.09 |
tcache smallbin 취약점 (0) | 2020.12.28 |
malloc.c 분석 [2] _int_malloc (fastbin, smallbin) (2) | 2020.12.28 |
malloc.c 분석 [1] __libc_malloc (feat. Arena) (0) | 2020.12.28 |