12.07.2015 Views

DANUT RUSU Protection Methods of Java Bytecode

DANUT RUSU Protection Methods of Java Bytecode

DANUT RUSU Protection Methods of Java Bytecode

SHOW MORE
SHOW LESS
  • No tags were found...

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Protection</strong> <strong>Methods</strong> <strong>of</strong> <strong>Java</strong> <strong>Bytecode</strong>Dănuţ RusuMathematical Analysis Department"Al. I. Cuza" University, Iaşi, Romaniae-mail: drusu@uaic.roAbstractThe paper presents some practical techniques <strong>of</strong><strong>Java</strong> bytecode’s protection by means <strong>of</strong> an“aggressive” mode <strong>of</strong> obfuscation. These methodshave been successfully tested against the bestexistent decompilers.1. IntroductionSince its <strong>of</strong>ficial introduction in 1995, <strong>Java</strong> hasbeen widely adopted more quickly than any otherprogramming language and it has become one <strong>of</strong> themost popular development platforms. Because <strong>of</strong> itsvery wide utilization in Internet, <strong>Java</strong> security ismore important than ever. It is important for the webusers, developers, system administrators, e-commerce makers, etc.One <strong>of</strong> the attack’s forms is the decompiling. Forexample, by means <strong>of</strong> decompiling, illegal code canbe placed in legitimate applets, which aresubsequently recompiled and passed <strong>of</strong>f as theoriginal.Unfortunately, dependable methods <strong>of</strong> protectionagainst decompiling do not exist.One mode <strong>of</strong> partial securing against decompiling isthe obfuscation. That is to say, transforming the <strong>Java</strong>bytecode by renaming the identifiers <strong>of</strong> the classes,fields and methods, removing the debugginginformation, encrypting the string literals,rearrangements, etc, aiming at making the resultantsource code much more difficult to read. The classfile is passed through the obfuscator and out comes amodified version whose useful information havebeen removed and any useful names changed tosomething syntactically correct but hopefullyconfusing to the programmer looking at thedecompiled code.In this paper we give some practical methods <strong>of</strong>“aggressive” obfuscation, successfully tested againstthe best existent decompilers. Thus, by means <strong>of</strong>replacing <strong>of</strong> control structures’ JVM instructionswith other instructions, the decompilers are not ableto recognize them, and consequently, they willgenerate incomprehensible code. We have mainlyused JCD (<strong>Java</strong> Class Decompiler - Disassembler,Decompiler, Obfuscator and Simulator), a programwritten by the author <strong>of</strong> this article and JAD, byPavel Kouznetsov. The latter is the fastest existentdecompiler. For a unitary presentation, we willinclude these methods into an obfuscating algorithm.2. Low-mode obfuscationBy low-mode obfuscation we understand themodifying <strong>of</strong> the class files by renaming theidentifiers, removing the debugging information andencrypting the String literals.2.1. Removing the debugging informationThe class files have a big amount <strong>of</strong> information,which are useful for debugging but unnecessary inthe execution time. This information is contained inLineNumberTable and LocalVariableTableattributes, both being attributes <strong>of</strong> Code attribute.LocalVariableTable attribute supplies the name,type and domain <strong>of</strong> each local variable and all theseare helpful in the decompiling process. Removing <strong>of</strong>these attributes does not affect the bytecode validityand can be made with the algorithm:for(int k = methods_count; k >= 1; k--){goto method[k].Code_ Attribute;int length = Code_Attribute.Bytes_Count;length - = 8 + Code_Count + 2;length - = Exception_Table_Length*8 + 2;goto Code's_Attributes;delete length bytes;Code's_Attributes_Count = 0;Code_Attribute.Bytes_Count - = length;}2.2. Renaming the identifiersConstant_Pool is scanned and entries <strong>of</strong> the typeCONSTANT_Fieldref and CONSTANT_Methodrefare identified. The algorithm renames their namesaccording to the rule: a, b,…, aa, ab, …, ba, bb,…These combinations can be also used: aA, aB, etc2.3. Encrypting the String literals214


