Java bytecode to class file

Java Class File

A Java class file is a file containing Java bytecode and having .class extension that can be executed by JVM. A Java class file is created by a Java compiler from .java files as a result of successful compilation. As we know that a single Java programming language source file (or we can say .java file) may contain one class or more than one class. So if a .java file has more than one class then each class will compile into a separate class files.
For Example: Save this below code as Test.java on your system.

For Compiling:

After compilation there will be 3 class files in corresponding folder named as:

A single class file structure contains attributes that describe a class file.
Representation of Class File Structure

Elements of class file are as follows:

  1. magic_number: The first 4 bytes of class file are termed as magic_number. This is a predefined value which the JVM use to identify whether the .class file is generated by valid compiler or not. The predefined value will be in hexadecimal form i.e. 0xCAFEBABE.
    Now let’s see what happen when JVM will not find valid magic number. Suppose we have a .java file named as Sample.java as follows and follow step by step process on your system.
// class Declaration class Sample < public static void main(String[] args) < System.out.println("Magic Number"); >>

Step 1: Compile using javac Sample.java
Step 2: Now open the Sample.class file. It will looks like following.

Читайте также:  Hello World

Sample.class File

Runtime Exception Due to Invalid magic number

Step 3: Now erase at least single symbol from this Sample.class file from starting of file and save it.
Step 4: Now try to run this using java Sample command and see the magic i.e. you will get run time exception (See the highlighted text in below image):

Note: This can vary depending on how much you remove the .class file data.

Note: Lower version compiler generated .class file can be executed by high version JVM but higher version compiler generated .class file cannot be executed by lower version JVM. If we will try to execute we will get run time exception.

This demonstration is for Windows OS as follows:

Step 1: Open a command prompt window and try to check java compiler version and JVM version using following commands respectively (Highlighted text in image are the commands)
Output for 1.8 version will be:
Java Compiler Version
JVM Version

Step 2: Now check with another version which may be higher or lower than already installed.thisDownload link.
And install this to your PC or laptops and note the installation address.
Step 3: Open a second command prompt window and set the path of bin folder of installed jdk installed during 2nd step. And check for Java compiler version ad JVM version.
Checking Version of JDK 1.6
Step 4: Now on 1st command prompt compile the any valid .java file. For example: See above Sample.java file. Compile it as:
Compiling with Compiler Version 1.8
Step 5: Now on 2nd command prompt window try to run the above compiled code class file and see what happen. There is a run time exception which I have highlighted in below image.

Runtime Exception Due to Invalid major and minor Version of class file

Note: Internally jdk 1.5 version means 49.0 and 1.6 means 50.0 and 1.7 means 51.0 etc. class file version where the digits before the decimal point represent the major_version and digits after decimal point represents the minor_version.

  1. constant_pool_count: It represents the number of the constants present in the constant pool (When a Java file is compiled, all references to variables and methods are stored in the class’s constant pool as a symbolic reference).
  2. constant_pool[]: It represents the information about constants present in constant pool file.
  3. access_flags: It provide the information about the modifiers which are declared to the class file.
  4. this_class: It represents fully qualified name of the class file.
  5. super_class: It represents fully qualified name of the immediate super class of current class. Consider above Sample.java file. When we will compile it, then we can say this_class will be Sample class and super_class will be Object class.
  6. interface_count: It returns the number of interfaces implemented by current class file.
  7. interface[]: It returns interfaces information implemented by current class file.
  8. fields_count: It represents the number of fields (static variable) present in current class file.
  9. fields[]: It represent fields (static variable) information present in current class file.
  10. method_count: It represents number of methods present in current class file.
  11. method[]: It returns information about all methods present in current class file.
  12. attributes_count: It returns the number of attributes (instance variables) present in current class file.
  13. attributes[]: It provides information about all attributes present in current class file.

Источник

VENISHJOE.NET

It’s 5:50 a.m. Do you know where your stack pointer is ?

Java Bytecode Manipulation

In this article, I will show how to manipulate a compiled class file directly without decompiling it to java.

