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 #includeusing 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