win32-eventlog 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +234 -226
- data/MANIFEST +19 -16
- data/README +78 -68
- data/Rakefile +6 -1
- data/doc/tutorial.txt +136 -136
- data/examples/example_notify.rb +23 -23
- data/examples/example_read.rb +83 -83
- data/examples/example_write.rb +64 -64
- data/lib/win32/eventlog.rb +1146 -1157
- data/lib/win32/mc.rb +120 -118
- data/lib/win32/windows/constants.rb +56 -0
- data/lib/win32/windows/functions.rb +51 -0
- data/lib/win32/windows/helper.rb +13 -0
- data/lib/win32/windows/structs.rb +30 -0
- data/misc/install_msg.rb +46 -46
- data/misc/rubymsg.mc +35 -35
- data/test/foo.mc +24 -24
- data/test/test_eventlog.rb +310 -319
- data/test/test_mc.rb +64 -67
- data/win32-eventlog.gemspec +30 -29
- metadata +90 -90
data/examples/example_notify.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
############################################################################
|
2
|
-
# example_notify.rb (win32-eventlog)
|
3
|
-
#
|
4
|
-
# A sample script for demonstrating the tail method. Start this in its own
|
5
|
-
# terminal. Then, in another terminal, write an entry to the event log
|
6
|
-
# (or force an entry via some other means) and watch the output.
|
7
|
-
#
|
8
|
-
# You can run this code via the 'example_notify' rake task.
|
9
|
-
############################################################################
|
10
|
-
Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
|
11
|
-
|
12
|
-
require 'win32/eventlog'
|
13
|
-
include Win32
|
14
|
-
|
15
|
-
log = EventLog.open
|
16
|
-
|
17
|
-
# replace 'tail' with 'notify_change' to see the difference
|
18
|
-
log.tail{ |struct|
|
19
|
-
puts "Got something"
|
20
|
-
p struct
|
21
|
-
puts
|
22
|
-
}
|
23
|
-
|
1
|
+
############################################################################
|
2
|
+
# example_notify.rb (win32-eventlog)
|
3
|
+
#
|
4
|
+
# A sample script for demonstrating the tail method. Start this in its own
|
5
|
+
# terminal. Then, in another terminal, write an entry to the event log
|
6
|
+
# (or force an entry via some other means) and watch the output.
|
7
|
+
#
|
8
|
+
# You can run this code via the 'example_notify' rake task.
|
9
|
+
############################################################################
|
10
|
+
Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
|
11
|
+
|
12
|
+
require 'win32/eventlog'
|
13
|
+
include Win32
|
14
|
+
|
15
|
+
log = EventLog.open
|
16
|
+
|
17
|
+
# replace 'tail' with 'notify_change' to see the difference
|
18
|
+
log.tail{ |struct|
|
19
|
+
puts "Got something"
|
20
|
+
p struct
|
21
|
+
puts
|
22
|
+
}
|
23
|
+
|
24
24
|
log.close
|
data/examples/example_read.rb
CHANGED
@@ -1,84 +1,84 @@
|
|
1
|
-
############################################################################
|
2
|
-
# example_read.rb (win32-eventlog)
|
3
|
-
#
|
4
|
-
# Test script for general futzing. This will attempt to read and backup
|
5
|
-
# your Event Log. You can run this example via the 'rake example_read'
|
6
|
-
# task.
|
7
|
-
#
|
8
|
-
# Modify as you see fit.
|
9
|
-
############################################################################
|
10
|
-
require 'win32/eventlog'
|
11
|
-
include Win32
|
12
|
-
|
13
|
-
puts "VERSION: " + EventLog::VERSION
|
14
|
-
sleep 1
|
15
|
-
|
16
|
-
# A few different ways to read an event log
|
17
|
-
|
18
|
-
el = EventLog.new("Application")
|
19
|
-
el.read{ |log|
|
20
|
-
p log
|
21
|
-
}
|
22
|
-
el.close
|
23
|
-
|
24
|
-
EventLog.read("Application"){ |log|
|
25
|
-
p log
|
26
|
-
puts
|
27
|
-
}
|
28
|
-
|
29
|
-
EventLog.open("Application") do |log|
|
30
|
-
log.read{ |struct|
|
31
|
-
p struct
|
32
|
-
puts
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
backup_file = "C:\\event_backup1"
|
37
|
-
File.delete(backup_file) if File.exists?(backup_file)
|
38
|
-
|
39
|
-
e1 = EventLog.open("System")
|
40
|
-
puts "System log opened"
|
41
|
-
|
42
|
-
e2 = EventLog.open("Application")
|
43
|
-
puts "Application log opened"
|
44
|
-
|
45
|
-
e3 = EventLog.open("Security")
|
46
|
-
puts "Security log opened"
|
47
|
-
|
48
|
-
puts "=" * 40
|
49
|
-
|
50
|
-
puts "Total system records: " + e1.total_records.to_s
|
51
|
-
puts "Total application records: " + e2.total_records.to_s
|
52
|
-
puts "Total security records: " + e3.total_records.to_s
|
53
|
-
|
54
|
-
puts "=" * 40
|
55
|
-
|
56
|
-
puts "Oldest system record number: " + e1.oldest_record_number.to_s
|
57
|
-
puts "Oldest application record number: " + e2.oldest_record_number.to_s
|
58
|
-
puts "Oldest security record number: " + e3.oldest_record_number.to_s
|
59
|
-
|
60
|
-
puts "=" * 40
|
61
|
-
|
62
|
-
e2.backup(backup_file)
|
63
|
-
puts "Application log backed up to #{backup_file}"
|
64
|
-
|
65
|
-
puts "=" * 40
|
66
|
-
|
67
|
-
e1.close
|
68
|
-
puts "System log closed"
|
69
|
-
|
70
|
-
e2.close
|
71
|
-
puts "Application log closed"
|
72
|
-
|
73
|
-
e3.close
|
74
|
-
puts "Security log closed"
|
75
|
-
|
76
|
-
e4 = EventLog.open_backup(backup_file)
|
77
|
-
e4.read{ |elr|
|
78
|
-
p elr
|
79
|
-
puts
|
80
|
-
}
|
81
|
-
puts "Finished reading backup file"
|
82
|
-
e4.close
|
83
|
-
|
1
|
+
############################################################################
|
2
|
+
# example_read.rb (win32-eventlog)
|
3
|
+
#
|
4
|
+
# Test script for general futzing. This will attempt to read and backup
|
5
|
+
# your Event Log. You can run this example via the 'rake example_read'
|
6
|
+
# task.
|
7
|
+
#
|
8
|
+
# Modify as you see fit.
|
9
|
+
############################################################################
|
10
|
+
require 'win32/eventlog'
|
11
|
+
include Win32
|
12
|
+
|
13
|
+
puts "VERSION: " + EventLog::VERSION
|
14
|
+
sleep 1
|
15
|
+
|
16
|
+
# A few different ways to read an event log
|
17
|
+
|
18
|
+
el = EventLog.new("Application")
|
19
|
+
el.read{ |log|
|
20
|
+
p log
|
21
|
+
}
|
22
|
+
el.close
|
23
|
+
|
24
|
+
EventLog.read("Application"){ |log|
|
25
|
+
p log
|
26
|
+
puts
|
27
|
+
}
|
28
|
+
|
29
|
+
EventLog.open("Application") do |log|
|
30
|
+
log.read{ |struct|
|
31
|
+
p struct
|
32
|
+
puts
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
backup_file = "C:\\event_backup1"
|
37
|
+
File.delete(backup_file) if File.exists?(backup_file)
|
38
|
+
|
39
|
+
e1 = EventLog.open("System")
|
40
|
+
puts "System log opened"
|
41
|
+
|
42
|
+
e2 = EventLog.open("Application")
|
43
|
+
puts "Application log opened"
|
44
|
+
|
45
|
+
e3 = EventLog.open("Security")
|
46
|
+
puts "Security log opened"
|
47
|
+
|
48
|
+
puts "=" * 40
|
49
|
+
|
50
|
+
puts "Total system records: " + e1.total_records.to_s
|
51
|
+
puts "Total application records: " + e2.total_records.to_s
|
52
|
+
puts "Total security records: " + e3.total_records.to_s
|
53
|
+
|
54
|
+
puts "=" * 40
|
55
|
+
|
56
|
+
puts "Oldest system record number: " + e1.oldest_record_number.to_s
|
57
|
+
puts "Oldest application record number: " + e2.oldest_record_number.to_s
|
58
|
+
puts "Oldest security record number: " + e3.oldest_record_number.to_s
|
59
|
+
|
60
|
+
puts "=" * 40
|
61
|
+
|
62
|
+
e2.backup(backup_file)
|
63
|
+
puts "Application log backed up to #{backup_file}"
|
64
|
+
|
65
|
+
puts "=" * 40
|
66
|
+
|
67
|
+
e1.close
|
68
|
+
puts "System log closed"
|
69
|
+
|
70
|
+
e2.close
|
71
|
+
puts "Application log closed"
|
72
|
+
|
73
|
+
e3.close
|
74
|
+
puts "Security log closed"
|
75
|
+
|
76
|
+
e4 = EventLog.open_backup(backup_file)
|
77
|
+
e4.read{ |elr|
|
78
|
+
p elr
|
79
|
+
puts
|
80
|
+
}
|
81
|
+
puts "Finished reading backup file"
|
82
|
+
e4.close
|
83
|
+
|
84
84
|
File.delete(backup_file)
|
data/examples/example_write.rb
CHANGED
@@ -1,65 +1,65 @@
|
|
1
|
-
##############################################################################
|
2
|
-
# example_write.rb
|
3
|
-
#
|
4
|
-
# Tests both the creation of an Event Log source and writing to the Event
|
5
|
-
# Log. You can run this via the 'rake example_write' task.
|
6
|
-
#
|
7
|
-
# Modify as you see fit.
|
8
|
-
##############################################################################
|
9
|
-
|
10
|
-
# Prompt user to continue, or not...
|
11
|
-
msg = <<TEXT
|
12
|
-
|
13
|
-
This script will create an event source 'foo' in your registry and
|
14
|
-
write an event to the 'Application' source.
|
15
|
-
Is that ok [y/N]?
|
16
|
-
TEXT
|
17
|
-
print msg
|
18
|
-
|
19
|
-
ans = STDIN.gets.chomp
|
20
|
-
unless ans == "y" || ans == "Y"
|
21
|
-
puts "Ok, exiting..."
|
22
|
-
exit!
|
23
|
-
end
|
24
|
-
|
25
|
-
require "win32/eventlog"
|
26
|
-
require "win32/mc"
|
27
|
-
include Win32
|
28
|
-
|
29
|
-
puts "EventLog VERSION: " + EventLog::VERSION
|
30
|
-
puts "MC VERSION: " + MC::VERSION
|
31
|
-
sleep 1
|
32
|
-
|
33
|
-
m = MC.new("foo.mc")
|
34
|
-
m.create_all
|
35
|
-
puts ".dll created"
|
36
|
-
sleep 1
|
37
|
-
|
38
|
-
dll_file = File.expand_path(m.dll_file)
|
39
|
-
|
40
|
-
EventLog.add_event_source(
|
41
|
-
'source' => "Application",
|
42
|
-
'key_name' => "foo",
|
43
|
-
'category_count' => 2,
|
44
|
-
'event_message_file' => dll_file,
|
45
|
-
'category_message_file' => dll_file
|
46
|
-
)
|
47
|
-
|
48
|
-
puts "Event source added to registry"
|
49
|
-
sleep 1
|
50
|
-
|
51
|
-
e1 = EventLog.open("Application")
|
52
|
-
|
53
|
-
e1.report_event(
|
54
|
-
:source => "foo",
|
55
|
-
:event_type => EventLog::WARN,
|
56
|
-
:category => "0x00000002L".hex,
|
57
|
-
:event_id => "0xC0000003L".hex,
|
58
|
-
:data => "Danger Will Robinson!"
|
59
|
-
)
|
60
|
-
|
61
|
-
puts "Event written to event log"
|
62
|
-
|
63
|
-
e1.close
|
64
|
-
|
1
|
+
##############################################################################
|
2
|
+
# example_write.rb
|
3
|
+
#
|
4
|
+
# Tests both the creation of an Event Log source and writing to the Event
|
5
|
+
# Log. You can run this via the 'rake example_write' task.
|
6
|
+
#
|
7
|
+
# Modify as you see fit.
|
8
|
+
##############################################################################
|
9
|
+
|
10
|
+
# Prompt user to continue, or not...
|
11
|
+
msg = <<TEXT
|
12
|
+
|
13
|
+
This script will create an event source 'foo' in your registry and
|
14
|
+
write an event to the 'Application' source.
|
15
|
+
Is that ok [y/N]?
|
16
|
+
TEXT
|
17
|
+
print msg
|
18
|
+
|
19
|
+
ans = STDIN.gets.chomp
|
20
|
+
unless ans == "y" || ans == "Y"
|
21
|
+
puts "Ok, exiting..."
|
22
|
+
exit!
|
23
|
+
end
|
24
|
+
|
25
|
+
require "win32/eventlog"
|
26
|
+
require "win32/mc"
|
27
|
+
include Win32
|
28
|
+
|
29
|
+
puts "EventLog VERSION: " + EventLog::VERSION
|
30
|
+
puts "MC VERSION: " + MC::VERSION
|
31
|
+
sleep 1
|
32
|
+
|
33
|
+
m = MC.new("foo.mc")
|
34
|
+
m.create_all
|
35
|
+
puts ".dll created"
|
36
|
+
sleep 1
|
37
|
+
|
38
|
+
dll_file = File.expand_path(m.dll_file)
|
39
|
+
|
40
|
+
EventLog.add_event_source(
|
41
|
+
'source' => "Application",
|
42
|
+
'key_name' => "foo",
|
43
|
+
'category_count' => 2,
|
44
|
+
'event_message_file' => dll_file,
|
45
|
+
'category_message_file' => dll_file
|
46
|
+
)
|
47
|
+
|
48
|
+
puts "Event source added to registry"
|
49
|
+
sleep 1
|
50
|
+
|
51
|
+
e1 = EventLog.open("Application")
|
52
|
+
|
53
|
+
e1.report_event(
|
54
|
+
:source => "foo",
|
55
|
+
:event_type => EventLog::WARN,
|
56
|
+
:category => "0x00000002L".hex,
|
57
|
+
:event_id => "0xC0000003L".hex,
|
58
|
+
:data => "Danger Will Robinson!"
|
59
|
+
)
|
60
|
+
|
61
|
+
puts "Event written to event log"
|
62
|
+
|
63
|
+
e1.close
|
64
|
+
|
65
65
|
puts "Finished. Exiting..."
|
data/lib/win32/eventlog.rb
CHANGED
@@ -1,1157 +1,1146 @@
|
|
1
|
-
require 'windows
|
2
|
-
require 'windows
|
3
|
-
require 'windows
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# The
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
#
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
#
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
#
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
end
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
#
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
end
|
1148
|
-
ensure
|
1149
|
-
if defined? Wow64RevertWow64FsRedirection
|
1150
|
-
Wow64RevertWow64FsRedirection(old_wow_val.unpack('L')[0])
|
1151
|
-
end
|
1152
|
-
end
|
1153
|
-
|
1154
|
-
[va_list0, buf.nstrip]
|
1155
|
-
end
|
1156
|
-
end
|
1157
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), 'windows', 'constants')
|
2
|
+
require File.join(File.dirname(__FILE__), 'windows', 'structs')
|
3
|
+
require File.join(File.dirname(__FILE__), 'windows', 'functions')
|
4
|
+
|
5
|
+
# The Win32 module serves as a namespace only.
|
6
|
+
module Win32
|
7
|
+
|
8
|
+
# The EventLog class encapsulates an Event Log source and provides methods
|
9
|
+
# for interacting with that source.
|
10
|
+
class EventLog
|
11
|
+
include Windows::Constants
|
12
|
+
include Windows::Structs
|
13
|
+
include Windows::Functions
|
14
|
+
extend Windows::Functions
|
15
|
+
|
16
|
+
# The EventLog::Error is raised in cases where interaction with the
|
17
|
+
# event log should happen to fail for any reason.
|
18
|
+
class Error < StandardError; end
|
19
|
+
|
20
|
+
# The version of the win32-eventlog library
|
21
|
+
VERSION = '0.6.0'
|
22
|
+
|
23
|
+
# The log is read in chronological order, i.e. oldest to newest.
|
24
|
+
FORWARDS_READ = EVENTLOG_FORWARDS_READ
|
25
|
+
|
26
|
+
# The log is read in reverse chronological order, i.e. newest to oldest.
|
27
|
+
BACKWARDS_READ = EVENTLOG_BACKWARDS_READ
|
28
|
+
|
29
|
+
# Begin reading from a specific record.
|
30
|
+
SEEK_READ = EVENTLOG_SEEK_READ
|
31
|
+
|
32
|
+
# Read the records sequentially. If this is the first read operation, the
|
33
|
+
# EVENTLOG_FORWARDS_READ or EVENTLOG_BACKWARDS_READ flags determines
|
34
|
+
# which record is read first.
|
35
|
+
SEQUENTIAL_READ = EVENTLOG_SEQUENTIAL_READ
|
36
|
+
|
37
|
+
# Event types
|
38
|
+
|
39
|
+
# Information event, an event that describes the successful operation
|
40
|
+
# of an application, driver or service.
|
41
|
+
SUCCESS = EVENTLOG_SUCCESS
|
42
|
+
|
43
|
+
# Error event, an event that indicates a significant problem such as
|
44
|
+
# loss of data or functionality.
|
45
|
+
ERROR_TYPE = EVENTLOG_ERROR_TYPE
|
46
|
+
|
47
|
+
# Warning event, an event that is not necessarily significant but may
|
48
|
+
# indicate a possible future problem.
|
49
|
+
WARN_TYPE = EVENTLOG_WARNING_TYPE
|
50
|
+
|
51
|
+
# Information event, an event that describes the successful operation
|
52
|
+
# of an application, driver or service.
|
53
|
+
INFO_TYPE = EVENTLOG_INFORMATION_TYPE
|
54
|
+
|
55
|
+
# Success audit event, an event that records an audited security attempt
|
56
|
+
# that is successful.
|
57
|
+
AUDIT_SUCCESS = EVENTLOG_AUDIT_SUCCESS
|
58
|
+
|
59
|
+
# Failure audit event, an event that records an audited security attempt
|
60
|
+
# that fails.
|
61
|
+
AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE
|
62
|
+
|
63
|
+
# The EventLogStruct encapsulates a single event log record.
|
64
|
+
EventLogStruct = Struct.new('EventLogStruct', :record_number,
|
65
|
+
:time_generated, :time_written, :event_id, :event_type, :category,
|
66
|
+
:source, :computer, :user, :string_inserts, :description
|
67
|
+
)
|
68
|
+
|
69
|
+
# The name of the event log source. This will typically be
|
70
|
+
# 'Application', 'System' or 'Security', but could also refer to
|
71
|
+
# a custom event log source.
|
72
|
+
#
|
73
|
+
attr_reader :source
|
74
|
+
|
75
|
+
# The name of the server which the event log is reading from.
|
76
|
+
#
|
77
|
+
attr_reader :server
|
78
|
+
|
79
|
+
# The name of the file used in the EventLog.open_backup method. This is
|
80
|
+
# set to nil if the file was not opened using the EventLog.open_backup
|
81
|
+
# method.
|
82
|
+
#
|
83
|
+
attr_reader :file
|
84
|
+
|
85
|
+
# Opens a handle to the new EventLog +source+ on +server+, or the local
|
86
|
+
# machine if no host is specified. Typically, your source will be
|
87
|
+
# 'Application, 'Security' or 'System', although you can specify a
|
88
|
+
# custom log file as well.
|
89
|
+
#
|
90
|
+
# If a custom, registered log file name cannot be found, the event
|
91
|
+
# logging service opens the 'Application' log file. This is the
|
92
|
+
# behavior of the underlying Windows function, not my own doing.
|
93
|
+
#
|
94
|
+
def initialize(source = 'Application', server = nil, file = nil)
|
95
|
+
@source = source || 'Application' # In case of explicit nil
|
96
|
+
@server = server
|
97
|
+
@file = file
|
98
|
+
|
99
|
+
# Avoid potential segfaults from win32-api
|
100
|
+
raise TypeError unless @source.is_a?(String)
|
101
|
+
raise TypeError unless @server.is_a?(String) if @server
|
102
|
+
|
103
|
+
if file.nil?
|
104
|
+
function = 'OpenEventLog'
|
105
|
+
@handle = OpenEventLog(@server, @source)
|
106
|
+
else
|
107
|
+
function = 'OpenBackupEventLog'
|
108
|
+
@handle = OpenBackupEventLog(@server, @file)
|
109
|
+
end
|
110
|
+
|
111
|
+
if @handle == 0
|
112
|
+
raise SystemCallError.new(function, FFI.errno)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Ensure the handle is closed at the end of a block
|
116
|
+
if block_given?
|
117
|
+
begin
|
118
|
+
yield self
|
119
|
+
ensure
|
120
|
+
close
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Class method aliases
|
126
|
+
class << self
|
127
|
+
alias :open :new
|
128
|
+
end
|
129
|
+
|
130
|
+
# Nearly identical to EventLog.open, except that the source is a backup
|
131
|
+
# file and not an event source (and there is no default).
|
132
|
+
#
|
133
|
+
def self.open_backup(file, source = 'Application', server = nil, &block)
|
134
|
+
@file = file
|
135
|
+
@source = source
|
136
|
+
@server = server
|
137
|
+
|
138
|
+
# Avoid potential segfaults from win32-api
|
139
|
+
raise TypeError unless @file.is_a?(String)
|
140
|
+
raise TypeError unless @source.is_a?(String)
|
141
|
+
raise TypeError unless @server.is_a?(String) if @server
|
142
|
+
|
143
|
+
self.new(source, server, file, &block)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Adds an event source to the registry. Returns the disposition, which
|
147
|
+
# is either REG_CREATED_NEW_KEY (1) or REG_OPENED_EXISTING_KEY (2).
|
148
|
+
#
|
149
|
+
# The following are valid keys:
|
150
|
+
#
|
151
|
+
# * source # Source name. Set to "Application" by default
|
152
|
+
# * key_name # Name stored as the registry key
|
153
|
+
# * category_count # Number of supported (custom) categories
|
154
|
+
# * event_message_file # File (dll) that defines events
|
155
|
+
# * category_message_file # File (dll) that defines categories
|
156
|
+
# * parameter_message_file # File (dll) that contains values for variables in the event description.
|
157
|
+
# * supported_types # See the 'event types' constants
|
158
|
+
#
|
159
|
+
# Of these keys, only +key_name+ is mandatory. An ArgumentError is
|
160
|
+
# raised if you attempt to use an invalid key. If +supported_types+
|
161
|
+
# is not specified then the following value is used:
|
162
|
+
#
|
163
|
+
# EventLog::ERROR_TYPE | EventLog::WARN_TYPE | EventLog::INFO_TYPE
|
164
|
+
#
|
165
|
+
# The +event_message_file+ and +category_message_file+ are typically,
|
166
|
+
# though not necessarily, the same file. See the documentation on .mc files
|
167
|
+
# for more details.
|
168
|
+
#
|
169
|
+
# You will need administrative privileges to use this method.
|
170
|
+
#
|
171
|
+
def self.add_event_source(args)
|
172
|
+
raise TypeError unless args.is_a?(Hash)
|
173
|
+
|
174
|
+
valid_keys = %w[
|
175
|
+
source
|
176
|
+
key_name
|
177
|
+
category_count
|
178
|
+
event_message_file
|
179
|
+
category_message_file
|
180
|
+
parameter_message_file
|
181
|
+
supported_types
|
182
|
+
]
|
183
|
+
|
184
|
+
key_base = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
|
185
|
+
|
186
|
+
# Default values
|
187
|
+
hash = {
|
188
|
+
'source' => 'Application',
|
189
|
+
'supported_types' => ERROR_TYPE | WARN_TYPE | INFO_TYPE
|
190
|
+
}
|
191
|
+
|
192
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
193
|
+
args.each{ |key, val|
|
194
|
+
key = key.to_s.downcase
|
195
|
+
unless valid_keys.include?(key)
|
196
|
+
raise ArgumentError, "invalid key '#{key}'"
|
197
|
+
end
|
198
|
+
hash[key] = val
|
199
|
+
}
|
200
|
+
|
201
|
+
# The key_name must be specified
|
202
|
+
unless hash['key_name']
|
203
|
+
raise ArgumentError, 'no event_type specified'
|
204
|
+
end
|
205
|
+
|
206
|
+
hkey = FFI::MemoryPointer.new(:uintptr_t)
|
207
|
+
disposition = FFI::MemoryPointer.new(:ulong)
|
208
|
+
|
209
|
+
key = key_base + hash['source']
|
210
|
+
|
211
|
+
rv = RegCreateKeyEx(
|
212
|
+
HKEY_LOCAL_MACHINE,
|
213
|
+
key,
|
214
|
+
0,
|
215
|
+
nil,
|
216
|
+
REG_OPTION_NON_VOLATILE,
|
217
|
+
KEY_WRITE,
|
218
|
+
nil,
|
219
|
+
hkey,
|
220
|
+
disposition
|
221
|
+
)
|
222
|
+
|
223
|
+
if rv != ERROR_SUCCESS
|
224
|
+
raise SystemCallError.new('RegCreateKeyEx', rv)
|
225
|
+
end
|
226
|
+
|
227
|
+
hkey = hkey.read_pointer.to_i
|
228
|
+
data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt"
|
229
|
+
|
230
|
+
begin
|
231
|
+
rv = RegSetValueEx(
|
232
|
+
hkey,
|
233
|
+
'File',
|
234
|
+
0,
|
235
|
+
REG_EXPAND_SZ,
|
236
|
+
data,
|
237
|
+
data.size
|
238
|
+
)
|
239
|
+
|
240
|
+
if rv != ERROR_SUCCESS
|
241
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
242
|
+
end
|
243
|
+
ensure
|
244
|
+
RegCloseKey(hkey)
|
245
|
+
end
|
246
|
+
|
247
|
+
hkey = FFI::MemoryPointer.new(:uintptr_t)
|
248
|
+
disposition = FFI::MemoryPointer.new(:ulong)
|
249
|
+
|
250
|
+
key = key_base << hash['source'] << "\\" << hash['key_name']
|
251
|
+
|
252
|
+
begin
|
253
|
+
rv = RegCreateKeyEx(
|
254
|
+
HKEY_LOCAL_MACHINE,
|
255
|
+
key,
|
256
|
+
0,
|
257
|
+
nil,
|
258
|
+
REG_OPTION_NON_VOLATILE,
|
259
|
+
KEY_WRITE,
|
260
|
+
nil,
|
261
|
+
hkey,
|
262
|
+
disposition
|
263
|
+
)
|
264
|
+
|
265
|
+
if rv != ERROR_SUCCESS
|
266
|
+
raise SystemCallError.new('RegCreateKeyEx', rv)
|
267
|
+
end
|
268
|
+
|
269
|
+
hkey = hkey.read_pointer.to_i
|
270
|
+
|
271
|
+
if hash['category_count']
|
272
|
+
data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['category_count'])
|
273
|
+
|
274
|
+
rv = RegSetValueEx(
|
275
|
+
hkey,
|
276
|
+
'CategoryCount',
|
277
|
+
0,
|
278
|
+
REG_DWORD,
|
279
|
+
data,
|
280
|
+
data.size
|
281
|
+
)
|
282
|
+
|
283
|
+
if rv != ERROR_SUCCESS
|
284
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
if hash['category_message_file']
|
289
|
+
data = File.expand_path(hash['category_message_file'])
|
290
|
+
data = FFI::MemoryPointer.from_string(data)
|
291
|
+
|
292
|
+
rv = RegSetValueEx(
|
293
|
+
hkey,
|
294
|
+
'CategoryMessageFile',
|
295
|
+
0,
|
296
|
+
REG_EXPAND_SZ,
|
297
|
+
data,
|
298
|
+
data.size
|
299
|
+
)
|
300
|
+
|
301
|
+
if rv != ERROR_SUCCESS
|
302
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
if hash['event_message_file']
|
307
|
+
data = File.expand_path(hash['event_message_file'])
|
308
|
+
data = FFI::MemoryPointer.from_string(data)
|
309
|
+
|
310
|
+
rv = RegSetValueEx(
|
311
|
+
hkey,
|
312
|
+
'EventMessageFile',
|
313
|
+
0,
|
314
|
+
REG_EXPAND_SZ,
|
315
|
+
data,
|
316
|
+
data.size
|
317
|
+
)
|
318
|
+
|
319
|
+
if rv != ERROR_SUCCESS
|
320
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
if hash['parameter_message_file']
|
325
|
+
data = File.expand_path(hash['parameter_message_file'])
|
326
|
+
data = FFI::MemoryPointer.from_string(data)
|
327
|
+
|
328
|
+
rv = RegSetValueEx(
|
329
|
+
hkey,
|
330
|
+
'ParameterMessageFile',
|
331
|
+
0,
|
332
|
+
REG_EXPAND_SZ,
|
333
|
+
data,
|
334
|
+
data.size
|
335
|
+
)
|
336
|
+
|
337
|
+
if rv != ERROR_SUCCESS
|
338
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
data = FFI::MemoryPointer.new(:ulong).write_ulong(hash['supported_types'])
|
343
|
+
|
344
|
+
rv = RegSetValueEx(
|
345
|
+
hkey,
|
346
|
+
'TypesSupported',
|
347
|
+
0,
|
348
|
+
REG_DWORD,
|
349
|
+
data,
|
350
|
+
data.size
|
351
|
+
)
|
352
|
+
|
353
|
+
if rv != ERROR_SUCCESS
|
354
|
+
raise SystemCallError.new('RegSetValueEx', rv)
|
355
|
+
end
|
356
|
+
ensure
|
357
|
+
RegCloseKey(hkey)
|
358
|
+
end
|
359
|
+
|
360
|
+
disposition.read_ulong
|
361
|
+
end
|
362
|
+
|
363
|
+
# Backs up the event log to +file+. Note that you cannot backup to
|
364
|
+
# a file that already exists or a Error will be raised.
|
365
|
+
#
|
366
|
+
def backup(file)
|
367
|
+
raise TypeError unless file.is_a?(String)
|
368
|
+
unless BackupEventLog(@handle, file)
|
369
|
+
raise SystemCallError.new('BackupEventLog', FFI.errno)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Clears the EventLog. If +backup_file+ is provided, it backs up the
|
374
|
+
# event log to that file first.
|
375
|
+
#
|
376
|
+
def clear(backup_file = nil)
|
377
|
+
raise TypeError unless backup_file.is_a?(String) if backup_file
|
378
|
+
|
379
|
+
unless ClearEventLog(@handle, backup_file)
|
380
|
+
raise SystemCallError.new('ClearEventLog', FFI.errno)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# Closes the EventLog handle. The handle is automatically closed for you
|
385
|
+
# if you use the block form of EventLog.new.
|
386
|
+
#
|
387
|
+
def close
|
388
|
+
CloseEventLog(@handle)
|
389
|
+
end
|
390
|
+
|
391
|
+
# Indicates whether or not the event log is full.
|
392
|
+
#
|
393
|
+
def full?
|
394
|
+
ptr = FFI::MemoryPointer.new(:ulong, 1)
|
395
|
+
needed = FFI::MemoryPointer.new(:ulong)
|
396
|
+
|
397
|
+
unless GetEventLogInformation(@handle, 0, ptr, ptr.size, needed)
|
398
|
+
raise SystemCallError.new('GetEventLogInformation', FFI.errno)
|
399
|
+
end
|
400
|
+
|
401
|
+
ptr.read_ulong != 0
|
402
|
+
end
|
403
|
+
|
404
|
+
# Returns the absolute record number of the oldest record. Note that
|
405
|
+
# this is not guaranteed to be 1 because event log records can be
|
406
|
+
# overwritten.
|
407
|
+
#
|
408
|
+
def oldest_record_number
|
409
|
+
rec = FFI::MemoryPointer.new(:ulong)
|
410
|
+
|
411
|
+
unless GetOldestEventLogRecord(@handle, rec)
|
412
|
+
raise SystemCallError.new('GetOldestEventLogRecord', FFI.errno)
|
413
|
+
end
|
414
|
+
|
415
|
+
rec.read_ulong
|
416
|
+
end
|
417
|
+
|
418
|
+
# Returns the total number of records for the given event log.
|
419
|
+
#
|
420
|
+
def total_records
|
421
|
+
total = FFI::MemoryPointer.new(:ulong)
|
422
|
+
|
423
|
+
unless GetNumberOfEventLogRecords(@handle, total)
|
424
|
+
raise SystemCallError.new('GetNumberOfEventLogRecords', FFI.errno)
|
425
|
+
end
|
426
|
+
|
427
|
+
total.read_ulong
|
428
|
+
end
|
429
|
+
|
430
|
+
# Yields an EventLogStruct every time a record is written to the event
|
431
|
+
# log. Unlike EventLog#tail, this method breaks out of the block after
|
432
|
+
# the event.
|
433
|
+
#
|
434
|
+
# Raises an Error if no block is provided.
|
435
|
+
#
|
436
|
+
def notify_change(&block)
|
437
|
+
unless block_given?
|
438
|
+
raise ArgumentError, 'block missing for notify_change'
|
439
|
+
end
|
440
|
+
|
441
|
+
# Reopen the handle because the NotifyChangeEventLog() function will
|
442
|
+
# choke after five or six reads otherwise.
|
443
|
+
@handle = OpenEventLog(@server, @source)
|
444
|
+
|
445
|
+
if @handle == 0
|
446
|
+
raise SystemCallError.new('OpenEventLog', FFI.errno)
|
447
|
+
end
|
448
|
+
|
449
|
+
event = CreateEvent(nil, false, false, nil)
|
450
|
+
|
451
|
+
unless NotifyChangeEventLog(@handle, event)
|
452
|
+
raise SystemCallError.new('NotifyChangeEventLog', FFI.errno)
|
453
|
+
end
|
454
|
+
|
455
|
+
wait_result = WaitForSingleObject(event, INFINITE)
|
456
|
+
|
457
|
+
begin
|
458
|
+
if wait_result == WAIT_FAILED
|
459
|
+
raise SystemCallError.new('WaitForSingleObject', FFI.errno)
|
460
|
+
else
|
461
|
+
last = read_last_event
|
462
|
+
block.call(last)
|
463
|
+
end
|
464
|
+
ensure
|
465
|
+
CloseHandle(event)
|
466
|
+
end
|
467
|
+
|
468
|
+
self
|
469
|
+
end
|
470
|
+
|
471
|
+
# Yields an EventLogStruct every time a record is written to the event
|
472
|
+
# log, once every +frequency+ seconds. Unlike EventLog#notify_change,
|
473
|
+
# this method does not break out of the block after the event. The read
|
474
|
+
# +frequency+ is set to 5 seconds by default.
|
475
|
+
#
|
476
|
+
# Raises an Error if no block is provided.
|
477
|
+
#
|
478
|
+
# The delay between reads is due to the nature of the Windows event log.
|
479
|
+
# It is not really designed to be tailed in the manner of a Unix syslog,
|
480
|
+
# for example, in that not nearly as many events are typically recorded.
|
481
|
+
# It's just not designed to be polled that heavily.
|
482
|
+
#
|
483
|
+
def tail(frequency = 5)
|
484
|
+
unless block_given?
|
485
|
+
raise ArgumentError, 'block missing for tail'
|
486
|
+
end
|
487
|
+
|
488
|
+
old_total = total_records()
|
489
|
+
flags = FORWARDS_READ | SEEK_READ
|
490
|
+
rec_num = read_last_event.record_number
|
491
|
+
|
492
|
+
while true
|
493
|
+
new_total = total_records()
|
494
|
+
if new_total != old_total
|
495
|
+
rec_num = oldest_record_number() if full?
|
496
|
+
read(flags, rec_num).each{ |log| yield log }
|
497
|
+
old_total = new_total
|
498
|
+
rec_num = read_last_event.record_number + 1
|
499
|
+
end
|
500
|
+
sleep frequency
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
# Iterates over each record in the event log, yielding a EventLogStruct
|
505
|
+
# for each record. The offset value is only used when used in
|
506
|
+
# conjunction with the EventLog::SEEK_READ flag. Otherwise, it is
|
507
|
+
# ignored. If no flags are specified, then the default flags are:
|
508
|
+
#
|
509
|
+
# EventLog::SEQUENTIAL_READ | EventLog::FORWARDS_READ
|
510
|
+
#
|
511
|
+
# Note that, if you're performing a SEEK_READ, then the offset must
|
512
|
+
# refer to a record number that actually exists. The default of 0
|
513
|
+
# may or may not work for your particular event log.
|
514
|
+
#
|
515
|
+
# The EventLogStruct struct contains the following members:
|
516
|
+
#
|
517
|
+
# * record_number # Fixnum
|
518
|
+
# * time_generated # Time
|
519
|
+
# * time_written # Time
|
520
|
+
# * event_id # Fixnum
|
521
|
+
# * event_type # String
|
522
|
+
# * category # String
|
523
|
+
# * source # String
|
524
|
+
# * computer # String
|
525
|
+
# * user # String or nil
|
526
|
+
# * description # String or nil
|
527
|
+
# * string_inserts # An array of Strings or nil
|
528
|
+
#
|
529
|
+
# If no block is given the method returns an array of EventLogStruct's.
|
530
|
+
#
|
531
|
+
def read(flags = nil, offset = 0)
|
532
|
+
buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE)
|
533
|
+
read = FFI::MemoryPointer.new(:ulong)
|
534
|
+
needed = FFI::MemoryPointer.new(:ulong)
|
535
|
+
array = []
|
536
|
+
lkey = HKEY_LOCAL_MACHINE
|
537
|
+
|
538
|
+
unless flags
|
539
|
+
flags = FORWARDS_READ | SEQUENTIAL_READ
|
540
|
+
end
|
541
|
+
|
542
|
+
if @server
|
543
|
+
hkey = FFI::MemoryPointer.new(:uintptr_t)
|
544
|
+
if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0
|
545
|
+
raise SystemCallError.new('RegConnectRegistry', FFI.errno)
|
546
|
+
end
|
547
|
+
lkey = hkey.read_pointer.to_i
|
548
|
+
end
|
549
|
+
|
550
|
+
while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) ||
|
551
|
+
FFI.errno == ERROR_INSUFFICIENT_BUFFER
|
552
|
+
|
553
|
+
if FFI.errno == ERROR_INSUFFICIENT_BUFFER
|
554
|
+
needed = needed.read_ulong / EVENTLOGRECORD.size
|
555
|
+
buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed)
|
556
|
+
unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed)
|
557
|
+
raise SystemCallError.new('ReadEventLog', FFI.errno)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
dwread = read.read_ulong
|
562
|
+
|
563
|
+
while dwread > 0
|
564
|
+
struct = EventLogStruct.new
|
565
|
+
record = EVENTLOGRECORD.new(buf)
|
566
|
+
|
567
|
+
event_source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/]
|
568
|
+
computer = buf.read_bytes(buf.size)[56 + event_source.length + 1..-1][/^[^\0]*/]
|
569
|
+
user = get_user(record)
|
570
|
+
|
571
|
+
strings, desc = get_description(buf, event_source, lkey)
|
572
|
+
|
573
|
+
struct.source = event_source
|
574
|
+
struct.computer = computer
|
575
|
+
struct.record_number = record[:RecordNumber]
|
576
|
+
struct.time_generated = Time.at(record[:TimeGenerated])
|
577
|
+
struct.time_written = Time.at(record[:TimeWritten])
|
578
|
+
struct.event_id = record[:EventID] & 0x0000FFFF
|
579
|
+
struct.event_type = get_event_type(record[:EventType])
|
580
|
+
struct.user = user
|
581
|
+
struct.category = record[:EventCategory]
|
582
|
+
struct.string_inserts = strings
|
583
|
+
struct.description = desc
|
584
|
+
|
585
|
+
struct.freeze # This is read-only information
|
586
|
+
|
587
|
+
if block_given?
|
588
|
+
yield struct
|
589
|
+
else
|
590
|
+
array.push(struct)
|
591
|
+
end
|
592
|
+
|
593
|
+
if flags & EVENTLOG_BACKWARDS_READ > 0
|
594
|
+
offset = record[:RecordNumber] - 1
|
595
|
+
else
|
596
|
+
offset = record[:RecordNumber] + 1
|
597
|
+
end
|
598
|
+
|
599
|
+
length = record[:Length]
|
600
|
+
|
601
|
+
dwread -= length
|
602
|
+
buf += length
|
603
|
+
end
|
604
|
+
|
605
|
+
buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE)
|
606
|
+
end
|
607
|
+
|
608
|
+
block_given? ? nil : array
|
609
|
+
end
|
610
|
+
|
611
|
+
# This class method is nearly identical to the EventLog#read instance
|
612
|
+
# method, except that it takes a +source+ and +server+ as the first two
|
613
|
+
# arguments.
|
614
|
+
#
|
615
|
+
def self.read(source='Application', server=nil, flags=nil, offset=0)
|
616
|
+
self.new(source, server){ |log|
|
617
|
+
if block_given?
|
618
|
+
log.read(flags, offset){ |els| yield els }
|
619
|
+
else
|
620
|
+
return log.read(flags, offset)
|
621
|
+
end
|
622
|
+
}
|
623
|
+
end
|
624
|
+
|
625
|
+
# Writes an event to the event log. The following are valid keys:
|
626
|
+
#
|
627
|
+
# * source # Event log source name. Defaults to "Application".
|
628
|
+
# * event_id # Event ID (defined in event message file).
|
629
|
+
# * category # Event category (defined in category message file).
|
630
|
+
# * data # String, or array of strings, that is written to the log.
|
631
|
+
# * event_type # Type of event, e.g. EventLog::ERROR_TYPE, etc.
|
632
|
+
#
|
633
|
+
# The +event_type+ keyword is the only mandatory keyword. The others are
|
634
|
+
# optional. Although the +source+ defaults to "Application", I
|
635
|
+
# recommend that you create an application specific event source and use
|
636
|
+
# that instead. See the 'EventLog.add_event_source' method for more
|
637
|
+
# details.
|
638
|
+
#
|
639
|
+
# The +event_id+ and +category+ values are defined in the message
|
640
|
+
# file(s) that you created for your application. See the tutorial.txt
|
641
|
+
# file for more details on how to create a message file.
|
642
|
+
#
|
643
|
+
# An ArgumentError is raised if you attempt to use an invalid key.
|
644
|
+
#
|
645
|
+
def report_event(args)
|
646
|
+
raise TypeError unless args.is_a?(Hash)
|
647
|
+
|
648
|
+
valid_keys = %w[source event_id category data event_type]
|
649
|
+
num_strings = 0
|
650
|
+
|
651
|
+
# Default values
|
652
|
+
hash = {
|
653
|
+
'source' => @source,
|
654
|
+
'event_id' => 0,
|
655
|
+
'category' => 0,
|
656
|
+
'data' => 0
|
657
|
+
}
|
658
|
+
|
659
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
660
|
+
args.each{ |key, val|
|
661
|
+
key = key.to_s.downcase
|
662
|
+
unless valid_keys.include?(key)
|
663
|
+
raise ArgumentError, "invalid key '#{key}'"
|
664
|
+
end
|
665
|
+
hash[key] = val
|
666
|
+
}
|
667
|
+
|
668
|
+
# The event_type must be specified
|
669
|
+
unless hash['event_type']
|
670
|
+
raise ArgumentError, 'no event_type specified'
|
671
|
+
end
|
672
|
+
|
673
|
+
handle = RegisterEventSource(@server, hash['source'])
|
674
|
+
|
675
|
+
if handle == 0
|
676
|
+
raise SystemCallError.new('RegisterEventSource', FFI.errno)
|
677
|
+
end
|
678
|
+
|
679
|
+
if hash['data'].is_a?(String)
|
680
|
+
strptrs = []
|
681
|
+
strptrs << FFI::MemoryPointer.from_string(hash['data'])
|
682
|
+
strptrs << nil
|
683
|
+
|
684
|
+
data = FFI::MemoryPointer.new(:pointer, strptrs.size)
|
685
|
+
|
686
|
+
strptrs.each_with_index do |p, i|
|
687
|
+
data[i].put_pointer(0, p)
|
688
|
+
end
|
689
|
+
|
690
|
+
num_strings = 1
|
691
|
+
elsif hash['data'].is_a?(Array)
|
692
|
+
strptrs = []
|
693
|
+
|
694
|
+
hash['data'].each{ |str|
|
695
|
+
strptrs << FFI::MemoryPointer.from_string(str)
|
696
|
+
}
|
697
|
+
|
698
|
+
strptrs << nil
|
699
|
+
data = FFI::MemoryPointer.new(:pointer, strptrs.size)
|
700
|
+
|
701
|
+
strptrs.each_with_index do |p, i|
|
702
|
+
data[i].put_pointer(0, p)
|
703
|
+
end
|
704
|
+
|
705
|
+
num_strings = hash['data'].size
|
706
|
+
else
|
707
|
+
data = nil
|
708
|
+
num_strings = 0
|
709
|
+
end
|
710
|
+
|
711
|
+
bool = ReportEvent(
|
712
|
+
handle,
|
713
|
+
hash['event_type'],
|
714
|
+
hash['category'],
|
715
|
+
hash['event_id'],
|
716
|
+
nil,
|
717
|
+
num_strings,
|
718
|
+
0,
|
719
|
+
data,
|
720
|
+
nil
|
721
|
+
)
|
722
|
+
|
723
|
+
unless bool
|
724
|
+
raise SystemCallError.new('ReportEvent', FFI.errno)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
alias :write :report_event
|
729
|
+
|
730
|
+
private
|
731
|
+
|
732
|
+
# A private method that reads the last event log record.
|
733
|
+
#
|
734
|
+
def read_last_event(handle=@handle, source=@source, server=@server)
|
735
|
+
buf = FFI::MemoryPointer.new(:char, BUFFER_SIZE)
|
736
|
+
read = FFI::MemoryPointer.new(:ulong)
|
737
|
+
needed = FFI::MemoryPointer.new(:ulong)
|
738
|
+
lkey = HKEY_LOCAL_MACHINE
|
739
|
+
|
740
|
+
flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ
|
741
|
+
|
742
|
+
unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed)
|
743
|
+
if FFI.errno == ERROR_INSUFFICIENT_BUFFER
|
744
|
+
needed = needed.read_ulong / EVENTLOGRECORD.size
|
745
|
+
buf = FFI::MemoryPointer.new(EVENTLOGRECORD, needed)
|
746
|
+
unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed)
|
747
|
+
raise SystemCallError.new('ReadEventLog', FFI.errno)
|
748
|
+
end
|
749
|
+
else
|
750
|
+
raise SystemCallError.new('ReadEventLog', FFI.errno)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
if @server
|
755
|
+
hkey = FFI::MemoryPointer.new(:uintptr_t)
|
756
|
+
if RegConnectRegistry(@server, HKEY_LOCAL_MACHINE, hkey) != 0
|
757
|
+
raise SystemCallError.new('RegConnectRegistry', FFI.errno)
|
758
|
+
end
|
759
|
+
lkey = hkey.read_pointer.to_i
|
760
|
+
end
|
761
|
+
|
762
|
+
record = EVENTLOGRECORD.new(buf)
|
763
|
+
|
764
|
+
event_source = buf.read_bytes(buf.size)[56..-1][/^[^\0]*/]
|
765
|
+
computer = buf.read_bytes(buf.size)[56 + event_source.length + 1..-1][/^[^\0]*/]
|
766
|
+
event_type = get_event_type(record[:EventType])
|
767
|
+
user = get_user(record)
|
768
|
+
strings, desc = get_description(buf, event_source, lkey)
|
769
|
+
|
770
|
+
struct = EventLogStruct.new
|
771
|
+
struct.source = event_source
|
772
|
+
struct.computer = computer
|
773
|
+
struct.record_number = record[:RecordNumber]
|
774
|
+
struct.time_generated = Time.at(record[:TimeGenerated])
|
775
|
+
struct.time_written = Time.at(record[:TimeWritten])
|
776
|
+
struct.event_id = record[:EventID] & 0x0000FFFF
|
777
|
+
struct.event_type = event_type
|
778
|
+
struct.user = user
|
779
|
+
struct.category = record[:EventCategory]
|
780
|
+
struct.string_inserts = strings
|
781
|
+
struct.description = desc
|
782
|
+
|
783
|
+
struct.freeze # This is read-only information
|
784
|
+
|
785
|
+
struct
|
786
|
+
end
|
787
|
+
|
788
|
+
# Private method that retrieves the user name based on data in the
|
789
|
+
# EVENTLOGRECORD buffer.
|
790
|
+
#
|
791
|
+
def get_user(rec)
|
792
|
+
return nil if rec[:UserSidLength] <= 0
|
793
|
+
|
794
|
+
name = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
795
|
+
domain = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
796
|
+
snu = FFI::MemoryPointer.new(:int)
|
797
|
+
|
798
|
+
name_size = FFI::MemoryPointer.new(:ulong)
|
799
|
+
domain_size = FFI::MemoryPointer.new(:ulong)
|
800
|
+
|
801
|
+
name_size.write_ulong(name.size)
|
802
|
+
domain_size.write_ulong(domain.size)
|
803
|
+
|
804
|
+
offset = rec[:UserSidOffset]
|
805
|
+
|
806
|
+
val = LookupAccountSid(
|
807
|
+
@server,
|
808
|
+
rec.pointer + offset,
|
809
|
+
name,
|
810
|
+
name_size,
|
811
|
+
domain,
|
812
|
+
domain_size,
|
813
|
+
snu
|
814
|
+
)
|
815
|
+
|
816
|
+
# Return nil if the lookup failed
|
817
|
+
return val ? name.read_string : nil
|
818
|
+
end
|
819
|
+
|
820
|
+
# Private method that converts a numeric event type into a human
|
821
|
+
# readable string.
|
822
|
+
#
|
823
|
+
def get_event_type(event)
|
824
|
+
case event
|
825
|
+
when EVENTLOG_ERROR_TYPE
|
826
|
+
'error'
|
827
|
+
when EVENTLOG_WARNING_TYPE
|
828
|
+
'warning'
|
829
|
+
when EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS
|
830
|
+
'information'
|
831
|
+
when EVENTLOG_AUDIT_SUCCESS
|
832
|
+
'audit_success'
|
833
|
+
when EVENTLOG_AUDIT_FAILURE
|
834
|
+
'audit_failure'
|
835
|
+
else
|
836
|
+
nil
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
# Private method that gets the string inserts (Array) and the full
|
841
|
+
# event description (String) based on data from the EVENTLOGRECORD
|
842
|
+
# buffer.
|
843
|
+
#
|
844
|
+
def get_description(buf, event_source, lkey)
|
845
|
+
rec = EVENTLOGRECORD.new(buf)
|
846
|
+
str = buf.read_bytes(buf.size)[rec[:StringOffset] .. -1]
|
847
|
+
num = rec[:NumStrings]
|
848
|
+
hkey = FFI::MemoryPointer.new(:uintptr_t)
|
849
|
+
key = BASE_KEY + "#{@source}\\#{event_source}"
|
850
|
+
buf = FFI::MemoryPointer.new(:char, 8192)
|
851
|
+
va_list = va_list0 = (num == 0) ? [] : str.unpack('Z*' * num)
|
852
|
+
|
853
|
+
begin
|
854
|
+
old_wow_val = FFI::MemoryPointer.new(:int)
|
855
|
+
Wow64DisableWow64FsRedirection(old_wow_val)
|
856
|
+
|
857
|
+
param_exe = nil
|
858
|
+
message_exe = nil
|
859
|
+
|
860
|
+
if RegOpenKeyEx(lkey, key, 0, KEY_READ, hkey) == 0
|
861
|
+
hkey = hkey.read_pointer.to_i
|
862
|
+
value = 'providerGuid'
|
863
|
+
|
864
|
+
guid = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
865
|
+
size = FFI::MemoryPointer.new(:ulong)
|
866
|
+
|
867
|
+
size.write_ulong(guid.size)
|
868
|
+
|
869
|
+
if RegQueryValueEx(hkey, value, nil, nil, guid, size) == 0
|
870
|
+
guid = guid.read_string
|
871
|
+
hkey2 = FFI::MemoryPointer.new(:uintptr_t)
|
872
|
+
key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\#{guid}"
|
873
|
+
|
874
|
+
if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0
|
875
|
+
hkey2 = hkey2.read_pointer.to_i
|
876
|
+
|
877
|
+
value = 'ParameterMessageFile'
|
878
|
+
file = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
879
|
+
size = FFI::MemoryPointer.new(:ulong)
|
880
|
+
|
881
|
+
size.write_ulong(file.size)
|
882
|
+
|
883
|
+
if RegQueryValueEx(hkey2, value, nil, nil, file, size) == 0
|
884
|
+
file = file.read_string
|
885
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
886
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
887
|
+
param_exe = exe.read_string
|
888
|
+
end
|
889
|
+
|
890
|
+
value = 'MessageFileName'
|
891
|
+
file = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
892
|
+
size = FFI::MemoryPointer.new(:ulong)
|
893
|
+
|
894
|
+
size.write_ulong(file.size)
|
895
|
+
|
896
|
+
if RegQueryValueEx(hkey2, value, nil, nil, file, size) == 0
|
897
|
+
file = file.read_string
|
898
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
899
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
900
|
+
message_exe = exe.read_string
|
901
|
+
end
|
902
|
+
|
903
|
+
RegCloseKey(hkey2)
|
904
|
+
end
|
905
|
+
else
|
906
|
+
value = 'ParameterMessageFile'
|
907
|
+
file = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
908
|
+
size = FFI::MemoryPointer.new(:ulong)
|
909
|
+
|
910
|
+
size.write_ulong(file.size)
|
911
|
+
|
912
|
+
if RegQueryValueEx(hkey, value, nil, nil, file, size) == 0
|
913
|
+
file = file.read_string
|
914
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
915
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
916
|
+
param_exe = exe.read_string
|
917
|
+
end
|
918
|
+
|
919
|
+
value = 'EventMessageFile'
|
920
|
+
file = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
921
|
+
size = FFI::MemoryPointer.new(:ulong)
|
922
|
+
|
923
|
+
size.write_ulong(file.size)
|
924
|
+
|
925
|
+
if RegQueryValueEx(hkey, value, nil, nil, file, size) == 0
|
926
|
+
file = file.read_string
|
927
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
928
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
929
|
+
message_exe = exe.read_string
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
RegCloseKey(hkey)
|
934
|
+
else
|
935
|
+
wevent_source = (event_source + 0.chr).encode('UTF-16LE')
|
936
|
+
|
937
|
+
begin
|
938
|
+
pubMetadata = EvtOpenPublisherMetadata(0, wevent_source, nil, 1024, 0)
|
939
|
+
|
940
|
+
if pubMetadata > 0
|
941
|
+
buf2 = FFI::MemoryPointer.new(:char, 8192)
|
942
|
+
val = FFI::MemoryPointer.new(:ulong)
|
943
|
+
|
944
|
+
bool = EvtGetPublisherMetadataProperty(
|
945
|
+
pubMetadata,
|
946
|
+
2, # EvtPublisherMetadataParameterFilePath
|
947
|
+
0,
|
948
|
+
buf2.size,
|
949
|
+
buf2,
|
950
|
+
val
|
951
|
+
)
|
952
|
+
|
953
|
+
unless bool
|
954
|
+
raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno)
|
955
|
+
end
|
956
|
+
|
957
|
+
file = buf2.read_string[16..-1]
|
958
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
959
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
960
|
+
param_exe = exe.read_string
|
961
|
+
|
962
|
+
buf2 = FFI::MemoryPointer.new(:char, 8192)
|
963
|
+
val = FFI::MemoryPointer.new(:ulong)
|
964
|
+
|
965
|
+
bool = EvtGetPublisherMetadataProperty(
|
966
|
+
pubMetadata,
|
967
|
+
3, # EvtPublisherMetadataMessageFilePath
|
968
|
+
0,
|
969
|
+
buf2.size,
|
970
|
+
buf2,
|
971
|
+
val
|
972
|
+
)
|
973
|
+
|
974
|
+
unless bool
|
975
|
+
raise SystemCallError.new('EvtGetPublisherMetadataProperty', FFI.errno)
|
976
|
+
end
|
977
|
+
|
978
|
+
file = buf2.read_string[16..-1]
|
979
|
+
exe = FFI::MemoryPointer.new(:char, MAX_SIZE)
|
980
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
981
|
+
message_exe = exe.read_string
|
982
|
+
end
|
983
|
+
ensure
|
984
|
+
EvtClose(pubMetadata) if pubMetadata
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
if param_exe != nil
|
989
|
+
va_list = va_list0.map{ |v|
|
990
|
+
va = v
|
991
|
+
|
992
|
+
v.scan(/%%(\d+)/).uniq.each{ |x|
|
993
|
+
param_exe.split(';').each{ |lfile|
|
994
|
+
hmodule = LoadLibraryEx(
|
995
|
+
lfile,
|
996
|
+
0,
|
997
|
+
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE
|
998
|
+
)
|
999
|
+
|
1000
|
+
if hmodule != 0
|
1001
|
+
res = FormatMessage(
|
1002
|
+
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
1003
|
+
hmodule,
|
1004
|
+
x.first.to_i,
|
1005
|
+
0,
|
1006
|
+
buf,
|
1007
|
+
buf.size,
|
1008
|
+
v
|
1009
|
+
)
|
1010
|
+
|
1011
|
+
if res == 0
|
1012
|
+
event_id = 0xB0000000 | event_id
|
1013
|
+
res = FormatMessage(
|
1014
|
+
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
|
1015
|
+
hmodule,
|
1016
|
+
event_id,
|
1017
|
+
0,
|
1018
|
+
buf,
|
1019
|
+
buf.size,
|
1020
|
+
nil
|
1021
|
+
)
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
FreeLibrary(hmodule)
|
1025
|
+
break if buf.nstrip != ""
|
1026
|
+
end
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
va = va.gsub("%%#{x.first}", buf.nstrip)
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
va
|
1033
|
+
}
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
if message_exe != nil
|
1037
|
+
buf = FFI::MemoryPointer.new(:char, 8192) # Reset the buffer
|
1038
|
+
|
1039
|
+
# Try to retrieve message *without* expanding the inserts yet
|
1040
|
+
message_exe.split(';').each{ |lfile|
|
1041
|
+
hmodule = LoadLibraryEx(
|
1042
|
+
lfile,
|
1043
|
+
0,
|
1044
|
+
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE
|
1045
|
+
)
|
1046
|
+
|
1047
|
+
event_id = rec[:EventID]
|
1048
|
+
|
1049
|
+
if hmodule != 0
|
1050
|
+
res = FormatMessage(
|
1051
|
+
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
|
1052
|
+
hmodule,
|
1053
|
+
event_id,
|
1054
|
+
0,
|
1055
|
+
buf,
|
1056
|
+
buf.size,
|
1057
|
+
nil
|
1058
|
+
)
|
1059
|
+
|
1060
|
+
if res == 0
|
1061
|
+
event_id = 0xB0000000 | event_id
|
1062
|
+
|
1063
|
+
res = FormatMessage(
|
1064
|
+
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
|
1065
|
+
hmodule,
|
1066
|
+
event_id,
|
1067
|
+
0,
|
1068
|
+
buf,
|
1069
|
+
buf.size,
|
1070
|
+
nil
|
1071
|
+
)
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
FreeLibrary(hmodule)
|
1075
|
+
break if buf.read_string != "" # All messages read
|
1076
|
+
end
|
1077
|
+
}
|
1078
|
+
|
1079
|
+
# Determine higest %n insert number
|
1080
|
+
max_insert = [num, buf.read_string.scan(/%(\d+)/).map{ |x| x[0].to_i }.max].compact.max
|
1081
|
+
|
1082
|
+
# Insert dummy strings not provided by caller
|
1083
|
+
((num+1)..(max_insert)).each{ |x| va_list.push("%#{x}") }
|
1084
|
+
|
1085
|
+
if num == 0
|
1086
|
+
va_list_ptr = FFI::MemoryPointer.new(:pointer)
|
1087
|
+
else
|
1088
|
+
strptrs = []
|
1089
|
+
va_list.each{ |x| strptrs << FFI::MemoryPointer.from_string(x) }
|
1090
|
+
strptrs << nil
|
1091
|
+
|
1092
|
+
va_list_ptr = FFI::MemoryPointer.new(:pointer, strptrs.size)
|
1093
|
+
|
1094
|
+
strptrs.each_with_index{ |p, i|
|
1095
|
+
va_list_ptr[i].put_pointer(0, p)
|
1096
|
+
}
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
message_exe.split(';').each{ |lfile|
|
1100
|
+
hmodule = LoadLibraryEx(
|
1101
|
+
lfile,
|
1102
|
+
0,
|
1103
|
+
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE
|
1104
|
+
)
|
1105
|
+
|
1106
|
+
event_id = rec[:EventID]
|
1107
|
+
|
1108
|
+
if hmodule != 0
|
1109
|
+
res = FormatMessage(
|
1110
|
+
FORMAT_MESSAGE_FROM_HMODULE |
|
1111
|
+
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
1112
|
+
hmodule,
|
1113
|
+
event_id,
|
1114
|
+
0,
|
1115
|
+
buf,
|
1116
|
+
buf.size,
|
1117
|
+
va_list_ptr
|
1118
|
+
)
|
1119
|
+
|
1120
|
+
if res == 0
|
1121
|
+
event_id = 0xB0000000 | event_id
|
1122
|
+
|
1123
|
+
res = FormatMessage(
|
1124
|
+
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
1125
|
+
hmodule,
|
1126
|
+
event_id,
|
1127
|
+
0,
|
1128
|
+
buf,
|
1129
|
+
buf.size,
|
1130
|
+
va_list_ptr
|
1131
|
+
)
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
FreeLibrary(hmodule)
|
1135
|
+
break if buf.read_string != "" # All messages read
|
1136
|
+
end
|
1137
|
+
}
|
1138
|
+
end
|
1139
|
+
ensure
|
1140
|
+
Wow64RevertWow64FsRedirection(old_wow_val.read_ulong)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
[va_list0, buf.read_string]
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
end
|