C++ Binary Internals

The idea for this post started out as an exploration of compiler optimizations in GCC. It turned into a static and dynamic C++ class exploration exercise. The piece of code we’ll look at:

// file: bin1.hpp
#include 
using namespace std;

class FirstClass
{
    public:
        FirstClass(int new_id) { id = new_id; };
        int get_id() const { return id; };
    private:
        int id;
};

// file: bin1.cpp
#include "bin1.hpp"

int main()
{
    FirstClass a(0x539);
    printf("%d\n", a.get_id());
    return 0;
}

It is best practice to declare constructors and member functions in the header file, then have the implementation in the cpp file. This class is extremely simple though so I’ve opted for a more concise class definition. bin1 initializes a new object of type FirstClass, with id of 0x539. Compile with g++ -o bin1 bin1.cpp then look at the resulting binary’s .text section with objdump -M intel –demangle=gnu-v3 –section=.text -S bin1. Nice! I’ll be focusing on the sections directly related to the FirstClass class.

.text section symbols

_start
Entry point of binary.

deregister_tm_clones
GCC transactional memory table tear down.

register_tm_clones
GCC transactional memory table setup.

__do_global_dtors_aux
Run all global destructors on exit when .fini_array is unavailable.

frame_dummy
?

main
Our defined int main() function.

__static_initialization_and_destruction_0
Calls constructors of classes with static storage duration, 
and registers destructors of classes with static storage duration. 
This shouldn't be relevant to FirstClass class, but is relevant for iostream.

_GLOBAL__sub_I_main
Wrapper function to the above function.

FirstClass::FirstClass(int)
Our FirstClass constructor

FirstClass::get_id() const
Our FirstClass get_id() member function

__libc_csu_init
Calls function that need to run before main().

__libc_csu_fini
Calls functions that need to run after main().

Now the disassembly of the FirstClass constructor:

FirstClass::FirstClass(int):

push   rbp                     ; function prologue
mov    rbp,rsp
mov    QWORD PTR [rbp-0x8],rdi ; rdi is a pointer to the object, move to stack
mov    DWORD PTR [rbp-0xc],esi ; esi is the id we provided, 0x529, move to stack
mov    rax,QWORD PTR [rbp-0x8] ; move object pointer to rax
mov    edx,DWORD PTR [rbp-0xc] ; move object id to edx
mov    DWORD PTR [rax],edx     ; move object id to 0th position in object
nop
pop    rbp
ret    
nop

Immediately after we return from this call, the top of the stack will be our object. (Object starts at 0x7fffffffded4)

0000| 0x7fffffffded0 --> 0x539ffffdfc0

The 0x539 is the ID we care about, the following bytes are garbage left behind in memory from some previous operation. Now the disassembly of the FirstClass get_id() member function:


FirstClass::get_id() const:

push   rbp                     ; function prologue
mov    rbp,rsp 
mov    QWORD PTR [rbp-0x8],rdi ; rdi points to object on stack
mov    rax,QWORD PTR [rbp-0x8] ; move object addr into rax
mov    eax,DWORD PTR [rax]     ; dereference object pointer, get 32 bits of data (the id)
pop    rbp
ret    

This has been a simple exploration of C++ classes during runtime.

-badbytes

Leave a Reply