Jump to content
  • Alterando o comportamento de um executável

       (0 reviews)

    Recentemente fui requisitado para verificar a possibilidade de um determinado sistema cliente-servidor funcionar em thin clients (terminais leves, que usam o processamento e o SO de um servidor de terminais).

    O sistema baseia-se em um aplicativo servidor e um pequeno aplicativo cliente, que deve ser instalado em todas as estações. Mas ele não foi feito para funcionar com terminal services: este pequeno aplicativo instalado nas estações mantém suas configurações numa chave de registro em HKEY_LOCAL_MACHINE. Logo, cada estação precisa de um SO para que o aplicativo crie esta chave e armazene suas configurações individuais.

    Imediatamente pensei que se este aplicativo armazenasse suas configurações em HKEY_CURRENT_USER, seu funcionamento em thin client seria viável, uma vez que esta chave do registro existe para cada usuário que se loga no sistema (diferente da chave HKEY_LOCAL_MACHINE, que é única no SO).

    Neste artigo vou mostrar como alterar o comportamento de um executável que armazena suas configurações em HKLM, fazendo-o armazenar em HKCU, para atingir o objetivo desejado.

    O primeiro passo é analisar o executável e verificar que valores ele cria no registro e qual o momento em que isto acontece. Para isso podemos usar o RegMon (Registry Monitor).

    Com o RegMon aberto, criei um filtro para incluir somente o cliente.exe na monitoração do RegMon e dar um highlight quando a operação for CreateKey (criar uma chave no registro).

    1-regmon.png.48e6c3e3a42a35dada3d611ef6f48c46.png

     

    2-filtro.png.925b575a55ccdd88ed48d1468fe1062b.png

     

    Ao executar cliente.exe, vamos ver o que o RegMon informa.

    3-regmon-keys1.png.32794b434b8240da929857dc7382715b.png

    Na primeira linha com highlight em vermelho, vemos que o software procura pela chave HKCUSoftwarePCSCliente. Como não existia tal chave, ele a criou (visível nas linhas que seguem).

    Depois o software procura pela chave HKLMSoftwarePCSCliente e também não encontra (veja a coluna RESULT) e também a cria.

    Rolando mais abaixo, vemos que ele procura por sub-chaves também, mas ainda não as cria:

    4-regmon-keys2.png.06d253f6a25a141e4a93d4b99665181b.png

    Cliquei no botão Aplicar do software, que salva as configurações e aí sim, o RegMon acusou várias criações de sub-chaves (CreateKey) e inclusive a criação de valores (SetValue). Veja que todos são em HKLM:

    5-regmon-keys3.png.e78457aec9f18e59696bdec2c6562ce7.png

    Nossa intenção é fazer com que o software crie estes valores em HKCU, para permitir o uso de thin clients. Já sabemos quando e onde o programa cria as chaves, agora precisamos saber qual é a API do Windows responsável por criar as chaves no registro. Para isso, basta consultar o Win32 SDK Online Help (win32.hlp). Na busca, procurei por “reg” e imediatamente achei a RegCreateKey e a RegCreateKeyEx. Mas a RegCreateKey é para compatibilidade com aplicativos do Windows 3.11, portanto a API correta é a RegCreateKeyEx. Vamos olhar o trecho inicial da documentação sobre ela:

    LONG RegCreateKeyEx(
    
    HKEY hKey, // handle of an open key
    LPCTSTR lpszSubKey, // address of subkey name
    DWORD dwReserved, // reserved
    LPTSTR lpszClass, // address of class string
    DWORD fdwOptions, // special options flag
    REGSAM samDesired, // desired security access
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of key security structure
    PHKEY phkResult, // address of buffer for opened handle
    LPDWORD lpdwDisposition // address of disposition value buffer
    );
    
    
    Parameters:
    
    hKey
    
    
    Identifies a currently open key or any of the following predefined reserved handle values:
    
    HKEY_CLASSES_ROOT
    HKEY_CURRENT_USER
    HKEY_LOCAL_MACHINE
    HKEY_USERS
    
    
    The key opened or created by the RegCreateKeyEx function is a subkey of the key identified by the hKey parameter.

    Basta ler este trecho da documentação para perceber que o parâmetro hKey, que é passado para a função RegCreateKey é o responsável por definir se a criação da sub-chave será em HKCR, HKCU, HKLM ou HKU.

    Então temos que encontrar o ponto onde o programa chama a função RegCreateKey, passando como parâmetro hKey o valor que representa HKCL e substituir pelo valor que representa HKCU. Mas que valores são esses? Bem, eu não achei na documentação mas podemos encontrar isso já debugando o programa. Mãos à obra!

    Ao abrir o cliente.exe no OllyDbg (um debugger de executáveis 32-bits), antes de rodar o software, precisamos definir breakpoints (pontos de parada, numa tradução livre) para que o debugger interrompa a execução do software quando a API que queremos for chamada, no caso, a RegCreateKeyEx. Para isso, na Command Bar, basta digitar “BP RegCreateKeyExA” (sem aspas) e pressionar [ENTER]. Este “A” é porque a função trabalha com caractes em ASCII. Para caracteres UNICODE, usa-se “W”.

    6-ollybp.png.24a2133f42eaebeb93e6098a0666506f.png

    Agora é só rodar o programa no OllyDbg (F9) e esperar parar.

    A primeira parada (breakpoint) na função RegCreateKeyExA não é a que esperamos, pois antes de criar a nossa chave, o software precisa criar outras chaves (de sistema) que são essenciais ao seu funcionamento. Portanto, vamos apertando F9 (Run) até que a parada seja na hora da criação de nossa chave. Aqui precisei rodar (F9) mais cinco vezes. Quando for a correta, a stack (pilha de memória) que o OllyDbg mostra, ficará como na imagem abaixo:

    7-olly-stack.png.cfdbfb5e2b43ebbb5cbdec06827d523c.png

    Perceba que foi passado o valor hexa 80000002 como parâmetro hKey e isso resultou HKEY_LOCAL_MACHINE (o OllyDbg nos mostra).

    Os possíveis valores são:

    Chave                                            Valor (em hexa)
    HKEY_CLASSES_ROOT                 80000000
    HKEY_CURRENT_USER                80000001
    HKEY_LOCAL_MACHINE              80000002
    HKEY_USERS                                    80000003
    HKEY_PERFORMANCE_DATA   80000004
    HKEY_CURRENT_CONFIG          80000005
    HKEY_DYN_DATA                          80000006

    DICA: Se clicarmos com o botão direito no valor 80000002, na stack, e escolhermos a opção “Modify”, poderemos mudar este valor em tempo de execução. Mas é claro que a alteração não será permanente pois estaríamos alterando na memória. Para alterar permanentemente, precisamos saber em que parte do programa está este valor 80000002 (HKLM) e mudar para 80000001 (HKCU), mas em respeito ao desenvolvedor do software, não mostrarei publicamente como fazer isso.


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest

  • Similar Content

    • By Fernando Mercês
      Máquina virtual do Windows 10 para desenvolvedores, disponibilizada pela própria Microsoft com licença válida por 3 meses. Tem 20 GB o download mas ela já vem com o seguinte:
      Windows 10 Windows 10 SDK Visual Studio 2019 com UWP, .NET desktop e Azure workflows habilitados, além da extensão do Windows Template Studio Visual Studio Code Windows Subsystem for Linux com Ubuntu instalado Modo desenvolvedor habilitado Pode ser uma mão na roda pra criar uma VM de análise de malware. 
    • By Fernando Mercês
      Passei um tempo usando o Windows 10 depois de anos em Linux e macOS. Logo percebi dificuldades específicas com shell, instalação de programas, etc. Mas também percebi que é possível deixar o Windows muito próximo, no quesito de usabilidade, de um Linux com shell completo ou macOS. Neste artigo vou mostrar as ferramentas e configurações que mais me ajudaram. Boa leitura! 😉
       
      Gerenciador de pacotes
      Vou começar falando dele por um motivo muito simples: outros programas podem ser instalados com ele. Pois é, parece que a Microsoft aprendeu com as distros Linux e projetos como o Chocolatey, já que finalmente anunciou seu gerenciador de pacotes, o winget. Para instalar, baixe o arquivo com extensão .appxbundle na área de releases do repositório do projeto no Github. Após isso você pode abrir qualquer terminal (Command Prompt / cmd.exe ou Powershell) e digitar winget pra ver seu novo gerenciador de pacotes:

      Vamos buscar por exemplo os programas que que tenham a palavra "security" em seu nome ou descrição, para testar:

      Qualquer um desses programas pode ser instalado com um simples "winget install". Faça o teste com o Notepad++ por exemplo:
      winget install notepad++ Os comandos list (listar os programas instalados) e upgrade (atualizar os programas instalados) ainda são experimentais, mas você pode habilitá-los da seguinte maneira:
      Digite winget settings no terminal. Um arquivo settings.json se abrirá. Nele, adicione: "experimentalFeatures": { "list" : true, "upgrade" : true }, O meu aqui tá assim:

      A lista de programas disponíveis está aumentando cada vez mais, já que o winget é de código aberto. 🙂
       
      Terminal decente
      Mas na real, a primeira coisa de que senti falta foi um terminal decente. Graças a Deus encontrei o Windows Terminal. Para instalar com o winget (não se assuste - dá pra usar a tecla Tab para autocompletar):
      winget install Microsoft.WindowsTerminalPreview Aí olha só quem chega na festa:

      Este terminal concentra tudo em um: Prompt de Comando, Powershell e Linux (rodando via WSL), e é multi-abas. Claro que tá longe de um Terminator ou iTerm2, mas também tá longe de um cmd.exe né? Deus me livre e guarde! kkkk
      O novo Windows Terminal também tem uma configuração baseada em JSON e uns recursos interessantes como a divisão automática da janela do terminal. Ao apertar Alt+Shitft+D, olha o que acontece:

      terminal_split.mp4 A divisão da janela (split) funciona para janelas com qualquer shell.
       
      Hashes
      Eu sempre curti um software pra Windows chamado TotalHash, que adiciona os hashes de arquivos em suas propriedades, mas este infelizmente tem dois problemas: não é livre nem suporta plugins (logo, não consigo extender para suportar os hashes que preciso), é pago e não é scriptável. Resolvi isso com o cmdlet Get-FieHash do Powershell. Por padrão ele exibe o SHA-256 de um arquivo, mas há várias opções, como se pode observar na imagem:

       
      Sniffer
      Por essa nem o futuro esperava! O Windows 10 agora tem um sniffer nativo: o pktmon. Se liga:

      E sim, ele suporta filtros. Por exemplo, para logar somente pacotes IPv4 na porta 80/tcp:
      pktmon filter add HttpPuro -d ipv4 -t tcp -p 80 Você pode adicionar mais de um filtro se quiser e com qualquer nome (aqui chamei o meu de HttpPuro). Depois, é só iniciar a captura pedindo para logar os eventos:
      pktmon start --etw Para parar a captura, o comando é o stop:
      pktmon stop Por padrão o pktmon cria um arquivo chamado PktMon.etl num formato próprio. Este pode ser convertido para texto:
      pktmon format PktMon.etl -o log.txt Ou para o formato PCAP-NG, suportado pelo Wireshark e outros analisadores de pacotes:
      pktmon pcapng PktMon.etl -o capture.pcap Há várias outras opções que valem serem exploradas. É de fato um sniffer completo.
      Em breve vou falar mais sobre as seguintes ferramentas:
      PowerToys Sysinternals O novo diskusage.exe. WSL 2.0. Por hora é isso. Conforme eu encontre coisas novas, vou adicionando aqui. Se souber de alguma novidade ou algo que ache que precise ser citado aqui, por favor comente aí embaixo!
      Espero que seja útil para quem precisa usar o Windows por algum tempo, por qualquer motivo. 🙂
    • By Leandro Fróes
      Faz algum tempo que ando botando mais a mão na massa na parte prática da análise de malware e nos conceitos que a envolvem. Este fato somado com minha paixão por tomar notas nos meus estudos acaba resultando na criação de alguns relatórios. Com isto em mente, decidi colocar aqui a análise do último sample no qual trabalhei, de um ransomware chamado Nephilin.   Overview   O Nephilin é uma variante do Nefilim, um Ransomware que acabou ficando bem conhecido no mês de fevereiro/março devido ao fato de ser uma variante do conhecido Nemty, que costumava trabalhar com operações de RaaS (Ransomware as a Service - um modelo de negócio utilizado por criminosos que facilita muito a distribuição de Ransomwares. Basicamente o criador do malware recruta pessoas para usarem o Ransomware e recebe uma parte de seus lucros, facilitando assim a distribuição e a entrada de pessoas não experientes no crime). Por mais que as formas de operação sejam diferentes, há similaridades de código entre essas três famílias de malware.   Análise Estática   O sample analisado foi compilado para x86 e não possui nenhuma técnica que possa dificultar nossa análise como por exemplo packers/protectors e o uso de ASLR. No entanto, este binário é assinado com um certificado válido:     Olhando os imports podemos notar que a Import Directory Table possui apenas uma entrada, a da kernel32.dll, levantando a suspeita da utilização de runtime linking, ou seja, o loader não resolve o endereço das funções em tempo de carregamento, pois eles são resolvidos em tempo de execução:     Podemos suspeitar ainda mais pelo fato do nome algumas DLLs estarem na lista de strings do binário, assim como o nome de algumas funções que não são exportadas pela kernel32.dll:     Não vou me preocupar muito com esta parte estática da análise, tendo em vista que a ideia deste artigo é cair de cabeça em cada funcionalidade do malware, isto é, executá-lo e ir analisando seu comportamento.   Análise Dinâmica   A primeira função que o malware chama é a que vai criar todo o contexto de criptografia para gerar uma chave que será a chave utilizada para descriptografar a Ransom Note:       Dentro desta função existem várias funções que permitem trabalhar com criptografia no Windows. A primeira função criptográfica chamada é a CryptAcquireContext, responsável por criar um handle para um key container dentro de um Cryptographic Service Provider (CSP). Após pegar o handle, o tamanho de uma string é calculado para posteriormente se criar e retornar um Hash Object dentro do CSP criado anteriormente. Esta string tem 66 bytes de tamanho (0x42 em hexa) e é uma string lotada de "a".    A função CryptCreateHash cria o Hash Object especificando o tipo como SHA1 e, logo depois, a função CryptHashData tira o hash do que está dentro de um buffer de tamanho 66 bytes sendo passado como parâmetro:         Por fim, o SHA1 gerado é derivado, para a geração de uma chave RC4. Como podemos ver quase todas as funções estão sendo importadas dinamicamente. ?   O que acontece após a função que gera esta chave RC4 é a criação de um Mutex com o nome "sofos delaet sosos":     Em seguida, uma string em base64 aparece e é decodada com a função CryptStringToBinary, resultando em uma chave pública RSA:   "BgIAAACkAABSU0ExAAgAAAEAAQDNFw18bUF1x32DZaZt4gnQtAnv5XH60d9B6UgIbVfRdHPeyEljZLKlGBKFPTsh+8xsDHe/9vynuOlnuPt91grReMAwcTDVkxBh/PDkf3Jq0bnFgZAWbgMvGX6lApXTDcTArf4US63VI3z8YPyDNJwEvBEWI13ywob8ECLsrD/C6BPkYG0mBU1ccixzOgkgad0iDvwS/C8iyW1Mi0PCoBa+3TCTVwt0Zpy/HceV5U7SevG7RRN5HrErv54Ihg6kTPPhdxkYdO+CUND19aLqh8MAVLRuP5hR6b6r7cjBNAW2+USaaMyT/llNXdPdySbatLlH6Mau4z1eqzYc7hMB2f+6"       Há depois uma tentativa de pegar um handle para um CSP onde o key container tem o nome  "rsa session" e, sem sucesso, o binário cria um novo chamado "skr skr skr":     Agora a chave pública decodada anteriormente será importada para o contexto "skr skr skr":       Ações padrão da maioria dos ransomwares incluem desabilitar a checagem de erros durante o boot, desabilitar o modo de recuperação, deletar backups, etc. O Nephlin faz isso através de uma chamada à função ShellExecuteA() passando uma linha com o cmd.exe como parâmetro:   "C:\\asdfgsdgasd\\..\\Windows\\asdgagsahsfahfhasahfsd\\..\\System32\\cmd.exe" "/c bcdedit /set {default} bootstatuspolicy ignoreallfailures & bcdedit /set {default} recoveryenabled no & wbadmin delete catalog -quiet & wmic shadowcopy delete"     Aqui foi utilizada uma abordagem um tanto curiosa, tendo em vista que o malware considera diretórios que provavelmente não existirão no sistema de arquivos da vítima ("asdgagsahsfahfhasahfsd", por exemplo) e sobe um nível no filesystem utilizando ".." para acessar de fato o que importa, ou seja, o caminho real seria simplesmente "C:\Windows\System32\cmd.exe" ?   Neste momento acontece uma checagem em relação à como o malware foi executado na linha de comando. Se foi passado algum parâmetro, ele checa pra ver se é um arquivo ou diretório. Se for um arquivo, ele chama direto a função que encripta. Caso seja um diretório, ele chama a função que checa uma lista de exclusão dos tipos de arquivos que ele não quer encriptar e esta função chama então a função que encripta.   É interessante notar que com essas checagens o ransomware pode ser usado em diversos cenários e não simplesmente para encriptar o sistema completamente. Ex: para manualmente testar se ele está funcionando (antes da possível invasão), ser executado manualmente após a invasão visando diretórios/arquivos específicos, etc:     Se nenhum parâmetro for passado via linha de comando, o binário começa a mapear os drives que estão no sistema e pegar seus tipos, buscando especificamente por drives fixos, removíveis (pen drives, etc) e mapeamentos de rede:     Após pegar o drive o seu nome é concatenado com a string "NEPHILIN-DECRYPT.txt", à fim de criar a Ransom Note na raiz do Drive em questão:     Após a chamada à CreateFile, podemos ver a Ransom Note sendo criada, mas vazia por enquanto:     Antes do conteúdo ser de fato escrito no arquivo, ele precisa ser decodado, tendo em vista que é uma string em base64 (sim, outra string em base64):     Abaixo está o buffer que contém o conteúdo da Ransom Note em base64:     Após decodar o base64 o buffer aparenta estar encriptado, e de fato está:     A função CryptDecrypt utiliza a chave RC4 gerada anteriormente para decriptar o conteúdo do buffer em questão. Podemos ver o conteúdo em clear text após a execução da função:     Por fim podemos ver a função WriteFile, que irá escrever o conteúdo no arquivo "NEPHILIN-DECRYPT.txt" criado anteriormente:       Agora que a Ransom Note foi criada, o processo de criptografia começa. O meio utilizado é através da criação de outra thread, isto é, para cada drive encontrado, uma nova thread é criada. A função CreateThread recebe como parâmetro para indicar seu início o endereço de uma função, que por sua vez chama a função que checa a lista de exclusão e depois começa a criptografia. Além disso, o nome do drive escolhido no momento é passado como parâmetro para esta função.

      Esta lista de exclusão é basicamente um lista que contém nomes de arquivos, diretórios e extensões das quais o malware não irá encriptar. Para cada arquivo encontrado o malware irá comparar com as especificações desta lista e, caso não bata, a função de criptografia será chamada:           Criptografia   A criptografia pode começar de 3 formas diferentes, como mencionado anteriormente: passando um arquivo como parâmetro pela linha de comando, passando um diretório ou mapeando os drives e criando threads.   Um trecho da função que faz as devidas checagens pode ser observada abaixo:     Se o arquivo checado não estiver na lista de exclusão, a função de criptografia é chamada:     O processo de criptografia se inicia com a abertura do arquivo em questão e a obtenção do seu tamanho. Depois disso, há duas chamadas para a função SystemFunction036 para gerar números aleatórios. Basicamente esta função é um alias para a função RtlGenRandom, que recebe como parâmetro um buffer e o tamanho do número aleatório que você quer gerar. O tamanho escolhido são 16 bytes (0x10):       Tendo 2 buffers de 10 bytes de tamanho cada, com os devidos números aleatórios gerados anteriormente, há duas chamadas à CryptEncrypt, uma para cada buffer. Aqui a chave pública RSA é utilizada para encriptar o buffer em questão, resultando em outros dois buffers de 256 bytes cada.       O conjunto de funções a seguir faz a mesma operação, mas apontando para lugares diferentes. A função SetFilePointerEx é utilizada para apontar para o fim do arquivo (baseando-se no tamanho obtido anteriormente) e depois a função WriteFile é utilizada para escrever os 256 bytes encriptados lá. A próxima chamada à SetFilePointerEx agora aponta para o fim do arquivo + 256 bytes e então escreve o segundo buffer encriptado onde o ponteiro está apontando.       Neste momento as checagens de tamanho de arquivo começam, assim como as chamadas de função e loops que envolvem a criptografia.   A primeira checagem feita é se o arquivo é maior que 64MB e, caso seja, as funções que criptografam o arquivo começam a ser chamadas de 125KB em 125KB. Caso o arquivo seja menor há uma outra checagem para ver se ele é menor que 1.2MB e caso ele não seja as funções de criptografia rodam em cima de 600KB apenas e finalizam. Caso o arquivo seja menor que 1.2MB ele é encriptado "de uma vez" e depois finaliza.         Para cada arquivo é gerada uma chave randômica com a função SystemFunction036 e depois esta é encriptada com a chave RSA pública. Esta abordagem dificulta bastante a criação de um decryptor, tendo em vista que a chave será sempre aleatória. Por outro lado, se tivemos a chave RSA privada em mãos a aleatoriedade não teria efeito nenhum pois para cada arquivo teríamos a chave responsável pela sua criptografia.   Por fim a extensão ".NEPHILIN" é adicionada ao arquivo aberto:             Uma coisa importante a se notar é que se a criptografia foi executada para um arquivo ou diretório específico tanto a Ransom Note quanto o wallpaper do Ransomware não são criados. Podemos observar que as funções de mapeamento de drives (que contém a criação da Ransom Note) e criação do papel de parede são ignoradas devido ao salto incondicional JMP:     E por fim...   Considerando ainda que não foram especificados arquivos e diretórios, a função responsável por criar a imagem do papel de parede é chamada. Há várias funções aqui e estas utilizam funções gráficas do Windows para editar a imagem em questão:     Uma das funções chamadas nesta função responsável por criar a imagem é justamente a função de decoda o base64 da Ransom Note, pois o que é escrito no papel de parede é a mesma coisa da Ransom Note. Após várias funções gráficas para preparar a imagem o arquivo é finalmente criado em %TEMP%, com nome god.jpg e seu conteúdo é escrito no arquivo:           Após configurar a imagem como papel de parede, o malware chama sua última função, que é responsável por fechar todos os handles e contextos de criptografia ainda pendentes:     Depois disso, o processo simplesmente sai retornando 0.   Lista de exclusão:   NEPHILIN-DECRYPT.txt $RECYCLE.BIN NTDETECT.COM MSDOS.SYS IO.SYS boot.ini AUTOEXEC.BAT ntuser.dat desktop.ini CONFIG.SYS BOOTSECT.BAK program files program files (x86) windows ntldr RECYCLER bootmgr programdata appdata .dll .NEPHILIM .exe .log .cab .cmd .com .cpl .ini .url .ttf .mp3 .pif .mp4 .msi .lnk   Espero que o estudo desta análise seja proveitoso assim como foi para mim e qualquer dúvida/feedback estou à disposição!   Abraços!
    • By paulosgf
      Pessoal,
      recebi recentemente esta referência de técnicas anti-debug atualizadas por uma fonte confiável, que é a empresa Check Point.
      Não cheguei a olhar ainda, mas achei interessante de compartilhar logo com os colegas, por ser um tema de grande relevância na área de engenharia reversa.
      Abraços!
      https://research.checkpoint.com/2020/cpr-anti-debug-encyclopedia-the-check-point-anti-debug-techniques-repository/
       
×
×
  • Create New...