Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Skali en 29 Julio 2018, 16:13 pm



Título: Obtener valor de retorno de una función antes de preprocesar
Publicado por: Skali en 29 Julio 2018, 16:13 pm
Buenas que tal!!! No se si sea claro, pero voy a tratar de ser lo más genérico posible. Necesito obtener el resultado de una función para tomar una decisión antes de la etapa de preprocesamiento. Dependiendo de ese valor, tengo que tomar una decisión para definir un código u ótro código, pero el valor solo se como obtenerlo empleando una función, que se ejecuta luego del preprocesamiento... Quería saber como resolver ésta clase de problema.


Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: Serapis en 29 Julio 2018, 17:07 pm
Se entra en una contradición cuando se dice:
Necesito obtener... una decisión antes de la etapa de preprocesamiento... ...que se ejecuta luego del preprocesamiento

Citar
Necesito obtener el resultado de una función para tomar una decisión antes de la etapa de preprocesamiento. Dependiendo de ese valor, tengo que tomar una decisión para definir un código u ótro código, pero el valor solo se como obtenerlo empleando una función, que se ejecuta luego del preprocesamiento...
De modo genérico, estos casos suelen caer en lo que se llama dependencia cíclica:
A depende del valor de B, pero B depende del valor de A.
La solución es eliminar la dependcia cíclica, cuando esto sea posible y esté a tu alcance. (observa mi comentario inicial, para entender la dependencia cíclica que se produce).

Pero el modo de hacerlo, depende del caso concreto de que se trate, y al respecto sin código, no puede indicarse nada más preciso...

Estas situaciones suelen producirse en dos casos:
A - Cuando algo no está bien enfocado desde un principio. Aquí la solución para por reestructurar el diseño del código. Por ejemplo, puede que baste con crear otra función (parcial), a base de copiar parte de aquella.

B - Cuando el propio diseño del lenguaje/compilador, no te ofrece alternativa (este caso se da solo si te refieres a funcionalidad del propio lenguaje). Caso típico del orden de la cascada de eventos que se disparan cuando se crea un objeto o invoca un método. En este caso, la solución pasa por subir un nivel en el código y acceder de forma más nativa.

Si la situación es inevitable, puede que no quede más remedio que ejecutar cierto código dos veces, o retrasar la ejecución de determinado código en tanto no se den las condiciones que deban decidir la situación.

Es decir pued ejecutarse una primera vez, para que ciertas variables tomen un valor 'sucio', pero que luego la dependencia tome ya un valor 'adecuado' y luego reejecutar dicha funcionalidad, para que tome definitivamente los valores 'buenos'.
Si el problema parte de tu código, trata de eliminar la dependencia aunque sea a base de duplicar algo (si resulta aceptable). Cuando de pende del sistema, es más complicado, no siempre merece la pena remontarse más arriba para algo sencillo y acabas proveyendo alguna funcionalidad que se reejecuta 2 o más veces, en distintos momentos.






Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: Skali en 29 Julio 2018, 17:38 pm
Hola NEBIRE!  Gracias por tu tiempo! El problema que tengo es un poco complejo, pero voy a ver de que forma elimino la dependencia cíclica


Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: MAFUS en 29 Julio 2018, 22:06 pm
Sé específico pues la explicación genérica no se entiende.
Por otra parte no se puede obtener el valor de una operación si antes no se ha hecho.
Ya te digo, expón el problema de forma más específica y a lo mejor encontramos otra forma de atacarlo.


Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: Skali en 30 Julio 2018, 01:02 am
El problema es bastante complejo. Estaba investigando el código fuente de un rootkit, me parece que había tenido una duda sobre una linea complicada, y vos me ayudaste a desmenuzarla hace un tiempo, asi que gracias otra vez, jaja. Bueno, finalmente logré comprender como trabajaba y me dediqué a tomar funcionalidad de otros rootkits y adaptarlos al primero que estaba analizando, y el mayor trabajo que estuve haciendo fue el de hacer que el rootkit fuera compatible con cualquier versión de kernel desde la 2.6 hasta la 4.17, y pensé que lo había logrado pero luego me encontré con cosas inesperadas.