I will be using Javassist (Java Programming Assistant), an external library for most of this tutorial. Download latest JAR file to get examples work. I am using version rel_3_22_0_cr1-4-g6a3ed31.

Every java file compiled will generate a class file which is a binary file containing Java bytecode which can be executed on any Java Virtual Machine. Since the class files are generally not dependent on the platform they are compiled on, it makes Java applications platform independent. In this article, we will explore how to statically analyze class files, modify them programmatically and execute.

Sample Class for Bytecode Manipulation

We will start with a simple test class (ByteCodeEditorTest) which we will use to modify using Javassist. This class file will get an input from user and check if it matches a predefined value within code and output message accordingly.

public String checkStatus(String _inputString)

Once compiled, and executed below is a sample behaviour of the class. We will modify compiled class file directly to change its behaviour by modifying equality operator.

$ java ByteCodeEditorTest TEST Wrong $ java ByteCodeEditorTest MAGIC Right! 

Let’s start by looking at the compiled class file using javap. I have provided snippet of checkStatus() method from test class.

$ javap -c ByteCodeEditorTest Compiled from "ByteCodeEditorTest.java" public java.lang.String checkStatus(java.lang.String); Code: 0: aload_1 1: ldc #7 // String MAGIC 3: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 6: ifeq 12 9: ldc #9 // String Right! 11: areturn 12: ldc #10 // String Wrong 14: areturn > 

The disassembled code contains mnemonic for Java bytecode instructions. We will be heavily using these as a part of bytecode manipulation. Refer to Java bytecode instruction listings Wikipedia article which contains all mnemonic and Opcode for Java bytecode.

Interesting line is on index 6 from disassembled code which contains mnemonic ifeq which compares input string against built in value. Let’s use Javassist to modify equality operator from ifeq to ifne.

Bytecode Manipulation using Javassist

Now that we have our test class and details on what has to be modified in bytecode, let’s create a new class file which loads compiled ByteCodeEditorTest class for manipulation. With Javassist JAR in classpath, let’s load the test class file using javassist.CtClass.

ClassPool _classPool = ClassPool.getDefault(); CtClass _ctClass = _classPool.makeClass(new FileInputStream("ByteCodeEditorTest.class")); 

Once ByteCodeEditorTest class is loaded, we will use javassist.CtMethod to extract all the methods from class and then use javassist.bytecode.CodeAttribute & javassist.bytecode.CodeIterator to manipulate the class.

CodeIterator allows us to traverse every bytecode instruction from class file and also provides methods to manipulate them. In our case, from the javap output we know index 6 has to modified to change instruction set from ifeq to ifne. Looking at Opcode reference, hex value for ifne is 9a. We will be using decimal format to update bytecode using CodeIterator.

So we will be using CodeIterator.writeByte() method to update index 6 of ByteCodeEditorTest from exising value to 154 (9a converted to decimal). Below table shows existing value (row1) and new value (row2)

Mnemonic Opcode (Hex) Opcode (Decimal)
ifeq 0x99 153
ifne 0x9a 154
for(CtMethod _ctMethods:_ctClass.getDeclaredMethods()) < CodeAttribute _codeAttribute = _ctMethods.getMethodInfo().getCodeAttribute(); CodeIterator _codeIterator = _codeAttribute.iterator(); while (_codeIterator.hasNext()) < int _indexOfCode = _codeIterator.next(); int _valueOfIndex8Bit = _codeIterator.byteAt(_indexOfCode); //Checking index 6 and if Opcode is ifeq if(_valueOfIndex8Bit==153 && _indexOfCode==6) < //Changing instruction from ifeq to ifne _codeIterator.writeByte(154, _indexOfCode); >> > //Write changes to class file _ctClass.writeFile(); 

Once this code is run, ByteCodeEditorTest class file will be modified with updated instructions. When running javap on ByteCodeEditorTest now, it will produce below result of checkStatus() method.

$ javap -c ByteCodeEditorTest Compiled from "ByteCodeEditorTest.java" public java.lang.String checkStatus(java.lang.String); Code: 0: aload_1 1: ldc #7 // String MAGIC 3: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 6: ifne 12 9: ldc #9 // String Right! 11: areturn 12: ldc #10 // String Wrong 14: areturn > 

