[LHC] [ESP8266 grátis!] Desafio para crackear programas

Leandro A. F. Pereira leandro at tia.mat.br
Thu Mar 16 19:31:38 PDT 2017


2017-02-26 13:16 GMT-08:00 Leandro A. F. Pereira <leandro at tia.mat.br>:
>
> O primeiro programa está aqui para quem quiser brincar:
> http://labs.hardinfo.org/tmp/crack1
>

Bom, passou o tempo, então vamos lá --

O programa está compactado com UPX: esse programa compacta um
executável e gera outro programa, que descompacta e executa o código
original. Se tentou descompilar esse executável e ficou perdido, já
sabe o que aconteceu. A dica pra descobrir isso era só ter usado o
programa "strings".

O pulo do gato é que se você baixar o UPX e tentar descompactar, não
vai conseguir, pois a assinatura foi mudada. Teria duas alternativas
aqui: modificar o UPX para ignorar a assinatura, ou ler o código
fonte, descobrir qual é a assinatura, e onde que ela é guardada no
arquivo original, e modificar o executável.  No caso, eu troquei
"UPX!" por "upx!", pois a licença do programa não permite alterar o
executável de forma que não diga que foi comprimido por ele.

        $ sed -i -e s/upx!/UPX!/g crack1
        $ upx -d crack1
                           Ultimate Packer for eXecutables
                              Copyright (C) 1996 - 2017
    UPX 3.93        Markus Oberhumer, Laszlo Molnar & John Reiser
Jan 29th 2017

            File size         Ratio      Format      Name
       --------------------   ------   -----------   -----------
         38700 <-     21656   55.96%   linux/amd64   crack1

    Unpacked 1 file.


Aí, tendo o executável descompactado, é possível ver com o comando
"file" que ele foi compilado com símbolos de debug. Usando o gdb, dá
pra fazer o breakpoint no main() e ir executando passo a passo. Mas
vamos tentar brincar um pouco aqui antes.

Um programa legal que tem no Linux é o strace. Toda chamada de sistema
que é feita (toda vez que o programa tem que pedir pro kernel alguma
coisa, tipo abrir arquivo, ler arquivo, etc), ele vai mostrar na tela
com os parâmetros. É excelente pra fazer troubleshooting e descobrir
qual é o arquivo que guarda as informações de registro.

Geralmente o programa pede pro kernel um monte de coisa, e estamos
interessados no momento somente em acesso à arquivos... então dá pra
executar o programa assim: strace -e trace=file ./crack1 -- o que
mostra somente arquivos sendo abertos, etc.

Aí rodando, vai descobrir que a licença fica em ~/.crack1rc:

    open("/home/leandro/.crack1rc", O_RDONLY|O_CLOEXEC) = 3

Não vai dar pra ver pela execução o que ele tenta achar no arquivo,
mas de novo, dá pra usar o strings e procurar por coisas suspeitas. No
caso, uma licença inválida é essa:

     Name=John Smith
     Serial=de onde tirou esse escudo?
     Type=enterprise

