domingo, 19 de outubro de 2008

ShellCoding parte 3 > 3.2 Um novo debugger, um novo ponto de vista... o mesmo programa. > 3.3 A base dos Shellcodes

O que me levou ao ollydbg foi a sua simplicidade... eu sou mais familiarizado com outro debugger, o windbg.
Apesar dele ser “mais complexo”, foi o primeiro que eu aprendi a mexer por influencia de alguns amigos, uma ótima influência.

O ollydbg é bem simples, mas nós somos machos certo?*coça o saco*
Então vamos mostrar como somos machos e que não temos medo de cara/interface feia e vamos aprender a mexer um pouco no windbg =]
Eu não me sentiria bem se passasse esse tutorial todo sem pelo menos fazer um “overview” do windbg... e aqui está ele. 


O windbg é muito poderoso e é usado pelo próprio time de programadores da Microsoft entre outras coisas, para desenvolver o Windows!

Sim, ele permite a depuração de drivers e afins.

Algumas afirmações que eu fiz para vocês, foram bem mastigadas, muito mesmo.
Agora vamos usar esse debugger poderoso para complementar essas informações.

Ele é gratuito e você não terá que gastar nenhum centavo, apenas alguns minutos fazendo o download do pacote de debugging da Microsoft.
Onde encontrá-lo?
Aqui:
http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx

Escolha a instalação com base na versão do SO que você tem(32 ou 64 bits).
Instale.

O pacote vem com entre outros aplicativos, o logger que é usado para identificar quais APIs um programa está usando, logview utilizado para visualizar os logs gerados pelo logger.exe ,KD que é um kernel debbuger e etc... por último mas não menos importante o windbg, o pacote também vem com outros debuggers user-mode application como o CDB mas não entraremos em detalhes. 




Agora, abra o windbg na pasta que você instalou o pacote de debugging tools for Windows.
Você vai ter uma tela meio feinha de começo com somente a command bar à mostra.

O que você vai fazer é pressionar “CTRL + E” e abrir aquele assembly program que nós debugamos com o olly, se lembra?

