IDA Pro C++ VTable

Hi Guys,

I would like to share about reversing C++ code in IDA especially related to the VTable which is used for pointer to a function during the polymorphism code in the class inheritance

VTables

Vtable is a table used by the application to point the function call of an object (C++ class). VTables appear when the compiler does not know which function to be called during compile time. It is because of the appearance of virtual function (Object Inheritance)

Let’s analyze the image below, Class C inherits the Class B. So that Class B will point to the function-specific to its member B::bar() and B::qux. While Class C that inherits from class B then it will mean to function member that overrides the function in class B that is C::bar() while class C will still point to B:qux() because class C does not override it

Understandig Virtual Tables in C++ | Pablo Arias

VTable in Assembly

How the above scheme is laid out into assembly language. It will follow this diagram

Dangling Pointers Exploitation | programmingexploitation

When an C++ object is created, a memory pointer will be created and assinged to it to hold the pointer to the object. You can see the operator new will return the object pointer into eax

As you may see that the first dword pointer of the following address from the class points to the first VFTable in the PE data section, where this VFTable is pointing to the address of a function in the assembly area. Like below

When we see the offset const Poligon::`vftable data section, it points to function Poligon::area(void) and the next 4 bytes will point to Poligon::keliling(void).

When the application want to call the function member of the class, it will access them like an array which is using bytes iterator such as eax+4 will call the second function which is Poligon::keliling(void)

Function Call

we can see that the sequence as below

mov eax, [ebp+Polygon_Object_Ref_2] : this will copy the the value of ebp+Polygon_Object_Ref_2 to eax.

mov edx, [eax] : it will copy the value of eax point to which is the VFTable. So EDX will represent the VFTable address

mov eax, [edx] : it will also again copy the value that the edx point to that is the actual code for Poligon::area()

call eax : It will bring you jump the address of Poligon::area() on 0041141A

Because the call function is not defined during compile time then the way it call the function will be different then normal. It will call the function using call eax

If we see the eax register will point to the virtual function Poligon::area()

Automate the Process

In order to automate the VTable mapping, we can use python code script. I do not create my self the code but i took it from

https://gist.githubusercontent.com/ALSchwalm/2c8a16576d713bacdbc3f9df36c0e843/raw/28cff7914f8cd97c12d0de976c9f082dc7687c1b/reversing-part-2-3.py

""" A simple script to locate vtable groups in binaries with the Itanium ABI.

Note that this script does not account for virtual inheritance or (more notably),
cases were the vtable contains null pointers. This may happen in more recent
compilers with purely abstract types.
"""

import idaapi
import idautils

def read_ea(ea):
    return (ea+4, idaapi.get_32bit(ea))

def read_signed_32bit(ea):
    return (ea+4, idaapi.as_signed(idaapi.get_32bit(ea), 32))

def get_table(ea):
    ''' Given an address, returns (offset_to_top, end_ea)
    for the table  located at that address or None if there
    is no table'''

    ea, offset_to_top = read_signed_32bit(ea)
    ea, rtti_ptr = read_ea(ea)
    if rtti_ptr != 0:
        return None
    func_count = 0
    while True:
        next_ea, func_ptr = read_ea(ea)
        if not func_ptr in idautils.Functions():
            break
        func_count += 1
        ea = next_ea
    if func_count == 0:
        return None
    return offset_to_top, ea

def get_table_group_bounds(ea):
    ''' Given an address, returns the (start_ea, end_ea) pair
    for the table group located at that address'''
    start_ea = ea
    prev_offset_to_top = None
    while True:
        table = get_table(ea)
        if table is None:
            break
        offset_to_top, end_ea = table
        if prev_offset_to_top is None:
            if offset_to_top != 0:
                break
            prev_offset_to_top = offset_to_top
        elif offset_to_top >= prev_offset_to_top:
            break
        ea = end_ea
    return start_ea, ea

def find_tablegroups(segname=".rodata"):
    ''' Returns a list of (start, end) ea pairs for the
    vtable groups in 'segname'
    '''
    seg = idaapi.get_segm_by_name(segname)
    ea = seg.startEA
    groups = []
    while ea < seg.endEA:
        bounds = get_table_group_bounds(ea)
        if bounds[0] == bounds[1]:
            ea += 4
            continue
        groups.append(bounds)
        ea = bounds[1]
    return groups

After running the above in the IDA python interpreter, you can execute find_tablegroups() to get a list of vtable group addresses.

Leave a Reply