/*++ Copyright (c) 1990 Microsoft Corporation Module Name: elfexts.c Abstract: This function contains the eventlog ntsd debugger extensions Author: Dan Hinsley (DanHi) 22-May-1993 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define DbgPrint(_x_) (lpOutputRoutine) _x_ #define DbgPrint(_x_) #define MAX_NAME 256 #define printf (lpOutputRoutine) #define GET_DATA(DebugeeAddr, LocalAddr, Length) \ Status = ReadProcessMemory( \ GlobalhCurrentProcess, \ (LPVOID)DebugeeAddr, \ LocalAddr, \ Length, \ NULL \ ); PNTSD_OUTPUT_ROUTINE lpOutputRoutine; PNTSD_GET_EXPRESSION lpGetExpressionRoutine; PNTSD_CHECK_CONTROL_C lpCheckControlCRoutine; HANDLE GlobalhCurrentProcess; BOOL Status; // // Initialize the global function pointers // VOID InitFunctionPointers( HANDLE hCurrentProcess, PNTSD_EXTENSION_APIS lpExtensionApis ) { // // Load these to speed access if we haven't already // if (!lpOutputRoutine) { lpOutputRoutine = lpExtensionApis->lpOutputRoutine; lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine; lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine; } // // Stick this in a global // GlobalhCurrentProcess = hCurrentProcess; } LPWSTR GetUnicodeString( PUNICODE_STRING pUnicodeString ) { DWORD Pointer; UNICODE_STRING UnicodeString; GET_DATA(pUnicodeString, &UnicodeString, sizeof(UNICODE_STRING)) Pointer = (DWORD) UnicodeString.Buffer; UnicodeString.Buffer = (LPWSTR) LocalAlloc(LMEM_ZEROINIT, UnicodeString.Length + sizeof(WCHAR)); GET_DATA(Pointer, UnicodeString.Buffer, UnicodeString.Length) return(UnicodeString.Buffer); } DWORD GetLogFileAddress( LPSTR LogFileName, PLOGFILE LogFile ) { ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; DWORD Pointer; DWORD LogFileAnchor; LPWSTR ModuleName; // // Convert the string to UNICODE // RtlInitAnsiString(&AnsiString, LogFileName); RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); // // Walk the logfile list looking for a match // LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead"); GET_DATA(LogFileAnchor, &Pointer, sizeof(DWORD)) while (Pointer != LogFileAnchor) { GET_DATA(Pointer, LogFile, sizeof(LOGFILE)) ModuleName = GetUnicodeString(LogFile->LogModuleName); if (!wcsicmp(ModuleName, UnicodeString.Buffer)) { break; } LocalFree(ModuleName); Pointer = (DWORD) LogFile->FileList.Flink; } RtlFreeUnicodeString(&UnicodeString); if (Pointer == LogFileAnchor) { return(0); } else { LocalFree(ModuleName); return(Pointer); } } // // Dump an individual record // DWORD DumpRecord( DWORD Record, DWORD RecordNumber, DWORD StartOfFile, DWORD EndOfFile ) { DWORD BufferLen; PCHAR TimeBuffer; PEVENTLOGRECORD EventLogRecord; LPWSTR Module; LPWSTR Computer; DWORD FirstPiece = 0; GET_DATA(Record, &BufferLen, sizeof(DWORD)) // // See if it's a ELF_SKIP_DWORD, and if it is, return the top of the // file // if (BufferLen == ELF_SKIP_DWORD) { return(StartOfFile + sizeof(ELF_LOGFILE_HEADER)); } // // See if it's the EOF record // if (BufferLen == ELFEOFRECORDSIZE) { return(0); } BufferLen += sizeof(DWORD); // get room for length of next record EventLogRecord = (PEVENTLOGRECORD) LocalAlloc(LMEM_ZEROINIT, BufferLen); // // If the record wraps, grab it piecemeal // if (EndOfFile && BufferLen + Record > EndOfFile) { FirstPiece = EndOfFile - Record; GET_DATA(Record, EventLogRecord, FirstPiece); GET_DATA((StartOfFile + sizeof(ELF_LOGFILE_HEADER)), ((PBYTE) EventLogRecord + FirstPiece), BufferLen - FirstPiece) } else { GET_DATA(Record, EventLogRecord, BufferLen) } // // If it's greater than the starting record, print it out // if (EventLogRecord->RecordNumber >= RecordNumber) { printf("\nRecord %d is %d [0x%X] bytes long starting at 0x%X\n", EventLogRecord->RecordNumber, EventLogRecord->Length, EventLogRecord->Length, Record); Module = (LPWSTR)(EventLogRecord+1); Computer = (LPWSTR)((PBYTE) Module + ((wcslen(Module) + 1) * sizeof(WCHAR))); printf("\tGenerated by %ws from system %ws\n", Module, Computer); TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeGenerated)); if (TimeBuffer) { printf("\tGenerated at %s", TimeBuffer); } else { printf("\tGenerated time field is blank\n"); } TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeWritten)); if (TimeBuffer) { printf("\tWritten at %s", TimeBuffer); } else { printf("\tTime written field is blank\n"); } printf("\tEvent Id = %d\n", EventLogRecord->EventID); printf("\tEventType = "); switch (EventLogRecord->EventType) { case EVENTLOG_SUCCESS: printf("Success\n"); break; case EVENTLOG_ERROR_TYPE: printf("Error\n"); break; case EVENTLOG_WARNING_TYPE: printf("Warning\n"); break; case EVENTLOG_INFORMATION_TYPE: printf("Information\n"); break; case EVENTLOG_AUDIT_SUCCESS: printf("Audit Success\n"); break; case EVENTLOG_AUDIT_FAILURE: printf("Audit Failure\n"); break; default: printf("Invalid value 0x%X\n", EventLogRecord->EventType); } printf("\t%d strings at offset 0x%X\n", EventLogRecord->NumStrings, EventLogRecord->StringOffset); printf("\t%d bytes of data at offset 0x%X\n", EventLogRecord->DataLength, EventLogRecord->DataOffset); } if (FirstPiece) { Record = StartOfFile + sizeof(ELF_LOGFILE_HEADER) + BufferLen - FirstPiece; } else { Record += EventLogRecord->Length; } LocalFree(EventLogRecord); return(Record); } // // Dump a record, or all records, or n records // VOID record( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { DWORD Pointer; LOGFILE LogFile; DWORD StartOfFile; DWORD EndOfFile = 0; DWORD RecordNumber = 0; InitFunctionPointers(hCurrentProcess, lpExtensionApis); // // Evaluate the argument string to get the address of // the record to dump. // if (lpArgumentString && *lpArgumentString) { if (*lpArgumentString == '.') { if (GetLogFileAddress(lpArgumentString+1, &LogFile) == 0) { printf("Logfile %s not found\n", lpArgumentString+1); return; } Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord; } else if (*lpArgumentString == '#') { RecordNumber = atoi(lpArgumentString + 1); printf("Dumping records starting at record #%d\n", RecordNumber); lpArgumentString = NULL; } else if (*lpArgumentString) { Pointer = (lpGetExpressionRoutine)(lpArgumentString); } else { printf("Invalid lead character 0x%02X\n", *lpArgumentString); return; } } if (!lpArgumentString || *lpArgumentString) { if (GetLogFileAddress("system", &LogFile) == 0) { printf("System Logfile not found\n"); return; } Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord; } StartOfFile = (DWORD) LogFile.BaseAddress; EndOfFile = (DWORD) LogFile.BaseAddress + LogFile.ActualMaxFileSize; // // Dump records starting wherever they told us to // while (Pointer < EndOfFile && Pointer && !(lpCheckControlCRoutine)()) { Pointer = DumpRecord(Pointer, RecordNumber, StartOfFile, EndOfFile); } return; } // // Dump a single LogModule structure if it matches MatchName (NULL matches // all) // PLIST_ENTRY DumpLogModule( HANDLE hCurrentProcess, DWORD pLogModule, LPWSTR MatchName ) { LOGMODULE LogModule; WCHAR ModuleName[MAX_NAME / sizeof(WCHAR)]; GET_DATA(pLogModule, &LogModule, sizeof(LogModule)) GET_DATA(LogModule.ModuleName, &ModuleName, MAX_NAME) if (!MatchName || !wcsicmp(MatchName, ModuleName)) { printf("\tModule Name %ws\n", ModuleName); printf("\tModule Atom 0x%X\n", LogModule.ModuleAtom); printf("\tPointer to LogFile 0x%X\n", LogModule.LogFile); } return (LogModule.ModuleList.Flink); } // // Dump selected, or all, LogModule structures // VOID logmodule( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { DWORD pLogModule; DWORD LogModuleAnchor; LPWSTR wArgumentString = NULL; ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; InitFunctionPointers(hCurrentProcess, lpExtensionApis); UnicodeString.Buffer = NULL; // // Evaluate the argument string to get the address of // the logmodule to dump. If no parm, dump them all. // if (lpArgumentString && *lpArgumentString == '.') { lpArgumentString++; RtlInitAnsiString(&AnsiString, lpArgumentString); RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); } else if (lpArgumentString && *lpArgumentString) { pLogModule = (lpGetExpressionRoutine)(lpArgumentString); DumpLogModule(hCurrentProcess, pLogModule, NULL); return; } LogModuleAnchor = (lpGetExpressionRoutine)("LogModuleHead"); GET_DATA(LogModuleAnchor, &pLogModule, sizeof(DWORD)) while (pLogModule != LogModuleAnchor && !(lpCheckControlCRoutine)()) { pLogModule = (DWORD) DumpLogModule(hCurrentProcess, pLogModule, UnicodeString.Buffer); if (!UnicodeString.Buffer) { printf("\n"); } } if (UnicodeString.Buffer) { RtlFreeUnicodeString(&UnicodeString); } return; } // // Dump a single LogFile structure if it matches MatchName (NULL matches // all) // PLIST_ENTRY DumpLogFile( HANDLE hCurrentProcess, DWORD pLogFile, LPWSTR MatchName ) { LOGFILE LogFile; LPWSTR UnicodeName; // // Get the fixed part of the structure // GET_DATA(pLogFile, &LogFile, sizeof(LogFile)) // // Get the Default module name // UnicodeName = GetUnicodeString(LogFile.LogModuleName); // // See if we're just looking for a particular one. If we are and // this isn't it, bail out. // if (MatchName && wcsicmp(MatchName, UnicodeName)) { LocalFree(UnicodeName); return (LogFile.FileList.Flink); } // // Otherwise print it out // printf("%ws", UnicodeName); LocalFree(UnicodeName); // // Now the file name of this logfile // UnicodeName = GetUnicodeString(LogFile.LogFileName); printf(" : %ws\n", UnicodeName); LocalFree(UnicodeName); if (LogFile.Notifiees.Flink == LogFile.Notifiees.Blink) { printf("\tNo active ChangeNotifies on this log\n"); } else { printf("\tActive Change Notify! Dump of this list not implemented\n"); } printf("\tReference Count: %d\n\tFlags: ", LogFile.RefCount); if (LogFile.Flags == 0) { printf("No flags set "); } else { if (LogFile.Flags & ELF_LOGFILE_HEADER_DIRTY) { printf("Dirty "); } if (LogFile.Flags & ELF_LOGFILE_HEADER_WRAP) { printf("Wrapped "); } if (LogFile.Flags & ELF_LOGFILE_LOGFULL_WRITTEN) { printf("Logfull Written "); } } printf("\n"); printf("\tMax Files Sizes [Cfg:Curr:Next] 0x%X : 0x%X : 0x%X\n", LogFile.ConfigMaxFileSize, LogFile.ActualMaxFileSize, LogFile.NextClearMaxFileSize); printf("\tRecord Numbers [Oldest:Curr] %d : %d\n", LogFile.OldestRecordNumber, LogFile.CurrentRecordNumber); printf("\tRetention period in days: %d\n", LogFile.Retention / 86400); printf("\tBase Address: 0x%X\n", LogFile.BaseAddress); printf("\tView size: 0x%X\n", LogFile.ViewSize); printf("\tOffset of beginning record: 0x%X\n", LogFile.BeginRecord); printf("\tOffset of ending record: 0x%X\n", LogFile.EndRecord); return (LogFile.FileList.Flink); } // // Dump selected, or all, LogFile structures // VOID logfile( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { DWORD pLogFile; DWORD LogFileAnchor; LPWSTR wArgumentString = NULL; ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; BOOL AllocateString = FALSE; InitFunctionPointers(hCurrentProcess, lpExtensionApis); UnicodeString.Buffer = NULL; // // Evaluate the argument string to get the address of // the logfile to dump. If no parm, dump them all. // if (lpArgumentString && *lpArgumentString) { if(*lpArgumentString == '.') { lpArgumentString++; RtlInitAnsiString(&AnsiString, lpArgumentString); RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); } else { pLogFile = (lpGetExpressionRoutine)(lpArgumentString); DumpLogFile(hCurrentProcess, pLogFile, NULL); return; } } LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead"); GET_DATA(LogFileAnchor, &pLogFile, sizeof(DWORD)) while (pLogFile != LogFileAnchor) { pLogFile = (DWORD) DumpLogFile(hCurrentProcess, pLogFile, UnicodeString.Buffer); if (!UnicodeString.Buffer) { printf("\n"); } } if (UnicodeString.Buffer) { RtlFreeUnicodeString(&UnicodeString); } return; } // // Dump a request packet structure // VOID request( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { ELF_REQUEST_RECORD Request; DWORD Pointer; DWORD RecordSize; WRITE_PKT WritePkt; READ_PKT ReadPkt; CLEAR_PKT ClearPkt; BACKUP_PKT BackupPkt; LPWSTR FileName; CHAR Address[18]; InitFunctionPointers(hCurrentProcess, lpExtensionApis); // // Evaluate the argument string to get the address of // the request packet to dump. // if (lpArgumentString && *lpArgumentString) { Pointer = (lpGetExpressionRoutine)(lpArgumentString); } else { printf("Must supply a request packet address\n"); return; } GET_DATA(Pointer, &Request, sizeof(ELF_REQUEST_RECORD)) switch (Request.Command ) { case ELF_COMMAND_READ: printf("\nRead packet\n"); GET_DATA(Request.Pkt.ReadPkt, &ReadPkt, sizeof(READ_PKT)) printf("\tLast Seek Position = %d\n", ReadPkt.LastSeekPos); printf("\tLast Seek Record = %d\n", ReadPkt.LastSeekRecord); printf("\tStart at record number %d\n", ReadPkt.RecordNumber); printf("\tRead %d bytes into buffer at 0x%X\n", ReadPkt.BufferSize, ReadPkt.Buffer); if (ReadPkt.Flags & ELF_IREAD_UNICODE) { printf("\tReturn in ANSI\n"); } else { printf("\tReturn in UNICODE\n"); } printf("\tRead flags: "); if (ReadPkt.ReadFlags & EVENTLOG_SEQUENTIAL_READ) { printf("Sequential "); } if (ReadPkt.ReadFlags & EVENTLOG_SEEK_READ) { printf("Seek "); } if (ReadPkt.ReadFlags & EVENTLOG_FORWARDS_READ) { printf("Forward "); } if (ReadPkt.ReadFlags & EVENTLOG_BACKWARDS_READ) { printf("Backwards "); } printf("\n"); break; case ELF_COMMAND_WRITE: printf("\nWrite packet\n"); if (Request.Flags == ELF_FORCE_OVERWRITE) { printf("with ELF_FORCE_OVERWRITE enabled\n"); } else { printf("\n"); } GET_DATA(Request.Pkt.WritePkt, &WritePkt, sizeof(WRITE_PKT)) RecordSize = (WritePkt.Datasize); DumpRecord((DWORD)WritePkt.Buffer, 0, 0, 0); break; case ELF_COMMAND_CLEAR: printf("\nClear packet\n"); GET_DATA(Request.Pkt.ClearPkt, &ClearPkt, sizeof(CLEAR_PKT)) FileName = GetUnicodeString(ClearPkt.BackupFileName); printf("Backup filename = %ws\n", FileName); LocalFree(FileName); break; case ELF_COMMAND_BACKUP: printf("\nBackup packet\n"); GET_DATA(Request.Pkt.BackupPkt, &BackupPkt, sizeof(BACKUP_PKT)) FileName = GetUnicodeString(BackupPkt.BackupFileName); printf("Backup filename = %ws\n", FileName); LocalFree(FileName); break; case ELF_COMMAND_WRITE_QUEUED: printf("\nQueued Write packet\n"); if (Request.Flags == ELF_FORCE_OVERWRITE) { printf("with ELF_FORCE_OVERWRITE enabled\n"); } else { printf("\n"); } printf("NtStatus = 0x%X\n", Request.Status); break; default: printf("\nInvalid packet\n"); } printf("\nLogFile for this packet:\n\n"); itoa((DWORD) Request.LogFile, Address, 16); logfile(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, Address); printf("\nLogModule for this packet:\n\n"); itoa((DWORD)Request.Module, Address, 16); logmodule(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, Address); return; } // // Online help // VOID help( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { InitFunctionPointers(hCurrentProcess, lpExtensionApis); printf("\nEventlog NTSD Extensions\n"); if (!lpArgumentString || *lpArgumentString == '\0' || *lpArgumentString == '\n' || *lpArgumentString == '\r') { printf("\tlogmodule - dump a logmodule structure\n"); printf("\tlogfile - dump a logfile structure\n"); printf("\trequest - dump a request record\n"); printf("\trecord - dump a eventlog record\n"); printf("\n\tEnter help for detailed help on a command\n"); } else { if (!stricmp(lpArgumentString, "logmodule")) { printf("\tlogmodule , where can be one of:\n"); printf("\t\tno argument - dump all logmodule structures\n"); printf("\t\taddress - dump the logmodule at specified address\n"); printf("\t\t.string - dump the logmodule with name string\n"); } else if (!stricmp(lpArgumentString, "logfile")) { printf("\tlogfile , where can be one of:\n"); printf("\t\tno argument - dump all logfile structures\n"); printf("\t\taddress - dump the logfile at specified address\n"); printf("\t\t.string - dump the logfile with name string\n"); } else if (!stricmp(lpArgumentString, "record")) { printf("\trecord , where can be one of:\n"); printf("\t\tno argument - dump all records in system log\n"); printf("\t\taddress - dump records starting at specified address\n"); printf("\t\t.string - dump all records in the log\n"); printf("\t\t# - dumps records starting at nnn in system log\n"); printf("\t\t# .string - dumps records starting at nnn in log\n"); } else if (!stricmp(lpArgumentString, "request")) { printf("\trequest - dump the request record at specified address\n"); } else { printf("\tInvalid command [%s]\n", lpArgumentString); } } }