Imediatamente ele nos mostra os módulos carregados e o estado dos registradores.(módulo é de certa forma só um outro nome para image file names(nome das “imagens” de arquivos /carregados/)



Existe um módulo em especial que sempre será carregado, é a DLL Kernel32.
Todos os programas que você tentar debuggar verá que essa DLL está presente.

Você quer saber por que isso te interessa?

Quando um processo é iniciado, o Windows cria um tipo de “header” para o processo.
Esse header se chama PEB(process enviroment block)¹.
Nele estão contidas todas as informações de um processo, inclusive os módulos carregados por ordem de loading.

Para nós, o conhecimento desse bloco é importante pois no Windows, nossos exploits precisam chamar APIs diferentemente de sistemas nix like que podemos chamar diretamente sys calls que estão sempre paradinhas la esperando por você, no entanto nós nem sempre sabemos o endereço em que uma DLL foi carregada e queremos que nosso exploit chame uma função da user32 por exemplo, o que fazemos?!

Bem como o kernel32 é sempre carregado e é um dos primeiros módulos carregados, ele está sempre no mesmo lugar dentro do PEB de um processo.

E esse módulo tem duas APIs importantíssimas para podermos usar qualquer uma que quisermos... São as LoadLibrary* e GetProcessAdress* que respectivamente carregam um módulo e pegam o endereço de uma API contida nele.

Mas você ainda não vai construir um exploit agora... só que a partir de agora, as informações que antes eu disse que foram “filtradas/mastigadas” não serão mais tão mastigadas, então se prepare para uma enxurrada controlada de nomes estranhos aparecendo na sua tela. =P

¹:http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html



Voltemos ao Windbg com nosso tuto.exe carregado, aperte “ALT + F7” para ver o disassembly.
E repare em uma coisa, nós estamos antes mesmo do programa começar, esse não é o código do nosso processo!
Então cadê ele?

O Windows ainda está por chamar o nosso programa mas se quisermos encontrar aonde começa o código dele é simples... vamos encontrar o entry point do módulo tuto.exe.

Entry point é basicamente aonde o código do programa começa, ele denota a parte do image file para o qual nós iremos dar um long jump, ou seja, a primeira função executada de um determinado processo.
Hoje em dia é normal que os programas só tenham um entry point, por exemplo o int WinMain() que nós vemos todo dia em nossos programas win app C++/C.

Image File ou imagem do arquivo, é a imagem que é criada na memória de um determinado arquivo.
Quando você executa um determinado programa, por exemplo, o nosso tuto.exe, o Windows copia ele todo para a memória, essa cópia ganha o nome de image file.

Mas não para por ai, não pense que memória é sinônimo de memória RAM física não.

Cada processo tem uma determinada quantidade de memória que é chamada de memória virtual.
Essa memória virtual é endereçável(lógico) mas o seu endereço é relativo, ou seja, nem sempre corresponde ao endereço REAL na memória física.
Isso quer dizer que uma variável de um programa pode estar claramente no endereço 0x0012ffaa, outro programa ter uma variável no mesmo endereço e no entanto, elas podem sim ter valores diferentes... isso porque os processos(normalmente) não compartilham sua memória virtual com outros, eles podem ter claramente o mesmo endereço virtual, sendo que o endereço virtual é mapeado(transformado) para um físico no momento de execução do processo.
Cada processo pode ter até 2GB de memória virtual, mas nem toda ela é alocável, parte é reservada pelo sistema e os endereços vão de 0x00000000e16 à 0x7FFFFFFFe16.(‘e16’ é notação científica)

Lembra-se que eu disse que a stack poderia chegar ao endereço 0x00 e o sistema falaria que falta memória?
 Aquilo ajudou a entender que a memória cresce do maior endereço para o menor, mas eu coloquei aquele “hipoteticamente falando” justamente porque isso não é possível, o endereço virtual 0x00 é reservado para o sistema e se sobrescrito possivelmente quebra o programa.
E sim, no win32 a stack faz parte da memória virtual de um processo e hoje em dia tem fronteiras bem definidas ao contrário das suas antigas versões.

O mundo era mais bonito quando o céu só podia ser azul não é?

Mas a vida real não é assim e as coisas são mais complicadas do que isso, então se concentre e não perca o foco porque eu ainda não comecei a parte mais complicada.




Aprendemos o que é o entry point, então vamos achá-lo?

O windbg vem com várias extensões muito úteis e vamos usar uma delas.
Na command bar digite “!dlls”.
Pronto!
Agora você está vendo informações específicas dos módulos carregados.



E está ai também o entry point do tuto.exe certo?
Então já sabemos seu endereço(401000), vamos colocar um breakpoint nesse endereço e mandar o windbg deixar o programa correr livre leve e solto até encontrar o que nós queremos.

Digite .cls para limpar a tela(so fresco mesmo =P)
Agora digite:
 
BP /1 401000 “.echo =============|| ENTRY POINT ||=================” [enter]


BP, seta um breakpoint.
/1 diz que assim que esse break point for executado uma vez, ele será deletado.
401000 é o endereço.
O que segue entre aspas é o comando que será executado quando esse breakpoint for encontrado.
No nosso caso, o echo vai exibir a string “...ENTRY P...” na tela.

Se aparecer o status “busy” ao lado da command bar, espere até o status voltar ao normal.

Agora digite ‘g’(sem aspas) que significa “oh baby, let’s Go” ou só “GO” se você preferir. =P

Ele vai rodar e vai para justamente aonde começa o nosso código tão conhecido. =]

(posteriormente você poderá substituir essa ação toda por "g @$exentry")

 

3.3 A base dos Shellcodes.


Novo capítulo, mas nós vamos continuar com aquele nosso debugger(windbg) e no ponto aonde paramos, mas desta vez, a idéia não é explicar o que vai acontecer com o programa e sim entender talvez o último ponto necessário para começarmos a construir nossos exploits.

Só para recapitular:



