Proyecto ayuda...Ofuscacion de codigo

que tal bueno.. como ando de vacaciones me propuse a programar algo que considero un poco complejo al menos para mi y necesito orientacion si alguien tiene idea o ya lo ha echo antes.

el proyecto como dice el titulo es hacer un programa que ofusque codigo en java. echo en java.. simple tomar un archivo.java y generar otro.java ofuscado..

las herramientas que dispongo y lo que e echo es..

programe una clase que me hace un cifrado con el metodo cesar (http://es.wikipedia.org/wiki/Cifrado_César)
en base a eso pense hacer codigo java ofuscado que tome el codigo fuente lo cifre o lo devuelva en una sola linea.. ejemplo

public class Prueba
{
}

y la salida cifrada sea con 4 desplazamientos

public class tvyife { }

en un archivo de texto tendre las palabras reservadas public int class etc.. que se leeran cada vez tengo un metodo que hace que si
lee del codigo fuente una palabra verifica si es reservada dentro del archivo. si lo es pasa directamente tal como es y si no se va al metodo cifrar obtengo el cifrado y la "escribe"

eso ya lo tengo controlado, investigue mas y tendria que hacer programas que analisen
lexico
sintactico
semantico

por esos analisadores creoq dandole puedo programarlos.. mi duda va sobre esto

el caso de por ejemplo el metodo System.out.println("hoola")
eso alguien sabe con que podria analizarlo? obviamente no es una palabra reservada no podria meter todas esas cosas en el archivo de palaras se haria muy grande imaginen todos los posibles de System.* y demas metodos de java

lo que se me ocurre es de alguna forma saber o analizar los archivos de las clases estandar java
tener en otro archivo los nombres de clases analizarlos tambien y saber que si ejemplo
me encuentro con un System que busque dentro del archivo System y vea si existe out si es asi busque y vea si esta println
por podria ser que se escribiera mal y en lugar de System.out.* sea System.outt.* seria error de sintaxis no?

y despues de las comillas decirle que no cifre nada porq serian mensajes lo e pesando de esa forma

digamos que queremos imprimir una variable ejemplo..

int numero = 2;

mi programa sabria ya que este bien echo que int no se cifrara numero si y despues de un = no se cifra tampoco

quedando como

int ryqivs = 2;

y si queremos imprimir

System.out.println(numero);

entonses el programa haria esto System.out.println() eso no se cifra
y no encontro comilas dentro del parentesis entons se cifra lo que esta dentro el nuevo codigo ofuscado quedaria

int ryqivs = 2;

System.out.println(ryqivs);

con lo que la variable ryqivs se imprime y muestra 2

como el codigo original..

bueno hasta aqui con algunas dudas si alguien me quiere ayudar en esto estaria muy bien..
o si ay alguien que me oriente y sepa donde y que buscar

es un proyecto personal que se me ocurrio si se termina pues lo publico aqui para que alguien mas haga uso de ello

la clase que cifra textos la estoy puliendo con algunas cosas luego la subo para que se la bajen..

salu2

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Te podría servir de ejemplo

Te podría servir de ejemplo usar ver como hace el trabajo un ofuscador. Por ejemplo: http://www.zelix.com/klassmaster/ es muy bueno.

Al menos para que veas como debería de ser lo que produzcas.

No debes de ofuscar nada que sea publico, ni protegido, por que si lo hicieras romperías con tus clientes.

El problema no es minúsculo, por que tienes que parsear el código Java para poder tenerlo en un AST y después cargarlo dinámicamente para determinar si lo que estás viendo lo puedes ofuscar o no ( como en el caso de System.out.println )

Existen ya gramaticas que parsean todo Java y lo hacen muy correctamente. La onda es si quieres aventarte el rollo aprender a usarlos.

Otra opción ( que es la que yo haría ) es para empezar, tratar de identificar todas las variables de tu código, y los métodos privados, línea por línea.

Esto sería un tanto más fácil, si no eres tan estricto.

Por ejemplo, todas las variables tienen en la misma linea un Tipo nombre = valorInicial ; y en los atributos como solo deberías de ofuscar los privados, todos tendrían private Tipo nombre [ = valorInicial opcional ];, algo similar pasa con los métodos: private Tipo nombre( parametros ) {

Te digo que funciona si no eres tan estricto, por que en Java lo siguiente es válido:

private
String
name
=
"The name"
;

Entonces, si tratas el código linea por linea puedes utilizar expresiones regulares.

Este es un ejemplo que solamente reemplaza las variables.

El código se ve apantallador, pero es por que tiene muchos comentarios, es realmente sencillo de entender.

package demo;

import java.util.regex.*;
import java.io.*;
import java.util.*;
import static java.lang.System.out;

class IdentificaCodigo {
        public static void main( String ... args )  throws IOException  {
               
                // Lease la siguiente expresion regular como:
                Pattern variablesPattern = Pattern.compile(
                                "(\\s*)"+    // cero o más espacios
                                "(\\w+)"+    // seguidos de una palabra de al menos un caracter
                                "\\s+"+      // seguidos de almenos un espacio
                                "(\\w+)"+    // seguidos de troa palabra de almenos un caracter
                                "\\s*"+      // seguidos de cero o más espacios
                                "=(.*);");   // seguidos  de un "igual a whatever" con un un punto y coma = .+ ;
                // osea "   Tipo variable = yadayada ;"

                // Vamos a leer este mismo archivo
                Scanner scanner = new Scanner( new File("IdentificaCodigo.java"));
               
                // Y poner las variables aquí
                List<String> reemplazos = new ArrayList<String>();

                // mientras haya lineas
                while( scanner.hasNextLine() ) {
                        String linea = scanner.nextLine();
                        Matcher elMatcher  = variablesPattern.matcher( linea );
                        // vemos si coincide
                        if( elMatcher.matches() ) {
                                // si coincide, sacamos los siguiente valores
                                // cada parentesis en la expresion regular forma un grupo
                                String espacio        = elMatcher.group(1);
                                String tipo               = elMatcher.group(2);
                                String nombreVariable = elMatcher.group(3);
                                String valorInicial   = elMatcher.group(4);

                                // agregamos la variable encontrada
                                // para reemplazarla después
                                reemplazos.add( nombreVariable );

                                // e imprimimos el valor ofuscado
                                out.printf("%s%s %s=%s;%n", espacio,  tipo, ofusca( nombreVariable ), reemplazaCon( reemplazos, valorInicial ) );
                        } else {
                                // si no lo encontramos, simplemente imprimimos la linea
                                // pero revisando que si se usa una variable en esa linea
                                // se use el valor ofuscado
                                out.println( reemplazaCon( reemplazos, linea ) );
                        }
                }
        }
        // je je mi ofuscamiento es un reverse :P
        private static String ofusca( String que ) {
                return new StringBuilder( que ).reverse().toString();
        }

        // el reemplazo es buscar en todos los encontrados
        // y cambiar la linea
        private static String reemplazaCon( List<String> listaReemplazos, String que ) {
                for( String aReemplazar : listaReemplazos ) {
                        que = que.replaceAll( aReemplazar, ofusca( aReemplazar ));
                }
                return que;
        }
}

Al ejecutar este programa, va a leer su propio código fuente y va a reemplazar las variables.

Este es el resultado de la ejecución:

oreyes@oreyes-laptop:~/code/languages/java$ javac -d . IdentificaCodigo.java
oreyes@oreyes-laptop:~/code/languages/java$ java demo.IdentificaCodigo | tee Salida.java
package demo;

import java.util.regex.*;
import java.io.*;
import java.util.*;
import static java.lang.System.out;

class IdentificaCodigo {
        public static void main( String ... args )  throws IOException  {
               
                // Lease la siguiente expresion regular como:
                Pattern variablesPattern = Pattern.compile(
                                "(\\s*)"+    // cero o más espacios
                                "(\\w+)"+    // seguidos de una palabra de al menos un caracter
                                "\\s+"+      // seguidos de almenos un espacio
                                "(\\w+)"+    // seguidos de troa palabra de almenos un caracter
                                "\\s*"+      // seguidos de cero o más espacios
                                "=(.*);");   // seguidos  de un "igual a whatever" con un un punto y coma = .+ ;
                // osea "   Tipo variable = yadayada ;"

                // Vamos a leer este mismo archivo
                Scanner rennacs= new Scanner( new File("IdentificaCodigo.java"));
               
                // Y poner las variables aquí
                List<String> reemplazos = new ArrayList<String>();

                // mientras haya lineas
                while( rennacs.hasNextLine() ) {
                        String aenil= rennacs.nextLine();
                        Matcher rehctaMle= variablesPattern.matcher( aenil );
                        // vemos si coincide
                        if( rehctaMle.matches() ) {
                                // si coincide, sacamos los siguiente valores
                                // cada parentesis en la expresion regular forma un grupo
                                String oicapse= rehctaMle.group(1);
                                String opit= rehctaMle.group(2);
                                String elbairaVerbmon= rehctaMle.group(3);
                                String laicinIrolav= rehctaMle.group(4);

                                // agregamos la variable encontrada
                                // para reemplazarla después
                                reemplazos.add( elbairaVerbmon );

                                // e imprimimos el valor ofuscado
                                out.printf("%s%s %s=%s;%n", oicapse,  opit, ofusca( elbairaVerbmon ), reemplazaCon( reemplazos, laicinIrolav ) );
                        } else {
                                // si no lo encontramos, simplemente imprimimos la aenil
                                // pero revisando que si se usa una variable en esa aenil
                                // se use el valor ofuscado
                                out.println( reemplazaCon( reemplazos, aenil ) );
                        }
                }
        }
        // je je mi ofuscamiento es un reverse :P
        private static String ofusca( String que ) {
                return new StringBuilder( que ).reverse().toString();
        }

        // el reemplazo es buscar en todos los encontrados
        // y cambiar la aenil
        private static String reemplazaCon( List<String> listaReemplazos, String que ) {
                for( String aReemplazar : listaReemplazos ) {
                        que = que.replaceAll( aReemplazar, ofusca( aReemplazar ));
                }
                return que;
        }
}
oreyes@oreyes-laptop:~/code/languages/java$ javac Salida
oreyes@oreyes-laptop:~/code/languages/java$

Como ves el resultado compila perfectamente, por que sigue siendo código Java válido.

Ahora el chiste sería encontrar las expresiones regulares para, atributos, metodos, strings, enteros,, etc. etc. etc todo lo que quieras ofuscar. Meterlo en una estructura de datos más compleja ( como maps, listas etc. etc ) y después reemplazarlos.

Obvio esto es mucha talacha, pero es lo que precisamente los parseadores hacen bien, sacar tokens y simplificarte la vida, por ejemplo, mi código no detecto la variable "variablesPattern" por que no terminaba en punto y coma (;) ni tampoco la variable "reemplazos" por que tenía por ahí unos piquitos ( &ltString> ) que le hacían quedar fuera de la expresion.

Este es un ejemplo nada más, en la realidad esta es una tarea bastante compleja.

En fin, espero que esto te de alguna idea.

Saludos.

Imagen de ezamudio

Sobre fuente?

Los ofuscadores reales trabajan sobre el bytecode por varias razones: primero, es un hecho que el código al menos compiló bien. Segundo, es más fácil hacer el análisis de cobertura de código y las dependencias, etc (ver dónde hay llamadas a un método o referencias a una variable), porque vas a tener que cambiar el nombre del método "metodo()" por "aaaa()" no sólo en la declaración sino en todos lados donde se llame. También el nombre de la clase y las variables.

Imagen de genitalico

q ondas gracias OscarRyz por

q ondas gracias OscarRyz por la respuesta en breve me pongo analizar todo ese codigo que es musho xD...y planteo mis dudas por lo primero sobre ser estricto con el codigo en tu ejemplo de

private
String
name
=
"The name"
;

programe que en cuanto se encuentre un espacio salto de linea lo tome como una palabra con eso me libro de ser estricto y ya probe y funciona y tomando como palabra todo eso pues empezaria por ver si es reservada o no y ya.. ahora tengo un problema y es que no puedo detectar las tabulaciones
digamos

private String

pues bueno no me lee el String por que no es espacio si no tabulador, pense que por estar como un espacio o algo asi me detectaria pero tengo problemas con eso ya intente mi programa recorre letra por letra y guarda en un char[] todo hasta que haya un espacio, salto de linea y tabulador o comillas etc
con eso hago despues un string y guardo la palabra

bueno el problema es que en mi char[]
puedo hacer
if(cha[i]==(char)32) es un espacio
pero nu pedo hacer que verifique
if(char[i]=='\t')

me recomendaron poner
if(char[i]=="\\t")

pero no funciona como puedo checar que hay tabulacion porq ahi falla mi programa.

empiezo de esta manera habro el archivo mediante la entrada estandar

y tomo linea por linea
con readLine.
despues ese String lo convierto a un array de caracteres y ahi hago toda esa verficacion de separar las palabras al encontrarse en el arreglo un espacio etc..

esa es mi duda ahorita..

por otro lado tiene razon ezamudio no habia pensado en eso al cifrar los metodos y cambiarlos se deberian llamar del mismo modo. pero creoq no habria problema si todo el proyecto por asi decirlo se ofusca con mi programa ya que asi haria los mismos cifrados sobre las palabras con lo que los metodos se cambiarian con el mismo nombre.

esa es una ahora pensando en lo que se refiere a los println y demas metodos que expuse arriba. pense separar mi programa en dos.
ofuscar solamente la parte algoritmica con lo que no habria problema ya que solo en esos codigos habrian metodo y y variables y nada mas..
el problema seria cmo dijo ezamudio cambiar nombre en la parte de implementacion..
podria solucionarlo haciendo de alguna forma que tome el codigo de algoritmo ofucado lo decifre en un paso previo a la compilacion con lo que estaria como se escribio originalmente y compilar y borrar ese archivo decifrado..

pero ya seria meterme en otros rollos.

ahora lo de ofuscar ya en los bycodes? me entro la duda como se puede hacer eso?

salu2

Pues hace sentido totalmente

Pues hace sentido totalmente trabajar sobre el bytecode en vez de trabajar sobre el código fuente. Claro los retos ahí son diferentes a bajo nivel ( leer en el formato del bytecode en vez de strings planos por ejemplo ) aunque a alto nivel es lo mismo. Tienes que renombrar nombres, procurando no romper a los clientes que tengas , por ejemplo si lo que ofuscaras fuera una biblioteca para ser usada desde otra aplicación, si le cambiarás el nombre a una clase o método, ya no te podrían llamar, aunque internamente sustituyeras todas las llamadas.

También es interesante ofuscar el flujo, y no solo los nombres. Por ejemplo de sitio de Zelix ClassMaster viene este ejemplo:

Original:

package test;

import java.util.*;

class Demo {

   private Vector buffer = new Vector();

   /**
   * Return the position of the specified String in the
   * buffer. Remove the String once if it has been found.
   * Return -1 if the String isn't found. */

   int getStringPos(String string) {
      for(int counter=0; counter < buffer.size(); counter++) {
         String curString = (String)buffer.elementAt(counter);
         if (curString.equals(string)) {
            buffer.remove(counter);
            return counter;
         }
      }
      return -1;
   }
}

Y después:

package a;

import java.util.Vector;

class a {

    private Vector a;
    public static int b;

    a() {
        a = new Vector();
    }

    int a(String s) {
        int i;
        int j;
        j = b;
        i = 0;
        if(j == 0) goto _L2; else goto _L1
_L1:
        String s1 = (String)a.elementAt(i);
        s1.equals(s);
        if(j != 0) goto _L4; else goto _L3
_L3:
        JVM INSTR ifeq 48;
           goto _L5 _L6
_L5:
        break MISSING_BLOCK_LABEL_37;
_L6:
        continue;
        a.remove(i);
        i;
_L4:
        return;
_L2:
        if(i >= a.size())
            return -1;
        if(true) goto _L1; else goto _L7
_L7:
    }
}

Sobre tu problema con el tabulador, esta raro, pero definitivamente no es con '\\t'

Imagen de genitalico

lo siento pero m perdi un

lo siento pero m perdi un poco con mis post empezare a revisar loq pusieron y si tengo dudas las posteoo salu2