From 282c9e9f2c156df9cf038095ecfadffcf5bb9079 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 27 May 2014 09:08:15 +0200 Subject: [PATCH 01/18] Fix Memory access offset=0 size=8 is out of bounds --- lib/win32/eventlog.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 003384e..97375ef 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -224,7 +224,7 @@ def self.add_event_source(args) raise SystemCallError.new('RegCreateKeyEx', rv) end - hkey = hkey.read_pointer.to_i + hkey = hkey.address data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt" begin @@ -266,7 +266,7 @@ def self.add_event_source(args) raise SystemCallError.new('RegCreateKeyEx', rv) end - hkey = hkey.read_pointer.to_i + hkey = hkey.address if hash['category_count'] data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['category_count']) @@ -858,7 +858,7 @@ def get_description(buf, event_source, lkey) message_exe = nil if RegOpenKeyEx(lkey, key, 0, KEY_READ, hkey) == 0 - hkey = hkey.read_pointer.to_i + hkey = hkey.address value = 'providerGuid' guid = FFI::MemoryPointer.new(:char, MAX_SIZE) @@ -872,7 +872,7 @@ def get_description(buf, event_source, lkey) key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0 - hkey2 = hkey2.read_pointer.to_i + hkey2 = hkey2.address value = 'ParameterMessageFile' file = FFI::MemoryPointer.new(:char, MAX_SIZE) From 3b12ac635660b678a30c673245a0b4f9fc2aeeed Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 27 May 2014 09:09:41 +0200 Subject: [PATCH 02/18] read_last_event to public Very useful to get the last event log. Needed in new input logstash plugin. --- lib/win32/eventlog.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 97375ef..c2a17a4 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -727,11 +727,12 @@ def report_event(args) alias :write :report_event - private - - # A private method that reads the last event log record. + # A method that reads the last event log record. # - def read_last_event(handle=@handle, source=@source, server=@server) + def read_last_event() + handle=@handle + source=@source + server=@server buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) read = FFI::MemoryPointer.new(:ulong) needed = FFI::MemoryPointer.new(:ulong) From c6291a5489d105746ccb074b670adb04505461b7 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 27 May 2014 09:20:24 +0200 Subject: [PATCH 03/18] Exclude data read from get_description, remove unnecessary variable copy --- lib/win32/eventlog.rb | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index c2a17a4..6670b5f 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -564,23 +564,16 @@ def read(flags = nil, offset = 0) struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - event_source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - computer = buf.read_bytes(buf.size)[56 + event_source.length + 1..-1][/^[^\0]*/] - user = get_user(record) - - strings, desc = get_description(buf, event_source, lkey) - - struct.source = event_source - struct.computer = computer + struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] + struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) struct.event_id = record[:EventID] & 0x0000FFFF struct.event_type = get_event_type(record[:EventType]) - struct.user = user + struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts = strings - struct.description = desc + struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) struct.freeze # This is read-only information @@ -760,26 +753,19 @@ def read_last_event() lkey = hkey.read_pointer.to_i end + struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - event_source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - computer = buf.read_bytes(buf.size)[56 + event_source.length + 1..-1][/^[^\0]*/] - event_type = get_event_type(record[:EventType]) - user = get_user(record) - strings, desc = get_description(buf, event_source, lkey) - - struct = EventLogStruct.new - struct.source = event_source - struct.computer = computer + struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] + struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) struct.event_id = record[:EventID] & 0x0000FFFF - struct.event_type = event_type - struct.user = user + struct.event_type = get_event_type(record[:EventType]) + struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts = strings - struct.description = desc + struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) struct.freeze # This is read-only information @@ -844,6 +830,7 @@ def get_event_type(event) # def get_description(buf, event_source, lkey) rec = EVENTLOGRECORD.new(buf) + str = rec[:DataLength] > 0 ? buf.read_bytes(rec[:DataOffset] - 1)[rec[:StringOffset] .. -1] : buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] str = buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] num = rec[:NumStrings] hkey = FFI::MemoryPointer.new(:uintptr_t) From 40e6894c9f0c39707e5416208506db5e87bbf7b9 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 27 May 2014 09:31:10 +0200 Subject: [PATCH 04/18] Add data binary to EventLogStruct, fix get_description duplicate line --- lib/win32/eventlog.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 6670b5f..c354d85 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -63,7 +63,7 @@ class Error < StandardError; end # The EventLogStruct encapsulates a single event log record. EventLogStruct = Struct.new('EventLogStruct', :record_number, :time_generated, :time_written, :event_id, :event_type, :category, - :source, :computer, :user, :string_inserts, :description + :source, :computer, :user, :string_inserts, :description, :data ) # The name of the event log source. This will typically be @@ -525,6 +525,7 @@ def tail(frequency = 5) # * user # String or nil # * description # String or nil # * string_inserts # An array of Strings or nil + # * data # binary data or nil # # If no block is given the method returns an array of EventLogStruct's. # @@ -574,6 +575,7 @@ def read(flags = nil, offset = 0) struct.user = get_user(record) struct.category = record[:EventCategory] struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) + struct.data = rec[:DataLength] <= 0 ? nil : buf.read_bytes(buf.size)[rec[:DataOffset], rec[:DataLength]] struct.freeze # This is read-only information @@ -766,6 +768,7 @@ def read_last_event() struct.user = get_user(record) struct.category = record[:EventCategory] struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) + struct.data = rec[:DataLength] <= 0 ? nil : buf.read_bytes(buf.size)[rec[:DataOffset], rec[:DataLength]] struct.freeze # This is read-only information @@ -831,7 +834,6 @@ def get_event_type(event) def get_description(buf, event_source, lkey) rec = EVENTLOGRECORD.new(buf) str = rec[:DataLength] > 0 ? buf.read_bytes(rec[:DataOffset] - 1)[rec[:StringOffset] .. -1] : buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] - str = buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] num = rec[:NumStrings] hkey = FFI::MemoryPointer.new(:uintptr_t) key = BASE_KEY + "#{@source}\\#{event_source}" From 8ba5778e84be407a255dc9e861652be4b92c5fd9 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 27 May 2014 16:01:21 +0200 Subject: [PATCH 05/18] Revert: Fix Memory access offset=0 size=8 is out of bounds & Fix: struct.data --- lib/win32/eventlog.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index c354d85..3f23654 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -224,7 +224,7 @@ def self.add_event_source(args) raise SystemCallError.new('RegCreateKeyEx', rv) end - hkey = hkey.address + hkey = hkey.read_pointer.to_i data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt" begin @@ -266,7 +266,7 @@ def self.add_event_source(args) raise SystemCallError.new('RegCreateKeyEx', rv) end - hkey = hkey.address + hkey = hkey.read_pointer.to_i if hash['category_count'] data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['category_count']) @@ -575,7 +575,7 @@ def read(flags = nil, offset = 0) struct.user = get_user(record) struct.category = record[:EventCategory] struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) - struct.data = rec[:DataLength] <= 0 ? nil : buf.read_bytes(buf.size)[rec[:DataOffset], rec[:DataLength]] + struct.data = record[:DataLength] <= 0 ? nil : (buf.read_string(buf.size)[record[:DataOffset], record[:DataLength]]).force_encoding('iso-8859-1') struct.freeze # This is read-only information @@ -768,7 +768,7 @@ def read_last_event() struct.user = get_user(record) struct.category = record[:EventCategory] struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) - struct.data = rec[:DataLength] <= 0 ? nil : buf.read_bytes(buf.size)[rec[:DataOffset], rec[:DataLength]] + struct.data = record[:DataLength] <= 0 ? nil : (buf.read_string(buf.size)[record[:DataOffset], record[:DataLength]]).force_encoding('iso-8859-1') struct.freeze # This is read-only information @@ -848,7 +848,7 @@ def get_description(buf, event_source, lkey) message_exe = nil if RegOpenKeyEx(lkey, key, 0, KEY_READ, hkey) == 0 - hkey = hkey.address + hkey = hkey.read_pointer.to_i value = 'providerGuid' guid = FFI::MemoryPointer.new(:char, MAX_SIZE) @@ -862,7 +862,7 @@ def get_description(buf, event_source, lkey) key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0 - hkey2 = hkey2.address + hkey2 = hkey2.read_pointer.to_i value = 'ParameterMessageFile' file = FFI::MemoryPointer.new(:char, MAX_SIZE) From 1c08c9d92c962b59453309b30f90adbd099d3ea2 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Wed, 28 May 2014 23:00:39 +0200 Subject: [PATCH 06/18] Fix memory leak If treat mass eventlog, memoryleak visible. Remove registry FFI call and use win32/registry ti fix memory leak. Some optimization on memory usage and buffer (I think we don't need to reset/renew pointer, because it is a pointer). --- lib/win32/eventlog.rb | 163 ++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 108 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 3f23654..a89eefa 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -1,6 +1,7 @@ require File.join(File.dirname(__FILE__), 'windows', 'constants') require File.join(File.dirname(__FILE__), 'windows', 'structs') require File.join(File.dirname(__FILE__), 'windows', 'functions') +require "win32/registry" # The Win32 module serves as a namespace only. module Win32 @@ -60,6 +61,8 @@ class Error < StandardError; end # that fails. AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE + EVENTLOGFIXDATALENGTH = 56 + # The EventLogStruct encapsulates a single event log record. EventLogStruct = Struct.new('EventLogStruct', :record_number, :time_generated, :time_written, :event_id, :event_type, :category, @@ -203,7 +206,7 @@ def self.add_event_source(args) raise ArgumentError, 'no event_type specified' end - hkey = FFI::MemoryPointer.new(:uintptr_t) + hkey = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8) disposition = FFI::MemoryPointer.new(:ulong) key = key_base + hash['source'] @@ -244,7 +247,7 @@ def self.add_event_source(args) RegCloseKey(hkey) end - hkey = FFI::MemoryPointer.new(:uintptr_t) + hkey = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8) disposition = FFI::MemoryPointer.new(:ulong) key = key_base << hash['source'] << "\\" << hash['key_name'] @@ -531,6 +534,7 @@ def tail(frequency = 5) # def read(flags = nil, offset = 0) buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + bufKeeper = buf read = FFI::MemoryPointer.new(:ulong) needed = FFI::MemoryPointer.new(:ulong) array = [] @@ -541,7 +545,7 @@ def read(flags = nil, offset = 0) end if @server - hkey = FFI::MemoryPointer.new(:uintptr_t) + hkey = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8) if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end @@ -553,7 +557,9 @@ def read(flags = nil, offset = 0) if FFI.errno == ERROR_INSUFFICIENT_BUFFER needed = needed.read_ulong / EVENTLOGRECORD.size + bufKeeper.free buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed) + bufKeeper = buf unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) raise SystemCallError.new('ReadEventLog', FFI.errno) end @@ -565,8 +571,10 @@ def read(flags = nil, offset = 0) struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] + variableData = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1] + + struct.source = variableData[/^[^\0]*/] + struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) @@ -574,9 +582,8 @@ def read(flags = nil, offset = 0) struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) - struct.data = record[:DataLength] <= 0 ? nil : (buf.read_string(buf.size)[record[:DataOffset], record[:DataLength]]).force_encoding('iso-8859-1') - + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOGFIXDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information if block_given? @@ -597,9 +604,9 @@ def read(flags = nil, offset = 0) buf += length end - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + buf = bufKeeper end - + buf.free block_given? ? nil : array end @@ -738,6 +745,7 @@ def read_last_event() unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) if FFI.errno == ERROR_INSUFFICIENT_BUFFER needed = needed.read_ulong / EVENTLOGRECORD.size + buf.free buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed) unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) raise SystemCallError.new('ReadEventLog', FFI.errno) @@ -748,7 +756,7 @@ def read_last_event() end if @server - hkey = FFI::MemoryPointer.new(:uintptr_t) + hkey = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8) if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end @@ -758,8 +766,10 @@ def read_last_event() struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] + variableData = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1] + + struct.source = variableData[/^[^\0]*/] + struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) @@ -767,8 +777,8 @@ def read_last_event() struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) - struct.data = record[:DataLength] <= 0 ? nil : (buf.read_string(buf.size)[record[:DataOffset], record[:DataLength]]).force_encoding('iso-8859-1') + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOGFIXDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information @@ -831,11 +841,9 @@ def get_event_type(event) # event description (String) based on data from the EVENTLOGRECORD # buffer. # - def get_description(buf, event_source, lkey) - rec = EVENTLOGRECORD.new(buf) - str = rec[:DataLength] > 0 ? buf.read_bytes(rec[:DataOffset] - 1)[rec[:StringOffset] .. -1] : buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] - num = rec[:NumStrings] - hkey = FFI::MemoryPointer.new(:uintptr_t) + def get_description(variableData, record, event_source, lkey) + str = record[:DataLength] > 0 ? variableData[record[:StringOffset] - EVENTLOGFIXDATALENGTH .. record[:DataOffset] - EVENTLOGFIXDATALENGTH - 1] : variableData[record[:StringOffset] - EVENTLOGFIXDATALENGTH .. -5] + num = record[:NumStrings] key = BASE_KEY + "#{@source}\\#{event_source}" buf = FFI::MemoryPointer.new(:char, 8192) va_list = va_list0 = (num == 0) ? [] : str.unpack('Z*' * num) @@ -846,81 +854,23 @@ def get_description(buf, event_source, lkey) param_exe = nil message_exe = nil - - if RegOpenKeyEx(lkey, key, 0, KEY_READ, hkey) == 0 - hkey = hkey.read_pointer.to_i - value = 'providerGuid' - - guid = FFI::MemoryPointer.new(:char, MAX_SIZE) - size = FFI::MemoryPointer.new(:ulong) - - size.write_ulong(guid.size) - - if RegQueryValueEx(hkey, value, nil, nil, guid, size) == 0 - guid = guid.read_string - hkey2 = FFI::MemoryPointer.new(:uintptr_t) - key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" - - if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0 - hkey2 = hkey2.read_pointer.to_i - - value = 'ParameterMessageFile' - file = FFI::MemoryPointer.new(:char, MAX_SIZE) - size = FFI::MemoryPointer.new(:ulong) - - size.write_ulong(file.size) - - if RegQueryValueEx(hkey2, value, nil, nil, file, size) == 0 - file = file.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string - end - - value = 'MessageFileName' - file = FFI::MemoryPointer.new(:char, MAX_SIZE) - size = FFI::MemoryPointer.new(:ulong) - - size.write_ulong(file.size) - - if RegQueryValueEx(hkey2, value, nil, nil, file, size) == 0 - file = file.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string - end - - RegCloseKey(hkey2) - end + hkey = Win32::Registry.open(lkey, key) rescue nil + if hkey != nil + guid = hkey["providerGuid"] + if guid != nil + key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" + param_file = Win32::Registry.open(lkey, key)["ParameterMessageFile"] rescue nil + message_file = Win32::Registry.open(lkey, key)["MessageFileName"] rescue nil + + param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) + message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) else - value = 'ParameterMessageFile' - file = FFI::MemoryPointer.new(:char, MAX_SIZE) - size = FFI::MemoryPointer.new(:ulong) - - size.write_ulong(file.size) - - if RegQueryValueEx(hkey, value, nil, nil, file, size) == 0 - file = file.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string - end - - value = 'EventMessageFile' - file = FFI::MemoryPointer.new(:char, MAX_SIZE) - size = FFI::MemoryPointer.new(:ulong) + param_file = Win32::Registry.open(lkey, key)["ParameterMessageFile"] rescue nil + message_file = Win32::Registry.open(lkey, key)["EventMessageFile"] rescue nil - size.write_ulong(file.size) - - if RegQueryValueEx(hkey, value, nil, nil, file, size) == 0 - file = file.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string - end + param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) + message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) end - - RegCloseKey(hkey) else wevent_source = (event_source + 0.chr).encode('UTF-16LE') @@ -944,13 +894,8 @@ def get_description(buf, event_source, lkey) raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno) end - file = buf2.read_string[16..-1] - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string - - buf2 = FFI::MemoryPointer.new(:char, 8192) - val = FFI::MemoryPointer.new(:ulong) + param_file = buf2.read_string[16..-1] + param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) bool = EvtGetPublisherMetadataProperty( pubMetadata, @@ -965,10 +910,11 @@ def get_description(buf, event_source, lkey) raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno) end - file = buf2.read_string[16..-1] - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string + message_file = buf2.read_string[16..-1] + message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) + + val.free + buf2.free end ensure EvtClose(pubMetadata) if pubMetadata @@ -1024,8 +970,6 @@ def get_description(buf, event_source, lkey) end if message_exe != nil - buf = FFI::MemoryPointer.new(:char, 8192) # Reset the buffer - # Try to retrieve message *without* expanding the inserts yet message_exe.split(';').each{ |lfile| hmodule = LoadLibraryEx( @@ -1034,7 +978,7 @@ def get_description(buf, event_source, lkey) DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) - event_id = rec[:EventID] + event_id = record[:EventID] if hmodule != 0 res = FormatMessage( @@ -1093,7 +1037,7 @@ def get_description(buf, event_source, lkey) DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) - event_id = rec[:EventID] + event_id = record[:EventID] if hmodule != 0 res = FormatMessage( @@ -1128,9 +1072,12 @@ def get_description(buf, event_source, lkey) end ensure Wow64RevertWow64FsRedirection(old_wow_val.read_ulong) + old_wow_val.free end - [va_list0, buf.read_string] + resultstr = buf.read_string + buf.free + [va_list0, resultstr] end end end From 187ff27c34a0469d3a1d390d127d3128fe60f1e0 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Sat, 31 May 2014 02:21:46 +0200 Subject: [PATCH 07/18] Fix get_description --- lib/win32/eventlog.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index a89eefa..c1b0a78 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -854,19 +854,21 @@ def get_description(variableData, record, event_source, lkey) param_exe = nil message_exe = nil - hkey = Win32::Registry.open(lkey, key) rescue nil + + # Todo: find a way to use lkey (for remote server) + hkey = Win32::Registry::HKEY_LOCAL_MACHINE.open(key) rescue nil if hkey != nil - guid = hkey["providerGuid"] + guid = hkey["providerGuid"] rescue nil if guid != nil - key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" - param_file = Win32::Registry.open(lkey, key)["ParameterMessageFile"] rescue nil - message_file = Win32::Registry.open(lkey, key)["MessageFileName"] rescue nil + key2 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" + param_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["ParameterMessageFile"] rescue nil + message_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["MessageFileName"] rescue nil param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) else - param_file = Win32::Registry.open(lkey, key)["ParameterMessageFile"] rescue nil - message_file = Win32::Registry.open(lkey, key)["EventMessageFile"] rescue nil + param_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["ParameterMessageFile"] rescue nil + message_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["EventMessageFile"] rescue nil param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) From 9bc2e88083fe5f65d9cc5c0d847bc26a718a7a1e Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Sat, 31 May 2014 02:22:42 +0200 Subject: [PATCH 08/18] buffer optimize memory --- lib/win32/eventlog.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index c1b0a78..70de2cf 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -557,9 +557,7 @@ def read(flags = nil, offset = 0) if FFI.errno == ERROR_INSUFFICIENT_BUFFER needed = needed.read_ulong / EVENTLOGRECORD.size - bufKeeper.free - buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed) - bufKeeper = buf + buf = bufKeeper unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) raise SystemCallError.new('ReadEventLog', FFI.errno) end From 23dba7ec148e56beb1c78aa5287e5a310df00293 Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Tue, 3 Jun 2014 14:49:41 +0200 Subject: [PATCH 09/18] Fix bad call --- lib/win32/eventlog.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 70de2cf..fde074d 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -865,8 +865,8 @@ def get_description(variableData, record, event_source, lkey) param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) else - param_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["ParameterMessageFile"] rescue nil - message_file = Win32::Registry::HKEY_LOCAL_MACHINE.open(key2)["EventMessageFile"] rescue nil + param_file = hkey["ParameterMessageFile"] rescue nil + message_file = hkey["EventMessageFile"] rescue nil param_exe = param_file == nil ? nil : Win32::Registry.expand_environ(param_file) message_exe = message_file == nil ? nil : Win32::Registry.expand_environ(message_file) From 175f69a9f746b46628fede077c73aca9e4fa46aa Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Wed, 4 Jun 2014 04:43:37 +0200 Subject: [PATCH 10/18] Memory optimization Better file description management: ignore empty file path Remove null byte in file path (some registry have bad entry) Fix miss variable in param_exe FormatMessage Fix buf is not a string in param_exe FormatMessage Fix bad parsing to build max_insert (can be memory leak in some case) --- lib/win32/eventlog.rb | 86 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 756fa00..b06709a 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -530,6 +530,7 @@ def tail(frequency = 5) # def read(flags = nil, offset = 0) buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + bufKeeper = buf read = FFI::MemoryPointer.new(:ulong) needed = FFI::MemoryPointer.new(:ulong) array = [] @@ -551,8 +552,11 @@ def read(flags = nil, offset = 0) FFI.errno == ERROR_INSUFFICIENT_BUFFER if FFI.errno == ERROR_INSUFFICIENT_BUFFER - needed = needed.read_ulong / EVENTLOGRECORD.size - buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed) + buf.free + bufKeeper = nil + buf = nil + buf = FFI::MemoryPointer.new(:char, needed.read_ulong) + bufKeeper = buf unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) raise SystemCallError.new('ReadEventLog', FFI.errno) end @@ -595,9 +599,17 @@ def read(flags = nil, offset = 0) buf += length end - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) - end + buf = bufKeeper + buf.clear + end + needed.free + needed = nil + read.free + read = nil + bufKeeper = nil + buf.free + buf = nil block_given? ? nil : array end @@ -732,8 +744,9 @@ def read_last_event unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) if FFI.errno == ERROR_INSUFFICIENT_BUFFER - needed = needed.read_ulong / EVENTLOGRECORD.size - buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed) + buf.free + buf = nil + buf = FFI::MemoryPointer.new(:char, needed.read_ulong) unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) raise SystemCallError.new('ReadEventLog', FFI.errno) end @@ -766,6 +779,13 @@ def read_last_event struct.freeze # This is read-only information + needed.free + needed = nil + read.free + read = nil + buf.free + buf = nil + struct end @@ -871,6 +891,7 @@ def get_description(buf, event_source, lkey) exe = FFI::MemoryPointer.new(:char, MAX_SIZE) ExpandEnvironmentStrings(file, exe, exe.size) param_exe = exe.read_string + exe.free end value = 'MessageFileName' @@ -883,6 +904,7 @@ def get_description(buf, event_source, lkey) exe = FFI::MemoryPointer.new(:char, MAX_SIZE) ExpandEnvironmentStrings(file, exe, exe.size) message_exe = exe.read_string + exe.free end RegCloseKey(hkey2) @@ -900,6 +922,7 @@ def get_description(buf, event_source, lkey) exe = FFI::MemoryPointer.new(:char, MAX_SIZE) ExpandEnvironmentStrings(file, exe, exe.size) param_exe = exe.read_string + exe.free end value = 'EventMessageFile' @@ -912,6 +935,7 @@ def get_description(buf, event_source, lkey) exe = FFI::MemoryPointer.new(:char, MAX_SIZE) ExpandEnvironmentStrings(file, exe, exe.size) message_exe = exe.read_string + exe.free end file_ptr.free @@ -984,6 +1008,11 @@ def get_description(buf, event_source, lkey) v.scan(/%%(\d+)/).uniq.each{ |x| param_exe.split(';').each{ |lfile| + if lfile.to_s.strip.length == 0 + next + end + #To fix "string contains null byte" on some registry entry (corrupted?) + lfile.gsub!(/\0/, '') hmodule = LoadLibraryEx( lfile, 0, @@ -991,6 +1020,7 @@ def get_description(buf, event_source, lkey) ) if hmodule != 0 + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, @@ -1002,7 +1032,8 @@ def get_description(buf, event_source, lkey) ) if res == 0 - event_id = 0xB0000000 | event_id + event_id = 0xB0000000 | x.first.to_i + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, @@ -1015,11 +1046,11 @@ def get_description(buf, event_source, lkey) end FreeLibrary(hmodule) - break if buf.nstrip != "" + break if buf.read_string.gsub(/\n+/, '') != "" end } - va = va.gsub("%%#{x.first}", buf.nstrip) + va = va.gsub("%%#{x.first}", buf.read_string.gsub(/\n+/, '')) } va @@ -1031,6 +1062,11 @@ def get_description(buf, event_source, lkey) # Try to retrieve message *without* expanding the inserts yet message_exe.split(';').each{ |lfile| + if lfile.to_s.strip.length == 0 + next + end + #To fix "string contains null byte" on some registry entry (corrupted?) + lfile.gsub!(/\0/, '') hmodule = LoadLibraryEx( lfile, 0, @@ -1040,6 +1076,7 @@ def get_description(buf, event_source, lkey) event_id = rec[:EventID] if hmodule != 0 + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, @@ -1052,7 +1089,7 @@ def get_description(buf, event_source, lkey) if res == 0 event_id = 0xB0000000 | event_id - + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, @@ -1070,15 +1107,16 @@ def get_description(buf, event_source, lkey) } # Determine higest %n insert number - max_insert = [num, buf.read_string.scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max + # Remove %% to fix: The %1 '%2' preference item in the '%3' Group Policy Object did not apply because it failed with error code '%4'%%100790273 + max_insert = [num, buf.read_string.gsub(/%%/, '').scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max # Insert dummy strings not provided by caller ((num+1)..(max_insert)).each{ |x| va_list.push("%#{x}") } + strptrs = [] if num == 0 - va_list_ptr = FFI::MemoryPointer.new(:pointer) + va_list_ptr = nil else - strptrs = [] va_list.each{ |x| strptrs << FFI::MemoryPointer.from_string(x) } strptrs << nil @@ -1090,6 +1128,11 @@ def get_description(buf, event_source, lkey) end message_exe.split(';').each{ |lfile| + if lfile.to_s.strip.length == 0 + next + end + #To fix "string contains null byte" on some registry entry (corrupted?) + lfile.gsub!(/\0/, '') hmodule = LoadLibraryEx( lfile, 0, @@ -1099,6 +1142,7 @@ def get_description(buf, event_source, lkey) event_id = rec[:EventID] if hmodule != 0 + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, @@ -1112,7 +1156,7 @@ def get_description(buf, event_source, lkey) if res == 0 event_id = 0xB0000000 | event_id - + buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, @@ -1128,12 +1172,24 @@ def get_description(buf, event_source, lkey) break if buf.read_string != "" # All messages read end } + if num != 0 + strptrs.each_with_index{ |p, i| + unless p.nil? + p.free + end + } + va_list_ptr.free + va_list_ptr = nil + end end ensure Wow64RevertWow64FsRedirection(old_wow_val.read_ulong) + old_wow_val.free end - [va_list0, buf.read_string] + resultstr = buf.read_string + buf.free + [va_list0, resultstr] end end end From 66cb9467cd28a1e3c3dc35af47ed028c692bb98c Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Mon, 9 May 2016 11:21:05 +0200 Subject: [PATCH 11/18] Add EVENTLOGFIXDATALENGTH --- lib/win32/eventlog.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 53c6ea2..5351ea6 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -60,6 +60,8 @@ class Error < StandardError; end # that fails. AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE + EVENTLOGFIXDATALENGTH = 56 + # The EventLogStruct encapsulates a single event log record. EventLogStruct = Struct.new('EventLogStruct', :record_number, :time_generated, :time_written, :event_id, :event_type, :category, @@ -568,8 +570,8 @@ def read(flags = nil, offset = 0) struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] + struct.source = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1][/^[^\0]*/] + struct.computer = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH + struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) @@ -766,8 +768,8 @@ def read_last_event record = EVENTLOGRECORD.new(buf) struct = EventLogStruct.new - struct.source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[56 + struct.source.length + 1..-1][/^[^\0]*/] + struct.source = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1][/^[^\0]*/] + struct.computer = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH + struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) From e201ce152166ba02789e27e2b2e5936d7eec7ae7 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Mon, 9 May 2016 16:36:16 +0200 Subject: [PATCH 12/18] Don't trust FFI MemoryPointer auto free --- lib/win32/eventlog.rb | 66 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 5351ea6..a2eb527 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -245,6 +245,11 @@ def self.add_event_source(args) ensure RegCloseKey(hkey) end + + hkey.free + hkey = nil + disposition.free + disposition = nil hkey = FFI::MemoryPointer.new(:uintptr_t) disposition = FFI::MemoryPointer.new(:ulong) @@ -285,6 +290,8 @@ def self.add_event_source(args) if rv != ERROR_SUCCESS raise SystemCallError.new('RegSetValueEx', rv) end + data.free + data.nil end if hash['category_message_file'] @@ -303,6 +310,8 @@ def self.add_event_source(args) if rv != ERROR_SUCCESS raise SystemCallError.new('RegSetValueEx', rv) end + data.free + data.nil end if hash['event_message_file'] @@ -321,6 +330,8 @@ def self.add_event_source(args) if rv != ERROR_SUCCESS raise SystemCallError.new('RegSetValueEx', rv) end + data.free + data.nil end if hash['parameter_message_file'] @@ -339,6 +350,8 @@ def self.add_event_source(args) if rv != ERROR_SUCCESS raise SystemCallError.new('RegSetValueEx', rv) end + data.free + data.nil end data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['supported_types']) @@ -355,11 +368,18 @@ def self.add_event_source(args) if rv != ERROR_SUCCESS raise SystemCallError.new('RegSetValueEx', rv) end + data.free + data.nil ensure RegCloseKey(hkey) end - disposition.read_ulong + valreturn = disposition.read_ulong + disposition.free + disposition = nil + hkey.free + hkey = nil + valreturn end # Backs up the event log to +file+. Note that you cannot backup to @@ -400,7 +420,12 @@ def full? raise SystemCallError.new('GetEventLogInformation', FFI.errno) end - ptr.read_ulong != 0 + valreturn = ptr.read_ulong != 0 + needed.free + needed = nil + ptr.free + ptr = nil + valreturn end # Returns the absolute record number of the oldest record. Note that @@ -414,7 +439,10 @@ def oldest_record_number raise SystemCallError.new('GetOldestEventLogRecord', FFI.errno) end - rec.read_ulong + valreturn = rec.read_ulong + rec.free + rec = nil + valreturn end # Returns the total number of records for the given event log. @@ -426,7 +454,10 @@ def total_records raise SystemCallError.new('GetNumberOfEventLogRecords', FFI.errno) end - total.read_ulong + valreturn = total.read_ulong + total.free + total = nil + valreturn end # Yields an EventLogStruct every time a record is written to the event @@ -548,6 +579,8 @@ def read(flags = nil, offset = 0) raise SystemCallError.new('RegConnectRegistry', FFI.errno) end lkey = hkey.read_pointer.to_i + hkey.free + hkey = nil end while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) || @@ -727,6 +760,11 @@ def report_event(args) nil ) + if data.nil? + data.free + data = nil + end + unless bool raise SystemCallError.new('ReportEvent', FFI.errno) end @@ -763,6 +801,8 @@ def read_last_event raise SystemCallError.new('RegConnectRegistry', FFI.errno) end lkey = hkey.read_pointer.to_i + hkey.free + hkey = nil end record = EVENTLOGRECORD.new(buf) @@ -822,7 +862,21 @@ def get_user(rec) ) # Return nil if the lookup failed - return val ? name.read_string : nil + namereturn = val ? name.read_string : nil + + domain_size.free + domain_size = nil + name_size.free + name_size = nil + + snu.free + snu = nil + domain.free + domain = nil + name.free + name = nil + + namereturn end # Private method that converts a numeric event type into a human @@ -1187,10 +1241,12 @@ def get_description(buf, event_source, lkey) ensure Wow64RevertWow64FsRedirection(old_wow_val.read_ulong) old_wow_val.free + old_wow_val = nil end resultstr = buf.read_string buf.free + buf = nil [va_list0, resultstr] end end From 6c1111171482bc8e3eb72834016eb363019ca6ba Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Tue, 10 May 2016 11:30:09 +0200 Subject: [PATCH 13/18] Use integrated win32/registry when possible Add EventLog binary data support Add missing registry close key Add constants Fix possible error during FreeLibrary(hmodule) Fix previous commit misplaced memory free --- lib/win32/eventlog.rb | 328 +++++++++++++++------------------ lib/win32/windows/constants.rb | 3 + 2 files changed, 153 insertions(+), 178 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index a2eb527..0feb438 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -2,6 +2,8 @@ require_relative 'windows/structs' require_relative 'windows/functions' +require 'win32/registry' + # The Win32 module serves as a namespace only. module Win32 @@ -60,12 +62,10 @@ class Error < StandardError; end # that fails. AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE - EVENTLOGFIXDATALENGTH = 56 - # The EventLogStruct encapsulates a single event log record. EventLogStruct = Struct.new('EventLogStruct', :record_number, :time_generated, :time_written, :event_id, :event_type, :category, - :source, :computer, :user, :string_inserts, :description + :source, :computer, :user, :string_inserts, :description, :data ) # The name of the event log source. This will typically be @@ -558,6 +558,7 @@ def tail(frequency = 5) # * user # String or nil # * description # String or nil # * string_inserts # An array of Strings or nil + # * data # binary data or nil # # If no block is given the method returns an array of EventLogStruct's. # @@ -568,6 +569,7 @@ def read(flags = nil, offset = 0) needed = FFI::MemoryPointer.new(:ulong) array = [] lkey = HKEY_LOCAL_MACHINE + hkey = nil unless flags flags = FORWARDS_READ | SEQUENTIAL_READ @@ -579,8 +581,6 @@ def read(flags = nil, offset = 0) raise SystemCallError.new('RegConnectRegistry', FFI.errno) end lkey = hkey.read_pointer.to_i - hkey.free - hkey = nil end while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) || @@ -600,11 +600,13 @@ def read(flags = nil, offset = 0) dwread = read.read_ulong while dwread > 0 - struct = EventLogStruct.new record = EVENTLOGRECORD.new(buf) - struct.source = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH + struct.source.length + 1..-1][/^[^\0]*/] + variableData = buf.read_bytes(buf.size)[EVENTLOG_FIXEDDATALENGTH..-1] + + struct = EventLogStruct.new + struct.source = variableData[/^[^\0]*/] + struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) @@ -612,8 +614,8 @@ def read(flags = nil, offset = 0) struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) - + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information if block_given? @@ -638,6 +640,11 @@ def read(flags = nil, offset = 0) buf.clear end + unless hkey.nil? + RegCloseKey(hkey) + hkey.free + hkey = nil + end needed.free needed = nil read.free @@ -760,7 +767,7 @@ def report_event(args) nil ) - if data.nil? + unless data.nil? data.free data = nil end @@ -779,6 +786,7 @@ def read_last_event read = FFI::MemoryPointer.new(:ulong) needed = FFI::MemoryPointer.new(:ulong) lkey = HKEY_LOCAL_MACHINE + hkey = nil flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ @@ -801,15 +809,15 @@ def read_last_event raise SystemCallError.new('RegConnectRegistry', FFI.errno) end lkey = hkey.read_pointer.to_i - hkey.free - hkey = nil end record = EVENTLOGRECORD.new(buf) + variableData = buf.read_bytes(buf.size)[EVENTLOG_FIXEDDATALENGTH..-1] + struct = EventLogStruct.new - struct.source = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH..-1][/^[^\0]*/] - struct.computer = buf.read_bytes(buf.size)[EVENTLOGFIXDATALENGTH + struct.source.length + 1..-1][/^[^\0]*/] + struct.source = variableData[/^[^\0]*/] + struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] struct.record_number = record[:RecordNumber] struct.time_generated = Time.at(record[:TimeGenerated]) struct.time_written = Time.at(record[:TimeWritten]) @@ -817,10 +825,16 @@ def read_last_event struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(buf, struct.source, lkey) + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information + unless hkey.nil? + RegCloseKey(hkey) + hkey.free + hkey = nil + end needed.free needed = nil read.free @@ -903,11 +917,9 @@ def get_event_type(event) # event description (String) based on data from the EVENTLOGRECORD # buffer. # - def get_description(buf, event_source, lkey) - rec = EVENTLOGRECORD.new(buf) - str = buf.read_bytes(buf.size)[rec[:StringOffset] .. -1] - num = rec[:NumStrings] - hkey = FFI::MemoryPointer.new(:uintptr_t) + def get_description(variableData, record, event_source, lkey) + str = record[:DataLength] > 0 ? variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH - 1] : variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. -5] + num = record[:NumStrings] key = BASE_KEY + "#{@source}\\#{event_source}" buf = FFI::MemoryPointer.new(:char, 8192) va_list = va_list0 = (num == 0) ? [] : str.unpack('Z*' * num) @@ -919,86 +931,30 @@ def get_description(buf, event_source, lkey) param_exe = nil message_exe = nil - if RegOpenKeyEx(lkey, key, 0, KEY_READ, hkey) == 0 - hkey = hkey.read_pointer.to_i - value = 'providerGuid' - - guid_ptr = FFI::MemoryPointer.new(:char, MAX_SIZE) - size_ptr = FFI::MemoryPointer.new(:ulong) - - size_ptr.write_ulong(guid_ptr.size) - - if RegQueryValueEx(hkey, value, nil, nil, guid_ptr, size_ptr) == 0 - guid = guid_ptr.read_string - hkey2 = FFI::MemoryPointer.new(:uintptr_t) - key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}" - - guid_ptr.free - - if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0 - hkey2 = hkey2.read_pointer.to_i - - value = 'ParameterMessageFile' - file_ptr = FFI::MemoryPointer.new(:char, MAX_SIZE) - size_ptr.clear.write_ulong(file_ptr.size) - - if RegQueryValueEx(hkey2, value, nil, nil, file_ptr, size_ptr) == 0 - file = file_ptr.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string - exe.free - end - - value = 'MessageFileName' - - file_ptr.clear - size_ptr.clear.write_ulong(file_ptr.size) - - if RegQueryValueEx(hkey2, value, nil, nil, file_ptr, size_ptr) == 0 - file = file_ptr.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string - exe.free - end - - RegCloseKey(hkey2) - - file_ptr.free - size_ptr.free + hkey = Win32::Registry::open(lkey, key) rescue nil + unless hkey.nil? + guid = hkey["providerGuid"] rescue nil + unless guid.nil? + key2 = PUBBASE_KEY + "#{guid}" + param_file = Win32::Registry::open(lkey, key2)["ParameterMessageFile"] rescue nil + message_file = Win32::Registry::open(ley, key2)["MessageFileName"] rescue nil + + unless param_file.nil? + param_exe = Win32::Registry.expand_environ(param_file) + param_file.close() end - else - value = 'ParameterMessageFile' - file_ptr = FFI::MemoryPointer.new(:char, MAX_SIZE) - size_ptr.clear.write_ulong(file_ptr.size) - - if RegQueryValueEx(hkey, value, nil, nil, file_ptr, size_ptr) == 0 - file = file_ptr.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string - exe.free - end - - value = 'EventMessageFile' - - file_ptr.clear - size_ptr.clear.write_ulong(file_ptr.size) - - if RegQueryValueEx(hkey, value, nil, nil, file_ptr, size_ptr) == 0 - file = file_ptr.read_string - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string - exe.free + unless message_file.nil? + message_exe = Win32::Registry.expand_environ(message_file) + message_file.close() end + else + param_file = hkey["ParameterMessageFile"] rescue nil + message_file = hkey["EventMessageFile"] rescue nil - file_ptr.free - size_ptr.free + param_exe = param_file.nil? ? nil : Win32::Registry.expand_environ(param_file) + message_exe = message_file.nil? ? nil : Win32::Registry.expand_environ(message_file) end - - RegCloseKey(hkey) + hkey.close() else wevent_source = (event_source + 0.chr).encode('UTF-16LE') @@ -1022,10 +978,8 @@ def get_description(buf, event_source, lkey) raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno) end - file = buf2.read_string[16..-1] - exe = FFI::MemoryPointer.new(:char, MAX_SIZE) - ExpandEnvironmentStrings(file, exe, exe.size) - param_exe = exe.read_string + param_file = buf2.read_string[16..-1] + param_exe = param_file.nil? ? nil : Win32::Registry.expand_environ(param_file) buf2.clear val.clear @@ -1043,22 +997,22 @@ def get_description(buf, event_source, lkey) raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno) end - exe.clear - file = buf2.read_string[16..-1] - ExpandEnvironmentStrings(file, exe, exe.size) - message_exe = exe.read_string + message_file = buf2.read_string[16..-1] + message_exe = message_file.nil? ? nil : Win32::Registry.expand_environ(message_file) - buf2.free val.free - exe.free + val = nil + buf2.free + buf2 = nil end ensure EvtClose(pubMetadata) if pubMetadata end end - if param_exe != nil + unless param_exe.nil? + buf.clear va_list = va_list0.map{ |v| va = v @@ -1069,40 +1023,44 @@ def get_description(buf, event_source, lkey) end #To fix "string contains null byte" on some registry entry (corrupted?) lfile.gsub!(/\0/, '') - hmodule = LoadLibraryEx( - lfile, - 0, - DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE - ) - - if hmodule != 0 - buf.clear - res = FormatMessage( - FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, - hmodule, - x.first.to_i, + begin + hmodule = LoadLibraryEx( + lfile, 0, - buf, - buf.size, - v + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) - if res == 0 - event_id = 0xB0000000 | x.first.to_i + if hmodule != 0 buf.clear res = FormatMessage( - FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, - event_id, + x.first.to_i, 0, buf, buf.size, - nil + v ) - end - FreeLibrary(hmodule) - break if buf.read_string.gsub(/\n+/, '') != "" + if res == 0 + event_id = 0xB0000000 | x.first.to_i + buf.clear + res = FormatMessage( + FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + hmodule, + event_id, + 0, + buf, + buf.size, + nil + ) + else + next + end + break if buf.read_string.gsub(/\n+/, '') != "" + end + ensure + FreeLibrary(hmodule) if hmodule && hmodule != 0 end } @@ -1113,7 +1071,7 @@ def get_description(buf, event_source, lkey) } end - if message_exe != nil + unless message_exe.nil? buf.clear # Try to retrieve message *without* expanding the inserts yet @@ -1123,28 +1081,17 @@ def get_description(buf, event_source, lkey) end #To fix "string contains null byte" on some registry entry (corrupted?) lfile.gsub!(/\0/, '') - hmodule = LoadLibraryEx( - lfile, - 0, - DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE - ) - - event_id = rec[:EventID] - - if hmodule != 0 - buf.clear - res = FormatMessage( - FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, - hmodule, - event_id, + #puts "message_exe#" + record[:RecordNumber].to_s + "lfile:" + lfile + begin + hmodule = LoadLibraryEx( + lfile, 0, - buf, - buf.size, - nil + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) - if res == 0 - event_id = 0xB0000000 | event_id + event_id = record[:EventID] + + if hmodule != 0 buf.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -1155,16 +1102,32 @@ def get_description(buf, event_source, lkey) buf.size, nil ) - end - FreeLibrary(hmodule) - break if buf.read_string != "" # All messages read + if res == 0 + event_id = 0xB0000000 | event_id + buf.clear + res = FormatMessage( + FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + hmodule, + event_id, + 0, + buf, + buf.size, + nil + ) + end + #puts "message_exe#" + record[:RecordNumber].to_s + "buf:" + buf.read_string + break if buf.read_string != "" # All messages read + end + ensure + FreeLibrary(hmodule) if hmodule && hmodule != 0 end } # Determine higest %n insert number # Remove %% to fix: The %1 '%2' preference item in the '%3' Group Policy Object did not apply because it failed with error code '%4'%%100790273 max_insert = [num, buf.read_string.gsub(/%%/, '').scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max + #puts "message_exe#" + record[:RecordNumber].to_s + "max_insert:" + max_insert.to_s # Insert dummy strings not provided by caller ((num+1)..(max_insert)).each{ |x| va_list.push("%#{x}") } @@ -1180,6 +1143,9 @@ def get_description(buf, event_source, lkey) strptrs.each_with_index{ |p, i| va_list_ptr[i].put_pointer(0, p) + #unless p.nil? + # puts "message_exe2#" + record[:RecordNumber].to_s + "va_list_ptr:" + i.to_s + "/" + p.read_string + #end } end @@ -1189,32 +1155,21 @@ def get_description(buf, event_source, lkey) end #To fix "string contains null byte" on some registry entry (corrupted?) lfile.gsub!(/\0/, '') - hmodule = LoadLibraryEx( - lfile, - 0, - DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE - ) - - event_id = rec[:EventID] - - if hmodule != 0 - buf.clear - res = FormatMessage( - FORMAT_MESSAGE_FROM_HMODULE | - FORMAT_MESSAGE_ARGUMENT_ARRAY, - hmodule, - event_id, + #puts "message_exe2#" + record[:RecordNumber].to_s + "lfile:" + lfile + begin + hmodule = LoadLibraryEx( + lfile, 0, - buf, - buf.size, - va_list_ptr + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) - if res == 0 - event_id = 0xB0000000 | event_id + event_id = record[:EventID] + + if hmodule != 0 buf.clear res = FormatMessage( - FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, + FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, event_id, 0, @@ -1222,10 +1177,27 @@ def get_description(buf, event_source, lkey) buf.size, va_list_ptr ) - end + #puts "message_exe2#" + record[:RecordNumber].to_s + "res1:" + res.to_s - FreeLibrary(hmodule) - break if buf.read_string != "" # All messages read + if res == 0 + event_id = 0xB0000000 | event_id + buf.clear + res = FormatMessage( + FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, + hmodule, + event_id, + 0, + buf, + buf.size, + va_list_ptr + ) + #puts "message_exe2#" + record[:RecordNumber].to_s + "res2:" + res.to_s + end + #puts "message_exe2#" + record[:RecordNumber].to_s + "buf:" + buf.read_string(60) + break if buf.read_string != "" # All messages read + end + ensure + FreeLibrary(hmodule) if hmodule && hmodule != 0 end } if num != 0 diff --git a/lib/win32/windows/constants.rb b/lib/win32/windows/constants.rb index fbcc118..eb122e8 100644 --- a/lib/win32/windows/constants.rb +++ b/lib/win32/windows/constants.rb @@ -16,6 +16,8 @@ module Constants EVENTLOG_FULL_INFO = 0 + EVENTLOG_FIXEDDATALENGTH = 56 + HKEY_LOCAL_MACHINE = 0x80000002 REG_OPTION_NON_VOLATILE = 0 @@ -32,6 +34,7 @@ module Constants WAIT_FAILED = 0xFFFFFFFF BASE_KEY = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\" + PUBBASE_KEY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\" STANDARD_RIGHTS_READ = 0x20000 STANDARD_RIGHTS_WRITE = 0x20000 From f72e102c94ab3089817528954280b9f6d9f7be00 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Tue, 10 May 2016 11:36:45 +0200 Subject: [PATCH 14/18] Use defined constant BASE_KEY --- lib/win32/eventlog.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 0feb438..a7fc943 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -183,8 +183,6 @@ def self.add_event_source(args) supported_types ] - key_base = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\" - # Default values hash = { 'source' => 'Application', @@ -208,7 +206,7 @@ def self.add_event_source(args) hkey = FFI::MemoryPointer.new(:uintptr_t) disposition = FFI::MemoryPointer.new(:ulong) - key = key_base + hash['source'] + key = BASE_KEY + hash['source'] rv = RegCreateKeyEx( HKEY_LOCAL_MACHINE, @@ -254,7 +252,7 @@ def self.add_event_source(args) hkey = FFI::MemoryPointer.new(:uintptr_t) disposition = FFI::MemoryPointer.new(:ulong) - key = key_base << hash['source'] << "\\" << hash['key_name'] + key = BASE_KEY << hash['source'] << "\\" << hash['key_name'] begin rv = RegCreateKeyEx( From d9ea62b1906f2a3e68a7f25b3355ae893cdeca6c Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Tue, 10 May 2016 11:38:53 +0200 Subject: [PATCH 15/18] Fix platform problem to create pointer to pointer --- lib/win32/eventlog.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index a7fc943..1af1aa3 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -726,7 +726,7 @@ def report_event(args) strptrs << FFI::MemoryPointer.from_string(hash['data']) strptrs << nil - data = FFI::MemoryPointer.new(:pointer, strptrs.size) + data = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8, strptrs.size) strptrs.each_with_index do |p, i| data[i].put_pointer(0, p) @@ -741,7 +741,7 @@ def report_event(args) } strptrs << nil - data = FFI::MemoryPointer.new(:pointer, strptrs.size) + data = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8, strptrs.size) strptrs.each_with_index do |p, i| data[i].put_pointer(0, p) @@ -1137,7 +1137,7 @@ def get_description(variableData, record, event_source, lkey) va_list.each{ |x| strptrs << FFI::MemoryPointer.from_string(x) } strptrs << nil - va_list_ptr = FFI::MemoryPointer.new(:pointer, strptrs.size) + va_list_ptr = FFI::MemoryPointer.new(FFI::Platform::ADDRESS_SIZE/8, strptrs.size) strptrs.each_with_index{ |p, i| va_list_ptr[i].put_pointer(0, p) From 30717efe35407e9399595a55173c4090db7093f6 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Tue, 10 May 2016 15:20:52 +0200 Subject: [PATCH 16/18] Use win32/registry constants Use win32/registry when add event Better use of win32/registry when get event description --- lib/win32/eventlog.rb | 191 ++++----------------------------- lib/win32/windows/constants.rb | 13 --- 2 files changed, 20 insertions(+), 184 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 1af1aa3..8a3bade 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -203,180 +203,39 @@ def self.add_event_source(args) raise ArgumentError, 'no event_type specified' end - hkey = FFI::MemoryPointer.new(:uintptr_t) - disposition = FFI::MemoryPointer.new(:ulong) - key = BASE_KEY + hash['source'] - - rv = RegCreateKeyEx( - HKEY_LOCAL_MACHINE, - key, - 0, - nil, - REG_OPTION_NON_VOLATILE, - KEY_WRITE, - nil, - hkey, - disposition - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegCreateKeyEx', rv) + Win32::Registry::HKEY_LOCAL_MACHINE.create(key, Win32::Registry::KEY_ALL_ACCESS, Win32::Registry::REG_OPTION_NON_VOLATILE) do |regkey| + data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt" + regkey.write('File', Win32::Registry::REG_EXPAND_SZ, data) end - hkey = hkey.read_pointer.to_i - data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt" - - begin - rv = RegSetValueEx( - hkey, - 'File', - 0, - REG_EXPAND_SZ, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - ensure - RegCloseKey(hkey) - end - - hkey.free - hkey = nil - disposition.free - disposition = nil - - hkey = FFI::MemoryPointer.new(:uintptr_t) - disposition = FFI::MemoryPointer.new(:ulong) + valreturn = nil key = BASE_KEY << hash['source'] << "\\" << hash['key_name'] - - begin - rv = RegCreateKeyEx( - HKEY_LOCAL_MACHINE, - key, - 0, - nil, - REG_OPTION_NON_VOLATILE, - KEY_WRITE, - nil, - hkey, - disposition - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegCreateKeyEx', rv) - end - - hkey = hkey.read_pointer.to_i - + Win32::Registry::HKEY_LOCAL_MACHINE.create(key, Win32::Registry::KEY_ALL_ACCESS, Win32::Registry::REG_OPTION_NON_VOLATILE) do |regkey| if hash['category_count'] - data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['category_count']) - - rv = RegSetValueEx( - hkey, - 'CategoryCount', - 0, - REG_DWORD, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - data.free - data.nil + regkey.write('CategoryCount', Win32::Registry::REG_DWORD, hash['category_count']) end if hash['category_message_file'] data = File.expand_path(hash['category_message_file']) - data = FFI::MemoryPointer.from_string(data) - - rv = RegSetValueEx( - hkey, - 'CategoryMessageFile', - 0, - REG_EXPAND_SZ, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - data.free - data.nil + regkey.write('CategoryMessageFile', Win32::Registry::REG_EXPAND_SZ, data) end if hash['event_message_file'] data = File.expand_path(hash['event_message_file']) - data = FFI::MemoryPointer.from_string(data) - - rv = RegSetValueEx( - hkey, - 'EventMessageFile', - 0, - REG_EXPAND_SZ, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - data.free - data.nil + regkey.write('EventMessageFile', Win32::Registry::REG_EXPAND_SZ, data) end if hash['parameter_message_file'] data = File.expand_path(hash['parameter_message_file']) - data = FFI::MemoryPointer.from_string(data) - - rv = RegSetValueEx( - hkey, - 'ParameterMessageFile', - 0, - REG_EXPAND_SZ, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - data.free - data.nil + regkey.write('ParameterMessageFile', Win32::Registry::REG_EXPAND_SZ, data) end - data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['supported_types']) - - rv = RegSetValueEx( - hkey, - 'TypesSupported', - 0, - REG_DWORD, - data, - data.size - ) - - if rv != ERROR_SUCCESS - raise SystemCallError.new('RegSetValueEx', rv) - end - data.free - data.nil - ensure - RegCloseKey(hkey) + regkey.write('TypesSupported', Win32::Registry::REG_DWORD, hash['supported_types']) + valreturn = regkey.disposition end - valreturn = disposition.read_ulong - disposition.free - disposition = nil - hkey.free - hkey = nil valreturn end @@ -929,30 +788,20 @@ def get_description(variableData, record, event_source, lkey) param_exe = nil message_exe = nil - hkey = Win32::Registry::open(lkey, key) rescue nil - unless hkey.nil? - guid = hkey["providerGuid"] rescue nil + regkey = Win32::Registry.open(lkey, key) rescue nil + unless regkey.nil? + guid = regkey["providerGuid"] rescue nil unless guid.nil? key2 = PUBBASE_KEY + "#{guid}" - param_file = Win32::Registry::open(lkey, key2)["ParameterMessageFile"] rescue nil - message_file = Win32::Registry::open(ley, key2)["MessageFileName"] rescue nil - - unless param_file.nil? - param_exe = Win32::Registry.expand_environ(param_file) - param_file.close() - end - unless message_file.nil? - message_exe = Win32::Registry.expand_environ(message_file) - message_file.close() + Win32::Registry.open(lkey, key2) do + param_exe = regkey2["ParameterMessageFile", REG_EXPAND_SZ] + message_exe = regkey2["MessageFileName", REG_EXPAND_SZ] end else - param_file = hkey["ParameterMessageFile"] rescue nil - message_file = hkey["EventMessageFile"] rescue nil - - param_exe = param_file.nil? ? nil : Win32::Registry.expand_environ(param_file) - message_exe = message_file.nil? ? nil : Win32::Registry.expand_environ(message_file) + param_exe = regkey["ParameterMessageFile", REG_EXPAND_SZ] + message_exe = regkey["EventMessageFile", REG_EXPAND_SZ] end - hkey.close() + regkey.close else wevent_source = (event_source + 0.chr).encode('UTF-16LE') diff --git a/lib/win32/windows/constants.rb b/lib/win32/windows/constants.rb index eb122e8..cb9f3b4 100644 --- a/lib/win32/windows/constants.rb +++ b/lib/win32/windows/constants.rb @@ -36,19 +36,6 @@ module Constants BASE_KEY = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\" PUBBASE_KEY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\" - STANDARD_RIGHTS_READ = 0x20000 - STANDARD_RIGHTS_WRITE = 0x20000 - SYNCHRONIZE = 0x100000 - - KEY_QUERY_VALUE = 0x0001 - KEY_SET_VALUE = 0x0002 - KEY_CREATE_SUB_KEY = 0x0004 - KEY_ENUMERATE_SUB_KEYS = 0x0008 - KEY_NOTIFY = 0x0010 - - KEY_WRITE = (STANDARD_RIGHTS_WRITE|KEY_SET_VALUE|KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE) - KEY_READ = (STANDARD_RIGHTS_READ|KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS| KEY_NOTIFY) & (~SYNCHRONIZE) - DONT_RESOLVE_DLL_REFERENCES = 0x00000001 LOAD_LIBRARY_AS_DATAFILE = 0x00000002 From c5da70975b6d9a99bc5e9da2fb75497873f14cfc Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Wed, 11 May 2016 14:46:25 +0200 Subject: [PATCH 17/18] Fix get description Dirty hack to access remote registry using Win32::Registry --- lib/win32/eventlog.rb | 69 +++++++++++++++++++++------------- lib/win32/windows/constants.rb | 2 - 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index 8a3bade..ad02af5 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -68,6 +68,11 @@ class Error < StandardError; end :source, :computer, :user, :string_inserts, :description, :data ) + # The EventLogStruct encapsulates a single event log record. + RegistryHKEYStruct = Struct.new('RegistryHKEYStruct', :hkey, + :parent, :keyname, :disposition + ) + # The name of the event log source. This will typically be # 'Application', 'System' or 'Security', but could also refer to # a custom event log source. @@ -420,13 +425,13 @@ def tail(frequency = 5) # If no block is given the method returns an array of EventLogStruct's. # def read(flags = nil, offset = 0) - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) bufKeeper = buf - read = FFI::MemoryPointer.new(:ulong) - needed = FFI::MemoryPointer.new(:ulong) - array = [] - lkey = HKEY_LOCAL_MACHINE - hkey = nil + read = FFI::MemoryPointer.new(:ulong) + needed = FFI::MemoryPointer.new(:ulong) + array = [] + reglkey = Win32::Registry::HKEY_LOCAL_MACHINE + hkey = nil unless flags flags = FORWARDS_READ | SEQUENTIAL_READ @@ -434,10 +439,15 @@ def read(flags = nil, offset = 0) if @server hkey = FFI::MemoryPointer.new(:uintptr_t) - if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0 + if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, hkey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end - lkey = hkey.read_pointer.to_i + # Dirty hack to access remote registry using Win32::Registry + reglkey = RegistryHKEYStruct.new + reglkey.hkey = hkey.read_pointer.to_i + reglkey.parent = nil + reglkey.keyname = "REMOTE_HKEY_LOCAL_MACHINE" + reglkey.disposition = Win32::Registry::REG_OPENED_EXISTING_KEY end while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) || @@ -471,7 +481,7 @@ def read(flags = nil, offset = 0) struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, reglkey) struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information @@ -498,7 +508,7 @@ def read(flags = nil, offset = 0) end unless hkey.nil? - RegCloseKey(hkey) + RegCloseKey(hkey.read_pointer.to_i) hkey.free hkey = nil end @@ -639,11 +649,11 @@ def report_event(args) # Reads the last event record. # def read_last_event - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) - read = FFI::MemoryPointer.new(:ulong) - needed = FFI::MemoryPointer.new(:ulong) - lkey = HKEY_LOCAL_MACHINE - hkey = nil + buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + read = FFI::MemoryPointer.new(:ulong) + needed = FFI::MemoryPointer.new(:ulong) + reglkey = Win32::Registry::HKEY_LOCAL_MACHINE + hkey = nil flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ @@ -662,10 +672,15 @@ def read_last_event if @server hkey = FFI::MemoryPointer.new(:uintptr_t) - if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0 + if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, hkey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end - lkey = hkey.read_pointer.to_i + # Dirty hack to access remote registry using Win32::Registry + reglkey = RegistryHKEYStruct.new + reglkey.hkey = hkey.read_pointer.to_i + reglkey.parent = nil + reglkey.keyname = "REMOTE_HKEY_LOCAL_MACHINE" + reglkey.disposition = Win32::Registry::REG_OPENED_EXISTING_KEY end record = EVENTLOGRECORD.new(buf) @@ -682,13 +697,13 @@ def read_last_event struct.event_type = get_event_type(record[:EventType]) struct.user = get_user(record) struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(variableData, record, struct.source, lkey) + struct.string_inserts, struct.description = get_description(variableData, record, struct.source, reglkey) struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) struct.freeze # This is read-only information unless hkey.nil? - RegCloseKey(hkey) + RegCloseKey(hkey.read_pointer.to_i) hkey.free hkey = nil end @@ -774,7 +789,7 @@ def get_event_type(event) # event description (String) based on data from the EVENTLOGRECORD # buffer. # - def get_description(variableData, record, event_source, lkey) + def get_description(variableData, record, event_source, reglkey) str = record[:DataLength] > 0 ? variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH - 1] : variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. -5] num = record[:NumStrings] key = BASE_KEY + "#{@source}\\#{event_source}" @@ -788,18 +803,18 @@ def get_description(variableData, record, event_source, lkey) param_exe = nil message_exe = nil - regkey = Win32::Registry.open(lkey, key) rescue nil - unless regkey.nil? + regkey = Win32::Registry.open(reglkey, key) rescue nil + if !regkey.nil? && regkey.open? guid = regkey["providerGuid"] rescue nil unless guid.nil? key2 = PUBBASE_KEY + "#{guid}" - Win32::Registry.open(lkey, key2) do - param_exe = regkey2["ParameterMessageFile", REG_EXPAND_SZ] - message_exe = regkey2["MessageFileName", REG_EXPAND_SZ] + Win32::Registry.open(reglkey, key2) do |regkey2| + param_exe = regkey2["ParameterMessageFile", REG_EXPAND_SZ] rescue nil + message_exe = regkey2["MessageFileName", REG_EXPAND_SZ] rescue nil end else - param_exe = regkey["ParameterMessageFile", REG_EXPAND_SZ] - message_exe = regkey["EventMessageFile", REG_EXPAND_SZ] + param_exe = regkey["ParameterMessageFile", REG_EXPAND_SZ] rescue nil + message_exe = regkey["EventMessageFile", REG_EXPAND_SZ] rescue nil end regkey.close else diff --git a/lib/win32/windows/constants.rb b/lib/win32/windows/constants.rb index cb9f3b4..428b6af 100644 --- a/lib/win32/windows/constants.rb +++ b/lib/win32/windows/constants.rb @@ -18,8 +18,6 @@ module Constants EVENTLOG_FIXEDDATALENGTH = 56 - HKEY_LOCAL_MACHINE = 0x80000002 - REG_OPTION_NON_VOLATILE = 0 REG_DWORD = 4 REG_EXPAND_SZ = 2 From 0fa6284f47154c480b45090223251113cae852a0 Mon Sep 17 00:00:00 2001 From: Manus Freedom Date: Fri, 13 May 2016 17:42:14 +0200 Subject: [PATCH 18/18] Big memory optimization to avoid create many objects in memory (less work for GC) Use correct variable (local instead of direct FFI constants) Fix null detection (possible NPE) Username now include domain name Fix string encoding for description and insertstring --- lib/win32/eventlog.rb | 438 +++++++++++++++++---------------- lib/win32/windows/constants.rb | 2 +- 2 files changed, 226 insertions(+), 214 deletions(-) diff --git a/lib/win32/eventlog.rb b/lib/win32/eventlog.rb index ad02af5..af40540 100644 --- a/lib/win32/eventlog.rb +++ b/lib/win32/eventlog.rb @@ -119,7 +119,35 @@ def initialize(source = 'Application', server = nil, file = nil) raise SystemCallError.new(function, FFI.errno) end - # Ensure the handle is closed at the end of a block + @totalrec = FFI::MemoryPointer.new(:ulong) + @oldestrec = FFI::MemoryPointer.new(:ulong) + @eventfullptr = FFI::MemoryPointer.new(:ulong, 1) + @eventfullneeded = FFI::MemoryPointer.new(:ulong) + + @readBuf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + @readPos = @readBuf + @readSize = FFI::MemoryPointer.new(:ulong) + @readNeeded = FFI::MemoryPointer.new(:ulong) + @readHKey = FFI::MemoryPointer.new(:uintptr_t) + + @readlBuf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) + @readlSize = FFI::MemoryPointer.new(:ulong) + @readlNeeded = FFI::MemoryPointer.new(:ulong) + @readlHKey = FFI::MemoryPointer.new(:uintptr_t) + + @guserName = FFI::MemoryPointer.new(:char, MAX_SIZE) + @guserName_size = FFI::MemoryPointer.new(:ulong) + @guserDomain = FFI::MemoryPointer.new(:char, MAX_SIZE) + @guserDomain_size = FFI::MemoryPointer.new(:ulong) + @guserSnu = FFI::MemoryPointer.new(:int) + + #Max buf size 64KB required by FormatMessage https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx + @gdescriptionBuf1 = FFI::MemoryPointer.new(:char, 65535) + @gdescriptionBuf2 = FFI::MemoryPointer.new(:char, 65535) + @gdescriptionOldWowVal = FFI::MemoryPointer.new(:int) + @gdescriptionRetVal = FFI::MemoryPointer.new(:ulong) + + # Ensure the handle is closed at the end of a block and all pointers are free if block_given? begin yield self @@ -269,25 +297,64 @@ def clear(backup_file = nil) # if you use the block form of EventLog.new. # def close + @gdescriptionBuf1.free + @gdescriptionBuf1 = nil + @gdescriptionBuf2.free + @gdescriptionBuf2 = nil + @gdescriptionOldWowVal.free + @gdescriptionOldWowVal = nil + @gdescriptionRetVal.free + @gdescriptionRetVal = nil + + @guserSnu.free + @guserSnu = nil + @guserDomain_size.free + @guserDomain_size = nil + @guserDomain.free + @guserDomain = nil + @guserName_size.free + @guserName_size = nil + @guserName.free + @guserName = nil + + @readlHKey.free + @readlHKey = nil + @readlNeeded.free + @readlNeeded = nil + @readlSize.free + @readlSize = nil + @readlBuf.free + @readlBuf = nil + + @readHKey.free + @readHKey = nil + @readNeeded.free + @readNeeded = nil + @readSize.free + @readSize = nil + @readBuf.free + @readPos = nil + @readBuf = nil + + @eventfullneeded.free + @eventfullneeded = nil + @eventfullptr.free + @eventfullptr = nil + @oldestrec.free + @oldestrec = nil + @totalrec.free + @totalrec = nil CloseEventLog(@handle) end # Indicates whether or not the event log is full. # def full? - ptr = FFI::MemoryPointer.new(:ulong, 1) - needed = FFI::MemoryPointer.new(:ulong) - - unless GetEventLogInformation(@handle, 0, ptr, ptr.size, needed) + unless GetEventLogInformation(@handle, 0, @eventfullptr, @eventfullptr.size, @eventfullneeded) raise SystemCallError.new('GetEventLogInformation', FFI.errno) end - valreturn = ptr.read_ulong != 0 - needed.free - needed = nil - ptr.free - ptr = nil - valreturn + @eventfullptr.read_ulong != 0 end # Returns the absolute record number of the oldest record. Note that @@ -295,31 +362,21 @@ def full? # overwritten. # def oldest_record_number - rec = FFI::MemoryPointer.new(:ulong) - - unless GetOldestEventLogRecord(@handle, rec) + unless GetOldestEventLogRecord(@handle, @oldestrec) raise SystemCallError.new('GetOldestEventLogRecord', FFI.errno) end - valreturn = rec.read_ulong - rec.free - rec = nil - valreturn + @oldestrec.read_ulong end # Returns the total number of records for the given event log. # def total_records - total = FFI::MemoryPointer.new(:ulong) - - unless GetNumberOfEventLogRecords(@handle, total) + unless GetNumberOfEventLogRecords(@handle, @totalrec) raise SystemCallError.new('GetNumberOfEventLogRecords', FFI.errno) end - valreturn = total.read_ulong - total.free - total = nil - valreturn + @totalrec.read_ulong end # Yields an EventLogStruct every time a record is written to the event @@ -425,64 +482,59 @@ def tail(frequency = 5) # If no block is given the method returns an array of EventLogStruct's. # def read(flags = nil, offset = 0) - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) - bufKeeper = buf - read = FFI::MemoryPointer.new(:ulong) - needed = FFI::MemoryPointer.new(:ulong) + @readBuf.clear + @readSize.clear + @readNeeded.clear array = [] reglkey = Win32::Registry::HKEY_LOCAL_MACHINE - hkey = nil + @readHKey.clear unless flags flags = FORWARDS_READ | SEQUENTIAL_READ end - if @server - hkey = FFI::MemoryPointer.new(:uintptr_t) - if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, hkey) != 0 + unless @server.nil? + if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, @readHKey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end # Dirty hack to access remote registry using Win32::Registry reglkey = RegistryHKEYStruct.new - reglkey.hkey = hkey.read_pointer.to_i + reglkey.hkey = @readHKey.read_pointer.to_i reglkey.parent = nil reglkey.keyname = "REMOTE_HKEY_LOCAL_MACHINE" reglkey.disposition = Win32::Registry::REG_OPENED_EXISTING_KEY end - while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) || + while ReadEventLog(@handle, flags, offset, @readBuf, @readBuf.size, @readSize, @readNeeded) || FFI.errno == ERROR_INSUFFICIENT_BUFFER if FFI.errno == ERROR_INSUFFICIENT_BUFFER - buf.free - bufKeeper = nil - buf = nil - buf = FFI::MemoryPointer.new(:char, needed.read_ulong) - bufKeeper = buf - unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) + @readBuf.free + @readBuf = nil + @readBuf = FFI::MemoryPointer.new(:char, @readNeeded.read_ulong) + unless ReadEventLog(@handle, flags, offset, @readBuf, @readBuf.size, @readSize, @readNeeded) raise SystemCallError.new('ReadEventLog', FFI.errno) end end - dwread = read.read_ulong + @readPos = @readBuf + dwread = @readSize.read_ulong while dwread > 0 - record = EVENTLOGRECORD.new(buf) - - variableData = buf.read_bytes(buf.size)[EVENTLOG_FIXEDDATALENGTH..-1] + recordItem = EVENTLOGRECORD.new(@readPos) struct = EventLogStruct.new - struct.source = variableData[/^[^\0]*/] - struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] - struct.record_number = record[:RecordNumber] - struct.time_generated = Time.at(record[:TimeGenerated]) - struct.time_written = Time.at(record[:TimeWritten]) - struct.event_id = record[:EventID] & 0x0000FFFF - struct.event_type = get_event_type(record[:EventType]) - struct.user = get_user(record) - struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(variableData, record, struct.source, reglkey) - struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) + struct.source = @readPos.get_string(EVENTLOG_FIXEDDATALENGTH) + struct.computer = @readPos.get_string(EVENTLOG_FIXEDDATALENGTH + struct.source.length + 1) + struct.record_number = recordItem[:RecordNumber] + struct.time_generated = Time.at(recordItem[:TimeGenerated]) + struct.time_written = Time.at(recordItem[:TimeWritten]) + struct.event_id = recordItem[:EventID] & 0x0000FFFF + struct.event_type = get_event_type(recordItem[:EventType]) + struct.user = get_user(recordItem) + struct.category = recordItem[:EventCategory] + struct.string_inserts, struct.description = get_description(@readPos, recordItem, struct.source, reglkey) + struct.data = recordItem[:DataLength] <= 0 ? nil : @readPos.get_bytes(recordItem[:DataOffset], recordItem[:DataLength]) struct.freeze # This is read-only information if block_given? @@ -491,34 +543,22 @@ def read(flags = nil, offset = 0) array.push(struct) end - if flags & EVENTLOG_BACKWARDS_READ > 0 - offset = record[:RecordNumber] - 1 + if flags & BACKWARDS_READ > 0 + offset = recordItem[:RecordNumber] - 1 else - offset = record[:RecordNumber] + 1 + offset = recordItem[:RecordNumber] + 1 end - length = record[:Length] + length = recordItem[:Length] dwread -= length - buf += length + @readPos += length end - - buf = bufKeeper - buf.clear end - unless hkey.nil? - RegCloseKey(hkey.read_pointer.to_i) - hkey.free - hkey = nil + if @readHKey.read_float.to_i != 0 + RegCloseKey(@readHKey.read_pointer.to_i) end - needed.free - needed = nil - read.free - read = nil - bufKeeper = nil - buf.free - buf = nil block_given? ? nil : array end @@ -590,6 +630,7 @@ def report_event(args) raise SystemCallError.new('RegisterEventSource', FFI.errno) end + data = FFI::MemoryPointer::NULL if hash['data'].is_a?(String) strptrs = [] strptrs << FFI::MemoryPointer.from_string(hash['data']) @@ -634,7 +675,13 @@ def report_event(args) nil ) - unless data.nil? + strptrs.each{ |p| + unless p.nil? + p.free + p = nil + end + } + unless data.null? data.free data = nil end @@ -649,71 +696,59 @@ def report_event(args) # Reads the last event record. # def read_last_event - buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE) - read = FFI::MemoryPointer.new(:ulong) - needed = FFI::MemoryPointer.new(:ulong) + @readlBuf.clear + @readlSize.clear + @readlNeeded.clear reglkey = Win32::Registry::HKEY_LOCAL_MACHINE - hkey = nil - - flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ + @readlHKey.clear - unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) - if FFI.errno == ERROR_INSUFFICIENT_BUFFER - buf.free - buf = nil - buf = FFI::MemoryPointer.new(:char, needed.read_ulong) - unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed) - raise SystemCallError.new('ReadEventLog', FFI.errno) - end - else - raise SystemCallError.new('ReadEventLog', FFI.errno) - end - end + flags = BACKWARDS_READ | SEQUENTIAL_READ - if @server - hkey = FFI::MemoryPointer.new(:uintptr_t) - if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, hkey) != 0 + unless @server.nil? + if RegConnectRegistry(@server, Win32::Registry::HKEY_LOCAL_MACHINE.hkey, @readlHKey) != 0 raise SystemCallError.new('RegConnectRegistry', FFI.errno) end # Dirty hack to access remote registry using Win32::Registry reglkey = RegistryHKEYStruct.new - reglkey.hkey = hkey.read_pointer.to_i + reglkey.hkey = @readlHKey.read_pointer.to_i reglkey.parent = nil reglkey.keyname = "REMOTE_HKEY_LOCAL_MACHINE" reglkey.disposition = Win32::Registry::REG_OPENED_EXISTING_KEY end - record = EVENTLOGRECORD.new(buf) + unless ReadEventLog(@handle, flags, 0, @readlBuf, @readlBuf.size, @readlSize, @readlNeeded) + if FFI.errno == ERROR_INSUFFICIENT_BUFFER + @readlBuf.free + @readlBuf = nil + @readlBuf = FFI::MemoryPointer.new(:char, @readlNeeded.read_ulong) + unless ReadEventLog(@handle, flags, 0, @readlBuf, @readlBuf.size, @readlSize, @readlNeeded) + raise SystemCallError.new('ReadEventLog', FFI.errno) + end + else + raise SystemCallError.new('ReadEventLog', FFI.errno) + end + end - variableData = buf.read_bytes(buf.size)[EVENTLOG_FIXEDDATALENGTH..-1] - struct = EventLogStruct.new - struct.source = variableData[/^[^\0]*/] - struct.computer = variableData[struct.source.length + 1..-1][/^[^\0]*/] - struct.record_number = record[:RecordNumber] - struct.time_generated = Time.at(record[:TimeGenerated]) - struct.time_written = Time.at(record[:TimeWritten]) - struct.event_id = record[:EventID] & 0x0000FFFF - struct.event_type = get_event_type(record[:EventType]) - struct.user = get_user(record) - struct.category = record[:EventCategory] - struct.string_inserts, struct.description = get_description(variableData, record, struct.source, reglkey) - struct.data = record[:DataLength] <= 0 ? nil : (variableData[record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH, record[:DataLength]]) + recordItem = EVENTLOGRECORD.new(@readlBuf) + struct = EventLogStruct.new + struct.source = @readlBuf.get_string(EVENTLOG_FIXEDDATALENGTH) + struct.computer = @readlBuf.get_string(EVENTLOG_FIXEDDATALENGTH + struct.source.length + 1) + struct.record_number = recordItem[:RecordNumber] + struct.time_generated = Time.at(recordItem[:TimeGenerated]) + struct.time_written = Time.at(recordItem[:TimeWritten]) + struct.event_id = recordItem[:EventID] & 0x0000FFFF + struct.event_type = get_event_type(recordItem[:EventType]) + struct.user = get_user(recordItem) + struct.category = recordItem[:EventCategory] + struct.string_inserts, struct.description = get_description(@readlBuf, recordItem, struct.source, reglkey) + struct.data = recordItem[:DataLength] <= 0 ? nil : @readlBuf.get_bytes(recordItem[:DataOffset], recordItem[:DataLength]) struct.freeze # This is read-only information - unless hkey.nil? - RegCloseKey(hkey.read_pointer.to_i) - hkey.free - hkey = nil + if @readlHKey.read_float.to_i != 0 + RegCloseKey(@readlHKey.read_pointer.to_i) end - needed.free - needed = nil - read.free - read = nil - buf.free - buf = nil - struct end @@ -725,44 +760,29 @@ def read_last_event def get_user(rec) return nil if rec[:UserSidLength] <= 0 - name = FFI::MemoryPointer.new(:char, MAX_SIZE) - domain = FFI::MemoryPointer.new(:char, MAX_SIZE) - snu = FFI::MemoryPointer.new(:int) + @guserName.clear + @guserName_size.clear + @guserDomain.clear + @guserDomain_size.clear + @guserSnu.clear - name_size = FFI::MemoryPointer.new(:ulong) - domain_size = FFI::MemoryPointer.new(:ulong) - - name_size.write_ulong(name.size) - domain_size.write_ulong(domain.size) + @guserName_size.write_ulong(@guserName.size) + @guserDomain_size.write_ulong(@guserDomain.size) offset = rec[:UserSidOffset] val = LookupAccountSid( @server, rec.pointer + offset, - name, - name_size, - domain, - domain_size, - snu + @guserName, + @guserName_size, + @guserDomain, + @guserDomain_size, + @guserSnu ) # Return nil if the lookup failed - namereturn = val ? name.read_string : nil - - domain_size.free - domain_size = nil - name_size.free - name_size = nil - - snu.free - snu = nil - domain.free - domain = nil - name.free - name = nil - - namereturn + val ? @guserDomain.read_string + "\\" + @guserName.read_string : nil end # Private method that converts a numeric event type into a human @@ -770,15 +790,15 @@ def get_user(rec) # def get_event_type(event) case event - when EVENTLOG_ERROR_TYPE + when ERROR_TYPE 'error' - when EVENTLOG_WARNING_TYPE + when WARN_TYPE 'warning' - when EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS + when INFO_TYPE, SUCCESS 'information' - when EVENTLOG_AUDIT_SUCCESS + when AUDIT_SUCCESS 'audit_success' - when EVENTLOG_AUDIT_FAILURE + when AUDIT_FAILURE 'audit_failure' else nil @@ -789,16 +809,15 @@ def get_event_type(event) # event description (String) based on data from the EVENTLOGRECORD # buffer. # - def get_description(variableData, record, event_source, reglkey) - str = record[:DataLength] > 0 ? variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. record[:DataOffset] - EVENTLOG_FIXEDDATALENGTH - 1] : variableData[record[:StringOffset] - EVENTLOG_FIXEDDATALENGTH .. -5] + def get_description(readerBufPtr, record, event_source, reglkey) num = record[:NumStrings] key = BASE_KEY + "#{@source}\\#{event_source}" - buf = FFI::MemoryPointer.new(:char, 8192) - va_list = va_list0 = (num == 0) ? [] : str.unpack('Z*' * num) + @gdescriptionBuf1.clear + va_list = va_list0 = (num == 0) ? [] : (record[:DataLength] > 0 ? readerBufPtr.get_bytes(record[:StringOffset], record[:DataOffset] - 1) : readerBufPtr.get_bytes(record[:StringOffset], readerBufPtr.size - record[:StringOffset])).unpack('Z*' * num) begin - old_wow_val = FFI::MemoryPointer.new(:int) - Wow64DisableWow64FsRedirection(old_wow_val) + @gdescriptionOldWowVal.clear + Wow64DisableWow64FsRedirection(@gdescriptionOldWowVal) param_exe = nil message_exe = nil @@ -824,35 +843,35 @@ def get_description(variableData, record, event_source, reglkey) pubMetadata = EvtOpenPublisherMetadata(0, wevent_source, nil, 1024, 0) if pubMetadata > 0 - buf2 = FFI::MemoryPointer.new(:char, 8192) - val = FFI::MemoryPointer.new(:ulong) + @gdescriptionBuf2.clear + @gdescriptionRetVal.clear bool = EvtGetPublisherMetadataProperty( pubMetadata, 2, # EvtPublisherMetadataParameterFilePath 0, - buf2.size, - buf2, - val + @gdescriptionBuf2.size, + @gdescriptionBuf2, + @gdescriptionRetVal ) unless bool raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno) end - param_file = buf2.read_string[16..-1] + param_file = @gdescriptionBuf2.read_string[16..-1] param_exe = param_file.nil? ? nil : Win32::Registry.expand_environ(param_file) - buf2.clear - val.clear + @gdescriptionBuf2.clear + @gdescriptionRetVal.clear bool = EvtGetPublisherMetadataProperty( pubMetadata, 3, # EvtPublisherMetadataMessageFilePath 0, - buf2.size, - buf2, - val + @gdescriptionBuf2.size, + @gdescriptionBuf2, + @gdescriptionRetVal ) unless bool @@ -860,13 +879,8 @@ def get_description(variableData, record, event_source, reglkey) end - message_file = buf2.read_string[16..-1] + message_file = @gdescriptionBuf2.read_string[16..-1] message_exe = message_file.nil? ? nil : Win32::Registry.expand_environ(message_file) - - val.free - val = nil - buf2.free - buf2 = nil end ensure EvtClose(pubMetadata) if pubMetadata @@ -874,7 +888,7 @@ def get_description(variableData, record, event_source, reglkey) end unless param_exe.nil? - buf.clear + @gdescriptionBuf1.clear va_list = va_list0.map{ |v| va = v @@ -893,40 +907,40 @@ def get_description(variableData, record, event_source, reglkey) ) if hmodule != 0 - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, x.first.to_i, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, v ) if res == 0 event_id = 0xB0000000 | x.first.to_i - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, event_id, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, nil ) else next end - break if buf.read_string.gsub(/\n+/, '') != "" + break if @gdescriptionBuf1.read_string.gsub(/\n+/, '') != "" end ensure FreeLibrary(hmodule) if hmodule && hmodule != 0 end } - va = va.gsub("%%#{x.first}", buf.read_string.gsub(/\n+/, '')) + va = va.gsub("%%#{x.first}", @gdescriptionBuf1.read_string.gsub(/\n+/, '')) } va @@ -934,7 +948,7 @@ def get_description(variableData, record, event_source, reglkey) end unless message_exe.nil? - buf.clear + @gdescriptionBuf1.clear # Try to retrieve message *without* expanding the inserts yet message_exe.split(';').each{ |lfile| @@ -954,32 +968,32 @@ def get_description(variableData, record, event_source, reglkey) event_id = record[:EventID] if hmodule != 0 - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, event_id, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, nil ) if res == 0 event_id = 0xB0000000 | event_id - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, event_id, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, nil ) end - #puts "message_exe#" + record[:RecordNumber].to_s + "buf:" + buf.read_string - break if buf.read_string != "" # All messages read + #puts "message_exe#" + record[:RecordNumber].to_s + "@gdescriptionBuf1:" + @gdescriptionBuf1.read_string + break if @gdescriptionBuf1.read_string != "" # All messages read end ensure FreeLibrary(hmodule) if hmodule && hmodule != 0 @@ -988,7 +1002,7 @@ def get_description(variableData, record, event_source, reglkey) # Determine higest %n insert number # Remove %% to fix: The %1 '%2' preference item in the '%3' Group Policy Object did not apply because it failed with error code '%4'%%100790273 - max_insert = [num, buf.read_string.gsub(/%%/, '').scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max + max_insert = [num, @gdescriptionBuf1.read_string.gsub(/%%/, '').scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max #puts "message_exe#" + record[:RecordNumber].to_s + "max_insert:" + max_insert.to_s # Insert dummy strings not provided by caller @@ -1028,44 +1042,45 @@ def get_description(variableData, record, event_source, reglkey) event_id = record[:EventID] if hmodule != 0 - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, event_id, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, va_list_ptr ) #puts "message_exe2#" + record[:RecordNumber].to_s + "res1:" + res.to_s if res == 0 event_id = 0xB0000000 | event_id - buf.clear + @gdescriptionBuf1.clear res = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hmodule, event_id, 0, - buf, - buf.size, + @gdescriptionBuf1, + @gdescriptionBuf1.size, va_list_ptr ) #puts "message_exe2#" + record[:RecordNumber].to_s + "res2:" + res.to_s end - #puts "message_exe2#" + record[:RecordNumber].to_s + "buf:" + buf.read_string(60) - break if buf.read_string != "" # All messages read + #puts "message_exe2#" + record[:RecordNumber].to_s + "@gdescriptionBuf1:" + @gdescriptionBuf1.read_string(60) + break if @gdescriptionBuf1.read_string != "" # All messages read end ensure FreeLibrary(hmodule) if hmodule && hmodule != 0 end } if num != 0 - strptrs.each_with_index{ |p, i| + strptrs.each{ |p| unless p.nil? p.free + p = nil end } va_list_ptr.free @@ -1073,14 +1088,11 @@ def get_description(variableData, record, event_source, reglkey) end end ensure - Wow64RevertWow64FsRedirection(old_wow_val.read_ulong) - old_wow_val.free - old_wow_val = nil + Wow64RevertWow64FsRedirection(@gdescriptionOldWowVal.read_ulong) end - resultstr = buf.read_string - buf.free - buf = nil + resultstr = @gdescriptionBuf1.read_string.force_encoding("Windows-1252") + va_list0.map! { |x| x.force_encoding("Windows-1252") } [va_list0, resultstr] end end diff --git a/lib/win32/windows/constants.rb b/lib/win32/windows/constants.rb index 428b6af..07c28d3 100644 --- a/lib/win32/windows/constants.rb +++ b/lib/win32/windows/constants.rb @@ -25,7 +25,7 @@ module Constants ERROR_SUCCESS = 0 ERROR_INSUFFICIENT_BUFFER = 122 - BUFFER_SIZE = 1024 * 64 + BUFFER_SIZE = 1024 * 64 - 1 MAX_SIZE = 512 MAX_STRINGS = 16 INFINITE = 0xFFFFFFFF