cutable and decipher each instruction and “execute” it in a virtual environ-
ment implemented in software. It is important to understand that not only are
these instructions not directly executed on the host processor, but also that the
data accessed by the bytecode program is managed by the interpreter. This
means that the bytecode program would not have direct access to the host
CPU’s registers. Any “registers” accessed by the bytecode would usually have
to be mapped to memory by the interpreter.
Interpreters have one major drawback: performance. Because each instruc-
tion is separately decoded and executed by a program running under the real
CPU, the program ends up running significantlyslower than it would were it
running directly on the host’s CPU. The reasons for this become obvious when
one considers the amount of work the interpreter must carry out in order to
execute a single high-level bytecode instruction.
For each instruction, the interpreter must jump to a special function or code
area that deals with it, determine the involved operands, and modify the sys-
tem state to reflect the changes. Even the best implementation of an interpreter
still results in each bytecode instruction being translated into dozens of
instructions on the physical CPU. This means that interpreted programs run
orders of magnitude slower than their compiled counterparts.
Just-in-Time Compilers
Modern virtual machine implementations typically avoid using interpreters
because of the performance issues described above. Instead they employ just-
in-time compilers, or JiTs. Just-in-time compilation is an alternative approach for
running bytecode programs without the performance penalty associated with
interpreters.
The idea is to take snippets of program bytecode at runtime and compile
them into the native processor’s machine language before running them.
These snippets are then executed natively on the host’s CPU. This is usually an
ongoing process where chunks of bytecode are compiled on demand, when-
ever they are required (hence the term just-in-time).
Reversing Strategies
Reversing bytecode programs is often an entirely different experience com-
pared to that of conventional, native executable programs. First and foremost,
most bytecode languages are far more detailed compared to their native
machine code counterparts. For example, Microsoft .NET executables contain
highly detailed data type information called metadata. Metadata provides
information on classes, function parameters, local variable types, and much
more.
62 Chapter 2