这些天仔细分析了OSSEC日志收集(LogCollector)部分的细节。
OSSEC-LogCollector是创建一个无限循环,类似一个监听日志的守护进程,只要监听到日志有变动,就用SOCEKT发送消息给日志分析(analysisd)的守护进程,然后进行解码、匹配等操作。
下面先讲OSSEC是如何读取日志的。以读取WINDOWS XP下的事件日志为例。
读取事件日志主要是用到了Windows.h中的几个API:
OpenEventLog,CloseEventLog,ReadEventLog,GetLastError,等。
代码在最下面,我先说明一下每个函数的作用。
int StartEL(char *app, HIDSEL *el)
打开日志,返回该日志的数目。
char *GetELCategory(int categoryID)
根据类型ID获取日志类型。
char *GetEventDLL(char *evt_name, char *source, char *evt)
从注册表获取日志的名称。
char *GetELMsg(EVENTLOGRECORD *er, char *name, char * source, LPTSTR *elSStr)
获取该条日志的描述信息。
void ReadEL(HIDSEL *el, int printit)
读取事件日志。注,若只需要读取一次日志,printit设为1即可。OSSEC设置此参数的做法,我想是要忽略已有的日志,只对新产生的日志做处理。
#include "stdio.h" #include "windows.h" #define BUFFER_SIZE 2048*256 #define HIDS_MAXSTR 6144 #define HIDS_FLSIZE 256 #define HIDS_LOG_HEADER 256 /*Event logging local structure*/ typedef struct _HIDSEL { int timeOfLast; char *name; EVENTLOGRECORD *er; HANDLE h; DWORD record; }HIDSEL; /* *Starts the event logging for each el */ int StartEL(char *app, HIDSEL *el) { DWORD recordNum = 0; /*Opening the event log*/ el->h = OpenEventLog(NULL,app); if(!el->h) { printf("HIDS-LogCollecotor ERROR: Uable to open the event log : %s.", app); return (-1); } el->name = app; if(GetOldestEventLogRecord(el->h, &el->record) == 0) { /*Unable to read oldest event log record*/ printf("HIDS-LogCollecotor ERROR: Uable to query last event log from: %s.", app); CloseEventLog(el->h); el->h = NULL; return(-1); } if(GetNumberOfEventLogRecords(el->h, &recordNum) == 0) { printf("HIDS-LogCollecotor ERROR: Uable to get the number of event logs from: %s.", app); CloseEventLog(el->h); el->h = NULL; return(-1); } if(recordNum <= 0) { return 0; } return (int)recordNum; } /* * Returns a string related to the category id of the log. */ char *GetELCategory(int categoryID) { char *cat; switch(categoryID) { case EVENTLOG_ERROR_TYPE: cat = "ERROR"; break; case EVENTLOG_WARNING_TYPE: cat = "WARNING"; break; case EVENTLOG_INFORMATION_TYPE: cat = "INFORMATION"; break; case EVENTLOG_AUDIT_SUCCESS: cat = "AUDIT_SUCCESS"; break; case EVENTLOG_AUDIT_FAILURE: cat = "AUDIT_FAILURE"; break; default: cat = "Unknown"; break; } return cat; } /** * Returns the event. */ char *GetEventDLL(char *evt_name, char *source, char *evt) { //char *retStr; HKEY key; DWORD ret; char keyName[512]; keyName[511] = '\0'; _snprintf(keyName, 510, "System\\CurrentControlSet\\Services\\EventLog\\%s\\%s", evt_name, source); /* Checking if we have it in memory. */ //retStr = OSHash_Get(dll_hash, keyName + 42); //if(retStr) //{ // return(retStr); //} /* Opening registry */ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) { return(NULL); } ret = MAX_PATH -1; if (RegQueryValueEx(key, "EventMessageFile", NULL, NULL, (LPBYTE)evt, &ret) != ERROR_SUCCESS) { evt[0] = '\0'; RegCloseKey(key); return(NULL); } else { /* Adding to memory. */ char *skey; char *sval; skey = strdup(keyName + 42); sval = strdup(evt); if(skey && sval) { //OSHash_Add(dll_hash, skey, sval); } else { printf("Log Collector: ERROR: Not enough Memory. Exiting."); } } RegCloseKey(key); return(evt); } /** * Returns a descriptive message of the event. */ char *GetELMsg(EVENTLOGRECORD *er, char *name, char * source, LPTSTR *elSStr) { DWORD fmFlags = 0; char strTmp[257]; char event[MAX_PATH +1]; char *curr_str; char *next_str; LPSTR message = NULL; HMODULE hevt; /* Initializing variables */ event[MAX_PATH] = '\0'; strTmp[256] = '\0'; /* Flags for format event */ fmFlags |= FORMAT_MESSAGE_FROM_HMODULE; fmFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; fmFlags |= FORMAT_MESSAGE_ARGUMENT_ARRAY; /* Get the file name from the registry (stored on event) */ if(!(curr_str = GetEventDLL(name, source, event))) { return(NULL); } /* If our event has multiple libraries, try each one of them */ while((next_str = strchr(curr_str, ';'))) { *next_str = '\0'; ExpandEnvironmentStrings(curr_str, strTmp, 255); /* Reverting back old value. */ *next_str = ';'; /* Loading library. */ hevt = LoadLibraryEx(strTmp, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); if(hevt) { if(!FormatMessage(fmFlags, hevt, er->EventID, 0, (LPTSTR) &message, 0, elSStr)) { message = NULL; } FreeLibrary(hevt); /* If we have a message, we can return it */ if(message) return(message); } curr_str = next_str +1; } /* Getting last value. */ ExpandEnvironmentStrings(curr_str, strTmp, 255); hevt = LoadLibraryEx(strTmp, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); if(hevt) { int hr; if(!(hr = FormatMessage(fmFlags, hevt, er->EventID, 0, (LPTSTR) &message, 0, elSStr))) { message = NULL; } FreeLibrary(hevt); /* If we have a message, we can return it */ if(message) return(message); } return(NULL); } /* *Reads the event log. */ void ReadEL(HIDSEL *el, int printit) { DWORD _evtid = 65535; DWORD nstr; DWORD usrSize; DWORD domainSize; DWORD read, needed; int sizeLeft; int strSize; int id; char mbuffer[BUFFER_SIZE +1]; LPSTR sstr = NULL; char *strTmp = NULL; char *category; char *source; char *computerName; char *descriptiveMsg; char elUsr[HIDS_FLSIZE +1]; char elDomain[HIDS_FLSIZE +1]; char elStr[HIDS_MAXSTR +1]; char finalMsg[HIDS_MAXSTR +1]; LPSTR elSStr[HIDS_FLSIZE +1]; /* Er must point to the mbuffer */ el->er = (EVENTLOGRECORD *) &mbuffer; /* Zeroing the values */ elStr[HIDS_MAXSTR] = '\0'; elUsr[HIDS_FLSIZE] = '\0'; elDomain[HIDS_FLSIZE] = '\0'; finalMsg[HIDS_MAXSTR] = '\0'; elSStr[0] = NULL; elSStr[HIDS_FLSIZE] = NULL; /* Event log is not open */ if(!el->h) { return; } /* Reading the event log */ while(ReadEventLog(el->h, EVENTLOG_FORWARDS_READ|EVENTLOG_SEQUENTIAL_READ, 0, el->er, BUFFER_SIZE - 1, &read, &needed)) { if(!printit) { /* Setting er to the beginning of the buffer */ el->er = (EVENTLOGRECORD *)&mbuffer; continue; } while(read > 0) { /* We need to initialize every variable before the loop */ category = GetELCategory(el->er->EventType); source = (LPSTR) ((LPBYTE) el->er + sizeof(EVENTLOGRECORD)); computerName = source + strlen(source) + 1; descriptiveMsg = NULL; /* Getting event id. */ id = (int)el->er->EventID & _evtid; /* Initialing domain/user size */ usrSize = 255; domainSize = 255; elDomain[0] = '\0'; elUsr[0] = '\0'; /* We must have some description */ if(el->er->NumStrings) { sizeLeft = HIDS_MAXSTR - 1024; sstr = (LPSTR)((LPBYTE)el->er + el->er->StringOffset); elStr[0] = '\0'; for (nstr = 0;nstr < el->er->NumStrings;nstr++) { strSize = strlen(sstr); if(sizeLeft > 1) { strncat(elStr, sstr, sizeLeft); } strTmp = strchr(elStr, '\0'); if(strTmp) { *strTmp = ' '; strTmp++; *strTmp = '\0'; } else { printf("Log Collector: Invalid application string (size+)"); } sizeLeft-=strSize + 2; if(nstr <= 92) { elSStr[nstr] = (LPSTR)sstr; elSStr[nstr +1] = NULL; } sstr = strchr((LPSTR)sstr, '\0'); if(sstr) sstr++; else break; } /* Get a more descriptive message (if available) */ descriptiveMsg = GetELMsg(el->er, el->name, source, elSStr); if(descriptiveMsg != NULL) { /* Remove any \n or \r */ /* Replace tabs from the argument field to spaces. * So whenever we have option:\tvalue\t, it will * become option: value\t */ strTmp = descriptiveMsg; while(*strTmp != '\0') { if(*strTmp == '\n') *strTmp = ' '; else if(*strTmp == '\r') *strTmp = ' '; else if((*strTmp == ':') && (strTmp[1] == '\t')) { strTmp[1] = ' '; strTmp++; } strTmp++; } } } else { strncpy(elStr, "(no message)", 128); } /* Getting username */ if(el->er->UserSidLength) { SID_NAME_USE account_type; if(!LookupAccountSid(NULL, (SID *)((LPSTR)el->er + el->er->UserSidOffset), elUsr, &usrSize, elDomain, &domainSize, &account_type)) { strncpy(elUsr, "(no user)", 255); strncpy(elDomain, "no domain", 255); } } else { strncpy(elUsr, "(no user)", 255); strncpy(elDomain, "no domain", 255); } if(printit) { DWORD _evtid = 65535; int id = (int)el->er->EventID & _evtid; finalMsg[HIDS_MAXSTR - HIDS_LOG_HEADER] = '\0'; finalMsg[HIDS_MAXSTR - HIDS_LOG_HEADER -1] = '\0'; printf("WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s\n", el->name, category, id, source, elUsr, elDomain, computerName, descriptiveMsg != NULL?descriptiveMsg:elStr); /*send msg to server ................... ................... */ } if(descriptiveMsg != NULL) { LocalFree(descriptiveMsg); } /* Changing the point to the er */ read -= el->er->Length; el->er = (EVENTLOGRECORD *)((LPBYTE) el->er + el->er->Length); } /* Setting er to the beginning of the buffer */ el->er = (EVENTLOGRECORD *)&mbuffer; } id = GetLastError(); if(id == ERROR_HANDLE_EOF) { return; } /* Event log was cleared. */ else if(id == ERROR_EVENTLOG_FILE_CHANGED) { printf("Log Collector: WARN: Event log cleared: '%s'", el->name); /* Closing the event log and reopenning. */ CloseEventLog(el->h); el->h = NULL; /* Reopening. */ if(StartEL(el->name, el) < 0) { printf("Log Collector: ERROR: Unable to reopen event log '%s'", el->name); } } else { printf("Log Collector: WARN: Error reading event log: %d", id); } } int main() { HIDSEL hidsEL[3]; int a,b,c; a = StartEL("System",&hidsEL[0]); //b = StartEL("Security",&hidsEL[1]); //c = StartEL("Application",&hidsEL[2]); ReadEL(&hidsEL[0],1); //ReadEL(&hidsEL[1],1); //ReadEL(&hidsEL[2],1); system("pause"); return 0; }