Joven hacker sonriendo

Hackeamos su software

cero falsos positivos

Inteligencia experta + tecnología especializada

Analizar Bytecode

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la programación segura en Java al analizar el bytecode para determinar una adecuada optimización en la concatenación de Strings, ésto permite una reducción en los tiempos de compilación del programa.

Necesidad

Analizar bytecode para determinar una adecuada optimización en la concatenación de Strings en Java.

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación con Java 7 o anterior.

Solución

El bytecode son los tipos de instrucciones que la máquina virtual de Java espera recibir, para posteriormente ser compiladas a lenguaje de maquina mediante un compilador JIT a la hora o momento de su ejecución.

Entonces, al compilar una aplicación Java, en realidad lo que estamos haciendo es generar instrucciones para la JVM (Java Virtual Machine).

Por otra parte, es posible hacer el desensamblado del bytecode para determinar, entre otras cosas, que tipo de optimizaciones está utilizando el compilador de Java.

  1. Inicialmente se comprueba la versión de Java instalada en el entorno de desarrollo.

    version.bash
    1
    2
    3
    4
    5
    6
    7
    8
    % java -version
    java version "1.7.0_147-icedtea"
    OpenJDK Runtime Environment (IcedTea7 2.0) (7~b147-2.0-0ubuntu0.11.10.1)
    OpenJDK Client VM (build 21.0-b17, mixed mode, sharing)
    % javac -version
    javac 1.7.0_147
    % javap -version
    1.7.0_147
    
  2. A continuación, tendremos dos códigos para comparar. El primero de ellos será Main.java en el cual, se usa la clase StringBuilder para la concatenación del String.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    class Main {
    
     public static void main(String args []) {
       String strPrueba="Fluid-Attacks-";
       String tmpString = new String ();
       StringBuilder strBuilder = new StringBuilder ();
       int numIteraciones=5000;
       for (int i=0; i < numIteraciones ;i++){
         strBuilder.append (strPrueba);
       }
       tmpString = strBuilder.toString();
       System.out.println(tmpString.length());
      }
    }
    
  3. Compilamos y ejecutamos.

    1
    2
    3
    % javac Main.java
    % java Main
    95000
    
  4. El segundo código será el mismo programa anterior, pero, en este, se cambiará el uso de StringBuilder por el operador de concatenación +=. Esto con el fin de determinar que optimizaciones hace la máquina virtual de Java.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    class MainUsandoMas {
       public static void main(String args []) {
         String strPrueba="Fluid-Attacks-";
         String tmpString = new String ();
         int numIteraciones=5000;
         for (int i=0; i < numIteraciones ;i++){
           tmpString += strPrueba;
         }
         System.out.println(tmpString.length());
       }
    }
    
  5. Se compila y ejecuta los programas para comprobar que el resultado es el mismo:

    1
    2
    3
    % javac MainUsandoMas.java
    % java MainUsandoMas
    95000
    
  6. Para desensamblar el código se utilizará la herramienta javap con el argumento c. Primero desensamblamos Main, a continuación se muestra un extracto de las instrucciones mostrando el ciclo.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    javap -c Main
    Compiled from "Main.java"
    class Main {
     Main();
     Code:
     27: iload 5
     29: iload 4
     31: if_icmpge 46
     34: aload_3
     35: aload_1
     36: invokevirtual #7
     // Method java/lang/StringBuilder.append:
     //(Ljava/lang/String;)Ljava/lang/StringBuilder;
     39: pop
     40: iinc 5, 1
     43: goto 27
    }
    
  7. Ahora desensamblamos MainUsandoMas. a continuación se muestra un extracto de las instrucciones que muestran en donde ser realiza el ciclo.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    javap -c MainUsandoMas
    Compiled from "MainUsandoMas.java"
    class MainUsandoMas {
     MainUsandoMas();
     Code:
     18: iload 4
     20: iload_3
     21: if_icmpge 49
     24: new #5
     // class java/lang/StringBuilder
     27: dup
     28: invokespecial #6
     // Method java/lang/StringBuilder."<init>":()V
     31: aload_2
     32: invokevirtual #7
     // Method java/lang/StringBuilder.append:
     //(Ljava/lang/String;)Ljava/lang/StringBuilder;
     35: aload_1
     36: invokevirtual #7
     // Method java/lang/StringBuilder.append:
     //(Ljava/lang/String;)Ljava/lang/StringBuilder;
     39: invokevirtual #8
     // Method java/lang/StringBuilder.toString:
     //()Ljava/lang/String;
     42: astore_2
     43: iinc 4, 1
     46: goto 18
    ...
    }
    
  8. Como conclusiones después de analizar las instrucciones de JVM de ambos programas encontramos que, al compilar, efectivamente, se hace una optimización del operador += para utilizar StringBuilder en vez de instanciar String múltiples veces. Sin embargo, no se hace del modo más óptimo, debido a que en cada iteración se instancia de nuevo a StringBuilder y usa el método toString cada vez que es llamado.

  9. Debe tenerse en cuenta que la optimización durante la compilación es una característica opcional la cual dependerá de una implementación específica. Citando a [1], en el capítulo "15.18.1 String Concatenation Operator +": "To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression".




Haz un comentario

Estado de los servicios - Términos de Uso