What happens if I manually change the bytecode before running it?

I'm a little curious about what happens if I manually change something into bytecode before execution. For example, suppose you assign a variable of type int to a variable of byte type without casting or delete a semicolon somewhere in the program or something that leads to a compile-time error . As I know, all compile-time errors are checked by the compiler before creating the .class file. So, what happened when I changed the byte code after a successful compilation of the program, and then manually changed the byte code? Is there any mechanism for this? or if not, how does the program behave after execution?

EDIT: -
Like Hot Faces, Darksonn and manouti have already given the correct satisfactory answers. Now I'm just ending for those readers who are all looking for the answer to this question: -

Each Java virtual machine has a class file verifier , which ensures that the loaded class files have a proper internal structure. If the file class verifier detects a problem with the class file, it throws an exception . Since a class file is just a sequence of binary data, the virtual machine cannot know whether a particular class file was generated by a well-designed Java compiler or shadow crackers, prone to compromising the integrity of the virtual machine. As a result, all JVM implementations have a file class verifier that can be called on untrusted classes to make sure the classes are safe to use.

See this for more details.

+5
source share
4 answers

Of course, you can use a hex editor (for example, the free "Hex Editor Neo") or some other tool to change the bytes of the Java.class file. But, obviously, you should do it in such a way as to maintain the file “integrity” (tables in the correct format, etc.). In addition, (and much more complicated), any modification you make must go through the JVM "verifier" compilation, which essentially double-checks everything that javac checked when compiling the program.

The verification process occurs during class loading and is quite complex. Basically, data flow analysis is performed for each procedure to ensure that only the correct data types can "reach" the point at which the data type is assumed. For example, you cannot change the load operation to load a link to a HashMap onto the "stack" when the end user of the loaded link considers it to be its string. (But listing all the verifications made by the verifier would be the most important task. I cannot recall half of them, although I wrote the verifier for the IBM iSeries JVM.)

(If you ask if it is possible to jailbreak a Java.class file to enter code that does unauthorized actions, the answer is no.)

+5
source

Most likely you will get java.lang.VerifyError :

Thrown when the “verifier” discovers that the class file, although well-formed, contains some kind of internal inconsistency or security problem.

+2
source

You can do this, and there are even tools to make it easier, like http://set.ee/jbe/ . Java runtime will run your modified bytecode the same way it will run the bytecode emitted by the compiler. What you are describing is a concrete Java example of a binary patch.

An example with a semicolon will not be a problem, since semicolons are intended only for the convenience of the compiler and are not displayed in bytecode.

+1
source

Either the bytecode is executed normally, or it executes the given commands, or jvm rejects them.

I played with programming directly in the java byte codec some time ago using jasmin , and I noticed some things.

If the bytecode you edited makes sense, it will work as expected. However, there are some bytecode patterns that are rejected with VerifyError.

For a specific example of access outside of access, you can compile code with outside limits just fine. They will bring you an ArrayIndexOutOfBoundsException at runtime.

 int[] arr = new int[20]; for (int i = 0; i < 100; i++) { arr[i] = i; } 

However, you can build bytecode, which is a more fundamental flaw than this. To give an example, I will first explain some things.

The java bytecode works with stack , and the instructions work with the top elements on the stack.
The stack naturally has different sizes in different places in the program, but sometimes you can use goto in the bytecode to make the stack look different depending on how you got there.

The stack can contain object, int , then you store the object in an array of objects and int in an array of int. Then you continue and from somewhere else in this bytecode you use goto, but now your stack contains int, object , which will cause int to be passed to an array of objects and vice versa.

This is just one example of what might happen, making your bytecode fundamentally wrong. The JVM detects these flaws when the class is loaded at run time, and then throws a VerifyError if something does not work.

+1
source

Source: https://habr.com/ru/post/1200251/


All Articles