elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Los 10 CVE más críticos (peligrosos) de 2020


  Mostrar Mensajes
Páginas: 1 2 3 4 5 6 7 8 9 10 11 12 13 [14] 15 16 17 18 19 20
131  Programación / Programación C/C++ / [C] Listas enlazadas. en: 15 Octubre 2011, 02:09 am

Información general:
Las listas enlazadas son un recurso dinámico muy potente que nos permite realizar tareas que generalmente podríamos requerir con arrays, pero se impide en objetos estáticos y en dinámicos sería muy complicado.
Una lista enlazada se basa en una estructura, la cual será el esqueleto o prototipo del contenido, y al final requerirá tener un puntero hacia la siguiente estructura del mismo tipo, es decir están enlazadas por su ubicación (dirección de memoria).


Funcionamiento y estructura:
Primero debemos tener la estructura o prototipo en el cual definiremos los datos que emplearemos y como debe ser, un puntero a otra estructura del mismo tipo:

Código
  1. typedef struct _nodo
  2. {
  3.    int valor;
  4.    struct _nodo * pNext;
  5. }_Nodo;

Utilizamos typedef para evitar estar utilizando struct _nodo para todo, así abreviamos y hacemos el código más legible.
Código
  1. typedef _Nodo * _pNodo;

Hay unas bases a tener en cuenta para trabajar con listas enlazadas:
  • El final de una lista será determinada por un puntero con valor NULL, es decir el último elemento tendrá como siguiente elemento a NULL.
  • Los elementos se pueden recorrer en un solo sentido (Inicio - Final), debido a que solo tenemos un apuntador al siguiente y no al anterior, las listas que cuentan con esta opción se conocen como doblemente enlazadas. Las trataremos en otro tema.
  • Es muy importante mantener siempre el valor inicial de la lista, en caso de perderle sería muy difícil de recuperar.
  • Es muy importante controlar muy bien los punteros hacia los elementos, un simple número distinto y podríamos causar perdida de control sobre el programa.

Recordemos, cada elemento tiene un apuntador al siguiente, por ello se llaman enlazados:



Para trabajar con listas, deberemos implementar una serie de funciones y procedimientos para el manejo de sus elementos entre ellas:
  • Crear lista nueva.
  • Insertar elemento (Al inicio, al final y posterior al otro elemento).
  • Buscar elemento.
  • Eliminar elementos(El primer elemento, un elemento en base a su dirección).
  • Imprimir contenido de listas (Este uso ya depende del usuario).

Creación de una lista nueva:
Para crear una nueva lista teniendo en cuenta el prototipo lo que utilizaremos son punteros, por lo cual suele ser más cómodo utilizar un typedef para más comodidad. En cuanto a teoría para crear una nueva lista deberíamos:
  • Crear una función cuyo tipo de valor de retorno será el mismo del prototipo.
  • La función creará una estructura dinámica con el tamaño del prototipo (malloc).
  • Se establecerá un valor inicial para los elementos de la estructura.
  • El apuntador al siguiente elemento será NULL, esto identificará la lista como vacía.
  • La función retornará la dirección de la lista creada (valor retornado por malloc).
Código
  1. _pNodo CrearLista(int valor)
  2. {
  3.    _pNodo Lista;
  4.  
  5.    Lista = (_pNodo) malloc (sizeof(_Nodo));
  6.    Lista->valor = valor;
  7.    Lista->pNext = NULL;
  8.  
  9.    return Lista;
  10. }

Inserción de elementos:
Para insertar nuevos elementos deberemos tener una lista previamente creada y contar con su referencia para acceder a ella, además se deberán hacer múltiples comparaciones con fines de prevenir errores, como cuando la lista está vacía, o el lugar donde se va a ingresar el nuevo elemento, etc.

Insertar elemento al final:
  • La función requerirá el valor del elemento a ingresar y el inicio de la lista.
  • Crear un nuevo prototipo de estructura, lo llamaremos Nodo.
  • Comprobar si la lista está vacía, en tal caso simplemente modificar la dirección a la que apunta por el nodo creado (NULL x Dirección nuevo Nodo). En caso de no estar vacía deberemos recorrer todos los elementos hasta reconocer al que apunta al NULL y editar por la dirección del nuevo Nodo.
  • En caso de recorrer la lista no olvidar utilizar una variable auxiliar para no perder la lista principal.
  • Al ser un elemento que va al final, estará obligado a tener su campo de siguiente estructura a NULL.
  • En mi caso bajo utilice la idea de retornar la dirección del nuevo elemento insertado, muchos códigos retornan la misma lista ingresada, cuando en realidad esta no sufre modificaciones y no es útil.