By means <strong>of</strong> a hex editor anyone can read andmodify the String literals <strong>of</strong> <strong>Java</strong> class files. If forthe appropriate CONSTANT_Utf8 entry, the fields“length” is modified, the length <strong>of</strong> these literals canbe also changed. A solution against this type <strong>of</strong>attack is the encrypting. Algorithm <strong>of</strong> insertion andencrypting must execute the following steps:1. Makes a random string with a specifiedminimum length (special characters andUnicode characters can be used). This stringis the key used by the encrypting anddecrypting functions.2. Gives a name to decrypting function,according to the algorithm <strong>of</strong> methods’rename. We suppose that the decryptingfunction is named dd.3. Searches CONSTANT_String entries intoConstant_Pool and encrypts theCONSTANT_Utf8 entries’ content addressedby these.4. Makes the following transforms in thebytecode:• increments suiting Constant_Pool_Count,• writes in Constant_Pool all encryptedmessage and increments/decrements, if isnecessary, CONSTANT_Utf8.length,• appends to Constant_Pool supplementaryentries with the name and signature <strong>of</strong>decrypting method, names and signatures<strong>of</strong> invoked methods by that, used literals,• Code attribute <strong>of</strong> each method whichcontains an encrypted message, mustsupport the following modifying:Bytes_Count and Code_Count areincremented with 3*N (where N is theencrypted messages’ number <strong>of</strong> themethod), Max_Stack is incremented with1 and, for each encrypted message, aninvokestatic instruction (which invokesthe dd method and its parameter is anindex at CONSTANT_Methodref entryappended to Constant_Pool), is insertedafter the ldc instruction’s code whichloads the encrypted message,• appends to bytecode the dd’s code, afterthe last method,• increments <strong>Methods</strong>_Count with 1.Now, we suppose that the random string is:String key = ".\rT\3752>l:h\u1212";and, we also suppose that the encrypting method is:String encrypt (String s) {char[] ac = s.toCharArray();char[] ac1 =key.toCharArray();for(int k = 0; k < ac.length; k++)ac[k] ^= ac1[k % ac1.length];return new String(ac);}If we apply encrypt to the string “<strong>Java</strong> is a portablelanguage”, we obtain the following encoded string:dl\"\234\022W\037\032\t\u1232^b&\211S\\\000_H\u127eOc3\210SY\t. If we apply encrypt to theencoded string, we obtain the initial string.encrypt’s inverse function is also encrypt.If the decrypting method’s name is dd, we mustinsert the code <strong>of</strong> the following method into theobfuscated bytecode:static String dd (String s) {char[] ac = s.toCharArray();char[] ac1 = null;ac1 =".\rT\3752>l:h\u1212".toCharArray();for(int k = 0; k < ac.length; k++)ac[k] ^= ac1[k % ac1.length];return new String(ac);}Let be n = Constant_Pool_Count and u, v, w, x, yare indexes at Constant_Pool such that:CONSTANT_Class Entry (u) (..)(reference at this class)CONSTANT_Utf8 Entry (v) CONSTANT_Utf8 Entry (w) CodeCONSTANT_Class Entry (x) (y)CONSTANT_Utf8 Entry (y) java/lang/StringWe look for u, v, w, x into Constant_Pool.if (x == 0) {x = n; y = x + 1; n++; flag = true;}Add at Constant_Pool the following entries:if(flag) {Constant_Pool_Count += 15;CONSTANT_Class Entry (x) (x+1)bytes: 07 (short)(x+1)(reference at String Class)CONSTANT_Utf8 Entry (x+1) java/lang/Stringbytes: 01 0010 6A61 7661 2F6C 616E 672F5374 7269 6E67(fully qualified form <strong>of</strong> String Class)} else Constant_Pool_Count += 13;CONSTANT_Methodref Entry (n) Class (u)Name/Type (n+1)bytes: 0A (short)u (short)(n+1)(reference at dd method from this Class)CONSTANT_NameAndType Entry (n+1) Name(n+2) Type (n+3)bytes: 0C (short)(n+2) (short)(n+3)(name and type <strong>of</strong> dd method)CONSTANT_Utf8 Entry (n+2) ddbytes: 0100 0264 64(decrypting method’s name)CONSTANT_Utf8 Entry (n+3)(Ljava/lang/String;)Ljava/lang/String;215


