Class: Win32EventLog

Inherits:
Object
  • Object
show all
Defined in:
lib/metadata/util/win32/Win32EventLog.rb

Constant Summary collapse

SYSTEM_LOGS =

Standard file log names

%w(Application System Security)
BUFFER_READ_SIZE =

10 MB buffer

10485760
ELF_LOGFILE_HEADER =
BinaryStruct.new([
  'L',  :header_size,           # The size of the header structure. The size is always 0x30.
  'a4', :signature,             # The signature is always 0x654c664c, which is ASCII for eLfL.
  'L',  :majorVersion,          # The major version number of the event log. The major version number is always set to 1.
  'L',  :minorVersion,          # The minor version number of the event log. The minor version number is always set to 1.
  'L',  :start_offset,          # The offset to the oldest record in the event log.
  'L',  :end_offset,            # The offset to the ELF_EOF_RECORD in the event log.
  'L',  :current_record_number, # The number of the next record that will be added to the event log.
  'L',  :oldest_record_number,  # The number of the oldest record in the event log. For an empty file, the oldest record number is set to 0.
  'L',  :max_size,              # The maximum size, in bytes, of the event log. The maximum size is defined when the event log is created.
  # The event-logging service does not typically update this value, it relies on the registry configuration.
  # The reader of the event log can use normal file APIs to determine the size of the file.
  'L',  :flags,                 # See ELF_ below.
  'L',  :retention,             # The retention value of the file when it is created.
  # The event-logging service does not typically update this value, it relies on the registry configuration.
  # For more information about registry configuration values, see Eventlog Key.
  'L',  :end_header_size        # The ending size of the header structure. The size is always 0x30.
])
ELF_DIRTY =

Event Log header flags.

0x00000001
ELF_WRAPPED =

If set, don’t rely on other values in the header.

0x00000002
ELF_LOGFULL =

Indicates the log is wrapped.

0x00000004
ELF_LOGFILE_ARCHIVE_SET =

Set if log full (extended implications in EventLogFormat.txt).

0x00000008
EVENTLOGEOF =
BinaryStruct.new([
  'L',   :record_size_beginning, # The beginning size of the ELF_EOF_RECORD. The beginning size is always 0x28.
  'a16', :magic,                # Always \001\001\001\001\002\002\002\002\003\003\003\003\004\004\004\004
  'L',   :begin_record,         # The offset to the oldest record. If the event log is empty, this is set to the start of this structure.
  'L',   :end_record,           # The offset to the start of this structure.
  'L',   :current_record_number, # The record number of the next event that will be written to the event log.
  'L',   :oldest_record_number, # The record number of the oldest record in the event log. The record number will be 0 if the event log is empty.
  'L',   :record_size_end       # The ending size of the ELF_EOF_RECORD. The ending size is always 0x28.
])
EVENTRECORD =
BinaryStruct.new([
  'L',  :record_length,         # The size of this event record, in bytes. Note that this value is stored at both ends
  # of the entry to ease moving forward or backward through the log. The length includes
  # any pad bytes inserted at the end of the record for DWORD alignment.
  'a4', :magic,                 # A DWORD value that is always set to ELF_LOG_SIGNATURE (the value is 0x654c664c), which is ASCII for eLfL.
  'L',  :record_num,            # The number of the record.
  'L',  :generated,             # The time at which this entry was submitted. This time is measured in the number of seconds elapsed since 00:00:00 January 1, 1970, Universal Coordinated Time.
  'L',  :written,               # The time at which this entry was received by the service to be written to the log. This time is measured in the number of seconds elapsed since 00:00:00 January 1, 1970, Universal Coordinated Time.
  'L',  :event_id,              # The event identifier. The value is specific to the event source for the event, and is used with source name to locate a description string in the message file for the event source
  'S',  :level,                 # See EVENTLOG_ below.
  'S',  :num_strings,           # The number of strings present in the log (at the position indicated by StringOffset). These strings are merged into the message before it is displayed to the user.
  'S',  :category,              # The category for this event. The meaning of this value depends on the event source.
  'S',  :reserved_flags,        # Reserved.
  'L',  :closing_rec_num,       # Reserved.
  'L',  :string_offset,         # Offset from beginning of record to UTF-16 strings.
  'L',  :user_sid_length,       # The size of the UserSid member, in bytes. This value can be zero if no security identifier was provided.
  'L',  :user_sid_offset,       # Offset from beginning of record.
  'L',  :data_length,           # Length of parameter data (0 if none).
  'L',  :data_offset,           # The offset of the event-specific information within this event log record, in bytes.
  # This information could be something specific (a disk driver might log the number of retries, for example),
  # followed by binary information specific to the event being logged and to the source that generated the entry.
])
EVENTRECORDLENGTH =
BinaryStruct.new([
  'L',  :record_length,         # The size of this event record, in bytes. Note that this value is stored at both ends
])
EVENT_TYPES =

