1- Introdução2- Assembly básico-sistemas numéricos
-introdução ao assembly
-Instruções ASM
3- Shell Coding básico4- Stack Buffer Overflow5- Sintaxes de chamada de funções-Cdecl
-FastCall
-Stdcall
6-Windows ShellCoding intermediário7-Análise de programas -Disassembling
-Criando um stack buffer overflow controlado
8- Considerações finais & Agradecimentos Pré-requisitos: conhecimento intermediário da linguagem C
1- IntroduçãoShell coding é uma arte a parte, algumas vezes, uma vulnerabilidade não é "exploitada" por que simplesmente o atacante não fez um shellcode pequeno o suficiente.
Achar vulnerabilidades é outra arte também... Saber juntar essas duas formas de arte para um objetivo principal pode e pode não ser uma tarefa difícil, mas tende a ser complexa muitas vezes.
Inicialmente, hackers e crackers viviam em mundos separados... A antítese de usuários comuns...
Hackers, historicamente vieram de sistemas de código aberto aonde as vulnerabilidades eram encontradas no código fonte liberado, não lidavam com dificuldades técnicas como unpacking e etc, no entanto conheciam a linguagem C muito bem.
Crackers vieram de um sistema de código fechado em especial(o windows) e costumeiramente tinham que "quebrar" o código de um software para conseguirem alcançar certos objetivos, que algumas vezes eram os mesmos que os dos hackers e outras vezes não eram.(como uso ilimitado de programas pagos)
De fato, a sabedoria popular é mais considerada do que a individual.
Digo isso porque a um tempo atrás a linguagem object pascal só poderia ser chamada assim. Como a maioria dos programadores novatos assimilavam o nome da linguagem com o do compilador(delphi), o nome da linguagem basicamente hoje em dia pode ser ambos.
Outro exemplo é a nossa língua em que palavras perdem acentos ou ganham de acordo com a forma que o povo mais escreve e fala.
Muitos hoje enxergam os cavaleiros como homens cheios de honra, moral e ética quando na realidade, os cavaleiros na época das cruzadas só sabiam matar e estuprar mulheres inocentes.
Assim é com hackers, na verdade a meu ver, o conceito só tem a ver com o conhecimento que você tem de hardwares e softwares... (vide o porquê do primeiro grupo de hackers terem se chamado assim)
No entanto como a sabedoria popular é a que tem a "última palavra", então hackers são seres angelicais, cheios de ética e com uma perfeita pronúncia da nossa língua nativa.
Crackers sempre foram e serão vistos como criaturas malvadas e quase sempre são confundidos com meros defacers com más intenções, principalmente em fóruns sensacionalistas.
Não que seja uma completa mentira, mas muitas vezes, um exagero.(poucos crackers praticam deface)
2- Assembly Básico 2.1 Sistemas numéricosNão tem, ou melhor, não deveria ter outra forma de introduzir alguém a uma linguagem sem passar por esse tema.
Sinta-se livre para passar por ele se já o conhecer.
Vou passar bem rápido por esse tema, ele é simples demais para perdermos tempo nele.
Um sistema numérico, é uma representação visual de uma contagem seqüencial.
Cada símbolo representa o número seguinte a ser contado, quando não há mais símbolos nós repetimos o segundo da seqüência ao lado esquerdo do número que estamos escrevendo e voltamos o número inicial ao seu primeiro símbolo.(famoso esquema do “vai um”)
A base que normalmente usamos para representar números é a decimal, logo nós temos:
1, 2, 3, 4, 5, 6, 7, 8, 9 e 10
Certo?
Errado!
0, 1, 2, 3, 4, 5, 6, 7, 8 e 9
É a seqüência certa...
Quando chegamos o no número nove (9) nós voltamos ao zero (0) e colocamos um ao seu lado para representar que já ultrapassamos da nossa seqüência uma vez e assim em diante.
Isso é fácil, mas é a base para qualquer base numérica, como a binária.
A base binária só tem dois números, o zero e o um e subseqüentemente segue a mesma lógica que a decimal para representar os mesmos números...
Vamos ver algumas representações em binário
Binário | Decimal
0 | 0
1 | 1
10 | 2
11 | 3
100 | 4
101 | 5
110 | 6
111 | 7
1000 | 8
1001 | 9
1010 | 10
Como podemos ver, cada vez que passamos do número de símbolos que temos, aumentamos um número a esquerda tanto na base decimal quanto na binária.
Parece bobeira de tão fácil, e é. Lolz
Geralmente nós temos que trocar de uma base para outra... Para trocar da base decimal para a binária e vice-versa eu uso uma tabela comum para técnicos e engenheiros eletrônicos, mas pouco comum no mundo da informática.
Ela é lógica e bem simples.
Vou fazer um exemplo da conversão do número 9 para binário utilizando ela.
16 | 8 | 4 | 2 | 1 <- cabeçalho
0 | 1 | 0 | 0 | 1
Essa tabela é composta por um cabeçalho, esse cabeçalho define colunas baseando-se em potencias de dois:
2 ^ 0 (^ significa “elevado a”) = 1
2 ^ 1 = 2
2 ^ 2 = 4
2 ^ 3 = 8
Esse cabeçalho pode ter quantos números você desejar.
Depois o que fazemos é pegar o número do cabeçalho que mais se aproxima do número que queremos transformar pra binário( 9 ), no nosso caso pegamos o oito.
Depois nós pegamos um número que possamos somar com o oito para se tornar nove.( o um/1)
Nas colunas dos números que nós pegamos nós colocamos o número um (1), em todas as outras, zero.
Vamos transformar o número 512 em binário para treinarmos. (PS: essa tabela é ótima para fazer em concursos porque ela é rápida e prática, melhor do que dividir milhares de vezes por dois e pegar o resto)
512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
Isso mesmo!
Como já achamos o 512 de primeira, o resto é tudo zero... Acreditem, da outra maneira vocês iriam gastar muito tempo dividindo por dois até o final de suas vidas. Lolz
E se o número fosse 511.. bem como temos um número antes de uma potencia de dois, é só marcar 1 em todas as colunas anteriores.. vejam:
512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1
111111111 é a resposta... Se quiserem somar todos os números anteriores com a calculadora de seus computadores verão que eu estou certo.
E para fazer o processo inverso?! Isso é, transformar um número binário em decimal?
Usamos nossa tabela novamente.
Digamos que queiramos converter o número 111 para decimal...
8 | 4 | 2 | 1 <- cabeçalho
0 | 1 | 1 | 1
Agora é só somarmos os números que estão acima de cada 1 binário...
4+2 = 6
6 + 1 = 7
E pronto.
Nós também temos a base hexadecimal, que tem a seguinte seqüência simbólica:
0 1 2 3 4 5 6 7 8 9 A B C D E F
Com ‘A’ representando o ‘10’, ‘B’ o ‘11’, ‘C’ o ‘12’ e assim por diante...
Converter binário para hexadecimal e vice-versa é muito simples.
O maior número individual hexadecimal é igual ao maior número contido em um nibble(4 casas binárias/ 1 1 1 1)
Então, cada número hexadecimal, nós convertemos para um nibble.. por exemplo “FF”(255)
O primeiro F é o mesmo que 15 em decimal e corresponde ao nibble 1 1 1 1 e o segundo outra sequencia de quatro 1’s.
Logo, 1 1 1 1 1 1 1 1.
Só mais uma para termos um melhor idéia da operação.. o número A1 para binário vira
A 1
1010 0001
Eu normalmente faço a transformação de hexadecimal para decimal usando o deslocamento extra para não ter que ficar multiplicando fatores de 16.
Por exemplo o FF.
F = 15
E um dos F’s está uma casa a frente do primeiro.
150 < uma casa acrescentada
15
______
165
O deslocamento é 90(15 * 6).
Logo, 90 + 165 = 255
Eu gosto dessa maneira porque me permite fazer de cabeça.. no entanto meus amigos acham ela de uma dificuldade absurda, talvez porque eu tenha criado ela de cabeça a um bom tempo atrás e acaba ficando mais fácil pra mim que visualizei a lógica por trás da conta do que para quem não visualizou a mesma. \=
Então não vou me ater a ela tanto assim.
Vamos a técnica mais popular.
Cada casa hexadecimal corresponde a uma quantidade de vezes que nós passamos dos 16 símbolos.
Logo, em FF
Nós temos
F x 16^0
+
F x 16^1
O que dá 255.
O procedimento contrário(transformar números decimais em hexadecimais se dá através de divisões consecutivas do número por 16 até não podermos mais dividir o resultado.
Let’s see it.
255 / 16 = 15,9375
15 é igual a F.
Ai nós multiplicamos o resto
0,9375 x 16 = 15
15 é F também, logo ‘FF’.
Para quem não entendeu, é porque eu realmente estou correndo... Infelizmente nem sempre a minha explicação é o suficiente, então se sinta à vontade para procurar em outros lugares a explicação sobre essa mesma matéria, eu pessoalmente recomendo esse link>
http://www.numaboa.com.br/informatica/o ... exPort.php 2.2 Introdução ao assemblyMuitas linguagens nascem e morrem.
Object pascal há um tempo, era uma linguagem exemplarmente fácil e perfeita para o ambiente industrial o que a disseminou pelos quatro cantos do mundo.
Hoje ela se torna ofuscada por linguagens relativamente novas como o JAVA.
Isso se dá, porque a facilidade de programar em alto nível aumenta de forma vertiginosa... obj pascal perde para esse novo tipo de linguagem na facilidade, na portabilidade e até na segurança(já que o tipo do qual estamos falando, são os type safe)... e se Java não puder fazer algo que o pascal possa, certamente o C Sharp poderá com a vantagem de também ser type safe.
No entanto, a dificuldade de se programar em baixo nível nunca mudou e nós sempre iremos precisar nos comunicar com a máquina na linguagem dela.(depois do ASM, é claro que antes era de outra forma)
Sempre precisarão de alguém que ponha a “mão na massa” e “faça acontecer”.(duas figuras de linguagem em uma única frase curta o0 )
As linguagens de alto nível vão morrer, talvez nem todas, mas sofrerão mudanças... no entanto nosso bom e velho assembly continua ai, com seus 58 anos de idade e eu realmente duvido que seja um dia substituído simplesmente porque “panela velha é que faz comida boa”... Boa e rápida, diga-se de passagem.
Assembly é a linguagem mais rápida e não é à toa...
Assembly é uma simples representação dos códigos operacionais.
Esses opcode’s(códigos operacionais) são a verdadeira linguagem dos microprocessadores.. no entanto como programar usando números seria algo desumano o assembly foi criado para suprir nossas necessidades.
Bem, para aprender assembly , existem alguns conceitos básicos pelos quais devemos passar.
O primeiro deles é o tamanho básico que alguns dados ocupam na memória.
O computador sabidamente trabalha com o sistema binário, o sistema binário varia de 1 a 0.
Essa comunicação dá-se pela tensão(força que impulsiona os elétrons).
Como eu não consegui achar o esquema na internet; os dados que eu irei passar são imprecisos já que se baseiam na minha memória. (ela só armazena 12kb =P)
De 0 volts a 1,4 é a zona morta e seu valor lógico é zero.
De 1,5 a 3,0 é a “zona de indecisão” em que basicamente o valor lógico pode ser um ou zero dependendo de que parte do circuito nós estamos.
E de 3,0 a 5,0 é a zona ativa aonde o valor lógico é um.
Para definir um padrão, nós adotamos que o valor da zona morta é sempre com a tensão zero e o da zona ativa sempre com a tensão em 5,0 v.(a zona de indecisão não é importante para nós)
O importante é perceber que apesar da tensão poder ter pequenas variações, o importante é o valor lógico ao qual ela está atrelada.
Bit significa binary digit ou dígito binário e por ser binário pode variar entre um ou zero.
Quando nós temos quatro bits juntos, nós temos um nibble.( de 0000 a 1111 )
Oito bits juntos são um byte.(de 00000000 a 11111111)
Dezesseis bits juntos ou 2 bytes são um Word(de 0000000000000000 a 1111111111111111)
E um Double Word são 32 bits ou 4 bytes.(não me peça pra escrever 64 bits aqui ¬¬)
O segundo conceito básico são as operações lógicas.
A maioria das operações lógicas tem dois operandos como as matemáticas(1 + 1)
A diferença fica por conta do resultado gerado.
A primeira operação lógica que nós iremos ver é a operação denominada AND.
And na língua portuguesa significa ‘E’, e ela compara o primeiro operando com o segundo... se ambos forem 1 logo sua saída/resposta será um, qualquer coisa diferente disse recebe o valor zero.
Vejamos sua utilização:
AND 1, 1 ; saída 1
AND 1, 0 ; saída 0
AND 0, 1 ; saída 0
AND 0, 0 ;saída 0
No mundo da eletrônica em geral(obviamente incluindo a informática), existe o que chamamos de tabela lógica, nada mais é do que uma representação do que vimos aqui mais arrumado e graficamente.
| B | A | S |
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
Nós temos os operandos( A & B ) e o resultado da operação/saída( S ) e abaixo, todas as possibilidades de valores que esses operandos podem assumir e as saídas que eles irão produzir.
Apesar das portas lógicas físicamente poderem ter mais do que duas variáveis, no assembly nós quase sempre a veremos com dois operandos.(pelo fato de quase sempre estarmos trabalhando com 4 bytes de informação)
A operação lógica OR, tem a saída 1 se uma das variáveis for também um.
| B | A | S |
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 1
A operação lógica XOR opera como a OR com a exceção de que ela só retorna um na saída se apenas uma das variáveis for um.
| B | A | S |
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0 <- reparem no zero quando as duas variáveis são um
Agora vamos entender o complemento de dois, muito usado em flip-flop’s pra que vem da eletrônica.
Nós já aprendemos como os números decimais podem ser representados na base binária.
Agora vamos entender como máquinas representam números decimais negativos binariamente(já que não existe – 0001 e + 0001)
Imaginemos um nibble, ele pode representar até o número 15(1111) em decimal.
No entanto, para que ele represente negativos e positivos, ele deverá ser dividido ao meio.
Como temos um nibble, números de 0000 a 0111(7) serão positivos... Os números negativos possuem o último bit como o valor 1.
Fazendo uma tabela:
Dec |Bin
-8 > 1000
-7 > 1001
-6 > 1010
-5 > 1011
-4 > 1100
-3 > 1101
-2 > 1110
-1 > 1111
0 > 0000
1 > 0001
2 > 0010
Isso segue até o sete(0111).
Para se adquirir um número negativo correspondente ao seu positivo, usa-se o complemento de dois, ele possui uma fórmula mas na verdade tudo o que você tem que fazer é inverter todos os bits de um número e somar 1 à ele.
Ex:
0111(sete)
1000(-8) + 1
1001(-7)
Como um nibble só tem 4 bits, então:
0111 (7)
+
1001(-7)
____
10000
Repare que 1+1 = 10(dois em binário) e você faz aquela velha operação do “vai um” decimal.
A resposta é 10000, mas como tem 5 bits a resposta e estamos falando de uma variável que só possui quatro, o último bit é descartado e ficamos com a resposta 0000 que é o resultado correto.
Simples né? =]
Como eu estou correndo MESMO, vou pular alguns temas muito importantes para que você aprenda definitivamente essa linguagem tão impressionante.
A CPU trabalha através de certos registradores, esse registradores são como as variáveis que nós usamos nos nossos exemplos de portas lógicas, eles armazenam uma certa quantidade de bits(8, 16, 32 e estamos partindo para 64)..
Vamos conhecer os registradores que nós mais veremos durante a vida útil desse tutorial.
A maioria dos registradores podem ser divididos em 4 partes.
Por exemplo o EAX(o nome do registrador é AX, o ‘E’ antes dele significa ‘extended’)
Um registrador Extended tem 32 bits no entanto ele também pode ser acessado como um registrador de 16 e 8 bits.
Veja por esse lado, EAX tem 32 bits
EAX pode ser dividido em EAL(‘L’ de LOW/Baixo) 16 bits e EAH(‘H’ de high/alto).
Ou seja, os 16 bits mais baixos e os 16 mais altos.
Eles ainda fazem parte de EAX, mas você pode acessar as partes de EAX separadamente se quiser.
Por sua vez, EAX pode ser acessado como AX como um registrador de 16 bits que também pode ser dividido em AL e AH cada um tendo 8 bits.(note que EAL é igual a AX)
EAX é um registrador de propósito geral o que quer dizer que ele logicamente pode ser usado de diversas maneiras para guardar qualquer tipo de dado... No entanto a prática não é assim. (devido á certas leis que não são imutáveis mas que são seguidas a risca por muitos compiladores)
EAX é um registrador importante, ele é usado para passar dados para uma função ou retornar esses dados de certa função, as APIs do Windows retornam HANDLES, ponteiros, estruturas e etc.
Tudo por ele.
Ele também é usado para ler e receber dados da memória(por ser um pouco mais rápido para isso que os outros)
EBX é muito usado para endereços de memória.
ECX como contador para instruções de loop.(não é regra e ás vezes é até usado como auxiliar do EAX o0)
EDX é o auxiliar do EAX oficial, se o EAX estiver em uso, então esse registrador armazenará o os dados por EAX.
Um resumão>
AX- acumulador
BX- Base
CX- contador
DX- dados
Todos esses apresentados podem ser divididos como registradores de 8, 16 e 32 bits...
Os próximos registradores que vou lhes apresentar, também são de uso geral no entanto como esses, vocês irão perceber que não são de uso “tão geral assim”.
ESI & EDI: esses dois registradores são quase sempre usados como source e index(fonte e destino) para operações envolvendo strings mas também são usados sempre que se precisa ler e escrever em uma área da memória.
ESP: Mantém o endereço do topo da stack(mais tarde veremos um pouco sobre ela)
EBP: Normalmente armazena o endereço da base de um stack frame, nós o veremos muito em prólogos e epílogos de funções.(não se assuste, o nome é estranho mas logo não será mais)
Esses registradores podem ser divididos entre 16 e 32 bits, sendo os de 32 bits os que possuem o prefixo “E”(ESP) e sem o prefixo os de 16(SP).
2.3 Instruções ASMTodas as operações do assembly são processor-dependent¹... não importa se você está programando para um micro-controlador ou em um pc comum, o set de instruções que um micro-processador aceita é designado de fábrica e você pode ler o “instruction set”² no site do fabricante.
¹: dependem diretamente do processador
²:Também referido como instructions table e por ai vaiA primeira função que veremos é a mov.
Mov, movimenta dados da origem(primeiro operador) para o destino(segundo operador).(isso muda na sintaxe AT&T, mais tarde eu farei um pequeno tuto sobre ela, no momento, nos focaremos na intel)
É como se fizéssemos isso:
“int EDX, EAX = 1;
EDX = EAX;”
Por exemplo, vamos usá-la com os registradores edx e eax.
mov EAX, 1 ;EAX = 1
mov EDX, EAX ; EDX = EAX
Reparem que tudo depois do caracter ‘;’ é uma linha de comentário diferentemente do C que usa os caracteres “/*” e “*/” ou “//”Outro conjunto de instruções, são as portas lógicas que lhes ensinei.
XOR, faz a operação XOR(exclusive or, ou exclusivo) bit a bit em dois operandos e o resultado é guardado no primeiro operando.
Ex:
mov AL, 1 ; AL = 0000 0001
mov AH, 2 ; AH = 0000 0010
xor AL, AH ; AL = 0000 0011xor é muitas vezes usado para zerar um registrador, por exemplo:
mov AX, 512 ; AX = 0000 0010 0000 0000
xor AX, AX ; AX = 0000 0000 0000 0000 Como o xor é o “ou exclusivo” e só retorna um se somente um dos bits for 1, a operação entre dois valores exatamente iguais sempre dará zero.
Lembre-se que ax, contém 16 bits e AL e AH somente 8.
OR faz a operação OR(ou) bit a bit em dois operandos.
mov AX, 512 ; AX = 0000 0010 0000 0000
or AX, AX ; AX = 0000 0010 0000 0000 AND, uso:
mov AL, 15 ; AL = 0000 1111
AND AL, 4 ; AL = 0000 0100INC, incrementa um operando em um:
mov AL, 0 ; AL = 0000 0000
inc AL ; AL = 0000 0001
inc AL ; AL = 0000 0010
DEC, decrementa um operando em um:
mov AL, 2 ; AL = 0000 0010
dec AL ; AL = 0000 00001
dec AL ; AL = 0000 0000
IMUL, multiplica dois operandos guardando o resultado no primeiro:
mov AL, 2 ;AL = 0000 0010
mov AH, 2 ; AH = 0000 0010
IMUL AH, AL ; AH = 0000 0100
; AX = 0000 0100 0000 0010
AH AL
Você ainda não conhece assembly o suficiente após ter lido isso, mas essa parte de assembly básico já está concluída, no entanto nós sempre veremos um pouco de assembly no decorrer do tutorial.
Eu escolhi essa organização porque por experiência própria, eu sei que uma aprendizagem dinâmica é melhor do que algo estático.(é sempre bom quando seu professor de matemática usa um pouco de outra matéria para desviar do assunto principal e lhe ensinar matemática de uma forma mais alternativa sem uma abordagem tão direta.)
3- Primeiros passos rumo ao Shell codingEssa seção apresenta duas ferramentas importantíssimas para quem quer encontrar vulnerabilidades; o assembler e o debugger.
Nessa seção também, nós aprofundaremos o conhecimento que você adquiriu da linguagem assembly, só que dessa vez, usando a prática para entender como, por exemplo, a stack funciona.
De começo, usaremos o MASM32 como assembler.
O MASM32 pode ser encontrado em:
http://www.masm32.com/E o ollydbg é o primeiro debugger que vamos usar e pode ser encontrado em:
http://www.ollydbg.de/download.htmUm assembler é uma espécie de compilador da linguagem assembly que passa as instruções asm para o código de máquina enquanto que um debugger também chamado de depurador, roda um programa deixando-o ver cada instrução executada pelo programa passo-a-passo.
3.1 Primeiro programa assemblyA partir de agora, as práticas serão mescladas com a teoria e a primeira parte da prática é bem simples e nos ensinará o funcionamento da stack.
Para começar, abra seu MASM32, e insira o seguinte código:
.386
.model flat, stdcall
option casemap:none
.code
start:
push 8
mov ECX, 0FFFFFFFFh
lg:
dec ECX
jnz lg
pop EAX
ret
end start
Vamos entende-lo por partes.
.386 indica qual intruction set¹ iremos usar, nesse caso, o do 80386 mesmo.
¹: podemos encontrar o instruction set da família x86 em:
http://en.wikipedia.org/wiki/X86_instruction_listings
http://home.comcast.net/~fbui/intel.html
http://www.x86.org/intel.doc/386manuals.htm .model flat, stdcall: você entenderá mais tarde...
.code: indica o início do código do programa...
start: é a label que indica todo o corpo do programa.
end start: é aonde ele termina, tudo o que iremos escrever ficará entre esses dois.
Na verdade, todas as funções que chamamos, são labels que indicam o offset(deslocamento) da próxima instrução a ser executada.. o MASM32 não entende isso muito bem já o NASM sim mas deixemos isso de lado por enquanto.
Agora clique em assemble & link como na imagem->