Código
  1. _pNodo InsertarElementoAlFinal(int valor, _pNodo ListaInicial)
  2. {
  3.    _pNodo NuevoNodo;
  4.    _pNodo Auxiliar = ListaInicial;
  5.    NuevoNodo =  malloc(sizeof(_Nodo));
  6.  
  7.    NuevoNodo->valor = valor;
  8.    NuevoNodo->pNext = NULL;
  9.  
  10.    if (ListaInicial->pNext == NULL)
  11.    {
  12.        ListaInicial->pNext = NuevoNodo;
  13.    }
  14.    else
  15.    {
  16.        while(Auxiliar->pNext != NULL)
  17.        {
  18.            Auxiliar =  Auxiliar->pNext;
  19.        }
  20.        Auxiliar->pNext = NuevoNodo;
  21.    }
  22.  
  23.    return NuevoNodo; /* Retornamos dirección del elemento insertado */
  24. }

Inserción de elementos al principio:
  • La función requerirá el valor del elemento a ingresar y el inicio de la lista.
  • Crear un nuevo Nodo, asignar el valor correspondiente.
  • El valor del campo siguiente elemento del Nodo creado deberá ser correspondiente al valor principal de la lista.
  • Retornamos el nuevo inicio de lista, correspondiente al nuevo Nodo.
Código
  1. _pNodo InsertarElementoAlInicio(int valor, _pNodo ListaInicial)
  2. {
  3.    _pNodo NuevoNodo;
  4.    NuevoNodo = malloc(sizeof(_Nodo));
  5.    NuevoNodo->valor = valor;
  6.    NuevoNodo->pNext = ListaInicial;
  7.  
  8.    return NuevoNodo; /* Retornamos nueva lista inicial */
  9. }

Inserción de un elemento posterior a otro:
  • La función requerirá el valor del elemento a ingresar y la dirección del elemento anterior a donde ingresaremos el nuevo.
  • Creamos el nuevo nodo y asignamos los valores correspondientes.
  • El nuevo Nodo creado ahora apuntará al elemento que apuntaba el Nodo anterior a este.
  • El Nodo anterior apuntará al nuevo Nodo insertado.
  • Retornamos la dirección del nuevo Nodo.
Código
  1. _pNodo InsertarElementoPosterior(int valor, _pNodo ElementoAnterior)
  2. {
  3.    _pNodo NuevoNodo;
  4.    NuevoNodo = malloc(sizeof(_Nodo));
  5.  
  6.    NuevoNodo->valor = valor;
  7.    NuevoNodo->pNext = ElementoAnterior->pNext;
  8.  
  9.    ElementoAnterior->pNext = NuevoNodo;
  10.  
  11.    return NuevoNodo; /* Retornamos dirección del elemento insertado */
  12. }

Eliminación de elementos:
La eliminación de elementos también es una función muy importante a la hora de trabajar con listas, sus implementaciones pueden ser muchas debido a la gran cantidad de casos posibles, como eliminar el primer o último elemento, eliminar un elemento según su dirección, posterior a otro, o buscando por valor (Eliminar primero, último o todos). Yo mostraré 2 casos, eliminando el primer elemento y eliminando por dirección del elemento.

Eliminación del primer elemento:
  • Para esta función únicamente necesitaremos el inicio de lista.
  • Usaremos una variable auxiliar la cual nos ayudará a almacenar datos temporalmente.
  • Comprobamos si la lista está vacía, en tal caso dejamos todo igual.
  • Almacenamos el elemento inicial de lista en la variable auxiliar.
  • Ahora el inicio de lista apuntará tendrá como valor el elemento al cual apuntaba.
  • Liberamos el espacio de memoria que contiene la variable auxiliar con free().
