Sistema de proteccion y friend (capitulo 32)¶
Resumen corto¶
Este capitulo explica como C++ permite romper el encapsulamiento de forma controlada usando friend.
- Sin
friend, una clase o funcion externa no puede tocar miembrosprivate. - Con
friend, habilitas una excepcion puntual. - Esa excepcion no se hereda ni se propaga automaticamente.
Reglas de amistad en C++¶
- No es transitiva: si
Aes amiga deByBdeC, no implica queAsea amiga deC. - No es heredable: si
Aes amiga deB, no automaticamente de derivadas deB. - No es simetrica: si
Aes amiga deB, no implica queBsea amiga deA.
Caso 1: funcion amiga externa¶
Patron:
Ver(A) no es metodo de A, pero puede leer/modificar A::a por la declaracion friend.
Lectura de reversing:
- Si sacas
friend, el codigo deja de compilar al acceder aXa.a. - No cambia calling convention: sigue siendo funcion externa normal.
Caso 2: metodo amigo de otra clase¶
Patron:
class A; // forward declaration
class B {
public:
bool EsMayor(A Xa);
};
class A {
private:
int a;
friend bool B::EsMayor(A Xa);
};
Puntos clave:
- Hace falta
forward declarationdeApara declararEsMayor(A). B::EsMayorpuede leerA::apor ser amiga.
Nota de reversing importante:
- Si dos metodos/constructores quedan identicos en codigo maquina, el compilador puede plegarlos (COMDAT/ICF segun build).
- Resultado: en ASM parece que "falta" una funcion, pero en realidad se reutiliza la misma implementacion.
Bajo nivel (caso A y B)¶
1) main crea ambos objetos en stack¶
Se ve Na y Nb como variables locales en el frame:
Na = A ptr -11ChNb = B ptr -0FCh
Construccion:
mov edx, 0Ah
lea rcx, [rbp+...+Na] ; this
call A::A(int)
mov edx, 0Ch
lea rcx, [rbp+...+Nb] ; this
call B::B(int)
RCX lleva this y EDX el entero del constructor.
2) Llamada a B::EsMayor(A) y paso de parametro¶
La llamada relevante es:
Punto importante: A en este ejemplo tiene solo un int, entonces MSVC lo pasa de forma efectiva como ese valor de 32 bits (no como objeto complejo en memoria).
3) Cuerpo de B::EsMayor(A) en asm¶
Dentro de la funcion:
mov rax, [rbp+...+this] ; B* this
mov ecx, [rbp+...+Xa.a] ; valor de A::a
cmp [rax], ecx ; compara B::b vs Xa.a
jle ...
[rax] es B::b (offset 0), porque B tambien contiene solo un int.
4) Donde aparece realmente friend¶
friend no genera una instruccion especial en asm.
Su efecto es de compilacion: permite que exista codigo fuente como Xa.a dentro de B::EsMayor.
Si quitas friend, ese acceso no compila.
Si lo dejas, compila y en asm solo ves una lectura normal del campo.
5) A::Ver(void) y B::Ver(void) en este build¶
En tu dump, ambas funciones tienen la misma estructura:
Interpretacion:
- Tanto
AcomoBtienen suinten offset+0. - Por eso ambos
Ver()se ven casi iguales en asm, salvo simbolos y etiquetas.
Caso 3: clase amiga completa (friend class)¶
Ejemplo del capitulo: lista enlazada con Elemento y Lista.
Lista puede tocar Elemento::tipo y Elemento::sig aunque sean private.
Mapeo de estructuras en ASM (MSVC x64)¶
Con el codigo del capitulo:
Elementoocupa 16 bytes:+0x0->int tipo+0x8->Elemento* sig(hay padding entre medio)Listaocupa 8 bytes:+0x0->Elemento* Cabeza
Funciones observadas en el ASM:
Elemento::Tipo()-> lee[this+0]Lista::Primero()-> devuelve[this+0](Cabeza)Lista::Siguiente(p)-> sip != 0, devuelve[p+8](sig)Elemento::Elemento(int t)-> guardatentipoysig = 0
Patron de insercion (Lista::Nuevo)¶
Secuencia en ASM:
operator new(16)para unElemento.- Llama
Elemento::Elemento(tipo). p->sig = this->Cabeza.this->Cabeza = p.
Este patron inserta siempre al principio (LIFO).
Si insertas 4, luego 2, luego 1, al recorrer imprimes 1,2,4.
Patron de liberacion (Lista::LiberarLista)¶
Loop observado:
p = CabezaCabeza = p->sigdelete p- repetir hasta
Cabeza == NULL
Este metodo lo llama el destructor Lista::~Lista().
Checklist de reversing para friend¶
- Busca accesos a campos privados desde clases/funciones externas.
- Si compila, revisa si hay
frienden headers/clase. - Mapea offsets de campos (
[this+0],[this+8]) antes de renombrar. - En listas enlazadas, identifica rapido el patron:
node->next = headhead = node- loop de
delete.