Reversing : The Hacker's Guide to Reverse Engineering

(ff) #1

Having this kind of information completely changes the reversing experi-
ence because it brings us much closer to the original high-level representation
of the program. In fact, this information allows for the creation of highly effec-
tive decompilers that can reconstruct remarkably readable high-level lan-
guage representations from bytecode executables. This situation is true for
both Java and .NET programs, and it presents a problem to software vendors
working on those platforms, who have a hard time protecting their executa-
bles from being easily reverse engineered. The solution in most cases is to use
obfuscators—programs that try to eliminate as much sensitive information
from the executable as possible (while keeping it functional).
Depending on the specific platform and on how aggressively an executable
is obfuscated, reversers have two options: they can either use a decompiler to
reconstruct a high-level representation of the target program or they can learn
the native low-level language in which the program is presented and simply
read that code and attempt to determine the program’s design and purpose.
Luckily, these bytecode languages are typically fairly easy to deal with because
they are not as low-level as the average native processor assembly language.
Chapter 12 provides an introduction to Microsoft’s .NET platform and to its
native language, the Microsoft Intermediate Language(MSIL), and demonstrates
how to reverse programs written for the .NET platform.


Hardware Execution Environments in Modern Processors


Since this book focuses primarily on the reversing process for native IA-32 pro-
grams, it might make sense to take a quick look at how code is executed inside
these processors to see if you can somehow harness that information to your
advantage while reversing.
In the early days of microprocessors things were much simpler. A micro-
processor was a collection of digital circuits that could perform a variety of
operations and was controlled using machine code that was read from mem-
ory. The processor’s runtime consisted simply of an endlessly repeating
sequence of reading an instruction from memory, decoding it, and triggering
the correct circuit to perform the operation specified in the machine code. The
important thing to realize is that execution was entirely serial. As the demand
for faster and more flexible microprocessors arose, microprocessor designers
were forced to introduce parallelism using a variety of techniques.
The problem is that backward compatibility has always been an issue. For
example, newer version of IA-32 processors must still support the original IA-
32 instruction set. Normally this wouldn’t be a problem, but modern proces-
sors have significant support for parallel execution, which is difficult to
achieve considering that the instruction set wasn’t explicitly designed to sup-
port it. Because instructions were designed to run one after the other and not
in any other way, sequential instructions often have interdependencies which


Low-Level Software 63
Free download pdf