Ofuscación de código.

La ofuscación de código se refiere al acto de escribir código que oculte una intención o alguna
funcionalidad a los ojos de otros programadores.
Vos te preguntaras ¿Para que es necesario ofuscar código? bueno, podría responderte que es muy útil cuando queres evitar la ingeniería inversa o cuando tu código queda expuesto al cliente, por ejemplo en el caso de JavaScript, y así poder evitar plagio.

Investigando he encontrado diversas técnicas para ofuscar código. Algunos hacen abuso del preprocesador de C, otros usan las palabras reservadas del lenguaje (con un guion bajo adelante) para definir variables o funciones y otros sencillamente tratan de engañarnos haciéndonos creer que esa porción de código hace algo totalmente diferente (nombrando de una manera inapropiada funciones y variables). Por ejemplo, si queres imprimir algo por pantalla puedo renombrar la funcion printf por la función fork. Creo que esto se entendería mejor con un breve ejemplo de mi autoría.

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4
 5 #define padding NULL
 6 #define block(a,c) a##mem_d==padding? c:;
 7 #define pad(a,b,c,d,e,f) _(a,b,c,d,e,f)
 8 #define fork(j,p) j##p()
 9 #define mmap(a) #a
10 #define _(d,an,fi,g,h,kk) f##h##fi##f(d##g, mmap(%s), fork(an, kk));
11
12 char* dismsg(void);
13
14 int main(int _int, char **_char)
15 {
16         int nt, hol, pri;
17         FILE *std = NULL;
18         void *mem_d = calloc(1024,1);
19         void *shmem_d = NULL;
20         char mun;
21
22
23         pad(std, dis,nt, out, pri, msg);
24 }
25
26 char* dismsg(void)
27 {
28         return (char *)"hola mundo\n";
29 }

Si compilamos este fuente de la siguiente manera "gcc ofusque.c -o ofusque" y ejecutamos el binario vemos que muestra un muy querido "Hola mundo". Ahora, ¿como logro mostrar un mensaje sin siquiera usar un printf o alguna función similar de biblioteca? Es la magia de la ofuscación de código!!!.

Una muy curiosa pagina para empezar a ver tecnicas de ofuscacion es http://www.ioccc.org/. Estos chicos hacen un concurso donde premian el mejor codigo ofuscado.


Encriptacion en linux (cbc_crypt)

Para encriptar buffers en linux he usado la función llamada cbc_crypt que, como veremos en la manpage de esta función, encripta datos en message-digest algorithm 5.
           Aunque el algoritmo md5 no es muy fuerte (*) y de hecho el mundo se esta inclinando hacia otros algoritmos de encriptacion como SHA1 o Blowfish, la comodidad de no depender de una librería externa, con los riesgos de portabilidad que esto trae, me han hecho inclinar la balanza hacia MD5.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timeb.h>
#include <rpc/des_crypt.h>
#include <assert.h>

#define H(a) (a ^ 0x87)

#define __(a,b,c)\
{\
        int rest;\
        b = (strnlen(c, 1024) + 1);\
        rest = b % 8;\
        if(rest > 0) {\
                rest = b / 8;\
                b = 8 * (rest + 1);\
        }\
\
        a=(char*)calloc(1,b);\
        strncpy(a, c, b);\
}

#define ___ cbc_crypt
#define padding __

static char IV[8];
static char yek_[] = { H('u'), H('n'), H('a'), H('c'), H('l'), H('a'), H('v'), H('e') };

void i_(void)
{
        for(int i=0;i<8;i++) {
                IV[i] = 1;
        }

      des_setparity(yek_);

}

void e(char *buff, int len)
{
       int r = ___(yek_, buff, len, DES_ENCRYPT|DES_SW, IV);
       // fprintf(stderr,"%d:%d:%d:%d <%d>\n", DESERR_NONE, DESERR_NOHWDEVICE, DESERR_HWERROR, DESERR_BADPARAM, r);
}

void d(char *buff, int len)
{
       int r=  ___(yek_, buff, len, DES_DECRYPT|DES_SW, IV);
       //fprintf(stderr,"%d:%d:%d:%d <%d>\n", DESERR_NONE, DESERR_NOHWDEVICE, DESERR_HWERROR, DESERR_BADPARAM, r);
}

int main(int argc, char **argv)
{
       int data_len;
       char *data = NULL;

       if(argc != 2) {

               return -1;
       }

      data_len = strnlen(argv[1], 1024);

      padding(data, data_len, argv[1]);

      //fprintf(stderr,"data: %s\n", data);

      i_();
      e(data, data_len);

      fprintf(stderr,"Dato encriptado: ");

      for(int i=0;i<data_len;i++)
      {
              fprintf(stderr,"%c", data[i]);
      }
      fprintf(stderr,"\n");

      i_();

      d(data, data_len);

      fprintf(stderr,"Dato desencriptado: ");

      for(int i=0;i<data_len;i++)
      {
              fprintf(stderr,"%c", data[i]);
      }

      fprintf(stderr,"\n");


      return 0;

}


Como ven, la macro H es una sencilla forma de esconder la clave en la seccion .data (datos locales) del programa.
           No hay que olvidar que esta función encripta por bloques, en este caso, MD5 tiene un cipher-block chaining de 8 bytes, asi que vendría bien una mirada a la macro padding (__). 

(*) A pesar de haber sido considerado criptográficamente seguro en un principio, ciertas investigaciones han revelado vulnerabilidades que hacen cuestionable el uso futuro del MD5. En agosto de 2004, Xiaoyun Wang, Dengguo Feng, Xuejia Lai y Hongbo Yu anunciaron el descubrimiento de colisiones de hash para MD5. Su ataque se consumó en una hora de cálculo con un clúster IBM P690.
Fuente: http://es.wikipedia.org/wiki/MD5#Seguridad