ytes: 01 0026 284C 6A61 7661 2F6C 616E672F 5374 7269 6E67 3B29 4C6A 6176 612F6C61 6E67 2F53 7472 696E 673B 0100 0A536F75 7263 6546 696C 65(decrypting method’s descriptor)CONSTANT_Methodref Entry (n+4) Class (x)Name/Type (n+5)bytes: 0A (short)x (short)(n+5)(reference at toCharArray from String Class)CONSTANT_NameAndType Entry (n+5) Name(n+6) Type (n+7)bytes: 0C (short)(n+6) (short)(n+7)(name and type <strong>of</strong> toCharArray method)CONSTANT_Utf8 Entry (n+6) toCharArraybytes: 01 000B 746F 4368 6172 4172 7261 79(toCharArray name)CONSTANT_Utf8 Entry (n+7) ()[Cbytes: 01 0004 2829 5B43(toCharArray descriptor)CONSTANT_String Entry (n+8) (n+9)bytes: 08 (short)(n+9)(reference at the encrypting key)CONSTANT_Utf8 Entry (n+9) .\rT\3752>l:h\u1212bytes: 01 000D 2E0D 54C3 BD32 3E6C 3A68E188 92(encrypting key)CONSTANT_Methodref Entry (n+10) Class (x)Name/Type (n+11)bytes: 0A (short)x (short)(n+11)(reference at the String(char[]) constructor)CONSTANT_NameAndType Entry (n+11) Name(b) Type (n+12)bytes: 0C (short)v (short)(n+12)(name and type <strong>of</strong> String(char[]) constructor)CONSTANT_Utf8 Entry (n+12) ([C)Vbytes: 0100 0528 5B43 2956(descriptor <strong>of</strong> String(char[]) constructor)for(int k = 1; k


if(!flag1){flag1 = true; maxs++;maxl++;}branch<strong>of</strong>fset += the count <strong>of</strong> theinserted bytes between i+3 andi+3+branch<strong>of</strong>fset;int n = flag2?maxl-2:maxl-1, h = 1;if(n > 4){inserts two bytes at i+3; count+=2;Byte[i] = 21; // iload nByte[i+1] = n; h = 2;}else{inserts one byte at i+3; count++;Byte[i] = 26+n; // iload_n}Byte[i+h] = 153; // ifeq branch<strong>of</strong>fsetwrite branch<strong>of</strong>fset into Byte[i+h+1]and Byte[i+h+2];continue;}// if instructionsif(tag is between 153 and 166 ||tag == 198 || tag == 199){if(branch<strong>of</strong>fset > 0){//this block inserts a sequence <strong>of</strong> the//following type before the current//instruction:// iload_(maxl-1)// ifne (with a branch<strong>of</strong>fset > 0)if(!flag){flag = true; <strong>of</strong>fset = i;continue;}if(!flag1){flag1 = true; maxs++;maxl++;}int n = flag2?maxl-2:maxl-1, h = 1;branch<strong>of</strong>fset = <strong>of</strong>fset - i;if(n > 4){inserts 5 bytes at i; count += 5;Byte[i] = 21; // iload nByte[i+1] = n;h = 2; <strong>of</strong>fset = i-5;}else{inserts 4 bytes at i; count += 4;Byte[i] = 26+n; // iload_n<strong>of</strong>fset = i-4;}Byte[i+h] = 154; // ifne branch<strong>of</strong>fsetwrite branch<strong>of</strong>fset into Byte[i+h+1]and Byte[i+h+2];continue;}else{//this block is similar with above//blocks and inserts a sequence <strong>of</strong> the//following type after the current//instruction:// iload_(maxl-1)// ifne <strong>of</strong>fset//where branch<strong>of</strong>fset < <strong>of</strong>fset < -8}}// tableswitch and lookupswitchif(tag == 170 || tag == 171){}if(!flag1){flag1 = true; maxs++;maxl++;}if(!flag2){flag2 = true; maxs++;maxl++;}//this block is similar with the above//blocks//it inserts before the current//instruction, between istore and iload,//a sequence <strong>of</strong> the type:// iload_(maxl-2)// iload_(maxl-1)// ifne (branch<strong>of</strong>fset=7)// ifne (branch<strong>of</strong>fset at an instruction//placed after lookupswitch)//inserts between iload and//lookupswitch a sequence <strong>of</strong> the type:// iload_(maxl-1)// ifne (branch<strong>of</strong>fset at an instruction//placed after lookupswitch)}}for each instruction with a negativebranch<strong>of</strong>fset, writes the actual branch<strong>of</strong>fse;Max_Stack = maxs; Max_Locals = maxl;Bytes_Count += count - Code_CountCode_Count = count;3.2. Some testsAll the following codes have been obfuscatedand disassembled by means <strong>of</strong> JCD and decompiledby means <strong>of</strong> JAD.3.2.1. for/while ObfuscationWe consider the method:static void a(){for(int i=0; i< 100;i++) System.out.println(i);}By obfuscating we obtain the following JVM code:0 iconst_03 istore_14 iconst_05 istore_06 iload_1 // goto substitute7 ifeq 2010 getstatic java/lang/System/out Ljava/io/PrintStream;13 iload_014 invokevirtual java/io/PrintStream/println (I)V17 iinc 0 120 iload_021 bipush 10023 if_icmplt 1026 iload_1 // code <strong>of</strong> confusion27 ifne 1730 returnInserted and replaced instructions are in bold-italics.217


This code has been decompiled by means <strong>of</strong> JADthus resulting:static void a(){int i;int j;j = a;i = 0;if(j == 0) goto _L2; else goto _L1_L1:System.out.println(i);_L4:i++;_L2:if(i < 100)continue; /* Loop/switch isn't completed */if(j == 0)return;if(true) goto _L4; else goto _L3_L3:if(true) goto _L1; else goto _L5_L5:}3.2.2. if ObfuscationIf we obfuscate the method:static void a(){int i = (int)(Math.random()*100);if(i


_L13:break MISSING_BLOCK_LABEL_67;_L14:break MISSING_BLOCK_LABEL_80;i;80;_L12:JVM INSTR icmpge 80;goto _L15 _L16_L15:break MISSING_BLOCK_LABEL_73;_L16:break MISSING_BLOCK_LABEL_80;i--;if(j == 0)break MISSING_BLOCK_LABEL_83;i += 10;}3.2.3. switch ObfuscationWe consider the method:static void b(){int i = (int)(Math.random()*100);switch(i){case 1: i += 2; break;case 3: i--; break;default: i += 10;}}By obfuscating we obtain the following JVM code:00000132 Access Flags ACC_STATIC00000134 Name a00000136 Type ()V00000138 Attributes Count 10000013a Attribute Name Code0000013c Bytes Count 9000000140 Max Stack 600000142 Max Locals 300000144 Code Count 780 iconst_03 istore_24 iconst_07 istore_18 invokestatic java/lang/Math/random ()D11 ldc2_w 100.0D14 dmul15 d2i16 istore_017 iload_1 // code <strong>of</strong> confusion18 iload_219 ifne 2622 ifne 6325 iload_026 iload_227 ifne 6030 lookupswitch32 Default = 7436 Pairs Count = 240 Key = 1, Offset = 5648 Key = 3, Offset = 6756 iinc 0 259 iload_1 // goto substitute60 ifeq 7763 iload_2 // code <strong>of</strong> confusion64 ifeq 7767 iinc 0 -170 iload_1 // goto substitute71 ifeq 7774 iinc 0 1077 return00000196 Exception Table Count 000000198 Code's Attributes Count 0JAD decompilaton result is:static void a(){int i;int j;int k;k = b;j = a;i = (int)(Math.random() * 100D);j;if(k != 0) goto _L2; else goto _L1_L1:if(j != 0) goto _L4; else goto _L3_L3:i;_L2:if(k != 0) goto _L6; else goto _L5_L5:JVM INSTR lookupswitch 2: default 74// 1: 56// 3: 67;goto _L7 _L8 _L9_L8:i += 2;j;_L6:JVM INSTR ifeq 77;goto _L4 _L10_L10:break MISSING_BLOCK_LABEL_77;_L4:if(k == 0)break MISSING_BLOCK_LABEL_77;_L9:i--;if(j == 0)break MISSING_BLOCK_LABEL_77;_L7:i += 10;}4. ConclusionsIn all these tests the decompilation result isstrongly different from the original and it is hard tounderstand it.The fact that <strong>Java</strong> programs are portable and areverified before execution, makes obfuscatingtransformations more difficult to apply.There are many practical aspects to be consideredwhen applying obfuscating transformations to <strong>Java</strong>programs. We need to implement more controlobfuscations and more categories <strong>of</strong> transformationsneed to be investigated.In the above algorithm, goto instructions have beenreplaced by if instructions. More complicatedcombinations can be constructed if we usetableswitch and lookupswitch for the jumps. Theseconstructions are harder to decompile. Also,additional obfuscations will require more data flowanalyses to be performed.219


5. References[1] James Gosling, Bill Joy, and Guy Steele, The<strong>Java</strong> TM Language Specification, Addison WesleyLongman, Inc. 1996[2] Pavel Kouznetsov, JAD,http://www.geocities.com/kpdus/jad.html[3] Tim Lindholm, and Frank Yellin, The <strong>Java</strong> TMVirtual Machine Specification, Second Edition, SunMicrosystems, Inc. 1999[4] Godfrey Nolan, Decompiling <strong>Java</strong>, The McGraw- Hill Companies, Inc. 1998[5] Gregory Wroblewski, “General Method <strong>of</strong>Program Code Obfuscation”, Proceedings <strong>of</strong> theInternational Conference on S<strong>of</strong>tware EngineeringResearch and Practice (SERP) 2002, Las Vegas,USA, June 2002, pp. 153-159220

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!