E algumas dessas strings estão no executável.

    $ strings crack1 | grep Name -C5
    []A\A]A^A_
    C8H9C(v
    G8H9G(v
    HOME
    %s/.crack1rc
    Name=
    Serial=
    Type=single-user
    Type=enterprise
    %s|%s
    %04d-%05x-%04X
    The registration information is invalid.
    This program is registered to:
             Name: %s
    Serial number: %s
     License type: %s
    This is a shareware version of the program.
    Unregistered
    Single user


O parâmetro -Cn mostra n linhas de contexto pra frente e pra trás de
um match. Ela é útil nesse caso pois é comum esse tipo de string
estarem próximas umas das outras... e dessa saída dá pra gente ver
algumas coisas:

     HOME: variável de ambiente que guarda o caminho pro diretório do usuário
     %/.crack1rc: parece uma string de formatação, e que é usada pra
juntar com o caminho apontado por $HOME (evidente pela chamada à
open() ali em cima)
     Name=: linha no arquivo de configuração
     Serial=: linha no arquivo de configuração
     Type=single-user: opa, temos um tipo de licença aqui!
     Type=enterprise: e outro tipo de licença!

E aí tem uma outra string de formatação: %04d-%05x-%04X. Isso parece a
formatação de um número serial, não? Então a gente já sabe que alguma
função do tipo printf é usada para gerar o número serial... então é
possível que o serial seja comparado como string? Vamos desassemblar
então:

    $ objdump -Ds ./crack1 | joe -

(Eu uso o joe como editor, mas você pode usar qualquer coisa aqui.)

Para comparar strings em C, é comum usar strcmp() ou strncmp(). Faça
uma busca por ela. Em algum canto, deve achar algum código próximo,
que também faz uma chamada pra alguma função da família printf(). Deve
ser a comparação da senha. Vamos ver se acha algo, então...

O primeiro resultado é esse aqui:

  40033e:       e8 ad 39 00 00          callq  403cf0 <strcmp>

(A primeira coluna é o endereço, as letras depois são o código de
máquina praquela instrução, e depois vem o mnemônico.)

O que tem pra cima? Bom, 3 instruções pra cima, tem isso aqui:

  400333:       e8 54 1d 00 00          callq  40208c <snprintf>

(Dá pra ver que a instrução "callq" é 0xe8, pois é igual nos dois
casos, só muda o imediato com o endereço.)

Opa, justamente o que a gente tava procurando! Será que é isso? Tem um
jeito fácil de resolver... vamos procurar por algum jump condicional,
e trocá-lo pelo inverso.

Colocando todo o código aqui desde a comparação:
  40033e:       e8 ad 39 00 00          callq  403cf0 <strcmp>
  400343:       85 c0                   test   %eax,%eax
  400345:       41 89 c5                mov    %eax,%r13d
  400348:       74 0c                   je     400356 <main+0x202>

Dá pra ver o "je", que é "jump if equal". O opcode dele é 0x74. Se der
uma olhada numa página que lista instruções, tipo [1], ou mais
especificamente a página sobre os jumps condicionais [2], dá pra
confirmar essa informação. O que a gente quer é uma instrução de 2
bytes também que faça o contrário do "je", um "jne" (jump if not
equal)... e na lista tem justamente o 0x75.  Vamos trocar esse byte lá
no executável e rodar pra ver o que acontece.

[1] http://www.felixcloutier.com/x86/
[2] http://www.felixcloutier.com/x86/Jcc.html

Dá pra fazer a troca desse byte com editor hexadecimal, alguns
disassemblers ou editores de texto (com o joe eu consigo), mas como
nem todo mundo usa o joe, vamos usar o bash com algumas ferramentas
que são bem comuns.

Primeiro a gente faz um hexdump do executável todo:

     hexdump -ve '1/1 "%.2x"' ./crack1 | \

Isso aí diz "coloca byte a byte do executável em hexa na tela pra mim,
sem espaço nenhum entre eles". Aí procura por alguma sequência
conhecida que provavelmente só vai ocorrer naquele excerto ali de
cima, trocando 74 por 75:

    sed "s/85C04189C5740C/85C04189C5750C/g" | \

Aí usa o xxd pra converter esse dump hexa em binário de novo:

    xxd -r -p > patched

E... tentar rodar:

    chmod +x patched
    ./patched
    This program is registered to:
             Name: John Smith

    Serial number: something something something

     License type: Enterprise

Ha! Sucesso! Trocando apenas um byte, o programa tá rodando com a
melhor licença.

Pra facilitar a vida, tá aqui um shell script que faz todo o trabalho
sujo, de descompactar o executável, fazer o patch, e até recompactar
de novo: https://hastebin.com/uyicitiraq.bash

E, claro, valeu aí Vitor e Rodrigo, que resolveram o problema. Esse
estava bem tranquilo, mas se houver algum próximo é bem provável que o
nível de dificuldade suba um bocado.

Se alguém tiver alguma dúvida, é só perguntar.

-- 
 Leandro



More information about the HSC mailing list