Para lograr la compatibilidad con todas las versiones de kernel, me fijé a través de la página
elixir.bootlin.com (http://elixir.bootlin.com) como iban cambiando las estructuras que mi rootkit usaba, eso junto con los errores que devuelve el compilador, me ayudaron a realizar ésta tarea. Parte de mi código es asi, por ejemplo:

Código
  1. struct proc_dir_entry {
  2. unsigned int low_ino;
  3. umode_t mode;
  4. nlink_t nlink;
  5. kuid_t uid;
  6. kgid_t gid;
  7. loff_t size;
  8. const struct inode_operations *proc_iops;
  9. const struct file_operations *proc_fops;
  10. #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) && \
  11.     LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
  12. struct proc_dir_entry *next, *parent, *subdir;
  13. #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) && \
  14.     LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
  15. struct proc_dir_entry *parent;
  16. struct rb_root subdir;
  17.     struct rb_node subdir_node;
  18. #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
  19. struct proc_dir_entry *parent;
  20. struct rb_root_cached subdir;
  21.     struct rb_node subdir_node;
  22. #endif
  23. void *data;
  24. atomic_t count; /* use count */
  25. atomic_t in_use; /* number of callers into module in progress; */
  26. /* negative -> it's going away RSN */
  27. struct completion *pde_unload_completion;
  28. struct list_head pde_openers; /* who did ->open, but not ->release */
  29. spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
  30. u8 namelen;
  31. char name[];
  32. };

Como ves, algunos campos de la estructura fueron cambiando en las distintas versiones de kernel. Ésta estructura estaba definida desde el principio en el rootkit que analicé, ya que "no está publicada en las cabeceras públicas de los nuevos kernels". Además de esa estructura, las variables que utilizo, que hacen referencia a campos de distintas estructuras tambien tienen esos macros dependientes de versiones de kernels. Luego de analizar detalladamente versión por versión, logré utilizar los macros adecuados para que el rootkit compilara sin errores en todos los linux que probé, todos con distitnas versiones de kernels. Aquí dejo la lista:


Kali Linux (Rolling)   4.17.9   x86_64
Kali Linux (Rolling)   4.16.18   x86_64
Linux Mint 19 (Tara)   4.15.0-20-generic   i686
Kali Linux (Rolling)   4.14.0-kali3-amd64   x86_64
Ubuntu 16.04.4 LTS   4.13.0-36-generic   x86_64
Fedora 26                   4.11.8-300.fc26   x86_64
Ubuntu 17.04           4.10.0-19-generic   x86_64
Debian 9                   4.9.0-7-686   i686
Kali Linux (Rolling)   4.4.142   x86_64
Kali Linux (Rolling)   3.16.57   x86_64
Linux Mint 17           3.13.0-37-generic   x86_64
Wifislax                   3.12.36   i686
Fedora 19                   3.9.5-301.fc19   x86_64
Ubuntu 12.10           3.5.0-17-generic   i686
Lihuen 5.10               3.2.0-4-amd64   x86_64
Ubuntu 10.04.4 LTS   2.6.32-38-generic-pae   i686

El problema fue cuando lo probé sobre openSUSE.  openSUSE Leap 15.0 con el kernel 4.12.14, utiliza la estructura proc_dir_entry que se empezó a utilizar en los kernels 4.14. Es como que openSUSE en su release, modificó esa estructura, pero manteniendo el número de la versión de kernel anterior, entonces, dentro de mi código, pasa por los macros que no debería pasar y no termina funcionando, ya que las estructuras no coinciden. Si yo utilizo un ubuntu con el kernel 4.12.14 (exactamente la misma version), el rootkit funciona, pero no sobre openSUSE. Entonces si quiero que funcione universalmente, en los macros debería agregar algo como ésto:

Código
  1. struct proc_dir_entry {
  2. unsigned int low_ino;
  3. umode_t mode;
  4. nlink_t nlink;
  5. kuid_t uid;
  6. kgid_t gid;
  7. loff_t size;
  8. const struct inode_operations *proc_iops;
  9. const struct file_operations *proc_fops;
  10. #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) && \
  11.     LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
  12. struct proc_dir_entry *next, *parent, *subdir;
  13. #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) && \
  14.     LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) && NO_ES_OPENSUSE
  15. struct proc_dir_entry *parent;
  16. struct rb_root subdir;
  17.     struct rb_node subdir_node;
  18. #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || ES_OPENSUSE
  19. struct proc_dir_entry *parent;
  20. struct rb_root_cached subdir;
  21.     struct rb_node subdir_node;
  22. #endif
  23. void *data;
  24. atomic_t count; /* use count */
  25. atomic_t in_use; /* number of callers into module in progress; */
  26. /* negative -> it's going away RSN */
  27. struct completion *pde_unload_completion;
  28. struct list_head pde_openers; /* who did ->open, but not ->release */
  29. spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
  30. u8 namelen;
  31. char name[];
  32. };

Para identificar si el SO es openSUSE, pensé en una forma, pero utilizando código que se ejecuta luego de la etapa de preprocesamientos, ya que no encontré macros que pregunten por una distribución en específico.

La solución por el momento sería crear dos rootkits distintos, uno especialmente echo para funcionar con openSUSE, y el otro para funcionar con todas las demas distros, pero yo no quería llegar a ésto, ya que mi idea era la de tener un solo rootkit universal que funcionara sobre cualquier linux.

Es bastante complicado el problema, si no fui claro en alguna parte, por favor escribime.

Gracias nuevamente MAFUS, que vos siempre estás aca para ayudar.

Saludos!
 


Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: MAFUS en 30 Julio 2018, 03:15 am
La única forma que se me ocurre es que mientras desconozcas el sistema operativo del cual se trata sean punteros a void. Ya que los punteros siempre tienen el mismo tamaño no habrá quejas por parte del compilador en ningún caso. Una vez conozca el tipo de sistema operativo que se trata puedes generar de forma dinámica el tipo de estructura que requieras mediante malloc. Lo único que tendrás que tener en cuenta es que debes liberarlo cuando cierres el programa en la función de limpiar recursos, en el main o con atexit, según hagas la limpieza.

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. int main() {
  6.    void* pointer;
  7.  
  8.    pointer = malloc(sizeof(int));
  9.    *(int*)pointer = 3;
  10.    printf("%d\n", *(int*)pointer);
  11.    free(pointer);
  12.  
  13.    pointer = malloc(sizeof(char)*(strlen("Una cadena")+1));
  14.    strcpy((char*)pointer, "Una cadena");
  15.    printf("%s\n", (char*)pointer);
  16.    free(pointer);
  17. }

Sí, el código es un poco feo, pero en C es la forma de tener un tipo de objeto genérico.


Título: Re: Obtener valor de retorno de una función antes de preprocesar
Publicado por: Skali en 30 Julio 2018, 03:26 am
Excelente MAFUS! Lo voy a tener en cuenta como una solución! Gracias nuevamente. Saludos