;You might not get everything the first time you read it. Therefore, play around with the code and change things to see how it's all changed. Strive to fully understand the text presented before you, here, before moving on, or future lessons will not be easily understood. It will help vastly to have the program running and for you to follow along with the execution. This lesson will be painful, but if you can get through it, you will have alot of assembly set before you.

include "macros.inc"
begin

;Real instructions have a simple syntax: most of them have the syntax "mneumonic oprand1, oprand2" where "mneumonic" is kind of like a verb, and oprands are the nouns that are invovled in the sentence. Usually the first oprand is the "destination" oprand, which is where the result goes.

        int3
        mov eax, 2
        int3

;As you will see, eax suddenly got the value 2 when we did that instruction. Each mneumonic has a set of oprands that it does and does not work with, however it's impractical to try to memorize it all. Instead, try it, and gain experience and that experience will tell you what you can and cannot use with each mneumonic. Just remember that instructions are the combination of mneumonics and their oprands, not the mneumonics themselves. However, due to mneumonic being a hard word to say and write, we often refer to mneumonics, informally, as instructions instead.

;Now before, I get started with more "data movement instructions," I need to show you a type of label (for the future). A label can be created with the syntax name:, which then comes to represent the numerical location (we point to what we want in RAM by using numbers [starting with 0]) of the very next thing after it that ends up in the binary.

foo:
        int3
bar:

;Remember, we can perform math on labels, so this will tell us how big the int3 instruction is, because bar is the location after int3 and foo is the beginning location int3. Therefore, subtracting the labels is subtracting the numbers that represent these locations.

        mov eax, bar-foo
        int3

;I already know the answer is 1 byte, so I want to go ahead and display what that 1 byte value is. However, if i simply move "foo" into al to display it, it will give us the LOCATION of that particular int3 instruction, but it will not provide us with the value that represents that instruction. Instead, we must have a separate method of specifying that we want to grab something at that location. To do that, we use "[]."

        mov eax, 0
        mov al, [foo]
        int3

;We can do this with registers, too.

        mov ebx, foo
        mov ah, [ebx]
        int3

;We can do this with a combination of labels ("constants" which are regular numbers) and registers, too. However, when you do this, the processor has to do some of the math, because the assembler cannot predict what's in the register.

        mov cl, [ebx+1] ;Puts the first byte of the "mov eax, bar-foo" instruction into cl
        int3

;There is a limit to the math that can be done, obviously, and you'll find that you cannot do certain things. Now to drive the point home, we are going to try a little experiment. We are going to try to replace a "mov eax, 6" instruction with int3s. Now if you play with the code above, you should be able to know that the instruction is 5 bytes long, and since the int3 instruction is 1 byte, we need to make sure we put 5 of these down to replace the whole instruction.

        mov eax, 0xdeaddead ;Setup

        mov ecx, 0xcccccccc ;We know from earlier that int3 is "0xcc."

        mov [replaceMovBeg], ecx ;put 4 down
        mov [replaceMovBeg+4], al ;put down the last one

replaceMovBeg:
        mov eax, 6

        mov ebp, 9
        int3 ;If we see 0xdeaddead in eax, we succeeded!

;The value of EBP here does 2 things. First, it let's us know where we're at because it's easy to loose track of our int3s here, so when we see that the id is 9, we know we're here. The second thing it does is makes sure we didn't write too many bytes. If we didn't write enough, the program would probably crash beause of half of an instruction occured. If we write too many, the mov to ebp would fail (and the program would also probably crash).

;Now more instructions. xchg swaps the two oprands.

        mov ebp, 1
        mov eax, 1
        mov ebx, 2
        xchg eax, ebx
        int3

;push takes 1 oprand. It takes whatever the oprand is, and stores it at the ram location pointed to by the ESP (stack pointer register), then subtracts the ESP register by the size of the oprand. pop does the exact opposite: it adds the size of the oprand, and stores what is at the memory location pointed to by ESP into the register. This pair of instructions forms what is known as a "stack." Think of it as storing numbers on plates then you "push" a plate onto the bottom of the stack, then if you push a second plate onto the bottom of the stack, you have to pop that second plate off of the stack before you can get to the first plate. This doesn't immediately sound useful, but it can be used for alot of things, especially if you run out of registers to store numbers when doing a really complicated calculation. pushd pushes 4 bytes, and pushw pushes 2 bytes (with an extra 2 bytes of 0s for alignment). Though it is usually ok to simply use "push." (The meaning behind "d" and "w" will be explained) in the next lesson. You can put more than 1 oprand on the line without separating with commas, however it is just an alias for individual pushing and popping instructions on separate lines.

        mov ebp, 2
        push ebx
        push eax
        pop ebx
        pop eax
        int3 ;the registers should be swapped again

;pusha is special. It pushes the registers in this order: eax, ecx, edx, ebx, esp (the value before the pusha instruction) ebp, esi, and edi. Naturally, popa does the reverse and puts them all back in the right order. It's great for preserving your registers before doing some technical math or giving control of your program to another for it to do some work for you. Remember, however that it is slow, so using it to only preserve 1 or 2 registers when a simple "push-pop" combo would do will make your program slower than it needs to be.

        pusha
        mov eax, 1
        mov ebx, 2
        mov ecx, 3
        mov edx, 4
        mov ebp, 5
        mov esi, 6
        mov edi, 7
        popa

        mov ebp, 3
        int3

;Note: we didn't change ESP because if we change ESP, we will loose track of the stack, and thus popa will not work, because it won't know where to pop the registers from.

terminate
end


Get your own web kitty here!
┬ęCopyright 2010, 2011, 2012, 2013, 2014, 2017. All rights reserved.