-
Notifications
You must be signed in to change notification settings - Fork 57
/
Analyze.py
366 lines (331 loc) · 15.3 KB
/
Analyze.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
# MemoryDump is the file we are going to analyze
# Walk detected page tables, logical OS EPROC and more through dynamic language runtime (IronPython)
# Automagically works for XEN, VMWARE, many .DMP versions and RAW!
#
# NO PROFILE CONFIG OR ANYTHING FOR ALL WINDOWS OS!
#
# YOU DO NEED TO HAVE SYMBOLS CONFIGURED.
# IF A GUID IS NOT FOUND, FIND IT MAY BE ON THE SYM CD WHICH IS NOT ON THE SERVER
#
# DLR reflected DIA symbols through physical memory
# make sure to have dia registered do "regsvr32 c:\\windows\system32\msdia140.dll"
# you also want symsrv.dll and dbghelp.dll in the current folder =)
#
# Play with the PTType and stuff for nested hypervisors =) (PTTYPE VMCS)
#
# NtBuildNumber == kernel version
#
import clr,sys
import System
clr.AddReferenceToFileAndPath("inVtero.net.dll")
clr.AddReferenceToFileAndPath("inVtero.net.ConsoleUtils.dll")
from inVtero.net import *
from inVtero.net.ConsoleUtils import *
from inVtero.net.Hashing import *
from System.IO import Directory, File, FileInfo, Path
from System import Environment, String, Console, ConsoleColor
from System import Text
from System.Diagnostics import Stopwatch
from List import *
print "\n\n\tCurrent directory [" + Directory.GetCurrentDirectory() + "]"
# This script can be pretty chatty to stdout, configure various output here
Vtero.VerboseOutput = True
Vtero.DiagOutput = False
Vtero.VerboseLevel = 1
Vtero.DisableProgressBar = True
# More option handling for each file
copts = ConfigOptions()
copts.IgnoreSaveData = True
copts.VersionsToEnable = PTType.GENERIC
copts.VerboseLevel = 1
# in the case of some dump tools (ENCASE) use this option
#copts.ForceSingleFlatMemRun = True
# This code fragment can be removed but it's a reminder you need symbols working
sympath = Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH")
if String.IsNullOrWhiteSpace(sympath):
sympath = "SRV*http://msdl.microsoft.com/download/symbols"
hwmiss = []
def ScanDump(MemoryDump, copts):
MemoryDumpSize = FileInfo(MemoryDump).Length
copts.FileName = MemoryDump
# Check StopWatch
runTime = Stopwatch.StartNew()
# since we are not ignoring SaveData, this just get's our state from
# the underlying protobuf, pretty fast
vtero = Scan.Scanit(copts)
proc_arr = vtero.Processes.ToArray()
low_proc = proc_arr[0]
for proc in proc_arr:
if proc.CR3Value < low_proc.CR3Value:
low_proc = proc
proc = low_proc
print "Assumed Kernel Proc: " + proc.ToString()
vtero.KernelProc = proc
#vtero.CheckpointSaveState()
proc.MemAccess = Mem(vtero.MemAccess)
#swModScan = Stopwatch.StartNew()
# by default this will scan for kernel symbols
if vtero.KVS is None:
kvs = proc.ScanAndLoadModules()
vtero.KVS = kvs
vtero.CheckpointSaveState()
else:
proc.LoadSymbols()
#apply some setup
kMinorVer = proc.GetSymValueLong("NtBuildNumber") & 0xffff
Console.ForegroundColor = ConsoleColor.Cyan
print "kernel build: " + kMinorVer.ToString()
# Use dynamic typing to walk EPROCES
logicalList = vtero.WalkProcList(proc)
print "Physical Proc Count: " + proc_arr.Count.ToString()
#for pproc in proc_arr:
# print pproc
if logicalList is not None:
print "Logical Proc Count: " + logicalList.Count.ToString()
for proc in logicalList:
# This is due to a structure member name change pre win 8
if proc.Dictionary.ContainsKey("VadRoot.BalancedRoot.RightChild"):
proc.VadRoot = proc.Dictionary["VadRoot.BalancedRoot.RightChild"]
print proc.ImagePath + " : " + proc.Dictionary["Pcb.DirectoryTableBase"].ToString("X") + " : " + proc.VadRoot.ToString("X") + " : " + proc.UniqueProcessId.ToString("X")
Console.ForegroundColor = ConsoleColor.Green
print "checking that all logical processes exist in the physical list."
# Miss list mostly bad for yellow printing
for proc in logicalList:
found=False
for hwproc in proc_arr:
if proc.Dictionary["Pcb.DirectoryTableBase"] == hwproc.CR3Value:
found=True
#print "Found logical proc[" + hwproc.CR3Value.ToString("X") + "] in physical array"
if found == False:
Console.ForegroundColor = ConsoleColor.Yellow
if proc.VadRoot == 0:
Console.ForegroundColor = ConsoleColor.Green
print "An expected, ",
print "Logical miss for " + proc.ImagePath + " : " + proc.Dictionary["Pcb.DirectoryTableBase"].ToString("X") + " : " + proc.VadRoot.ToString("X") + " : " + proc.UniqueProcessId.ToString("X")
print "Checking that all physical processes exist in the logical list"
for hwproc in proc_arr:
found=False
for proc in logicalList:
if proc.Dictionary["Pcb.DirectoryTableBase"] == hwproc.CR3Value:
found=True
#print "Found physical proc[" + proc.Dictionary["Pcb.DirectoryTableBase"].ToString("X") + "] in logical array"
if found == False:
Console.ForegroundColor = ConsoleColor.Yellow
hwmiss.append(hwproc)
print "physical miss for " + hwproc.ToString()
Console.ForegroundColor = ConsoleColor.White
print "PART RUNTIME: " + runTime.Elapsed.ToString() + " (seconds), INPUT DUMP SIZE: " + MemoryDumpSize.ToString("N") + " bytes."
print "SPEED: " + ((MemoryDumpSize / 1024) / ((runTime.ElapsedMilliseconds / 1000)+1)).ToString("N0") + " KB / second (all phases aggregate time)"
return vtero
# This one is recursive down a tree
def ListVAD(proc, VadRoot):
if VadRoot == 0:
return
#pMMVadArr = proc.GetVirtualLong(VadRoot)
mmvad = proc.xStructInfo("_MMVAD", VadRoot)
IsExec = False
# This is to support 7 and earlier kernels
if mmvad.Dictionary.ContainsKey("Core"):
if mmvad.Core.u.VadFlags.Protection.Value & 2 != 0:
IsExec = True
else:
# TODO: Double check this one!
if mmvad.u.VadFlags.Protection.Value & 2 != 0:
IsExec = True
# Check VAD Flags for execute permission before we spend time looking at this entry
if IsExec:
subsect = proc.xStructInfo("_SUBSECTION", mmvad.Subsection.Value)
control_area = proc.xStructInfo("_CONTROL_AREA", subsect.ControlArea.Value)
#segment = proc.xStructInfo("_SEGMENT", control_area.Segment.Value)
# look for file pointer
if control_area.FilePointer.Value != 0:
file_pointer = proc.xStructInfo("_FILE_OBJECT", control_area.FilePointer.Value & -16)
print "Mapped File: " + file_pointer.FileName.Value
else:
print "Mapped anonymous memory " + mmvad.Core.StartingVpn.Value.ToString("x")
# Core is the more recent kernels
if mmvad.Dictionary.ContainsKey("Core"):
ListVAD(proc, mmvad.Core.VadNode.Left.Value)
ListVAD(proc, mmvad.Core.VadNode.Right.Value)
else:
ListVAD(proc, mmvad.LeftChild.Value)
ListVAD(proc, mmvad.RightChild.Value)
# Here we walk another LIST_ENTRY
def WalkETHREAD(proc, eThreadHead):
typedef = proc.xStructInfo("_ETHREAD")
ThreadOffsetOf = typedef.ThreadListEntry.OffsetPos
_ETHR_ADDR = eThreadHead
print "top reading thread " + _ETHR_ADDR.ToString("x") + "-" + ThreadOffsetOf.ToString("x") + "=" + (_ETHR_ADDR - ThreadOffsetOf).ToString("X")
_ETHREAD = proc.xStructInfo("_ETHREAD", _ETHR_ADDR - ThreadOffsetOf)
while True:
print "Thread [" + _ETHREAD.Cid.UniqueThread.Value.ToString("X") + "] BASE",
print "[0x" + _ETHREAD.Tcb.StackBase.Value.ToString("X") + "] LIMIT [0x" + _ETHREAD.Tcb.StackLimit.Value.ToString("X") + "]"
_ETHR_ADDR = _ETHREAD.ThreadListEntry.Value
if _ETHR_ADDR == eThreadHead:
return
_ETHREAD = proc.xStructInfo("_ETHREAD", _ETHR_ADDR - ThreadOffsetOf)
print "top reading thread " + _ETHR_ADDR.ToString("x") + "-" + ThreadOffsetOf.ToString("x") + "=" + (_ETHR_ADDR - ThreadOffsetOf).ToString("X")
def WalkModules(proc, ModLinkHead, Verbose):
modlist = []
_LDR_DATA_ADDR = ModLinkHead
while True:
_LDR_DATA = proc.xStructInfo("_LDR_DATA_TABLE_ENTRY", _LDR_DATA_ADDR)
if Verbose:
print "Loaded Base: 0x" + _LDR_DATA.DllBase.Value.ToString("x") + " EntryPoint: 0x" + _LDR_DATA.EntryPoint.Value.ToString("x") + " Length: 0x" + _LDR_DATA.SizeOfImage.Value.ToString("x8") + " \t Module: " + _LDR_DATA.FullDllName.Value
modlist.append(_LDR_DATA)
_LDR_DATA_ADDR = _LDR_DATA.InLoadOrderLinks.Flink.Value
if _LDR_DATA_ADDR == ModLinkHead:
return modlist
def WalkList(proc, type, ptr, head, offsetOf, typeLen):
while ptr != head:
if ptr == None or ptr == 0:
ptr = head
yield proc.xStructInfo(type, ptr - offsetOf, typeLen)
# Walk registry hives
def hives(proc):
h = WalkList(p, "_CMHIVE", _HIVE_HEAD_ADDR, hiveOffsetOf)
for x in h:
print "HiveRootPath: " + x.Obj.HiveRootPath.Value
print "FileUserName: " + x.Obj.FileUserName.Value
# YaraRules = "c:\\temp\\yara\\index.yar"
# Do a parallel Yara Scan
def YaraAll(YaraRules, vtero):
Vtero.VerboseLevel = 1
Vtero.DiagOutput = False
dumptime = Stopwatch.StartNew()
yall = vtero.YaraAll(YaraRules, True, False)
print "elapsed " + dumptime.Elapsed.ToString()
return yall
def QuickSetup(MemoryDump, IgnoreSave = False):
# Basic option handling
copts = ConfigOptions()
copts.IgnoreSaveData = IgnoreSave
copts.FileName = MemoryDump
copts.VersionsToEnable = PTType.GENERIC
copts.VerboseOutput = False
copts.VerboseLevel = 0
# Vtero options are global
Vtero.DiagOutput = False
Vtero.VerboseLevel = 0
# perform full page scan
# this scans the input in it's entirety and set's up
# the basis for traversing memory appropiatly as the CPU would
# through the page table (including nested)
vtero = Scan.Scanit(copts)
proc_arr = vtero.Processes.ToArray()
low_proc = proc_arr[0]
for proc in proc_arr:
if proc.CR3Value < low_proc.CR3Value:
low_proc = proc
proc = low_proc
print "Assumed Kernel Proc: " + proc.ToString()
vtero.KernelProc = proc
proc.MemAccess = Mem(vtero.MemAccess)
# Here we bring up symbol support to the Windows Kernel
# if we have a save state we skip the scan and directly load it
if vtero.KVS is None:
kvs = proc.ScanAndLoadModules()
vtero.KVS = kvs
else:
proc.LoadSymbols()
# having the kernel build info displayed mean's were good to go
kMinorVer = proc.GetSymValueLong("NtBuildNumber") & 0xffff
Console.ForegroundColor = ConsoleColor.Cyan
print "Kernel build: " + kMinorVer.ToString()
vtero.MemAccess.MapViewSize = 128 * 1024
logicalList = vtero.WalkProcList(vtero.KernelProc)
vtero.KernelProc.InitSymbolsForVad()
return vtero
#########################################################################
# search symbols like this
# SymList(proc, "*POOL*")
# pretty print symbols matching string
#########################################################################
def SymList(proc, MatchString):
for match in proc.MatchSymbols(MatchString):
print match.Item1 + " @ 0x" + match.Item2.ToString("x")
def dt(dynObj):
print dynObj.TypeName,
print " len: 0x" + dynObj.Length.ToString("x")
for member in dynObj.Dictionary.Keys:
pyMember = getattr(dynObj, member, None)
print " +0x" + pyMember.OffsetPos.ToString("x"),
currName = pyMember.MemberName.split(".")
print currName[currName.Count-1] + "\t\t:",
bitPos = getattr(pyMember, "BitPosition", None)
bitCnt = getattr(pyMember, "BitCount", None)
if bitPos is not None:
print "Pos " + bitPos.ToString() + ", " + bitCnt.ToString() + " Bits",
prop = getattr(pyMember, "IsPtr", None)
if prop:
print "*",
ptrType = getattr(pyMember, "PtrTypeName", None)
if ptrType is not None:
print ptrType,
arrCnt = getattr(pyMember, "ArrayCount", None)
if arrCnt is not None:
print "[" + arrCnt.ToString() + "] ",
arrType = getattr(pyMember, "ArrayMemberType", None)
if arrType is not None:
print arrType,
print " member len(" + pyMember.ArrayMemberLen.ToString() + ")",
typName = getattr(pyMember, "TypeName", None)
if typName is not None:
print typName,
value = getattr(pyMember, "ConstValue", None)
if value is not None:
print "0x" + value.ToString("x"),
print " len(0x" + pyMember.Length.ToString("x") + ")",
if dynObj.Dictionary[member] is not None:
print " = 0x" + dynObj.Dictionary[member].ToString("x")
else:
print " "
# Example of walking process list
def WalkProcListExample(proc):
#
# WALK KERNEL
#
print "Walking Kernel modules..."
pModuleHead = proc.GetSymValueLong("PsLoadedModuleList")
WalkModules(proc, pModuleHead, True)
# Get a typedef
print "Walking Kernel processes..."
x = proc.xStructInfo("_EPROCESS")
ProcListOffsetOf = x.ActiveProcessLinks.Flink.OffsetPos
ImagePath = ""
psHead = proc.GetSymValueLong("PsActiveProcessHead")
_EPROC = proc.xStructInfo("_EPROCESS", psHead - ProcListOffsetOf)
while True:
# Deeply hidden value in _EPROCESS to find full path!! ;)
ImagePath = _EPROC.SeAuditProcessCreationInfo.ImageFileName.Name.Value
print "Process ID [" + _EPROC.UniqueProcessId.Value.ToString("X") + "] EXE [" + ImagePath + "]",
# Nice to see members
print " CR3/DTB [" + _EPROC.Pcb.DirectoryTableBase.Value.ToString("X") + "] VADROOT [" + _EPROC.VadRoot.Value.ToString("X") + "]"
# Walk VAD & look for +X file mapped entries
if _EPROC.VadRoot.Value != 0:
ListVAD(proc, _EPROC.VadRoot.Value)
# Depending on Thr Count this takes too long
#if _EPROC.ThreadListHead.Value != 0:
# WalkETHREAD(proc, _EPROC.ThreadListHead.Value)
# We read this 2x, simply readability ? =)
_EPROC_ADDR = _EPROC.ActiveProcessLinks.Flink.Value
if _EPROC_ADDR == psHead:
return
_EPROC = proc.xStructInfo("_EPROCESS", _EPROC_ADDR - ProcListOffsetOf)
vtero = None
def test(MemList):
TotalRunTime = Stopwatch.StartNew()
TotalSizeAnalyzed = 0
for MemoryDump in MemList:
print " ++++++++++++++++++++++++++++++ ANALYZING INPUT [" + MemoryDump + "] ++++++++++++++++++++++++++++++ "
if not File.Exists(MemoryDump):
print "Can not find dump to analyze: " + MemoryDump
continue
copts.FileName = MemoryDump
vtero = ScanDump(MemoryDump, copts)
TotalSizeAnalyzed += vtero.FileSize
print " ++++++++++++++++++++++++++++++ DONE WITH INPUT [" + MemoryDump + "] ++++++++++++++++++++++++++++++ "
print " ++++++++++++++++++++++++++++++ ALL DONE... Please explore! ++++++++++++++++++++++++++++++"
print "TOTAL RUNTIME: " + TotalRunTime.Elapsed.ToString() + " (seconds), TOTAL DATA ANALYZED: " + TotalSizeAnalyzed.ToString("N") + " bytes."
print "SPEED: " + ((TotalSizeAnalyzed / 1024) / ((TotalRunTime.ElapsedMilliseconds / 1000)+1)).ToString("N0") + " KB / second (all phases aggregate time)"
return vtero