As explained in my previous post on the subject of my Java class file parasitic infector, I had to tackle three important problems: loading the virus code, locating hosts to infect and infecting a host. The latter required extensive knowledge of the class file format, and I already gave a quick rundown of the solutions to several issues that arose during infection in the previous article. There is, however, one important part that I did not talk about yet: the trigger.
As with my Pascal virus, you may be able to augment an executable file with all sorts of data, without changing its semantics. This is exactly what the Java virus has achieved in the form presented so far, as detailed at the end of my previous article. Let’s recap the virus steps so far:
- Read the host constant pool
- Read the host fields
- Read the host methods
- Write the host constant pool
- Write the viral constant pool, relocating constant pool references
- Write the host fields
- Write the viral fields, relocating constant pool references
- Write the host methods
- Write the viral methods, relocating constant pool references
This yields a host with just an addition of inactive code in it. But it’s useless code. The machinery is there, but there’s nothing to start it. It lacks one important piece: the trigger. We would need to change the old class file’s code and generate code that invokes our inactive code.
So just about in what ways does one hijack a class file? In traditional EXE infectors (to name just the preferred host type) it is customary to overwrite the one entry point into the executable program, therefore running the virus at the very moment the infected host is executed, this is not a hard and fast rule. Some viruses hijack various other parts of the program, such as function entrypoints. There is such a EXE infector that scans a prospective host for snippets of code consistent with C or Pascal function entrypoints and hijacks just such an entrypoint. The downside is that there is no guarantee that the virus will execute all that often, simply because the hijacked portion of code may be in unluckily rare service. The benefits are not immediately noticeable, but there is one advantage stemming from the limitations of traditional anti-virus scanning technology. Historically, because of the bias shown by infectors towards hijacking the entry point of their host, virus scanner tools have been known to have a respective bias towards scanning program entry points. For this reason, a virus that hijacks its host at an unorthodox location stands a better chance against detection by scanners.
Class files, on the other hand, have slightly different semantics. Class files are not executed, but loaded. A class may or may not be initialized. Throughout its lifetime, a class may or may not generate instances. From a functional perspective, class files are finer grained than standard executable files, providing multiple possible entry points. They are more like libraries of reusable codes, more like DLLs or ELF shared object files. The class methods, if any, may or may not execute. Similar considerations apply at the instance level. There is, however, a special method that is guaranteed to execute when the class is initialized. And the virtual machine guarantees that all classes are initialized prior to being used. The method I’m referring to is the class initializer. If a class gets a shot at initialization (and most do), the virtual machine executes this method as part of the initialization. The virtual machine also initializes fields with attached ConstantValue attributes, but that is not of relevance here. There can be at most one static initializer method per class and its name must be <clinit>.
Armed with this knowledge, I altered the infection step as follows. Let Z be the name of a method guaranteed not to be present in either victim or virus.
- During infection, look for the class initializer of the prospective victim.
- If a class initializer is found, rename it to Z.
- Otherwise, create an empty method named Z.
- Create a class initializer that (a) invokes the virus and (b) invokes method Z.
Note that, just before step 4, the prospective victim is in a state where it does not possess a class initializer. Step 4 creates a new class initializer, thus the whole procedure guarantees that the victim remains in a valid state, provided it was in a valid state prior to the procedure.
A second note pertains to Z, the name of the method guaranteed, at the beginning of the procedure, not to be in use by both the victim and the virus. Just what is the process that infers the name Z? The answer is a bit more subtle, Z is the chosen name of a method existent in the original copy of the virus. The victim selection process always chooses victims that do not clash with any viral method, Z included. This condition guarantees that any prospective victim contains no method named Z. Additionally, the viral copying procedure outlined at the beginning of the article ignores method Z. This guarantees that the victim still contains no Z method. Finally, the class initializer hijacking procedure guarantees that a method named Z will exist in the new victim, hence future infections can proceed from a state much like the initial state.
The class initializer, the mother of all entry points, is now hijacked. Note, as an optimization step, we do not need to dynamically generate a class initializer in step 4. Because the content of the class initializer is fixed, the class initializer can exist as a normal method within the virus, and be absorbed into the copying process. The one special processing step is to avoid, during victim selection, rejecting a victim for having a class initializer. It may sound silly, but the class initializer is, after all, just another method; if the prospect validator algorithm didn’t exclude the class initializer, it would infer that a victim is not infectable because it has a class initializer: a large percentage of the classes out there would become non-infectable because of such a small limitation.
Voila, these are the concepts behind the virus. Download jsphere-1.0.1-docs.zip to get a feel of what lies beneath.
Post a Comment