Aldeia Numaboa
Um portal diferente em Português do Brasil
Informática da Aldeia

Tutoriais

Na Aldeia

Há 99 visitantes e 1 usuário registrado online

3293 registros
0 hoje
11 nesta semana
28 neste mês

Boas vindas: Armaggedon

Estatística

Artigos: 1062
Leituras: 6007197
Arquivados: 21
Downloads: 533
Baixados: 171220
Glossário: 1208
Bibliografia: 25
Links: 90

Visitas de onde

Top 5:
Brasil flag 73%Brasil (37820)
Portugal flag 4%Portugal (2326)
EUA flag 3%EUA (1786)
Holanda flag 0%Holanda (235)
Rússia flag 0%Rússia (217)
52069 visitas de 97 países

Hoje:4
Ontem:2471
No mês:26254
Mês passado:25815
Total:52069
Recorde:3037
No dia:04.03.10
Leituras hoje:6096
Leituras Total:223992
Bots hoje:10
Dados desde:16.02.2010

Login



Kanji da hora




Faça contato






Sex

15

Dez

2006


20:23

O folgado (masm) PDF Imprimir Indique esta página
(8 votos, média 4.5 de 5)
Escrito por vovó Vicki   


Índice do Artigo
O folgado (masm)
O executável
Todas as páginas

Nível intermediário Aprender fazendo, este é o segredo. Neste tutorial ainda tem muita teoria e o primeiro resultado é, no mínimo, frustrante. Mas não desanime: veja como fazer a API do Windows trabalhar para você.

Projeto

Vamos realizar um projeto extremamente simples, copiado descaradamente do tutorial 3 do Iczelion. A idéia é genial: um programa cuja única função é retornar ao Windows, ou seja, dá a impressão de que não faz nada - um folgado. Apesar disso, é um programa! Na verdade, o que interessa realmente são os conceitos necessários para programar "o folgado". Sei que ainda é muita teoria para pouca prática, mas o começo é assim mesmo.

Planejando o folgado

Vamos escrever um programa que tem apenas uma linha de código. Através deste projeto teremos a oportunidade de rever a estrutura de um programa, além de ampliar nossos conhecimentos. Então vamos planejar:

  1. Nosso sistema operacional é o Windows de 32 bits (ou 64, se você é dos apressadinhos).
  2. Não precisamos mais do que o conjunto de instruções do processador 386.
  3. Temos o MASM à disposição para fazer o trabalho pesado.
  4. O objetivo do programa é voltar para o Windows assim que for executado. Nada mais sorriso

Botando a mão na massa

Chame o QEditor do MASM e digite o seguinte:

.386
.MODEL FLAT,STDCALL
.CODE
inicio:

end inicio

O sistema operacional win32 possui uma quantidade muito grande de funções que ele utiliza - nada impede que a gente pegue uma carona e também as usemos. Esta imensa coleção de funções do win32 é chamada de API (Application Programming Interface). As funções estão organizadas em bibliotecas denominadas bibliotecas de vínculo dinâmico (dynamic-linked libraries) ou DLL. Três delas são as mais importantes e as mais utilizadas: kernel32.dll, user32.dll e gdi32.dll. A kernel32.dll contém funções API que lidam com a memória e com a administração de processos. A user32.dll possui funções que controlam a aparência da interface com o usuário e a gdi32.dll tem funções responsáveis por operações gráficas. Caso exista uma função na API win32 que execute exatamente o trabalho que pretendemos realizar, podemos usá-la diretamente no nosso programa ao invés de escrever todo o procedimento.

A função que precisamos chama-se ExitProcess e está localizada na biblioteca kernel32.lib. Dando uma olhada na referência da API do Windows, encontramos o seguinte:

VOID ExitProcess(
UINT uExitCode // código de saída para todos as linhas de execução (threads)
);

Esta função não tem valor de retorno (é VOID) e exige um parâmetro (uExitCode) do tipo UINT. UINT é apenas um dos muitos nomes que o Windows usa para uma DWORD (double word - palavra de 32 bits).

