.

.

#1 Malware Development: Detectando Sandboxes - Parte 1

Autor: @mrempy

      ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣶⠖⠀⠀⠲⣶⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
      ⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡿⠋⠀⠀⠀⠀⠀⠀⠙⢿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀
      ⠀⠀⠀⠀⠀⠀⢀⣾⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣷⡀⠀⠀⠀⠀⠀⠀
      ⠀⠀⠀⠀⠀⠀⣾⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣷⠀⠀⠀⠀⠀⠀
      ⠀⠀⠀⠀⠀⠀⣿⣿⣿⣇⣤⠶⠛⣛⣉⣙⡛⠛⢶⣄⣸⣿⣿⣿⠀⠀⠀⠀⠀⠀
      ⠀⠀⠀⠀⢀⣀⣿⣿⣿⡟⢁⣴⣿⣿⣿⣿⣿⣿⣦⡈⢿⣿⣿⣿⣀⡀⠀⠀⠀⠀
      ⠀⠀⢠⣴⣿⣿⣿⣿⡟⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡌⢿⣿⣿⣿⣿⣦⡄⠀⠀
      ⠀⣴⣿⣿⡿⠿⢛⣻⡇⢸⡟⠻⣿⣿⣿⣿⣿⡿⠟⢻⡇⣸⣛⡛⠿⣿⣿⣿⣦⠀
      ⢸⣿⡿⠋⠀⠀⢸⣿⣿⡜⢧⣄⣀⣉⡿⣿⣉⣀⣠⣼⢁⣿⣿⡇⠀⠀⠙⢿⣿⡆
      ⣿⣿⠁⠀⠀⠀⠈⣿⣿⡇⣿⡿⠛⣿⣵⣮⣿⡟⢻⡿⢨⣿⣿⠀⠀⠀⠀⠈⣿⣿
      ⢿⡟⠀⠀⠀⠀⠀⠘⣿⣷⣤⣄⡀⣿⣿⣿⣿⢁⣤⣶⣿⣿⠃⠀⠀⠀⠀⠀⣿⡟
      ⠘⠇⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⡇⢿⣿⣿⣿⢸⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠻⠃
      ⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⢩⣦⣘⡘⠋⣛⣸⡍⠁⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀
      ⠀⠀⠘⢿⣷⣤⣤⣄⣤⣤⣶⣿⣿⣿⡿⢿⣿⣿⣿⣷⣤⣤⣠⣤⣴⣾⡿⠁⠀⠀
      ⠀⠀⠀⠀⠉⠛⠿⠿⠿⡿⠿⠿⠛⠉⠀⠀⠉⠛⠿⠿⣿⠿⠿⠿⠛⠉⠀⠀⠀⠀



        ╔═══════════════[Sumário]═════════════╗
        ║                                     ║
        ║     1. Introdução                   ║
        ║     2. Escrevendo Códigos           ║
        ║       2.1 Anti-Debugging            ║
        ║       2.2 Anti-Dumping              ║
        ║       2.3 Processos Suspeitos       ║
        ║     3. Código Completo              ║
        ║                                     ║
        ╚═════════════════════════════════════╝
        

        Introdução

        Uma das coisas fundamentais em um bom malware é a capacidade de não se executar se estiver em um ambiente controlado, conhecido
        como sandboxes. Vários malware developers implementam em seus códigos técnicas anti-sandbox, para evitar com que o malware
        researcher não consiga depurar e analisar o malware.

        Nesse paper, irei apresentar 3 técnicas para implementar em seus códigos contra sandboxes. Vamos lá!


        Escrevendo Códigos

        Vamos escrever 3 funções, essas são:
            * DebuggerPresent() - Detecta se há depurador presente no processo do executável.
            * ErasePEHeader() - Técnica para evitar o dumping do executável em memória (Anti-Dumping).
            * SandboxSuspectProcess() - Irá detectar se há alguns processos suspeitos em sandboxes.


        Anti-Debugging

        Para essa função, vamos usar duas APIs do Windows, conhecidas com IsDebuggerPresent e CheckRemoteDebuggerPresent.
        O uso dessas APIs é bem simples, basta implementá-las dessa maneira:

        ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
        ║ // 1. Debugger Present                                                                                                   ║
        ║ BOOL DebuggerPresent() {                                                                                                 ║
        ║     if (IsDebuggerPresent()) {                                              // Check if there is a debugger present      ║
        ║         return 1;                                                                                                        ║
        ║     }                                                                                                                    ║
        ║                                                                                                                          ║
        ║     BOOL remoteDbgPresent;                                                                                               ║ 
        ║     CheckRemoteDebuggerPresent(GetCurrentProcess(), &remoteDbgPresent);     // Verify that it is being debugged remotely ║ 
        ║     if (remoteDbgPresent) {                                                                                              ║
        ║         return 1;                                                                                                        ║
        ║     }                                                                                                                    ║
        ║                                                                                                                          ║
        ║     return 0;                                                                                                            ║
        ║ }                                                                                                                        ║
        ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

        IsDebuggerPresent: essa API faz parte da biblioteca padrão do Windows (windows.h). O uso dela é bem simples, você só precisa
        adicioná-la junto com um "if" em uma função booleana para retorna true ou false (0 ou 1), como no código acima. Se houver um
        depurador anexado ao processo, ele vai retornar falso, caso ao contrário, irá retornar verdadeiro.

        CheckRemoteDebuggerPresent: essa é outra API que faz parte da mesma biblioteca da API anterior. Ela serve para detectar
        se um processo remoto está sendo depurado. Se houver um depurador, ele irá retornar falso, caso ao contrário retornará verdadeiro.
        A booleana "remoteDbgPresent" ficará responsável por esse estado.

        Podemos chamar nossa função dessa forma:

        ╔══════════════════════════════════════╗
        ║ if (DebuggerPresent() != 0) {        ║
        ║     puts("[-] Debugger is present"); ║
        ║ }                                    ║
        ╚══════════════════════════════════════╝

        OBS: Se você chamar sua função dentro de uma "int", não esqueça de adicionar "return 1;", ou se for uma "void", adicione
        "exit(0);".


        Anti-Dumping

        Anti-Dumping é uma técnica que não permite um usuário a dumpar a memória do processo em execução, limpando o cabeçalho do Portable
        Executable (PE Header). Usando essa técnica, você pode acabar dificultando o processo de engenharia reversa de seu malware, pois
        quando o malware researcher passar por sua função e tentar dumpar a memória do processo do executável, o resultado será inválido,
        e ele não poderá executar o EXE dumpado do processo.

        Vamos criar nossa função para esse trabalho:

        ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
        ║ // 2. Anti-Dumping                                                                                                                   ║
        ║ BOOL ErasePEHeader() {                                                                                                               ║
        ║     DWORD oldProtect = 0;                                                                                                            ║
        ║                                                                                                                                      ║
        ║     char* processBaseAddress = (char*)GetModuleHandle(NULL);                  // Get the base address of our running EXE             ║
        ║                                                                                                                                      ║ 
        ║     VirtualProtect(processBaseAddress, 4096, PAGE_READWRITE, &oldProtect);   // Releases access to read and write to the pe32 header ║
        ║                                                                                                                                      ║ 
        ║     RtlZeroMemory(processBaseAddress, 4096);                                 // Clean PE header                                      ║
        ║                                                                                                                                      ║ 
        ║     return 0;                                                                                                                        ║
        ║ }                                                                                                                                    ║
        ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

        DWORD oldProtect: adicionaremos uma variável do tipo DWORD para armazenar o velho protect do processo.

        char* processBaseAddress: essa linha de código irá retornar o handle do processo, ou seja, a base da memória do nosso executável, onde
        será armazenado na variável processBaseAddress.

        VirtualProtect: essa API será responsável por permitir a leitura e escrita a partir do endereço base do processo até os 4096 bytes.

        RtlZeroMemory: essa API irá ser usada para preencher os 4096 bytes do PE header com zeros.

        Você pode chamar essa função apenas escrevendo "ErasePEHeader();".


        Processos Suspeitos

        Nessa parte, vamos trabalhar com detecção de processos baseando-se nos nomes deles, por exemplo, o processo do wireshark, ollydbg, e outros.
        Para isso, precisamos criar uma lista de nomes de processos, vou disponibilizar essa lista e o código completo:

        ╔═════════════════════════════════════╗
        ║ const wchar_t* sandboxProcess[] = { ║
        ║     L"filemon.exe",                 ║
        ║     L"PETools.exe",                 ║
        ║     L"tcpview.exe",                 ║
        ║     L"idaq.exe",                    ║
        ║     L"regmon.exe",                  ║
        ║     L"dumpcap.exe",                 ║
        ║     L"LordPE.exe",                  ║
        ║     L"idaq64.exe",                  ║
        ║     L"httpdebugger.exe",            ║
        ║     L"tcpview.exe",                 ║
        ║     L"proc_analyzer.exe",           ║
        ║     L"idaq.exe",                    ║
        ║     L"procmon.exe",                 ║
        ║     L"ResourceHacker.exe",          ║
        ║     L"joeboxcontrol.exe",           ║
        ║     L"joeboxserver.exe",            ║
        ║     L"ollydbg.exe",                 ║
        ║     L"HookExplorer.exe",            ║
        ║     L"SysInspector.exe",            ║
        ║     L"autorunsc.exe",               ║
        ║     L"autoruns.exe",                ║
        ║     L"Windbg.exe",                  ║
        ║     L"Fiddler.exe",                 ║
        ║     L"joeboxserver.exe",            ║
        ║     L"ImmunityDebugger.exe",        ║
        ║     L"filemon.exe",                 ║
        ║     L"sniff_hit.exe",               ║
        ║     L"Wireshark.exe",               ║
        ║     L"x64dbg.exe"                   ║
        ║ };                                  ║
        ╚═════════════════════════════════════╝

        Agora precisamos criar uma função para obter o process id desses processos:

        ╔════════════════════════════════════════════════════════════════════╗
        ║ DWORD GetPID(LPCWSTR exeName) {                                    ║
        ║     DWORD processId = 0;                                           ║
        ║     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); ║
        ║                                                                    ║
        ║     if (snap != INVALID_HANDLE_VALUE)                              ║
        ║     {                                                              ║
        ║         PROCESSENTRY32 pe32;                                       ║
        ║         pe32.dwSize = sizeof(pe32);                                ║
        ║                                                                    ║
        ║         if (Process32First(snap, &pe32))                           ║
        ║         {                                                          ║
        ║             if (!pe32.th32ProcessID)                               ║
        ║                Process32Next(snap, &pe32);                         ║
        ║            do                                                      ║
        ║             {                                                      ║
        ║                 if (!lstrcmpiW((LPCWSTR)pe32.szExeFile, exeName))  ║
        ║                 {                                                  ║
        ║                     processId = pe32.th32ProcessID;                ║
        ║                     break;                                         ║
        ║                 }                                                  ║
        ║             } while (Process32Next(snap, &pe32));                  ║
        ║         }                                                          ║
        ║     }                                                              ║
        ║     CloseHandle(snap);                                             ║
        ║                                                                    ║
        ║     return processId;                                              ║
        ║ }                                                                  ║
        ╚════════════════════════════════════════════════════════════════════╝

        Essa função irá retornar o ID do processo após encontrá-lo, se não encontrar, ele irá retornar 0.

        Agora vamos criar a função que irá passar pela a lista de processos e vê se existe ou não:

        ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
        ║ // 3. Sandbox Process                                                                                                                                    ║
        ║ BOOL SandboxSuspiciousProcess() {                                                                                                                        ║
        ║     for (int i = 0; i < sizeof(sandboxProcess) / sizeof(sandboxProcess[0]); i++) {          // Cycle through the entire list of common sandbox processes ║
        ║         DWORD pid = GetPID(sandboxProcess[i]);                                              // Get the PID of the process by name                        ║
        ║                                                                                                                                                          ║
        ║         if (pid != 0) {                                                                     // Checks if the returned PID is different from 0            ║
        ║             return 1;                                                                       // If it is different from 0, it means there is a            ║
        ║         }                                                                                   // suspicious process                                        ║
        ║     }                                                                                                                                                    ║
        ║                                                                                                                                                          ║
        ║     return 0;                                                                                                                                            ║
        ║ }                                                                                                                                                        ║
        ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

        Em resumo, essa função irá percorrer a lista de processos suspeitos e irá obter o PID através da função "GetPID" e irá armazenar o resultado
        na variável "pid". Se o resultado da variável "pid" não for igual a 0, isso significa que há um dos processos da lista em execução, então a função irá
        retornar falso. Se todos os resultados de PIDs forem 0, ele irá retornar 0.

        Implemente sua função dessa forma:

        ╔══════════════════════════════════════════════════════════════╗
        ║ if (SandboxSuspiciousProcess() != 0) {                       ║
        ║     puts("[-] There are sandbox processes on this machine"); ║
        ║ }                                                            ║
        ║ else {                                                       ║
        ║     puts("[+] There are no suspicious processes");           ║
        ║ }                                                            ║
        ╚══════════════════════════════════════════════════════════════╝

        Perfeito! Está tudo pronto para ser compilado e testado.
        
        Código Completo

        O código completo pode ser encontrado em:

        https://github.com/AmoloHT/PapersArchives/tree/main/1-malware-development-detecting-sandboxes-part-1

        O código está finalizado, agora é hora de compilar e executar :)

                             _--_
                            /   -)
                        ___/___|___
           ____-----=~~///|     ||||~~~==-----_____
         //~////////////~/|     |//|||||\\\\\\\\\\\\\
       ////////////////////|   |///////|\\\\\\\\\\\\\\\
      /////~~~~~~~~~~~~~~~\ |.||/~~~~~~~~~~~~~~~~~`\\\\\
     //~                  /\\|\\                      ~\\
                         ///W^\W\
                        ////|||\\\
                        ~~~~~~~~~~     

                      Amolo Hunters!