Saltar a contenido

Modificadores para miembros de clase (C++ Reversing)

Objetivo

Entender tres modificadores que aparecen mucho en C++ y que afectan lo que vas a ver en reversing:

  • inline
  • const (en metodos y retornos)
  • static (miembros de clase)

1) inline en metodos de clase

Idea

inline sugiere al compilador reemplazar una llamada por el cuerpo de la funcion.

class Ejemplo {
public:
    inline int Lee() const { return a; }
private:
    int a;
};

Regla importante

En compiladores modernos, inline es una sugerencia, no una orden.

  • Con optimizaciones desactivadas, suele quedar call.
  • Con optimizaciones agresivas, el compilador puede inlinear aunque no pongas inline.
  • Con optimizaciones agresivas, incluso puede eliminar clases/metodos y dejar solo constantes si el resultado final es equivalente.

Lectura de reversing

  • No uses la presencia/ausencia de call como prueba de que habia inline en fuente.
  • Primero identifica build flags (/Od, /O2, LTO, etc.).

2) Metodos const

Idea

Un metodo marcado como const promete no modificar el estado observable del objeto.

class Ejemplo {
public:
    int Lee() const { return a; }  // solo lectura
private:
    int a;
};

Si intentas modificar un miembro dentro de Lee() const, falla compilacion:

int Lee() const {
    a++;       // error
    return a;
}

Tambien restringe llamadas

Desde un metodo const no puedes llamar metodos no-const del mismo objeto (salvo casts explicitos).

Lectura de reversing

const de metodo no deja una "instruccion especial" en ASM. Es una restriccion de tipo en compilacion.


3) const en el valor de retorno

Caso tipico con punteros

Si devuelves un puntero a memoria interna privada, puedes romper encapsulamiento si no cuidas el tipo.

Caso riesgoso:

char* Leer() { return cad; }

Desde fuera:

char* p = obj.Leer();
p[1] = '2';   // modifica memoria interna de obj

Caso mas seguro:

const char* Leer() const { return cad; }

Desde fuera, compila lectura pero no escritura:

const char* p = obj.Leer();
// p[1] = '2'; // error

Nota

const char* protege contra escritura accidental por esa ruta. No reemplaza una politica completa de ownership/copias.


4) Miembros static de clase

Idea

Un miembro static pertenece a la clase, no a cada objeto. Hay una sola copia compartida por todas las instancias.

class Numero {
public:
    Numero(int v) : valor(v) { ++cuenta; suma += v; calcularMedia(); }
    ~Numero() { --cuenta; suma -= valor; calcularMedia(); }

    static int LeerCuenta() { return cuenta; }
    static int LeerMedia() { return media; }

private:
    int valor;
    static int cuenta;
    static int suma;
    static int media;
    static void calcularMedia() {
        media = (cuenta > 0) ? (suma / cuenta) : 0;
    }
};

Propiedades clave

  • Se definen/almacenan fuera del objeto (seccion global, tipicamente .data/.bss).
  • Son compartidas entre todos los objetos.
  • Si son private, no puedes leerlas directo desde main; necesitas metodos de acceso.

Lectura de reversing

Patrones comunes:

  • Constructor: incrementa contador global de clase y actualiza acumuladores.
  • Destructor: decrementa contador y ajusta acumuladores.
  • Los objetos siguen teniendo sus campos no static dentro del this.
  • Los static aparecen como simbolos globales referenciados por varias funciones.

5) Lo que cambia en ASM y lo que no

  • inline: puede cambiar mucho la forma (con o sin call), segun optimizacion.
  • const (metodo/retorno): casi siempre no cambia instrucciones, cambia reglas de compilacion.
  • static: si cambia layout y accesos, porque deja de vivir dentro del objeto y pasa a almacenamiento de clase/global.

6) Checklist para reversing rapido

  • Verifica modo de compilacion antes de inferir semantica (/Od vs optimizado).
  • Si un campo parece "global compartido", evalua si era static de clase.
  • Si una API devuelve puntero a buffer interno, evalua riesgo de romper encapsulamiento.
  • Distingue restricciones de compilacion (const) de efectos reales en runtime.

7) Errores comunes

  • Suponer que inline siempre elimina llamadas.
  • Pensar que const "protege memoria" en runtime por si solo.
  • Tratar miembros static como si fuesen campos por-instancia.
  • Exponer punteros mutables a estado privado.