The x86 Adventures series teaches you your computer's language - x86 Assembly language, from scratch. No prior knowledge is assumed.
This is the full collection of x86 Assembly Adventures. It covers everything from the real basics to being an independent (and tough) x86 Assembly programmer.
Main topics covered:
Why learn x86 Assembly Language?
The course is made of video lectures. A lecture could be from a presentation, or a real world example, showing me doing stuff at the computer.
Almost every video lecture is accompanied by some kind of exercise (You will be told during the lecture, don't worry :) ) The exercises are open source. They are attached here as a rar file, however you could also get them on github. (See "About this course" video for more information).
It is crucial that you complete the exercises. You will learn a lot from the lectures, but it is pretty much a waste of your time and money if you don't do the exercises. (Or at least verify that you know how to do them, if you are more experienced).
Course tech stack
No prior knowledge is assumed for this course, but I do assume some things regarding your system, so make sure that everything here describes you:
For the tech savvy, some more details about the tools we are going to use in this course:
Using Linux? Most of the exercises were ported to linux, however the videos show me using windows 7. Contact me if you are not sure.
General information about the course: List of main subjects to be covered during this course, some technical details and some requirements for the course.
Explaining the file explorer - The program we use to view files on our computer. We also discuss Total Commander, which I am going to use during this course.
Explaining the Hex Editor - A program that allows us to view the internal structures of files inside our computer. Specifically we discuss the HxD Hex Editor.
We discuss the idea of numeric bases, and specifically we take a look at base 10 and base 2. Finally we have some examples of addition and subtraction in base 2.
We introduce three ways to convert numbers between different numeric representations: Direct evaluation, Finding largest power and Remainder evaluation.
We discuss how to decide which conversion method to use in different cases, and also show a few examples of converting numbers between different bases.
We discuss the Hexadecimal base (Base 16), and show its special relation to base 2.
We answer the question: Why is every hex digit is represented by exactly 4 binary digits?
We discuss the subtraction operation, and also negative numbers, in the base 10 representation. We study how to invoke subtraction using only the addition operation.
We introduce the two's complement representation, which allows us to deal with signed numbers in base 2, just like we did previously in base 10.
We look at examples of signed addition, we deal with some exceptions regarding
the two's complement representation, then we view a graphical representation of
the positive and negative numbers in the two's complement, and finally we
discuss some philosophy of representation.
Basic history of the x86 architecture: The first processors in the family, the idea of backwards compatibility in the x86 architecture, and the different modes inside the x86 processors.
We explain how programs are stored in memory, and how they are executed by the processor. Next we study about the x86 32-bit registers.
We learn about the general structure of x86 instructions, and we discuss the MOV instruction.
We introduce the ADD and SUB instructions.
We introduce the INC, DEC and MUL instructions.
We show examples for exceptions generated by using the DIV instruction.
We look at some examples of using the DIV instruction (With numeric examples). Finally we summarize the Basic Arithmetic series of lessons.
Introducing FASM - The Flat Assembler.
We discuss a few different common assemblers, and finally we describe FASM, and a few different reasons to use FASM. We end with a short description of how to install FASM. We will expand on this in the next lessons.
Fasm installation. We take a look at Fasm's website, and explain how to download and install Fasm.
We assemble our first program - bare.asm, and view the generated file in a hex editor.
We take a look at the program console.asm, which is our first console program. We discuss the source code of console.asm
We try to assemble the console.asm program, and we have to fix the INCLUDE environment variable to make that happen. Finally we manage to assemble and run the program. We then analyze the output file with a hex editor.
We introduce first_program.asm. This is our first program that actually has input and output. We take a look at the source code, and compare it to console.asm, the previous program. Later we try to assemble the program. For that task, we have to update the INCLUDE environment variable. Finally we run the program, and analyze the output of the program.
We create our own program, based on first_program.asm. Our program will read two numbers from the user, add those two numbers, and then print the result back to the console.
We introduce different examples of assembly time errors, and how to deal with them.
We begin with the Write Failed Error.
We introduce more examples of assembly time errors and how to deal with them:
File not found error, Undefined Symbol Error, Illegal Instruction Error, Sizes
don't match error, Another Undefined symbol error and finally Invalid Operand
We introduce the idea of branching: Having our program run in a nonlinear
fashion. We then discuss the EIP register.
We introduce the JMP instruction, we discuss labels, and we view a simple
example of using the JMP instruction.
We take a look at two examples using the JMP instruction: An infinite loop, and an example of skipping instructions using the JMP instruction.
We introduce the Flags register, and discuss the Zero flag.
We discuss the Sign flag and Carry flag. Then we look at a few examples about the Carry Flag.
We discuss the Overflow flag and view different examples regarding the Overflow flag.
We compare the behavior of the Overflow and Carry flags. We also view different
ways of using the Overflow flag and Carry flag for the Signed and Unsigned
We describe the idea of conditional branching, and see some examples of using the JZ and JNZ conditional branching instructions. We then briefly mention some other conditional branching instructions.
We introduce the CMP instruction, and the idea of comparing numbers. Then we discuss unsigned and signed comparison, and we introduce specialized instructions for unsigned and signed comparisons.
We describe signed comparison in a more detailed fashion, and add some note about writing readable code with specialized jump instructions.
We introduce the idea of Structured Branching and its advantages for writing good code. We then discuss the pattern of conditional execution.
We discuss the pattern of loops, and breaking from loops.
We introduce some branching rules of thumb. Then we show an example of simplifying a spaghetti code using the idea of Structured Branching.
We learn about the NEG instruction, and we discuss the concept of sign
extension. We introduce the instructions MOVZX and MOVSX.
We discuss the differences between the MOVZX and MOVSX instructions. Then we
study the instructions CBW, CWDE, CWD, CDQ.
We introduce the IMUL and IDIV instructions, the sign aware versions of the MUL and DIV instructions. We explain how they work, and finally show an example of using those instructions.
We introduce the idea of basic statements, and the basic operators NOT, AND and OR.
We discuss the truth tables and Venn diagrams of the basic operators (NOT, AND,
OR). Then we view a few examples involving the basic operators.
Basic properties of the NOT, AND and OR operators: Double negation, Commutative laws, Associative laws, Distributive laws. Finally we show an example of using Venn diagram to verify the truthfulness of an equation.
We introduce De Morgan's laws, followed by a few examples of simplifying statements. Then we discuss the XOR operator. Finally further research subjects are mentioned.
We introduce the NOT, AND, OR and XOR instructions. Then we discuss Bit Shifting using the SHL and SHR instructions.
We discuss arithmetic shifting (Shifting that understands the sign) using the SAL and SAR instructions. Finally we introduce the ROL and ROR instructions, used for bit rotation.
We study some basic bit manipulation techniques: Extracting one specific bit from a number, Counting the number of 1-s in a binary number, Calculating modulo powers of two using bit operations and finally squeezing a few small numbers into the same container.
We first discuss the reasons for which we need more data in our programs. Next
we describe the basic model of the computer memory, and discuss memory devices
which reside outside the processor.
We discuss memory abstraction mechanisms supported by the x86 processor. We
mention Segmentation and Paging, and show a few illustrations of the Paging
We introduce the program reverse.asm, which uses the memory (The RAM) to reverse
a list of numbers. We run the program and analyze the source code.
We analyze the internal structure of the output executable file of reverse.asm using a hex editor.
Next we continue with reverse_constant.asm, which improves reverse.asm by using a constant.
We introduce the get_addr.asm program, which demonstrate the meaning of labels, and possible syntax when using labels.
The program corrupt.asm demonstrates what happens when we try to access memory that we do not own. fix_corrupt.asm demonstrates how to fix corrupt.asm, and finally fix_corrupt_small.asm uses the bss section to get a much more efficient solution.
We discuss different ways of defining data, and inspect the output for each data definition. We learn about bytes, words, dwords, arrays and a bit more complex data definitions.
We introduce the PE sections, and show an example of a program that contains different sections: text, data, bss and idata. Finally we inspect the resulting executable file with a hex editor.
We show how to access memory using assembly instructions. In this lecture we study how to count addresses properly, how to store a dword in memory (Endianity), advanced addressing with the brackets syntax, and some limitations when accessing memory in the x86 architecture.
We introduce the LEA instruction. We discuss the syntax of this instruction, and then show a few examples of using the LEA instruction.
We explain the need for a way to organize our data, and propose the structure syntax as a solution. We then discuss different ways of defining and declaring structures.
We show different examples of using structures, and then we discuss the possibility of nesting structures.
We discuss Unions, and we view an example of using unions (Unions IPV4 example).
We take a look at a few examples of memory constructs: Array of structures and
Two dimensional table. We put emphasis on the techniques of storing those
constructs inside our program's linear memory.
We show an example of storing a multiplication table into a two dimensional
array. Then we discuss the possibility of storing higher dimensional arrays
inside the program's memory.
We discuss the memory construct of Binary Tree, and show an example of representing a Binary Tree in memory.
We describe a few rules of thumb regarding address arithmetic. Those rules help distinguish between addresses and offsets.
We introduce the ASCII standard for encoding text as numbers inside the computer. We also mention the Unicode standard.
We introduce the Character Map windows program, which is a tool for viewing different characters and their corresponding ASCII or Unicode codes.
In this video we explain how to represent text strings inside our own assembly programs. We also introduce two schools of strings: Length prefix and Null terminated strings.
We show an example of using Unicode characters inside a Fasm assembly program. In this example we want to store two Chinese characters in memory. Eventually we assemble the program and view the output file inside a hex editor.
We introduce the STOS instruction, and show a simple example of using this
instruction. Then we discuss the direction flag and its significance.
We introduce the instructions LODS, MOVS, and we show simple usage examples for
both of those instructions.
We introduce the REP prefix, and use it together with STOSB to clear an array. Next we introduce the SCAS instruction. Finally we introduce the REPNZ prefix, and use it together with the SCAS instruction to search for the NULL terminator of a string.
We discuss the REPZ and the REPNZ prefixes. Then we introduce the CMPS instruction, and show an example of using CMPS with the REPZ prefix to check the equality of two memory buffers.
Subroutines are independent pieces of code. We explain the need for subroutines, and next we try to create our own subroutines using a simple JMP instruction. This first attempt is not so successful, but it prepares us for a more advanced idea of implementing subroutines, which we are going to see in the next lessons.
We study the Stack data structure. Then we study the x86 stack implementation and instructions (PUSH and POP). Eventually we see some simple examples of using the stack.
We show an example of using the CALL and RET instruction to implement a
subroutine. Then we study CALL and RET in detail.
We see a few examples of using the CALL and RET instructions. Then we explain
the Stack's meaning with respect to function calls.
We explore different methods of communicating with subroutines: Registers, Global data and the Stack.
We discuss the idea of calling conventions. Then we introduce the CDECL and
STDCALL calling conventions, and compare them.
We introduce the EBP register, and we explain how to use it to hold the stack frame in our functions. Next we discuss local variables, and how to use the stack to store local variables for the current function.
We introduce the idea of The Call Stack, and next we study the ENTER and LEAVE instructions, which are shortcuts for creating and destroying a function's stack frame.
While many believe that some technologies are long lost gone, we believe that knowing how things really work is what gives you the edge, and help you become a top developer.
Assembly language programming was taught so far in ways that appeal to the experienced developer, however unreachable for the beginner. We put emphasis on creating material that is easy to learn and understand, even for the absolute beginner.