Intel Architecture and Windows 10
We’ll begin this module with a brief overview of the Intel architecture
and discuss some essential assembly operations in both the
32 and 64-bit versions of Windows 10.
Although the differences between these versions may be subtle to the casual user,
they are significant at the assembly level.
The 64-bit architecture is an extension of the 32-bit architecture
and as such, there are many similarities.
At the assembly level, both make significant use of data areas
like the stack or the heap and both use CPU registers.
The stack typically stores the content of variables
that are of static size and limited scope,
whereas the heap is used for dynamic memory allocation
and long-runtime persistent memory.
32-bit versions of Windows allocate 2 gigabytes of memory space to applications.
64-bit versions of Windows, on the other hand, support 128 terabytes of memory.
Although we won’t delve into memory management in this module,
it’s important to understand that unlike higher level languages like C#,
there are no variables in assembler
Instead, all data is stored either in memory or in a CPU register.
In a 32-bit environment, the CPU maintains and uses a series of nine 32-bit registers
Most of these registers can be subdivided into smaller segments.
In 64-bit environments, the 32-bit registers are extended,
and include new registers named R8 through R15
The most important registers for us to understand in our current context
are the 32-bit EIP and ESP registers
and their 64-bit extended counterparts RIP and RSP.
EIP and RIP contain the address of the assembly instruction to be executed by
the CPU and the memory address of the top of the stack is in ESP or RSP.
In order to understand assembly execution flow,
we should discuss two types of instructions:
function calls and conditional branches.
Let’s first discuss function calls and how they are called,
what happens when they finish executing, and how parameters are passed into them.
The call assembly instruction,
transfers program execution to the address of the function –
and places the address to execute once the function is complete –
on the top of the stack where ESP (or RSP) is pointing.
Once the function is complete, the ret instruction is executed,
which fetches the return address from the stack and restores it to EIP/RIP
When a function requires arguments, a calling convention specifies how, exactly,
arguments are passed to that function.
On a 32-bit architecture,
the __stdcall calling convention reads all arguments from the stack.
However, the 64-bit __fastcall calling convention,
expects the first four arguments to be in RCX, RDX, R8, and R9 in that order,
with any remaining arguments on the stack.
Conditional branching is the second aspect of assembly execution flow
that we should discuss
In assembly, conditional branching
(similar to the if and else statements in higher-level languages)
is implemented through a comparison and a jump instruction.
Specifically, we might use a compare,
or test instruction,
and based on the result of this comparison,
we could execute a conditional jump instruction to another section of code.
This extremely brief introduction
sets the stage for a discussion of code analysis and debugging.