diff --git a/pkg/callstack/callstack.go b/pkg/callstack/callstack.go index 051db7c58..a60ae1b4d 100644 --- a/pkg/callstack/callstack.go +++ b/pkg/callstack/callstack.go @@ -169,19 +169,28 @@ func (s *Callstack) Depth() int { return len(*s) } // IsEmpty returns true if the callstack has no frames. func (s *Callstack) IsEmpty() bool { return s.Depth() == 0 } -// FinalUserFrame returns the final user space frame. +// FinalUserFrame returns the final frame that corresponds +// to the user code execution. That usually translates to +// the last frame before ntdll or kernel32 modules. func (s *Callstack) FinalUserFrame() *Frame { - var i int if s.IsEmpty() { return nil } - for ; i < s.Depth()-1 && !(*s)[i].Addr.InSystemRange(); i++ { + var n int + for n = s.Depth() - 1; n > 0; n-- { + f := (*s)[n] + if f.Addr.InSystemRange() { + continue + } + mod := filepath.Base(strings.ToLower(f.Module)) + if mod != "ntdll.dll" && mod != "kernel32.dll" && mod != "kernelbase.dll" { + break + } } - i-- - if i > 0 && i < s.Depth()-1 { - return &(*s)[i] + if n >= 0 && n < s.Depth()-1 { + return &(*s)[n] } return nil diff --git a/pkg/callstack/callstack_test.go b/pkg/callstack/callstack_test.go index 664fc9909..5ac3e4b48 100644 --- a/pkg/callstack/callstack_test.go +++ b/pkg/callstack/callstack_test.go @@ -47,9 +47,9 @@ func TestCallstack(t *testing.T) { uframe := callstack.FinalUserFrame() require.NotNil(t, uframe) - assert.Equal(t, "7ffb5c1d0396", uframe.Addr.String()) - assert.Equal(t, "CreateProcessW", uframe.Symbol) - assert.Equal(t, "C:\\WINDOWS\\System32\\KERNELBASE.dll", uframe.Module) + assert.Equal(t, "7ffb3138592e", uframe.Addr.String()) + assert.Equal(t, "Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly", uframe.Symbol) + assert.Equal(t, "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll", uframe.Module) kframe := callstack.FinalKernelFrame() require.NotNil(t, kframe) @@ -57,3 +57,69 @@ func TestCallstack(t *testing.T) { assert.Equal(t, "ObDeleteCapturedInsertInfo", kframe.Symbol) assert.Equal(t, "C:\\WINDOWS\\system32\\ntoskrnl.exe", kframe.Module) } + +func TestCallstackFinalUserFrame(t *testing.T) { + var tests = []struct { + callstack Callstack + expectedMod string + expectedSym string + }{ + {callstack: callstackFromFrames( + Frame{Addr: 0xf259de, Module: unbacked, Symbol: "?"}, + Frame{Addr: 0x7ffe4fda6e3b, Module: "C:\\Windows\\System32\\KernelBase.dll", Symbol: "SetThreadContext"}, + Frame{Addr: 0x7ffe52942b24, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "ZwSetContextThread"}, + Frame{Addr: 0xfffff807e228c555, Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe", Symbol: "setjmpex"}, + Frame{Addr: 0xfffff807e264805c, Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe", Symbol: "ObOpenObjectByPointerWithTag"}), + expectedMod: "unbacked", + expectedSym: "?", + }, + {callstack: callstackFromFrames( + Frame{Addr: 0x7ffff0f3bf6c, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "RtlUserThreadStart"}, + Frame{Addr: 0x7ffff03ee8d7, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "BaseThreadInitThunk"}, + Frame{Addr: 0x7ffff0ee5f13, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "TpCallbackMayRunLong"}, + Frame{Addr: 0x7ffff0c78788, Module: "C:\\Windows\\System32\\rpcrt4.dll", Symbol: "RpcGetBufferWithObject"}, + Frame{Addr: 0x7ffff0c797e3, Module: "C:\\Windows\\System32\\rpcrt4.dll", Symbol: "RpcImpersonateClient"}, + Frame{Addr: 0x7fffee58d16a, Module: "C:\\Windows\\System32\\KernelBase.dll", Symbol: "CreateProcessInternalW"}, + Frame{Addr: 0x7ffff0fe1204, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "ZwCreateUserProcess"}), + expectedMod: "C:\\Windows\\System32\\rpcrt4.dll", + expectedSym: "RpcImpersonateClient", + }, + {callstack: callstackFromFrames( + Frame{Addr: 0x7fffa7e3bf6c, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "RtlUserThreadStart"}, + Frame{Addr: 0x7fffa60de8d7, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "BaseThreadInitThunk"}, + Frame{Addr: 0x7ff6163cfc68, Module: "C:\\Program Files\\Mozilla Firefox\\firefox.exe", Symbol: "TargetCreateThread"}, + Frame{Addr: 0x7fffee58d16a, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "ZwMapViewOfSection"}, + Frame{Addr: 0xfffff8028deeed1d, Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe", Symbol: "NtMapViewOfSection"}), + expectedMod: "C:\\Program Files\\Mozilla Firefox\\firefox.exe", + expectedSym: "TargetCreateThread", + }, + {callstack: callstackFromFrames( + Frame{Addr: 0x7fffa7e3bf6c, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "RtlUserThreadStart"}, + Frame{Addr: 0x7fffa60de8d7, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "BaseThreadInitThunk"}, + Frame{Addr: 0x7ffff0c78788, Module: "C:\\Windows\\System32\\rpcrt4.dll", Symbol: "NdrServerCallNdr64"}, + Frame{Addr: 0x7ffff0c574ed, Module: "C:\\Windows\\System32\\rpcrt4.dll", Symbol: "NdrStubCall2"}, + Frame{Addr: 0x7ffff03fb090, Module: "C:\\Windows\\System32\\kernel32.dll", Symbol: "CreateProcessInternalW"}, + Frame{Addr: 0x7fffee58a923, Module: "C:\\Windows\\System32\\kernel32.dll", Symbol: "CreateProcessAsUserW"}, + Frame{Addr: 0x7ffff0fe1204, Module: "C:\\Windows\\System32\\ntdll.dll", Symbol: "ZwCreateUserProcess"}), + expectedMod: "C:\\Windows\\System32\\rpcrt4.dll", + expectedSym: "NdrStubCall2", + }, + } + + for _, tt := range tests { + t.Run(tt.expectedMod+"!"+tt.expectedSym, func(t *testing.T) { + f := tt.callstack.FinalUserFrame() + require.NotNil(t, f) + assert.Equal(t, tt.expectedMod, f.Module) + assert.Equal(t, tt.expectedSym, f.Symbol) + }) + } +} + +func callstackFromFrames(frames ...Frame) Callstack { + var c Callstack + for _, frame := range frames { + c.PushFrame(frame) + } + return c +} diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index b5cead9b3..2481e660b 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -355,13 +355,14 @@ func TestThreadFilter(t *testing.T) { Modules: []pstypes.Module{ {Name: "C:\\Windows\\System32\\kernel32.dll", Size: 2312354, Checksum: 23123343, BaseAddress: va.Address(0x7ffb5c1d0396), DefaultBaseAddress: va.Address(0x7ffb5c1d0396)}, {Name: "C:\\Windows\\System32\\user32.dll", Size: 32212354, Checksum: 33123343, BaseAddress: va.Address(0x7ffb313953b2), DefaultBaseAddress: va.Address(0x7ffb313953b2)}, + {Name: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll", Size: 32212354, Checksum: 33123343, BaseAddress: va.Address(0x7ffb3138592e), DefaultBaseAddress: va.Address(0x7ffb3138592e)}, }, }, } // append the module signature cert := &sys.Cert{Subject: "US, Washington, Redmond, Microsoft Corporation, Microsoft Windows", Issuer: "US, Washington, Redmond, Microsoft Corporation, Microsoft Windows Production PCA 2011"} - signature.GetSignatures().PutSignature(0x7ffb5c1d0396, &signature.Signature{Filename: "C:\\Windows\\System32\\kernel32.dll", Level: 4, Type: 1, Cert: cert}) + signature.GetSignatures().PutSignature(0x7ffb3138592e, &signature.Signature{Filename: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll", Level: 4, Type: 1, Cert: cert}) // simulate unbacked RWX frame base, err := windows.VirtualAlloc(0, 1024, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE) @@ -382,9 +383,9 @@ func TestThreadFilter(t *testing.T) { kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x2638e59e0a5, Offset: 0, Symbol: "?", Module: "unbacked"}) kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: va.Address(base), Offset: 0, Symbol: "?", Module: "unbacked"}) kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb313853b2, Offset: 0x10a, Symbol: "Java_java_lang_ProcessImpl_create", Module: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll"}) - kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb3138592e, Offset: 0x3a2, Symbol: "Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly", Module: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll"}) + kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb3138592e, ModuleAddress: 0x7ffb3138592e, Offset: 0x3a2, Symbol: "Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly", Module: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll"}) kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb5d8e61f4, Offset: 0x54, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNEL32.DLL"}) - kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb5c1d0396, ModuleAddress: 0x7ffb5c1d0396, Offset: 0x66, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}) + kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0x7ffb5c1d0396, Offset: 0x66, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}) kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0xfffff8072ebc1f6f, Offset: 0x4ef, Symbol: "FltRequestFileInfoOnCreateCompletion", Module: "C:\\WINDOWS\\System32\\drivers\\FLTMGR.SYS"}) kevt.Callstack.PushFrame(callstack.Frame{PID: kevt.PID, Addr: 0xfffff8072eb8961b, Offset: 0x20cb, Symbol: "FltGetStreamContext", Module: "C:\\WINDOWS\\System32\\drivers\\FLTMGR.SYS"}) @@ -416,9 +417,9 @@ func TestThreadFilter(t *testing.T) { {`thread.callstack.callsite_trailing_assembly matches ('*mov r10, rcx|mov eax, 0x*|syscall*')`, true}, {`thread.callstack.is_unbacked`, true}, {`thread.callstack.addresses intersects ('7ffb5d8e61f4', 'fffff8072eb8961b')`, true}, - {`thread.callstack.final_user_module.name = 'KERNELBASE.dll'`, true}, - {`thread.callstack.final_user_module.path = 'C:\\WINDOWS\\System32\\KERNELBASE.dll'`, true}, - {`thread.callstack.final_user_symbol.name = 'CreateProcessW'`, true}, + {`thread.callstack.final_user_module.name = 'java.dll'`, true}, + {`thread.callstack.final_user_module.path = 'C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll'`, true}, + {`thread.callstack.final_user_symbol.name = 'Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly'`, true}, {`thread.callstack.final_kernel_module.name = 'FLTMGR.SYS'`, true}, {`thread.callstack.final_kernel_module.path = 'C:\\WINDOWS\\System32\\drivers\\FLTMGR.SYS'`, true}, {`thread.callstack.final_kernel_symbol.name = 'FltGetStreamContext'`, true}, diff --git a/pkg/kevent/kevent_windows_test.go b/pkg/kevent/kevent_windows_test.go index 8c7329cfd..4347b3649 100644 --- a/pkg/kevent/kevent_windows_test.go +++ b/pkg/kevent/kevent_windows_test.go @@ -19,7 +19,6 @@ package kevent import ( - "github.com/rabbitstack/fibratus/pkg/callstack" "github.com/rabbitstack/fibratus/pkg/fs" "github.com/rabbitstack/fibratus/pkg/kevent/kparams" "github.com/rabbitstack/fibratus/pkg/kevent/ktypes" @@ -120,46 +119,3 @@ func TestPartialKey(t *testing.T) { }) } } - -func TestCallstack(t *testing.T) { - e := &Kevent{ - Type: ktypes.CreateProcess, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateProcess", - Timestamp: time.Now(), - Category: ktypes.Process, - } - - e.Callstack.Init(9) - assert.Equal(t, 9, cap(e.Callstack)) - - e.Callstack.PushFrame(callstack.Frame{Addr: 0x2638e59e0a5, Offset: 0, Symbol: "?", Module: "unbacked"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0x7ffb313853b2, Offset: 0x10a, Symbol: "Java_java_lang_ProcessImpl_create", Module: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0x7ffb3138592e, Offset: 0x3a2, Symbol: "Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly", Module: "C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0x7ffb5d8e61f4, Offset: 0x54, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNEL32.DLL"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0x7ffb5c1d0396, Offset: 0x66, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0xfffff8015662a605, Offset: 0x9125, Symbol: "setjmpex", Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0xfffff801568e9c33, Offset: 0x2ef3, Symbol: "LpcRequestPort", Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe"}) - e.Callstack.PushFrame(callstack.Frame{Addr: 0xfffff8015690b644, Offset: 0x45b4, Symbol: "ObDeleteCapturedInsertInfo", Module: "C:\\WINDOWS\\system32\\ntoskrnl.exe"}) - - assert.True(t, e.Callstack.ContainsUnbacked()) - assert.Equal(t, 9, e.Callstack.Depth()) - assert.Equal(t, "0xfffff8015690b644 C:\\WINDOWS\\system32\\ntoskrnl.exe!ObDeleteCapturedInsertInfo+0x45b4|0xfffff801568e9c33 C:\\WINDOWS\\system32\\ntoskrnl.exe!LpcRequestPort+0x2ef3|0xfffff8015662a605 C:\\WINDOWS\\system32\\ntoskrnl.exe!setjmpex+0x9125|0x7ffb5c1d0396 C:\\WINDOWS\\System32\\KERNELBASE.dll!CreateProcessW+0x66|0x7ffb5d8e61f4 C:\\WINDOWS\\System32\\KERNEL32.DLL!CreateProcessW+0x54|0x7ffb5c1d0396 C:\\WINDOWS\\System32\\KERNELBASE.dll!CreateProcessW+0x61|0x7ffb3138592e C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll!Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly+0x3a2|0x7ffb313853b2 C:\\Program Files\\JetBrains\\GoLand 2021.2.3\\jbr\\bin\\java.dll!Java_java_lang_ProcessImpl_create+0x10a|0x2638e59e0a5 unbacked!?", e.Callstack.String()) - assert.Equal(t, "KERNELBASE.dll|KERNEL32.DLL|KERNELBASE.dll|java.dll|unbacked", e.Callstack.Summary()) - - uframe := e.Callstack.FinalUserFrame() - require.NotNil(t, uframe) - assert.Equal(t, "7ffb5c1d0396", uframe.Addr.String()) - assert.Equal(t, "CreateProcessW", uframe.Symbol) - assert.Equal(t, "C:\\WINDOWS\\System32\\KERNELBASE.dll", uframe.Module) - - kframe := e.Callstack.FinalKernelFrame() - require.NotNil(t, kframe) - assert.Equal(t, "fffff8015690b644", kframe.Addr.String()) - assert.Equal(t, "ObDeleteCapturedInsertInfo", kframe.Symbol) - assert.Equal(t, "C:\\WINDOWS\\system32\\ntoskrnl.exe", kframe.Module) -}