Código
  1. _pNodo EliminarPrimerElemento(_pNodo Lista)
  2. {
  3.    _pNodo Auxiliar;
  4.    Auxiliar = Lista;
  5.  
  6.    if (Auxiliar->pNext == NULL)
  7.    {
  8.        return Lista; /* Si no hay más elementos dejamos todo igual */
  9.    }
  10.    Lista = Auxiliar->pNext;
  11.    free(Auxiliar);
  12.  
  13.    return Lista; /* Retornamos la nueva base de la lista */
  14. }

Eliminar elemento por su dirección:
  • Necesitaremos inicio de lista y dirección del elemento a eliminarla.
  • Utilizamos una variable auxiliar para buscar el elemento anterior al elemento a eliminar recorriendo toda la lista.
  • En caso de no encontrar el elemento comparando su dirección retornamos 0 (FALSE)
  • Comprobamos si el elemento a eliminar es el último, en tal caso el elemento anterior apuntará a NULL, de lo contrario el elemento anterior al que eliminamos apuntará al elemento que apuntaba el elemento que eliminamos.
  • Liberamos la memoria del elemento a eliminar.
  • Retornamos 1(TRUE).
Código
  1. int EliminarElemento(_pNodo Elemento, _pNodo Lista)
  2. {
  3.    _pNodo Auxiliar;
  4.    Auxiliar = Lista;
  5.    while (Auxiliar != NULL)
  6.    {
  7.        if (Auxiliar->pNext == Elemento)
  8.        {
  9.            break;
  10.        }
  11.        Auxiliar = Auxiliar->pNext;
  12.    }
  13.    if (Auxiliar == NULL)
  14.    {
  15.        return 0;
  16.    }
  17.    else
  18.    {
  19.        if (Elemento->pNext == NULL)
  20.        {
  21.            Auxiliar->pNext = NULL;
  22.        }
  23.        else
  24.        {
  25.            Auxiliar->pNext = Elemento->pNext;
  26.        }
  27.  
  28.        free(Elemento);
  29.        return 1;
  30.    }
  31. }

Búsqueda de elementos:
La búsqueda de elementos generalmente se realiza para localizar la posición de un objeto comparando su valor, ya que si se tiene su dirección simplemente haciendo referencia podríamos acceder a su contenido.

Búsqueda por valor:
  • Necesitaremos el valor a buscar y el inicio de lista.
  • Utilizaremos una variable auxiliar para no perder el inicio de la lista.
  • Recorremos toda la lista y vamos comparando si cada elemento es igual al buscado.
  • Una vez localizado simplemente retornamos la dirección del elemento.
  • Si no se lo encuentra se retornará NULL.
Código
  1. _pNodo BuscarElemento(int valor, _pNodo Lista)
  2. {
  3.    _pNodo Auxiliar;
  4.  
  5.    Auxiliar = Lista;
  6.    while(Auxiliar != NULL)
  7.    {
  8.        if (Auxiliar->valor == valor)
  9.        {
  10.            break;
  11.        }
  12.        Auxiliar = Auxiliar->pNext;
  13.    }
  14.    return Auxiliar; /* Retornamos dirección del elemento encontrado */
  15. }

Bueno, espero hayan comprendido el articulo y prometo trabajar más estructuras de datos.
Hasta la próxima.