Event types.

{
  0x0000 => :info,   # EVENTLOG_SUCCESS
  0x0001 => :error,  # EVENTLOG_ERROR_TYPE
  0x0002 => :warn,   # EVENTLOG_WARNING_TYPE
  0x0004 => :info,   # EVENTLOG_INFORMATION_TYPE
  0x0008 => :info,   # VENTLOG_AUDIT_SUCCESS
  0x0010 => :error,  # EVENTLOG_AUDIT_FAILURE
}
MAGIC_HDR =

Magic numbers used by log record types.

"LfLe"
MAGIC_CSR =
"\x11\x11\x11\x11\x22\x22\x22\x22\x33\x33\x33\x33\x44\x44\x44\x44"
HKLM =

Registry constants.

0x80000002
KEY_READ =
0x00020019
REG_MULTI_SZ =
7
SIZE_BUF =

Key name buffer size.

256
INVALID_HANDLE_VALUE =

Misc Windows constants.

-1
ERROR_SUCCESS =
0
ERROR_NO_MORE_ITEMS =
259
FORMAT_TR =

Lookup object for translating the common %? sequences in the messages

Hash.new { |_h, k| k }.merge(
  '% '  => " ",
  '%b'  => " ",
  '%.'  => ".",
  '%!'  => "!",
  '%n'  => "\r\n",
  '%r'  => "\r",
  '%t'  => "\t",
  '%0'  => "",
  '!s!' => ""
)
NODE_REC_KEYS =

Keys that will be in the final node record

[:generated, :event_id, :level, :category, :computer_name, :source, :message]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vmMiqFs = nil) ⇒ Win32EventLog

Returns a new instance of Win32EventLog.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 148

def initialize(vmMiqFs = nil)
  # vmMiqFs is an MiqFS instance for the file system
  # of the guest vm if guest logs or nil if host logs.

  # If an MiqFS instance was not passed, then the OS has to be (or emulate) Win32.
  # If an MiqFS instance *was* passed, then if the guest OS is not Windows then getSystemRoot will throw.
  raise "#{self.class}::initialize: Filesystem is not MiqFS: cannot continue" if !vmMiqFs.class.to_s.include?('Miq')

  # Get a file system instance if we don't already have one.
  @fs = vmMiqFs
  @fs = File if @fs.nil?

  # Init times.
  @readTimes = {}

  @msgtbl_cache = {}

  # Get root, system message tables & init messagetable cache.
  if @fs == File
    @systemRoot = 'c:/windows/system32'
    @kernel32_fn = 'c:/windows/system32/kernel32.dll'
  else
    @systemRoot = Win32::SystemPath.systemRoot(@fs)
    @kernel32_fn = "#{Win32::SystemPath.system32Path(@fs, @systemRoot)}/kernel32.dll"
  end
end

Instance Attribute Details

#customFileNameObject (readonly)

