Análisis de malware

Código C en ensamblaje

16 de septiembre de por Srinivas

Los analistas de ingeniería inversa tienen un buen conocimiento del lenguaje de código C y de cómo se convierte en listados de ensamblaje. El código C fue diseñado para funcionar como una forma corta de lenguaje ensamblador que, a pesar de llevar mucho tiempo codificar, tenía eficiencias inherentes. El código C pudo aprovechar algunas de estas eficiencias empleando construcciones de código.

Este artículo es una descripción general del código C en ensamblador, incluidas variables y declaraciones “if”, bucles “for” y ” while”, declaraciones de cambio, matrices, estructuras, listas vinculadas, pilas y montones.

variables

Las variables se utilizan en el código para contener valores. Según dónde se declara la variable, las variables son de dos tipos: variables locales y variables globales. Se puede acceder a los valores almacenados en una variable local dentro de una función, mientras que se puede acceder a los valores almacenados en variables globales desde cualquier parte del programa.

El siguiente programa en C muestra cómo se utilizan las variables locales y globales en un programa en C.

#incluir stdio.h

int a = 10; /**variable global**/

vacío principal()

{

int b =/**variable local**/

a = a+b;

printf(“El nuevo valor de a es %dn”, a);

}

El siguiente es el equivalente ensamblador del código anterior. Esto lo genera OllyDbg cuando se carga el ejecutable.

Declaraciones “si”

Las declaraciones “si” se utilizan comúnmente en la programación en C. Se utilizan para cambiar el flujo de control en función de determinadas condiciones. El siguiente ejemplo de código muestra una condición if que se utiliza en un programa en C.

#incluir stdio.h

vacío principal()

{

int a = 30;

int b =/p>

si (a b){

printf(“a es mayor que bn”);

}

demás{

printf(“b es mayor que an”);

}

}

El siguiente es el equivalente ensamblador del código anterior. Esto lo genera OllyDbg cuando se carga el ejecutable.

Bucles

Los bucles se utilizan en programación para ejecutar tareas repetitivas. Se utilizan para ejecutar un bloque de declaraciones repetidamente hasta que una condición determinada devuelve falso. Los bucles “For” tienen la siguiente sintaxis:

para (inicialización, condición, incremento/decremento)

{

//código que se ejecutará hasta que falle la condición.

}

El siguiente programa es un ejemplo de un bucle “for” en C.

#incluir stdio.h

vacío principal()

{

ent yo;

para (i=0; i7; i++){

printf(“el valor de a es %dn”, i);

}

}

El siguiente es el equivalente ensamblador del código anterior. Esto lo genera OllyDbg cuando se carga el ejecutable.

Los bucles “While” son otro concepto comúnmente utilizado en programación y tienen un propósito similar al de los bucles for. Se utilizan para ejecutar un bloque de declaraciones repetidamente hasta que falla una condición determinada. Los bucles “While” tienen la siguiente sintaxis.

mientras (condición) {

//código a ejecutar hasta que la condición falle

}

El siguiente es un ejemplo de un bucle while en C.

#incluir stdio.h

vacío principal()

{

int i=0;

mientras(yo7){

printf(“el valor de a es %dn”, i);

yo ++;

}

}

Al desensamblar muestras de malware, uno debería poder identificar estos bucles for y while, ya que son comunes en el malware.

El siguiente es el equivalente ensamblador del código anterior. Esto lo genera OllyDbg cuando se carga el ejecutable.

Cambiar declaraciones

Otro concepto comúnmente utilizado en C es la declaración de cambio. Las declaraciones de cambio se pueden usar para escribir múltiples bloques de código, que se escriben en etiquetas de casos y ejecutan uno de ellos. Esto se hace evaluando una expresión y comparando el resultado con los valores de cada etiqueta de caso.

La siguiente es la sintaxis de las declaraciones de cambio en C.

cambiar (expresión)

{

valor de caso 1:

//sentencias a ejecutar

romper;

valor de caso 2:

//sentencias a ejecutar

romper;

valor de caso 3:

//sentencias a ejecutar

romper;

por defecto:

//sentencias a ejecutar

romper;

}

El siguiente código es un ejemplo de cómo se pueden utilizar las declaraciones de cambio en la programación en C.

#incluir stdio.h

vacío principal()

{

int yo = 3;

cambiar(yo)

{

caso 1: printf(“El valor ingresado es 1n”);

romper;

caso 2: printf(“El valor ingresado es 2n”);

romper;

caso 3: printf(“El valor ingresado es 3n”);

romper;

predeterminado: printf(“Valor fuera del rango”);

}

}

El siguiente es el equivalente ensamblador del código anterior. Esto lo genera OllyDbg cuando se carga el ejecutable.

Matrices y estructuras

Los programadores utilizan matrices y estructuras para almacenar varios elementos. Ambos operan de manera similar, pero las matrices se usan para almacenar elementos del mismo tipo, mientras que las estructuras pueden tener elementos de diferentes tipos.

Los autores de malware pueden utilizar matrices y estructuras en su código y es importante poder identificar estas construcciones de código al examinar el desmontaje de un ejecutable.

El siguiente fragmento es un ejemplo de cómo se pueden utilizar las matrices en la programación en C.

#incluir stdio.h

int arreglo[] = {5,8,7,1};

vacío principal()

{

ent yo;

para(i=0; i4; i++){

printf(“el valor de la matriz es %dn”, arr[i]);

}

}

El siguiente fragmento es un ejemplo de cómo se pueden utilizar estructuras en la programación en C.

#incluir stdio.h

#incluir cadena.h

coche de estructura

{

identificación interna;

marca de carbón[10];

};

vacío principal()

{

entrada de coche de estructura = {0};

entrada.id = 1;

strcpy(entrada.marca, “Audi”);

printf(“La marca es %sn”, entrada.marca);

}

Apilar y amontonar

Es importante tener en cuenta cómo se utilizan la pila y el montón. La pila se utiliza para la asignación de memoria estática y el montón se utiliza para la asignación de memoria dinámica. También es importante comprender que la pila se utiliza cuando se realizan llamadas a funciones. Los argumentos de la función y las variables locales se colocan en la pila antes de ejecutar la definición de la función.

Conclusión

El malware de ingeniería inversa requiere que los analistas comprendan cómo el código C en ensamblaje se vincula con las instrucciones de la máquina. Necesitan una base de conocimiento que incluya el propósito y las capacidades de las construcciones de código C como variables y declaraciones “si”, declaraciones de cambio, matrices, estructuras, listas enlazadas, pilas, montones y, en particular, “para” y “mientras”. Los bucles son comúnmente utilizados por el malware.

Este artículo proporciona una descripción general de los conceptos de programación en código C junto con fragmentos de ejemplo de codificación. Independientemente del nivel de habilidad, el conocimiento y la comprensión del código C en ensamblaje resultarán fundamentalmente útiles tanto para estudiantes como para profesionales en su análisis de código continuo.

Fuentes

  1. Brian W. Kernighan y Dennis M. Ritchie, “Lenguaje de programación C, segunda edición”, Prentice Hall, abril de 1988
  2. Michael Sikorski y Andrew Honig, “Practical Malware Analysis”, No Starch Press, febrero de
  3. Ingeniería inversa para principiantes , Dennis Yurichev