sábado, 25 de julio de 2026
domingo, 21 de junio de 2026
[C]Bruteforce Recursivo vs Iterativo
/*Me encontré este post en elhacker y lo comparto con uds... no es de mi autoría Hay casos en que es más conveniente usar la recursividad para solucionar un problema. Hay que cambiar system("PAUSE") y los scanf"*/
//////////////////////////////////////
/*BruteForce Iterativo en Lenguaje C*/
//////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
int contadores[50]={0}, len=0, cant=0, i=0;
char caracteres[200]={0}, pass[50]={0};
printf ("Ingrese los caracteres a usar para la contrasena: \n");
scanf ("%s", caracteres);
printf ("Ingrese la longitud de la contraseña: \n");
scanf ("%i", &len);
cant = strlen (caracteres);
/*contadores = (int *) malloc (sizeof (int) * (len+1) );
pass = (char *) malloc (len+1);*/
for (i=0 ; i<len;i++){
pass[i] = caracteres[0];contadores[i] = 0;}
contadores[i] = 0;pass [i] = '\0';
while (!contadores[len]){
printf ("%s\n", pass);
for (i=0;contadores[i]==cant-1;i++)
pass[i] = caracteres [contadores[i] = 0];
pass[i] = caracteres [++contadores[i]];}
/*free (contadores);free (pass);*/
system("PAUSE");return 0;} //end of main
///////////////////////////////////////*BruteForce Recursivo en Lenguaje C*/////////////////////////////////////// #include <stdio.h> #include <string.h> void brute_force (char caracteres[], int cant, int pos, char *password){ if(pos==-1){printf ("%s\n", password);return;} for(int i=0;i
int main(int argc, char *argv[]){ char caracteres[256]={0}, *password=NULL;int longitud=0, cantidad=0; double tiempo=0;time_t t1, t2; printf ("Ingrese los caracteres a usar: "); scanf ("%s", caracteres); cantidad = strlen(caracteres); printf ("Ingrese la longitud maxima de la contraseña: "); scanf ("%i", &longitud); password = (char*) malloc (longitud*sizeof(char)); time (&t1);password[longitud]= '\0'; brute_force (caracteres, cantidad, longitud-1, password); time (&t2);tiempo = difftime (t2, t1); printf ("\tTiempo: %7.2f\n\n", tiempo); free (password); //importante:liberar memoria system("PAUSE");return 0;}
martes, 11 de marzo de 2025
Intro al Reversing
Registros Un registro es, de forma simplificada, un pequeño almacén en la CPU y es, evidentemente, la forma más rápida que tiene la CPU de acceder a datos.
En la arquitectura x86 de Intel existen ocho registros de propósito general: EAX, EDX, ECX, ESI, EDI, EBP, ESP y EBX (cambiar E por R en arquitecturas de 64 bits).Son utilizados para facilitar la tarea en el procesado de las instrucciones, cómo para almacenar datos que se utilizaran posteriormente por las mismas. El registro EAX (RAX) El registro EDX (RDX) El registro ECX (RCX) Los registros ESI (XSI) y EDI (RDI) Los registros ESP (RSP) y EBP (RBP) El registro EBX (RBX) Registro especial EIP (RIP) Estos son alguno de los registros básicos que existen: -EAX (Accumulator register): Utilizado tanto para realizar cálculos, cómo para el almacenamiento de valores de retorno en "calls". -EDX (Data register): Extensión de EAX, utilizada para el almacenamiento de datos en cálculos más complejos. -ECX (Count register): Utilizado en funciones que necesiten de contadores, como por ejemplo bucles. -EBX (Base register): Se suele utilizar para apuntar a datos situados en la memoria. -ESI (Source index): Utilizado para la lectura de datos. -EDI (Destination index): Utilizado para la escritura de datos. -ESP (Stack pointer): Apunta a la cima de la pila “stack”. -EBP (Base pointer: Apunta a la base de la pila “stack”. Instrucciones -Son acciones predefinidas en el lenguaje ensamblador. Algunas de las más habituales de ver son: -PUSH: Guarda el valor en la pila. -POP: Recupera valor de la pila. -MOV (dst, src): Copia el operador “src” en el operador “dst”. -LEA (reg, src): Copia una dirección de memoria en el registro destino (ej: EAX). -ADD (o1, o2): Suma los dos operadores y los almacena en el operador uno. -SUB (o1, o2): Resta el valor del segundo operador sobre el primero y lo almacena en el primer operador. -INC: Incrementa en 1 el valor del operador indicado. -DEC: Decrementa en 1 el valor del operador indicado. -AND: El resultado es 1 si los dos operadores son iguales, y 0 en cualquier otro caso. -OR: El resultado es 1 si uno o los dos operadores es 1, y 0 en cualquier otro caso. -CMP: Compara dos operadores. -JMP Salta a la dirección indicada. -CALL: Llama/Salta a la dirección/función indicada. -NOP: Not Operation.
Estructura de la pila (Stack) La pila es una estructura FILO (First In, Last Out) donde los argumentos son apilados en la cima de la misma cuando se invoca una función y retirados cuando la función finaliza. El registro ESP se usa para seguir la pista a la parte más alta del marco de la pila y el registro EBP para seguirle la pista a la parte baja (aunque ya vimos que ciertos compiladores pueden decidir no usar ebp para eso).
Puntos de Interrupción (Breakpoints) La habilidad de parar un proceso que está siendo depurado se consigue mediante el fijado de puntos de interrupción o breakpoints. Al parar el proceso podemos inspeccionar el valor de las variables, los argumentos de la pila, y las direcciones de memoria sin que el proceso modifique estos valores hasta que no se lo indicas de esa forma al depurador. Los puntos de interrupción son la herramienta principal proporcionada por los depuradores. Los puntos de interrupción le permiten interrumpir la ejecución del programa en un lugar específico. Hay dos tipos de puntos de interrupción: Puntos de interrupción de software Puntos de interrupción de hardware Es muy difícil realizar ingeniería inversa de software sin puntos de interrupción. Las tácticas populares de ingeniería anti-reversa se basan en la detección de puntos de interrupción, proporcionando una serie de métodos anti-depuración correspondientes.
Cómo omitir una verificación de punto de interrupción de software No existe un enfoque universal para evitar una verificación de punto de interrupción de software. Para evitar esta protección, debes encontrar el código que calcula la suma de verificación y sustituir el valor devuelto con una constante, así como los valores de todas las variables que almacenan sumas de verificación de funciones.
Puntos de interrupción de hardware DR0-DR3 – breakpoint registers -“Debug Address Registers” or “Address-Breakpoint Registers” DR4 - DR5 – reserved - “Reserved Debug Registers” DR6 – debug status - "Debug Status Register” DR7 – debug control -“Debug Control Register”
SEH (Structured Exception Handling) El manejo estructurado de excepciones es un mecanismo proporcionado por el sistema operativo a una aplicación que le permite recibir notificaciones sobre situaciones excepcionales como la división por cero, la referencia a un puntero inexistente o la ejecución de una instrucción restringida. Este mecanismo le permite manejar excepciones dentro de una aplicación, sin la participación del sistema operativo. Si no se maneja una excepción, dará como resultado la finalización anormal del programa. Los desarrolladores suelen ubicar punteros a SEH en la pila, que se denominan marcos SEH. La dirección de trama SEH actual se encuentra en el desplazamiento 0 en relación con el selector FS (o el selector GS para los sistemas x64).
VEH (Vectored Exception Handler) VEH se introdujo en Windows XP y es una variación de SEH. VEH y SEH no dependen el uno del otro y funcionan simultáneamente. Cuando se agrega un nuevo "manejador" VEH, la cadena SEH no se ve afectada ya que la lista de manejadores VEH se almacena en la variable no exportada ntdll! LdrpVectorHandlerList. Los mecanismos VEH y SEH son bastante similares, la única diferencia es que las funciones documentadas se utilizan para configurar y eliminar un controlador VEH.
Exploiting sobre Linux-x86 La porción de RAM utilizada por un programa cuando se llama a una función es comunmente llamada pila o stack, debemos tener en cuenta que la pila crece de arriba a abajo, como podemos ver hay una parte de la ram que almacena la copia del registro IP(EIP) en el momento de realizar la llamada a la función, en el programa no se tiene en cuenta que el dato introducido sea de una longitud concreta, se realiza la copia a ciegas a la variable nombre, aprovechando este descuido podemos hacer crecer la variable nombre hasta llegar a ocupar la dirección de retorno EIP haciendo así que el software termine retornando a otra posición de memoria y no la que se guardó al realizar la llamada a la función func.
¿Qué es una Shellcode? Una shellcode no es mas que el conjunto de opcodes (instrucciones en hexadecimal) que ejecutará el procesador para realizar un acción en concreto, las shellcodes suelen estar escritas en ensamblador ya que nos permite un control total sobre el proceso de ejecución además de un tamaño inferior de la shellcode.
Definición Buffer Un espacio de memoria de cierto tamaño que se reserva para guardar datos, y manejar los mismos. Un ejemplo básico es una lata de 20 litros que tengo vacía para guardar allí un contenido, el mismo podrá ser menor o igual a 20 litros, el cual es el tamaño máximo que puedo guardar en este buffer de 20 litros, si quisiera guardar más en un solo depósito debería buscar la forma de tener un buffer más grande, sino, al tratar de guardar por ejemplo 40 litros en una lata de 20 litros se desbordaría. Un buffer overflow ocurre cuando un programa informático excede el uso de cantidad de memoria reservado para ello , escribiendo en el bloque de memoria contiguo. En verdad, un buffer overflow se produce en una aplicación informática cuando no cuenta con los chequeos de seguridad necesarios en su código de programación, como por ejemplo medir la cantidad de datos que se copiara a un buffer y que no exceda el tamaño del mismo. Los tipos más comunes de buffer overflows son los stack buffer overflows y los heap buffer overflows. Bueno aqui vemos la definición de buffer overflow, y en nuestro ejemplo anterior si trato de guardar 40 litros en una lata de 20 litros se desbordara como vimos, ese desborde que se produce es el buffer overflow, o sea el desbordamiento de mi depósito al sobrepasar la máxima capacidad del mismo.
Stack y heap overflow Ahora una idea acerca de la diferencia entre stack y heap: STACK : El stack se utiliza para guardar las variables locales de una función que sólo necesitan durar tanto como la ejecución de la función. En la mayoría de los lenguajes de programación es fundamental que sepamos en tiempo de compilación qué tan grande es una variable si queremos almacenarla en el stack. HEAP: El heap se utiliza para reservar memoria dinámica, cuya vida útil no se sabe muy bien por adelantado, pero se espera que duren un tiempo. Si no sabemos su tamaño o el mismo se decide en tiempo de ejecución se deberá calcular y reservar en el heap.
Stack Buffer Overflow
Anti-sandboxing y Anti-debugging Debuggers como OllyDbg, WindDbg, Radare o IDA ya sabréis que se requieren conocimientos sobre lenguaje ensamblador y estructuras internas de ficheros. Una técnica anti-debugging muy fácil de implementar, pero también muy fácil de evadir, es la función “IsDebuggerPresent”.
PEB (Process Environment Block) Los códigos de espagueti y basura hacen que las herramientas de análisis comunes sean ineficaces El primer problema de ofuscación que requiere una solución es la eliminación de las instrucciones basura y el "código de espagueti", que es una técnica que pretende confundir los programas de desamblado El código de espagueti hace que el programa fluya de forma difícil de leer al agregar saltos continuos de código, de ahí el nombre. Este problema no es nuevo, y en situaciones comunes se conocen complementos de reversing que pueden ayudar en esta tarea. Sin embargo, a veces no podemos encontrar un buen complemento de desensamblador interactivo (IDA) que pueda normalizar el flujo de código.
VM detection hardware IDs are VmBus in case of HyperV VEN_15AD in case of VMware Debugger detection IsDebuggerPresent ThreadHideFromDebugger ProcessDebugPort ProcessDebugObjectHandle ProcessDebugFlags CheckRemoteDebuggerPresent NtQueryInformationProcess ProcessBasicInformation Fuente:elhacker.net
miércoles, 5 de junio de 2024
No encontrar las librerías .h en linux
viernes, 31 de mayo de 2024
Los 11 fallos más comunes en Lenguaje C
(2)Sobrepasando límites de un arreglo. (3)Perdiendo la condición base de una funcion recursiva. (4)Usando constantes de caracteres en vez de literales de cadena y viceversa. (5)Literales de tipo float son por defecto de tipo double. (6)Olvidando liberar la memoria. (7)Añadiendo un semicolon a #define. (8)No ser cuidadoso con el semicolon. (9)Erroneamente escribir = o ==. (10)Copiando demasiado. (11)Las macros son solo reeemplazo de cadenas. 1.- Mezclar enteros signed y unsigned en operaciones aritmeticas
int main(void){ unsigned int a = 1000;signed int b = -1; if (a > b) puts("a is more than b"); else puts("a is less or equal than b"); return 0;}
2.- Sobrepasando los límites de un arreglo. Los arreglos siempre empiezan en 0 y terminan en la longitud del arreglo - 1.
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=1; x<=5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 2 3 4 5 GarbageValue
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=0; x<5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 1 2 3 4 5
3.- Perdiendo la condición base en una función recursiva. Calcular la factorización de un número es un ejemplo clásico de recursividad:
int factorial(int n){return n * factorial(n - 1);}
int main(){ printf("Factorial %d = %d\n", 3, factorial(3));return 0;} //Typical output: Segmentation fault
int factorial(int n){ // Base Condition, very crucial in designing the recursive functions.
if (n == 1){return 1;}else{return n * factorial(n - 1);}}
int main(){printf("Factorial %d = %d\n", 3, factorial(3));return 0;}
//Esta función va a terminar tan pronto alcance 1. //Output : Factorial 3 = 6
1 Iniciar el algoritmo. Las funciones recursivas necesitan con frecuencia un valor inicial con el que empezar. Esto es acompañado sea por un parametro de la función o una función puerta que no es recursiva pero pone los valores iniciales para la recursión.
2 Revisar para ver si los valores actuales que se están procesando coinciden con el caso base.Si es asi, entonces procesa y devuelve un valor
3 Redefine la respuesta en terminos de un problema pequeño o simple subproblema o subproblemas.
4 Ejecuta el algoritmo en un subproblema. 5 Combina los resultados en la formulación de la respuesta. 6 Retorna los resultados.
4.- Usando constantes de caracteres en vez de literales de cadena y viceversa. En lenguaje C, las cadenas de caracteres y literales de cadena son cosas distintas. 'a' ..esto es una cadena de caracter. Una cadena de caracter es de tipo entero que tiene asignado un número para ese caracter. "asdf"... es un literal de caracteres. Un literal de caracteres un arreglo inmodificable cuyos elementos son de tipo char. "asdf" tiene 5 caracteres, porque el caracter final es un \0. Este caracter final es conocido como carácter nulo. {a,s,d,f,\0}.
#include stdio.h
int main(void){ const char *hello = 'hello, world'; /* bad */puts(hello);return 0;}
//ejemplo 2: un literal de cadena se usa donde se debería usar una cadena de caracteres. El resultado es una cosa sin sentido.
#include stdio.h int main(void) { char c = "a"; /* bad */ printf("%c\n", c); return 0;}
5.- Literales de tipo float son por defecto de tipo double. Hay que tener cuidado al inicializar una variable float a valores literales o compararlas con estos. Esto es debido a que literales float de valor 0.1 por lo regular son de tipo double. Este tipo de cosas nos puede conducir a sorpresas:
#include stdio.h
int main(){ float n = 0.1; if (n > 0.1) printf("Wierd\n");return 0;} // Prints "Wierd" when n is float
6.- Olvidando liberar la memoria. Uno siempre debe recordar liberar la memoria alojada, sea una función hecha por tí o por una función de librería llamada por tu función.
int main(void){ char *line = NULL;size_t size = 0;
/* memory implicitly allocated in getline */ getline(&line, &size, stdin);
/* uncomment the line below to correct the code */ /* free(line); */return 0;}
7.- Añadiendo un semicolon a #define Muchas veces me pasó a mí!!! Es fácil confundirse con el preprocesador de C, y tratarlo como parte del lenguaje. Pero es un error, porque el preprocesador es sólo un mecanismo de reemplazo de texto.
//lo cual se traduce como int arr[100]={0}; //Que se traduce como error de sintaxis.
//Significa esto: if (x > a) {} a = x;
A veces hacemos: /* assign y to x */ if (x = y) {/* logic */}
//cuando lo que uno queria era: /* compare if x is equal to y */ if (x == y) {/* logic */}
//lo cual es equivalente a: /* compare if x is equal to y */ if (x == y) != 0{/* logic */}
10.- Copiando demasiado.
printf("What is your name?\n"); scanf("%s", buf); /* WRONG */ scanf("%7s", buf); /* RIGHT */
11.- Las Macros son solo reemplazo de cadenas
//este es el error. int main(void){printf("%d\n", SQUARE(1+2));return 0;}
Esperarías que este código devolviese 9, pero devolverá 5 porque la macro será expandida a 1+2*1+2. Para evadir este problema debe encerrar entre () las fichas para evadir este problema. #include stdio.h
#define SQUARE(x) ((x)*(x))
int main(void){printf("%d\n", SQUARE(1+2));return 0;}
TextView and EditText en Android
Diferencias entre const int y int const
miércoles, 22 de mayo de 2024
Colores c.nanorc
viernes, 12 de abril de 2024
ELF Basics Internal: Elf Basics.
So let is begin with a very simple hello world program in C
int main(){printf("\nHello World\n");return 0;}
As I am on a 64 bit Linux system I will compile the binary for both 32bit and 64bit mode.
We will compile this code with gcc by issuing the commandfor 32 bit -> gcc hello.c -m32 -o hello32
, ( in case we get error we can install gcc multilib by issuing command sudo apt-get install gcc-multilib ) If we issue file command on the binary we created we would see the following output
pentest@ubuntu:~/Desktop$ file hello64 hello64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=797fa6ea8a92b773eb5106c822a76788441ceac1, not stripped
pentest@ubuntu:~/Desktop$ file hello32 hello32: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ba188ad09ee9ff9ac774833b8a7c87d8afbc443a, not stripped
So let us try to understand what all these mean (we will analyze the result of 64 bit binary)
hello64: This is the filename of the binary on which we are executing the file command
ELF - Executable and Linking Format or Executable and Linkable Format - This mean that the binary type is associated with mainly UNIX type operating system , like Linux, Solaris but also supports certain non UNIX operating system.
64bit - This gives us information tells about the architecture of the binary that it is 64 bit. So if it is a 32 bit binary it will be shown as 32 If we are in a 64bit machine and if we want to create a 32 bit binary we can pass the -m32 option
LSB - Least Significant Byte - It means the binary is in little endian format. In Intel architecture you will often find this as LSB. However in architectures like PowerPC , SPARC and so on it is possible to get this in big endian format i.e MSB ( Most Significant Byte )
Shared object - This result can either be Shared Object or Relocatable or Executable.
Let us see how these are different from each other and how we can generate them using gcc. The two terms which we are going to use here are PIC ( Position Independent Code ) and PIE ( Position Independent Executable) .
When we are planning to create a library that can be called by many process, we need to make it a PIC so that they can be loaded in the memory at any virtual address and just because they are position independent it can be accessed with relative offsets without worrying about the clashes of fixed locations in memory. We can create a PIE when Shared Object - By default the gcc compiler compiles the source code with -fPIC which makes address of the sections in the program relative to each other.
Executable - This mean this is not a PIE application. This loads with absolute address and thus we can find no reference of .plt.got sections here as the program is loaded in memory with fixed address .We can disable PIE with -no-pie option in gcc and thus we will get a executable object file.
Relocatable - This means this is just an object code without any linking of libraries or files that are necessary for the execution.
There are some steps involved when we make a program that can be executable ( Please Note: The term executable here means here is to make it run or execute and should not be confused with the above executable object type ).
To make an executable from source program the following process is involved.
Normally in gcc we do in one step like gcc hello.c -o hello.out
but however we can do in 2 steps like gcc -c hello.c ; this will create an object file called hello.o This is how the disassembly of main looks like in object code.
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # b <main+0xb>
b: e8 00 00 00 00 call 10 <main+0x10>
10: 90 nop
11: 5d pop rbp
12: c3 ret
This program cannot run or do anything because the object code doesn't have the necessary linked objects or libraries required for execution. We can generate an executable binary from object code using the command
gcc hello.o -o hello-executable Now if we run objdump on the binary we can see lots of sections getting created with the location to the linkers.
This is how the disassembly of main looks like after linking
63a: 55 push rbp
63b: 48 89 e5 mov rbp,rsp
63e: 48 8d 3d 8f 00 00 00 lea rdi,[rip+0x8f] # 6d4
<_IO_stdin_used+0x4>
645: e8 c6 fe ff ff call 510
<puts@plt>
64a: 90 nop
64b: 5d pop rbp
64c: c3 ret
64d: 0f 1f 00 nop DWORD PTR [rax]
Dynamically linked, interpreter /lib/ld-linux.so.2, - It means that the binary uses some dynamically linked libraries. There is 2 possible values possible for this.Dynamically linked and Statically Linked.
Dynamically Linked - It means the linker actually uses a reference to load dynamically linked libraries in memory during execution of the program from the location /lib/ld-linux.so.2
We can verify it by running the ldd on the binary
pentest@ubuntu:~/Desktop$ ldd helloNOPIE linux-vdso.so.1 (0x00007ffffe990000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcb5e8c0000) /lib64/ld-linux-x86-64.so.2 (0x00007fcb5ecb1000)
shellcode a ASM y al reves
lunes, 25 de marzo de 2024
conversion explicita (casting)
C++, conversión explícita o cast Cuando necesitamos convertir una variable perteneciente a un tipo de dato (cadena, numérico, fecha, etc.) a otro tipo diferente del suyo original, debemos decirle al programa explícitamente que tipo de conversión deseamos.
La conversión explícita en vez de realizarla el compilador automáticamente se indica de forma explícita, en C++ utilizamos la forma (nombre_de_tipo)expresión. No es exclusiva de C++ pues también se puede emplear en otros programas aunque cambie ligeramente la implementación.
Notación cast Si tenemos una función previamente definida que espera un tipo determinado, por ejemplo la función raíz cuadrada (sqrt) espera como argumento un tipo double, para evitar una salida inesperada en el caso de pasar un argumento de otro tipo, podemos escribir: sqrt ( ( double ) ( n + 2) ); De este modo forzamos para que el resultado de ( n + 2 ) sea siempre un tipo double y se lo pase a la función sqrt.
En C++ es posible expresar también una construcción cast de la forma siguiente: nombre_de_tipo(expresión) esta recibe el nombre de notación funcional, y no se puede utilizar con tipos que tengan un nombre simple.
Notación funcional Para convertir un valor a un tipo puntero utilizando la notación cast escribiríamos: int *p = ( int *)0x1F5; Pero utilizando la notación funcional escribiremo: typedef int *pint; int *p = pint(0x1F5);
Una variable de un determinado tipo no siempre puede ser convertida explícitamente a otro tipo. En este caso: struct { unsigned int a : 3; // bits 0 a 2 unsigned int b : 1; // bit 3 unsigned int c : 3; // bits 4 a 6 unsigned int d : 1; // bit 7 } atributo; La variable atributo tiene una longitud de ocho bits. Pero si intentamnos copiar la variable atributo a una variable atrib de tipo char y escribimos. char atrib = char atributo; // error Da un error, ya que C++ no permite convertir una estructura a un tipo como char aunque las longitudes de ambos sean iguales.
Conversión de Punteros Utilizando conversiones explícitas de tipo sobre punteros, es posible convertir el valor de una variable de un determinado tipo a otro cualquiera. El formato general para hacer esto es: cualquier_tipo *p = ( cualquier_tipo *)&variable. Aplicando esto al caso anterior, obtendríamos: char * atrib = ( char *)&atributo; Con lo que char a = * atrib; define la variable a de tipo char, cuyo contenido es el mismo que el de la estructura atributo. Constructores y operadores de conversión Cuando trabajamos con clases, nosotros mismos tenemos que construir las conversiones que deseamos que realice el compilador cuando utilice un objeto de una clase. Estas conversiones pueden ser o entre una clase y un tipo predefinido. Para ello podemos utilizar dos mecanismos; constructores y operadores de conversión. Constructores Podemos definir una conversión a través de un constructor que tome un argumento de un determinado tipo como entrada y lo convierta en un objeto de una determinada clase. En la definición de una clase podemos definir el siguiente constructor: nombre_de_clase (int r) { real = ( double )r; imag = 0; } Que sirve para construir un número complejo. Este constructor además de inicializar un objeto complejo utilizando solamente un valor también permite asignar directamente un entero int a un objeto complejo como se muestra a continuación. complejo c(3); // construye el complejo (3,0) c = 6; // equivale a c = complejo(6) Operador de conversión Ahora se desea que se realice también de una forma implícita o explícita, si existe ambigüedad, la conversión de un tipo definido por el usuario a un tipo básico: double d; CRacional r(1,2); d = r ; // r tiene que convertirse a double para que la instrucción d = r se ejecute correctamente, es necesario realizar una conversión implícita de CRacional a double. Este tipo de conversión no está permitido con un constructor, ya que no podemos definir un constructor de un tipo base. Cuando necesitamos convertir objetos de un tipo de clase a otro tipo, tenemos que utilizar un operador de conversión. La sintaxis para este operador es: C::operator T(); Donde T es el nombre de un tipo. La conversión que se realiza es de C a T. Para convertir de CRacional a double. class CRacional { //....... operador double(); }; inline CRacional::operator double() { return ( double )numerador/( double )denominador; } Un operador de conversión no puede tener argumentos ni tipo del valor que se retorna. Un operador de conversión se puede llamar de a través de las formas siguientes: double d; CRacional r(1,2); d = r.operator double();/llamada explícita a la función d = double(r);//conversión explícita (notación funcional ) d = ( double )r; //conversión explícita cast d = r ; // conversión implícita Un operador de conversión puede llamarse explícitamente pero su principal utilidad es que sea llamado automáticamente por el compilador cuando la evaluación de una expresión requiere el tipo de conversión realizado por él. También puede definirse un operador de conversión que convierta un objeto de una clase a otro objeto de otra clase. Conversión del tipo void* El tipo void se puede utilizar para definir un puntero a un elemento genérico. Como podemos ver la función C malloc se define como: void *malloc(size_t n); En C, la conversión del tipo void* a otro tipo podía realizarse de forma implícita de este modo. char *p = malloc(longitud + 1); Pero en C++ esta conversión tiene que realizarse de forma explícita. Esto es. char *p = ( char * ) malloc ( longitud + 1 );