Returns the value of attribute customFileName.



145
146
147
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 145

def customFileName
  @customFileName
end

#readTimesObject (readonly)

Returns the value of attribute readTimes.



146
147
148
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 146

def readTimes
  @readTimes
end

#xmlDocObject (readonly)

Returns the value of attribute xmlDoc.



145
146
147
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 145

def xmlDoc
  @xmlDoc
end

Instance Method Details

#readAllLogs(options) ⇒ Object



175
176
177
178
179
180
181
182
183
184
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 175

def readAllLogs(options)
  options = options.collect { |l| {:name => l, :filter => nil} } if options[0].kind_of?(String)

  options.each do |o|
    start = Time.now
    readLog(o[:name], o[:filter])
    @readTimes[o[:name]] = Time.now - start
  end
  @xmlDoc
end

#readLog(log, filter = nil) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/metadata/util/win32/Win32EventLog.rb', line 186

def readLog(log, filter = nil)
  filter ||= {}
  EventLogFilter.prepare_filter!(filter)

  # Get message source files.  (This also caches the event log registry entries.)
  sources = getEventSourceMessageFiles(log)
  @f = @buf = nil

  # Get event log file and validate it is a format we support
  event_file = mkLogPath(log)
  unless File.extname(event_file).downcase == ".evt"
    raise MiqException::NtEventLogFormat, "#{self.class}: Unsupported Win32 Eventlog format [#{File.extname(event_file)}] for event log [#{log}].  File:[#{event_file}]"
  end

  # Start an XML document
  recordsNode = mkXmlDoc(log, event_file)

  getFileObj(event_file) do |f, filename|
    st = Time.now
    $log.info "#{self.class}: Opening file for [#{log}]" if $log
    @f = f
    @offset = BUFFER_READ_SIZE * -1

    hdr = ELF_LOGFILE_HEADER.decode(read_buffer(0, ELF_LOGFILE_HEADER.size))
    hdr[:wrapped] = !(hdr[:flags] & ELF_WRAPPED).zero?
    @file_size = @fs == File ? File.size(filename) : @fs.fileSize(filename)

    $log.info "#{self.class}: Opened file for [#{log}] in [#{Time.now - st}] seconds.  Data Size:[#{@file_size}]  Wrapped:[#{hdr[:wrapped]}]" if $log

    parse_time = Time.now
    recs_found = 0
    recs_processed = 0
    @dup_check = {}

    each_record(hdr, log) do |rec|
      recs_processed += 1

      # Get log record components & filter on them
      rec[:generated] = Time.at(rec[:generated]).utc.iso8601
      break if EventLogFilter.filter_by_generated?(rec[:generated], filter)

      rec[:level] = EVENT_TYPES[rec[:level]]
      next if EventLogFilter.filter_by_level?(rec[:level], filter)

      getSourceName(rec)
      next if EventLogFilter.filter_by_source?(rec[:source], filter)

      getStrings(rec)
      getMessage(log, rec, sources)
      next if EventLogFilter.filter_by_message?(rec[:message], filter)

      # Get the rest of the record components
      getComputerName(rec)
      # There are not presently being used, so there is no need to collect them
      # rec[:written] = Time.at(rec[:written]).utc.iso8601
      # getSID(buf, pos, rec)
      # getData(buf, pos, rec)

      # Add the node to the XML
      recs_found += 1 if addNodeRec(recordsNode, rec)

      # Quit if we've found enough records
      break if EventLogFilter.filter_by_rec_count?(recs_found, filter)
    end

    # Clean up
    @dup_check = nil

    # Store based on log.
    recordsNode.add_attribute(:num_records, recs_found)

    $log.info "#{self.class}: Parsed [#{recs_processed}] [#{log}] records in [#{Time.now - parse_time}] seconds.  Collected [#{recs_found}] records.  Total time [#{Time.now - st}] seconds." if $log
  end
  @f = nil
end