Agora o que nós vamos fazer, é abrir o ollydbg, clicar em File>Open e no binário gerado(tuto.exe no meu caso) e poderemos ver o código gerado por ele em ação passo a passo.(apesar de estar usando ele, eu recomendo o SoftIce, Windbg ¹ e o IDA Pro como disassembler no entanto não vou ter tempo de lhes ensinar como usar todos... na verdade, em breve mostrarei como criar suas próprias ferramentas, o que é bem melhor do que contar com softwares que quase sempre deixam a desejar diante das suas necessidades e tem bugs que são largamente explorados por aplicações cujos donos não querem que sejam ‘debugadas’)
¹: Recomendo a leitura desse blog> http://www.1bit.com.br/content.1bit/windbg1O que termos é uma imagem parecida com a seguinte:

A área marcada com o número um contém o disassembly do programa, que é o código assembly gerado por um compilador comum ou assembler, como nós fizemos o programa em assembly o código é igual ao que nós fizemos, no entanto se fosse outra linguagem de programação, nós veríamos um código assembly gerado muito maior, principalmente depois do processo de “linkagem”.
A coluna “address” indica o endereço virtual de cada instrução asm.
A coluna hex dump nada mais é do que a coluna disassembly traduzida para os códigos operacionais que são o que o compilador realmente entende.
Comment, é a coluna aonde os comentários ficam, muitas vezes o próprio olly vai lhe fornecer comentários para dizer por exemplo, que certa operação é a passagem de parâmetros para uma função determinada.
A área 2 é basicamente a janela da CPU aonde você pode ver o estado de diferentes registradores.
A área 3 é a stack.
A stack é basicamente uma área da memória para acesso rápido.
O processador é otimizado para acesso à pilha, portanto ela não é como o resto da memória virtual de um processo.
Ela cresce de 32 bits em 32 bits.
No Windows ela tem a peculiaridade de estar armazenada em endereços baixos o que é prejudicial(não tanto) para construirmos nossos shellcodes.
Ela tem outra peculiaridade que é crescer de cima para baixo e quando removemos algo dela, removemos primeiro o que está em cima.
Veremos isso daqui a pouco na prática.
A área 4 é a RAM do processo, nesse tuto não veremos muito sobre essa janela.
Agora voltemos ao nosso ollydbg.
Ele é um debugger o que significa que podemos executar cada instrução de um determinado programa e ver o que acontece na íntegra com os registradores, a stack e etc.
Então o que vamos fazer é apertar F7 para que a primeira instrução seja executada.(push 8 )
O que essa instrução faz, é colocar um valor na stack, no nosso caso o 8.

