Reversing For Everyone
Reversing For Everyone
Table of C ontents
Introduction 1.1
1
Part 1: G oals
Part 17 - AD D S 1.3.17
Part 20 – AD C 1.3.20
Part 21 – D ebugging AD C 1.3.21
2
Part 1: G oals
3
Part 1: G oals
4
Part 1: G oals
5
Part 1: G oals
Whew, that was quite a mouthful, wasn' t it? Well, it is one of the main reasons
why this tutorial set exists. To make reverse engineering as simple as possible.
This comprehensive set of reverse engineering tutorials covers x86, x64 as well
as 32-bit ARM and 64-bit architectures. If you' re a newbie looking to learn
reversing, or just someone looking to revise on some concepts, you' re at the right
place. As a beginner, these tutorials will carry you from nothing upto the mid-
basics of reverse engineering, a skill that everyone within the realm of cyber-
6
Part 1: G oals
security should possess. If you' re here just to refresh some concepts, you can
conveniently use the side bar to take a look at the sections that has been covered
so far.
You can get the entire tutorial set in PD F or MOBI format. All these ebook
versions will get updated automatically as new tutorials will be added.
7
Part 1: G oals
8
Part 1: G oals
Part 1: G oals
Essential to the discussion of basic reverse engineering is the concept of modern
malware analysis. Malware analysis is the understanding and examination of
information necessary to respond to a network intrusion.
This short tutorial will begin with the basic concepts of malware reverse
engineering and graduate to an entry-level basic examination of Assembly
Language.
The keys to the kingdom so to speak are rooted in the break-down of the
respective suspected malware binary and how to find it on your network and
ultimately to contain it.
Upon full identification of the files required for deeper analysis, it is critical to
develop signatures to detect malware infections throughout your network whether
it be a home based LAN or complex corporate WAN to which malware analysis is
necessary to develop host-based and network signatures.
9
Part 1: G oals
Static analysis uses software tools to examine the executable without looking at
the actual decompiled instructions in Assembly. We will not focus on this type of
analysis as we are going to focus on actual disassembled binaries instead.
Reverse engineering is much more than just malware analysis. At the end of our
series, our capstone tutorial will utilize ID A as we will create a real-world scenario
where you will be tasked by the CEO of ABC Biochemicals to secretly try to
ethically hack his companies software that controls a bullet-proof door in a very
sensitive Bio-Chemical lab in order to test how well the software works against
real threats. The project will be very basic however it will ultimately showcase the
power of Assembly Language and how one can use it to reverse engineer and
ultimately provide solutions on how to better design the code to make it safer.
10
Part 1: G oals
Malware falls into several categories of which I will touch briefly upon below.
A backdoor is malicious code that embeds itself into a computer to allow a remote
attacker access with very little or sometimes no authority to execute various
commands on any respective local computer.
A downloader is nothing more than malicious code that has only one purpose
which is to install other malicious software. D ownloaders are frequently installed
when a hacker gains access to a system initially. The downloader then installs
additional software to control the system.
There are malicious programs that launch other malicious programs which use
non-standard options to get increased access or a greater cloaking/hiding
technique when penetrating a system.
One of the most dangerous forms of malware is the rootkit which hides the
existence of itself and additional malware from the user which makes it extremely
hard to locate. A rootkit can manipulate processes such as hiding their IP in an IP
scan so that a user may never know that they have a direct socket to a botnet or
other remote computer.
There are also various kinds of malware that send spam from a target machine
which generates income for the attacker by allowing them to sell various services
to other users.
The final form of malware is that of a traditional worm or virus which copies itself
and goes after other computers.
This is the end the road for now regarding our discussion of malware because we
first need to go back to the beginning and understand how a computer works at
it' s base level.
11
Part 1: G oals
In our next lesson we will begin our long journey into x86 Assembly Language. In
order to truly understand the very basics of reverse engineering and malware we
need to over the next several months take a deep dive into the core and build our
way up.
12
Part 1: G oals
Ladies and G entlemen, boys and girls, children of all ages! We are about to
embark on a journey that will change your life forever!
The first question we must answer is what is x86 Assembly Language to which
the answer is a family of backward-compatible Assembly Languages which
provide compatibility back to the Intel 8000 series of microprocessors. x86
Assembly Languages are used to produce object code for the aforementioned
series of processors. It uses mnemonics to represent the instructions that the
CPU can execute.
Assembly Language for the x86 microprocessor works in conjunction with various
operating systems. We will focus on Linux Assembly Language utilizing the Intel
syntax in addition to learning how to program in C to which we will disassemble
the source code an analyze the respective Assembly.
x86 Assembly Language has two choices of syntax. The AT& T syntax was
dominant in the Unix world since the OS was developed at AT& T Bell Labs. In
contrast, the Intel syntax was originally used for the documentation of the x86
platform and was dominant in the MS-D OS and Windows environments.
The main differences between the two is in the AT& T syntax, the source comes
before the destination and in the Intel syntax, the destination comes before the
source. We will discuss this in more detail later in the tutorial.
Before you run for the door and regret embarking on this journey, remember,
some basic context helps to which we will develop throughout our quest. Many of
these topics may be confusing at this point which is perfectly normal as we will
develop them in time.
Linux is also open source and there are many versions. We will focus on Ubuntu
in our demonstrations which can be freely obtained. In contrast, the Windows
operating system is owned and controlled by one company, Microsoft, to which all
updates, security patches and service patches come directly from them where
Linux has millions of professionals providing the same absolutely free!
13
Part 1: G oals
In our next lesson we discuss the binary number system. G rab your cup of coffee
you are going to need it!
14
Part 1: G oals
Binary numbers are what define the core of a computer. A bit within a computer is
either on or off. A bit has either electricity turned on to it or it is absent of such. We
will dive into this deeper in future tutorials.
H ave no fear! The binary number system is here! It is important to understand that
in binary, each column has a value two times the column to its right and there are
only two digits in the base which happen to be 0 and 1.
In decimal, base 10, say we have the number 15 which means (1 x 10) + (5 x 1) =
15 therefore the 5 is the number times 1 and the 1 is that number times 10.
Binary works in a similar fashion however we are now referring to base 2. That
same number in binary is 1111. To illustrate:
Binary numbers are important because using them instead of the decimal system
simplifies the design of computers and related technologies. The simplest
definition of the binary number system is a system of numbering that uses only
two digits, as we mentioned above, to represent numbers necessary for a
computer architecture rather than using the digits 1 through 9 plus 0 to represent
such.
In our next lesson we discuss the hexadecimal number system. It only gets more
exciting from here!
15
Part 1: G oals
Now that we are binary masters, it' s time to tackle the numbering system of
numbering systems!
This exciting number system is called hexadecimal. The reason why we use this
number system is that in x86 Assembly it is much easier to express binary
number representations in hexadecimal than it is in any other numbering system.
Ok I see the smoke coming out of your ears but its ok! In decimal, everything is
dealt with in the power of 10. Let' s take the number 42 and examine it in decimal:
2 x 10 ^ 0 = 2
4 x 10 ^ 1 = 40
16
Part 1: G oals
(2 * b ^ 0) + (4 * b ^ 1)
(2 * 10 ^ 0) + (4 * 10 ^ 1) = 42
0x2^0=0
1x2^1=2
0x2^2=0
1x2^3=8
0x2^4=0
1 x 2 ^ 5 = 32
0x2^6=0
0x2^7=0
0 + 2 + 0 + 8 + 0 + 32 + 0 + 0 = 42 decimal
10 * 16 ^ 0 = 10
2 * 16 ^ 1 = 32
10 * 1 = 10
2 * 16 = 32
5 x 16 ^ 0 = 5
15 x 16 ^ 1 = 240
17
Part 1: G oals
Lets look at this another way. Lets work with some more hexadecimal numbers
and convert them to decimal:
D --- 13 x 1 = 13
C --- 12 x 16 = 192
Addition in hexadecimal works as follows. From this point forward all numbers in
hexadecimal will have a ' h' next to the number:
18
Part 1: G oals
19
Part 1: G oals
You are probably asking yourself why is this guy spending so much time going
over so many different ways of learning this! The answer is that each of us learn a
little different from the next. I wanted to show several representations of
hexadecimal compared to decimal and binary to help put together the whole
picture.
20
Part 1: G oals
In our last lesson, we took a very deep dive into the hexadecimal number system.
I am going to keep this weeks lesson short so that you can re-read last weeks
lesson. I can not emphasize how important it is to understand hexadecimal
number conversions in addition to the ability to manually add and subtract them.
In the real world, we have calculators, in the real world we use the Windows
operating system, in the real world professional reverse engineers use G UI
debuggers like ID A Pro and others.
The question is, why am I not jumping right into the core of what real reverse
engineers do? The answer is simple, one must have a deep respect and
understanding of the machine in order to become great. We will never change the
world without fully understanding it first. Patience and perseverance win the day.
As I step off the soap box, lets get back to the basics of computers so here we go!
Electronic computers are simply made out of transistor switches. Transistors are
microscopic crystals of silicon that use electrical properties of silicon to act as
switches. Modern computers have what are referred to as field-effect transistors.
When we zoom out a bit we see that there are also diodes and capacitors when
taken together with the transistor switches we now have a memory cell. A
memory cell keeps a minimum current flow to which when you put a small voltage
on its input pin and a similar voltage on its select pin, a voltage will appear and
remain on its output pin. The output voltage remains in its set state until the
voltage is removed from the input pin in conjunction with the select pin.
Why is this important you ask. Very simply, the presence of voltage indicates a
binary 1 and the absence of voltage indicates a binary 0 therefore the memory
cell holds one binary digit or bit which is either 1 or 0 meaning on or off.
21
Part 1: G oals
Memory is measured in bytes. A byte is 8 bits. Two bytes are called a word and
two words are called a double word which is four bytes (32-bit) and a quad word
is eight bytes (64-bit).
A byte is 8 bits and is 2^8 power which is 256. The number of binary numbers 8
bits in size is one of 256 values starting at 0 and going to 255.
Every byte of memory in a computer has its own unique address. Let' s review the
disassembled instructions for a simple hello world application in Linux by setting a
breakpoint on the main function. We will use the G D B debugger:
D on' t worry if this does not make sense yet. The point of utilizing this example is
to give you a sneak peek into our first program that we will examine in addition to
learning about memory in a computer.
Below is an examination of the ESP register. Again, it is not critical that you
understand what a register is or what ESP does. We simply want to see what a
memory location looks like:
22
Part 1: G oals
Those of you new to assembly have now had your first look. D on' t get
discouraged or frustrated if you do not know what is going on here. We will take
our time and go through dozens of examples to break down each step in future
lessons. What is important is that you take your time and examine what each
lesson is discussing. Please always feel free to comment below with any
questions.
23
Part 1: G oals
The basic architecture is made up of a CPU, memory and I/O devices which are
input/output devices which are all connected by a system bus as detailed below.
1)Control Unit - Retrieves and decodes instructions from the CPU and then
storing and retrieving them to and from memory.
24
Part 1: G oals
We will discuss 32-bit x86 so therefore a 32-bit CPU first fetches a double word (4
bytes or 32-bits in length) from a specific address in memory and is read from
memory and loaded into the CPU. At this point the CPU looks at the binary
pattern of bits within the double word and begins executing the procedure that the
fetched machine instruction directs it to do.
We can immediately see that if we controlled flow of EIP, we can alter the
program to do things it was NOT intended to do. This is a popular technique upon
which malware operates.
The entire fetch and execute process is tied to the system clock which is an
oscillator that emits square-wave pulses at precise intervals.
In our next tutorial we will dive deeper into the IA-32 Architecture with a
discussion of the G eneral-purpose Registers.
25
Part 1: G oals
G eneral-purpose registers can be used to hold any type of data to which some
have acquired specific use which are used in programs. Lets review the 8
general-purpose registers in an IA-32 architecture.
E BX : The Base Register. Pointer to data in the D S segment. Used to store the
base address of the program.
E C X : The Counter register is often used to hold a value representing the number
of times a process is to be repeated. Used for loop and string operations.
E SI: Source Index register. Pointer to data in the segment pointed to by the D S
register. Used as an offset address in string and array operations. It holds the
address from where to read data.
E DI: D estination Index register. Pointer to data (or destination) in the segment
pointed to by the ES register. Used as an offset address in string and array
operations. It holds the implied write address of all string operations.
E BP: Base Pointer. Pointer to data on the stack (in the SS segment). It points to
the bottom of the current stack frame. It is used to reference local variables.
E SP: Stack Pointer (in the SS segment). It points to the top of the current stack
frame. It is used to reference local variables.
Keep in mind each of the above registers are 32-bit in length or 4 bytes in length.
Each of the lower 2 bytes of the EAX , EBX , ECX , and ED X registers can be
referenced by AX and then subdivided by the names AH , BH , CH and D H for high
bytes and AL, BL, CL and D L for the low bytes which are 1 byte each.
In addition, the ESI, ED I, EBP and ESP can be referenced by their 16-bit
equivalent which is SI, D I, BP, SP.
This can be a bit confusing to someone who has not studied computer
engineering however let me illustrate in the table below:
26
Part 1: G oals
EAX would have AX as its 16-bit segment and then you can further subdivide AX
into AL for the low 8 bits and AH for the high 8 bits. The same holds true for EBX ,
ECX and ED X as well. EBX would have BX as its 16-bit segment and then you
can further subdivide BX into BL for the low 8 bits and BH for the high 8 bits. ECX
would have CX as its 16-bit segment and then you can further subdivide CX into
CL for the low 8 bits and BH for the high 8 bits. ED X would have D X as its 16-bit
segment and then you can further subdivide D X into D L for the low 8 bits and D H
for the high 8 bits.
ESI, ED I, EBP and ESP can be broken down into its 16-bit segments as follows:
ESI would have SI as its 16-bit segment, ED I would have D I as its 16-bit segment,
EBP would have BP as its 16-bit segment and ESP would have SP as its 16-bit
segment.
In our next tutorial we will continue our discussion of the IA-32 Architecture with
the Segment Registers.
27
Part 1: G oals
The segment registers are used specifically for referencing memory locations.
There are three different methods of accessing system memory of which we will
focus on the flat memory model which is relevant for our purposes.
C S: Code segment register stores the base location of the code section (.text
section) which is used for data access.
DS: D ata segment register stores the default location for variables (.data section)
which is used for data access.
SS: Stack segment register stores the base location of the stack segment and is
used when implicitly using the stack pointer or when explicitly using the base
pointer.
Each segment register is 16-bits and contains the pointer to the start of the
memory-specific segment. The CS register contains the pointer to the code
segment in memory. The code segment is where the instruction codes are stored
in memory. The processor retrieves instruction codes from memory based on the
CS register value and an offset value contained in the instruction pointer (EIP)
register. Keep in mind no program can explicitly load or change the CS register.
The processor assigns its values as the program is assigned a memory space.
The D S, ES, FS and G S segment registers are all used to point to data segments.
Each of the four separate data segments help the program separate data
elements to ensure that they do no overlap. The program loads the data segment
registers with the appropriate pointer value for the segments and then reference
individual memory locations using an offset value.
The stack segment register (SS) is used to point to the stack segment. The stack
contains data values passed to functions and procedures within the program.
Segment registers are considered part of the operating system and can neither
read nor be changed directly in almost all cases. When working in the protected
mode flat model (x86 architecture which is 32-bit), your program runs and
receives a 4G B address space to which any 32-bit register can potentially
address any of the four billion memory locations except for those protected areas
defined by the operating system. Physical memory may be larger than 4G B
however a 32-bit register can only express 4,294,967,296 different locations. If
you have more than 4G B of memory in your computer, the OS must arrange a
28
Part 1: G oals
4G B region within memory and your programs are limited to that new region. This
task is completed by the segment registers and the OS keeps close control of
this.
In our next tutorial we will continue our discussion of the IA-32 Architecture with
the Instruction Pointer Register.
29
Part 1: G oals
The instruction pointer register called the EIP register is simply the most important
register you will deal with in any reverse engineering. The EIP keeps track of the
next instruction code to execute. EIP points to the next instruction to execute. If
you were to alter that pointer to jump to another area in the code you have
complete control over that program.
Lets jump ahead and dive into some code. H ere is an example of a simple hello
world application in C that we will go into more detail much later in our tutorial
series. For our purposes today, we will see the raw POWER of assembly
language and particularly that of the EIP register and what we can do to
completely hack program control.
D on’t worry if you do not understand what it does or its functionality. What to take
note of here is the fact we have a function called unreachableFunction that is
never called by the main function. As you will see if we can control the EIP
register we can hack this program to execute that code!
We have simply compiled the code to work with the IA32 instruction set and ran
it. As you can see there is no call to the unreachableF unction of any kind as it is
unreachable under normal conditions as you can see the ‘ H ello World!` printed
when executed.
30
Part 1: G oals
Lets examine the unreachableF unction and see where it starts in memory and
write down that address.
31
Part 1: G oals
The next step is to set EIP to address 0x0804843b so that we hijack program flow
to run the unreachableFunction.
Now that we have hacked control of EIP, lets continue and watch how we have
hijacked the operation of a running program to our advantage!
So the question in your mind is why did you show me this when I have no idea of
what any of this is? It is important to understand that when we are doing a lengthy
tutorial such as this we should sometimes look forward to see why we are taking
so many steps to learn the basics before we dive in. It is important however to
show you that if you stay with the tutorial your hard work will pay off as we will
learn how to hijack any running program to make it do whatever we want in
addition to proactively breaking down a malicious program so that we can not only
disable it but trace it back to a potential IP of where the hack originated.
In our next tutorial we will continue our discussion of the IA-32 Architecture with
the Control Registers.
32
Part 1: G oals
Their are five control registers which are used to determine the operating mode of
the CPU and the characteristics of the current executing task. Each control
register is as follows:
C R 0: System flag that control the operating mode and various states of the
processor.
C R 4 : Flags that enable processor feathers and indicate feature capabilities of the
processor.
The values in each of the control registers can’t be directly accessed however the
data in the control register can be moved to one of the general-purpose registers
and once the data is in a G P register, a program can examine the bit flags in the
register to determine the operating status of the processor in conjunction with the
current running task.
If a change is required to a control register flag value, the change can be made to
the data in the G P register and the register moved to the CR. Low-level System
Programmers usually modify the values in control registers. Normal application
programs do not usually modify control register entries however they might query
flag values to determine the capabilities of the host processor chip on which the
program is currently running.
In our next tutorial we will continue our discussion of the IA-32 Architecture with
the topic of Flags.
33
Part 1: G oals
Part 14 : F lags
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
The topic of flags are one of the most extremely complex and complicated
concepts of assembly language and program flow control when reverse
engineering. This information below will become much clearer as we enter into
the final phase of our training when we reverse engineer C applications into
assembly language.
What is important here is to take away the fact that flags help control, check and
verify program execution and are a mechanism to determine whether each
operation that is performed by the processor is successful or not.
Flags are critical to assembly language applications as they are a check to verify
each programs functions successful execution.
We are dealing with 32-bit assembly to which a single 32-bit register which
contains a group of status, control and system flags exist. This register is called
the EFLAG S register as it contains 32 bits of information that are mapped to
represent specific flags of information.
There are three kinds of flags which are status flags, control flags and system
flags.
C F : Carry Flag
PF : Parity Flag
AF : Adjust Flag
Z F : Z ero Flag
SF : Sign Flag
O F : Overflow Flag
The carry flag is set when a math operation on an unsigned integer value
generates a carry or borrow for the most significant bit. This is an overflow
condition for the register involved in the math operation. When this occurs, the
remaining data in the register is not the correct answer to the math operation.
The parity flag is used to indicate corrupt data as a result of a math operation in a
register. When checked, the parity flag is set if the total number of 1 bits in the
result is even and is cleared if the total number of 1 bits in the result is odd. When
the parity flag is checked, an application can determine whether the register has
been corrupted since the operation.
The adjust flag is used in Binary Coded D ecimal math operations and is set if a
carry or borrow operation occurs from bit 3 of the register used for the calculation.
34
Part 1: G oals
The sign flag is set to the most significant bit of the result which is the sign bit and
indicates whether the result is positive or negative.
The overflow flag is used in signed integer arithmetic when a positive value is too
big or a negative value is too small to be represented in the register.
Control flags are utilized to control specific behavior in the processor. The D F flag
which is the direction flag is used to control the way strings are handled bu the
processor. When set, string instructions automatically decrement memory
addresses to get the next byte in the string. When cleared, string instructions
automatically increment memory addresses to get the next byte in the string.
System flags are used to control OS level operations which should NEVER be
modified by any respective program or application.
TF : Trap Flag
R F : Resume Flag
The trap flag is set to enable single-step mode and when in this mode the
processor performs only one instruction code at a time, waiting for a signal to
perform the next instruction. This is essential when debugging.
The interrupt enable flag controls how the processor responds to signals received
from external sources.
The I/O privilege field indicates the input-output privilege level of the currently
running task and defines access levels for the input-output address space which
must be less than or equal to the access level required to access the respective
address space. In the case where it is not less than or equal to the access level
required, any request to access the address space will be denied.
The nested task flag controls whether the currently running task is linked to the
previously executed task and is used for chaining interrupted and called tasks.
The resume flag controls how the processor responds to exceptions when in
debugging mode.
The VM flag indicates that the processor is operating in virtual-8086 mode instead
of protected or real mode.
35
Part 1: G oals
The alignment check flag is used in conjunction with the AM bit in the CR0 control
register to enable alignment checking of memory references.
The virtual interrupt flag replicates the IF flag when the processor is operating in
virtual mode.
The virtual interrupt pending flag is used when the processor is operating in virtual
mode to indicate that n interrupt is pending.
The ID flag indicates whether the processor supports the CPUID instruction.
36
Part 1: G oals
Part 15 : Stack
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
The stack pointer is a register that contains the top of the stack. The stack pointer
contains the smallest address, lets say for example 0x00001000, such that any
address smaller than 0x00001000 is considered garbage and any address greater
than 0x00001000 is considered valid.
The above address is random and is not an absolute where you will find the stack
pointer from program to program as it will vary. Lets look at what the stack looks
like from an abstract perspective:
The above diagram is what I want you to keep clear in your mind as that is what is
actually happening in memory. The next series of diagrams will show the opposite
of what is shown above.
You will see the stack growing upward in the below diagrams however in reality it
is growing downward from higher memory to lower memory.
In the addMe example below, the stack pointer (ESP), when examined in memory
on a breakpoint on the main function, lists 0xffffd050. When the program calls the
addMe function from main, ESP is now 0xffffd030 which is LOWER in memory.
37
Part 1: G oals
Therefore the stack grows D OWNWARD despite the diagram showing it pointing
upward. Just keep in mind when the arrows below are pointing upward they are
actually pointing to lower memory addresses.
The stack bottom is the largest valid address of the stack and is located in the
larger address area or top of the memory model. This can be confusing as the
stack bottom is higher in memory. The stack grows downward in memory and it is
critical that you understand that now as we go forward.
The stack limit is the smallest valid address of the stack. If the stack pointer gets
smaller than this, there is a stack overflow which can corrupt a program to allow
an attacker to take control of a system. Malware attempts to take advantage of
stack overflows. As of recent, there are protections build into modern OS that
attempt to prevent this from happening.
There are two operations on the stack which are push and pop. You can push one
or more registers by setting the stack pointer to a smaller value. This is usually
done by subtracting four times the number of registers to be pushed onto the
stack and copying the registers to the stack.
You can pop one or more registers by copying the data from the stack to the
registers, then to add a value to the stack pointer. This is usually done by adding
four times the number of registers to be popped on the stack.
Let us look at how the stack is used to implement functions. For each function call
there is a section of the stack reserved for the function. This is called the stack
frame.
Let’s look at the C program we created in tutorial 12 and examine what the main
function looks like:
We see two functions here. The first one is the unreachableFunction to which will
never execute under normal circumstances and we also see the main function
that will always be the first function to be called onto the stack.
When we run this program, the stack will look like this:
38
Part 1: G oals
We can see the stack frame for int main(void) above. It is also referred to as the
activation record. A stack frame exists whenever a function has started but yet to
complete. For example, inside of the body of the int main(void) there is a call to int
addMe(int a, int b) which takes two arguments a and b. There needs to be
assembly language code in int main(void) to push the arguments for int addMe(int
a, int b) onto the stack. Lets examine some code.
When we compile and run this program we will see the value of 5 to be print out
like this:
Very simply, int main(void) calls int addMe(int a, int b) first and will get put on the
stack like this:
39
Part 1: G oals
You can see that by placing the arguments on the stack, the stack frame for int
main( void) has increased in size. We also reserved space for the return value
which is computed by int addMe( int a, int b) and when the function returns, the
return value in int main( void) gets restored and execution continues in int
main( void) until it finishes.
Once we get the instructions for int addMe( int a, int b) , the function may need
local variables so the function needs to push some space on the stack which
would look like:
40
Part 1: G oals
int addMe( int a, int b) can access the arguments passed to it from int
main( void) because the code in int main( void) places the arguments just as int
addMe( int a, int b) expects it.
FP is the frame pointer and points to the location where the stack pointer was just
before int addMe( int a, int b) moved the stack pointer or SP for int addMe( int a,
int b) ’s own local variables.
The use of a frame pointer is essential when a function is likely to move the stack
pointer several times throughout the course of running the function. The idea is to
keep the frame pointer fixed for the duration of int addMe( int a, int b) ’s stack
frame. In the meantime, the stack pointer can change values.
We can use the frame pointer to compute the locations in memory for both
arguments as well as local variables. Since it does not move, the computations for
those locations should be some fixed offset from the frame pointer.
Once it is time to exit int addMe( int a, int b) , the stack pointer is set to where the
frame pointer is which pops off the int addMe( int a, int b) stack frame.
In sum, the stack is a special region of memory that stores temporary variables
created by each function including main. The stack is a LIFO which is last in, first
out data structure which is managed and optimized by the CPU closely. Every
time a function declares a new variable it is pushed onto the stack. Every time a
function exists, all of the variables pushed onto the stack by that function are
freed or deleted. Once a stack variable is freed, that region of memory becomes
available for other stack variables.
The advantage of the stack to store variables is that memory is managed for you.
You do not have to allocate memory manually or free it manually. The CPU
manages and organizes stack memory very efficiently and is very fast.
41
Part 1: G oals
It is critical that you understand that when a function exits, all of its variables are
popped off the stack and lost forever. The stack variables are local. The stack
grows and shrinks as functions push and pop local variables.
I can see your head spinning around and around. Keep in mind, these topics are
complicated and will continue to develop in future tutorials. We have been dealing
with a lot of confusing topics such as registers, memory and now the stack and it
can be overwhelming. If you ever have questions, please comment below and I
will help you to better understand this framework.
42
Part 1: G oals
Our next step in the Basic Malware Reverse Engineering section focuses on the
heap. Keep in mind, the stack grows downward and the heap grows upward. It is
very, very important that you understand this concept as we progress forward in
our future tutorials.
The heap is the region of your computer' s memory that is not managed
automatically for you, and is not as tightly managed by the CPU. It is free-floating
region of memory and is larger than the stack allocation of memory.
To allocate memory on the heap, you must use malloc( ) or calloc( ) , which are
built-in C functions. Once you have allocated memory on the heap, you are
responsible for freeing it by using free( ) to de-allocate that memory once you
don' t need it any more.
If you don’t do this step, your program will have what is known as a memory leak.
That is, memory on the heap will still be set aside and won' t be available to other
processes that need it.
Unlike the stack, the heap does not have size restrictions on variable size. The
only thing that would limit the heap is the physical limitations of your computer.
H eap memory is slightly slower to be read from and written to, because you have
to to use pointers to access memory on the heap. When we dive into our C
tutorial series we will demonstrate this.
Unlike the stack, variables created on the heap are accessible by any function,
anywhere in your program. H eap variables are essentially global in scope.
43
Part 1: G oals
If you need to allocate a large block of memory for something like a struct or a
large array and you need to keep that variable around for a good duration of the
program to which must be accessed globally, then you should choose the heap
for this purpose. If you need variables like arrays and structs that can change size
dynamically such as arrays that can grow or shrink as needed, then you will likely
need to allocate them on the heap, and use dynamic memory allocation functions
like malloc( ) , calloc( ) , realloc( ) and free( ) to manage that memory manually.
The next step is to dive into programming C in the Linux environment where we
step-by-step disassemble each C program so in effect you will be learning both C
programming and Assembly so that you can progress your skills in Malware
Analysis and Reverse Engineering.
I look forward to seeing you all next week when we take a comprehensive step-
by-step tutorial on how to install Linux on your current computer using the FREE
Virtual Box software tool.
44
Part 1: G oals
If you do not have Linux installed on a computer within your household, I would
suggest installing Virtual Box which is an open-source free virtual environment
which you can install on your existing computer to have a version of Linux you
can program with. Below is a link to download and install Virtual Box as there are
versions for both Windows and Mac.
https://www.virtualbox.org/wiki/D ownloads
In addition, you will need a copy of Linux to which I will be working with Ubuntu.
Below is a link to download the .iso file to which you will install once you have
Virtual Box installed.
http://www.ubuntu.com/download/desktop
After you download the above .iso, go to your Dow nload directory and first
execute and run the V irtualBox-5 .0.24 -10835 5 -W in.exe or whatever version of
VirtualBox that is currently available. If you are running a Mac, you will download
the .dmg file. Simply double-click on the file to execute and run it.
45
Part 1: G oals
After you install V irtualBox-5 .0.24 -10835 5 -W in.exe or the Mac .dmg file and you
will see this screen:
Click on the New button above which is located in the top-left corner of the screen
as it is a big blue cog-looking circle.
In the name field above, type Ubuntu and click the next button.
46
Part 1: G oals
It is important to click on the blue slider bar above and select an amount of ram
that points to an area in green so that it does not overwhelm your computer
resources. After moving the blue slider, click next.
47
Part 1: G oals
48
Part 1: G oals
Please move the dial up to 16.00 G B rather than 8.00 G B shown above then click
create.
49
Part 1: G oals
The next step is to click on the yellow folder just above the cancel button.
The next step is to click on the .iso file that should be in your D ownload directory
and click open.
50
Part 1: G oals
The next step is to let the install begin and click Install Ubuntu.
51
Part 1: G oals
The next step is to check each of the boxes to D ownload updates while installing
Ubuntu and click continue.
The next step is to select Erase disk and install Ubuntu and click install now.
52
Part 1: G oals
The next step is to click continue and progress forward to the screen where you
will select your timezone to which you will select continue.
The next step is to select your keyboard layout and click continue.
53
Part 1: G oals
The next step is to create a name for your account. I chose noroot and did the
same for the username. In addition, create a password and re-type it for
verification and click continue.
At this point it will take some time to install the operating system. When the
process is finished, click restart now. If the window locks up, click Power Off The
Machine and click close or next.
54
Part 1: G oals
Enter in your password that you created earlier and click enter on your
keyboard. You can click on the blue x buttons in the top right corner as they are
just some information you can close out.
55
Part 1: G oals
Click on the top left icon and type terminal and double-click on the first Terminal
icon with the > _ in the window.
56
Part 1: G oals
You will see a Terminal icon at the bottom left of your screen. Right-click on it and
select Lock to Launcher so that it will be available for you once you close the
window.
In the terminal window type cd D esktop and press Enter. Then type mkdir Code
and press enter. The first command moves you into the D esktop directory and the
mkdir command creates a folder on the D esktop called Code so that we have a
place to store our software applications that we create.
It is important you keep your version of Linux up to date. Every time you login, you
should type the following commands. First, sudo apt-get update and press enter.
57
Part 1: G oals
Next you should then type sudo apt-get upgrade and press enter.
In order to work with 32-bit Assembly examination, we need to install the gcc
multilib package so that we can compile 32-bit versions of C code for
examination. Type sudo apt-get install gcc-multilib and press enter.
Finally click on Devices and click Insert G uest Additions C D Image… in order
to get a better working functionality out of your VM.
This has been a very long tutorial however necessary to get you a working copy
of Linux so that we can continue with our future tutorials.
I look forward to seeing you all next week when we learn how to use the vim text
editor to begin coding!
58
Part 1: G oals
Now that we have a working version of Linux, we need a text editor that we can
work with in the terminal.
This will open up the vi text editor. The first thing you need to type is the letter ‘ i’ to
set the editor to insert mode so you may begin typing.
After you a done typing, press the ‘ esc’ key and type ‘ : w q ’ and press enter.
Congratulations! You created your first file! This is a one time file that we need to
create in order to use our text editor they way we want it to perform.
The first line states set number which means we would like each file to show line
numbers as this is essential for debugging code. The set smartindent, set
tabstop, set shiftw idth and set expandtab statements set forth rules to properly
format code and allow 4 spaces per tab indent which will help our code to look
clean.
There are several commands you need to be aware of. Keep in mind, to go into
command mode rather than insert mode you must press the ‘ esc’ key. Below are
the most common commands:
59
Part 1: G oals
:w [ save file]
You will be consistently moving between command mode ‘ esc’ and insert mode
‘ i’. Remember that when you want to insert characters you need to be in insert
mode and when you want to move the cursor other than moving to the next line,
you need to be in command mode.
Now that we have vi configured, lets install vim which has some better
functionality. Simply type:
I look forward to seeing you all next week when we talk about why it’s important to
learn Assembly Language.
60
Part 1: G oals
So many people ask me this question and it is true, Java is H OT and in the
greatest demand and there is nothing wrong with learning Java however the
threats that face society more than anything in this world, above everything else,
is the Cyber Security threat. With that said, Java offers a great career path and I
would encourage you to learn it however Java is not the only game in town.
The hackers use a multitude of high-level languages and the demand for new
professional Malware Analyst Reverse Engineers continue to grow daily.
When we examine malware, more than not we get only a compiled binary. The
only thing we can do with a compiled binary is to break it down, instruction-by-
instruction, in Assembly Language as EVERYTH ING ultimately goes down to
Assembly Language.
When someone says Assembly Language is a dinosaur I say to those people, lets
have that conversation when your entire network is brought to its knees and you
can’t login to a single terminal or manipulate a single machine on your network.
Lets talk about how useless Assembly Language is at that time.
Assembly Language is low-level and has many more instructions than you would
see in a higher-level application.
The prior 18 lessons in this tutorial series gave you the basics of x86 hardware.
As I have stated in prior tutorials, we will focus on 32-bit Assembly debugging as
most malware is going to try to affect as many systems as possible and although
61
Part 1: G oals
I look forward to seeing you all next week when we learn the basics of instruction
code handling.
62
Part 1: G oals
A CPU reads instruction codes that are stored in memory as each code set can
contain one of more bytes of information that guide the processor to perform a
very specific task. As each instruction code is read in from memory, any data
needed for the instruction code is also stored and read into memory.
Keep in mind, memory that contain instruction codes are no different than the
bytes that contain the data used by the CPU and special pointers are used to help
the CPU keep track of where in memory data is and where instruction codes are
stored.
A data pointer helps the CPU keep track of where the data area in memory starts
which is the stack. When new data elements are placed in the stack, the stack
pointer moves down in memory and as data is read from the stack the stack
pointer moves up in memory. Please review Part 15 – Stack if you don’t
understand this concept.
The instruction pointer is used to help the CPU keep track of which instruction
codes have already been processed and what code is to be processed next.
Please review Part 12 – Instruction Pointer Register if you don’t understand this
concept.
Each and every instruction code must include an opcode that defines the basic
function or task to be performed by the CPU to which opcodes are between 1 and
3 bytes in length and uniquely defines the function that is performed.
All we are doing is creating a main function of type integer to which it has a void
parameter and returning 0. All this program does is simply exit the OS.
Lets use the objdump tool to and find the main function within it.
H ere is a snippet of the results you would get by running the above
command. H ere are the contents of the main function. Keep in mind the below is
in Intel syntax as we spoke about in the last tutorial.
63
Part 1: G oals
On the far left we have the corresponding memory addresses. In the center we
have the opcodes and finally on the right we have the corresponding assembly
language in Intel syntax.
To keep this simple, lets examine memory address 804 83de where we see op
codes b8 00 00 00 00. We can see that the b8 opcode corresponds with the mov
eax, 0x0 instruction on the right. The next series of 00 00 00 00 represents 4
bytes of the value 0. We see mov eax, 0x0 therefore the value of 0 is moved into
eax therefore representing the above code. Keep in mind, the IA-32 platform uses
what we call little-endian notation which means the lower-value bytes appear first
in order when reading right to left.
I want to make sure you have this straight in your head so lets pretend the value
above was:
b8 01 00 00 00
If you are confused it is ok. Remember little-endian? Keep in mind eax is 32-bits
wide therefore that is 4 bytes (8 bits = 1 byte). The values are listed in reverse
order therefore we see the above representation.
I look forward to seeing you all next week when we dive into the details about how
to compile a program.
64
Part 1: G oals
Let’s look again at last weeks C program and take a deeper look at how we turn
that source code into an executable file.
This single step will create exit.o which is the binary object file and exit which is
the binary executable file.
Let’s begin with the -S switch. The -S switch will create comparable AT& T Syntax
Assembly source code. The -m32 will create a 32-bit executable and the -O0 will
tell the compiler how much optimization to use when compiling the binary. That is
the capital O and the numeric 0. Numeric 0 in that case means no optimization
which means it is at the most human readable instruction set. If you were to
substitute a 1, 2 or 3 the amount of optimization increases as the values go up.
This step above creates exit.s which is the equivalent Assembly Language source
code as we mentioned above.
We then need to compile the Assembly source code into a binary object file which
will generate a exit.o file.
65
Part 1: G oals
Finally we need to use a linker to create the actual binary executable code from
the binary object file which will create an executable called exit.
Last week when we examined the executable file exit in a program called
objdump, and examined the main area we saw the following below except this
time we will use AT& T Assembly Language Syntax:
Lets examine the code in the debugger. Let’s start G D B which is the G NU
debugger and first list the source code by typing l, then set a breakpoint on main
and run the program. Finally we will disassemble and review the output below:
In each of the three above examinations, you will essentially see the same set of
instructions which we will take a deeper look as to what is exactly going on in
future tutorials.
Throughout this tutorial series thus far we have been looking at Intel Syntax
Assembly Language. We are going to turn our focus to AT& T Syntax as I have
stated above as this is the natural syntax utilized in Linux with the G NU
Assembler and G NU D ebugger.
The biggest different you will see is that in AT& T Syntax, the source and
destinations are reversed.
66
Part 1: G oals
AT& T Syntax : movl %esp, %ebp [ This means move esp into ebp.]
Intel Syntax : mov esp, ebp [ This means move ebp into esp.]
You will also see some additional variances as AT& T uses additional variances
which we will cover in a later tutorial.
If we wanted to create a pure Assembly Code program which does the same thing
above we would type:
To run any executable in Linux you type ./ and the name of the binary executable.
In this case we type ./exit and press return. When we do so, nothing happens.
That is good as all we did was create a program that exited to the OS.
I look forward to seeing you all next week when we dive into more assembly code!
67
Part 1: G oals
I appreciate everyone being patient as it has taken 21 lessons to get to our first
ASM program however very necessary background had to be covered in order to
fully understand where we begin when developing assembly language.
For the most part we have been working with Intel syntax when it comes to
assembly however I am going to focus on the native AT& T syntax going forward.
It is very easy to convert back and forth between Intel and AT& T syntax as I have
demonstrated in prior tutorials.
1)Data Section: This section is used for declaring initialized data or constants as
this data does not ever change at runtime. You can declare constant values,
buffer sizes, file names, etc.
2)BSS Section: This section is used for declaring uninitialized data or variables.
3)Text Section: This section is used for the actual code sections as it begins with
a global _ start which tells the kernel where execution begins.
Critical to any development is the use of comments. In the AT& T syntax we use
use the # symbol to declare a comment as any data after that symbol on a
respective line will be ignored by the compiler.
Keep in mind, assembly language statements are entered in one statement per
line as you do not have to end the line with a semicolon like many other
languages. The structure of a statement is as follows:
A basic instruction has two parts of which the first one is the name of the
instruction or the mnemonic which is executed and the second part is the
operands or parameters of the command.
Our first program will demonstrate how to move immediate data to a register and
immediate data to memory.
Lets open VIM and create a program called moving_immediate_data.s and type
the following:
68
Part 1: G oals
./moving_immediate_data
I would like to show you what it would look like in Intel syntax as well. Before we
examine this part you will need to type sudo apt-get install nasm in a command
prompt which will install the Netwide Assembler:
./moving_immediate_data
Ok what the heck! There is no output! That is correct and you did not do anything
wrong. Many of our programs will not actually do anything as they are not much
more than sandbox programs that we will use in G D B for analysis and
manipulation.
69
Part 1: G oals
Next week we will dive into the G NU G D B debugger and see what is going on
under the hood.
I want to take some time and discuss the code at line 20 – 22 in the AT& T version
and the Intel Syntax version as well. This set of instructions takes advantage of
what we call a software interrupt. On line 20 in the AT& T Syntax, we movl $1,
%eax meaning we move the decimal value of 1 into eax which specifies the
sys_ exit call which will properly terminate program execution back to Linux so that
there is no segmentation fault. On line 21, we movl $0, %ebx which moves 0 into
ebx to show that the program successfully executed and finally we see int $0x80.
Line 20 and 21 set up the software interrupt which we call on line 22 with the
instruction int $0x80. Let’s dive into this a little deeper.
In Linux, there are two distinct areas of memory. At the very bottom of memory in
any program execution we have the Kernel Space which is made up of the
D ispatcher section and the Vector Table.
At the very top of memory in any program execution we have the User Space
which is made up of The Stack, The H eap and finally your code all of which can
be illustrated in the below diagram:
70
Part 1: G oals
When we load the values as we demonstrated above and call INT 0x80, the very
next instruction’s address in the User Space, ASM Code section which is your
code, is placed into the Return Address area in The Stack. This is critical so that
when INT 0x80 does its work, it can properly know what instruction is to be
carried out next to ensure proper and sequential program execution.
Keep in mind in modern versions of Linux, we are utilizing Protected Mode which
means you do NOT have access to the Linux Kernel Space. Everything under the
long line that runs in the middle of the diagram above represents the Linux Kernel
Space.
The natural question is why can’t we access this? The answer is very simple,
Linux will NOT allow your code to access operating system internals as that would
be very dangerous as any Malware could manipulate those components of the
OS to track all sorts of things such as user keystrokes, activities and the like.
The way that we have our code communicate with the Linux Kernel is through the
use of a kernel servies call gate which is a protected gateway between User
Space where your program is running and Kernel Space which is implemented
through the Linux Software Interrupt of 0x80.
At the very, very bottom of memory where segment 0, offset 0 exists is a lookup
table with 256 entries. Every entry is a memory address including segment and
offset portions which comprise of 4 bytes per entry as the first 1,024 bytes are
reserved for this table and NO OTH ER COD E can be manipulated there. Each
address is called an interrupt vector which comprises the whole called the
interrupt vector table where every vector has a number from 0 to 255 to which
vector 0 starts off occupying bytes 0 to 3. This continues with vector 1 which
contains 4 to 7, etc.
Keep in mind, none of these addresses are part of permanent memory. What is
static is vector 0x80 which points to the services dispatcher which point to Linux
kernel service routines.
When the return address is popped off the stack returns to the next instruction,
the instruction is called the Interrupt Return or IRET which completes the
execution of program flow.
Take some time and look at the entire table of system calls by opening up a
terminal and typing:
cat /usr/include/asm/unistd_32.h
Below is a snapshot of just a few of them. As you can see the exit 1 represents
the sys_ exit that we utilized in our above code.
71
Part 1: G oals
1)Program
2)Debug
3)Hack
Each week we will start with a program like you see here, the following week we
will take it into G D B and examine what exactly is going on at the assembly level
and finally in the third series of each week we will hack the data in G D B to change
it to whatever we want demonstrating the ability to control program flow which
includes learning how to hack malware to a point where it is not a threat.
We will not necessarily look at malware directly as I would rather focus on the
topics of assembly language programs that will give you the tools and
understanding so that ANY program can be debugged and manipulated to your
liking. That is the purpose of these tutorials.
The information you will learn in this tutorial series can be used with high-level
G UI debuggers like ID A Pro as well however I will focus only on the G NU G D B
debugger.
I look forward to seeing you all next week when we dive into creating our first
assembly debug!
72
Part 1: G oals
gdb -q moving_immediate_dat
The native syntax as I have stated many times before is AT& T syntax which you
see above. I painfully go back and forth deliberately so that you have comfort in
each however going forward I will be sticking to the AT& T syntax however wanted
to show you a few examples of both. I will state again that if you ever want to see
Intel syntax simply type set-disassembly-flavor intel and you will have what you
are looking for.
We first use the command si which means step-into to advance to the next
instruction. What we see here at _start+ 0 is you are moving the hex value of
0x64 into E AX . This is simply moving decimal 100 or as the computer sees it, hex
0x64 into E AX which demonstrates moving an immediate value into a register.
73
Part 1: G oals
We step-into again and then use the command i r which keep in mind has a space
between them to give us information on the state of the CPU registers. We can
see EAX now has the value of 0x64 hex or 100 decimal.
After we step-into again and do a disas, we see that we have then moved the
value of 0x5 0 into the buffer label as can refer back to the source code from last
week to see.
When dealing with non-register data, we can use the print command above as we
type print /x buffer and it clearly shows us that the value inside buffer is 0x5 0.
The /x designation means show us the value in hex.
Consequently you can review slide 2 of this tutorial above you see at _start+ 5 the
immediate value of 0x5 0 loaded into the buffer label or in this case the address of
buffer which is 0x804 9 09 0 and we can examine it by using the examine
instruction by typing x/xb 0x804 9 09 0 which shows us one hex byte at that
location which yields 0x5 0.
We will be doing this with every program example so that we can dive into the
debugging process. If there are any questions, please leave them below in the
comments.
I look forward to seeing you all next week when we dive into creating our first
assembly hack!
74
Part 1: G oals
gdb -q moving_immediate_data
Lets have some fun! At this point lets si once and do an i r to see that 0x64 has in
fact been moved into E AX .
75
Part 1: G oals
We can see E AX has the value of 0x64 or 100 decimal. Lets H ACK that value
now by setting E AX to say something like 0x66 by typing set $eax = 0x66.
BAM! There we go! You can see the ULTIMATE power of assembly here! We just
hacked the value from 0x64 to 0x66 or 100 to 102 decimal. This is a trivial
example however you can clearly see when you learn to master these concepts
you develop a greater power over the computer. With each program that we
create, we will have a very simple lesson like this where we will hijack at least one
portion of the code so we can not only see how the program is created and
debugged but how we can manipulate it to whatever we want.
I look forward to seeing you all next week when we dive into creating our second
assembly program!
76
Part 1: G oals
In our second program we will demonstrate how we can move data between
registers. Moving data from one register to another is the fastest way to move
data. It is always advisable to keep data between registers as much as can be
engineered for speed.
Specifically we will move the value in ED X into EAX . We will initialize this program
with a simple immediate value of 22 decimal which will go into ED X and ultimately
into EAX .
Keep in mind you can only move similar registers between each other. We know
that EAX and ED X are 32-bit registers. We know that each of these registers can
be accessed by their 16-bit values as ax and dx respectively. You can’t move a
32-bit value into a 16-bit value and vice-versa.
I look forward to seeing you all next week when we dive into debugging our
second assembly program!
77
Part 1: G oals
Lets fire up G D B and break on _ start, run the binary and disas:
As we can see the value of 0x16 or 22 decimal did move into ED X successfully.
Now lets si again.
78
Part 1: G oals
I look forward to seeing you all next week when we dive into hacking our second
assembly program!
79
Part 1: G oals
Lets fire up G D B and break on _ start, run the binary and disas:
80
Part 1: G oals
As we can see the value of 0x16 or 22 decimal did move into ED X successfully.
This is what we did in the last lesson however here we are going to hack that
value to something else.
H opefully you see some very simple patterns now that we are diving into very
simple assembly language programs. The key is to understand how to manipulate
values and instructions so that you have complete control over the binary.
We are going to continue to move at a snails pace throughout the rest of this
tutorial as my goal is to give everyone very small bite-size examples of how to
understand x86 assembly.
I look forward to seeing you all next week when we dive into writing our third
assembly program!
81
Part 1: G oals
In our third program we will demonstrate how we can move data between memory
and registers.
Specifically we will move the value of inside the constant integer of 10 decimal
into ECX .
I look forward to seeing you all next week when we dive into debugging our third
assembly program!
82
Part 1: G oals
Let’s debug!
Specifically we will move the value of inside the constant integer of 10 decimal
into ECX .
We open G D B in quiet mode and break on _ start and run by following the
commands above.
83
Part 1: G oals
After we step into twice, we now see the value of ECX as 10 decimal of 0xa hex.
I look forward to seeing you all next week when we dive into hacking our third
assembly program!
84
Part 1: G oals
Let’s hack!
Specifically we will move the value of inside the constant integer of 10 decimal
into ECX like before.
We open G D B in quiet mode and break on _ start and run by following the
commands above.
85
Part 1: G oals
As we can see when we info registers the value of ECX is 0. Let’s do a si and
another si.
As you can see the value of ECX is 10 decimal or 0xa hex as it was in the prior
lesson now lets hack that value to something else.
As you can clearly see we have hacked the value of ECX to 0x539 hex or 1337
decimal.
As I have stated throughout this series. Each of these lessons are very bite-sized
examples so that you get the hard muscle memory on how to hack through a
variety of situations so that you ultimately have a complete mastery of processor
control.
I look forward to seeing you all next week when we dive into creating our fourth
assembly program!
86
Part 1: G oals
In our fourth program we will demonstrate how we can move data between
registers and memory.
Specifically we will move the immediate value of 777 decimal into EAX . We then
move that value stored in EAX into the constant value in memory which initially
had the value of 10 decimal at runtime. Keep in mind we could have called the
value anything however I called it constant as it was set up as a constant in the
.data section.
This code is purely an academic exercise as variable data normally would be set
up under the .bss section however I wanted to demonstrate that the above is
possible to show the absolute flexibility of assembly language.
I look forward to seeing you all next week when we dive into debugging our fourth
assembly program!
87
Part 1: G oals
In our fourth program we will demonstrate how we can move data between
registers and memory.
Specifically we will move the immediate value of 777 decimal into EAX . We then
move that value stored in EAX into the constant value in memory which initially
had the value of 10 decimal at runtime. Keep in mind we could have called the
value anything however I called it constant as it was set up as a constant in the
.data section.
As you can see above we go into G D B and clearly see that the value of constant
has been replaced with 777 decimal where in the code it was clearly set at 10
decimal in line 6 of the code at the beginning of this tutorial.
We can clearly see that in line 16 of the code the value of 777 decimal was
successfully moved into EAX and into the memory value of constant.
I look forward to seeing you all next week when we dive into hacking our fourth
assembly program!
88
Part 1: G oals
We again can see above that we will move the immediate value of 777 decimal
into EAX . We then move that value stored in EAX into the constant value in
memory which initially had the value of 10 decimal at runtime. Keep in mind we
could have called the value anything however I called it constant as it was set up
as a constant in the .data section.
As you can see above we go into G D B and clearly see that the value of constant
has been replaced with 777 decimal where in the code it was clearly set at 10
decimal in line 6 of the code at the beginning of this tutorial.
We can clearly see that in line 16 of the code the value of 777 decimal was
successfully moved into EAX and into the memory value of constant.
89
Part 1: G oals
We took the very steps as we did last time with the debugging lesson. H ere we
hack the value of constant to which we hack the value from 777 to 666.
I look forward to seeing you all next week when we dive into creating our fifth
assembly program!
90
Part 1: G oals
We can place more than one value in memory as indicated above. In the past, our
memory location contained one single value. In the above as you can see the
value of constants contains 11 separate values.
This creates a sequential series of data values placed in memory. Each data
value occupies one unit of memory which is an integer or 4 bytes.
We will utilize the indexed memory mode where the memory address is
determined by a base address, an offset address to add to the base address and
the size of the data element, in our case an integer of 4 bytes and an index to
determine which data element to select.
Keep in mind an array starts with index 0. Therefore in the above code we see 1
moving into edi which is the 2nd index which ultimately goes into ebx.
We will dive deeper into this in the next lesson we debug however I want you to
take some time to study the code above and get a good grasp of what is going on.
91
Part 1: G oals
I look forward to seeing you all next week when we dive into debugging our fifth
assembly program!
92
Part 1: G oals
I want to start by addressing the question of why I use AT& T syntax. In previous
lessons I provided many ways to easily convert back and forth between AT& T
syntax and Intel syntax.
I deliberately choose this path so that it forces you to be comfortable with the
most complex flavor of x86. If you are confused with this syntax please review the
prior lessons as I go through the differences between both.
Let’s recap. We will use objdump to take a compiled binary such as the one
above that we compiled in our last lesson and show how we can view it’s Intel
source code.
Let’s load the binary into G D B and break on _ start, step a few steps and examine
6 of the 11 values inside the constants label.
93
Part 1: G oals
We then move the memory address of the constants label into edi and move the
immediate value of 25 decimal into the second index of our array. This is in
essence a source code hack as we are changing the original value of 8 to 25.
If you examine the source code you see line 18 where we load the value of 1 into
edi. Keep in mind this is the second value as arrays are 0 based.
This is our first introduction to arrays in assembly language. It is critical that you
understand how they work as you may someday be a Malware Analyst or
Reverse Engineer looking at the compiled binary of any number of higher-level
program compiled arrays.
In our next lesson we will manually hack one of the values in G D B. Keep in mind,
we will have to overwrite the contents inside an actual memory address with an
immediate value. The fun is only beginning!
I look forward to seeing you all next week when we dive into hacking our fifth
assembly program!
94
Part 1: G oals
Let’s once again load the binary into G D B and break on _ start.
As we look above we see the command print * 0x804909e. We see that it yields a
value of 5 decimal. The binary at runtime puts the values inside the constants
label to a respective memory address.
In this case we see that the pointer to 0x804909e or * 0x804909e holds 5 decimal
as we have stated above. An integer holds 4 bytes of data. The next value in our
array will be stored in 0x80490a2. This memory location will hold the value of 8.
If we were to continue to advance through the array we would move 4 bytes to the
next value and so forth. Remember each memory location in x86 32-bit assembly
holds 4 bytes of data.
Let’s hack!
95
Part 1: G oals
After we broke on _ start and ran, we examined the array like we did in our prior
lesson. H ere we hack the value at 0x80490a2 to 66 decimal instead of 8 decimal
and we can see that we successfully changed one element of the array.
This lesson is very important to understand how arrays are ultimately stored in
memory and how to manipulate and hack them. If you have any questions, please
leave them in the comments below.
I look forward to seeing you all next week when we dive into programming our
sixth assembly program!
96
Part 1: G oals
In our sixth program we will demonstrate how we can work with CMOV
instructions.
Before we dive into some code lets talk about CMOV is. CMOV can prevent the
processor from utilizing the JMP instructions and speeds up the respective binary.
Keep in mind to review the relationships between the unsigned and signed
operations. The unsigned instructions utilize the CF, Z F and PF to determine the
difference between the two operands where the signed instructions utilize the SF
and OF to indicate the condition of the comparison between the operands.
If you need a refresher on the flag please review Part 14 on Flags in this series.
97
Part 1: G oals
The CMOV instructions rely on a mathematical instruction that sets the EFLAG S
register to operate and therefore saves the programmer to use JMP statements
after the compare statement. Lets examine some source code.
Ok lets begin with lines 21 and 22. This is nothing new that we have experienced
as we are simply moving the array into ebx.
On line 24 we see the find_ smallest_ value function to where we are cycling
through the array and using the CMOVB to find the lowest value ultimately.
We see cmp %ebx, %eax to which cmp subtracts the first operand from the
second and sets the EFLAG S register appropriately. At this point the cmovb is
used to replace the value in ebx with the value in eax if the value is smaller than
what was originally in the ebx register.
After we exit the loop we see three sets of sys_ writes to first display our message,
second to display our converted integer to ascii value and then finally a period
and line feed.
98
Part 1: G oals
I look forward to seeing you all next week when we dive into debugging our sixth
assembly program!
99
Part 1: G oals
Lets break on 0x08048092 which is line 31. Lets do a r to run and then type print
$ebx. We can see the value of 7.
100
Part 1: G oals
Ok now lets break on 0x080480b1 which is line 46. Remember when we are
examining the value of answ er, it has been converted to its ascii printable
equivalent so in order to see the value of ‘ 7’ you would type x/1c & answ er.
I look forward to seeing you all next week when we dive into hacking our sixth
assembly program!
101
Part 1: G oals
Let’s now run the binary. We see that the smallest value is 7 which is expected.
Our final bit of instruction in this tutorial will teach you how to jump to any part of
the execution that you so choose.
We set $eip = 0x0804 80dd which is the exit routine. We see now that it bypasses
all of the code from the nop instruction when we broke on _ start. You now can use
this command to jump anywhere inside of any binary within the debugger.
I look forward to seeing you all next week when we wrap up our tutorial series.
102
Part 1: G oals
Part 4 0 - C onclusion
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
This has been an extensive and hopefully beneficial tutorial series for you all.
Understanding assembly language is so important to everyone when trying to
understand how Malware works in addition to programming no matter bare-metal
assembly, c, c++ or even Java, Python or iOS or Android development.
I want to thank you all for joining me on this tutorial series and look forward to you
all making an impact in the future of tomorrow!
103
Part 1: G oals
104
Part 1: G oals
This course is a comprehensive series where we learn every facet of C++ and
how it relates to the ARM 64 architecture as we will reverse engineer each step in
ARM 64 assembly language to get a full understanding of the environment.
There are roughly over 2,000 hacks a day world-wide and so few who truly
understand how the hacks are executed on a fundamental level. This course is
going to take a very basic and step-by-step approach to understanding low-level
architecture as it relates to the ARM 64.
105
Part 1: G oals
At the core of the microprocessor are a series of binary numbers which are either
+5V (on or 1) or 0V (off or 0). Each 0 or 1 represents a bit of information within the
microprocessor. A combination of 8 bits results in a single byte.
Before we dive into binary, lets examine the familiar decimal. If we take the
number 2017, we would understand this to be two thousand and seventeen.
Let’s take a look at the binary system and the basics of how it operates.
If we were to convert a binary number into decimal, we would very simply do the
following. Lets take a binary number of 0101 1101 and as you can see it is 93
decimal.
106
Part 1: G oals
When we want to convert binary to hex we simply work with the following table.
Lets convert a binary number such as 0101 1111 to hex. To do this we very
simply look at the table and compare each nibble which is a combination of 4 bits.
Keep in mind, 8 bits is equal to a byte and 2 nibbles are equal to a byte.
0101 = 5
1111 = F
Therefore 0101 1111 binary = 0x5f hex. The 0x notation denotes hex.
To go from hex to binary it’s very simple as you have to simply do the opposite
such as:
3 = 0011
A = 1010
It is important to understand that each hex digit is a nibble in length therefore two
hex digits are a byte in length.
0x5f = 95
5 = 5 x 16^1 = 5 x 16 = 80
F = 15 x 16^0 = 15 x 1 = 15
Finally to convert from decimal to hex. Lets take the number 850 decimal which is
352 hex.
We put the numbers together from bottom to the top and we get 352 hex.
107
Part 1: G oals
“Why the hell would I waste my time learning all this crap when the computer
does all this for me!”
If you happen to know any reverse engineers please if you would take a moment
and ask them the above question.
The reality is, if you do NOT have a very firm understanding of how all of the
above works, you will have a hard time getting a grasp on how the ARM
processor registers hold and manipulate data. You will also have a hard time
getting a grasp on how the ARM processor deals with a binary overflow and it’s
effect on how carry operations work nor will you understand how compare
operations work or even the most basic operations of the most simple assembly
code.
I am not suggesting you memorize the above, nor am I suggesting that you do a
thousand examples of each. All I ask is that you take the time to really understand
that literally everything and I mean everything goes down to binary bits in the
processor.
108
Part 1: G oals
0 + 0 = 0
1 + 0 = 1
0 + 1 = 1
1 + 1 = 0 (1) [ O ne P lus O ne Equals Z ero, C arry O ne]
Keep in mind the (1) means a carry bit. It very simply means an overflow.
0111
+ 0100
= 1011
We see an obvious carry in the 3rd bit. If the 8th bit had a carry then this would
generate a carry flag within the CPU.
01110000
+ 01010101
= 11000101
If we had:
11110000
+ 11010101
= (1)11000101
H ere we see a carry bit which would trigger the carry flag within the CPU to be 1
or true. We will discuss the carry flag in later tutorials. Please just keep in mind
this example to reference as it is very important to understand.
109
Part 1: G oals
Binary subtraction is nothing more than adding the negative value of the number
to be subtracted. For example 8 + - 4, the starting point would be zero to which
we move 8 points in the positive direction and then four points in the negative
direction yielding a value of 4.
We represent a sign bit in binary to which bit 7 indicates the sign of number where
0 is positive and 1 is negative.
We utilize the concept of twos compliment which inverts each bit and then finally
adding 1.
00000010
11111101
So what is the (1) you may ask, that is the overflow bit. In future tutorials we will
examine what we refer to as the overflow flag and carry flag.
110
Part 1: G oals
The system on chip we are working with has a 32-bit ARM CPU. 32-bits is
actually 4 bytes of information which make up a word.
If you remember my prior tutorial on x86 Assembly, a word was 16-bits. Every
different architecture defines a word differently.
The most significant bit of a word for our ARM CPU is located at bit 31 therefore a
carry is generated if an overflow occurs there.
0x00000000
0x00000004
0x00000008
0x0000000C
So why is this important? There is the concept of fetching and executing to which
the processor deals with instructions to which it must work in this fashion for
proper execution.
Before we dive into coding assembly it is critical that you understand some basics
of how the CPU operates. There will be a number of more lectures going over the
framework so I appreciate everyone hanging in there!
111
Part 1: G oals
Part 6 – R egisters
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
Our ARM microprocessor has internal storage which make any operation must
faster as there is no external memory access needed. There are two modes, User
and Thumb. We will be focusing on User Mode as we are ultimately focused on
developing for a system on chip within a Linux OS rather than bare-metal
programming which would be better suited on a microcontroller device.
In User Mode we have 16 registers and a CPSR register to which have a word
length each which is 32-bits each or 8 bytes each.
The chip we are working with is known as a load and store machine. This means
we load a register with the contents of a register or memory location and we can
store a register with the contents of a memory or register location. For example:
112
Part 1: G oals
str, r9 , [ r4] @
store r9 contents into location in r4, if r9 had 0x02
hex,
0x02 would be stored into location r4
The @ simply indicates to the compiler that what follows it on a given line is a
comment and to be ignored.
The next few weeks we will take our time and look at each of the special purpose
registers so you have a great understanding of what they do.
Next week we will dive into more information on the program counter! Stay tuned!
113
Part 1: G oals
We will dive into the registers over the coming weeks to make sure you obtain a
firm understand of their role and what they can do.
We begin with the PC or program counter. The program counter is responsible for
directing the CPU to what instruction will be executed next. The PC literally holds
the address of the instruction to be fetched next.
When coding you can refer to the PC as PC or R15 as register 15 is the program
counter. You MUST treat it with care as you can set it wrong and crash the
executable quite easily.
I would not suggest trying that as we are not in Thumb mode and that will cause a
fault as you would be going to an OS area rather than designated program area.
Regarding our ARM processor, we follow the standard calling convention meaning
params are passed by placing the param values into regs R0 – R3 before calling
the subroutine and the subroutine returns a value by putting it in R0 before
returning.
This is important to understand when we think about how execution flows when
dealing with a stack operation and the link register which we will discuss in future
tutorials.
When you are hacking or reversing a binary, controlling the PC is essential when
you want to test for subroutine execution and learning about how the program
flows in order to break it down and understand exactly what it is doing.
Next week we will dive into more information on the CPSR! Stay tuned!
114
Part 1: G oals
Part 8 - C PSR
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
The CPSR register stores information about the program and the results of a
particular operation. Bits that are in the respective registers have pre-assigned
conditions that are tested for an occurrence which are flags.
There are 32-bits that total this register. The highest 4 we are concerned with
most which are:
When the instruction completes the CPSR can get updated if it falls into one of
the aforementioned scenarios. If one of the conditions occurs, a 1 goes into the
respective bits.
There are two instructions that directly effect the CPSR flags which are CMP and
CMN. CMP is compare such as:
The most logical command that usually follows is BEQ = branch if equal, meaning
the zero flag was set and branches to another label within the code.
Regarding CMP, if two operands are equal then the result is zero. CMN makes
the same comparison but with the second operand negated for example:
C M N R 1, R 0 @ R 1 - (-R 0) or R 1 + R 0
When dealing with the SUB command, the result would NOT update the CPSR
you would have to use the SUBS command to make any flag update respectively.
Next week we will dive into more information on the Link Register! Stay tuned!
115
Part 1: G oals
The Link Register, R14, is used to hold the return address of a function call.
When a BL (branch with link) instruction performs a subroutine call, the link
register is set to the subroutine return address. BL jumps to another location in
the code and when complete allows a return to the point right after the BL code
section. When the subroutine returns, the link register returns the address back to
the program counter.
The link register does not require the writes and reads of the memory containing
the stack which can save a considerable percentage of execution time with
repeated calls of small subroutines.
When BL has executed, the return address which is the address of the next
instruction to be executed, is loaded into the LR or R14. When the subroutine has
finished, the LR is copied directly to the PC (Program Counter) or R15 and code
execution continues where it was prior in the sequential code source.
COD E TIME! D on’t be discouraged if you don’t understand everything in the code
example here. It will become clear over the next few lessons.
To compile:
as -o lr_demo.o lr_demo.s
ld -o lr_demo lr_demo.o
116
Part 1: G oals
It is necessary that we jump into G D B which is our debugger to see exactly what
happens with each step:
As you can see with every step inside the debugger it shows you exactly the
progression from no_return to my _function skipping w rap_up until the program
counter gets the address from the link register.
117
Part 1: G oals
This is a fundamental operation when we see next week how the stack operates
as the LR is an essential part of this process.
Next week we will dive into the Stack Pointer! Stay tuned!
118
Part 1: G oals
The Stack is an abstract data type to which is a LIFO (Last In First Out). When we
push a value onto the stack it goes into the Stack Pointer and when it is popped
off of the stack it pops the value off of the stack and into a register of your
choosing.
To compile:
as -o sp_demo.o sp_demo.s
ld -o sp_demo sp_demo.o
Once again lets load the binary into G D B to see what is happening.
119
Part 1: G oals
120
Part 1: G oals
We see the value of the sp change from 0x7 efff3a0 to 0xefff39 c. That is a
movement backward 4 by tes. Why the heck is the stack pointer going backward
you may ask!
The answer revolves around the fact that the stack grows DO W N W AR D. When
we say the top of the stack you can imagine a series of plates being placed
BE N E ATH of each other.
When we pushed r7 onto the stack, the new value of the Stack Pointer is now
0x7 efff39 c so we can see the Stack truly grows DO W N W AR D in memory.
121
Part 1: G oals
We can see the value of hex 10 or decimal 16 moved into r7 . Notice the sp did
not change.
Before we step into again, lets look at the value inside the sp.
We see the value in the stack was popped off the stack and put back into r7
therefore the value of hex 30 is back in r7 as well as the sp is back at 0x7 3fff3a0.
122
Part 1: G oals
Please take the time to type out the code, compile and link it and then step
through the binary in G D B. Stack operations are critical to understanding Reverse
Engineering and Malware Analysis as well as any debugging of any kind.
123
Part 1: G oals
Let’s take a moment to talk about what happens when we first power on our
Raspberry Pi device.
As soon as the Pi receives power, the graphics processor is the first thing to run
as the processor is held in a reset state to which the G PU starts executing code.
The ROM reads from the SD card and reads bootcode.bin to which gets loaded
into memory in C2 cache and turns on the rest of the RAM to which start.elf then
loads.
The start.elf is an OS for the graphics processor and reads config.txt to which
you can mod. The kernel.img then gets loaded into 0x8000 in memory which is
the Linux kernel.
Once loaded, kernel.img turns on the CPU and starts running at 0x8000 in
memory.
If we wanted, we could create our own kernel.img to which we can hard code
machine code into a file and replace the original image and then reboot. Keep in
mind the ARM word size is 32 bit long which go from bit 0 to 31.
As stated, when kernel.img is loaded the first byte, which is 8-bits, is loaded into
address 0x8000.
F E F F F F E A
Actually something did happen, you created your first bare-metal firmware! Time
to break out the champagne!
When the Pi boots, the below code when it reached kernel.img loads the
following:
F E F F F F E A
124
Part 1: G oals
https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-
Peripherals.pdf
I know this may be a lot to wrap your mind around however it is critical that you
take the time and read the datasheet linked above. D o not cut corners if you truly
have the passion to understand the above. READ TH E D ATASH EET!
G lad you asked! The single most dangerous malware on planet earth today is that
of the root-kit variety. If you do not have a basic understanding of the above, you
will never begin to even understand what a root-kit is as you progress in your
understanding.
Anyone can simply replace the kernel.img file with their own hacked version and
you can have total control over the entire process from boot.
125
Part 1: G oals
ARM is a load and store machine to which the Arithmetic Logic Unit only operates
on the registers themselves and any data that needs to be stored out to RAM, the
control unit moves the data between memory and the registers which share the
same data bus.
The CPU chip of this architecture holds a control unit and the arithmetic logic unit
(along with some local memory) and the main memory is in the form of RAM
sticks located on the motherboard.
126
Part 1: G oals
1)F etch Phase – The control unit grabs the instruction from memory and loads it
into the instruction register.
2)Decode Phase – The control unit configures all of the hardware within the
processor to perform the instruction.
3)E xecute Phase – The processor computes the result of the instruction or
operation.
Keep in mind, if a branch instruction occurs, the pipeline might be flushed and
start over again with a fresh set of cycles.
You now have a strong basis and background of ARM Assembly and how it works
regarding its load and store capability between memory and the respective
registers and the basics of how the instruction set flows.
127
Part 1: G oals
Part 14 - ADD
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
In ARM Assembly, we have three instructions that handle addition, the first being
AD D , the second AD C (Add With Carry) and the final AD D S (Set Flag). This week
we will focus on AD D .
H ere we see that we move decimal 67 into r1 and decimal 5 3 into r2. We then
add r1 and r2 and put the result into r0.
" So what the heck is all that and why should I care? "
This series is going to be unlike any other in it' s class. The goal is to take small
pieces of code and see exactly what it does. If you are going to understand how
to reverse a binary or malware of any kind, it is critical that you understand the
basics. Learning ARM Assembly basics will help you when reversing an iPhone or
Android. This tutorial series is going to work to take extremely small bites of code
and talk about:
1)The C ode: (H ere) we speak briefly about what the code does.
2)The Debug: We break down the binary in the G D B D ebugger and step though
each instruction and see what specifically it does to program flow, register values
and flags.
This approach will allow you to spend just a few minutes each week to get a good
grasp on what is going on behind the scenes.
128
Part 1: G oals
Again we see that we move decimal 67 into r1 and decimal 5 3 into r2. We then
add r1 and r2 and put the result into r0.
Let’s compile:
as -o add.o add.s
ld -o add add.o
gdb -q add
129
Part 1: G oals
We can see that when we b _start, break on start and r, run we see the
disassembly. If you do an i r we see the info registers where we notice our cpsr is
0x10.
130
Part 1: G oals
We notice 0x4 3 hex or 67 decimal into r1. We also notice that the flags are
unchanged (cpsr 0x10).
We can see r0 now holds 0x7 8 hex or 120 decimal. We successfully saw the add
instruction in place and we again notice that the flags register (cpsr) remains
unchanged by this operation.
131
Part 1: G oals
132
Part 1: G oals
Let’s debug:
133
Part 1: G oals
Now we see we have hacked the program so when it adds the values it will have
a different output. If you remember back to the last lecture, r0 = 120. H ere we see
we have hacked r1 and now the value of r0 is 119 !
134
Part 1: G oals
This combination of instructions will help you to get hands on experience when
learning how to have absolute control over an application and in the case of
malware reverse engineering gives you the ability to make the binary do exactly
what you want!
135
Part 1: G oals
Part 17 - ADDS
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
We add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2. We then add r1 and r2 and
place in r0.
We see adds which sets the flags in the CPSR. We have to remember when we
debug in G D B, the value of the CPSR is in hex. In order to see what flags are set,
we must convert the hex to binary. This will make sense as we start to debug and
hack this example in the coming tutorials.
as -o adc.o adc.s
ld -o adc adc.o
We need to remember that bits 31, 20, 29 and 28 in the CPSR indicate the
following:
Therefore if the value in binary was 0110 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
Z ero F lag SE T
C arry F lag SE T
136
Part 1: G oals
It is critical that you compile, debug and hack each exercise in order to
understand what is going on here.
137
Part 1: G oals
We again add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2. We then add r1 and r2
and place in r0.
Lets debug:
We again see adds which sets the flags in the CPSR. We have to remember
when we debug in G D B, the value of the CPSR is in hex. In order to see what
flags are set, we must convert the hex to binary. This will make sense as we start
138
Part 1: G oals
We need to remember that bits 31, 20, 29 and 28 in the CPSR indicate the
following:
Therefore if the value in binary was 0001 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
Z ero F lag N O T SE T
C arry F lag N O T SE T
There is nothing in code above which set the O verflow F lag however in it’s
natural state upon executing this binary it is set.
139
Part 1: G oals
We see the addition that transpires above and notice the value in r0 is 9 9
decimal after 100 decimal and 4 29 4 9 67 29 5 decimal were added together. H ow
is that possible? The answer is simple, we overflowed the 32-bit register of r0
from this addition.
If we examine the C PSR we now see 20000010 hex or 0010 0000 0000 0000
0000 0000 0001 0000 binary . We only have to focus on the most significant bits
which are 0010:
The value in binary is 0010 of bit 31, 30, 29 and 28 (N Z C V ) that would mean:
Z ero F lag N O T SE T
C arry F lag SE T
We see that the C arry F lag was set and the O verflow F lag was NOT set. Why is
that?
The C arry F lag is a flag set when two unsigned numbers were added and the
result is larger than the register where it is saved. We are dealing with a 32-bit
register. We are also dealing with unsigned numbers therefore the C F is set and
the O F was not as the O F flag deals with signed numbers.
140
Part 1: G oals
We again add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2. We then add r1 and r2
and place in r0.
Lets debug:
We again see adds which sets the flags in the CPSR. We have to remember
when we debug in G D B, the value of the CPSR is in hex. In order to see what
flags are set, we must convert the hex to binary. This will make sense as we start
141
Part 1: G oals
We need to remember that bits 31, 20, 29 and 28 in the CPSR indicate the
following:
Therefore if the value in binary was 0001 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
Z ero F lag N O T SE T
C arry F lag N O T SE T
142
Part 1: G oals
The value in binary is 0010 of bit 31, 30, 29 and 28 (N Z C V ) that would mean:
Z ero F lag N O T SE T
C arry F lag SE T
143
Part 1: G oals
We hacked r2 and changed the value to 1 decimal and 0x1 hex. NOW we know
before the C PSR went to 0010 last time however now that we hacked this, lets
see what happens to the C PSR when we step.
BAM! We hacked it and see r0 is 101 and therefore did NOT trigger the carry flag
and kept the C PSR at 0x10 hex which means 0001 binary which means:
Therefore if the value in binary was 0001 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
Z ero F lag N O T SE T
C arry F lag N O T SE T
It is so important that you understand this lesson in its entirety. If not, please
review the last two weeks lessons.
144
Part 1: G oals
145
Part 1: G oals
Part 20 – ADC
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
We add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2, 100 decimal into r3 and 100
decimal into r4 . We then add r1 and r2 and place in r0 and then add r3 and r4
and place into r5 .
We see adds which sets the flags in the CPSR. We have to once again
remember when we debug in G D B, the value of the CPSR is in hex. In order to
see what flags are set, we must convert the hex to binary. This will make sense as
we start to debug and hack this example in the coming tutorials.
as -o adc.o adc.s
ld -o adc adc.o
I want you to ask yourself what is going to happen when r3( 100 decimal) is
added to r4 ( 100 decimal) ? What do you think the value of r5 will be with the
above example of setting the flags with the adds result? Think about the first
sentence in this tutorial and keep this in mind for the next tutorial.
146
Part 1: G oals
We add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2, 100 decimal into r3 and 100
decimal into r4 . We then add r1 and r2 and place in r0 and then add r3 and r4
and place into r5 .
We see adds which sets the flags in the CPSR. We have to once again
remember when we debug in G D B, the value of the CPSR is in hex. In order to
see what flags are set, we must convert the hex to binary. This will make sense as
we start to debug and hack this example in the coming tutorials.
Last week I raised a question where I wanted you to ask yourself what is going to
happen when r3( 100 decimal) is added to r4 ( 100 decimal) ? What do you think
the value of r5 will be with the above example of setting the flags with the adds
result?
147
Part 1: G oals
Ok so we add 100 decimal and 100 decimal together in r3 and r4 and we get
201 decimal in r5 ! Is something broken? AD C is the same as AD D except it adds
a 1 if the carry flag is set. Therefore we get the extra 1 in r5 .
We again need to remember that bits 31, 20, 29 and 28 in the CPSR indicate the
following:
148
Part 1: G oals
We see the C PSR at 20000010 hex. The most significant bits of 20000010 hex in
binary is 0010.
Therefore if the value in binary was 0010 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
C arry F lag SE T
As we can clearly see the carry flag was set. I hope you can digest and
understand each of these very simple operations and how they have an effect on
the CPSR.
149
Part 1: G oals
To recap again, AD C is the same as AD D except it adds a 1 if the carry flag is set.
We need to pay particular attention to the CPSR or Status Register when we work
with AD C.
We add 100 decimal into r1, 4 ,29 4 ,9 67 ,29 5 into r2, 100 decimal into r3 and 100
decimal into r4 . We then add r1 and r2 and place in r0 and then add r3 and r4
and place into r5 .
150
Part 1: G oals
We run the program and step to where we move 4 ,29 4 ,9 67 ,29 5 into r2. Let’s hack
that value in r2 and change it to 100 decimal.
151
Part 1: G oals
Ok so now we add 100 decimal and 100 decimal together in r3 and r4 and we
get 200 decimal in r5 ! D o you remember last week when we had 201? Let’s
examine the CPSR below.
We again need to remember that bits 31, 20, 29 and 28 in the CPSR indicate the
following:
We see the C PSR at 10 hex. The most significant bits of 10 hex in binary is 0001.
Therefore if the value in binary was 0001 of bit 31, 30, 29 and 28 (N Z C V ) that
would mean:
C arry F lag N O T SE T
152
Part 1: G oals
As we can clearly see the carry flag was NOT set. I hope you can digest and
understand each of these very simple operations and how they have an effect on
the CPSR. Please take the time and review last weeks lesson for comparison.
153
Part 1: G oals
Part 23 – SU B
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
Subtraction in ARM has four instructions which are SUB, SBC, RSB and RSC.
We will start today with SUB.
Please keep in mind when you add the S suffix on the end of each such as SUBS,
SBCS, RSBS, RSCS, it will affect the flags. We have spent enough time on flags
in the prior lessons so that you should have a firm grasp on this now.
To compile:
as -o sub.o sub.s
ld -o sub sub.o
We simply take 67 decimal and move into r1 and 5 3 decimal and move into r2
and subtract r1 – r2 and put the result in r0.
154
Part 1: G oals
Part 24 – Debugging SU B
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
As stated, subtraction in ARM has four instructions which are SUB, SBC, RSB
and RSC. We will start today with SUB.
Please keep in mind when you add the S suffix on the end of each such as SUBS,
SBCS, RSBS, RSCS, it will affect the flags. We have spent enough time on flags
in the prior lessons so that you should have a firm grasp on this now.
We simply take 67 decimal and move into r1 and 5 3 decimal and move into r2
and subtract r1 – r2 and put the result in r0.
Let’s debug.
155
Part 1: G oals
As we can see the registers are clear. Lets step through and see what the value
of r0 becomes.
As you can see above r0 now has decimal 14 which works as expected.
156
Part 1: G oals
Part 25 – Hacking SU B
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
As stated, subtraction in ARM has four instructions which are SUB, SBC, RSB
and RSC. We will start today with SUB.
Please keep in mind when you add the S suffix on the end of each such as SUBS,
SBCS, RSBS, RSCS, it will affect the flags. We have spent enough time on flags
in the prior lessons so that you should have a firm grasp on this now.
We simply take 67 decimal and move into r1 and 5 3 decimal and move into r2
and subtract r1 – r2 and put the result in r0.
Let’s hack.
157
Part 1: G oals
As we can see the registers are clear. Lets step through and see what the value
of r0 becomes when we do a little hacking.
158
Part 1: G oals
As you can see above r0 now has decimal 17 which works as expected as we
hacked the value of r2 to decimal 5 0 instead of decimal 5 3.
I want to thank you all for taking this journey to learn ARM Assembly. This is the
end of the series as I encourage you all to take what you have learned and
continue to work through the ARM instruction set and continue your progress.
This tutorial’s purpose was to provide you a solid foundation in ARM Assembly
and I believe we have done that. Thank you all and I look forward to seeing you
all become future Reverse Engineers!
159
Part 1: G oals
160
Part 1: G oals
Welcome to the ARM Reverse Engineering tutorial. This is the third tutorial series
that I have done focusing on Assembly Language and Reverse Engineering.
The first series was on x86 Assembly and the second was on ARM Assembly.
This series will be an expansion series on ARM focusing on ARM Reverse
Engineering so rather than create programs directly in Assembly alone and then
Reverse Engineer the binary in Assembly we will work with Assembly and C
together and Reverse Engineer in Assembly so that you will get a flavor for a real-
world series of applications and what it looks like disassembled.
We will not be working with G UI tools such as ID A Pro as we will be working with
G D B in CLI shell. We will not be working in a traditional lab environment where
we are going to put a binary into a debugger rather we are going to SSH into the
ARM device and actually attach to a running process (PID ) and Reverse Engineer
the process as it is running.
The first 13 weeks will be an exact review of the ARM Assembly series as it is
critical that we re-examine these concepts so that we have a very firm grasp when
it comes time to reverse our binaries.
I wanted to bring back the original quote below before we get started...
“So if I go to college and learn Java will I make a million dollars and have nice
things? ”
I felt it necessary to start out this tutorial series with such a statement. This is
NOT an attack on Java as I have used Java in Android D evelopment, Spring and
JavaEE. In today’s Agile environment, rapid-development is reality. With the
increased challenges in both the commercial market and the government sector,
software development will continue to focus on more robust libraries that will do
more with less. React, Python, Java, C# and the like will continue to grow not
shrink as the race for project completion augments with each passing second of
time.
Like it or not, hardware is getting smaller and smaller and the trend is going from
CISC to RISC. A CISC is your typical x86/x64 computer with a complex series of
instructions. CISC computers will always exist however with the trend going
toward cloud computing and the fact that RISC machines with a reduced
instruction set are so enormously powerful today, they are the obvious choice for
consumption.
H ow many cell phones do you think exist on earth today? Most of them are RISC
machines. H ow many of you have a Smart TV or Amazon Echo or any number of
devices considered part of the IOT or Internet Of Things? Each of these devices
have one thing in common – they are RISC and all are primarily ARM based.
161
Part 1: G oals
“Well who cares about ARM, you can call it anything you want, I know Java and
that’s all I need to know cause when I program it works everywhere so I don’t
have to worry about anything under the hood.”
I again just want you to reflect on the above statement for a brief moment. As
every day continues to pass, more and more systems are becoming vulnerable to
attack and compromise. Taking the time to understand what is going on under the
hood can only help to curb this unfortunate reality.
This series will focus on ARM Reverse Engineering. We will work with a
Raspberry Pi 3 which contains the Broadcom BCM2837 SoC with a 4x ARM
Cortex-A53, 1.2G H z CPU and 1 G B LPD D R2 RAM. We will work with the
Raspbian Jessie, Linux-based operating system. If you don’t own a Raspberry Pi
3, they are usually available for $ 35 on Amazon or any number of retailers. If you
would like to learn more visit https://www.raspberrypi.org.
We will work solely in the terminal so no pretty pictures and graphics as we are
keeping it to the hardcore bare-bones utilizing the G NU toolkit to compile and
debug our code base.
Next week we will dive into the binary number system and compare and contrast
it with decimal and hexadecimal so we have a proper framework of understanding
to move forward.
162
Part 1: G oals
Let’s take a moment to talk about what happens when we first power on our
Raspberry Pi device.
As soon as the Pi receives power, the graphics processor is the first thing to run
as the processor is held in a reset state to which the G PU starts executing
code. The ROM reads from the SD card and reads bootcode.bin to which gets
loaded into memory in C2 cache and turns on the rest of the RAM to which
start.elf then loads.
The start.elf is an OS for the graphics processor and reads config.txt to which
you can mod. The kernel.img then gets loaded into 0x8000 in memory which is
the Linux kernel.
Once loaded, kernel.img turns on the CPU and starts running at 0x8000 in
memory.
If we wanted, we could create our own kernel.img to which we can hard code
machine code into a file and replace the original image and then reboot. Keep in
mind the ARM word size is 32 bit long which go from bit 0 to 31.
As stated, when kernel.img is loaded the first byte, which is 8-bits, is loaded into
address 0x800.
F E F F F F E A
Actually something did happen, you created your first bare-metal firmware! Time
to break out the champagne!
When the Pi boots, the below code when it reached kernel.img loads the
following:
F E F F F F E A
163
Part 1: G oals
https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-
Peripherals.pdf
I know this may be a lot to wrap your mind around however it is critical that you
take the time and read the datasheet linked above. D o not cut corners if you truly
have the passion to understand the above. READ TH E D ATASH EET!
G lad you asked! The single most dangerous malware on planet earth today is that
of the root-kit variety. If you do not have a basic understanding of the above, you
will never begin to even understand what a root-kit is as you progress in your
understanding.
Anyone can simply replace the kernel.img file with their own hacked version and
you can have total control over the entire process from boot.
164
Part 1: G oals
Today we begin our journey into the world of C++ and gaining a better
understanding of how C++ interacts with our ARM processor.
The prior lessons in this series focus on the basics of the ARM processor and
touch upon its architecture and how everything ultimately translates down to
Assembly Language and then ultimately opcodes into machine language.
We start with our first program in C++ which is our “H ello World” program. Let’s
dive in and break each line down step-by-step and see how this language
works. We will call this example1.cpp and save it to our device.
#include <iostream>
int main(void) {
std::cout << “H ello W orld” std::endl;
return 0;
}
./example1
165
Part 1: G oals
We call iostream because we need a declaration for a function called cout and
endl. The cout function allows us to print text to the standard output or terminal
and the endl function creates a new line after the text has been displayed.
The main section which is of type integer is the entry point into the main
application or binary. You will notice a void inside the ( ) which indicates that it
does not have any parameters which will be passed into the function.
You will see many examples where they declare a using namespace std; however
I will NEVER utilize this approach as it can cause naming collisions in more
complex applications.
The < < operator is referred to as an overloaded operator. They are essentially a
function very similar to printf in the C language. We are simply moving the “Hello
W orld” string into the cout function through the use of the < < overloaded
operator. We then push the endl which creates a new line to the console.
The final line is the return 0. Since our main function is of type int, we have to
return something. In C++ 11 there is no need for this in the main function however
is required for every other function. I will stick to tradition and simply include it.
The next stage is that we compile the file. The first thing that occurs is the entire
contents of the iostream header goes into the source file as we discussed. The
compile process is where the C++ code gets translated into machine code. The
next stage of compilation occurs when the rest of the lines of our existing code
are parsed through. Essentially we have all of the contents of iostream into a new
file and then all of the contents of our existing file added to a single file.
Compiling takes our text file the cpp file and converts it into an intermediate
format called an obj file. An abstract syntax tree is created which is a conversion
of constant data, variables and instructions.
Once the tree is created the code is generated. This means we now have
machine code that our ARM CPU will execute. Every cpp file (translation units)
which will have its own respective obj file associated with it.
Linking takes our obj files, our compiled files, in addition to the C++ Standard
Library and finds where each symbol and function is and link them all together
into one executable.
The concepts above may appear a bit confusing if you are new to programming
however as you code and compile and later debug and hack in Assembly
Language it will all become very clear and you will learn to master the processor.
166
Part 1: G oals
167
Part 1: G oals
Let’s debug! Let’s fire up G D B which is the G NU D ebugger to which we will break
down the C++ binary and step through it line-by-line in ARM Assembly.
This is the ARM disassembly that we are seeing. No matter what language you
program in, it ultimately will go down to this level.
This might be a bit scary to you if you did not take my prior course on ARM
Assembly. If you need to do a refresher, please link back to that series.
You are probably asking yourself why we are not debugging with the original
source code and seeing how it matches nicely to the assembly. The answer is
when you are a professional Reverse Engineer, you do not get the luxury of
seeing source code when you are reversing binaries.
This is a childishly simple example and we will continue through the series with
very simple examples so that you can learn effective techniques. We are using a
text-based debugger here so that you fully understand what is going on and to
168
Part 1: G oals
also get some training if you had to ever attach yourself to a running process
inside a foreign machine you will know how to properly debug or hack.
I will focus SOLELY on this method rather than using a nice graphical debugger
like ID A or the like so that you are able to manipulate at a very low-level.
We start with loading the link register into r11 and adding 4 to the stack pointer
and then adding it to r11. This is simply a routine which will allow the binary to
preserve the link register and setting up space on the stack.
We notice memory address 0x107 5 0 being loaded from memory to the register
r1. Let’s do a string examination and see what is located at that address.
Voila! We see our string. “Hello W orld! ” located at that memory address.
Let’s now take a look at what is inside the r1 register and then step through the
binary.
169
Part 1: G oals
We see the “Hello W orld! ” string now residing inside of r1 which resides at
memory address 0x1084 8. Finally let’s continue through the binary.
170
Part 1: G oals
Let’s once again examine the contents of the string at memory address 0x107 5 0
and continue through the execution of the program.
As you can see it holds the “Hello W orld! ” string and when we continue through it
echo’s back to the terminal as such.
Let’s hack! Let’s now overwrite the value inside of the memory address with the
string, “Hacked W orld! ” and continue execution.
171
Part 1: G oals
Woohoo! Our first hack! As you can see as you understand Assembly you have
ABSOLUTE control over the entire binary no matter what language it is written
in. In this very simple example we were able to hack the value inside the memory
address of 0x107 5 0 to which when executed it echoed, “Hacked W orld! ” to the
terminal or standard output.
Let’s now do the same procedure however lets si 3x and examine the string
inside of r1. We see that it contains, “Hello W orld! ” as it has been successfully
ldr (load from memory into the register) at main+ 12.
Let’s now set r1 to “Hacked W orld! ” and continue execution. As you can see we
now hacked it coming out of the register rather than in memory. You can clearly
begin to see there are a number of ways to hack anything and here is a simple
example of two such ways.
172
Part 1: G oals
173
Part 1: G oals
Part 17 - C onstants
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
So far we have created, debugged and hacked a simple string echo to the
standard terminal. We will expand upon that example by adding a constant.
A constant in C++ is a value that will not change throughout program execution
(unless hacked). It is used such that you have a declaration early in the code so
that if your future program architecture ever changes you can redefine the
constant in one place rather than having to update code all through your code
base.
It is standard practice to code our constants in all CAPS so that when we see it
referenced somewhere in the code we know that value is a constant.
We start with our second program in C++ which is our “Constant” program. Let’s
dive in and break each line down step-by-step and see how this language
works. We will call this example2.cpp and save it to our device.
#include <iostream>
int main(void) {
cons tint YEAR = 2017 ;
return 0;
}
./example2
174
Part 1: G oals
We then utilize the cout function to print it to the standard output or terminal and
add a new line with the endl function.
175
Part 1: G oals
Let’s debug!
As we can see the value in the memory address 0x107 30 is equal to 2017 . Let’s
continue and watch the value print to the standard output (terminal) as it did last
week when we ran it.
We can see very clearly that we move the value from memory into r1 and then we
branch to our cout function to print to the terminal. At this stage you should feel a
little more comfortable with understanding what the assembly is doing above.
176
Part 1: G oals
Let’s hack!
As we can see the value in the memory address 0x107 30 is equal to 2017 . Let’s
change that value in memory to 19 81. Let’s continue and watch the value turn to
19 81! Successful hack!
Let’s hack a second way! Re-start the program and set a breakpoint at main+28
and continue to the breakpoint.
177
Part 1: G oals
Let' s continue and we see the value in r1 is 2017 . Let’s change the value in r1 to
19 81. We continue and see the program successfully hacked to 19 81!
178
Part 1: G oals
The next stage in our journey is that of character variables. Unlike the strings we
have dealt with thus far, a character only takes up one byte of data.
Keep in mind, when we deal with any character data, we deal with literally two hex
digits which are the ASCII code that represents an actual character that we see
on our respective terminals.
Remember that each hex digit is 4 bits in length. Therefore two hex digits are 8
bits in length or a byte long.
To recap, each character translates down to an ASCII code in hex which the
processor understands. The value of n is 0x6e hex or 110 decimal. You can
review any ASCII table to see where we derived this value. This will come in
handy in the next lesson.
We start with our third program in C++ which is our “Character Variable”
program. Let’s dive in and break each line down step-by-step and see how this
language works. We will call this example3.cpp and save it to our device.
#include <iostream>
int main(void) {
return 0;
179
Part 1: G oals
./example3
We then utilize the cout function to print it to the standard output or terminal and
add a new line with the endl function.
180
Part 1: G oals
Let’s debug!
Woah! This is confusing. I don’t see any clear memory addresses being loaded
into a register to manipulate the data.
Let’s keep in mind that we are dealing with a single byte character variable.
If you remember from last week each character translates down to an ASCII code
in hex which the processor understands. The value of n is 0x6e hex or 110
decimal. You can review any ASCII table to see where we derived this value.
181
Part 1: G oals
If we step into a few times we notice the value has been placed into r3. When we
print the value in r3 we now see our ‘ n’ character.
Let’s continue.
It is important that you understand this process and understand that each
character translates into an ASCII value to which the processor loads directly into
a respective register. Our previous experience we have seen a string loaded
directly into a memory location and this is not the case here.
182
Part 1: G oals
Let’s hack!
We again see the direct value of 0x6e moved into r3 at main+ 12 which is our ‘ n’.
After stepping into 4 times and verify the value in r3 which we clearly see as ‘ n’.
Let’s hack the value in r3 to a ‘ y ’ and then reexamine the value in r3. We can now
clearly see it has been changed to ‘ y ’.
183
Part 1: G oals
184
Part 1: G oals
The next stage in our journey is that of Boolean variables. The name goes back to
the great G eorge Boole to which all modern computer science has derived.
At the lowest level a value is either 0 or 1, false or true, + < 5 volts or +5 volts, etc.
#include <iostream>
int main(void) {
return 0;
./example4
185
Part 1: G oals
186
Part 1: G oals
Let’s debug.
187
Part 1: G oals
As we can clearly see the value in isHacked is 0 or false which makes sense
based on our c++ source code.
I know these lessons may seem trivial however Reverse Engineering is all about
breaking things down in their most basic components. Reverse Engineering is
about patience and logical flow. It is critical that you take the time and work
through all of these examples with a Raspberry Pi device so that you can have a
proper appreciation for how the process actually works.
188
Part 1: G oals
Let’s hack!
Let’s break at main, run and disas in addition to step into four times.
189
Part 1: G oals
It’s that simple folks! These elementary examples will help build your mental
library of examples of how to approach everything in code and understanding how
to take control of code execution no matter what!
190
Part 1: G oals
A 32-bit register can store 2^32 different values. The range of integer values that
can be stored in 32 bits depends on the integer representation used. With the two
most common representations, the range is 0 through 4,294,967,295 (2^32 − 1)
for representation as an (unsigned) binary number, and − 2,147,483,648 (− 2^31)
through 2,147,483,647 (2^31 − 1) for representation as two' s complement.
Keep in mind with 32-bit memory addresses you can directly access a maximum
of 4 G B of byte-addressable memory.
#include <iostream>
int main(void) {
return 0;
./example5
191
Part 1: G oals
We assign the integer 7 7 7 directly into the variable my N umber and then print it
out to the terminal with the c++ cout function.
192
Part 1: G oals
Let’s review our code. I again want to include the below information from last
week’s lesson to emphasize what is going on regarding integers.
A 32-bit register can store 2^32 different values. The range of integer values that
can be stored in 32 bits depends on the integer representation used. With the two
most common representations, the range is 0 through 4,294,967,295 (2^32 − 1)
for representation as an (unsigned) binary number, and − 2,147,483,648 (− 2^31)
through 2,147,483,647 (2^31 − 1) for representation as two' s complement.
Keep in mind with 32-bit memory addresses you can directly access a maximum
of 4 G B of byte-addressable memory.
Let’s debug!
We see at main+ 12 the address at 0x107 30 loading data into r3. Let’s take a
closer look.
193
Part 1: G oals
When we examine the data inside 0x107 30 we clearly see the integer 7 7 7
present. When we continue we see 7 7 7 echoed back to the terminal which makes
sense as we utilized the cout function within c++.# linux # arm # asm # cplusplus
# reverseengineering
194
Part 1: G oals
Let’s hack!
Let’s take a look again inside the memory location of 0x107 30.
As we can clearly see the integer value of 7 7 7 appears and when we continue it
echoes out to the terminal the value of 7 7 7 which corresponds with our c++
function cout.
Let’s hack the value inside of 0x107 30 and set the value to 666 and then
reexamine the value inside 0x107 30 and continue.
195
Part 1: G oals
196
Part 1: G oals
#include <iostream>
float main(void) {
return 0;
./example6
We assign the floating-point variable directly into the variable my N umber and
then print it out to the terminal with the c++ cout function.
197
Part 1: G oals
Thus far we have a good understanding of the ARM registers however next week
we will introduce the registers within the math co-processor that work with
floating-point variables. The registers you have worked with up to now only store
whole numbers or integers and at the Assembly level, any fractional value must
be manipulated through the math co-processor registers.
198
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
Let’s debug!
199
Part 1: G oals
Let’s examine what value is inside r11-8. We clearly see it is 1337 .09 9 9 8 which
approximates our value in our original c++ code. Keep in mind a float has roughly
7 decimal digits of precision and that is why we do not see 1337 .1 so please
remember that as we go forward.
200
Part 1: G oals
We see a strange new instruction. We see vldr and the value within r11, # 8 being
moved into s0. So what is s0? We have a math co-processor which has a series
of additional registers that work with decimal or floating-point numbers. H ere we
see an example of such to which the value of 1337 .09 9 9 8 is being moved into
s0. The vldr instruction loads a constant value into every element of a single-
precision or double-precision register such as s0.
We can only see these special registers if we do a info registers all command as
we do below.
201
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
202
Part 1: G oals
Let’s examine what value is inside r11-8. We clearly see it is 1337 .09 9 9 8 which
approximates our value in our original c++ code. Keep in mind a float has roughly
7 decimal digits of precision and that is why we do not see 1337 .1 so please
remember that as we go forward.
203
Part 1: G oals
We see a strange new instruction. We see vldr and the value within r11, # 8 being
moved into s0. So what is s0? We have a math co-processor which has a series
of additional registers that work with decimal or floating-point numbers. H ere we
see an example of such to which the value of 1337 .09 9 9 8 is being moved into
s0. The vldr instruction loads a constant value into every element of a single-
precision or double-precision register such as s0.
We can only see these special registers if we do a info registers all command as
we do below.
Let’s hack!
Let’s now look at the registers and see what has transpired.
204
Part 1: G oals
As you can see we have hacked the value (less the precision issue of the float
variable accurate up to 6 decimal places)!
Finally as we continue we see our hacked value echoed back out to the terminal
when the c++ cout function executes.
205
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
./example7
We assign the floating-point variable directly into the variable my N umber and
then print it out to the terminal with the c++ cout function.
206
Part 1: G oals
207
Part 1: G oals
int main(void) {
return 0;
Let’s debug!
208
Part 1: G oals
We see the strd r2, [r11, # -12] and we have to fully understand that this means
we are storing the value at the offset of -12 from register r11 into r2. Let’s now
examine what exactly resides there.
Voila! We see 1337 .7 7 at that offset location or specifically stored into 0x7 efff230
in memory.
Let’s step into twice which executes the vldr d0, [r11, # -12] as we understand
that 1337 .7 7 will now be loaded into the double precision math co-processor d0
register. Let’s now print the value at that location below.
Finally let’s continue and watch the value echo to the terminal. This completes our
cout c++ function.
209
Part 1: G oals
int main(void) {
return 0;
Let’s debug!
210
Part 1: G oals
We see the strd r2, [r11, # -12] and we have to fully understand that this means
we are storing the value at the offset of -12 from register r11 into r2. Let’s now
examine what exactly resides there.
Voila! We see 1337 .7 7 at that offset location or specifically stored into 0x7 efff230
in memory.
Let’s step into twice which executes the vldr d0, [r11, # -12] as we understand
that 1337 .7 7 will now be loaded into the double precision math coprocessor d0
register. Let’s now print the value at that location below.
Let’s continue.
Successfully hacked!
211
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
./example8
212
Part 1: G oals
213
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
214
Part 1: G oals
Let’s break on main+ 20 as we can see the value of 4 being moved into r3.
Let’s examine what is going on at main+ 16 as we can see that we are storing into
the value of $r11-8 that which exists in r3 which in our case is 16. This makes
sense as when we examine our original code the value of my N umber was in fact
16. We can see this here when we examine the value inside $r11-8.
As we can see above the value inside $r11-12 is 4 as that represents the value
that Siz eO f is returning as the integer 16 is in fact 4 bytes wide.
215
Part 1: G oals
Finally when we continue execution we in fact see the value 4 echoed to the
terminal.
216
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
Let’s review last week’s code as we start with debugging and breaking on main.
217
Part 1: G oals
Let’s break on main+ 20 as we can see the value of 4 being moved into r3.
Let’s examine what is going on at main+ 16 as we can see that we are storing into
the value of $r11-8 that which exists in r3 which in our case is 16. This makes
sense as when we examine our original code the value of my N umber was in fact
16. We can see this here when we examine the value inside $r11-8.
As we can see above the value inside $r11-12 is 4 as that represents the value
that Siz eO f is returning as the integer 16 is in fact 4 bytes wide.
218
Part 1: G oals
Finally when we continue execution we in fact see the value 4 echoed to the
terminal.
Let’s hack!
219
Part 1: G oals
We see the value in r1 is 4 which should make logical sense as the value was
stored from r3 into r11-12 and then back to r1.
220
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
./example9
221
Part 1: G oals
222
Part 1: G oals
#include <iostream>
int main(void) {
return 0;
./example9
223
Part 1: G oals
Let’s debug.
We do our normal start in gdb and break on main. Take note at main+ 24 we are
moving the value of 1 into r3. We then see at main+ 28 we are storing that value
at r11-8 to which we will set a breakpoint and continue.
We see that when we continue through the code the value 17 is successfully
echoed to the terminal as expected.
224
Part 1: G oals
#include <iostream>
int main(void) {
int
myN ewN umber = ++myN umber;
std::cout
<< myN ewN umber << std::endl;
return 0;
./example9
225
Part 1: G oals
Let’s debug.
We do our normal start in gdb and break on main. Take note at main+ 24 we are
moving the value of 1 into r3. We then see at main+ 28 we are storing that value
at r11-8 to which we will set a breakpoint and continue.
We see that when we continue through the code the value 17 is successfully
echoed to the terminal as expected.
226
Part 1: G oals
Let’s hack! H ere were review the value in r3 which we know to be 17. Let’s hack it
to something else.
Success! As we can see when we continue we now see the hacked value echoing
to the terminal.
227
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = ++myN umber;
return 0;
}
./example10
228
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = ++myN umber;
return 0;
}
Let' s debug.
229
Part 1: G oals
As we can see the value in r3 is 16 and the value in r2 is 17. We can see that as
they are loaded from memory into the registers in * main+ 12 directly by the mov
instruction and * main+ 24 we add 1 into r3 and then put that value into r2.
As we continue we can see the cout c++ function called which echos out the
values to the terminal (standard output) as expected.
230
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = ++myN umber;
return 0;
}
Let' s debug.
231
Part 1: G oals
As we can see the value in r3 is 16 and the value in r2 is 17 . We can see that as
they are loaded from memory into the registers in * main+ 12 directly by
the mov instruction and * main+ 24 we add 1 into r3 and then put that value
into r2.
As we continue we see the c++ cout function echo our new hacked value to the
screen!
232
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = --myN umber;
return 0;
}
The value of my N umber was 16 and when it is assigned with the pre-decrement
operator we see that the new value is 15 as it is assigned into my N ew N umber.
Next week we will dive into the D ebuggin Pre-D ecrement Operator.
233
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = --myN umber;
return 0;
}
Let' s debug.
234
Part 1: G oals
Let' s break.
As we can see r3 holds 15. Keep in mind hacking this value may not be the final
place it may be stored. Remember this for next week and re-examine the debug
code above to see if you can figure it out.
As we continue we see our cout function echoing 15 for both areas as expected.
Next week we will dive into the H acking Pre-D ecrement Operator.
235
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = --myN umber;
return 0;
}
Let' s debug.
236
Part 1: G oals
Let' s break.
We re-run the binary and break and see the value here at r1 hold 15 .
237
Part 1: G oals
This is your first experience with really breaking down the registers and seeing
where things are stored and how it can affect outcome. Take time and run this
yourself so you really have a firm handle on this.
238
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = myN umber--;
return 0;
}
Next week we will dive into the D ebugging Post-D ecrement Operator.
239
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = myN umber--;
return 0;
}
We see our very simple C++ code above to which we are doing nothing more
than assigning a number into a variable to which we init another int variable and
assign the original variable to which it is post-decremented. We then output each
value to the terminal.
Let' s debug.
240
Part 1: G oals
It is clear that the value for the post-decrement operator gets loaded into r1 at
main+ 68 so let' s break at main+ 7 2.
We can clearly see that r1 does in fact hold the value of 15 to which was
decremented from our original value.
241
Part 1: G oals
242
Part 1: G oals
#include <iostream>
int main(void) {
int myN umber = 16;
int myN ewN umber = myN umber--;
return 0;
}
243
Part 1: G oals
Once again we have manipulated and changed program execution to our own
bidding. With each of these bite-size lessons you continue to get a better grasp on
the processor and how it interfaces with the binary.
I hope this series gives you a solid framework for understanding the ARM
processor. This concludes the series. Thank you all for coming along on the
journey!
244
Part 1: G oals
245
Part 1: G oals
I often wonder when I see all the latest hacks on a variety of networks, computers
and IoT devices how many people really have even the most basic understanding
of what goes on down to the microprocessor level.
For years I have published x86 and ARM Assembly and Reverse Engineering
tutorials with the intent of opening up the eyes of the public to better understand
what Assembly Language is in addition to the notion that there is actually more
than just the decimal number system.
Today we have drones, AI, IoT and smart devices that the public rarely
understands what the true impact is on their privacy or security.
Everything is Cyber. No matter what you do or where you go or where you live or
where you work you will be forced to engage " The Cyber Revolution" .
This tutorial series is your opportunity to learn FREE OF CH ARG E the very basics
of x64 Assembly. Naturally you might ask what is x64 Assembly and why would I
possibly want to understand the basics of it let alone Reverse Engineering?
Just about every computer and server today including the cloud runs on an x64
based chipset. Just about every phone, IoT and tablet device runs on an ARM
chip (with a number of exceptions). Our last tutorial series dove deep into the
ARM chip so if you would like to dive in please review the archives here on my
LinkedIn profile.
Understanding x64 will give you a better idea of the very infrastructure that
supports just about everything we do. You do not have to have any computer
science skills to take this FREE course. Simply a few minutes of your time once a
week will do.
246
Part 1: G oals
Part 2 - Transistors
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
Q uite simply the flow of electricity represents a 1 and the lack of such an
electricity flow represents a 0. This is a boolean on or off architecture to which we
need to take a deeper dive into the binary number system at a later time.
I deliberately try to keep these lessons short so that it draws the largest audience
to take just a few minutes each week to properly grasp some complicated
architectures.
Next week we will touch on logic gates and discuss how the combination of such
gates make up the core of how the processor works. We will only discuss them on
a high level as it would be an entire additional course in electrical engineering to
really get into how the processor is made to which we will stick to the basics and
spend more of our time on how to program the chip.
247
Part 1: G oals
After some basics about the processor and an introduction to the binary and
hexadecimal number systems we will build our very own bootable operating
system.
248
Part 1: G oals
In our last tutorial we spoke briefly about binary to which we represent as either
true or false. In binary, true is equal to 1 and false is equal to 0. Computers are
ultimately built on this very simple concept to which at the core we have four
possible logic gates which can be combined in an infinite amount of sequences.
In an AND G ate there are two binary values to which outputs 1 only if both binary
values are 1.
In a NOT G ate it simply takes a single binary value and negates it.
" The Why..." Ok so why am I going over this? What does this have to do with
understanding Assembly or Reverse Engineering? Well... At the very CORE of all
processors are these simple logic gates that when combined together form
complex instructions. I could spend literally years showing you this in practice
249
Part 1: G oals
however I will leave that for another to pick up the charge. What is important is
that you get a basic understanding of what is going on here when we ultimately
see instructions such as AND , OR, X OR, etc when we code in Assembly and
more importantly when we Reverse Engineer.
Stay tuned! We will be building our own very SIMPLE Operating System shortly!
250
Part 1: G oals
It really all breaks down to 1 and 0. No matter how sophisticated the future
frameworks evolve they all including interpreted languages ultimately use a JVM
or the like and go down to Assembly then Machine Code then binary.
Why would we need to even talk about number systems? Why is it relevant to our
series here? The answer is simple. In addition to everything going down to 1 and
0, the instructions and memory in addition to the processor registers all utilize
another number system called hexadecimal.
Let’s discuss binary! At the core of the microprocessor are a series of binary
numbers which are either +5V (on or 1) or 0V (off or 0). Each 0 or 1 represents a
bit of information within the microprocessor. A combination of 8 bits results in a
single byte.
Before we dive into binary, let' s examine the familiar decimal. If we take the
number 2017, we would understand this to be two thousand and seventeen.
Let’s take a look at the binary system and the basics of how it operates.
If we were to convert a binary number into decimal, we would very simply do the
following. Let' s take a binary number of 0101 1101 and as you can see it is 93
decimal.
251
Part 1: G oals
When we want to convert binary to hex we simply work with the following table.
Let' s convert a binary number such as 0101 1111 to hex. To do this we very
simply look at the table and compare each nibble which is a combination of 4 bits.
Keep in mind, 8 bits is equal to a byte and 2 nibbles are equal to a byte.
Therefore 0101 1111 binary = 0x5f hex. The 0x notation denotes hex.
252
Part 1: G oals
To go from hex to binary it’s very simple as you have to simply do the opposite
such as:
It is important to understand that each hex digit is a nibble in length therefore two
hex digits are a byte in length.
Finally to convert from decimal to hex. Let' s take the number 850 decimal which is
352 hex.
“Why the hell would I waste my time learning all this crap when the computer
does all this for me!”
I am not suggesting you memorize the above, nor am I suggesting that you do a
thousand examples of each. All I ask is that you take the time to really understand
that literally everything and I mean everything goes down to binary bits in the
processor.
We as humans operate on the base 10 decimal system. Let’s expand our mind to
base 2 binary and base 16 hexadecimal!
253
Part 1: G oals
Keep in mind the (1) means a carry bit. It very simply means an overflow.
We see an obvious carry in the 3rd bit. If the 8th bit had a carry then this would
generate a carry flag within the CPU.
If we had:
H ere we see a carry bit which would trigger the carry flag within the CPU to be 1
or true. We will discuss the carry flag in later tutorials. Please just keep in mind
this example to reference as it is very important to understand.
254
Part 1: G oals
Binary subtraction is nothing more than adding the negative value of the number
to be subtracted. For example 8 + - 4, the starting point would be zero to which
we move 8 points in the positive direction and then four points in the negative
direction yielding a value of 4.
We represent a sign bit in binary to which bit 7 indicates the sign of number where
0 is positive and 1 is negative.
We utilize the concept of twos compliment which inverts each bit and then finally
adding 1.
Add 1.
So what is the (1) you may ask, that is the overflow bit. In future tutorials we will
examine what we refer to as the overflow flag and carry flag.
255
Part 1: G oals
Before we dive into the architecture lets talk about how we define various bits and
how they are structured within the processor.
In both x64 and x86, we define a byte as 8 bits. We define a word as 16 bits. We
define a double word as 32 bits and a quadword as 64 bits. Finally we define a
double quadword as 128 bits.
Intel processors store bytes as what we refer to as " little endian," meaning lower
significant bytes are stored in lower memory addresses. Lets give an example of
a simple 16-bit or 2 byte value. On disk - 0xAABB. When it goes into memory it is
stored as 0xBBAA as I hope this provides a good visual as this concept can be
quite confusing.
Keep in mind, 8 bits make up a byte. 4 bits are also called a nibble which are
equivalent to one hex digit.
256
Part 1: G oals
The term " x64" includes both AMD 64 and Intel64. The instruction sets are similar.
x64 extends x86' s 8 general-purpose registers to be 64-bit, and adds 8 new 64-bit
registers. The 64-bit registers have names beginning with " r" , so for example the
64-bit extension of eax is called rax. The new registers are named r8 through r15 .
The lower 32 bits, 16 bits, and 8 bits of each register are directly addressable in
operands. This includes registers, like esi, whose lower 8 bits were not previously
addressable. The following table specifies the assembly-language names for the
lower portions of 64-bit registers.
257
Part 1: G oals
The high 8 bits of ax, bx, cx, and dx are still addressable as ah, bh, ch, dh, but
cannot be used with all types of operands.
The instruction pointer, eip, and flags register have been extended to 64 bits (rip
and rflags, respectively) as well.
258
Part 1: G oals
The first two D WORD or smaller arguments that are found in the argument list
from left to right are passed in ecx and edx registers; all other arguments are
passed on the stack from right to left.
If you have two parameters you are passing from a function, for example int x and
int y and it is a Q WORD , x will go into rcx and y will go into rdx.
If you have five parameters you are passing for example int a, int b, int c, int d, int
e and it is a WORD in length, a will go into cx, b into dx, c into r8w , d into r9w
and e into the stack.
259
Part 1: G oals
There are four boolean instructions to which exist are AND , OR, X OR and
NOT. Earlier in this tutorial we briefly discussed gates which took advantage of the
same logic down to the metal. We will see this logic throughout our reversing so it
is important to understand what it does down at the individual bit level.
AND = If the first number has a 0 and the second number has a 0, the result is 0.
AND = If the first number has a 0 and the second number has a 1, the result is 0.
AND = If the first number has a 1 and the second number has a 0, the result is 0.
AND = If the first number has a 1 and the second number has a 1, the result is 1.
ex: 0 0 1 0 0 0 1 0
ex: 0 1 1 0 1 1 1 0
ex:— — — — — — —
ex: 0 0 1 0 0 0 1 0
OR = If the first number has a 0 and the second number has a 0, the result is 0.
OR = If the first number has a 0 and the second number has a 1, the result is 1.
OR = If the first number has a 1 and the second number has a 0, the result is 1.
OR = If the first number has a 1 and the second number has a 1, the result is 1.
ex: 0 0 1 0 0 0 1 0
ex: 0 1 1 0 1 1 1 0
ex:— — — — — — —
ex: 0 1 1 0 1 1 1 0
X OR = If the first number has a 0 and the second number has a 0, the result is 0.
X OR = If the first number has a 0 and the second number has a 1, the result is 1.
X OR = If the first number has a 1 and the second number has a 0, the result is 1.
X OR = If the first number has a 1 and the second number has a 1, the result is 0.
ex: 0 0 1 0 0 0 1 0
ex: 0 1 1 0 1 1 1 0
ex:— — — — — — —
ex: 0 1 0 0 1 1 0 0
260
Part 1: G oals
ex: 0 0 1 0 0 0 1 0
ex:— — — — — — —
ex: 1 1 0 1 1 1 0 1
261
Part 1: G oals
Part 11 - Pointers
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
x64 utilizes the flat memory model to which we have one large array of addresses
that exist within the processor.
A pointer is nothing more than the address of a specific value in memory. Let’s
take an example:
To get the value inside rax at 0x10, you would use the following syntax:
Let’s assume the value inside memory 0x10 was 0x20 therefore rax points to the
value inside 0x10 which when you dereference by [rax] contains 0x20. 0x20 is
the value inside of the register rax.
If we do:
This will put the value of 0x66 into the memory location at 0x10. We know that the
value inside 0x10 memory location was 0x20 so therefore the new value inside
the memory at 0x10 will be 0x66.
This can get confusing however when we get into code over the coming months
this will become more apparent.
Next week we will dive into load effective address! Stay tuned!
262
Part 1: G oals
When a binary executes in RAM the OS will unmap the code into a data segment
where it finds free space in memory.
Load Effective Address loads a given memory address as a pointer to any given
variable. For example:
In C++, a pointer actually adds what the user would see as one if something was
incremented however it is actually moving it 2 bytes forward under the hood
assuming it is a word in length or 16 bits or 2 bytes. Same thing.
inc rax
Let’s say the value of 0x20 is in rbx. This above instruction will place the value of
0x20 into a non-word boundary which will result in an error. You would have to
increment rax by 2 to ensure that does not happen.
Next week we will dive into the data segment! Stay tuned!
263
Part 1: G oals
The data segment allocates memory on the heap in memory rather than the stack
as they are not local variables they are known throughout the entire binary.
There are SSE math registers which are separate from the CPU which hold the
following:
1)real4 - This is a single or what you would think of as a floating point numbers as
this is 4 bytes long.
Finally there are arrays which can be single or multidimensional arrays where you
can allocate against a db, dw, dd, dq, xmmword or ymmword.
We will see this in code when we get more advanced into the series however its
critical that you understand the variables within a function are local and go to the
stack as they do not last throughout the program. These variables which are part
of the data segment are not local they are global and go to the heap.
The stack - local vars - grows down in memory so they start at a high memory
address and grow down. The heap - global vars - grows from a lower memory
address and grows up.
If you have questions please ask them in the comments as it is critical you get this
concept down when we start to build our very basic operating system.
264
Part 1: G oals
Let’s assume the register al holds 01010101b which is an 8-bit binary value. Let’s
assume the instruction is shl al, 2. Below is what transpires as we see the values
move two bits to the left.
00010101
00010101
10100000
265
Part 1: G oals
Let’s assume the register al holds 00010100b which is an 8-bit binary value. Let’s
assume the instruction is shr al, 2. Below is what transpires as we see the values
move two bits to the left.
00010100
00010100
00000101
266
Part 1: G oals
Part 16 - R O L Instruction
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
In our simple x64 example on an Ubuntu Linux machine above we see we mov 1
into al and rotate left by 1 bit.
ld -o test test.o
We can see here in the debugger that al starts with 1 and when we rotate left it
goes to 10b.
You can ROL with additional bits as well. The logic would remain the same as the
bits will rotate left just as we demonstrated above.
267
Part 1: G oals
Part 17 - R O R Instruction
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
In our simple x64 example on an Ubuntu Linux machine above we see we mov
1 into al and rotate right by 1 bit.
ld -o test test.o
We can see here in the debugger that al starts with 1 and when we rotate right it
goes to 10000000b.
Next week we will dive into Boot Sector Basics! Stay tuned!
268
Part 1: G oals
Over the next few tutorials we are going to write a very basic x86 Operating
System to which we will use Q EMU which is a full system emulator or OS
emulator. You could also install VirtualBox and ultimately convert our boot loader
to an ISO if you so choose.
At the very core of a computer booting is what we refer to as the boot loader. The
boot loader physically reads the first sector or sector 0 from your H D or other
media to ultimately bootstrap an OS.
When the computer boots it reads the first sector which is exactly 0x200 bytes
(hex) or 512 bytes in decimal.
The system that is reading this boot loader is what is referred to as BIOS which is
a basic input output system and it loads in 16-bit mode. It does this to be
compatible with older processors. Modern processors immediately switch to what
we refer to as UEFI which is a more sophisticated IO system however we will
focus on the very basics here with BIOS.
Next week we will discuss what exactly goes on when BIOS reads the boot
sector.
269
Part 1: G oals
We are at the stage where we are going to start integrating real-world code. If you
do not have an active linux desktop I would suggest you get Virtualbox and
Ubuntu on either your Windows or Mac. I have a prior tutorial that will walk you
through this process below. For some reason I am not able to embed the link so
please just copy and paste it into your browser.
You will additionally need a text editor for the terminal. I use VIM. You will find a
link to set that up as well below.
In addition you will have to install nasm so you may simply type:
NASM is the assembler we will use and we will focus on the intel syntax. First go
into the terminal and fire up Vim and type the following:
Remember to type ' i' to insert and then ' esc' and ' wq' to go into command mode
and save your file.
The above line simply sets an infinite loop and does nothing more. The loop label
is created to which we simply j mp back to itself. This code in itself will compile
however it will not run in an OS as it does not trigger what we refer to as the
magic number to which BIOS looks to understand this is the end of your boot
sector. We will cover more on that in future lectures.
We type the above command assuming you saved your file in vim as
bootsector.asm. This will create a binary file to which we will examine the
contents within a hex editor. A hex editor is an application that examines each
byte of data that is compiled into a file. We will see that our assembly instructions
above will ultimately get translated down to their raw opcode values. The
processor only understands raw opcodes which are simply operation codes.
Below is a link to a table identifying the opcodes. I saved you the effort of
referencing the intel dataset as it is literally thousands of pages and several
volumes:
Let' s use a hex editor like ghex and open up our bin file.
270
Part 1: G oals
We see E B F E which are hex bytes and each letter is a nibble (a nibble is 4 bits
or half a byte). Both E B F E make up two full bytes. Keep in mind the processor
reads from disk in reverse byte order such that F E gets read first and then E B.
This process is called little endian and is how the x64 processor works.
If you review the table to which I provided the link you will see that F E represents
an IN C or increment by one. This is our loop value.
Next you will find that E B stands for J MP which is our jump instruction above.
This is alot of information if you are new to assembly. Take it step-by-step and
follow along with me in a real linux OS and with each lesson you will get a better
understanding of the basics.
Next week we will build upon this lesson by adding some simple data to our
binary.
271
Part 1: G oals
For those of you that are familiar with assembly these next several weeks/months
might seem like we are progressing very slowly however the aim is to help those
with little understanding of hardware to get a better understanding of the very
systems that power the cloud.
The vast majority of AWS and Azure as well as many other cloud services utilize
x64 based operating systems. Understanding what happens when these systems
boot is of significant value and that is why we are going to go thorough a very
slow process looking at each piece of a boot sector when a machine loads.
Last week we learned the opcodes for line 1 and 2 to which we do not have to
review. Today we add a byte of data into our code. Notice this is a hexadecimal
number and will match our binary upon inspection. In future lessons we will see
how it looks when we do decimal and other systems.
Let' s compile. If you do not have NASM installed please ensure you type sudo
apt-get install nasm.
Let' s look at our binary in a hex editor. I use G H ex as I keep to the G NU tradition
as we will in future lessons use the G NU debugger called G D B. These are all on
your Linux systems as I am using Ubuntu for these tutorials.
272
Part 1: G oals
Next week we will keep adding to our code and progress in our OS development
series.
273
Part 1: G oals
Today we continue our Boot Sector Basics. Let' s examine the code:
Closely examine the above. We see our original code which we do not have to
review however now we see a series of numbers, hex numbers that represent
ASCII characters. We see that each letter corresponds with a letter. When we say
that ultimately everything goes down to 0 and 1 this is a proof of concept. As you
can see E B is selected above and we can see those hex values ultimately go to
11101011 in binary.
H omework: G oogle and research the ASCII conversion table and do some
research on your own and better understand how hex values represent
characters.
274
Part 1: G oals
We are simply adding a padding algorithm on line 7 that simply examines how
many bytes are left after we subtract 200h or 512 and then it pads the remaining
bytes with zeros. At the end you will see what we refer to as the magic number
which is 0xaa5 5 as this is a signature that the cpu is looking for to identify a boot
sector. Remember this code is at sector 0 when it boots as there is no file system
so if it finds the successful signature it will attempt to boot it.
We build the binary with the code above. Now let' s look at the code in the hex
editor.
As you can see it pads out the remaining bytes up to 200h or 512 with 0' s as we
anticipated. Below is the remainder of the binary.
275
Part 1: G oals
As you can see at the very end we have 5 5 AA. We remember that our processor
is little endian so when we code it it was aa 5 5 and which is in it' s mapped format.
When it goes into the cpu it reverses the byte order. This is critical that you
understand this.
Next week we will simply do nothing more than launch our new operating system.
Stay tuned.
276
Part 1: G oals
This week we will focus on how to use Q EMU which is an emulator to boot our
simple new OS.
You will see the following. Keep in mind it does nothing but an infinite loop jump
which we discussed in detail in previous lessons. This however is the most basic
x86 OS one can create.
It simply looks for the signature which we spoke of last week (if this does not
make sense please review last weeks lecture) and if it is exactly 200h bytes and it
is placed at the first sector of the boot medium the process will be successful.
If you are interested there are different emulators for different architectures.
Next week we will discuss memory addressing so that we can set up a stack
within our simple os.
277
Part 1: G oals
1)0x0 = Interrupt V ector Table - This is where our interrupt table exists at the
very base of memory. This is where all of our interrupt calls exist.
2)0x4 00 = BIO S Data Area - This stores variables about the state of the bootable
device.
3)0x7 c00 = L oaded Boot Sector - This has our machine code that will be loaded
into RAM by the bootloader firmware (note: firmware is simply code that runs
before an OS runs like what we are doing).
4)0x7 e00 = F ree - This is your stack area that you can develop in.
5)0x9 fc00 = E xtended BIO S Data Area - H olds data from disk track buffers and
other connected devices as remember there is no file system as of yet.
6)0xa0000 = V ideo Memory - BIOS maps your video memory here at boot.
This is critical that you understand how memory is laid out at boot. In our next
lesson we will create a simple stack at 0x7 e00.
278
Part 1: G oals
Today we will put all the pieces together. We will create our custom OS that does
nothing but boot-up, set a video mode and then only accept numeric digits in the
console. This is the final tutorial in this mini-series of Boot Sector Basics.
The first thing we do is move to the programable area of the boot sector code at
address 0x7c00. We then set the stack base and identify the area for our stack
and set the base pointer into the stack pointer.
We then call our video mode function where we set a 640x200 greyscale console.
We then call our get character input function that will only allow digits 0 to 9 as
you can see 0x30 is the hex ascii value for 0 and 0x39 is the hex ascii value of 9.
If the user types anything else in the console literally nothing will enter into the
console. This is the absolute control you have in Assembly.
279
Part 1: G oals
As you can see I am only able to type numeric digits in our OS. Try it for yourself.
Write the code and compile and run in the qemu editor. If you do not have qemu
installed I show you in detail how to install it in the last two tutorials.
Take the time to really review what I am doing here as it is critical to understand
that this is how your computer boots before going into 32 then 64-bit mode.
Next week we will simply discuss the high-level concept of how your computer
bridges a 64-bit OS.
280
Part 1: G oals
Before we dive into x64 Assembly I want to talk very briefly about what we refer to
as long mode.
When the computer boots it needs to enable what we refer to as the A-20 line. In
early architectures, processors had 20 address lines which were A-0 to A-19 to
which could access 2 to the power of 20 bytes of information. The A-20 line is an
external memory reference containing a 16-bit offset address added to a 16-bit
segmented number which shifts 4 bits to get the additional access.
This process combined with the G lobal D escriptor Table allows you to work with
your Control Register to to execute a far jump to enter protected mode which is
32-bits.
Long mode which is 64-bit mode which we are all familiar with in our modern
architectures extend the address space to access 0xFFFFFFFFFFFFFFFF.
This topic alone can take weeks to explain however I wanted to at a very high
level touch base on the fact that the processor needs to bridge to 32-bit mode and
then finally to 64-bit through setting the A-20 line, working with the control register
and G D T in combination with paging.
I took several months to get to this point so that you have a basic understanding
of Assembly as we will start to get into actual 64-bit Assembly in the following
tutorials and then our C++ tutorial to which we will reverse engineer each code
block into 64-bit Assembly.
281
Part 1: G oals
Today we begin our actual x64 code basics. Over the next few weeks I will create
very simple examples so we get a grasp of the x64 architecture. Let' s start with a
basic code block:
We begin by declaring the .data section to which all of our global data is stores. If
we had a string or some other form of hard coded data it would go in that block. In
our example we will leave it empty.
The .text section declares where the entry point of the program will begin in our
case we use _start or you can use main.
We simply move the value of decimal 16 or hex 10 into the 64-bit RAX register.
We will see in a moment that the processor will use only the lower EAX when we
debug in G D B.
The last piece is just a simple exit routine which we move 60 into RAX and then
syscall. It simply returns operation back to the OS.
Let' s debug in G D B:
Let' s set the debugger for intel syntax and set a break on start:
As we can see 16 decimal or hex 10 is about to be moved into EAX but as we can
see it has not been completed until we step forward.
282
Part 1: G oals
We will spend several weeks on these simple examples so you can get
comfortable with how the processor operates and its internal workings.
283
Part 1: G oals
As we can see we are moving 0x10 into R AX and adding 0x05 into R AX .
284
Part 1: G oals
We see 0x15 or 21 decimal moved into R AX . Take the time to carefully try these
very simple examples as we go forward.
285
Part 1: G oals
Today we continue our tutorial with a simple subtract example. Let' s examine the
source code:
As we can see very we load 16 or 0x10 hex into E AX and then subtract 5 from it
in the next instruction.
286
Part 1: G oals
As we can see the result is 0xb hex or 11 decimal as expected. It is important that
you try these simple examples to get a grasp of what happens when we start to
debug C++ code in future tutorials.
287
Part 1: G oals
Today we will code our simple, " hello world" program in x64 Assembly.
We simply create a string in the .data section and add a return character at the
end of the statement. We then perform a simple write call which utilizes the OS' s
interrupt vector table to spit out our string in the standard output or terminal.
As we can see " Hello W orld!" has been echoed to the terminal. Next week we
will debug this simple program in G D B.
288
Part 1: G oals
This lecture will be a bit longer than most however it is important that you all take
the time to really code and practice the topics discussed below. Let' s review our
code:
As we can see from last week we successfully created our simple " H ello World"
program successfully.
In prior lessons I touched upon the x64 register set however I will review again
with this table:
289
Part 1: G oals
In our example above we used the label of text not to be confused with the .text
section. Our compiler will take all of our labels and determine an actual mapped
memory location and replace each label with the memory in the actual binary file.
290
Part 1: G oals
In last week' s lecture' s comments, Aaron pointed out something that is very
critical that you understand when looking at Assembly in an Operating System vs
Firmware such as the code we created for our Operating System in our prior
lectures.
Aaron carefully pointed out in the comments last week that a SYSCALL is
completely dependent on the operating system. System calls will differ depending
on the OS because each OS has a different Kernel and each have their own
vector interrupt tables which have an ID associated with them with a
corresponding number value.
There are 328 SYSCALLS in a traditional linux kernel. As we see above in our
code we use both the SY S_W R ITE and SY S_E X IT. Let' s illustrate:
291
Part 1: G oals
Please take a moment to look at our code above to see how this works. In
SY S_W R ITE we load 1 into R AX which is our SYSCALL. We load 1 into R DI
which is our first argument which represents our standard output (0 = standard
input & 2 = standard error). Our second argument is loaded into R SI which is our
text label to which when compiled will have an actual memory address as you will
see this in a debugger. Finally our third argument will hold the string length which
is 13 in our case and loaded into R DX . As an exercise I want you to write out how
SY S_E X IT does the same and keep in mind there is only 1 argument there.
PLEASE REVIEW the code above to firmly understand this before moving on!
In addition we have our _start label to which our respective operating system will
look for otherwise it will throw an error when it seeks to find an entry point to our
code. The global declaration tells the linker the actual address of the data.
292
Part 1: G oals
Compile...
D ebug...
As we can see " Hello W orld" with the return character will then be moved into our
R SI register.
293
Part 1: G oals
Let' s compile...
We debug and see the string being moved into 0x6000d8 and then R SI.
Just to verify we can see the string at the aforementioned address. NOW FOR A
BIT OF FUN :)...
H ere we demonstrate we have the power to simply hack and redefine the string in
memory. We are simply setting a char byte length and setting a new string.
294
Part 1: G oals
We continue and run through the binary and see that our hack continues through
R SI.
Finally we see when we run the binary we have successfully hacked it' s
operation. This is a very simple example however shows the power of truly
understanding assembly at this level. G UI debugger tools will also provide this
functionality however I like to use the command line tools so that they could be
used on every environment.
The purpose of these tools is to UND ERSTAND how this is done and what to look
for when you are professionally reversing in real-time. You need to understand
how an attacker can alter memory and/or instructions. We need more professional
RE' s to help defend infrastructures throughout the world and hopefully these
tutorials motivate you toward a career in such.
295
Part 1: G oals
Today we start our RE with the C++ language. The vast majority of malware is
written in C++ and walking through simple code examples over the coming
months and breaking them down in a debugger will give you a real hands-on
approach to learning true RE.
We will use Kali Linux going forward with Radare 2. You can get VirtualBox and
download the Kali Linux x64 Appliance to follow along.
H ere we simply create a main function and use the C++ output stream library to
output the text " H ello World" with a new line at the end to the terminal. Let' s
compile and link:
Next week we will introduce Radare 2 and debug the code and examine what it
looks like in x64 Assembly.
296
Part 1: G oals
Compile:
Run:
For literally years I have been using G D B as the debugger of choice. The reason
is that it is on every Linux based system which runs just about every IoT and
Server in the world. In addition, there are versions for Windows.
I have struggled hard with this but have decided to introduce another terminal
based debugger called Radare 2. The reason I like Radare 2 so much is that it is
still terminal based yet more robust with its feature set. If you are running a Kali
Linux VM like I am here you can simply the below.
Let' s open up our binary for write mode and simply analyze the binary.
Ok, there is a lot going on here. Let' s break it down. First, we open up Radare 2 in
write mode by typing ' r2 -w ./1' and then use the ' aaa' command to analyze the
binary. We then use ' s sy m.main' to seek to the main routine of the binary which
is our entry point. We then do a ' pdf' command to disassemble the binary.
We see what we refer to as the prologue where we push rbp the stack base
pointer onto the stack. We then move rsp into rbp for safe keeping and then we
reserve 0x10 hex bytes or 16 decimal bytes on the stack to make room for our
string.
297
Part 1: G oals
If none of this makes sense please go back to the beginning of the tutorial series
to review basic assembly and the registers as it is CRITICAL you understand this
before we move forward.
We can clearly see the qword of ' Hello W orld\ n' at memory address 0x2005 and
then we see our C++ library call for the output stream which is cout to display our
string to the terminal.
Next week we will hack the value and modify the binary. I highly encourage you all
to install VirtualBox which is free and get the Kali Linux VirtualBox image and
install Vim as well.
There are tutorials on all of this in my prior series. Stay tuned for the hack next
week!
298
Part 1: G oals
Compile:
Run:
Let' s remember this line above when we compare against our hacked binary.
Let' s open up our binary for write mode and simply analyze the binary.
Ok, there is a lot going on here. Let' s break it down. First, we open up Radare 2 in
write mode by typing ' r2 -w ./1' and then use the ' aaa' command to analyze the
binary. We then use ' s sy m.main' to seek to the main routine of the binary which
is our entry point. We then do a ' pdf' command to disassemble the binary.
We see what we refer to as the prologue where we push rbp the stack base
pointer onto the stack. We then move rsp into rbp for safe keeping and then we
reserve 0x10 hex bytes or 16 decimal bytes on the stack to make room for our
string.
If none of this makes sense please go back to the beginning of the tutorial series
to review basic assembly and the registers as it is CRITICAL you understand this
before we move forward.
We can clearly see the qword of ' Hello W orld\ n' at memory address 0x2005 and
then we see our C++ library call for the output stream which is cout to display our
string to the terminal.
299
Part 1: G oals
BOOM! As we can see we have hacked the value and when we quit Radare 2 it
will write it and modify our binary as such.,
As you can see we have hacked the binary! This is very basic but now you have
an elementary level of understanding of Reverse Engineering a C++ binary.
Next week we will continue our journey into C and step-by-step reverse
engineering.
300
Part 1: G oals
Congrats you wrote, compiled and hacked your first C++ program. For the rest of
this tutorial I am going to focus on the father of all programming languages from
" H ello World" to web servers in the programming language to which ALL modern
languages come from C.
Like the variety of religions there are programming languages. Nonetheless there
is the ROOT religion or language to which all spawn which is C. I am going to
over the next several months teach you C and Reverse Engineer each binary so
you have a mastery over the MASTER language of all existence.
Think of C as if you are in church where Python or Java or C# you are in a secular
environment. C will allow TOTAL and complete control over your program or
environment where Java or Python will allow only partial control however they are
NECESSARY languages in today' s rapid development business logic
environments.
In our next lesson we begin with the basic " hello world" program as we did in our
prior lesson however we now will work with C. Remember Einstein - " I w ant to
know G od' s thoughts, the rest are details." This is the difference between C
and any other language you are sitting at the ROOT of engineering design for
portable systems!
301
Part 1: G oals
Ok so what now? Where are we in the world? What is our purpose? What shall I
focus on? What shall I learn?
There are over 30 billion devices connected to the Internet today. Nonetheless,
the common thread in all basic architecture is the C programming language.
4)TR AN SPO R T L AY E R - Reliably deliver data between two hosts which must
split it up into chunks to send. Protocols associated: NetBIOS, TCP, UD P, etc...
7)APPL IC ATIO N L AY E R - Web browsers and apps that use web interfaces like
email, etc. Protocols associated: D H CP, D NS, H TTP, H TTPS, POP3, SMTP, FTP,
TELNET, etc...
This is an important series of concepts that you must understand in any basic
networking. This is NOT a course in networking as we will touch BRIEFLY on
these concepts so I would suggest you find a free course on YouTube for
networking if you are stuck. I want to get through some basic theory so we can
work with C networking apps.
302
Part 1: G oals
Part 39 - W hy C ?
For a complete table of contents of all the lessons please click below as it will give
you a brief of each lesson in addition to the topics it will
cover. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial
A small level above that we are at machine code which is a series of hex digits
which translate into machine instructions and/or data.
303
Part 1: G oals
Ok it is time we look at the most basic C program, debug it and hack it. If we are
to have mastery we must create and destroy in a single-step so that we have
mastery over the domain.
Let us fire up VIM and type out the following. We include our standard library and
create a main function to which we use the library function of printf to echo a
string of chars and since the type of main is int meaning integer we return 0.
As we see like we did in our C++ example we see ' Hello W orld! ' echoed
successfully.
This is simple, we use aaa to analyze the binary and seek to main with s
sy m.main.
I went over this in detail in the previous lessons on Assembly but let us review.
1)We push rbp which means we push the value currently in the base pointer onto
the stack.
304
Part 1: G oals
2)We lea rdi, q w ord str.Hello_W orld which means we load the effective address
of the quad word of our string into the rdi register. So far should be simple for you
to follow along.
3)We then call sy m.imp.puts um wait! We used printf what the hell! Well our
compiler optimizes our code and the compiler chose the puts function in the stdio
library to echo the string to our terminal. Again easy enough.
4)We clean out eax and then pop the original value in the rbp register back into
rbp. If you are confused by this review the earlier part of the series please.
We know our string ' Hello W orld! ' lives at a pretty house in Arlington, VA at the
address of 0x2004 well ok, it' s not Arlington, VA but it is in mapped memory (since
we are not technically debugging we are messing with mapped code meaning the
same values on disk).
To confirm we see the value at 0x2004 is ' Hello W orld! ' Let' s hack that value to
anything we want with the w command and write directly to that mapped memory
address.
Success! We hacked the value and when we exit our debugger we see:
This is alot to digest here. If you are stumped ask questions in the comments
PLEASE! D o not continue as I am here to help. It is CRITICAL you understand
these most basic things before we continue!
305
Part 1: G oals
In C we have several data types to which we can create variables. I will use a few
simple examples:
Ok as we can see we have a character an integer and a double. These are some
of the most basic data types in C to which we have created a series of variables
as shown above.
306
Part 1: G oals
307
Part 1: G oals
As we can clearly see at memory address 0x0000113d we in fact see ' b' . We
have successfully hacked this portion.
We exit out of Radare and re-run the binary and we can see we have successfully
hacked the value.
H OMEWORK TIME! I want you to with this knowledge now hack the int and the
double. I want you to put your results in the comment sections below. It is VERY
important that you type all of this out and actually explore the exercises so I am
looking forward to seeing your hacks in the comments!
308
Part 1: G oals
As we can plainly see we init an int to 1 and if the variable is equal to 1 the first if
statement prints a response to standard output.
Let' s compile:
Let' s run:
As we can logically see the first branch is taken. Let' s take it into Radare and look
around at the assembly:
309
Part 1: G oals
We can see the branching logic with the aqua colored arrows. At 0x0000114 a we
see our first branch being loaded into rdi. Take note at 0x0000114 8 we see a j ne
0x115 8. At 0x0000115 8 we see our second branch being loaded into rdi.
The j ne means jump if not equal. This means if what is being compared in
0x0000114 4 is not equal to 1 (we see 1 being compared to what is in local_4 h
which we know is pseudo code for what is actually in rbp-0x4 . This should make
sense as I went over this in detail last week if you are confused please revisit our
last lesson.
When we exit Radare we can see we have hacked the binary successfully:
Stay tuned!
310
Part 1: G oals
We are at the end of the road. This is the final video in the x64 series. The final
topic is that of pointers.
311
Part 1: G oals
Let us compile:
Then we compile:
312
Part 1: G oals
Let' s debug:
Then we disassemble:
313
Part 1: G oals
Let' s hack!
314
Part 1: G oals
I hope over the years through the literal hundreds of x86, ARM and x64 tutorials
you have a basic knowledge of how to do G OOD to protect critical infrastructures
from malicious hands by understanding how the enemy works. G o and do G OOD
work!
315
Part 1: G oals
316
Part 1: G oals
Today we are going to set up our development environment. We will need the
following:
R aspberry P i 4
64GB M icroS D C ard
M icro S D C ard R eader/W riter
Download 64-bit K ali L inux AR M Image
Download balenaEtcher
F lash K ali L inux AR M Image
O P T IO N AL : V ideo [ L oad K ali R P I 4]
H ow T o Install V IM
Git C lone & B uild R adare2 S oftware
R aspberry Pi 4
https://www.adafruit.com/product/4292
64 G B MicroSD C ard
https://www.sparkfun.com/products/16498
317
Part 1: G oals
https://www.offensive-security.com/kali-linux-arm-images
https://www.balena.io/etcher
https://youtu.be/Jquf9BD m4iU
How To Install V IM
https://www.simplified.guide/ubuntu/install-vim
After obtaining all the necessary devices and software please watch the video on
how to set up your environment as Null Byte did an amazing job with a step-by-
step tutorial which will get you set-up in minutes.
The next step is to git clone and build the Radare2 software as this will we want
the latest version as the standard version built into Kali will not be sufficient for our
needs.
https://github.com/radareorg/radare2
cd Documents
git clone https://github.com/radareorg/radare2.git
sys/install.sh
Finally we will be using a text editor to build our code. Kali has both the VIM and
Nano text editors built-in. We will be using VIM but you are free to use whatever
one you are comfortable with.
In our next lesson we will write our first C++ program which will be " H ello World!" .
318
Part 1: G oals
Today we are going to start at the beginning and take a very simple C++ program
that does nothing more than use the stream insertion operator to send a string
literal to the stdout and then use the end line manipulator to flush the output
buffer.
Let' s start by creating a file 0x01_ asm64_ helloworld.cpp and type the following
into it.
#include <iostream>
int main()
{
std::cout << "H ello W orld!" << std::endl;
return 0;
}
Let' s run.
./0x01_asm64_helloworld
H ello W orld!
In our next lesson we will debug this very simple binary using our dev build of
Radare2.
319
Part 1: G oals
Today we are going to debug our first program utilizing our dev build of Radare2.
radare2 ./0x01_asm_64_helloworld
aaa
The next thing we want to do logically is fire up the program in debug mode so it
maps the raw machine code from disk to a running process.
ood
Now that we have a running instance we can seek to the main entry point of the
binary.
s main
The output from Radare2 is entirely too large to display in this course however as
you follow along in your own environment you will be able to follow along. We will
keep this convention throughout this course for better readability of the document.
D o you notice that as your mapped memory values are different than mine? That
is because of ALSR.
320
Part 1: G oals
We can see that it is in fact true that at the mapped memory address of
0x55629 cab48 we see the string "H ello W orld!".
Let' s also look at the hex view so we can see and better understand what is going
on at the machine code level.
We see our " H ello World!" string and we can again see that it exists starting at the
mapped memory address of 0x55629cab48.
We see that our machine code instructions are 16 bytes long or 64-bits long as
we can see the first column start at 48 and end with 00.
It is VERY important we understand a few key things. First is the fact that a single
hex digit is 4-bits wide or a nibble or a half of a byte. In our case 4 is a half of a
byte and 8 is the other half of the byte. Together they form a byte and in our case
a valid ascii char code.
321
Part 1: G oals
http://www.asciitable.com
Second, we need to understand what the machine code translates to. Let' s look
up what 48 is in hex. We see that it is a capital ' H ' . That maps perfectly as you
see in the right hand column of the image above we see a 0 and below it the letter
H .
Obviously 65 hex is 'e' and so on and so forth. You can extrapolate the rest for
yourself now that you have a basic understanding of what you are looking at.
Let' s now define a breakpoint on main and execute this binary to verify in fact that
when we continue on from main it will print "H ello W orld" to the stdout.
Let us continue and verify our hypothesis. First we continue and break on main.
Now we step again and since there are no other breakpoints we will conclude the
execution and verify our result in stdout.
q
y
y
Let us rerun Radare2 again and this time not run the binary and simply look at the
unmapped binary that is on disk.
radare2 ./0x01_asm_64_helloworld
aaa
322
Part 1: G oals
s main
Then view.
Notice that we have "H ello W orld!" this time at the unmapped memory address of
0xb48. You notice that when you ran the binary the executable had an offset to
this value but the LSB were 48 hex.
I hope this lesson helps you to understand the basics of 64-bit ARM assembly
and how to reverse it properly.
323
Part 1: G oals
In the last lesson we spent a good deal of time really understanding what is going
on inside our binary. This laid the groundwork for an easy hack.
radare2 -w ./0x01_asm_64_helloworld
aaa
Seek to main.
s main
View disassembly.
We see the memory addresses as they are on disk as we are not running the
binary as we discussed in the last lesson.
324
Part 1: G oals
./0x01_asm_64_helloworld
H acked W orld
We see that we very easily hacked the binary. These lessons will help you
understand how an attacker creates a workflow so you can learn how to anticipate
and better reverse engineer.
325
Part 1: G oals
Today we are going to look at a basic I/O C++ program that has some minimal
validation.
Before I get into the brief lecture as I try to keep these short, I wanted to explain
why I am not using the textbook straight cin examples that you see across the
globe.
The cin, standard input stream, which takes input from the keyboard is referred to
as our stdin.
What cin does is use whitespace, tab and newline as a terminator to the input
stream. For example if you input ' abc' and hit a tab or put a whitespace or newline
by hitting return the data to the right of it will be ignored.
The problem is if you read from cin again it will pick up the remaining data in the
stream if you do not flush the input buffer.
If the user enters 1 and then leaves a space and then 2 and presses enter, you
have no issue. 1 will be assigned into v al1 and 2 will be assigned to v al2 as they
are chained.
The problem is what if you enter ' H ey J ude' instead of an integer? What happens
is it tries to read an integer and it goes into a failed state and from that point
everything else it is extracting is unreliable.
I did not mean to be long winded but I really wanted to emphasize why you would
NEVER use cin by itself and I mean NEVER!
Let' s take a look at our basic i/o program that we will debug in the next lesson
with a very basic C++ program that validates input.
326
Part 1: G oals
#include <iostream>
#include <sstream>
#include <string>
int main()
{
int age = 0;
bool valid = false;
char null = '\0';
while (!valid)
{
std::cout << "Enter Age: ";
// Init stringstream
std::stringstream is(line);
std::cout << "Your are " << age << " years old, seems
legit!" << std::endl;
return 0;
}
327
Part 1: G oals
We then prompt the user to enter their age. We then create a string object called
line and take advantage of C++ getline( ) which is a standard C++ library function
that is used to read a string or a line from an input stream properly.
Then before we echo data to stdout we do a little validation. We first check to see
if age is the type it was defined as which is an int OR is there a white space in the
stream after a given integer OR is age greater than 100 or less than 0. Very
simply it provides a response if the input does not meet this criteria.
Let' s run.
./0x02_asm64_basicio
In our next lesson we will debug this very simple binary using our dev build of
Radare2.
328
Part 1: G oals
Today we are going to debug our very basic input validation program from last
lecture.
radare2 ./0x02_asm_64_basicio
aaa
The next thing we want to do logically is fire up the program in debug mode so it
maps the raw machine code from disk to a running process.
ood
Now that we have a running instance we can seek to the main entry point of the
binary.
s main
A couple things to note we see at 0x5566be00cc the output of "Enter Age: " and
at 0x5566be017c a call to istream which is going to capture the values from stdin
to which we identify a failure condition at 0x5566be01d0 where we find "Dude be
real!" and we see the results of a proper input validation starting at 0x5566be0218
_where we say " You are " and then we see a call to the output stream at
_0x5566be0238 and then the continuation of the validation string at
0x5566be0244 where we say " years old, seems legit!".
q
V V
ppppp
329
Part 1: G oals
This is our zoomed out visual graph. We can see how the program moves from
function to function. You will notice there are a series of tags such as [ ol] or [ ok]
and you can literally type the following:
p
ol
qq
s main
V V
This will take us to an expanded graph that we can also use our arrow keys to
look around.
As you can see we cycled the loop and entered in a correct validation and was
able to get our success return.
330
Part 1: G oals
radare2 -w ./0x02_asm_64_basicio
aaa
Seek to main.
s main
View disassembly.
Let' s look at the visual graph and begin with the first b. ne which under the proper
expected conditions it will only accept a valid integer between 0 and 100 as we
demonstrated in the last lecture.
The b. ne meaning branch if not eq ual. The assembly before it simply does not
matter in this case as we know if we leave b.ne as is the input validation will be in
tact.
331
Part 1: G oals
If we fail the validation we will be sent to the false condition to obtain new input.
Let' s quit.
Now when we run the binary it will simply ignore any input at all let alone input
validation and simply arrive at the desired point.
kali@ kali:~/Documents/0x02_asm_64_basicio$
./0x02_asm_64_basicio
Your are 0 years old, seems legit!
kali@ kali:~/Documents/0x02_asm_64_basicio$
332
Part 1: G oals
These are VERY simple examples however when you combine these as you
progress you will literally be able to Reverse Engineer anything.
In our next lesson we will discuss the char primitive data type.
333
Part 1: G oals
Today we are going to talk about the first of the C++ primitive. The char dataype is
used to store a single character and must be surrounded by single quotes.
#include <iostream>
int main()
{
char my_char = 'c';
return 0;
}
Extremely simple. We are simply creating a char variable called my_char _and
assigning it the character _c.
Let' s run.
./0x03 _asm64_char_primitive_datatype
334
Part 1: G oals
Today we are going to debug our very simple character primitive datatype.
aaa
The next thing we want to do logically is fire up the program in debug mode so it
maps the raw machine code from disk to a running process.
ood
Now that we have a running instance we can seek to the main entry point of the
binary.
s main
We can see that at 0x5576bf f 9 ec we are moving 0x63 or ascii ' c' into the w0
register. REMEMBER your address will be different due to ASLR.
335
Part 1: G oals
This is very simple but let' s break it down. We set our breakpoint and continued.
We looked inside the register w0 and saw that the value is 0x01.
We then stepped once and looked again to see that 0x63 was successfully moved
into w0 as now we see it does in fact contain 0x63.
In our next lesson we will hack the char to another value of our choice.
336
Part 1: G oals
aaa
Seek to main.
s main
View disassembly.
All we have to do is write assembly to 0x000009ec and specify a new char of our
choosing.
Let' s quit and run the new binary from the terminal.
337
Part 1: G oals
As you can see we successfully and permanently hacked the binary! It is very
trivial but when you take the last series of lessons together with each new
successive lesson you build a real skill-set!
In our next lesson we will work with the boolean primitive datatype.
338
Part 1: G oals
Today we are going to talk about the C++ boolean datatype that stores either a 0
or 1 to represent 0 for f alse _and _1 for anything true.
This kind of flag is used extensively in programming in general and we will look at
another very basic program to understand its simple usage.
#include <iostream>
int main()
{
bool my_bool = true;
return 0;
}
We see that we are creating a bool and assigning it a _ true _ value or _ 1 _ value
and printing it.
g++ -o 0x04_asm64_boolean_primitive_datatype
0x04_asm64_boolean_primitive_datatype.cpp
Let' s run.
./0x04_asm64_boolean_primitive_datatype
339