Para poder usar esta função, é preciso passar algumas informações para o assembler e para o linker: o nome da função e a biblioteca onde ela está. Com estas informações, o assembler/linker indicará ao executável onde ele deve buscar a função que deve ser executada. O código da função NÃO é adicionado ao nosso executável, apenas as informações de qual função (ExitProcess) deve ser executada e onde encontrá-la (na kernel32.dll) em tempo de execução. De posse dessas informações, nosso programa será capaz de executar uma operação de chamada, ou seja, um call ExitProcess. A instrução call faz parte do conjunto de instruções do 386 e funciona da seguinte maneira:

Chamada de função

Antes de incluirmos a instrução call ExitProcess no nosso código, precisamos colocar os parâmetros correspondentes na pilha. A pilha é um registrador especial da CPU, cujo conteúdo indica o endereço da memória onde se deposita uma informação que deva ser temporariamente guardada no curso do processamento. No nosso exemplo, é apenas o parâmetro uExitCode, o valor que o Windows recebe quando o nosso programa termina. Para isto usamos a instrução push (empurre para a pilha) e o fragmento de código fica assim:

push 0
call ExitProcess

Se esquecermos de "pushar" o valor do parâmetro para a pilha, o assembler/linker não irá notar a falta. Usamos uma instrução do conjunto de instruções do 386 e o assembler não tem como checar os parâmetros (quem usa a instrução deve saber o que está fazendo...) ao produzir o executável. Você só vai notar o erro quando executar o programa que, logicamente, dá pau.

Existe um modo mais seguro e cômodo de fazer chamadas. É através do INVOKE, uma sintaxe de chamada de alto nível. A sintaxe de INVOKE é a seguinte:

INVOKE expressão[,argumentos]

onde expressão pode ser o nome de uma função (ou um ponteiro para a função) e os argumentos (parâmetros) são separados por vírgulas. Neste caso o assembler/linker tem condições de verificar a sintaxe porque os parâmetros são citados explicitamente (e não apenas "pushados" para a pilha). Dá para perceber que um pouquinho de alto nível no assembly não faz mal a ninguém. Só tem um porém... para poder utilizar o INVOKE é necessário fornecer um protótipo da função que se quer usar.

O protótipo de uma função informa os atributos desta função para que o assembler (e o linker) possam fazer uma checagem dos tipos. O formato de um protótipo é o nome da função seguido da palavra-chave PROTO, e esta seguida da lista de parâmetros formando pares de nome:tipo de dado separados por vírgulas.

NomeDaFunção PROTO [NomeDoParâmetro]:TipoDeDado,[NomeDoParâmetro]:TipoDeDado...

Tendo essas informações podemos construir o protótipo, que nada mais faz do que definir ExitProcess como uma função que usa apenas um parâmetro do tipo DWORD:

ExitProcess PROTO uExitCode:DWORD

Uau! Agora podemos mostrar o protótipo da ExitProcess ao construtor (assembler). É claro que o construtor precisa ser apresentado à função ANTES de fazer uso da mesma no código. A apresentação consiste no protótipo e na biblioteca que contém a função. Colocamos então este par junto com as outras solicitações:

.386
.MODEL FLAT,STDCALL
includelib \masm32\lib\kernel32.lib
ExitProcess PROTO uExitCode:DWORD

.CODE
inicio:

end inicio

Mas que negócio é este de includelib? A diretiva includelib é apenas uma maneira de indicar ao assembler quais as bibliotecas de importação que o programa usa. Quando o assembler encontra este tipo de diretiva, ele põe um comando para o linker no arquivo objeto para que o linker saiba quais bibliotecas de importação precisam ser vinculadas ao programa. É o caminho das pedras...

Como eu disse na introdução, é muita teoria. Espero que, até este ponto, tudo tenha ficado claro para que possamos criar nosso fantástico programa que não faz nada sem graca



Última atualização ( Ter, 16.02.2010 21:29 )
 

Topo

Topo

Exceto onde especificamente citado, todo material deste site está sob Licença Creative Commons