A stack cresce de cima para baixo como eu disse, um novo valor é sempre posto no topo dela e o que vemos em vermelho, é que em primeiro lugar, o valor antigo da stack ficou abaixo do novo que nós colocamos(00000008) e em segundo lugar, ainda em vermelho, podemos notar que o endereço do valor da stack desce 4 bytes(de 0012FFA4 para 0012FFA0), isso se deve pelo fato de que o maior endereço que a stack pode alcançar, é zero.
Ou seja, ela crescerá para baixo e seu topo será o zero.. quando você usar o suficiente dela para chegar no 0, seria hipoteticamente falando, a hora que o Windows diria que falta memória.

Vemos em roxo também(voltando para a figura anterior), que os registradores que sofrem alguma mudança, o olly os realça em uma cor vermelha(pode mudar de acordo com suas configurações).
Os registradores que mudaram com a instrução ‘push 8’, foram o EIP e o ESP.
O ESP nós sabemos que aponta para o local aonde nós queremos que seja o topo da stack.
E como podemos ver, o seu valor na janela da CPU é 0012FFA0, exatamente o endereço da área onde está o nosso 00000008.
Quando PUSHamos um valor para a stack, o ESP é decrementado em 4.
O outro registrador que mudou de valor, foi o EIP.
EIP é o registrador que aponta para a próxima instrução a ser executada.
Como podemos ver, a próxima instrução está em abóbora e seu endereço é o mesmo que o do EIP.
O EIP é automaticamente atualizado pela CPU para apontar para a próxima instrução e ele não pode ser mudado por instruções como mov.
EX:
mov EIP, 00401002h -> errado
(reparem também que quando eu quero escrever algum número hexadecimal, eu coloco o terminador ‘h’, caso o número já comece com alguma letra, eu tenho que por um zero antes para que o assembler possa distingui-lo de uma variável.(ex: 0DEADBEEFh)