Saludos.
132  Seguridad Informática / Análisis y Diseño de Malware / Re: Intento de agregar sección a ejecutable en: 7 Octubre 2011, 17:59 pm
Código
  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4.  
  5. DWORD align(DWORD number, DWORD alignment)
  6. {
  7.    if(number % alignment == 0)
  8.       return number;
  9.    else
  10.       return (number / alignment) * alignment + alignment;
  11. }
  12.  
  13. int main()
  14. {
  15.   IMAGE_DOS_HEADER dh;
  16.   IMAGE_NT_HEADERS nth;
  17.  
  18.   IMAGE_SECTION_HEADER * sección;
  19.   IMAGE_SECTION_HEADER nSeccion;
  20.    // char * stub_dos -> Datos del STUB_DOS
  21.    // char * dSecciones -> Datos de las secciones
  22.   long TamanoSecciones = 0;
  23.   int i;
  24.  
  25.   FILE * archivo = fopen("c:\\Mad.exe","r+b");
  26.   if (archivo == NULL)
  27.   {
  28.       printf("Error al leer el archivo\n");
  29.       getchar();
  30.  
  31.       return 1;
  32.   }
  33.  
  34.   fread(&dh,sizeof(dh),1,archivo); // Rellenamos IMAGE_DOS_HEADER
  35.  
  36.   char * stub_dos = (char*)malloc(dh.e_lfanew-0x40);
  37.  
  38.   fread(stub_dos,1,dh.e_lfanew-0x40,archivo); // Leemos el Stub DOS
  39.   fread(&nth,sizeof(nth),1,archivo); // leemos nt headers
  40.  
  41.   sección = (IMAGE_SECTION_HEADER*)malloc(sizeof(IMAGE_SECTION_HEADER)*nth.FileHeader.NumberOfSections);
  42.  
  43.   fread(sección,sizeof(IMAGE_SECTION_HEADER),nth.FileHeader.NumberOfSections,archivo);
  44.  
  45.   /*for (i=0;i<=nth.FileHeader.NumberOfSections-1;i++)
  46.    {
  47.        printf("%i\n",sección[i].SizeOfRawData);
  48.        TamanoSecciones += sección[i].SizeOfRawData;
  49.    }*/
  50.   TamanoSecciones =  (sección[nth.FileHeader.NumberOfSections-1].PointerToRawData + sección[nth.FileHeader.NumberOfSections-1].SizeOfRawData) - sección[0].PointerToRawData;
  51.  
  52.   fseek(archivo, dh.e_lfanew + 248 + (nth.FileHeader.NumberOfSections * 0x28)+0x28, SEEK_SET);
  53.  
  54.   char * dSecciones = (char*)malloc(TamanoSecciones + (nth.OptionalHeader.SizeOfHeaders - ftell(archivo)));
  55.   int HeaderSize = nth.OptionalHeader.SizeOfHeaders - ftell(archivo);
  56.   char * Header = (char*) malloc(HeaderSize);
  57.   memset(Header,0, HeaderSize);
  58.   memset(dSecciones, 0, TamanoSecciones);
  59.   fread(Header, HeaderSize,1, archivo);
  60.   fread(dSecciones,TamanoSecciones,1,archivo); //leo todos los datos de las secciones.
  61.  
  62.   fclose(archivo); // terminamos de leer
  63.  
  64.   memset(&nSeccion,0, 0x28);
  65.   strncpy((char*)nSeccion.Name,".fary",5); // nombre de la nueva sección: .fary
  66.   nSeccion.VirtualAddress = align(sección[nth.FileHeader.NumberOfSections-1].VirtualAddress + sección[nth.FileHeader.NumberOfSections-1].Misc.VirtualSize, nth.OptionalHeader.SectionAlignment);
  67.   nSeccion.SizeOfRawData = align(0x50, nth.OptionalHeader.FileAlignment);
  68.   nSeccion.PointerToRawData = sección[nth.FileHeader.NumberOfSections-1].PointerToRawData + sección[nth.FileHeader.NumberOfSections-1].SizeOfRawData;//align(0x50,nth.OptionalHeader.FileAlignment);
  69.   nSeccion.Characteristics = 0x60000020;
  70.   nSeccion.Misc.VirtualSize = 0x50;
  71.  
  72.   nth.FileHeader.NumberOfSections += 1; // sumamos la nueva sección
  73.   nth.OptionalHeader.SizeOfImage = align(0x50+nSeccion.VirtualAddress,nth.OptionalHeader.SectionAlignment);
  74.   //nth.OptionalHeader.SizeOfHeaders += 0x28;
  75.  
  76.  
  77.   FILE * nuevo = fopen("Nuevo.exe","wb+");
  78.  
  79.   fwrite(&dh,sizeof(dh),1,nuevo);
  80.   fwrite(stub_dos,dh.e_lfanew-0x40,1,nuevo);
  81.   fwrite(&nth,sizeof(nth),1,nuevo);
  82.   nth.FileHeader.NumberOfSections -= 1;
  83.   fwrite(sección,sizeof(IMAGE_SECTION_HEADER)*nth.FileHeader.NumberOfSections,1,nuevo);
  84.   fwrite(&nSeccion,sizeof(IMAGE_SECTION_HEADER),1,nuevo);
  85.   fwrite(Header, HeaderSize,1, nuevo);
  86.   fwrite(dSecciones,TamanoSecciones,1,nuevo);
  87.   if (fseek(nuevo,0,SEEK_END) != 0)
  88.   {
  89.       printf("error\n");
  90.   }
  91.   int Size = nSeccion.SizeOfRawData;
  92.   char DatosSeccion[0x1000] = "Hola yo soy la sección de prueba, y ocupo exactamente la cantidad de 0x50 bytes.\0"; //Debe tener la cantidad de bytes del SizeOfRawData
  93.   fwrite(DatosSeccion,nSeccion.SizeOfRawData,1,nuevo);
  94.  
  95.  
  96.   fclose(nuevo);
  97.   system("PAUSE");
  98.   return 0;
  99. }

