Linea de código dificil de entender

Iniciado por Skali, 3 Julio 2018, 02:37 AM

0 Miembros y 1 Visitante están viendo este tema.

Skali

Buenas, estaba analizando el código fuente de un kernel rootkit, y leyendo la documentación de las apis que se utilizan más o menos me estoy dando mania, aunque hay lineas que tal vez me cuestan bastante comprender, como ésta:

*(void **)&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] = h->modified_function;

Aquí les dejo un fragmento del rootkit para que les sea más facil entender:

// ========== ASM HOOK LIST ==========

#if defined __i386__
   // push 0x00000000, ret
   #define ASM_HOOK_CODE "\x68\x00\x00\x00\x00\xc3"
   #define ASM_HOOK_CODE_OFFSET 1
   // alternativly we could do `mov eax 0x00000000, jmp eax`, but it's a byte longer
   //#define ASM_HOOK_CODE "\xb8\x00\x00\x00\x00\xff\xe0"
#elif defined __x86_64__
   // there is no push that pushes a 64-bit immidiate in x86_64,
   // so we do things a bit differently:
   // mov rax 0x0000000000000000, jmp rax
   #define ASM_HOOK_CODE "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0"
   #define ASM_HOOK_CODE_OFFSET 2
#else
   #error ARCH_ERROR_MESSAGE
#endif

struct asm_hook {
   void *original_function;
   void *modified_function;
   char original_asm[sizeof(ASM_HOOK_CODE)-1];
   struct list_head list;
};

LIST_HEAD(asm_hook_list);

/**
* Patches machine code of the original function to call another function.
* This function should not be called directly.
*/
void _asm_hook_patch(struct asm_hook *h)
{
   DISABLE_W_PROTECTED_MEMORY
   memcpy(h->original_function, ASM_HOOK_CODE, sizeof(ASM_HOOK_CODE)-1);
   *(void **)&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] = h->modified_function;
   ENABLE_W_PROTECTED_MEMORY
}


Link del rootkit: https://github.com/nurupo/rootkit/blob/master/rootkit.c

no quiero que me expliquen todo el código, solo quiero saber como interpretar la línea que marqué más arriba (sería la línea 36 en éste fragmento).

Muchas gracias desde ya. Saludos


srWhiteSkull

#1
Pues es lo que ves, un array que se le asigna a una de sus celdas una función  ;D

Skali

Buenas! Gracias srWhiteSkull por leer y responder, pero todavía no termino de entender... Si el código fuera asi:

h->original_function[ASM_HOOK_CODE_OFFSET] = h->modified_function;

Podría decir que al campo original_function del registro h en la posición ASM_HOOK_CODE_OFFSET se le asigna el campo modified_function del mismo registro h, pero lo que me hace marear mucho es lo que está antes, es como una conversión de tipos muy extraña, por eso me gustaría si pudieramos dividir la linea parte por parte y analizarla para entenderla bien. Gracias desde ya.

Saludos!

srWhiteSkull

Me imagino que será un array char, que a su vez contiene el puntero de una función que toma de argumento un void ** ... creo  :rolleyes:

MAFUS

*(void **)&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] = h->modified_function;

h->original_function : objeto, un puntero.

(char *)h->original_function : un puntero casteado a puntero a char.

((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] : dado que h->original_function ha sido casteado a char * moverse a través de él será byte a byte, por tanto esto devolverá lo que hay en el byte ASM_HOOK_CODE_OFFSET de h->original_function.

&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] : devuelve la dirección del byte ASM_HOOK_CODE_OFFSET del objeto h->original_function.

(void **)&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] : castea a void ** la dirección del byte ASM_HOOK_CODE_OFFSET del objeto h->original_function. Se reconvierte la dirección devuelta al puntero de un puntero a void.

*(void**)&((char *)h->original_function)[ASM_HOOK_CODE_OFFSET] : se obtiene la dirección del byte ASM_HOOK_CODE_OFFSET del objeto h->original_function como puntero a void.

Skali

Excelente MAFUS! Muchas gracias por desmenuzar la línea parte por parte y analizarla, me ayudaste a entender con éste ejemplo práctico cómo leer una línea compleja teniendo en cuenta la precedencia de operadores. Ahora todo queda bastante más claro. Gracias nuevamente! Saludos.

MAFUS

En el siguiente link https://medium.com/@bartobri/untangling-complex-c-declarations-9b6a0cf88c96 te enseñarán a desmenuzar todas estas declaraciones complicadas. Hay muchos otros ejemplos en la red. Son un puzzle bastante bonito a resolver.

srWhiteSkull

CitarHace un casting a char* para poder avanzar byte a byte ya que sino avanzaria el tamaño de un puntero 4bytes, etc.. supongo que es o codigo viejo o de un programa en C, ya que hoy en dia en C++ no hay que hacer estas barbaridades tan poco legibles.

MAFUS

Seguramente es C, aunque en realidad C++ por debajo termina haciendo lo mismo. Son cosas de la expresividad del lenguaje.

Por otra parte: no se movería 4 bytes, eso en caso de que fuera un puntero a int, pero el objeto es un puntero a void. ¿Qué ocupa un dato de tipo void? Ni siquiera existe. Algunos compiladores, como GCC consideran que sumar 1 a un void* ha de apuntar al siguiente byte, pero claro puede haber otros desarrolladores de compiladores que no consideren así.

srWhiteSkull

La cita la extraí de una conversación con mi master al que le pregunté sobre la explicación de ese casting tan raro. Este hombre, mi master, es un señor que lleva muchísimos años programando, sobre todo en C++. Con él me inicié en el C y luego en el C++. Incluso sólo viendo la línea dedujo que se trataría de algún hook (la línea se la mostré sin la constante ASM_HOOK_CODE_OFFSET).

Yo no soy experto en C, puntualmente hago alguna cosilla en C (no es algo que se demande).