As you can see, index 6 is now changed to ifne. Running ByteCodeEditorTest now will produce results which we were after.

$ java ByteCodeEditorTest TEST Right! 

ByteCodeEditorTest class file was successfully modified to alter program flow without the need for re-compilation or decompilation.

While this is a simple modification to a class file, we can do complex changes of adding new methods, classes, injecting code etc. using Javassist library. I will cover complex scenarios in another article, but will give a high level overview of frequently used in APIs in next section.

Other Javassist APIs

While I covered bytecode manipulation, Javassist is a powerful library which can be used for complex changes. Highlighting some of those features here.

javassist.CtMethod class can be used to inject new methods to existing class files.

//Defrosts so that the class can be modified _ctClass.defrost(); CtMethod _ctMethod = CtNewMethod.make("public int newMethodFromJA() < return 1; >", _ctClass); _ctClass.writeFile(); 

javassist.CtMethod class can also be used to inject code to existing class/methods using insertBefore(), insertAfter() and insertAt() methods.

for(CtMethod method:_ctClass.getDeclaredMethods()) < //Defrosts so that the class can be modified _ctClass.defrost(); method.insertBefore("System.out.println(\"Before every method call. \");"); _ctClass.writeFile(); >

Javassist can also be used for static analysis of class files by displaying all method code (disassembled) of a class file or to display bytecode of a class file.

//Display Method Code PrintStream _printStream = new PrintStream(System.out); InstructionPrinter instructionPrinter = new InstructionPrinter(_printStream); for(CtMethod method:_ctClass.getDeclaredMethods()) < System.out.println("Method: " + method.getName()); instructionPrinter.print(method); >//Display Bytecode for(CtMethod _ctMethods:_ctClass.getDeclaredMethods()) < _ctClass.defrost(); System.out.println("Method: " +_ctMethods.getName()); CodeAttribute _codeAttribute = _ctMethods.getMethodInfo().getCodeAttribute(); CodeIterator _codeIterator = _codeAttribute.iterator(); while (_codeIterator.hasNext()) < int _indexOfInstruction = _codeIterator.next(); int _indexValue8Bit = _codeIterator.byteAt(_indexOfInstruction); System.out.println(Mnemonic.OPCODE[_indexValue8Bit]); >> 

Full source code for all snippets referenced in this article is available in my github page.

I have the ability to arrange 1’s and 0’s in such an order that an x86 processor can actually interpret and execute those commands. I make the world a better place by writing mindless back-end programs that no-one will ever see nor even know that it’s there. But I know; and that’s all that matters. -Alucard

Источник

Java View Bytecode of a Class File

Many times, we need to understand what a compiler is doing under the hood. How the java statements we are writing, will be reordered and executed. Also, we need to see the byte code for learning purposes also, I do it seldom. In this tutorial, I am giving an example of how to generate the byte code for a class file in java.

To demonstrate the example, I am using the java file created for my other tutorial related to automatic resource management in java 7.

1. Compile Java File using the command javac

This is optional because you might have the .class file already.

prompt > javac C://temp/java/test/ResourceManagementInJava7.java

This will generate the .class file ResourceManagementInJava7.class.

2. Execute javap Command and Redirect Output to .bc File

C:>javap -c C://temp/java/test/ResourceManagementInJava7.class > C://temp/java/test/bytecode.bc

Folder view

Let’s look at the command run on the prompt.

java_byte_code_javap_command_window

A file bytecode.bc file will be generated at a given location. It will be something like this:

public class com.howtodoinjava.java7.tryCatch.ResourceManagementInJava7 < public com.howtodoinjava.java7.tryCatch.ResourceManagementInJava7(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/io/BufferedReader 3: dup 4: new #3 // class java/io/FileReader 7: dup 8: ldc #4 // String C:/temp/test1.txt 10: invokespecial #5 // Method java/io/FileReader."":(Ljava/lang/String;)V 13: invokespecial #6 // Method java/io/BufferedReader."":(Ljava/io/Reader;)V 16: astore_1 17: aconst_null 18: astore_2 19: new #2 // class java/io/BufferedReader 22: dup 23: new #3 // class java/io/FileReader 26: dup 27: ldc #7 // String C:/temp/test2.txt 29: invokespecial #5 // Method java/io/FileReader."":(Ljava/lang/String;)V 32: invokespecial #6 // Method java/io/BufferedReader."":(Ljava/io/Reader;)V 35: astore_3 36: aconst_null 37: astore 4 39: new #2 // class java/io/BufferedReader 42: dup 43: new #3 // class java/io/FileReader 46: dup 47: ldc #8 // String C:/temp/test3.txt 49: invokespecial #5 // Method java/io/FileReader."":(Ljava/lang/String;)V 52: invokespecial #6 // Method java/io/BufferedReader."":(Ljava/io/Reader;)V 55: astore 5 57: aconst_null 58: astore 6 60: aload 5 62: ifnull 138 65: aload 6 67: ifnull 90 70: aload 5 72: invokevirtual #9 // Method java/io/BufferedReader.close:()V 75: goto 138 78: astore 7 80: aload 6 82: aload 7 84: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 87: goto 138 90: aload 5 92: invokevirtual #9 // Method java/io/BufferedReader.close:()V 95: goto 138 98: astore 8 100: aload 5 102: ifnull 135 105: aload 6 107: ifnull 130 110: aload 5 112: invokevirtual #9 // Method java/io/BufferedReader.close:()V 115: goto 135 118: astore 9 120: aload 6 122: aload 9 124: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 127: goto 135 130: aload 5 132: invokevirtual #9 // Method java/io/BufferedReader.close:()V 135: aload 8 137: athrow 138: aload_3 139: ifnull 219 142: aload 4 144: ifnull 166 147: aload_3 148: invokevirtual #9 // Method java/io/BufferedReader.close:()V 151: goto 219 154: astore 5 156: aload 4 158: aload 5 160: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 163: goto 219 166: aload_3 167: invokevirtual #9 // Method java/io/BufferedReader.close:()V 170: goto 219 173: astore 5 175: aload 5 177: astore 4 179: aload 5 181: athrow 182: astore 10 184: aload_3 185: ifnull 216 188: aload 4 190: ifnull 212 193: aload_3 194: invokevirtual #9 // Method java/io/BufferedReader.close:()V 197: goto 216 200: astore 11 202: aload 4 204: aload 11 206: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 209: goto 216 212: aload_3 213: invokevirtual #9 // Method java/io/BufferedReader.close:()V 216: aload 10 218: athrow 219: aload_1 220: ifnull 290 223: aload_2 224: ifnull 243 227: aload_1 228: invokevirtual #9 // Method java/io/BufferedReader.close:()V 231: goto 290 234: astore_3 235: aload_2 236: aload_3 237: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 240: goto 290 243: aload_1 244: invokevirtual #9 // Method java/io/BufferedReader.close:()V 247: goto 290 250: astore_3 251: aload_3 252: astore_2 253: aload_3 254: athrow 255: astore 12 257: aload_1 258: ifnull 287 261: aload_2 262: ifnull 283 265: aload_1 266: invokevirtual #9 // Method java/io/BufferedReader.close:()V 269: goto 287 272: astore 13 274: aload_2 275: aload 13 277: invokevirtual #11 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 280: goto 287 283: aload_1 284: invokevirtual #9 // Method java/io/BufferedReader.close:()V 287: aload 12 289: athrow 290: goto 298 293: astore_1 294: aload_1 295: invokevirtual #13 // Method java/io/IOException.printStackTrace:()V 298: return Exception table: from to target type 70 75 78 Class java/lang/Throwable 110 115 118 Class java/lang/Throwable 98 100 98 any 147 151 154 Class java/lang/Throwable 39 138 173 Class java/lang/Throwable 39 138 182 any 193 197 200 Class java/lang/Throwable 173 184 182 any 227 231 234 Class java/lang/Throwable 19 219 250 Class java/lang/Throwable 19 219 255 any 265 269 272 Class java/lang/Throwable 250 257 255 any 0 290 293 Class java/io/IOException >

Источник

Оцените статью