O folgado (masm)

Transcrição

O folgado (masm)
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
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
:smile:
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
1/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
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:
2/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
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...
3/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
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 masm32libkernel32.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 :blush:
Criando o folgado
Está tudo em riba? Se você seguiu o tutorial, o arquivo texto que você digitou no editor de texto
do MASM deve estar com esta cara:
.386
.MODEL FLAT,STDCALL
includelib masm32libkernel32.lib
ExitProcess PROTO uExitCode:DWORD
.CODE
inicio:
4/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
invoke ExitProcess,0
end inicio
E isso é tudo. O arquivo texto está pronto. Agora o trabalho vai ser o de clicar no menu do
editor do MASM:
- Clique em [File / Save As] e salve o arquivo texto como folgado.asm
- Clique em [Project / Assemble ASM file] e o construtor do MASM irá produzir o arquivo
folgado.obj
- Clique em [Project / Link OBJ File] e o linker do MASM irá produzir o arquivo folgado.exe
Se por acaso esquecermos o parâmetro da função (neste caso seria invoke ExitProcess ou
invés de invoke ExitProcess,0), na etapa 2 o construtor (assembler) dará a seguinte mensagem
de erro:
Assembling E:masm32ProjetosFolgadofolgado.asm
C:masm32ProjetosFolgadofolgado.asm (6) : error A2137 : too few arguments to INVOKE
Introduza o erro no texto e faça o teste só para ir pegando o jeito. E não se esqueça de
SEMPRE salvar o arquivo texto antes de assemblar ou linkar.
Se tudo correu bem após a estapa 2, o que significa que texto está sem erros que o construtor
possa detectar, temos dois arquivos no diretório indicado: o folgado.asm, de 163 bytes, e o
folgado.obj, de 463 bytes. E se tudo continuou a correr bem, após a etapa 3 tem mais um
arquivo no diretório: folgado.exe, de 1.536 bytes. Enxutinho, não é mesmo? Tá certo que o
programa não faz grande coisa, mas mesmo assim...
Execute o folgado.exe e... surpresa! Tá pensando que eu vou dizer novamente que dá a
impressão de que o programa não faz nada? Nananinanão! A surpresa é que o programa fez
exatamente o que se esperava dele e que NÃO DEU ERRO!
Mais um pouco de teoria
Folgado é o programa, nóis não! Só mais uma coisinha...
5/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
Já pensou ficar montando protótipos de todas as funções que se queira importar de DLLs? É
muito pra cabeça e vai acabar dando calo nos dedos (e detonando o teclado). Existe um modo
muito mais elegante e folgado de obter o mesmo resultado: guardando os protótipos das
funções em arquivos que possam ser incluídos no texto dos nossos programas. Na verdade,
estes arquivos já estão prontos. Para cada biblioteca existe um arquivo de inclusão com todos
os protótipos de todas as funções desta biblioteca. São os arquivos .inc. Para a kernel32.lib
existe a kernel32.inc, para a user32.lib existe a user32.inc e assim por diante.
A diretiva include é que faz a mágica. Basta usar include seguido pelo nome do arquivo que
você quiser inserir no local onde se encontra a diretiva. Usando este expediente, o texto ficaria
assim:
.386
.MODEL FLAT,STDCALL
includelib masm32libkernel32.lib
include masm32includekernel32.inc
.CODE
inicio:
invoke ExitProcess,0
end inicio
Quando o MASM32 processa a linha "include masm32includekernel32.inc", ele abre o arquivo
kernel32.inc, que se encontra no diretório masm32include, e processa o conteúdo do arquivo
como se o conteúdo do kernel32.inc estivesse presente no seu código. Desta forma, pode-se
substituir um caminhão de linhas com protótipos de funções com umas poucas linhas de
include.
Se você tiver curiosidade, abra num editor de texto um arquivo include qualquer. Estes arquivos
estão em texto ASCII raso, o que significa que você pode lê-los com facilidade além de poder
fazer seus próprios arquivos include e incorporá-los aos seus programas quando necessário.
Os arquivos .inc podem conter outras definições (constantes, estruturas, etc) além de
protótipos de funções. Um dos mais completos (e constantemente atualizado) é o arquivo
windows.inc do Iczelion & Hutch. É pau pra toda obra. Você encontra este arquivo no pacote do
MASM32 versão 7 do Hutch no site do Iczelion e no site do hutch .
Conferindo o trabalho do MASM
6/7
O folgado (masm)
Escrito por vovó Vicki
Sex, 15.12.2006 20:23 - Última atualização Ter, 16.02.2010 21:29
Existem diversos desassembladores muito bons, como o WDasm e o ADA. Eu trabalho muito
com o OllyDbg, um debugger gratuito para Windows com um desassemblador excelente. Você
pode encontrá-lo no site do autor em http://home.t-online.de/home/Ollydbg/ . Abrindo o
executável com o OllyDbg encontramos o seguinte:
00401000
00401002
00401007
00401008
0040100E
0040100F
... ...
00401FFF
> $ 6A 00 PUSH 0 ; /ExitCode = 0
. E8 01000000 CALL ; ExitProcess
. CC INT3
$- FF25 00204000 JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>
00 DB 00
00 DB 00
00 DB 00
No endereço 401000, ponto de entrada do executável (module entrypoint), já encontramos o
push do parâmetro da função KERNEL32.ExitProcess. Logo depois vem o call para a função.
O MASM trabalhou direitinho. Note que o nosso programa ocupa 4096 bytes (ou 4 Kb) de
memória - vai do endereço 401000 até 401FFF - apesar de só ocupar 14 bytes com o código.
O restante é preenchido com zeros.
:vovo: Facilite a sua vida
Para facilitar a sua vida coloquei um arquivo zipado com o tutorial e o código fonte na seção
de downloads da Aldeia. Procure na categoria tutoriais/Assembly Numaboa.
7/7