Tu primer error estaba a la hora de leer el resto de la cabecera, posterior no agregabas el tamaño del SizeOfRawData completo, de lo contrario se corrompe el exe.

Si es un ejecutable VB solo bastaría dejar BoundImport a 0 o moverlo más abajo.

133  Programación / Programación Visual Basic / Re: Manejar aplicaciones desde otra aplicación en: 3 Octubre 2011, 05:45 am
USSF, la mejor opción ya que es para un Instalador.

Un saludo.
134  Programación / Programación C/C++ / [C] Variación cifrado ROT. en: 3 Octubre 2011, 04:13 am
Buenas noches,
Creo que a Zero le había comentado de mi idea de darle una pequeña variación al cifrado ROT común.

Idea:
  • Utilizar una contraseña numérica con la cual se hará la rotación.
  • Se operará en bloques igual a la longitud de números de la contraseña.
  • Individualmente actuará cada número a la hora de cifrar.
  • Si el dígito actual es menor que 5 se suma, de lo contrario se resta.
  • El tercer parámetro es para evitar prácticamente replicar la función para descifrar.

Les dejo un pequeño ejemplo, lo pueden adaptar y optimizar. Si quieren adaptarlo a binarios deberán añadir comprobación del límite ASCII y no usar strlen.

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. char * ROTPassword(char * String, int password,int descifrar);
  6.  
  7.  
  8. int main()
  9. {
  10.    printf("%s ", ROTPassword("hsec",152,0));
  11.    printf("%s ", ROTPassword("jnfe",152,1));
  12.    getchar();
  13.    return 0;
  14. }
  15.  
  16. char * ROTPassword(char * String, int password, int descifrar)
  17. {
  18.    int array[10] = {0};
  19.    int passcopy = password, counter = 0,i,strlength, passlength = 0;
  20.    char * cifrado;
  21.  
  22.    strlength = strlen(String);
  23.    while(passcopy >= 1)
  24.    {
  25.        array[counter] = (passcopy % 10);
  26.        passcopy /= 10;
  27.        counter++;
  28.    }
  29.  
  30.    cifrado = (char*) malloc(strlength+1);
  31.    memset(cifrado,0,strlength+1);
  32.  
  33.    for (i = 0; i < strlength; i++)
  34.    {
  35.        if (array[passlength] < 5)
  36.        {
  37.            (descifrar == 1)?cifrado[i] = String[i] - array[passlength]: (cifrado[i] = String[i] + array[passlength]);
  38.        }
  39.        else
  40.        {
  41.            (descifrar == 1)?cifrado[i] = String[i] + array[passlength]: (cifrado[i] = String[i] - array[passlength]);
  42.        }
  43.        passlength++;
  44.        if (passlength >= counter) passlength = 0;
  45.  
  46.    }
  47.  
  48.    return cifrado;
  49. }

Un saludo, Gracias Zero.
135  Seguridad Informática / Análisis y Diseño de Malware / Re: Que es un RunPE? en: 30 Septiembre 2011, 01:39 am
Buena tarde,
Con respecto a las respuestas, todas están erradas, pero vamos no del todo y no es por desmeritarles. En realidad el RunPE obviamente tiene estrecha relación con el Formato PE, pero no es exactamente eso.
El famoso RunPE es una técnica la cual permite ejecutar archivos ("on the fly" - Karcrack), es decir sin necesidad de que el archivo ocupe tamaño físico, esto se hace porque el archivo está plasmado en un buffer en memoria. Esta técnica es usada en malware generalmente en Cifradores, los cuales tienen en su cuerpo el archivo cifrado, luego en memoria lo descifran y lo ejecutan, y esto no llamará mucho la atención.