A primeira coisa que quero que você faça após encontrar o entry point de tuto.exe é apertar f10, o debugger vai dar uma “passo” a frente colocando 00000008 na stack.
Ok, isso nós já sabemos.
O windbg já mostra o estado dos registradores, a próxima instrução a ser executada e etc, normal.

Vamos agora fazer um dump ou seja, ver o valor que está em certo local da memória virtual do nosso processo.

A função ‘d’ do windbg é responsável por fazer o dump da memória virtual de um processo.
Ela funciona com diferentes tamanhos.
Por exemplo, se você escrever ‘db *endereço*’ ela vai te mostrar o dump hexadecimal em bytes(8 bits) do endereço desejado.

Bem, nós sabemos que esp aponta para o topo da stack certo?
Então como nós já sabemos que o 0~8 já deve estar na stack, vamos ver essa função em ação passando para ela o endereço que está em esp.

Digite ‘dd esp’ e logo você verá isso:
0012ffa0 00000008 76373833 7ffdd000 0012ffec
0012ffb0 77a5a9bd 7ffdd000 00129e72 00000000
0012ffc0 00000000 7ffdd000 00000000 00000000
0012ffd0 00000000 0012ffb8 00000000 ffffffff
0012ffe0 77a28bf2 77a69096 00000000 00000000
0012fff0 00000000 00401000 7ffdd000 00000000
00130000 78746341 00000020 00000001 00003008
00130010 000000dc 00000000 00000020 00000000

Percebe o 8 no topo?
Repare que dd faz ele exibir em Double words ou seja, de 32 em 32 bits ou 8 caracteres hexadecimais/4 bytes.

Repare também que ele exibe 5 colunas, a primeria é o endereço do valor da segunda coluna na memória vitual do processo.

Nós podemos obrigá-la a exibir apenas uma coluna, assim:

‘dd /c 1 esp’
Assim nós visualizaremos como se estivéssemos no ollydbg, de quatro em quatro bytes e seus respectivos endereços ao lado.

0012ffa0 00000008
0012ffa4 76373833
0012ffa8 7ffdd000
0012ffac 0012ffec
0012ffb0 77a5a9bd
0012ffb4 7ffdd000
0012ffb8 00129e72
0012ffbc 00000000
0012ffc0 00000000
0012ffc4 7ffdd000
0012ffc8 00000000
0012ffcc 00000000
0012ffd0 00000000
0012ffd4 0012ffb8
0012ffd8 00000000
0012ffdc ffffffff
0012ffe0 77a28bf2
0012ffe4 77a69096
0012ffe8 00000000
0012ffec 00000000
0012fff0 00000000
0012fff4 00401000
0012fff8 7ffdd000
0012fffc 00000000
00130000 78746341
00130004 00000020
00130008 00000001
0013000c 00003008
00130010 000000dc
00130014 00000000
00130018 00000020
0013001c 00000000

Nós podemos visualizar de byte em byte ao invés de 4 em 4 como antes.
Usando o db como foi dito antes e podemos ainda mais, podemos dizer apartir de qual endereço da memória nós queremos ver e até qual.
Assim:
‘db esp esp+3’
0:000> db esp esp+3
0012ffa0 08 00 00 00

Ou seja, ele vai exibir esp(12ffa0) até esp+3(12ffa43) em outras palavras ele vai exibir o 
12ffa0
12ffa1
12ffa2
12ffa3

Ou seja, um double Word(4 bytes) só que de byte em byte.

Agora, se lembra que eu falei que as instruções asm são representações para os opcodes¹ e que esses sim é que rodam e fazem algo?
Bem, o programa já foi compilado e esses opcodes já estão na memória virtual do processo, o disassembly só reconverte esses opcodes para código asm novamente o que não é nada difícil.

¹: tem um tuto mto bom que explica como são feitos, ele foi feito pelo alucard e está aqui>http://alucard.dxs.googlepages.com/Tutorial_OpCode.html

Bem, se esses opcodes estão no endereço em que vemos o disassembly, então nós podemos “ripalos”.
Ou seja, nós podemos ver quais são esses opcodes e colocar em outro programa.

