Reversing : The Hacker's Guide to Reverse Engineering

(ff) #1
Registers

In order to avoid having to access the RAM for every single instruction,
microprocessors use internal memory that can be accessed with little or no
performance penalty. There are several different elements of internal memory
inside the average microprocessor, but the one of interest at the moment is the
register. Registers are small chunks of internal memory that reside within the
processor and can be accessed very easily, typically with no performance
penalty whatsoever.
The downside with registers is that there are usually very few of them. For
instance, current implementations of IA-32 processors only have eight 32-bit
registers that are truly generic. There are quite a few others, but they’re mostly
there for specific purposes and can’t always be used. Assembly language code
revolves around registers because they are the easiest way for the processor to
manage and access immediate data. Of course, registers are rarely used for
long-term storage, which is where external RAM enters into the picture. The
bottom line of all of this is that CPUs don’t manage these issues automatically—
they are taken care of in assembly language code. Unfortunately, managing
registers and loading and storing data from RAM to registers and back cer-
tainly adds a bit of complexity to assembly language code.
So, if we go back to our little code sample, most of the complexities revolve
around data management. xand ycan’t be directly multiplied from memory,
the code must first read one of them into a register, and then multiply that reg-
ister by the other value that’s still in RAM. Another approach would be to copy
both values into registers and then multiply them from registers, but that
might be unnecessary.
These are the types of complexities added by the use of registers, but regis-
ters are also used for more long-term storage of values. Because registers are so
easily accessible, compilers use registers for caching frequently used values
inside the scope of a function, and for storing local variables defined in the
program’s source code.
While reversing, it is important to try and detect the nature of the values
loaded into each register. Detecting the case where a register is used simply to
allow instructions access to specific values is very easy because the register is
used only for transferring a value from memory to the instruction or the other
way around. In other cases, you will see the same register being repeatedly
used and updated throughout a single function. This is often a strong indica-
tion that the register is being used for storing a local variable that was defined
in the source code. I will get back to the process of identifying the nature of val-
ues stored inside registers in Part II, where I will be demonstrating several
real-world reversing sessions.


Low-Level Software 39
Free download pdf