Offuschiamo il bytecode Java per divertimento: prima parte

Se non sapevate che il bytecode Java, quello che c’è nei vostri file .class è semplice da mettere in chiaro, bhé allora sapevatelo ora: è semplice da disassemblare.
Se volete proteggere i vostri file .class, dovete usare un tool di obfuscation, ad esempio proguard, oppure rivolgervi a strumenti commerciali.
Oppure potete giocare con la libreria javassist.
In questo post faremo un offuscamento del codice molto leggero, andremo a rinominare metodi, attributi della classe originale, che rinomineremo essa stessa. Andremo inoltre ad iniettare del codice dummy all’interno del nostro sorgente da proteggere.
Quello che voglio ottenere è un secondo file ,class, avente lo stesso comportamento della classe di partenza, ma dall’aspetto interiore completamente diverso.
Deve essere chiaro che la tecnica di renaming e di code injection, alza l’asticella per scremare gli script kiddies. Puoi alzare l’asticella quanto vuoi, andando ad iniettare più codice, ma un reverser vero impiegherà solo più tempo a capire cosa fa la tua classe. L’obiettivo è rendere questo processo non vantaggioso in termini di effort.
La vittima
Il file che vogliamo procedere è la pià classica delle classi. Un main, che chiama un metodo che restituisce la stringa “Hello Wordl!” e che rende la nostra giornata migliore, dopo aver visto il nostro interprete java, salutarci calorosamente.
Il codice della classe HelloWorld, è il seguente:
public class HelloWorld {
public final static String sayHello() {
return "Hello World!";
}
public static void main(String[] arg) {
System.out.println(sayHello());
}
}
Una volta compilato, il codice si presenta così:
00000000 ca fe ba be 00 00 00 34 00 21 0a 00 07 00 12 08 |.......4.!......|
00000010 00 13 09 00 14 00 15 0a 00 06 00 16 0a 00 17 00 |................|
00000020 18 07 00 19 07 00 1a 01 00 06 3c 69 6e 69 74 3e |..........<init>|
00000030 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f |...()V...Code...|
00000040 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 |LineNumberTable.|
00000050 00 08 73 61 79 48 65 6c 6c 6f 01 00 14 28 29 4c |..sayHello...()L|
00000060 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 |java/lang/String|
00000070 3b 01 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 |;...main...([Lja|
00000080 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 |va/lang/String;)|
00000090 56 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 |V...SourceFile..|
000000a0 0f 48 65 6c 6c 6f 57 6f 72 6c 64 2e 6a 61 76 61 |.HelloWorld.java|
000000b0 0c 00 08 00 09 01 00 0c 48 65 6c 6c 6f 20 57 6f |........Hello Wo|
000000c0 72 6c 64 21 07 00 1b 0c 00 1c 00 1d 0c 00 0c 00 |rld!............|
000000d0 0d 07 00 1e 0c 00 1f 00 20 01 00 0a 48 65 6c 6c |........ ...Hell|
000000e0 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61 |oWorld...java/la|
000000f0 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61 |ng/Object...java|
00000100 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f |/lang/System...o|
00000110 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 |ut...Ljava/io/Pr|
00000120 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76 |intStream;...jav|
00000130 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d |a/io/PrintStream|
00000140 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a |...println...(Lj|
00000150 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b |ava/lang/String;|
00000160 29 56 00 21 00 06 00 07 00 00 00 00 00 03 00 01 |)V.!............|
00000170 00 08 00 09 00 01 00 0a 00 00 00 1d 00 01 00 01 |................|
00000180 00 00 00 05 2a b7 00 01 b1 00 00 00 01 00 0b 00 |....*...........|
00000190 00 00 06 00 01 00 00 00 01 00 19 00 0c 00 0d 00 |................|
000001a0 01 00 0a 00 00 00 1b 00 01 00 00 00 00 00 03 12 |................|
000001b0 02 b0 00 00 00 01 00 0b 00 00 00 06 00 01 00 00 |................|
000001c0 00 03 00 09 00 0e 00 0f 00 01 00 0a 00 00 00 26 |...............&|
000001d0 00 02 00 01 00 00 00 0a b2 00 03 b8 00 04 b6 00 |................|
000001e0 05 b1 00 00 00 01 00 0b 00 00 00 0a 00 02 00 00 |................|
000001f0 00 07 00 09 00 08 00 01 00 10 00 00 00 02 00 11 |................|
00000200
Usando il disassembler, fornito dal JDK, abbiamo già un bel po’ di informazioni:
$ javap HelloWorld
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
public static final java.lang.String sayHello();
public static void main(java.lang.String[]);
}
A questo punto, anche un reverser, non troppo esperto può capire da cosa è composto questo codice, cosa fa e mettere in chiaro il grande segreto dietro il metodo sayHello().
Mescolatina di carte
Il nostro aiutante di oggi è javassist, una libreria per la manipolazione dei file .class. L’ho scoperta mentre facevo un po’ di scouting per il progetto Owasp Orizon.
Ho pensato mi potesse venire utile, sia per fare introspezione dei metodi, sia per giocare un po’ a guardia e ladri con il byecode java.
Primo obiettivo è modificare il metodo sayHello() che contiene tutta la nostra logica applicativa.
Per prima cosa, devo andare a caricare la classe java che voglio modificare. Deve essere nel mio class path, quindi devo ricordarmi di includere il ‘.’ al parametro ‘-cp’ quando andrò ad eseguire il mio offuscatore. Pena, una ClassNotFoundException.
javassist mi aiuta in questo e mi permette di andare a recuperare, tra i metodi dichiarati nella classe, il metodo che voglio modificare, sayHello, nel mio caso.
ClassPool classPool=ClassPool.getDefault();
CtClass ctClass = classPool.get(HelloWorld.class.getName());
...
CtMethod method = ctClass.getDeclaredMethod("sayHello");
...
method.setName(UUID.randomUUID().toString());
method.insertBefore("boolean ret=false; if (ret) { System.err.println(\"This is so foo\"); } for (int i=0; i==0; i++) { int a=0; a+=1; }");
method.insertAfter("int key=1231213; if (key==1231213) { int z=0; } else { for (int y=0; y<=10; y++) { System.err.println(\"I'm using key: \" + createSecretKey() ) ; }}");
...
CodeConverter cc = new CodeConverter();
cc.redirectMethodCall("sayHello", method);
ctClass.instrument(cc);
Una volta recuperato il metodo, mi diverto a giocarci un po’. Cambio il nome usando un uuid ed aggiungo in testa ed in coda al metodo un po’ di codice.
Se prestate attenzione, ho aggiunto del codice che non fa assolutamente niente. L’unico mio scopo è quello di rendere più grosso il metodo sayHello().
Non contento, aggiungo alla classe un attributo ed un paio di metodi connessi tra di loro e connessi al codice iniettato nel metodo sayHello() originale. Questo punto è importante, altrimenti il compilatore, cercando di ottimizzare,
avrebbe tolto il codice non necessario.
...
CtMethod nM1 = CtNewMethod.make("public static abstract String a();", ctClass);
ctClass.addMethod(nM1);
nM1.setBody(" { String key = new String(); key = \"jj\"; return key; } ");
CtMethod nM2 = CtNewMethod.make("public static String createSecretKey(); ", ctClass);
ctClass.addMethod(nM2);
nM2.setBody("{ String key = new String(); key = \"a great string in the sky\"; a(); return key; }");
...
CtField f = new CtField(CtClass.intType, UUID.randomUUID().toString(), ctClass);
ctClass.addField(f);
f = new CtField(CtClass.floatType, UUID.randomUUID().toString(), ctClass);
ctClass.addField(f);
f = new CtField(CtClass.booleanType, UUID.randomUUID().toString(), ctClass);
ctClass.addField(f);
...
ctClass.writeFile("./output");
L’ultima riga, crea un file .class, contenente le nostre modifiche in una directory output. Il file ottenuto ha lo stesso comportamento del file di partenza, pesa il triplo ed ha al suo interno nomi non parlanti e codice completamente inutile.
00000000 ca fe ba be 00 00 00 34 00 48 01 00 24 66 33 61 |.......4.H..$f3a|
00000010 62 63 66 32 35 2d 62 30 65 31 2d 34 30 63 34 2d |bcf25-b0e1-40c4-|
00000020 38 35 63 66 2d 61 61 34 35 62 38 66 30 61 36 65 |85cf-aa45b8f0a6e|
00000030 32 07 00 01 01 00 10 6a 61 76 61 2f 6c 61 6e 67 |2......java/lang|
00000040 2f 4f 62 6a 65 63 74 07 00 03 01 00 06 3c 69 6e |/Object......<in|
00000050 69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 |it>...()V...Code|
00000060 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 |...LineNumberTab|
00000070 6c 65 0c 00 05 00 06 0a 00 04 00 09 01 00 24 61 |le............$a|
00000080 63 62 37 39 38 39 36 2d 31 63 30 66 2d 34 65 39 |cb79896-1c0f-4e9|
00000090 66 2d 39 63 37 38 2d 63 30 62 33 33 31 34 33 39 |f-9c78-c0b331439|
000000a0 32 65 34 01 00 14 28 29 4c 6a 61 76 61 2f 6c 61 |2e4...()Ljava/la|
000000b0 6e 67 2f 53 74 72 69 6e 67 3b 01 00 10 6a 61 76 |ng/String;...jav|
000000c0 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 07 00 0d |a/lang/String...|
000000d0 01 00 0d 53 74 61 63 6b 4d 61 70 54 61 62 6c 65 |...StackMapTable|
000000e0 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 |...java/lang/Sys|
000000f0 74 65 6d 07 00 10 01 00 03 65 72 72 01 00 15 4c |tem......err...L|
00000100 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 |java/io/PrintStr|
00000110 65 61 6d 3b 0c 00 12 00 13 09 00 11 00 14 01 00 |eam;............|
00000120 0e 54 68 69 73 20 69 73 20 73 6f 20 66 6f 6f 08 |.This is so foo.|
00000130 00 16 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72 69 |.....java/io/Pri|
00000140 6e 74 53 74 72 65 61 6d 07 00 18 01 00 07 70 72 |ntStream......pr|
00000150 69 6e 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c |intln...(Ljava/l|
00000160 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 0c 00 1a |ang/String;)V...|
00000170 00 1b 0a 00 19 00 1c 01 00 0c 48 65 6c 6c 6f 20 |..........Hello |
00000180 57 6f 72 6c 64 21 08 00 1e 03 00 12 c9 6d 01 00 |World!.......m..|
00000190 16 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e |.java/lang/Strin|
000001a0 67 42 75 66 66 65 72 07 00 21 0a 00 22 00 09 01 |gBuffer..!.."...|
000001b0 00 0f 49 27 6d 20 75 73 69 6e 67 20 6b 65 79 3a |..I'm using key:|
000001c0 20 08 00 24 01 00 06 61 70 70 65 6e 64 01 00 2c | ..$...append..,|
000001d0 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 |(Ljava/lang/Stri|
000001e0 6e 67 3b 29 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 |ng;)Ljava/lang/S|
000001f0 74 72 69 6e 67 42 75 66 66 65 72 3b 0c 00 26 00 |tringBuffer;..&.|
00000200 27 0a 00 22 00 28 01 00 0f 63 72 65 61 74 65 53 |'..".(...createS|
00000210 65 63 72 65 74 4b 65 79 0c 00 2a 00 0c 0a 00 02 |ecretKey..*.....|
00000220 00 2b 01 00 08 74 6f 53 74 72 69 6e 67 0c 00 2d |.+...toString..-|
00000230 00 0c 0a 00 22 00 2e 01 00 04 6d 61 69 6e 01 00 |....".....main..|
00000240 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 |.([Ljava/lang/St|
00000250 72 69 6e 67 3b 29 56 01 00 03 6f 75 74 0c 00 32 |ring;)V...out..2|
00000260 00 13 09 00 11 00 33 0c 00 0b 00 0c 0a 00 02 00 |......3.........|
00000270 35 01 00 24 38 33 31 39 63 66 31 63 2d 36 39 38 |5..$8319cf1c-698|
00000280 63 2d 34 61 34 32 2d 38 63 38 63 2d 61 66 35 30 |c-4a42-8c8c-af50|
00000290 38 64 31 39 36 62 62 62 0a 00 0e 00 09 01 00 02 |8d196bbb........|
000002a0 6a 6a 08 00 39 01 00 24 63 30 64 38 66 64 33 30 |jj..9..$c0d8fd30|
000002b0 2d 30 31 64 62 2d 34 39 62 39 2d 38 66 63 61 2d |-01db-49b9-8fca-|
000002c0 64 64 36 33 30 32 39 62 64 37 34 38 01 00 19 61 |dd63029bd748...a|
000002d0 20 67 72 65 61 74 20 73 74 72 69 6e 67 20 69 6e | great string in|
000002e0 20 74 68 65 20 73 6b 79 08 00 3c 0c 00 37 00 0c | the sky..<..7..|
000002f0 0a 00 02 00 3e 01 00 24 34 32 35 62 31 38 61 63 |....>..$425b18ac|
00000300 2d 65 39 64 32 2d 34 64 66 65 2d 39 37 66 63 2d |-e9d2-4dfe-97fc-|
00000310 30 66 38 30 34 37 31 65 33 38 66 31 01 00 01 49 |0f80471e38f1...I|
00000320 01 00 24 63 66 34 66 38 30 39 66 2d 30 32 63 61 |..$cf4f809f-02ca|
00000330 2d 34 65 36 66 2d 39 64 65 63 2d 39 61 36 66 36 |-4e6f-9dec-9a6f6|
00000340 35 35 39 64 38 33 34 01 00 01 46 01 00 24 35 64 |559d834...F..$5d|
00000350 34 35 32 61 66 39 2d 37 65 64 30 2d 34 30 30 63 |452af9-7ed0-400c|
00000360 2d 38 31 30 39 2d 37 64 64 66 62 36 31 31 65 30 |-8109-7ddfb611e0|
00000370 35 36 01 00 01 5a 01 00 0a 53 6f 75 72 63 65 46 |56...Z...SourceF|
00000380 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64 |ile...HelloWorld|
00000390 2e 6a 61 76 61 04 21 00 02 00 04 00 00 00 03 00 |.java.!.........|
000003a0 00 00 40 00 41 00 00 00 00 00 42 00 43 00 00 00 |..@.A.....B.C...|
000003b0 00 00 44 00 45 00 00 00 05 00 01 00 05 00 06 00 |..D.E...........|
000003c0 01 00 07 00 00 00 1d 00 01 00 01 00 00 00 05 2a |...............*|
000003d0 b7 00 0a b1 00 00 00 01 00 08 00 00 00 06 00 01 |................|
000003e0 00 00 00 01 00 19 00 0b 00 0c 00 01 00 07 00 00 |................|
000003f0 00 ab 00 05 00 08 00 00 00 67 03 3b 1a 99 00 0b |.........g.;....|
00000400 b2 00 15 12 17 b6 00 1d 03 3c 1b 03 a0 00 0f 03 |.........<......|
00000410 3d 1c 04 60 3d 84 01 01 a7 ff f2 12 1f a7 00 03 |=..`=...........|
00000420 3a 04 12 20 36 05 15 05 12 20 a0 00 09 03 36 06 |:.. 6.... ....6.|
00000430 a7 00 2e 03 36 07 15 07 10 0a a3 00 24 b2 00 15 |....6.......$...|
00000440 bb 00 22 59 b7 00 23 12 25 b6 00 29 b8 00 2c b6 |.."Y..#.%..)..,.|
00000450 00 29 b6 00 2f b6 00 1d 84 07 01 a7 ff db 19 04 |.)../...........|
00000460 b0 00 00 00 02 00 08 00 00 00 06 00 01 00 21 00 |..............!.|
00000470 03 00 0f 00 00 00 26 00 07 fc 00 0e 01 fc 00 01 |......&.........|
00000480 01 10 44 07 00 0e ff 00 12 00 06 01 01 00 00 07 |..D.............|
00000490 00 0e 01 00 00 fd 00 02 00 01 f9 00 27 00 09 00 |............'...|
000004a0 30 00 31 00 01 00 07 00 00 00 26 00 02 00 01 00 |0.1.......&.....|
000004b0 00 00 0a b2 00 34 b8 00 36 b6 00 1d b1 00 00 00 |.....4..6.......|
000004c0 01 00 08 00 00 00 0a 00 02 00 00 00 07 00 09 00 |................|
000004d0 08 00 09 00 37 00 0c 00 01 00 07 00 00 00 19 00 |....7...........|
000004e0 02 00 01 00 00 00 0d bb 00 0e 59 b7 00 38 4b 12 |..........Y..8K.|
000004f0 3a 4b 2a b0 00 00 00 00 00 09 00 3b 00 0c 00 01 |:K*........;....|
00000500 00 07 00 00 00 1d 00 02 00 01 00 00 00 11 bb 00 |................|
00000510 0e 59 b7 00 38 4b 12 3d 4b b8 00 3f 57 2a b0 00 |.Y..8K.=K..?W*..|
00000520 00 00 00 00 01 00 46 00 00 00 02 00 47 |......F.....G|
0000052d
Javap lo vede in questo modo:
Compiled from "HelloWorld.java"
public abstract class f3abcf25-b0e1-40c4-85cf-aa45b8f0a6e2 {
int 425b18ac-e9d2-4dfe-97fc-0f80471e38f1;
float cf4f809f-02ca-4e6f-9dec-9a6f6559d834;
boolean 5d452af9-7ed0-400c-8109-7ddfb611e056;
public f3abcf25-b0e1-40c4-85cf-aa45b8f0a6e2();
public static final java.lang.String acb79896-1c0f-4e9f-9c78-c0b3314392e4();
public static void main(java.lang.String[]);
public static java.lang.String 8319cf1c-698c-4a42-8c8c-af508d196bbb();
public static java.lang.String c0d8fd30-01db-49b9-8fca-dd63029bd748();
}
Off by one
Chiaramente, quello presentato in questo esempio, non è un vero offuscatore di codice. Però implementa i primi stage dell’arte dell offuscamento.
Da qui possiamo iniziare un viaggio, nel creare confusione ed entropia nel nostro bytecode per rendere, si spera il più possibile non conveniente farne il reverse.
Enjoy it!
Vuoi aiutarmi a portare avanti il progetto Codice Insicuro con una donazione? Fantastico, allora non ti basta che premere il pulsante qui sotto.
Supporta il progetto