Vamos dumpar todo o código do nosso tuto.exe.

Digite ‘dd 401000 40100b’

401000 é o endereço do entry point do nosso módulo como já vimos, e 40100b é o endereço da sua última instrução(ret).
A saída desse dump é:
00401000 ffb9086a 49ffffff c358fd75

A primeira coluna é um endereço como já sabemos e as 3 seguintes são o dump dessa área da memória.

Se lembra que nosso programa põe o 8 na stack e depois o coloca em eax?
Se lembra que eu também disse que eax é usado para retornar valores de funções.

Logo, imagine esse código:


int soma()
{
 int eax = 8, ecx = 0xFFFFFFFF;
 for(;ecx>0;ecx--);
 return eax;
}
 

Pois bem, o nosso programa é a completa representação desse código C.

Ele põe 8 na stack, cria um loop de 0xFFFFFFFF até 0x00(uma espécie de delay hardcoded) põe 8 em eax e ret retorna para o ponto aonde o processo chamador parou tirando da stack o endereço pra próxima instrução e colocando em eip, como eax é usado para retornar valores e resultados de funções então logo a nossa função retorna um número, o 8.

Bem, nós já temos os opcodes, que tal colocarmos ele em um programa e ver se roda? 

Bem, vamos primeiro visualizá-lo como um programa C completo:

#include  stdio.h


int soma()
{
  int eax = 8, ecx = 0xFFFFFFFF;

  for (;ecx > 0; ecx--);

  return eax;
}

int main()
{
  printf("Valor: %d", soma() );

}

Isso nós já sabemos né?

Então que tal colocarmos aqueles opcodes e ver se tem o mesmo resultado?

Vamos fazer isso, repare que temos 3 colunas de 4 bytes de dados.
4 bytes = int

Então vamos criar um array de 3 int’s e vamos forçar o nosso programa C a chamá-lo como se fosse uma função...

Primeiro vamos copiar essas 3 colunas de opcodes e transformar em um array de ints.

int OpCodes[] = {0xffb9086a, 0x49ffffff, 0xc358fd75};

Veja que eu coloco o ‘0x’ na frente para o compilador saber que são valores hexadecimais.

Agora que nós temos o tal array vamos forçar o compilador a chamá-lo como uma função que retornar um inteiro:

((int (*) ())OpCodes)()

 

Não se assuste, olhe por um lado, nós temos o array OpCodes.

Vamos usar um cast de uma função que retorna um inteiro n’ele:


( int (*)() ) <- esse é o cast

Envolvemos o cast e o endereço do primeiro elemento de Opcodes entre parênteses:

((int) (*) ()) OpCodes)

E agora que já o transformamos em uma função que retorna um inteiro, vamos chamar essa função:

((int) (*) ()) OpCodes) () 

Bem, vamos colocar isso tudo num printf para que ele exiba o valor 8:

#include stdio.h


int main()
{
  int OpCodes[] = {0xffb9086a, 0x49ffffff, 0xc358fd75};

  printf("Valor: %d", ((int (*) ())OpCodes)() );

}  

Vê?!

Alguns preferem fazer a mesma coisa só que com uma string.

Strings em C são simples arrays de bytes... isso significa que nós iremos ter que colocar um ‘\x’¹ para cada byte(2 dígitos hex), no final o resultado é o mesmo, a diferença é quem escreve mais:

#include


int main()
{
  int OpCodes[] = {0xffb9086a, 0x49ffffff, 0xc358fd75};

  char szOpCodes[] =
  "\x6a\x08\xb9\xff"
  "\xff\xff\xff\x49"
  "\x75\xfd\x58\xc3";


  printf("Valor: %d", ((int (*) ())szOpCodes)() );

}

Repare que eu só adicionei um ‘sz’² e troquei a ordem dos bytes.

Você já vai saber o porque dessa ordem trocada... mas por hoje é só, cya. =]


¹: indica que o próximo byte não deve ser interpretado como caracter
²: notação húngara indicando uma string terminada em zero.


2 comentários:

><))).>