OZ1NG의 뽀나블(Pwnable)

[C++] new operator 정적 할당(된 곳에 변수 할당) 본문

Tips

[C++] new operator 정적 할당(된 곳에 변수 할당)

OZ1NG 2022. 7. 19. 18:07

[*] tcmalloc을 분석하다가 아래와 같은 코드를 봤는데 뭔지 잘모르겠어서 삽질해보게 되었다.

new (&pageheap_.memory) PageHeap;

먼저 두괄식으로 결과부터 말하자면 보통 C++에서 동적 할당할 때 사용하는 new operator는 정적할당도 가능하다는 것이다. (정확히는 정적할당된 공간에 클래스를 저장한다)

 

[*] 사용법

new (void * address) Class;

위와 같은 형태로 구성된다.

결과 : address에 Class가 할당된다.

정적 변수에 클래스를 할당 할 수 있다.

 

[*] 테스트 코드

// cpp new test
#include <iostream>

using namespace std;

class PageHeap{
public:
    int a = 1;
    int b = 2;

    PageHeap(){
        cout << "PageHeap!" << endl;
        a = 3;
    }
};

union PageHeapStorage {
    char memory[sizeof(PageHeap)];
    uintptr_t aaaa; 
};
static PageHeapStorage pageheap_;

int main(){

    // new로 먼저 공간을 할당 한 뒤, PageHeap 생성자가 실행됨 // pageheap_의 주소를 리턴
    new (&pageheap_.memory) PageHeap; 

    // cout << "a" << &pageheap_.memory.a << endl; // 자료형이 바뀐게 아니라 진짜 저장만 하는 용도이기 때문에 접근 불가

    return 0;
}
oz1ng@LAPTOP-6F0C4A2N:/mnt/c/Users/ghdxo/Desktop/oz1ng_Lab/tcmalloc/test$ ./new_test
PageHeap!

생성자가 실행되는 것을 확인 할 수 있다.

 

[*] Assembly 코드

pwndbg> disassemble
Dump of assembler code for function main:
...
   0x00000000004011c7 <+17>:    lea    rbx,[rip+0x2fd2]        # 0x4041a0 <pageheap_>
   0x00000000004011ce <+24>:    mov    rsi,rbx
   0x00000000004011d1 <+27>:    mov    edi,0x8
   0x00000000004011d6 <+32>:    call   0x40127b <operator new(unsigned long, void*)> // [A]
   0x00000000004011db <+37>:    mov    r12,rax
   0x00000000004011de <+40>:    mov    rdi,r12
   0x00000000004011e1 <+43>:    call   0x4012a4 <PageHeap::PageHeap()>               // [B]
   0x00000000004011e6 <+48>:    mov    eax,0x0
   0x00000000004011eb <+53>:    jmp    0x40120a <main+84>
...
End of assembler dump.

- [A] :

  1. operator new(unsigned long, void*) 함수를 사용하여 메모리 할당을 시도한다.

  2. 첫 번째 인자인 rdi에는 sizeof(PageHeap) 값이 들어가고, 두 번째 인자인 rsi에 ()안의 명시해준 address인 pageheap_의 주소 값이 들어간다.

- [B] : HeapPage 클래스의 생성자를 호출한다.

- [B]까지의 실행 결과

pwndbg> x/2gx 0x4041a0
0x4041a0 <pageheap_>:   0x0000000200000003      0x0000000000000000

[*] 결과   

멤버 변수 a와 b에 해당하는 부분이 각각 2와 3으로 바뀐것으로 pageheap_에 PageHeap 클래스가 정적 변수에 할당되었다는 것을 확인 할 수 있다.

 

[+] 추가 : new operator를 동적할당 했을때의 어셈코드 비교

// c
int main(){
    PageHeap * a = new PageHeap;
    return 0;
}

// asm
pwndbg> disassemble
Dump of assembler code for function main:
...
   0x0000000000401205 <+15>:    mov    edi,0x8
   0x000000000040120a <+20>:    call   0x4010c0 <operator new(unsigned long)@plt> // [A]
   0x000000000040120f <+25>:    mov    rbx,rax
   0x0000000000401212 <+28>:    mov    rdi,rbx
   0x0000000000401215 <+31>:    call   0x4012b4 <PageHeap::PageHeap()>
   0x000000000040121a <+36>:    mov    QWORD PTR [rbp-0x18],rbx
   0x000000000040121e <+40>:    mov    eax,0x0
   0x0000000000401223 <+45>:    jmp    0x401244 <main+78>
...
End of assembler dump.

- [A] 

  1. : operator new(unsigned long)@plt 함수를 호출한다.

        즉, new 함수는 오버로드 함수로 인자에 따라 호출되는 new 함수가 다르다는 것을 알 수 있다.

        (때문에 new 연산자를 사용했을 때, rsi에 값이 있다고 정적할당과 같이 쓸 수는 없다...)

 

[*] 종합

1. new operator는 동적할당 뿐만 아니라 정적할당 또한 가능하다.

2. 동적할당할 때와 정적할당 할 때의 new operator는 서로 다른 오버로드된 함수를 사용하여 구분한다.

3. 정적할당할 때의 new operator는 첫 번째 인자로 size 값, 두 번째 인자로 정적 할당할 주소를 사용한다.

4. 동적할당할 때의 new operator는 첫 번째 인자로 size 값만을 받는다.

Comments