Como funciona un RunPE:
  • Comprobar que es un ejecutable válido (MZ & PE/x0/x0 Signature).
  • Crear un nuevo proceso suspendido (Generalmente con el mismo ejecutable).
  • Desasignar la proyección del archivo en ese proceso, ImageBase. (Limpiar el ejecutable del proceso).
  • Obtener contextos (Registros).
  • Reservar en memoria la dirección del ImageBase del ejecutable con un tamaño del campo SizeOfImage. (ImageBase y SizeOfImage del archivo a ejecutar).
  • Escribir la cabecera y las secciones alineadas por el campo SectionAlignment.
  • Editar EAX en los registros leídos por la dirección del punto de entrada del archivo a ejecutar.
  • Editar EBX + 8 por el ImageBase del ejecutable a cargar (Cambiar base de la imagen del nuevo proceso).
  • Editar con el nuevo contexto (Escribir registros).
  • Iniciar el proceso suspendido.

Un saludo.
136  Programación / Programación C/C++ / Re: No me carga la DLL LoadLibrary en: 17 Septiembre 2011, 17:11 pm
Tratas de llamar a una función que jamás exportaste para uso externo.
Aquí hay un ejemplo claro:
Código:
http://www.infernodevelopment.com/simple-c-dll-loading-message-box

Un saludo.
137  Seguridad Informática / Análisis y Diseño de Malware / Re: Firmas en la cabecera un gran reto! en: 17 Septiembre 2011, 16:27 pm
El cambio de firmas, es porque además de las firmas en la cabecera tiene firmas en el cuerpo del archivo, te recomiendo que pruebes el famoso método de reemplazar valores de los distintos offsets, identificar la firma y el sitio donde esté, pero trabaja con el archivo que tenga el cuerpo en 00. Luego identificas con un editor PE sobre que campo recae la firma y vemos que se puede hacer.

Un saludo.
138  Seguridad Informática / Análisis y Diseño de Malware / Re: Firmas en la cabecera un gran reto! en: 16 Septiembre 2011, 04:07 am
@lukaz
Creo que lo que "crees" o en tu concepto son los offsets detectados, no son otra cosa que el famoso campo del IMAGE_DOS_HEADER, l_fanew el cual tiene el valor que apunta a la firma PE/x0/x0.
Si Avira (juraría que es éste) o cualquier otro te detecta eso, es porque generalmente está emulando tu código y al no funcionar al cambiar eso la firma se va.

Te recomiendo poner todo el cuerpo del ejecutable a 0, generalmente en Visual Basic a valor 4096d, o lo puedes mirar con un editor PE el cual te proporcione el valor del SizeOfHeraders.

Con eso sabrás realmente si está en la cabecera.

Un saludo.
139  Seguridad Informática / Análisis y Diseño de Malware / Re: Firmas en la cabecera un gran reto! en: 15 Septiembre 2011, 22:04 pm
No os gusta casos como por ejemplo
en VB:
- Bound Import a 0
- Secciones distintas a las de código demasiado grandes  :silbar:
- Archivos desalineados (SizeOfInitializedData y compañia).
- Tamaños no alineados (SizeOfImage, SizeOfHeaders, etc).

y varias cosas más, no te vendría nada mal leerte el Formato PE.

Un saludo.
140  Seguridad Informática / Análisis y Diseño de Malware / Re: Avira 1 - Yo 0 en: 5 Septiembre 2011, 18:37 pm
Y sí, de acuerdo con Karcrack la detección está casi segura en la sección de importaciones (Pero en VB la estructura cambia). Cifralas con algún modulo (Call API) que use como mucho RtlMoveMemory y luego mueve la referencia de esta cadena en cualquier hueco de la cabecera PE y adiós firma ;)
Páginas: 1 2 3 4 5 6 7 8 9 10 11 12 13 [14] 15 16 17 18 19 20
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines