├── .gitignore ├── LICENSE ├── README.md ├── RemoteWriteMonitor ├── .clang-format ├── RemoteWriteMonitor.sln ├── RemoteWriteMonitor │ ├── Arch │ │ ├── AMD64 │ │ │ └── amd64.asm │ │ └── x86 │ │ │ └── asm.cpp │ ├── RemoteWriteMonitor.cpp │ ├── RemoteWriteMonitor.vcxproj │ ├── RemoteWriteMonitor.vcxproj.filters │ ├── RemoteWriteMonitor.vcxproj.user │ ├── asm.h │ ├── check.cpp │ ├── check.h │ ├── inline.cpp │ ├── inline.h │ ├── log.cpp │ ├── log.h │ ├── ssdt.cpp │ ├── ssdt.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── util.cpp │ └── util.h ├── TestInjector │ ├── ScopedResource │ │ ├── README.md │ │ ├── scope_exit.h │ │ └── unique_resource.h │ ├── TestInjector.cpp │ ├── TestInjector.vcxproj │ ├── TestInjector.vcxproj.filters │ ├── stdafx.cpp │ ├── stdafx.h │ └── targetver.h ├── clean.bat └── make_release_folder.bat └── img └── injector.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.opensdf 2 | *.log 3 | *.sdf 4 | *.suo 5 | ipch 6 | Debug 7 | Release 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tandasat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | ================================================================================ 24 | Portions of this software are Copyright (C) 2009-2014 Tsuda Kageyu. 25 | ================================================================================ 26 | MinHook - The Minimalistic API Hooking Library for x64/x86 27 | Copyright (C) 2009-2014 Tsuda Kageyu. 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without 31 | modification, are permitted provided that the following conditions 32 | are met: 33 | 34 | 1. Redistributions of source code must retain the above copyright 35 | notice, this list of conditions and the following disclaimer. 36 | 2. Redistributions in binary form must reproduce the above copyright 37 | notice, this list of conditions and the following disclaimer in the 38 | documentation and/or other materials provided with the distribution. 39 | 40 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 41 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 42 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 43 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 44 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 45 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 46 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 47 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 48 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 49 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 50 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 | 52 | ================================================================================ 53 | Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov. 54 | ================================================================================ 55 | Hacker Disassembler Engine 32 C 56 | Copyright (c) 2008-2009, Vyacheslav Patkov. 57 | All rights reserved. 58 | 59 | Redistribution and use in source and binary forms, with or without 60 | modification, are permitted provided that the following conditions 61 | are met: 62 | 63 | 1. Redistributions of source code must retain the above copyright 64 | notice, this list of conditions and the following disclaimer. 65 | 2. Redistributions in binary form must reproduce the above copyright 66 | notice, this list of conditions and the following disclaimer in the 67 | documentation and/or other materials provided with the distribution. 68 | 69 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 70 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 71 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 72 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 73 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 74 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 75 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 76 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 77 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 78 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 79 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 80 | 81 | ------------------------------------------------------------------------------- 82 | Hacker Disassembler Engine 64 C 83 | Copyright (c) 2008-2009, Vyacheslav Patkov. 84 | All rights reserved. 85 | 86 | Redistribution and use in source and binary forms, with or without 87 | modification, are permitted provided that the following conditions 88 | are met: 89 | 90 | 1. Redistributions of source code must retain the above copyright 91 | notice, this list of conditions and the following disclaimer. 92 | 2. Redistributions in binary form must reproduce the above copyright 93 | notice, this list of conditions and the following disclaimer in the 94 | documentation and/or other materials provided with the distribution. 95 | 96 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 97 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 98 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 99 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 100 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 101 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 102 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 103 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 104 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 105 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 106 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 107 | 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RemoteWriteMonitor 2 | =================== 3 | 4 | RemoteWriteMonitor is a tool to help malware analysts tell that the sample is 5 | injecting code to another process. This tool is designed to find a possible 6 | remote code injection and execution without use of NtCreateThread/Ex(), APC or 7 | thread context manipulation. 8 | 9 | A supporting tool 'TestInjector' is a sample program doing that type of code 10 | injection. 11 | 12 | A related blog entry can be found here: 13 | 14 | http://standa-note.blogspot.ca/2015/03/section-based-code-injection-and-its.html 15 | 16 | 17 | Installation and Uninstallation 18 | -------------------------------- 19 | 20 | Get an archive file for compiled files form this link: 21 | 22 | https://github.com/tandasat/RemoteWriteMonitor/releases/latest 23 | 24 | Then use the 'sc' command. For installation: 25 | 26 | >sc create rwmon type= kernel binPath= C:\Users\user\Desktop\RemoteWriteMonitor.sys 27 | >sc start rwmon 28 | 29 | For uninstallation: 30 | 31 | >sc stop rwmon 32 | >sc delete rwmon 33 | 34 | On the x64 bit platform, you have to enable test signing to install the driver. 35 | To do that, open the command prompt with the administrator privilege and type 36 | the following command: 37 | 38 | >bcdedit /set {current} testsigning on 39 | 40 | Then, reboot the system to activate the change. You also have to disable the 41 | Kernel Patch Protection (PatchGuard); [DisPG](https://github.com/tandasat/PgResarch/tree/master/DisPG) 42 | may be use of. 43 | 44 | 45 | Usage 46 | ------ 47 | 48 | Once you have installed it, you may execute the sample and see output from the 49 | driver if any. 50 | 51 | The driver reports when any process newly created after the installation called 52 | NtWriteVirtualMemory() or NtMapViewOfSection() against another process and saves 53 | what was written or mapped into the remote process. Output can be seen with 54 | DebugView and are all saved under the C:\Windows\RemoteWriteMonitor\ 55 | directory. Written and mapped data is stored as \.bin apart from a log file. 56 | 57 | 'TestInjector' could be used to test the driver's function. Injecting and executing 58 | code into notepad.exe can be done by the following commands: 59 | 60 | >notepad && tasklist | findstr notepad 61 | notepad.exe 3368 Console 1 4,564 K 62 | 63 | >TestInjector 3368 section context 64 | Remote Address : 00180000 65 | Waiting for the thread get executed. 66 | Remote Thread ID : 1912 67 | 68 | >TestInjector 3368 alloc context 69 | Remote Address : 001B0000 70 | Remote Thread ID : 2156 71 | 72 | Output on DebugView would look like this: 73 | ![DebugView](/img/injector.png) 74 | 75 | Note that the TestInjector only works against 32 bit processes. 76 | 77 | 78 | Caveats 79 | -------- 80 | 81 | - It reports all those API calls regardless of its memory protection, contents 82 | being written and whether it gets executed. Thus, you should only focus on 83 | output related to the sample you are analyzing as it reports a lot of legit 84 | activities too. 85 | 86 | - It was designed so because it is far more difficult to track all written 87 | regions and reports only when it is executed. 88 | 89 | - It does not monitor any of processes existed when the driver was installed. 90 | Thus, the second injection will not be reported if the sample injects code 91 | into explorer.exe, and then the injected code in the explorer.exe injects 92 | code into another process. 93 | 94 | - Saved memory contents may or may not be the same as what was executed because 95 | the driver only takes dump at occurrence of those API calls. This is particularly true 96 | in the case of ZwMapViewOfSection(). 97 | 98 | - These are limitations but will be fine for letting analysts know injection 99 | may be happening. 100 | 101 | 102 | Supported Platform(s) 103 | ---------------------- 104 | - Windows 7 SP1 and 8.1 (x86/x64) 105 | 106 | 107 | License 108 | ----------------- 109 | This software is released under the MIT License, see LICENSE. 110 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: true 6 | AlignEscapedNewlinesLeft: true 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortIfStatementsOnASingleLine: true 13 | AllowShortLoopsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: All 15 | AlwaysBreakAfterDefinitionReturnType: false 16 | AlwaysBreakTemplateDeclarations: true 17 | AlwaysBreakBeforeMultilineStrings: true 18 | BreakBeforeBinaryOperators: None 19 | BreakBeforeTernaryOperators: true 20 | BreakConstructorInitializersBeforeComma: false 21 | BinPackParameters: true 22 | BinPackArguments: true 23 | ColumnLimit: 80 24 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 25 | ConstructorInitializerIndentWidth: 4 26 | DerivePointerAlignment: true 27 | ExperimentalAutoDetectBinPacking: false 28 | IndentCaseLabels: true 29 | IndentWrappedFunctionNames: false 30 | IndentFunctionDeclarationAfterType: false 31 | MaxEmptyLinesToKeep: 1 32 | KeepEmptyLinesAtTheStartOfBlocks: false 33 | NamespaceIndentation: None 34 | ObjCBlockIndentWidth: 2 35 | ObjCSpaceAfterProperty: false 36 | ObjCSpaceBeforeProtocolList: false 37 | PenaltyBreakBeforeFirstCallParameter: 1 38 | PenaltyBreakComment: 300 39 | PenaltyBreakString: 1000 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyExcessCharacter: 1000000 42 | PenaltyReturnTypeOnItsOwnLine: 200 43 | PointerAlignment: Left 44 | SpacesBeforeTrailingComments: 2 45 | Cpp11BracedListStyle: true 46 | Standard: Auto 47 | IndentWidth: 2 48 | TabWidth: 8 49 | UseTab: Never 50 | BreakBeforeBraces: Attach 51 | SpacesInParentheses: false 52 | SpacesInSquareBrackets: false 53 | SpacesInAngles: false 54 | SpaceInEmptyParentheses: false 55 | SpacesInCStyleCastParentheses: false 56 | SpaceAfterCStyleCast: false 57 | SpacesInContainerLiterals: true 58 | SpaceBeforeAssignmentOperators: true 59 | ContinuationIndentWidth: 4 60 | CommentPragmas: '^ IWYU pragma:' 61 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 62 | SpaceBeforeParens: ControlStatements 63 | DisableFormat: false 64 | ... 65 | 66 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RemoteWriteMonitor", "RemoteWriteMonitor\RemoteWriteMonitor.vcxproj", "{EB2A41A1-8AEE-4097-B5E9-70E23C34994B}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestInjector", "TestInjector\TestInjector.vcxproj", "{D90F53FD-D478-4111-9F00-4B75B91589FD}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6A637F3A-BF61-47C3-8A3C-BD9D8CD24F2C}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\LICENSE = ..\LICENSE 13 | ..\README.md = ..\README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scavenger", "Scavenger", "{7EEF5F1E-F2D6-4D7E-BAE0-6ACEF7D5BEA3}" 17 | ProjectSection(SolutionItems) = preProject 18 | .clang-format = .clang-format 19 | clean.bat = clean.bat 20 | make_release_folder.bat = make_release_folder.bat 21 | EndProjectSection 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Win7 Debug|Win32 = Win7 Debug|Win32 26 | Win7 Debug|x64 = Win7 Debug|x64 27 | Win7 Release|Win32 = Win7 Release|Win32 28 | Win7 Release|x64 = Win7 Release|x64 29 | Win8.1 Debug|Win32 = Win8.1 Debug|Win32 30 | Win8.1 Debug|x64 = Win8.1 Debug|x64 31 | Win8.1 Release|Win32 = Win8.1 Release|Win32 32 | Win8.1 Release|x64 = Win8.1 Release|x64 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|Win32.ActiveCfg = Win8.1 Debug|Win32 36 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|Win32.Build.0 = Win8.1 Debug|Win32 37 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|Win32.Deploy.0 = Win8.1 Debug|Win32 38 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64 39 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|x64.Build.0 = Win7 Debug|x64 40 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Debug|x64.Deploy.0 = Win7 Debug|x64 41 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|Win32.ActiveCfg = Win7 Release|Win32 42 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|Win32.Build.0 = Win7 Release|Win32 43 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|Win32.Deploy.0 = Win7 Release|Win32 44 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|x64.ActiveCfg = Win7 Release|x64 45 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|x64.Build.0 = Win7 Release|x64 46 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win7 Release|x64.Deploy.0 = Win7 Release|x64 47 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|Win32.ActiveCfg = Win8.1 Debug|Win32 48 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|Win32.Build.0 = Win8.1 Debug|Win32 49 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|Win32.Deploy.0 = Win8.1 Debug|Win32 50 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|x64.ActiveCfg = Win8.1 Debug|x64 51 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|x64.Build.0 = Win8.1 Debug|x64 52 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Debug|x64.Deploy.0 = Win8.1 Debug|x64 53 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|Win32.ActiveCfg = Win8.1 Release|Win32 54 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|Win32.Build.0 = Win8.1 Release|Win32 55 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|Win32.Deploy.0 = Win8.1 Release|Win32 56 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64 57 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64 58 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B}.Win8.1 Release|x64.Deploy.0 = Win8.1 Release|x64 59 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Debug|Win32.ActiveCfg = Debug|Win32 60 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Debug|Win32.Build.0 = Debug|Win32 61 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Debug|Win32.Deploy.0 = Debug|Win32 62 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Debug|x64.ActiveCfg = Debug|Win32 63 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Release|Win32.ActiveCfg = Release|Win32 64 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Release|Win32.Build.0 = Release|Win32 65 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Release|Win32.Deploy.0 = Release|Win32 66 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win7 Release|x64.ActiveCfg = Release|Win32 67 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Debug|Win32.ActiveCfg = Debug|Win32 68 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Debug|Win32.Build.0 = Debug|Win32 69 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Debug|Win32.Deploy.0 = Debug|Win32 70 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Debug|x64.ActiveCfg = Debug|Win32 71 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Release|Win32.ActiveCfg = Release|Win32 72 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Release|Win32.Build.0 = Release|Win32 73 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Release|Win32.Deploy.0 = Release|Win32 74 | {D90F53FD-D478-4111-9F00-4B75B91589FD}.Win8.1 Release|x64.ActiveCfg = Release|Win32 75 | EndGlobalSection 76 | GlobalSection(SolutionProperties) = preSolution 77 | HideSolutionNode = FALSE 78 | EndGlobalSection 79 | GlobalSection(NestedProjects) = preSolution 80 | {7EEF5F1E-F2D6-4D7E-BAE0-6ACEF7D5BEA3} = {6A637F3A-BF61-47C3-8A3C-BD9D8CD24F2C} 81 | EndGlobalSection 82 | EndGlobal 83 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/Arch/AMD64/amd64.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (c) 2015, tandasat. All rights reserved. 2 | ; Use of this source code is governed by a MIT-style license that can be 3 | ; found in the LICENSE file. 4 | 5 | ; 6 | ; This module implements stub functions implementing overwritten part of the 7 | ; hooked function and providing accesses to the those original functionalities. 8 | ; 9 | ; Those functions are taken from ntoskrnl.exe and used only on x64 build. 10 | ; 11 | 12 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 13 | .CONST 14 | 15 | 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | .DATA 18 | 19 | 20 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 21 | .CODE 22 | 23 | 24 | ; Implements jump to an arbitrary location without modifying registers. 25 | ; 0ffffffffffffffffh is used as a mark to be replaced with a correct address. 26 | JMP_TEMPLATE MACRO 27 | nop ; This is space for implanting int 3 for debugging 28 | jmp qword ptr [jmp_address] 29 | jmp_address: 30 | dq 0ffffffffffffffffh 31 | ENDM 32 | 33 | 34 | ; NtMapViewOfSection for Windows 8.1 and 7 (both are identical) 35 | AsmNtMapViewOfSection_Win81_7 PROC 36 | mov qword ptr [rsp+10h], rbx 37 | mov qword ptr [rsp+18h], rsi 38 | mov qword ptr [rsp+8h], rcx 39 | push rdi 40 | JMP_TEMPLATE 41 | AsmNtMapViewOfSection_Win81_7 ENDP 42 | AsmNtMapViewOfSection_Win81_7End PROC 43 | nop 44 | AsmNtMapViewOfSection_Win81_7End ENDP 45 | 46 | 47 | ; NtWriteVirtualMemory for Win 8.1 48 | AsmNtWriteVirtualMemory_Win81 PROC 49 | sub rsp, 38h 50 | mov rax, [rsp+60h] 51 | mov dword ptr [rsp+28h], 20h 52 | mov [rsp+20h], rax 53 | JMP_TEMPLATE 54 | AsmNtWriteVirtualMemory_Win81 ENDP 55 | AsmNtWriteVirtualMemory_Win81End PROC 56 | nop 57 | AsmNtWriteVirtualMemory_Win81End ENDP 58 | 59 | 60 | ; NtWriteVirtualMemory for Win 7 61 | AsmNtWriteVirtualMemory_Win7 PROC 62 | mov rax, rsp 63 | mov qword ptr [rax+8h], rbx 64 | mov qword ptr [rax+10h], rsi 65 | mov qword ptr [rax+18h], rdi 66 | mov qword ptr [rax+20h], r12 67 | JMP_TEMPLATE 68 | AsmNtWriteVirtualMemory_Win7 ENDP 69 | AsmNtWriteVirtualMemory_Win7End PROC 70 | nop 71 | AsmNtWriteVirtualMemory_Win7End ENDP 72 | 73 | 74 | END 75 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/Arch/x86/asm.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements empty functions for Asm functions to allow us to build 7 | // the code on x86. Those Asm functions are not used on x86. 8 | // 9 | #include "stdafx.h" 10 | #include "../../asm.h" 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | // 14 | // macro utilities 15 | // 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | // 19 | // constants and macros 20 | // 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // 24 | // types 25 | // 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | // 29 | // prototypes 30 | // 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | // 34 | // variables 35 | // 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | // 39 | // implementations 40 | // 41 | 42 | EXTERN_C void AsmNtMapViewOfSection_Win81_7(){}; 43 | 44 | EXTERN_C void AsmNtMapViewOfSection_Win81_7End(){}; 45 | 46 | EXTERN_C void AsmNtWriteVirtualMemory_Win81(){}; 47 | 48 | EXTERN_C void AsmNtWriteVirtualMemory_Win81End(){}; 49 | 50 | EXTERN_C void AsmNtWriteVirtualMemory_Win7(){}; 51 | 52 | EXTERN_C void AsmNtWriteVirtualMemory_Win7End(){}; 53 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements an entry point of the driver and initializes other 7 | // components in this module. 8 | // 9 | #include "stdafx.h" 10 | #include "log.h" 11 | #include "asm.h" 12 | #include "inline.h" 13 | #include "check.h" 14 | #include "ssdt.h" 15 | #include "util.h" 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | // 19 | // macro utilities 20 | // 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // 24 | // constants and macros 25 | // 26 | 27 | static const wchar_t RWMONP_OUT_DIRECTORY_PATH[] = 28 | L"\\SystemRoot\\RemoteWriteMonitor"; 29 | static const wchar_t RWMONP_LOG_FILE_PATH[] = 30 | L"\\SystemRoot\\RemoteWriteMonitor\\RemoteWriteMonitor.log"; 31 | 32 | #if DBG 33 | static const auto RWMONP_LOG_LEVEL = LOG_PUT_LEVEL_DEBUG; 34 | #else 35 | static const auto RWMONP_LOG_LEVEL = LOG_PUT_LEVEL_INFO; 36 | #endif 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | // 40 | // types 41 | // 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | // 45 | // prototypes 46 | // 47 | 48 | EXTERN_C 49 | NTSTATUS NTAPI 50 | NtMapViewOfSection(_In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, 51 | _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, 52 | _In_ SIZE_T CommitSize, 53 | _Inout_opt_ PLARGE_INTEGER SectionOffset, 54 | _Inout_ PSIZE_T ViewSize, 55 | _In_ SECTION_INHERIT InheritDisposition, 56 | _In_ ULONG AllocationType, _In_ ULONG Win32Protect); 57 | using NtMapViewOfSectionType = decltype(&NtMapViewOfSection); 58 | 59 | EXTERN_C 60 | NTSTATUS NTAPI NtWriteVirtualMemory(_In_ HANDLE ProcessHandle, 61 | _In_ PVOID BaseAddress, _In_ PVOID Buffer, 62 | _In_ ULONG BytesToWrite, 63 | _Out_opt_ PULONG BytesWritten); 64 | 65 | using NtWriteVirtualMemoryType = decltype(&NtWriteVirtualMemory); 66 | 67 | EXTERN_C DRIVER_INITIALIZE DriverEntry; 68 | 69 | EXTERN_C static NTSTATUS RWMonpCreateDirectory(_In_ const wchar_t *PathW); 70 | 71 | EXTERN_C static NTSTATUS RWMonpInitVersionDependentValues(); 72 | 73 | EXTERN_C static DRIVER_UNLOAD RWMonpDriverUnload; 74 | 75 | EXTERN_C static NTSTATUS RWMonpInstallHooks(); 76 | 77 | EXTERN_C static NTSTATUS RWMonpUninstallHooks(); 78 | 79 | EXTERN_C static NTSTATUS NTAPI 80 | RWMonpNtWriteVirtualMemory_Hook(_In_ HANDLE ProcessHandle, 81 | _In_ PVOID BaseAddress, _In_ PVOID Buffer, 82 | _In_ ULONG BytesToWrite, 83 | _Out_opt_ PULONG BytesWritten); 84 | 85 | EXTERN_C static NTSTATUS NTAPI RWMonpNtMapViewOfSection_Hook( 86 | _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, 87 | _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, 88 | _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, 89 | _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, 90 | _In_ ULONG Win32Protect); 91 | 92 | //////////////////////////////////////////////////////////////////////////////// 93 | // 94 | // variables 95 | // 96 | 97 | static InlineHookInfo g_RWMonpNtMapViewOfSectionInfo = {}; 98 | static InlineHookInfo g_RWMonpNtWriteVirtualMemoryInfo = {}; 99 | 100 | static NtMapViewOfSectionType g_RWMonpNtMapViewOfSectionOriginal = nullptr; 101 | static NtWriteVirtualMemoryType g_RWMonpNtWriteVirtualMemoryOriginal = nullptr; 102 | 103 | static ULONG g_RWMonpNtMapViewOfSectionSSDTIndex = 0; 104 | static ULONG g_RWMonpNtWriteVirtualMemorySSDTIndex = 0; 105 | 106 | //////////////////////////////////////////////////////////////////////////////// 107 | // 108 | // implementations 109 | // 110 | 111 | // 112 | // INIT section begin 113 | // 114 | ALLOC_TEXT(INIT, DriverEntry) 115 | EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, 116 | _In_ PUNICODE_STRING RegistryPath) { 117 | PAGED_CODE(); 118 | UNREFERENCED_PARAMETER(RegistryPath); 119 | 120 | auto status = STATUS_UNSUCCESSFUL; 121 | DriverObject->DriverUnload = RWMonpDriverUnload; 122 | DBG_BREAK(); 123 | 124 | // Create a directory for a log file and dumped files before initializing 125 | // the Log system 126 | status = RWMonpCreateDirectory(RWMONP_OUT_DIRECTORY_PATH); 127 | if (!NT_SUCCESS(status)) { 128 | return status; 129 | } 130 | 131 | // Initialize the Log system 132 | status = LogInitialization( 133 | RWMONP_LOG_LEVEL | LOG_OPT_DISABLE_TIME | LOG_OPT_DISABLE_FUNCTION_NAME, 134 | RWMONP_LOG_FILE_PATH, nullptr); 135 | if (!NT_SUCCESS(status)) { 136 | return status; 137 | } 138 | 139 | // Init SSDT 140 | status = SSDTInitialization(); 141 | if (!NT_SUCCESS(status)) { 142 | LogTermination(nullptr); 143 | return status; 144 | } 145 | 146 | // Init globals 147 | status = RWMonpInitVersionDependentValues(); 148 | if (!NT_SUCCESS(status)) { 149 | SSDTTermination(); 150 | LogTermination(nullptr); 151 | return status; 152 | } 153 | 154 | // Init the Check subsystem 155 | status = CheckInitialization(RWMONP_OUT_DIRECTORY_PATH); 156 | if (!NT_SUCCESS(status)) { 157 | SSDTTermination(); 158 | LogTermination(nullptr); 159 | return status; 160 | } 161 | 162 | // Install hooks 163 | status = RWMonpInstallHooks(); 164 | if (!NT_SUCCESS(status)) { 165 | CheckTermination(); 166 | SSDTTermination(); 167 | LogTermination(nullptr); 168 | return status; 169 | } 170 | 171 | LOG_INFO("RemoteWriteMonitor installed"); 172 | return status; 173 | } 174 | 175 | // Perform version check and fill out global variable based on the version 176 | ALLOC_TEXT(INIT, RWMonpInitVersionDependentValues) 177 | EXTERN_C static NTSTATUS RWMonpInitVersionDependentValues() { 178 | PAGED_CODE(); 179 | 180 | // Check the OS version and initialize right indexes for SSDT hook. 181 | RTL_OSVERSIONINFOW osVersion = {sizeof(osVersion)}; 182 | auto status = RtlGetVersion(&osVersion); 183 | if (!NT_SUCCESS(status)) { 184 | LOG_ERROR("RtlGetVersion failed (%08x)", status); 185 | return status; 186 | } 187 | 188 | if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 1) { 189 | // Windows 7 190 | if (IsX64()) { 191 | // x64 192 | g_RWMonpNtMapViewOfSectionSSDTIndex = 0x25; 193 | g_RWMonpNtMapViewOfSectionOriginal = 194 | reinterpret_cast( 195 | AsmNtMapViewOfSection_Win81_7); 196 | status = InlineInitHookInfo( 197 | reinterpret_cast( 198 | SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)), 199 | reinterpret_cast(RWMonpNtMapViewOfSection_Hook), 200 | reinterpret_cast(AsmNtMapViewOfSection_Win81_7), 201 | reinterpret_cast(AsmNtMapViewOfSection_Win81_7End), 202 | &g_RWMonpNtMapViewOfSectionInfo); 203 | if (!NT_SUCCESS(status)) { 204 | return status; 205 | } 206 | 207 | g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x37; 208 | g_RWMonpNtWriteVirtualMemoryOriginal = 209 | reinterpret_cast( 210 | AsmNtWriteVirtualMemory_Win7); 211 | status = InlineInitHookInfo( 212 | reinterpret_cast( 213 | SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)), 214 | reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook), 215 | reinterpret_cast(AsmNtWriteVirtualMemory_Win7), 216 | reinterpret_cast(AsmNtWriteVirtualMemory_Win7End), 217 | &g_RWMonpNtWriteVirtualMemoryInfo); 218 | if (!NT_SUCCESS(status)) { 219 | return status; 220 | } 221 | } else { 222 | // x86 223 | g_RWMonpNtMapViewOfSectionSSDTIndex = 0xa8; 224 | g_RWMonpNtMapViewOfSectionOriginal = 225 | reinterpret_cast( 226 | SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)); 227 | 228 | g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x18f; 229 | g_RWMonpNtWriteVirtualMemoryOriginal = 230 | reinterpret_cast( 231 | SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)); 232 | } 233 | } else if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 3) { 234 | // Windows 8.1 235 | if (IsX64()) { 236 | // x64 237 | g_RWMonpNtMapViewOfSectionSSDTIndex = 0x27; 238 | g_RWMonpNtMapViewOfSectionOriginal = 239 | reinterpret_cast( 240 | AsmNtMapViewOfSection_Win81_7); 241 | status = InlineInitHookInfo( 242 | reinterpret_cast( 243 | SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)), 244 | reinterpret_cast(RWMonpNtMapViewOfSection_Hook), 245 | reinterpret_cast(AsmNtMapViewOfSection_Win81_7), 246 | reinterpret_cast(AsmNtMapViewOfSection_Win81_7End), 247 | &g_RWMonpNtMapViewOfSectionInfo); 248 | if (!NT_SUCCESS(status)) { 249 | return status; 250 | } 251 | 252 | g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x39; 253 | g_RWMonpNtWriteVirtualMemoryOriginal = 254 | reinterpret_cast( 255 | AsmNtWriteVirtualMemory_Win81); 256 | status = InlineInitHookInfo( 257 | reinterpret_cast( 258 | SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)), 259 | reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook), 260 | reinterpret_cast(AsmNtWriteVirtualMemory_Win81), 261 | reinterpret_cast(AsmNtWriteVirtualMemory_Win81End), 262 | &g_RWMonpNtWriteVirtualMemoryInfo); 263 | if (!NT_SUCCESS(status)) { 264 | return status; 265 | } 266 | 267 | } else { 268 | // x86 269 | g_RWMonpNtMapViewOfSectionSSDTIndex = 0xf6; 270 | g_RWMonpNtMapViewOfSectionOriginal = 271 | reinterpret_cast( 272 | SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)); 273 | 274 | g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x3; 275 | g_RWMonpNtWriteVirtualMemoryOriginal = 276 | reinterpret_cast( 277 | SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)); 278 | } 279 | } else { 280 | LOG_ERROR("Unsupported OS version"); 281 | return STATUS_DEVICE_CONFIGURATION_ERROR; 282 | } 283 | return status; 284 | } 285 | 286 | // Create a directory 287 | ALLOC_TEXT(INIT, RWMonpCreateDirectory) 288 | EXTERN_C static NTSTATUS RWMonpCreateDirectory(_In_ const wchar_t *PathW) { 289 | PAGED_CODE(); 290 | 291 | UNICODE_STRING path = {}; 292 | RtlInitUnicodeString(&path, PathW); 293 | OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( 294 | &path, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); 295 | 296 | IO_STATUS_BLOCK ioStatus = {}; 297 | HANDLE directory = nullptr; 298 | NTSTATUS status = ZwCreateFile( 299 | &directory, GENERIC_WRITE, &objAttr, &ioStatus, nullptr, 300 | FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 301 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE, nullptr, 0); 302 | if (NT_SUCCESS(status)) { 303 | ZwClose(directory); 304 | } 305 | 306 | return status; 307 | } 308 | 309 | // Unloading the driver. Close and restore everything. 310 | ALLOC_TEXT(PAGED, RWMonpDriverUnload) 311 | EXTERN_C static void RWMonpDriverUnload(_In_ PDRIVER_OBJECT DriverObject) { 312 | PAGED_CODE(); 313 | UNREFERENCED_PARAMETER(DriverObject); 314 | 315 | LOG_DEBUG("Being terminated."); 316 | // DBG_BREAK(); 317 | 318 | RWMonpUninstallHooks(); 319 | UtilSleep(1000); 320 | CheckTermination(); 321 | SSDTTermination(); 322 | LogTermination(nullptr); 323 | } 324 | 325 | // Install hooks 326 | ALLOC_TEXT(INIT, RWMonpInstallHooks) 327 | EXTERN_C static NTSTATUS RWMonpInstallHooks() { 328 | PAGED_CODE(); 329 | 330 | auto status = STATUS_SUCCESS; 331 | if (IsX64()) { 332 | status = InlineInstallHook(g_RWMonpNtMapViewOfSectionInfo); 333 | if (!NT_SUCCESS(status)) { 334 | return status; 335 | } 336 | status = InlineInstallHook(g_RWMonpNtWriteVirtualMemoryInfo); 337 | if (!NT_SUCCESS(status)) { 338 | InlineUninstallHook(g_RWMonpNtMapViewOfSectionInfo); 339 | return status; 340 | } 341 | } else { 342 | SSDTSetProcAdderss( 343 | g_RWMonpNtMapViewOfSectionSSDTIndex, 344 | reinterpret_cast(RWMonpNtMapViewOfSection_Hook)); 345 | SSDTSetProcAdderss( 346 | g_RWMonpNtWriteVirtualMemorySSDTIndex, 347 | reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook)); 348 | } 349 | return status; 350 | } 351 | 352 | // Uninstall hooks 353 | ALLOC_TEXT(PAGED, RWMonpUninstallHooks) 354 | EXTERN_C static NTSTATUS RWMonpUninstallHooks() { 355 | PAGED_CODE(); 356 | 357 | auto status = STATUS_SUCCESS; 358 | if (IsX64()) { 359 | status = InlineUninstallHook(g_RWMonpNtWriteVirtualMemoryInfo); 360 | status = InlineUninstallHook(g_RWMonpNtMapViewOfSectionInfo); 361 | } else { 362 | SSDTSetProcAdderss( 363 | g_RWMonpNtMapViewOfSectionSSDTIndex, 364 | reinterpret_cast(g_RWMonpNtMapViewOfSectionOriginal)); 365 | SSDTSetProcAdderss( 366 | g_RWMonpNtWriteVirtualMemorySSDTIndex, 367 | reinterpret_cast(g_RWMonpNtWriteVirtualMemoryOriginal)); 368 | } 369 | return status; 370 | } 371 | 372 | // 373 | // Hook Handlers 374 | // 375 | 376 | // A hook handler for NtMapViewOfSection 377 | ALLOC_TEXT(PAGED, RWMonpNtMapViewOfSection_Hook) 378 | EXTERN_C static NTSTATUS NTAPI RWMonpNtMapViewOfSection_Hook( 379 | _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, 380 | _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, 381 | _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, 382 | _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, 383 | _In_ ULONG Win32Protect) { 384 | PAGED_CODE(); 385 | 386 | const auto result = g_RWMonpNtMapViewOfSectionOriginal( 387 | SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, 388 | SectionOffset, ViewSize, InheritDisposition, AllocationType, 389 | Win32Protect); 390 | if (NT_SUCCESS(result)) { 391 | CheckData(ProcessHandle, *BaseAddress, nullptr, 392 | static_cast(*ViewSize)); 393 | } 394 | return result; 395 | } 396 | 397 | // A hook handler for NtWriteVirtualMemory 398 | ALLOC_TEXT(PAGED, RWMonpNtWriteVirtualMemory_Hook) 399 | EXTERN_C static NTSTATUS NTAPI 400 | RWMonpNtWriteVirtualMemory_Hook(_In_ HANDLE ProcessHandle, 401 | _In_ PVOID BaseAddress, _In_ PVOID Buffer, 402 | _In_ ULONG BytesToWrite, 403 | _Out_opt_ PULONG BytesWritten) { 404 | PAGED_CODE(); 405 | 406 | const auto result = g_RWMonpNtWriteVirtualMemoryOriginal( 407 | ProcessHandle, BaseAddress, Buffer, BytesToWrite, BytesWritten); 408 | if (NT_SUCCESS(result)) { 409 | CheckData(ProcessHandle, BaseAddress, Buffer, BytesToWrite); 410 | } 411 | return result; 412 | } 413 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Win8.1 Debug 6 | Win32 7 | 8 | 9 | Win8.1 Release 10 | Win32 11 | 12 | 13 | Win7 Debug 14 | Win32 15 | 16 | 17 | Win7 Release 18 | Win32 19 | 20 | 21 | Win8.1 Debug 22 | x64 23 | 24 | 25 | Win8.1 Release 26 | x64 27 | 28 | 29 | Win7 Debug 30 | x64 31 | 32 | 33 | Win7 Release 34 | x64 35 | 36 | 37 | 38 | {EB2A41A1-8AEE-4097-B5E9-70E23C34994B} 39 | {1bc93793-694f-48fe-9372-81e2b05556fd} 40 | v4.5 41 | 11.0 42 | Win8.1 Debug 43 | Win32 44 | RemoteWriteMonitor 45 | 46 | 47 | 48 | WindowsV6.3 49 | true 50 | WindowsKernelModeDriver8.1 51 | Driver 52 | KMDF 53 | 54 | 55 | WindowsV6.3 56 | false 57 | WindowsKernelModeDriver8.1 58 | Driver 59 | KMDF 60 | 61 | 62 | Windows7 63 | true 64 | WindowsKernelModeDriver8.1 65 | Driver 66 | KMDF 67 | 68 | 69 | Windows7 70 | false 71 | WindowsKernelModeDriver8.1 72 | Driver 73 | KMDF 74 | 75 | 76 | WindowsV6.3 77 | true 78 | WindowsKernelModeDriver8.1 79 | Driver 80 | KMDF 81 | 82 | 83 | WindowsV6.3 84 | false 85 | WindowsKernelModeDriver8.1 86 | Driver 87 | KMDF 88 | 89 | 90 | Windows7 91 | true 92 | WindowsKernelModeDriver8.1 93 | Driver 94 | KMDF 95 | 96 | 97 | Windows7 98 | false 99 | WindowsKernelModeDriver8.1 100 | Driver 101 | KMDF 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | DbgengKernelDebugger 113 | 114 | 115 | DbgengKernelDebugger 116 | 117 | 118 | DbgengKernelDebugger 119 | 120 | 121 | DbgengKernelDebugger 122 | 123 | 124 | DbgengKernelDebugger 125 | 126 | 127 | DbgengKernelDebugger 128 | 129 | 130 | DbgengKernelDebugger 131 | 132 | 133 | DbgengKernelDebugger 134 | 135 | 136 | 137 | trace.h 138 | true 139 | 140 | 141 | GsDriverEntry@8 142 | Ksecdd.lib;%(AdditionalDependencies) 143 | 144 | 145 | 146 | 147 | trace.h 148 | true 149 | 150 | 151 | GsDriverEntry@8 152 | Ksecdd.lib;%(AdditionalDependencies) 153 | 154 | 155 | 156 | 157 | trace.h 158 | true 159 | 160 | 161 | GsDriverEntry@8 162 | Ksecdd.lib;%(AdditionalDependencies) 163 | 164 | 165 | 166 | 167 | trace.h 168 | true 169 | 170 | 171 | GsDriverEntry@8 172 | Ksecdd.lib;%(AdditionalDependencies) 173 | 174 | 175 | 176 | 177 | trace.h 178 | true 179 | 180 | 181 | GsDriverEntry 182 | Ksecdd.lib;%(AdditionalDependencies) 183 | 184 | 185 | 186 | 187 | trace.h 188 | true 189 | 190 | 191 | GsDriverEntry 192 | Ksecdd.lib;%(AdditionalDependencies) 193 | 194 | 195 | 196 | 197 | trace.h 198 | true 199 | 200 | 201 | GsDriverEntry 202 | Ksecdd.lib;%(AdditionalDependencies) 203 | 204 | 205 | 206 | 207 | trace.h 208 | true 209 | 210 | 211 | GsDriverEntry 212 | Ksecdd.lib;%(AdditionalDependencies) 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | true 222 | true 223 | true 224 | true 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | true 246 | true 247 | true 248 | true 249 | Document 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | {f7b6d55a-ed6d-4b41-ad2f-b0655f29c8e5} 22 | 23 | 24 | {8074da67-3160-466f-9c24-8a646e3d8642} 25 | 26 | 27 | {f0256296-1a4f-45ba-8c8a-3938e86b5bf2} 28 | 29 | 30 | 31 | 32 | Source Files 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | Source Files 42 | 43 | 44 | Source Files 45 | 46 | 47 | Source Files 48 | 49 | 50 | Source Files 51 | 52 | 53 | Source Files\Arch\x86 54 | 55 | 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | 80 | 81 | Source Files\Arch\AMD64 82 | 83 | 84 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | TestSign 5 | 6 | 7 | TestSign 8 | 9 | 10 | TestSign 11 | 12 | 13 | TestSign 14 | 15 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/asm.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to functions written in assembler. 7 | // 8 | #pragma once 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // macro utilities 13 | // 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // 17 | // constants and macros 18 | // 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // 22 | // types 23 | // 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // 27 | // prototypes 28 | // 29 | 30 | EXTERN_C void AsmNtMapViewOfSection_Win81_7(); 31 | 32 | EXTERN_C void AsmNtMapViewOfSection_Win81_7End(); 33 | 34 | EXTERN_C void AsmNtWriteVirtualMemory_Win81(); 35 | 36 | EXTERN_C void AsmNtWriteVirtualMemory_Win81End(); 37 | 38 | EXTERN_C void AsmNtWriteVirtualMemory_Win7(); 39 | 40 | EXTERN_C void AsmNtWriteVirtualMemory_Win7End(); 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | // 44 | // variables 45 | // 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | // 49 | // implementations 50 | // 51 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/check.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements functions for checking if data is written 7 | // by a remote process and saving it if so. 8 | // 9 | #include "stdafx.h" 10 | #include "check.h" 11 | #include "log.h" 12 | #include "util.h" 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | // 16 | // macro utilities 17 | // 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | // 21 | // constants and macros 22 | // 23 | 24 | static const auto CHECKP_WHITELIST_ARRAY_SIZE = 1000; 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | // 28 | // types 29 | // 30 | 31 | struct SYSTEM_PROCESS_INFORMATION { 32 | ULONG NextEntryOffset; 33 | ULONG NumberOfThreads; 34 | BYTE Reserved1[48]; 35 | PVOID Reserved2[3]; 36 | HANDLE UniqueProcessId; 37 | PVOID Reserved3; 38 | ULONG HandleCount; 39 | BYTE Reserved4[4]; 40 | PVOID Reserved5[11]; 41 | SIZE_T PeakPagefileUsage; 42 | SIZE_T PrivatePageCount; 43 | LARGE_INTEGER Reserved6[6]; 44 | }; 45 | 46 | enum SYSTEM_INFORMATION_CLASS { 47 | SystemProcessInformation = 5, 48 | }; 49 | 50 | 51 | enum INTER_PROCESS_TYPE 52 | { 53 | INTER_PROCESS_WRITE, 54 | INTER_PROCESS_MAP, 55 | }; 56 | 57 | struct CHECK_WORK_ITEM_CONTEXT 58 | { 59 | WORK_QUEUE_ITEM WorkItem; 60 | void *Data; 61 | ULONG DataSize; 62 | INTER_PROCESS_TYPE Type; 63 | PEPROCESS WriterProcess; 64 | PEPROCESS TargetProcess; 65 | void *RemoteAddress; 66 | }; 67 | 68 | //////////////////////////////////////////////////////////////////////////////// 69 | // 70 | // prototypes 71 | // 72 | 73 | EXTERN_C NTKERNELAPI UCHAR *NTAPI 74 | PsGetProcessImageFileName(_In_ PEPROCESS Process); 75 | 76 | EXTERN_C NTSTATUS NTAPI ZwQuerySystemInformation( 77 | _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, 78 | _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, 79 | _Out_opt_ PULONG ReturnLength); 80 | 81 | EXTERN_C static NTSTATUS CheckpForEachProcess( 82 | _In_ bool (*Callback)(_In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, 83 | _In_opt_ void *Context), 84 | _In_opt_ void *Context); 85 | 86 | EXTERN_C static bool CheckpSaveExistingPID( 87 | _In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, _In_ void *Context); 88 | 89 | _Success_(return == true) EXTERN_C 90 | static bool CheckpIsInterprocessWrite(_In_ HANDLE ProcessHandle, 91 | _Out_ PEPROCESS *TargetProcess); 92 | 93 | EXTERN_C static NTSTATUS CheckpCopyDataFromUserSpace( 94 | _Out_ void *Buffer, _In_ const void *BaseAddress, _In_ ULONG DataSize, 95 | _In_opt_ PEPROCESS TargetProcess); 96 | 97 | EXTERN_C static NTSTATUS CheckpTryCopyMemory(_Out_ void *Destionation, 98 | _In_ const void *Source, 99 | _In_ SIZE_T Length); 100 | 101 | EXTERN_C static void CheckpWorkItemRoutine(_In_ void *Context); 102 | 103 | _Success_(return == true) EXTERN_C 104 | static bool CheckpGetSha1(_Out_ UCHAR(&Sha1Hash)[20], _In_ void *Data, 105 | _In_ ULONG DataSize); 106 | 107 | EXTERN_C static NTSTATUS CheckpWriteFile(_In_ const wchar_t *OutPathW, 108 | _In_ void *Buffer, 109 | _In_ ULONG BufferSize, 110 | _In_ ACCESS_MASK DesiredAccess, 111 | _In_ ULONG CreateDisposition); 112 | 113 | //////////////////////////////////////////////////////////////////////////////// 114 | // 115 | // variables 116 | // 117 | 118 | static wchar_t g_CheckpLogDirecotry[MAX_PATH] = {}; 119 | static HANDLE g_CheckpWhiteListedProcessIDs[CHECKP_WHITELIST_ARRAY_SIZE] = {}; 120 | static BCRYPT_ALG_HANDLE g_CheckpSha1AlgorithmHandle = nullptr; 121 | static volatile long g_CheckpNumberOfActiveWorkQueueItems = 0; 122 | 123 | //////////////////////////////////////////////////////////////////////////////// 124 | // 125 | // implementations 126 | // 127 | 128 | // Initialize the Check subsystem 129 | ALLOC_TEXT(INIT, CheckInitialization) 130 | EXTERN_C NTSTATUS CheckInitialization(_In_ const wchar_t *LogDirectry) { 131 | PAGED_CODE(); 132 | 133 | auto status = RtlStringCchCopyW( 134 | g_CheckpLogDirecotry, RTL_NUMBER_OF(g_CheckpLogDirecotry), LogDirectry); 135 | if (!NT_SUCCESS(status)) { 136 | LOG_ERROR("RtlStringCchCopyW failed (%08x)", status); 137 | return status; 138 | } 139 | 140 | // Save existing processes' IDs in a white list 141 | auto index = 0; 142 | status = CheckpForEachProcess(CheckpSaveExistingPID, &index); 143 | if (!NT_SUCCESS(status)) { 144 | LOG_ERROR("ForEachProcess failed (%08x)", status); 145 | return status; 146 | } 147 | 148 | // Initialize the crypt APIs. 149 | status = BCryptOpenAlgorithmProvider(&g_CheckpSha1AlgorithmHandle, 150 | BCRYPT_SHA1_ALGORITHM, nullptr, 0); 151 | if (!NT_SUCCESS(status)) { 152 | LOG_ERROR("BCryptOpenAlgorithmProvider failed (%08x)", status); 153 | return status; 154 | } 155 | return status; 156 | } 157 | 158 | // Terminates the check subsystem 159 | ALLOC_TEXT(PAGED, CheckTermination) 160 | EXTERN_C void CheckTermination() { 161 | PAGED_CODE(); 162 | 163 | // while (g_CheckpNumberOfActiveWorkQueueItems != 0) 164 | while (InterlockedCompareExchange(&g_CheckpNumberOfActiveWorkQueueItems, 0, 165 | 0) != 0) { 166 | UtilSleep(500); 167 | } 168 | 169 | BCryptCloseAlgorithmProvider(g_CheckpSha1AlgorithmHandle, 0); 170 | } 171 | 172 | // Apply Callback for each process. Enumeration can be discontinued by returning 173 | // false from Callback. 174 | ALLOC_TEXT(INIT, CheckpForEachProcess) 175 | EXTERN_C static NTSTATUS CheckpForEachProcess( 176 | _In_ bool (*Callback)(_In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, 177 | _In_opt_ void *Context), 178 | _In_opt_ void *Context) { 179 | PAGED_CODE(); 180 | 181 | auto processInfo = reinterpret_cast( 182 | ExAllocatePoolWithTag(PagedPool, 0x10000, RWMON_POOL_TAG_NAME)); 183 | if (!processInfo) { 184 | return STATUS_MEMORY_NOT_ALLOCATED; 185 | } 186 | 187 | ULONG returnLength = 0; 188 | auto status = ZwQuerySystemInformation(SystemProcessInformation, processInfo, 189 | 0x10000, &returnLength); 190 | if (!NT_SUCCESS(status) && returnLength) { 191 | ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); 192 | processInfo = 193 | reinterpret_cast(ExAllocatePoolWithTag( 194 | PagedPool, returnLength + PAGE_SIZE, RWMON_POOL_TAG_NAME)); 195 | if (!processInfo) { 196 | return STATUS_MEMORY_NOT_ALLOCATED; 197 | } 198 | 199 | status = 200 | ZwQuerySystemInformation(SystemProcessInformation, processInfo, 201 | (returnLength + PAGE_SIZE), &returnLength); 202 | } 203 | if (!NT_SUCCESS(status)) { 204 | goto End; 205 | } 206 | 207 | for (auto current = processInfo; current; /**/) { 208 | if (!Callback(current, Context)) { 209 | break; 210 | } 211 | 212 | if (!current->NextEntryOffset) { 213 | break; 214 | } 215 | current = reinterpret_cast( 216 | reinterpret_cast(current) + current->NextEntryOffset); 217 | } 218 | 219 | End:; 220 | ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); 221 | return status; 222 | } 223 | 224 | // A callback routine saving existing processes' IDs into a white list. 225 | ALLOC_TEXT(INIT, CheckpSaveExistingPID) 226 | EXTERN_C static bool CheckpSaveExistingPID( 227 | _In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, _In_ void *Context) { 228 | PAGED_CODE(); 229 | 230 | auto &index = *static_cast(Context); 231 | if (index >= 232 | CHECKP_WHITELIST_ARRAY_SIZE - 1) { // -1 to have 0 at the end at least 233 | return false; 234 | } 235 | if (ProcessInfo->UniqueProcessId) { 236 | g_CheckpWhiteListedProcessIDs[index++] = ProcessInfo->UniqueProcessId; 237 | } 238 | return true; 239 | } 240 | 241 | // Check if the call is inter-process write, and log it if so. 242 | ALLOC_TEXT(PAGED, CheckData) 243 | EXTERN_C bool CheckData(_In_ HANDLE ProcessHandle, _In_ void *RemoteAddress, 244 | _In_opt_ void *Contents, _In_ ULONG DataSize) { 245 | PAGED_CODE(); 246 | 247 | // Check if it is a interprocess operation 248 | PEPROCESS targetProcess = nullptr; 249 | if (!CheckpIsInterprocessWrite(ProcessHandle, &targetProcess)) { 250 | return false; 251 | } 252 | 253 | // Allocate a memory to copy written data 254 | auto data = ExAllocatePoolWithTag(PagedPool, DataSize, RWMON_POOL_TAG_NAME); 255 | if (!data) { 256 | goto FailureEnd; 257 | } 258 | 259 | // Copy the written data 260 | auto status = STATUS_SUCCESS; 261 | if (Contents) { 262 | status = CheckpCopyDataFromUserSpace(data, Contents, DataSize, nullptr); 263 | } else { 264 | status = CheckpCopyDataFromUserSpace(data, RemoteAddress, DataSize, 265 | targetProcess); 266 | } 267 | if (!NT_SUCCESS(status)) { 268 | LOG_ERROR_SAFE("CopyDataFromUserSpace failed (%08x)", status); 269 | goto FailureEnd; 270 | } 271 | 272 | // Allocate and queue an work queue item 273 | auto workItemContext = reinterpret_cast( 274 | ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CHECK_WORK_ITEM_CONTEXT), 275 | RWMON_POOL_TAG_NAME)); 276 | if (!workItemContext) { 277 | goto FailureEnd; 278 | } 279 | 280 | status = ObReferenceObjectByPointer(PsGetCurrentProcess(), 0, *PsProcessType, KernelMode); 281 | if (!NT_SUCCESS(status)) 282 | { 283 | LOG_ERROR_SAFE("ObReferenceObjectByPointer failed (%08x)", status); 284 | ExFreePoolWithTag(workItemContext, RWMON_POOL_TAG_NAME); 285 | goto FailureEnd; 286 | } 287 | 288 | InterlockedIncrement(&g_CheckpNumberOfActiveWorkQueueItems); 289 | ExInitializeWorkItem(&workItemContext->WorkItem, CheckpWorkItemRoutine, 290 | workItemContext); 291 | workItemContext->Data = data; 292 | workItemContext->DataSize = DataSize; 293 | workItemContext->Type = (Contents) ? INTER_PROCESS_WRITE : INTER_PROCESS_MAP; 294 | workItemContext->WriterProcess = PsGetCurrentProcess(); 295 | workItemContext->TargetProcess = targetProcess; 296 | workItemContext->RemoteAddress = RemoteAddress; 297 | ExQueueWorkItem(&workItemContext->WorkItem, DelayedWorkQueue); 298 | return true; 299 | 300 | FailureEnd:; 301 | if (data) { 302 | ExFreePoolWithTag(data, RWMON_POOL_TAG_NAME); 303 | } 304 | ObDereferenceObject(targetProcess); 305 | return false; 306 | } 307 | 308 | // Check if the write operation is interprocess and from a not white listed 309 | // process 310 | ALLOC_TEXT(PAGED, CheckpIsInterprocessWrite) 311 | _Success_(return == true) EXTERN_C 312 | static bool CheckpIsInterprocessWrite(_In_ HANDLE ProcessHandle, 313 | _Out_ PEPROCESS *TargetProcess) { 314 | PAGED_CODE(); 315 | 316 | if (ProcessHandle == ZwCurrentProcess()) { 317 | return false; 318 | } 319 | 320 | const auto pid = PsGetCurrentProcessId(); 321 | for (auto i = 0; g_CheckpWhiteListedProcessIDs[i]; ++i) { 322 | if (g_CheckpWhiteListedProcessIDs[i] == pid) { 323 | return false; 324 | } 325 | } 326 | 327 | auto status = ObReferenceObjectByHandle( 328 | ProcessHandle, 0, *PsProcessType, UserMode, 329 | reinterpret_cast(TargetProcess), nullptr); 330 | if (!NT_SUCCESS(status)) { 331 | LOG_ERROR_SAFE("ObReferenceObjectByHandle failed (%08x)", status); 332 | return false; 333 | } 334 | 335 | if (*TargetProcess == PsGetCurrentProcess()) { 336 | ObDereferenceObject(*TargetProcess); 337 | return false; 338 | } 339 | return true; 340 | } 341 | 342 | // Copy data from user-space 343 | ALLOC_TEXT(PAGED, CheckpCopyDataFromUserSpace) 344 | EXTERN_C static NTSTATUS CheckpCopyDataFromUserSpace( 345 | _Out_ void *Buffer, _In_ const void *BaseAddress, _In_ ULONG DataSize, 346 | _In_opt_ PEPROCESS TargetProcess) { 347 | PAGED_CODE(); 348 | 349 | auto status = STATUS_UNSUCCESSFUL; 350 | if (TargetProcess) { 351 | // Need to switch to another process memory space to access the data 352 | KAPC_STATE apcState = {}; 353 | KeStackAttachProcess(TargetProcess, &apcState); 354 | status = CheckpTryCopyMemory(Buffer, BaseAddress, DataSize); 355 | KeUnstackDetachProcess(&apcState); 356 | } else { 357 | // The current process contains the data 358 | status = CheckpTryCopyMemory(Buffer, BaseAddress, DataSize); 359 | } 360 | return status; 361 | } 362 | 363 | // RtlCopyMemory wrapped with SEH 364 | ALLOC_TEXT(PAGED, CheckpTryCopyMemory) 365 | EXTERN_C static NTSTATUS CheckpTryCopyMemory(_Out_ void *Destionation, 366 | _In_ const void *Source, 367 | _In_ SIZE_T Length) { 368 | PAGED_CODE(); 369 | 370 | auto status = STATUS_SUCCESS; 371 | __try { 372 | RtlCopyMemory(Destionation, Source, Length); 373 | } __except (EXCEPTION_EXECUTE_HANDLER) { 374 | status = GetExceptionCode(); 375 | } 376 | return status; 377 | } 378 | 379 | // Calculate SHA1 of the data and write it to a file 380 | ALLOC_TEXT(PAGED, CheckpWorkItemRoutine) 381 | EXTERN_C static void CheckpWorkItemRoutine(_In_ void *Context) 382 | { 383 | PAGED_CODE(); 384 | 385 | auto parameter = reinterpret_cast(Context); 386 | 387 | // Calculate SHA1 of the written data 388 | UCHAR sha1Hash[20] = {}; 389 | if (!CheckpGetSha1(sha1Hash, parameter->Data, parameter->DataSize)) 390 | { 391 | goto End; 392 | } 393 | wchar_t sha1HashW[41] = {}; 394 | for (auto i = 0; i < RTL_NUMBER_OF(sha1Hash); ++i) 395 | { 396 | const auto outW = sha1HashW + i * 2; 397 | RtlStringCchPrintfW(outW, 3, L"%02x", sha1Hash[i]); 398 | } 399 | 400 | // Save it to a file 401 | wchar_t outPathW[260]; 402 | auto status = 403 | RtlStringCchPrintfW(outPathW, RTL_NUMBER_OF(outPathW), L"%s\\%s.bin", 404 | g_CheckpLogDirecotry, sha1HashW); 405 | if (!NT_SUCCESS(status)) 406 | { 407 | LOG_ERROR("RtlStringCchPrintfW failed (%08x)", status); 408 | goto End; 409 | } 410 | status = CheckpWriteFile(outPathW, parameter->Data, parameter->DataSize, 411 | GENERIC_WRITE, FILE_CREATE); 412 | if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_COLLISION) 413 | { 414 | LOG_ERROR("WriteFile failed (%08x)", status); 415 | goto End; 416 | } 417 | 418 | const auto collision = 419 | (status == STATUS_OBJECT_NAME_COLLISION) ? "dup with" : "saved as"; 420 | 421 | // Log it 422 | LOG_INFO("Remote %s from %5lu (%-15s) to %5lu (%-15s) at %p (%s %S, %lu bytes)", 423 | (parameter->Type == INTER_PROCESS_WRITE) ? "write" : "map ", 424 | PsGetProcessId(parameter->WriterProcess), 425 | PsGetProcessImageFileName(parameter->WriterProcess), 426 | PsGetProcessId(parameter->TargetProcess), 427 | PsGetProcessImageFileName(parameter->TargetProcess), 428 | parameter->RemoteAddress, collision, sha1HashW, parameter->DataSize); 429 | 430 | End:; 431 | ExFreePoolWithTag(parameter->Data, RWMON_POOL_TAG_NAME); 432 | ObDereferenceObject(parameter->WriterProcess); 433 | ObDereferenceObject(parameter->TargetProcess); 434 | ExFreePoolWithTag(parameter, RWMON_POOL_TAG_NAME); 435 | InterlockedDecrement(&g_CheckpNumberOfActiveWorkQueueItems); 436 | } 437 | 438 | // Calculate SHA1 439 | ALLOC_TEXT(PAGED, CheckpGetSha1) 440 | _Success_(return == true) EXTERN_C 441 | static bool CheckpGetSha1(_Out_ UCHAR(&Sha1Hash)[20], _In_ void *Data, 442 | _In_ ULONG DataSize) { 443 | PAGED_CODE(); 444 | bool result = false; 445 | 446 | BCRYPT_HASH_HANDLE hashHandle = nullptr; 447 | auto status = BCryptCreateHash(g_CheckpSha1AlgorithmHandle, &hashHandle, 448 | nullptr, 0, nullptr, 0, 0); 449 | if (!NT_SUCCESS(status)) { 450 | LOG_ERROR_SAFE("BCryptCreateHash failed (%08x)", status); 451 | goto End; 452 | } 453 | 454 | status = BCryptHashData(hashHandle, static_cast(Data), DataSize, 0); 455 | if (!NT_SUCCESS(status)) { 456 | LOG_ERROR_SAFE("BCryptHashData failed (%08x)", status); 457 | goto End; 458 | } 459 | 460 | static_assert(sizeof(Sha1Hash) == 20, "Size check"); 461 | status = BCryptFinishHash(hashHandle, Sha1Hash, sizeof(Sha1Hash), 0); 462 | if (!NT_SUCCESS(status)) { 463 | LOG_ERROR_SAFE("BCryptFinishHash failed (%08x)", status); 464 | goto End; 465 | } 466 | result = true; 467 | 468 | End:; 469 | if (hashHandle) { 470 | BCryptDestroyHash(hashHandle); 471 | } 472 | return result; 473 | } 474 | 475 | // Write data to a file 476 | ALLOC_TEXT(PAGED, CheckpWriteFile) 477 | EXTERN_C static NTSTATUS CheckpWriteFile(_In_ const wchar_t *OutPathW, 478 | _In_ void *Buffer, 479 | _In_ ULONG BufferSize, 480 | _In_ ACCESS_MASK DesiredAccess, 481 | _In_ ULONG CreateDisposition) { 482 | PAGED_CODE(); 483 | 484 | UNICODE_STRING outPath = {}; 485 | RtlInitUnicodeString(&outPath, OutPathW); 486 | OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( 487 | &outPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); 488 | 489 | IO_STATUS_BLOCK ioStatus = {}; 490 | HANDLE file = nullptr; 491 | auto status = ZwCreateFile( 492 | &file, DesiredAccess, &objAttr, &ioStatus, nullptr, FILE_ATTRIBUTE_NORMAL, 493 | FILE_SHARE_READ | FILE_SHARE_WRITE, CreateDisposition, 494 | FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT | 495 | FILE_NON_DIRECTORY_FILE, 496 | nullptr, 0); 497 | if (!NT_SUCCESS(status)) { 498 | return status; 499 | } 500 | 501 | status = ZwWriteFile(file, nullptr, nullptr, nullptr, &ioStatus, Buffer, 502 | BufferSize, nullptr, nullptr); 503 | ZwClose(file); 504 | return status; 505 | } 506 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/check.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to functions for checking if data is written 7 | // by a remote process and saving it if so. 8 | // 9 | #pragma once 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // 13 | // macro utilities 14 | // 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | // 18 | // constants and macros 19 | // 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // 23 | // types 24 | // 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | // 28 | // prototypes 29 | // 30 | 31 | EXTERN_C NTSTATUS CheckInitialization(_In_ const wchar_t *LogDirectry); 32 | 33 | EXTERN_C void CheckTermination(); 34 | 35 | EXTERN_C bool CheckData(_In_ HANDLE ProcessHandle, _In_ void *RemoteAddress, 36 | _In_opt_ void *Contents, _In_ ULONG DataSize); 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | // 40 | // variables 41 | // 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | // 45 | // implementations 46 | // 47 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/inline.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements inline hook related functions. 7 | // 8 | #include "stdafx.h" 9 | #include "inline.h" 10 | #include "log.h" 11 | #include "util.h" 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | // 15 | // macro utilities 16 | // 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // constants and macros 21 | // 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // 25 | // types 26 | // 27 | 28 | // A structure reflects inline hook code. 29 | #include 30 | struct TrampolineCode { 31 | UCHAR jmp[6]; 32 | FARPROC FunctionAddress; 33 | }; 34 | #include 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | // 38 | // prototypes 39 | // 40 | 41 | EXTERN_C 42 | NTSTATUS static InlinepFixupAsmCode(_In_ UCHAR *OriginalRoutine, 43 | _In_ FARPROC AsmHandler, 44 | _In_ FARPROC AsmHandlerEnd); 45 | 46 | EXTERN_C static TrampolineCode InlinepMakeTrampolineCode( 47 | _In_ UCHAR *HookAddress, _In_ FARPROC HookHandler); 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | // 51 | // variables 52 | // 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | // 56 | // implementations 57 | // 58 | 59 | // Fill out a InlineHookInfo struct with given parameters 60 | ALLOC_TEXT(INIT, InlineInitHookInfo) 61 | EXTERN_C NTSTATUS 62 | InlineInitHookInfo(_In_ UCHAR *HookAddress, _In_ FARPROC HookHandler, 63 | _In_ FARPROC AsmHandler, _In_ FARPROC AsmHandlerEnd, 64 | _Out_ InlineHookInfo *Info) { 65 | PAGED_CODE(); 66 | NT_ASSERT(HookHandler); 67 | NT_ASSERT(AsmHandler); 68 | NT_ASSERT(AsmHandlerEnd); 69 | NT_ASSERT(Info); 70 | 71 | if (!HookAddress) { 72 | return STATUS_INVALID_PARAMETER; 73 | } 74 | 75 | Info->HookHandler = HookHandler; 76 | Info->HookAddress = HookAddress; 77 | Info->OriginalCodeSize = sizeof(TrampolineCode); 78 | memcpy(Info->OriginalCode, Info->HookAddress, Info->OriginalCodeSize); 79 | 80 | auto status = InlinepFixupAsmCode(HookAddress, AsmHandler, AsmHandlerEnd); 81 | if (!NT_SUCCESS(status)) { 82 | return status; 83 | } 84 | 85 | LOG_DEBUG("HookHandler= %p, HookAddress= %p, OriginalCodeSize= %d", 86 | Info->HookHandler, Info->HookAddress, Info->OriginalCodeSize); 87 | 88 | return status; 89 | } 90 | 91 | // Build and return trampoline code. 92 | ALLOC_TEXT(PAGED, InlinepMakeTrampolineCode) 93 | EXTERN_C static TrampolineCode InlinepMakeTrampolineCode( 94 | _In_ UCHAR *HookAddress, _In_ FARPROC HookHandler) { 95 | PAGED_CODE(); 96 | UNREFERENCED_PARAMETER(HookAddress); 97 | 98 | // jmp qword ptr [nextline] 99 | // nextline: 100 | // dq HookHandler 101 | return { 102 | { 103 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 104 | }, 105 | HookHandler, 106 | }; 107 | } 108 | 109 | // Replaces placeholder (0xffffffffffffffff) in AsmHandler with a given 110 | // OriginalRoutine. AsmHandler does not has to be writable. Race condition 111 | // between multiple processors should be taken care of by a programmer. 112 | ALLOC_TEXT(PAGED, InlinepFixupAsmCode) 113 | EXTERN_C 114 | NTSTATUS static InlinepFixupAsmCode(_In_ UCHAR *OriginalRoutine, 115 | _In_ FARPROC AsmHandler, 116 | _In_ FARPROC AsmHandlerEnd) { 117 | PAGED_CODE(); 118 | ASSERT(AsmHandlerEnd > AsmHandler); 119 | 120 | const auto asmHandlerSize = reinterpret_cast(AsmHandlerEnd) - 121 | reinterpret_cast(AsmHandler); 122 | 123 | ULONG64 pattern = 0xffffffffffffffff; 124 | auto addressOfMarker = UtilMemMem(reinterpret_cast(AsmHandler), 125 | asmHandlerSize, &pattern, sizeof(pattern)); 126 | ASSERT(addressOfMarker); 127 | auto destinationAddress = 128 | reinterpret_cast(OriginalRoutine + asmHandlerSize - 15); 129 | return UtilForceMemCpy(addressOfMarker, &destinationAddress, 130 | sizeof(destinationAddress)); 131 | } 132 | 133 | // Install a inline hook (modify code) based on InlineHookInfo. It is not 134 | // multi-processor safe. 135 | EXTERN_C NTSTATUS InlineInstallHook(_In_ const InlineHookInfo &Info) { 136 | LOG_DEBUG("%p => %p", Info.HookAddress, Info.HookHandler); 137 | auto newCode = InlinepMakeTrampolineCode(Info.HookAddress, Info.HookHandler); 138 | 139 | KIRQL oldIrql = 0; 140 | KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); 141 | auto status = UtilForceMemCpy(Info.HookAddress, newCode.jmp, sizeof(newCode)); 142 | UtilInvalidateInstructionCache(Info.HookAddress, sizeof(newCode)); 143 | KeLowerIrql(oldIrql); 144 | return status; 145 | } 146 | 147 | // Uninstall a inline hook (modify code) based on InlineHookInfo. 148 | EXTERN_C NTSTATUS InlineUninstallHook(_In_ const InlineHookInfo &Info) { 149 | KIRQL oldIrql = 0; 150 | KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); 151 | auto status = UtilForceMemCpy(Info.HookAddress, Info.OriginalCode, 152 | Info.OriginalCodeSize); 153 | UtilInvalidateInstructionCache(Info.HookAddress, Info.OriginalCodeSize); 154 | KeLowerIrql(oldIrql); 155 | return status; 156 | } 157 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/inline.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to inline hook related functions. 7 | // 8 | #pragma once 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // macro utilities 13 | // 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // 17 | // constants and macros 18 | // 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // 22 | // types 23 | // 24 | 25 | // Holds a necessary context for installing and uninstalling inline hook. 26 | struct InlineHookInfo { 27 | UCHAR *HookAddress; // An address to install inline hook 28 | FARPROC HookHandler; // A hook handler to be called instead 29 | SIZE_T OriginalCodeSize; // A size of saved original code 30 | UCHAR OriginalCode[32]; // A saved original code 31 | }; 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // 35 | // prototypes 36 | // 37 | 38 | EXTERN_C NTSTATUS 39 | InlineInitHookInfo(_In_ UCHAR *HookAddress, _In_ FARPROC HookHandler, 40 | _In_ FARPROC AsmHandler, _In_ FARPROC AsmHandlerEnd, 41 | _Out_ InlineHookInfo *Info); 42 | 43 | EXTERN_C NTSTATUS InlineInstallHook(_In_ const InlineHookInfo &Info); 44 | 45 | EXTERN_C NTSTATUS InlineUninstallHook(_In_ const InlineHookInfo &Info); 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | // 49 | // variables 50 | // 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | // 54 | // implementations 55 | // 56 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/log.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements logging functions. 7 | // 8 | #include "stdafx.h" 9 | #include "log.h" 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // 13 | // macro utilities 14 | // 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | // 18 | // constant and macro 19 | // 20 | 21 | // A size for log buffer in NonPagedPool. Two buffers are allocated with this 22 | // size. Exceeded logs are ignored silently. Make it bigger if a buffered log 23 | // size often reach this size. 24 | static const auto LOGP_BUFFER_SIZE_IN_PAGES = 5ul; 25 | 26 | // An actual log buffer size in bytes. 27 | static const auto LOGP_BUFFER_SIZE = PAGE_SIZE * LOGP_BUFFER_SIZE_IN_PAGES; 28 | 29 | // A size that is usable for logging. Minus one because the last byte is kept 30 | // for \0. 31 | static const auto LOGP_BUFFER_USABLE_SIZE = LOGP_BUFFER_SIZE - 1; 32 | 33 | // An interval to flush buffered log entries into a log file. 34 | static const auto LOGP_AUTO_FLUSH_INTERVAL_MSEC = 50; 35 | 36 | static const ULONG LOGP_POOL_TAG_NAME = ' gol'; 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | // 40 | // types 41 | // 42 | 43 | struct LogBufferInfo { 44 | volatile char *LogBufferHead; // A pointer to a buffer currently used. 45 | // It is either LogBuffer1 or LogBuffer2. 46 | volatile char *LogBufferTail; // A pointer to where the next log should 47 | // be written. 48 | char *LogBuffer1; 49 | char *LogBuffer2; 50 | SIZE_T LogMaximumUsage; // Holds the biggest buffer usage to 51 | // determine a necessary buffer size. 52 | HANDLE LogFileHandle; 53 | KSPIN_LOCK SpinLock; 54 | ERESOURCE Resource; 55 | bool ResourceInitialized; 56 | volatile bool BufferFlushThreadShouldBeAlive; 57 | HANDLE BufferFlushThreadHandle; 58 | }; 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | // 62 | // prototypes 63 | // 64 | 65 | EXTERN_C NTKERNELAPI UCHAR *NTAPI 66 | PsGetProcessImageFileName(_In_ PEPROCESS Process); 67 | 68 | EXTERN_C static NTSTATUS LogpInitializeBufferInfo( 69 | _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, 70 | _Inout_ LogBufferInfo *Info); 71 | 72 | EXTERN_C static void LogpFinalizeBufferInfo( 73 | _In_opt_ PDEVICE_OBJECT DeviceObject, _In_ LogBufferInfo *Info); 74 | 75 | #ifdef _X86_ 76 | _Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) 77 | _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ 78 | _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL 79 | KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock); 80 | #endif 81 | 82 | EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, 83 | _In_ const char *FunctionName, 84 | _In_ const char *LogMessage, 85 | _Out_ char *LogBuffer, 86 | _In_ size_t LogBufferLength); 87 | 88 | EXTERN_C static const char *LogpFindBaseFunctionName( 89 | _In_ const char *FunctionName); 90 | 91 | EXTERN_C static NTSTATUS LogpPut(_In_ char *Message, 92 | _In_ ULONG Attribute); 93 | 94 | EXTERN_C static NTSTATUS LogpWriteLogBufferToFile(_In_opt_ LogBufferInfo *Info); 95 | 96 | EXTERN_C static NTSTATUS LogpWriteMessageToFile(_In_ const char *Message, 97 | _In_ const LogBufferInfo &Info); 98 | 99 | EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, 100 | _In_opt_ LogBufferInfo *Info); 101 | 102 | EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info); 103 | 104 | EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level); 105 | 106 | EXTERN_C static KSTART_ROUTINE LogpBufferFlushThreadRoutine; 107 | 108 | EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond); 109 | 110 | //////////////////////////////////////////////////////////////////////////////// 111 | // 112 | // variables 113 | // 114 | 115 | static auto g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; 116 | static LogBufferInfo g_LogpLogBufferInfo = {}; 117 | 118 | //////////////////////////////////////////////////////////////////////////////// 119 | // 120 | // implementations 121 | // 122 | 123 | ALLOC_TEXT(INIT, LogInitialization) 124 | EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, 125 | _In_opt_ const wchar_t *LogFilePath, 126 | _In_opt_ PDEVICE_OBJECT DeviceObject) { 127 | PAGED_CODE(); 128 | 129 | auto status = STATUS_SUCCESS; 130 | 131 | g_LogpDebugFlag = Flag; 132 | 133 | if (DeviceObject && !LogFilePath) { 134 | return STATUS_INVALID_PARAMETER; 135 | } 136 | 137 | // Initialize a log file if a log file path is specified. 138 | if (LogFilePath) { 139 | status = LogpInitializeBufferInfo(LogFilePath, DeviceObject, 140 | &g_LogpLogBufferInfo); 141 | if (!NT_SUCCESS(status)) { 142 | return status; 143 | } 144 | } 145 | 146 | // Test the log. 147 | status = LOG_INFO( 148 | "Log system was initialized (Flag= %08x, Buffer= %p %p, File= %S).", Flag, 149 | g_LogpLogBufferInfo.LogBuffer1, g_LogpLogBufferInfo.LogBuffer2, 150 | LogFilePath); 151 | if (!NT_SUCCESS(status)) { 152 | goto Fail; 153 | } 154 | return status; 155 | 156 | Fail: 157 | if (LogFilePath) { 158 | LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); 159 | } 160 | return status; 161 | } 162 | 163 | // Initialize a log file related code such as a flushing thread. 164 | ALLOC_TEXT(INIT, LogpInitializeBufferInfo) 165 | EXTERN_C static NTSTATUS LogpInitializeBufferInfo( 166 | _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, 167 | _Inout_ LogBufferInfo *Info) { 168 | NT_ASSERT(LogFilePath); 169 | NT_ASSERT(Info); 170 | 171 | KeInitializeSpinLock(&Info->SpinLock); 172 | 173 | auto status = ExInitializeResourceLite(&Info->Resource); 174 | if (!NT_SUCCESS(status)) { 175 | return status; 176 | } 177 | Info->ResourceInitialized = true; 178 | 179 | if (DeviceObject) { 180 | // We can handle IRP_MJ_SHUTDOWN in order to flush buffered log entries. 181 | status = IoRegisterShutdownNotification(DeviceObject); 182 | if (!NT_SUCCESS(status)) { 183 | LogpFinalizeBufferInfo(DeviceObject, Info); 184 | return status; 185 | } 186 | } 187 | 188 | // Allocate two log buffers on NonPagedPool. 189 | Info->LogBuffer1 = reinterpret_cast(ExAllocatePoolWithTag( 190 | NonPagedPoolNx, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); 191 | if (!Info->LogBuffer1) { 192 | LogpFinalizeBufferInfo(DeviceObject, Info); 193 | return STATUS_INSUFFICIENT_RESOURCES; 194 | } 195 | 196 | Info->LogBuffer2 = reinterpret_cast(ExAllocatePoolWithTag( 197 | NonPagedPoolNx, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); 198 | if (!Info->LogBuffer2) { 199 | LogpFinalizeBufferInfo(DeviceObject, Info); 200 | return STATUS_INSUFFICIENT_RESOURCES; 201 | } 202 | 203 | // Initialize these buffers 204 | RtlFillMemory(Info->LogBuffer1, LOGP_BUFFER_SIZE, 0xff); // for debug 205 | Info->LogBuffer1[0] = '\0'; 206 | Info->LogBuffer1[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end 207 | 208 | RtlFillMemory(Info->LogBuffer2, LOGP_BUFFER_SIZE, 0xff); // for debug 209 | Info->LogBuffer2[0] = '\0'; 210 | Info->LogBuffer2[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end 211 | 212 | // Buffer should be used is LogBuffer1, and location should be written logs 213 | // is the head of the buffer. 214 | Info->LogBufferHead = Info->LogBuffer1; 215 | Info->LogBufferTail = Info->LogBuffer1; 216 | 217 | // Initialize a log file 218 | UNICODE_STRING logFilePathU = {}; 219 | RtlInitUnicodeString(&logFilePathU, LogFilePath); 220 | 221 | OBJECT_ATTRIBUTES oa = {}; 222 | InitializeObjectAttributes(&oa, &logFilePathU, 223 | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, nullptr, 224 | nullptr); 225 | 226 | IO_STATUS_BLOCK ioStatus = {}; 227 | status = ZwCreateFile( 228 | &Info->LogFileHandle, FILE_APPEND_DATA | SYNCHRONIZE, &oa, &ioStatus, 229 | nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, 230 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, nullptr, 0); 231 | if (!NT_SUCCESS(status)) { 232 | LogpFinalizeBufferInfo(DeviceObject, Info); 233 | return status; 234 | } 235 | 236 | // Initialize a log buffer flush thread. 237 | Info->BufferFlushThreadShouldBeAlive = true; 238 | status = PsCreateSystemThread(&Info->BufferFlushThreadHandle, GENERIC_ALL, 239 | nullptr, nullptr, nullptr, 240 | LogpBufferFlushThreadRoutine, Info); 241 | if (!NT_SUCCESS(status)) { 242 | LogpFinalizeBufferInfo(DeviceObject, Info); 243 | return status; 244 | } 245 | 246 | return status; 247 | } 248 | 249 | // Terminates the log functions without releasing resources. 250 | ALLOC_TEXT(PAGED, LogIrpShutdownHandler) 251 | EXTERN_C void LogIrpShutdownHandler() { 252 | PAGED_CODE(); 253 | 254 | LOG_DEBUG("Flushing... (Max log usage = %08x bytes)", 255 | g_LogpLogBufferInfo.LogMaximumUsage); 256 | LOG_INFO("Bye!"); 257 | g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; 258 | 259 | // Wait until the log buffer is emptied. 260 | auto &info = g_LogpLogBufferInfo; 261 | while (info.LogBufferHead[0]) { 262 | LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); 263 | } 264 | } 265 | 266 | // Terminates the log functions. 267 | ALLOC_TEXT(PAGED, LogTermination) 268 | EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject) { 269 | PAGED_CODE(); 270 | 271 | LOG_DEBUG("Finalizing... (Max log usage = %08x bytes)", 272 | g_LogpLogBufferInfo.LogMaximumUsage); 273 | LOG_INFO("Bye!"); 274 | g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; 275 | LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); 276 | } 277 | 278 | // Terminates a log file related code. 279 | ALLOC_TEXT(PAGED, LogpFinalizeBufferInfo) 280 | EXTERN_C static void LogpFinalizeBufferInfo( 281 | _In_opt_ PDEVICE_OBJECT DeviceObject, _In_ LogBufferInfo *Info) { 282 | PAGED_CODE(); 283 | NT_ASSERT(Info); 284 | 285 | // Closing the log buffer flush thread. 286 | if (Info->BufferFlushThreadHandle) { 287 | Info->BufferFlushThreadShouldBeAlive = false; 288 | auto status = 289 | ZwWaitForSingleObject(Info->BufferFlushThreadHandle, FALSE, nullptr); 290 | if (!NT_SUCCESS(status)) { 291 | DBG_BREAK(); 292 | } 293 | ZwClose(Info->BufferFlushThreadHandle); 294 | Info->BufferFlushThreadHandle = nullptr; 295 | } 296 | 297 | // Cleaning up other things. 298 | if (Info->LogFileHandle) { 299 | ZwClose(Info->LogFileHandle); 300 | Info->LogFileHandle = nullptr; 301 | } 302 | if (Info->LogBuffer2) { 303 | ExFreePoolWithTag(Info->LogBuffer2, LOGP_POOL_TAG_NAME); 304 | Info->LogBuffer2 = nullptr; 305 | } 306 | if (Info->LogBuffer1) { 307 | ExFreePoolWithTag(Info->LogBuffer1, LOGP_POOL_TAG_NAME); 308 | Info->LogBuffer1 = nullptr; 309 | } 310 | 311 | if (DeviceObject) { 312 | IoUnregisterShutdownNotification(DeviceObject); 313 | } 314 | if (Info->ResourceInitialized) { 315 | ExDeleteResourceLite(&Info->Resource); 316 | Info->ResourceInitialized = false; 317 | } 318 | } 319 | 320 | #ifdef _X86_ 321 | _Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) 322 | _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ 323 | _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL 324 | KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock) { 325 | KIRQL irql = {}; 326 | KeAcquireSpinLock(SpinLock, &irql); 327 | return irql; 328 | } 329 | #endif 330 | 331 | // Actual implementation of logging API. 332 | EXTERN_C NTSTATUS LogpPrint(_In_ ULONG Level, _In_ const char *FunctionName, 333 | _In_ const char *Format, ...) { 334 | auto status = STATUS_SUCCESS; 335 | 336 | if (!LogpIsLogNeeded(Level)) { 337 | return status; 338 | } 339 | 340 | va_list args; 341 | va_start(args, Format); 342 | char logMessage[412]; 343 | status = 344 | RtlStringCchVPrintfA(logMessage, RTL_NUMBER_OF(logMessage), Format, args); 345 | va_end(args); 346 | if (!NT_SUCCESS(status)) { 347 | return status; 348 | } 349 | if (logMessage[0] == '\0') { 350 | return STATUS_INVALID_PARAMETER; 351 | } 352 | 353 | const auto pureLevel = Level & 0xf0; 354 | const auto attribute = Level & 0x0f; 355 | 356 | // A single entry of log should not exceed 512 bytes. See 357 | // Reading and Filtering Debugging Messages in MSDN for details. 358 | char message[512]; 359 | static_assert(RTL_NUMBER_OF(message) <= 512, 360 | "One log message should not exceed 512 bytes."); 361 | status = LogpMakePrefix(pureLevel, FunctionName, logMessage, message, 362 | RTL_NUMBER_OF(message)); 363 | if (!NT_SUCCESS(status)) { 364 | return status; 365 | } 366 | 367 | return LogpPut(message, attribute); 368 | } 369 | 370 | // Concatenates meta information such as the current time and a process ID to 371 | // user given log message. 372 | EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, 373 | _In_ const char *FunctionName, 374 | _In_ const char *LogMessage, 375 | _Out_ char *LogBuffer, 376 | _In_ size_t LogBufferLength) { 377 | char const *levelString = nullptr; 378 | switch (Level) { 379 | case LOGP_LEVEL_DEBUG: 380 | levelString = "DBG"; 381 | break; 382 | case LOGP_LEVEL_INFO: 383 | levelString = "INF"; 384 | break; 385 | case LOGP_LEVEL_WARN: 386 | levelString = "WRN"; 387 | break; 388 | case LOGP_LEVEL_ERROR: 389 | levelString = "ERR"; 390 | break; 391 | default: 392 | return STATUS_INVALID_PARAMETER; 393 | } 394 | 395 | auto status = STATUS_SUCCESS; 396 | 397 | char timeBuffer[20] = {}; 398 | if ((g_LogpDebugFlag & LOG_OPT_DISABLE_TIME) == 0) { 399 | // Want the current time. 400 | TIME_FIELDS timeFields; 401 | LARGE_INTEGER systemTime, localTime; 402 | KeQuerySystemTime(&systemTime); 403 | ExSystemTimeToLocalTime(&systemTime, &localTime); 404 | RtlTimeToTimeFields(&localTime, &timeFields); 405 | 406 | status = RtlStringCchPrintfA(timeBuffer, RTL_NUMBER_OF(timeBuffer), 407 | "%02u:%02u:%02u.%03u\t", timeFields.Hour, 408 | timeFields.Minute, timeFields.Second, 409 | timeFields.Milliseconds); 410 | if (!NT_SUCCESS(status)) { 411 | return status; 412 | } 413 | } 414 | 415 | char functionNameBuffer[50] = {}; 416 | if ((g_LogpDebugFlag & LOG_OPT_DISABLE_FUNCTION_NAME) == 0) { 417 | // Want the function name 418 | const auto baseFunctionName = LogpFindBaseFunctionName(FunctionName); 419 | status = RtlStringCchPrintfA(functionNameBuffer, 420 | RTL_NUMBER_OF(functionNameBuffer), "%-40s\t", 421 | baseFunctionName); 422 | if (!NT_SUCCESS(status)) { 423 | return status; 424 | } 425 | } 426 | 427 | // 428 | // It uses PsGetProcessId(PsGetCurrentProcess()) instead of 429 | // PsGetCurrentThreadProcessId() because the later sometimes returns 430 | // unwanted value, for example: 431 | // PID == 4 but its image name != ntoskrnl.exe 432 | // The author is guessing that it is related to attaching processes but 433 | // not quite sure. The former way works as expected. 434 | // 435 | status = RtlStringCchPrintfA( 436 | LogBuffer, LogBufferLength, "%s%s\t%5lu\t%5lu\t%-15s\t%s%s\r\n", timeBuffer, 437 | levelString, 438 | reinterpret_cast(PsGetProcessId(PsGetCurrentProcess())), 439 | reinterpret_cast(PsGetCurrentThreadId()), 440 | PsGetProcessImageFileName(PsGetCurrentProcess()), functionNameBuffer, 441 | LogMessage); 442 | return status; 443 | } 444 | 445 | // Returns the function's base name, for example, 446 | // NamespaceName::ClassName::MethodName will be returned as MethodName. 447 | EXTERN_C static const char *LogpFindBaseFunctionName( 448 | _In_ const char *FunctionName) { 449 | if (!FunctionName) { 450 | return nullptr; 451 | } 452 | 453 | auto ptr = FunctionName; 454 | auto name = FunctionName; 455 | while (*(ptr++)) { 456 | if (*ptr == ':') { 457 | name = ptr + 1; 458 | } 459 | } 460 | return name; 461 | } 462 | 463 | // Logs the entry according to Attribute and the thread condition. 464 | EXTERN_C static NTSTATUS LogpPut(_In_ char *Message, 465 | _In_ ULONG Attribute) { 466 | auto status = STATUS_SUCCESS; 467 | 468 | // Log the entry to a file or buffer. 469 | auto &info = g_LogpLogBufferInfo; 470 | if (LogpIsLogFileEnabled(info)) { 471 | // Can it log it to a file now? 472 | if (((Attribute & LOGP_LEVEL_OPT_SAFE) == 0) && 473 | KeGetCurrentIrql() == PASSIVE_LEVEL && !KeAreAllApcsDisabled()) { 474 | // Yes, it can. Do it. 475 | LogpWriteLogBufferToFile(&info); 476 | status = LogpWriteMessageToFile(Message, info); 477 | } else { 478 | // No, it cannot. Buffer it. 479 | status = LogpBufferMessage(Message, &info); 480 | } 481 | } 482 | 483 | // Can it safely be printed? 484 | if (KeGetCurrentIrql() >= CLOCK_LEVEL) { 485 | return STATUS_UNSUCCESSFUL; 486 | } 487 | 488 | const auto cr = strlen(Message) - 2; 489 | Message[cr] = '\n'; 490 | Message[cr + 1] = '\0'; 491 | DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "%s", Message); 492 | return status; 493 | } 494 | 495 | // Switch the current log buffer and save the contents of old buffer to the log 496 | // file. This function does not flush the log file, so code should call 497 | // LogpWriteMessageToFile() or ZwFlushBuffersFile() later. 498 | EXTERN_C static NTSTATUS LogpWriteLogBufferToFile( 499 | _In_opt_ LogBufferInfo *Info) { 500 | NT_ASSERT(Info); 501 | NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 502 | 503 | auto status = STATUS_SUCCESS; 504 | 505 | // Enter a critical section and acquire a reader lock for Info in order to 506 | // write a log file safely. 507 | ExEnterCriticalRegionAndAcquireResourceExclusive(&Info->Resource); 508 | 509 | // Acquire a spin lock for Info.LogBuffer(s) in order to switch its head 510 | // safely. 511 | const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); 512 | auto oldLogBuffer = const_cast(Info->LogBufferHead); 513 | if (oldLogBuffer[0]) { 514 | Info->LogBufferHead = (oldLogBuffer == Info->LogBuffer1) ? Info->LogBuffer2 515 | : Info->LogBuffer1; 516 | Info->LogBufferHead[0] = '\0'; 517 | Info->LogBufferTail = Info->LogBufferHead; 518 | } 519 | KeReleaseSpinLock(&Info->SpinLock, irql); 520 | 521 | // Write all log entries in old log buffer. 522 | IO_STATUS_BLOCK ioStatus = {}; 523 | for (auto currentLogEntry = oldLogBuffer; currentLogEntry[0]; /**/) { 524 | const auto currentLogEntryLength = strlen(currentLogEntry); 525 | status = 526 | ZwWriteFile(Info->LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, 527 | currentLogEntry, static_cast(currentLogEntryLength), 528 | nullptr, nullptr); 529 | if (!NT_SUCCESS(status)) { 530 | // It could happen when you did not register IRP_SHUTDOWN and call 531 | // LogIrpShutdownHandler() and the system tried to log to a file after 532 | // a filesystem was unmounted. 533 | DBG_BREAK(); 534 | } 535 | 536 | currentLogEntry += currentLogEntryLength + 1; 537 | } 538 | oldLogBuffer[0] = '\0'; 539 | 540 | ExReleaseResourceAndLeaveCriticalRegion(&Info->Resource); 541 | return status; 542 | } 543 | 544 | // Logs the current log entry to and flush the log file. 545 | EXTERN_C static NTSTATUS LogpWriteMessageToFile( 546 | _In_ const char *Message, _In_ const LogBufferInfo &Info) { 547 | NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 548 | 549 | IO_STATUS_BLOCK ioStatus = {}; 550 | auto status = 551 | ZwWriteFile(Info.LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, 552 | const_cast(Message), 553 | static_cast(strlen(Message)), nullptr, nullptr); 554 | if (!NT_SUCCESS(status)) { 555 | // It could happen when you did not register IRP_SHUTDOWN and call 556 | // LogIrpShutdownHandler() and the system tried to log to a file after 557 | // a filesystem was unmounted. 558 | DBG_BREAK(); 559 | } 560 | status = ZwFlushBuffersFile(Info.LogFileHandle, &ioStatus); 561 | return status; 562 | } 563 | 564 | // Buffer the log entry to the log buffer. 565 | EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, 566 | _In_opt_ LogBufferInfo *Info) { 567 | NT_ASSERT(Info); 568 | 569 | // Acquire a spin lock to add the log safely. 570 | const auto oldIrql = KeGetCurrentIrql(); 571 | if (oldIrql < DISPATCH_LEVEL) { 572 | KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); 573 | } 574 | NT_ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); 575 | 576 | // Copy the current log to the buffer. 577 | size_t usedBufferSize = Info->LogBufferTail - Info->LogBufferHead; 578 | auto status = 579 | RtlStringCchCopyA(const_cast(Info->LogBufferTail), 580 | LOGP_BUFFER_USABLE_SIZE - usedBufferSize, Message); 581 | 582 | // Update Info.LogMaximumUsage if necessary. 583 | if (NT_SUCCESS(status)) { 584 | const auto messageLength = strlen(Message) + 1; 585 | Info->LogBufferTail += messageLength; 586 | usedBufferSize += messageLength; 587 | if (usedBufferSize > Info->LogMaximumUsage) { 588 | Info->LogMaximumUsage = usedBufferSize; // Update 589 | } 590 | } else { 591 | Info->LogMaximumUsage = LOGP_BUFFER_SIZE; // Indicates overflow 592 | } 593 | *Info->LogBufferTail = '\0'; 594 | 595 | if (oldIrql < DISPATCH_LEVEL) { 596 | KeReleaseSpinLock(&Info->SpinLock, oldIrql); 597 | } 598 | return status; 599 | } 600 | 601 | // Returns true when a log file is enabled. 602 | EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info) { 603 | if (Info.LogFileHandle) { 604 | NT_ASSERT(Info.LogBuffer1); 605 | NT_ASSERT(Info.LogBuffer2); 606 | NT_ASSERT(Info.LogBufferHead); 607 | NT_ASSERT(Info.LogBufferTail); 608 | return true; 609 | } 610 | NT_ASSERT(!Info.LogBuffer1); 611 | NT_ASSERT(!Info.LogBuffer2); 612 | NT_ASSERT(!Info.LogBufferHead); 613 | NT_ASSERT(!Info.LogBufferTail); 614 | return false; 615 | } 616 | 617 | // Returns true when logging is necessary according to the log's severity and 618 | // a set log level. 619 | EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level) { 620 | return !!(g_LogpDebugFlag & Level); 621 | } 622 | 623 | // A thread runs as long as info.BufferFlushThreadShouldBeAlive is true and 624 | // flushes a log buffer to a log file every LOGP_AUTO_FLUSH_INTERVAL_MSEC msec. 625 | ALLOC_TEXT(PAGED, LogpBufferFlushThreadRoutine) 626 | EXTERN_C static VOID LogpBufferFlushThreadRoutine(_In_ void *StartContext) { 627 | PAGED_CODE(); 628 | auto status = STATUS_SUCCESS; 629 | auto info = reinterpret_cast(StartContext); 630 | LOG_DEBUG("Log thread started."); 631 | NT_ASSERT(LogpIsLogFileEnabled(*info)); 632 | 633 | while (info->BufferFlushThreadShouldBeAlive) { 634 | LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); 635 | if (info->LogBufferHead[0]) { 636 | NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 637 | NT_ASSERT(!KeAreAllApcsDisabled()); 638 | status = LogpWriteLogBufferToFile(info); 639 | // Do not flush the file for overall performance. Even a case of 640 | // bug check, we should be able to recover logs by looking at both 641 | // log buffers. 642 | } 643 | } 644 | LOG_DEBUG("Log thread is ending."); 645 | PsTerminateSystemThread(status); 646 | } 647 | 648 | // Sleep the current thread's execution for Millisecond milli-seconds. 649 | ALLOC_TEXT(PAGED, LogpSleep) 650 | EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond) { 651 | PAGED_CODE(); 652 | 653 | LARGE_INTEGER interval = {}; 654 | interval.QuadPart = -(10000 * Millisecond); // msec 655 | return KeDelayExecutionThread(KernelMode, FALSE, &interval); 656 | } 657 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to logging functions. 7 | // 8 | #pragma once 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // macro utilities 13 | // 14 | 15 | // 16 | // Does log with respective severities. Here are some ideas to decide which 17 | // level is appropriate: 18 | // DEBUG: For developers. 19 | // INFO: For all. 20 | // WARN: For all. It may require some attention but does not prevent the 21 | // program working properly. 22 | // ERROR: For all. It stops the program working properly. 23 | // 24 | #define LOG_DEBUG(format, ...) \ 25 | LogpPrint(LOGP_LEVEL_DEBUG, __FUNCTION__, (format), __VA_ARGS__) 26 | #define LOG_INFO(format, ...) \ 27 | LogpPrint(LOGP_LEVEL_INFO, __FUNCTION__, (format), __VA_ARGS__) 28 | #define LOG_WARN(format, ...) \ 29 | LogpPrint(LOGP_LEVEL_WARN, __FUNCTION__, (format), __VA_ARGS__) 30 | #define LOG_ERROR(format, ...) \ 31 | LogpPrint(LOGP_LEVEL_ERROR, __FUNCTION__, (format), __VA_ARGS__) 32 | 33 | // Buffers the log to buffer. It is recommended to use it when a status of 34 | // callee is no predictable in order to avoid bug checks. 35 | #define LOG_DEBUG_SAFE(format, ...) \ 36 | LogpPrint(LOGP_LEVEL_DEBUG | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ 37 | __VA_ARGS__) 38 | #define LOG_INFO_SAFE(format, ...) \ 39 | LogpPrint(LOGP_LEVEL_INFO | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ 40 | __VA_ARGS__) 41 | #define LOG_WARN_SAFE(format, ...) \ 42 | LogpPrint(LOGP_LEVEL_WARN | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ 43 | __VA_ARGS__) 44 | #define LOG_ERROR_SAFE(format, ...) \ 45 | LogpPrint(LOGP_LEVEL_ERROR | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ 46 | __VA_ARGS__) 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | // 50 | // constants and macros 51 | // 52 | 53 | // (internal) Save this log to buffer and not try to write to a log file. 54 | static const auto LOGP_LEVEL_OPT_SAFE = 0x1ul; 55 | 56 | // (internal) Log levels. 57 | static const auto LOGP_LEVEL_DEBUG = 0x10ul; 58 | static const auto LOGP_LEVEL_INFO = 0x20ul; 59 | static const auto LOGP_LEVEL_WARN = 0x40ul; 60 | static const auto LOGP_LEVEL_ERROR = 0x80ul; 61 | 62 | // For LogInitialization(). Specifies what level of verbosity is needed. 63 | static const auto LOG_PUT_LEVEL_DEBUG = 64 | LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO | LOGP_LEVEL_DEBUG; 65 | static const auto LOG_PUT_LEVEL_INFO = 66 | LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO; 67 | static const auto LOG_PUT_LEVEL_WARN = LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN; 68 | static const auto LOG_PUT_LEVEL_ERROR = LOGP_LEVEL_ERROR; 69 | static const auto LOG_PUT_LEVEL_DISABLE = 0x00ul; 70 | 71 | // For LogInitialization(). Does not log a current time. 72 | static const auto LOG_OPT_DISABLE_TIME = 0x100ul; 73 | 74 | // For LogInitialization(). Does not log a current function name. 75 | static const auto LOG_OPT_DISABLE_FUNCTION_NAME = 0x200ul; 76 | 77 | //////////////////////////////////////////////////////////////////////////////// 78 | // 79 | // types 80 | // 81 | 82 | //////////////////////////////////////////////////////////////////////////////// 83 | // 84 | // prototypes 85 | // 86 | 87 | EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, 88 | _In_opt_ const wchar_t *FilePath, 89 | _In_opt_ PDEVICE_OBJECT DeviceObject); 90 | 91 | EXTERN_C void LogIrpShutdownHandler(); 92 | 93 | EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject); 94 | 95 | EXTERN_C NTSTATUS LogpPrint(_In_ ULONG Level, _In_ const char *FunctionName, 96 | _In_ const char *Format, ...); 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | // 100 | // variables 101 | // 102 | 103 | //////////////////////////////////////////////////////////////////////////////// 104 | // 105 | // implementations 106 | // 107 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/ssdt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements SSDT hook related functions. 7 | // 8 | #include "stdafx.h" 9 | #include "ssdt.h" 10 | #include "log.h" 11 | #include "util.h" 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | // 15 | // macro utilities 16 | // 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // constants and macros 21 | // 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // 25 | // types 26 | // 27 | 28 | struct SERVICE_DESCRIPTOR_TABLE { 29 | PULONG ServiceTable; 30 | PULONG CounterTable; 31 | ULONG_PTR TableSize; 32 | PUCHAR ArgumentTable; 33 | }; 34 | static_assert(sizeof(SERVICE_DESCRIPTOR_TABLE) == sizeof(void *) * 4, 35 | "Size check"); 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | // 39 | // prototypes 40 | // 41 | 42 | EXTERN_C PVOID NTAPI 43 | RtlPcToFileHeader(_In_ PVOID PcValue, _Out_ PVOID *BaseOfImage); 44 | 45 | EXTERN_C static SERVICE_DESCRIPTOR_TABLE *SSDTpFindTable(); 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | // 49 | // variables 50 | // 51 | 52 | static SERVICE_DESCRIPTOR_TABLE *g_SSDTpTable = nullptr; 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | // 56 | // implementations 57 | // 58 | 59 | // Initialize the SSDT subsystem 60 | ALLOC_TEXT(INIT, SSDTInitialization) 61 | EXTERN_C NTSTATUS SSDTInitialization() { 62 | PAGED_CODE(); 63 | 64 | g_SSDTpTable = SSDTpFindTable(); 65 | if (!g_SSDTpTable) { 66 | return STATUS_UNSUCCESSFUL; 67 | } 68 | return STATUS_SUCCESS; 69 | } 70 | 71 | // Returns an address of KeServiceDescriptorTable 72 | ALLOC_TEXT(INIT, SSDTpFindTable) 73 | EXTERN_C static SERVICE_DESCRIPTOR_TABLE *SSDTpFindTable() { 74 | PAGED_CODE(); 75 | 76 | if (!IsX64()) { 77 | UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeServiceDescriptorTable"); 78 | return reinterpret_cast( 79 | MmGetSystemRoutineAddress(&name)); 80 | } 81 | 82 | // 83 | // On x64, we have to manually locate an address of nt!KeServiceDescriptorTable 84 | // because it is neither exported and referenced from _ETHREAD.Tcb.ServiceTable. 85 | // A relatively widely used and stable way to get it is finding an offset to 86 | // the table from the image base by searching KeAddSystemServiceTable. For more 87 | // details, see this thread. 88 | // https://code.google.com/p/volatility/issues/detail?id=189 89 | // 90 | UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeAddSystemServiceTable"); 91 | auto pKeAddSystemServiceTable = 92 | reinterpret_cast(MmGetSystemRoutineAddress(&name)); 93 | if (!pKeAddSystemServiceTable) { 94 | return nullptr; 95 | } 96 | 97 | UNICODE_STRING name2 = RTL_CONSTANT_STRING(L"RtlPcToFileHeader"); 98 | auto pRtlPcToFileHeader = reinterpret_cast( 99 | MmGetSystemRoutineAddress(&name2)); 100 | if (!pRtlPcToFileHeader) { 101 | return nullptr; 102 | } 103 | 104 | // Locate an offset to the KeServiceDescriptorTable 105 | ULONG offset = 0; 106 | for (auto i = 0; i < 0x40; ++i) { 107 | auto dwordBytes = *reinterpret_cast(pKeAddSystemServiceTable + i); 108 | // 4?83bc?? cmp qword ptr [r?+r?+ ... 109 | if ((dwordBytes & 0x00fffff0) == 0x00bc8340) { 110 | // offset <= ... ????????h] 111 | offset = *reinterpret_cast(pKeAddSystemServiceTable + i + 4); 112 | break; 113 | } 114 | } 115 | if (!offset) { 116 | return nullptr; 117 | } 118 | 119 | // Get a base address of ntoskrnl.exe 120 | UCHAR *base = nullptr; 121 | if (!pRtlPcToFileHeader(pKeAddSystemServiceTable, 122 | reinterpret_cast(&base))) { 123 | return nullptr; 124 | } 125 | return reinterpret_cast(base + offset); 126 | } 127 | 128 | // Terminates the SSDT subsystem 129 | ALLOC_TEXT(PAGED, SSDTTermination) 130 | EXTERN_C void SSDTTermination() { PAGED_CODE(); } 131 | 132 | // Returns an address of a system service API specified by the Index 133 | ALLOC_TEXT(PAGED, SSDTGetProcAdderss) 134 | EXTERN_C FARPROC SSDTGetProcAdderss(_In_ ULONG Index) { 135 | PAGED_CODE(); 136 | 137 | if (IsX64()) { 138 | return reinterpret_cast( 139 | (g_SSDTpTable->ServiceTable[Index] >> 4) + 140 | reinterpret_cast(g_SSDTpTable->ServiceTable)); 141 | } else { 142 | return reinterpret_cast(g_SSDTpTable->ServiceTable[Index]); 143 | } 144 | } 145 | 146 | // Get an original value of the SSDT and replace it with a new value. 147 | EXTERN_C void SSDTSetProcAdderss(_In_ ULONG Index, _In_ FARPROC HookRoutine) { 148 | 149 | #ifdef _AMD64_ 150 | UNREFERENCED_PARAMETER(Index); 151 | UNREFERENCED_PARAMETER(HookRoutine); 152 | DBG_BREAK(); 153 | #else 154 | 155 | // Need to rise IRQL not to allow the system to change an execution processor 156 | // during the operation because this code changes a state of processor (CR0). 157 | KIRQL oldIrql = 0; 158 | KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); 159 | 160 | UtilDisableWriteProtect(); 161 | g_SSDTpTable->ServiceTable[Index] = reinterpret_cast(HookRoutine); 162 | UtilEnableWriteProtect(); 163 | KeLowerIrql(oldIrql); 164 | 165 | #endif 166 | 167 | } 168 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to SSDT hook related functions. 7 | // 8 | #pragma once 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // macro utilities 13 | // 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // 17 | // constants and macros 18 | // 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // 22 | // types 23 | // 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // 27 | // prototypes 28 | // 29 | 30 | EXTERN_C NTSTATUS SSDTInitialization(); 31 | 32 | EXTERN_C void SSDTTermination(); 33 | 34 | EXTERN_C FARPROC SSDTGetProcAdderss(_In_ ULONG Index); 35 | 36 | EXTERN_C void SSDTSetProcAdderss(_In_ ULONG Index, _In_ FARPROC HookRoutine); 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | // 40 | // variables 41 | // 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | // 45 | // implementations 46 | // 47 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // stdafx.cpp : source file that includes just the standard includes 6 | // meow.pch will be the pre-compiled header 7 | // stdafx.obj will contain the pre-compiled type information 8 | 9 | #include "stdafx.h" 10 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/stdafx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // stdafx.h : include file for standard system include files, 7 | // or project specific include files that are used frequently, but 8 | // are changed infrequently 9 | // 10 | 11 | #pragma once 12 | 13 | extern "C" { 14 | #pragma warning(push, 0) 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #define NTSTRSAFE_NO_CB_FUNCTIONS 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #pragma warning(pop) 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | // 33 | // macro utilities 34 | // 35 | 36 | // Specifies where the code should be located 37 | #ifdef ALLOC_PRAGMA 38 | #define ALLOC_TEXT(Section, Name) __pragma(alloc_text(Section, Name)) 39 | #else 40 | #define ALLOC_TEXT(Section, Name) 41 | #endif 42 | 43 | // Break point that works only when a debugger is enabled 44 | #ifndef DBG_BREAK 45 | #ifdef _ARM_ 46 | // Nullify it since an ARM device never allow us to attach a debugger. 47 | #define DBG_BREAK() 48 | #else // _ARM_ 49 | #define DBG_BREAK() \ 50 | if (KD_DEBUGGER_ENABLED) { \ 51 | __debugbreak(); \ 52 | } else { \ 53 | } \ 54 | reinterpret_cast(0) 55 | #endif // _ARM_ 56 | #endif // DBG_BREAK 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | // 60 | // constants and macros 61 | // 62 | 63 | static const ULONG RWMON_POOL_TAG_NAME = 'nmwr'; 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | // 67 | // types 68 | // 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | // 72 | // prototypes 73 | // 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | // 77 | // variables 78 | // 79 | 80 | //////////////////////////////////////////////////////////////////////////////// 81 | // 82 | // implementations 83 | // 84 | 85 | // Returns true when it is running on the x64 system. 86 | inline bool IsX64() { 87 | #ifdef _AMD64_ 88 | return true; 89 | #else 90 | return false; 91 | #endif 92 | } -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/util.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module implements various utility functions. 7 | // 8 | #include "stdafx.h" 9 | #include "util.h" 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // 13 | // macro utilities 14 | // 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | // 18 | // constants and macros 19 | // 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // 23 | // types 24 | // 25 | 26 | union CR0_REGISTER { 27 | ULONG_PTR Value; 28 | struct { 29 | unsigned PE : 1; // [0] Protected Mode Enabled 30 | unsigned MP : 1; // [1] Monitor Coprocessor FLAG 31 | unsigned EM : 1; // [2] Emulate FLAG 32 | unsigned TS : 1; // [3] Task Switched FLAG 33 | unsigned ET : 1; // [4] Extension Type FLAG 34 | unsigned NE : 1; // [5] Numeric Error 35 | unsigned Reserved1 : 10; // [6-15] 36 | unsigned WP : 1; // [16] Write Protect 37 | unsigned Reserved2 : 1; // [17] 38 | unsigned AM : 1; // [18] Alignment Mask 39 | unsigned Reserved3 : 10; // [19-28] 40 | unsigned NW : 1; // [29] Not Write-Through 41 | unsigned CD : 1; // [30] Cache Disable 42 | unsigned PG : 1; // [31] Paging Enabled 43 | } Fields; 44 | }; 45 | static_assert(sizeof(CR0_REGISTER) == sizeof(void *), "Size check"); 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | // 49 | // prototypes 50 | // 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | // 54 | // variables 55 | // 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | // 59 | // implementations 60 | // 61 | 62 | // Disable the write protection 63 | EXTERN_C void UtilDisableWriteProtect() { 64 | CR0_REGISTER cr0 = {__readcr0()}; 65 | cr0.Fields.WP = false; 66 | __writecr0(cr0.Value); 67 | } 68 | 69 | // Enable the write protection 70 | EXTERN_C void UtilEnableWriteProtect() { 71 | CR0_REGISTER cr0 = {__readcr0()}; 72 | cr0.Fields.WP = true; 73 | __writecr0(cr0.Value); 74 | } 75 | 76 | // memmem(). 77 | EXTERN_C void *UtilMemMem(_In_ const void *SearchBase, _In_ SIZE_T SearchSize, 78 | _In_ const void *Pattern, _In_ SIZE_T PatternSize) { 79 | if (PatternSize > SearchSize) { 80 | return nullptr; 81 | } 82 | auto searchBase = static_cast(SearchBase); 83 | for (size_t i = 0; i <= SearchSize - PatternSize; i++) { 84 | if (!memcmp(Pattern, &searchBase[i], PatternSize)) { 85 | return const_cast(&searchBase[i]); 86 | } 87 | } 88 | return nullptr; 89 | } 90 | 91 | // Does memcpy safely even if Destination is a read only region. 92 | EXTERN_C NTSTATUS UtilForceMemCpy(_In_ void *Destination, 93 | _In_ const void *Source, _In_ SIZE_T Length) { 94 | auto mdl = IoAllocateMdl(Destination, static_cast(Length), FALSE, FALSE, 95 | nullptr); 96 | if (!mdl) { 97 | return STATUS_INSUFFICIENT_RESOURCES; 98 | } 99 | MmBuildMdlForNonPagedPool(mdl); 100 | 101 | // 102 | // Following MmMapLockedPagesSpecifyCache() call causes bug check in case 103 | // you are using Driver Verifier. The reason is explained as follows: 104 | // 105 | // A driver must not try to create more than one system-address-space 106 | // mapping for an MDL. Additionally, because an MDL that is built by the 107 | // MmBuildMdlForNonPagedPool routine is already mapped to the system 108 | // address space, a driver must not try to map this MDL into the system 109 | // address space again by using the MmMapLockedPagesSpecifyCache routine. 110 | // -- MSDN 111 | // 112 | // This flag modification hacks Driver Verifier's check and prevent leading 113 | // bug check. 114 | // 115 | mdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL; 116 | mdl->MdlFlags |= MDL_PAGES_LOCKED; 117 | 118 | auto writableDest = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, nullptr, 119 | FALSE, NormalPagePriority); 120 | if (!writableDest) { 121 | IoFreeMdl(mdl); 122 | return STATUS_INSUFFICIENT_RESOURCES; 123 | } 124 | memcpy(writableDest, Source, Length); 125 | MmUnmapLockedPages(writableDest, mdl); 126 | IoFreeMdl(mdl); 127 | return STATUS_SUCCESS; 128 | } 129 | 130 | // Invalidates an instruction cache for the specified region. 131 | EXTERN_C void UtilInvalidateInstructionCache(_In_ void *BaseAddress, 132 | _In_ SIZE_T Length) { 133 | UNREFERENCED_PARAMETER(BaseAddress); 134 | UNREFERENCED_PARAMETER(Length); 135 | #if _AMD64_ 136 | __faststorefence(); 137 | #else 138 | _mm_sfence(); 139 | #endif 140 | } 141 | 142 | // Sleep. 143 | ALLOC_TEXT(PAGED, UtilSleep) 144 | EXTERN_C NTSTATUS UtilSleep(_In_ LONG Millisecond) 145 | { 146 | PAGED_CODE(); 147 | 148 | LARGE_INTEGER interval = {}; 149 | interval.QuadPart = -(10000 * Millisecond); // msec 150 | return KeDelayExecutionThread(KernelMode, FALSE, &interval); 151 | } 152 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/RemoteWriteMonitor/util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // 6 | // This module declares interfaces to various utility functions. 7 | // 8 | #pragma once 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // macro utilities 13 | // 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // 17 | // constants and macros 18 | // 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // 22 | // types 23 | // 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // 27 | // prototypes 28 | // 29 | 30 | EXTERN_C void UtilDisableWriteProtect(); 31 | 32 | EXTERN_C void UtilEnableWriteProtect(); 33 | 34 | EXTERN_C void *UtilMemMem(_In_ const void *SearchBase, _In_ SIZE_T SearchSize, 35 | _In_ const void *Pattern, _In_ SIZE_T PatternSize); 36 | 37 | EXTERN_C NTSTATUS UtilForceMemCpy(_In_ void *Destination, 38 | _In_ const void *Source, _In_ SIZE_T Length); 39 | 40 | EXTERN_C void UtilInvalidateInstructionCache(_In_ void *BaseAddress, 41 | _In_ SIZE_T Length); 42 | 43 | EXTERN_C NTSTATUS UtilSleep(_In_ LONG Millisecond); 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | // 47 | // variables 48 | // 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // implementations 53 | // 54 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/ScopedResource/README.md: -------------------------------------------------------------------------------- 1 | ScopedResource 2 | ============== 3 | 4 | Scoped Resource - Generic RAII Wrapper for the Standard Library by Peter Sommerlad and Andrew L. Sandoval 5 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/ScopedResource/scope_exit.h: -------------------------------------------------------------------------------- 1 | // 2 | // N4189: Scoped Resource - Generic RAII Wrapper for the Standard Library 3 | // Peter Sommerlad and Andrew L. Sandoval 4 | // 5 | #ifndef SCOPE_EXIT_H_ 6 | #define SCOPE_EXIT_H_ 7 | #ifndef _MSC_VER 8 | #define SCOPE_GUARD_NOEXCEPT(...) noexpect(__VA_ARGS__) 9 | #else 10 | #define SCOPE_GUARD_NOEXCEPT(...) 11 | #endif 12 | #if _HAS_EXCEPTIONS 13 | #define SCOPE_GUARD_TRY_BEGIN try { 14 | #define SCOPE_GUARD_CATCH_ALL } catch (...) { 15 | #define SCOPE_GUARD_CATCH_END } 16 | #else 17 | #define SCOPE_GUARD_TRY_BEGIN {{ 18 | #define SCOPE_GUARD_CATCH_ALL \ 19 | __pragma(warning(push)) \ 20 | __pragma(warning(disable:4127)) \ 21 | } if (0) { \ 22 | __pragma(warning(pop)) 23 | #define SCOPE_GUARD_CATCH_END }} 24 | #endif 25 | 26 | // modeled slightly after Andrescu’s talk and article(s) 27 | namespace std { 28 | namespace experimental { 29 | template 30 | struct scope_exit 31 | { 32 | // construction 33 | explicit 34 | scope_exit(EF &&f) SCOPE_GUARD_NOEXCEPT() 35 | : exit_function(std::move(f)) 36 | , execute_on_destruction{ true } 37 | { 38 | } 39 | // move 40 | scope_exit(scope_exit &&rhs) SCOPE_GUARD_NOEXCEPT() 41 | : exit_function(std::move(rhs.exit_function)) 42 | , execute_on_destruction{ rhs.execute_on_destruction } 43 | { 44 | rhs.release(); 45 | } 46 | // release 47 | ~scope_exit() SCOPE_GUARD_NOEXCEPT(noexcept(this->exit_function())) 48 | { 49 | if (execute_on_destruction) 50 | this->exit_function(); 51 | } 52 | void release() SCOPE_GUARD_NOEXCEPT() 53 | { 54 | this->execute_on_destruction = false; 55 | } 56 | private: 57 | scope_exit(scope_exit const &) = delete; 58 | void operator=(scope_exit const &) = delete; 59 | scope_exit& operator=(scope_exit &&) = delete; 60 | EF exit_function; 61 | bool execute_on_destruction; // exposition only 62 | }; 63 | template 64 | auto make_scope_exit(EF &&exit_function) -> decltype(scope_exit>(std::forward(exit_function))) SCOPE_GUARD_NOEXCEPT() 65 | { 66 | return scope_exit>(std::forward(exit_function)); 67 | } 68 | } 69 | } 70 | 71 | #undef SCOPE_GUARD_TRY_BEGIN 72 | #undef SCOPE_GUARD_CATCH_ALL 73 | #undef SCOPE_GUARD_CATCH_END 74 | #undef SCOPE_GUARD_NOEXCEPT 75 | #endif /* SCOPE_EXIT_H_ */ -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/ScopedResource/unique_resource.h: -------------------------------------------------------------------------------- 1 | // 2 | // N4189: Scoped Resource - Generic RAII Wrapper for the Standard Library 3 | // Peter Sommerlad and Andrew L. Sandoval 4 | // 5 | #ifndef UNIQUE_RESOURCE_H_ 6 | #define UNIQUE_RESOURCE_H_ 7 | 8 | #include 9 | 10 | #ifndef _MSC_VER 11 | #define UNIQUE_RESOURCE_NOEXCEPT(...) noexpect(__VA_ARGS__) 12 | #else 13 | #define UNIQUE_RESOURCE_NOEXCEPT(...) 14 | #endif 15 | #if _HAS_EXCEPTIONS 16 | #define UNIQUE_RESOURCE_TRY_BEGIN try { 17 | #define UNIQUE_RESOURCE_CATCH_ALL } catch (...) { 18 | #define UNIQUE_RESOURCE_CATCH_END } 19 | #else 20 | #define UNIQUE_RESOURCE_TRY_BEGIN {{ 21 | #define UNIQUE_RESOURCE_CATCH_ALL \ 22 | __pragma(warning(push)) \ 23 | __pragma(warning(disable:4127)) \ 24 | } if (0) { \ 25 | __pragma(warning(pop)) 26 | #define UNIQUE_RESOURCE_CATCH_END }} 27 | #endif 28 | 29 | namespace std { 30 | namespace experimental { 31 | template 32 | class unique_resource 33 | { 34 | R resource; 35 | D deleter; 36 | bool execute_on_destruction; // exposition only 37 | unique_resource& operator=(unique_resource const &) = delete; 38 | unique_resource(unique_resource const &) = delete; // no copies! 39 | public: 40 | // construction 41 | explicit 42 | unique_resource(R && resource, D && deleter, bool shouldrun = true) UNIQUE_RESOURCE_NOEXCEPT() 43 | : resource(std::move(resource)) 44 | , deleter(std::move(deleter)) 45 | , execute_on_destruction{ shouldrun } 46 | { 47 | } 48 | // move 49 | unique_resource(unique_resource &&other) UNIQUE_RESOURCE_NOEXCEPT() 50 | : resource(std::move(other.resource)) 51 | , deleter(std::move(other.deleter)) 52 | , execute_on_destruction{ other.execute_on_destruction } 53 | { 54 | other.release(); 55 | } 56 | unique_resource& 57 | operator=(unique_resource &&other) UNIQUE_RESOURCE_NOEXCEPT(noexcept(this->reset())) 58 | { 59 | this->reset(); 60 | this->deleter = std::move(other.deleter); 61 | this->resource = std::move(other.resource); 62 | this->execute_on_destruction = other.execute_on_destruction; 63 | other.release(); 64 | return *this; 65 | } 66 | // resource release 67 | ~unique_resource() UNIQUE_RESOURCE_NOEXCEPT(noexcept(this->reset())) 68 | { 69 | this->reset(); 70 | 71 | } 72 | void reset() UNIQUE_RESOURCE_NOEXCEPT(noexcept(this->get_deleter()(resource))) 73 | { 74 | if (execute_on_destruction) 75 | { 76 | this->execute_on_destruction = false; 77 | this->get_deleter()(resource); 78 | } 79 | } 80 | void reset(R && newresource) UNIQUE_RESOURCE_NOEXCEPT(noexcept(this->reset())) 81 | { 82 | this->reset(); 83 | this->resource = std::move(newresource); 84 | this->execute_on_destruction = true; 85 | } 86 | R const & release() UNIQUE_RESOURCE_NOEXCEPT() 87 | { 88 | this->execute_on_destruction = false; 89 | return this->get(); 90 | } 91 | // resource access 92 | R const & get() const UNIQUE_RESOURCE_NOEXCEPT() 93 | { 94 | return this->resource; 95 | } 96 | operator R const &() const UNIQUE_RESOURCE_NOEXCEPT() 97 | { 98 | return this->resource; 99 | } 100 | R 101 | operator->() const UNIQUE_RESOURCE_NOEXCEPT() 102 | { 103 | return this->resource; 104 | } 105 | std::add_lvalue_reference_t < 106 | std::remove_pointer_t < R >> 107 | operator*() const 108 | { 109 | return *this->resource; 110 | } 111 | // deleter access 112 | const D & 113 | get_deleter() const UNIQUE_RESOURCE_NOEXCEPT() 114 | { 115 | return this->deleter; 116 | } 117 | }; 118 | //factories 119 | template 120 | auto 121 | make_unique_resource(R && r, D &&d) -> decltype(unique_resource>( 122 | std::move(r) 123 | , std::forward>(d) 124 | , true)) UNIQUE_RESOURCE_NOEXCEPT() 125 | { 126 | return unique_resource>( 127 | std::move(r) 128 | , std::forward>(d) 129 | , true); 130 | 131 | } 132 | template 133 | auto 134 | make_unique_resource_checked(R r, R invalid, D d) -> decltype(unique_resource(std::move(r), std::move(d), shouldrun)) UNIQUE_RESOURCE_NOEXCEPT() 135 | { 136 | bool shouldrun = not bool(r == invalid); 137 | return unique_resource(std::move(r), std::move(d), shouldrun); 138 | } 139 | } 140 | } 141 | 142 | 143 | #undef UNIQUE_RESOURCE_TRY_BEGIN 144 | #undef UNIQUE_RESOURCE_CATCH_ALL 145 | #undef UNIQUE_RESOURCE_CATCH_END 146 | #undef UNIQUE_RESOURCE_NOEXCEPT 147 | #endif /* UNIQUE_RESOURCE_H_ */ -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/TestInjector.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, tandasat. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "stdafx.h" 6 | 7 | // C/C++ standard headers 8 | // Other external headers 9 | // Windows headers 10 | // Original headers 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | // 14 | // macro utilities 15 | // 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | // 19 | // constants and macros 20 | // 21 | 22 | static const auto PAGE_SIZE = 0x1000; 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // 26 | // types 27 | // 28 | 29 | namespace ntdll { 30 | 31 | NTSTATUS NTAPI 32 | ZwCreateSection(_Out_ PHANDLE SectionHandle, _In_ ACCESS_MASK DesiredAccess, 33 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 34 | _In_opt_ PLARGE_INTEGER MaximumSize, 35 | _In_ ULONG SectionPageProtection, 36 | _In_ ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle); 37 | 38 | enum SECTION_INHERIT { 39 | ViewShare = 1, 40 | ViewUnmap = 2, 41 | }; 42 | 43 | NTSTATUS NTAPI 44 | ZwMapViewOfSection(_In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, 45 | _Inout_ PVOID* BaseAddress, _In_ ULONG_PTR ZeroBits, 46 | _In_ SIZE_T CommitSize, 47 | _Inout_opt_ PLARGE_INTEGER SectionOffset, 48 | _Inout_ PSIZE_T ViewSize, 49 | _In_ SECTION_INHERIT InheritDisposition, 50 | _In_ ULONG AllocationType, _In_ ULONG Win32Protect); 51 | 52 | NTSTATUS NTAPI 53 | ZwUnmapViewOfSection(_In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress); 54 | 55 | NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle); 56 | 57 | NTSTATUS NTAPI ZwSuspendProcess(_In_ HANDLE ProcessHandle); 58 | 59 | NTSTATUS NTAPI ZwResumeProcess(_In_ HANDLE ProcessHandle); 60 | 61 | } // ntdll 62 | 63 | struct REMOTE_THREAD_CONTEXT { 64 | using CreateThreadType = decltype(&::CreateThread); 65 | using CloseHandleType = decltype(&::CloseHandle); 66 | using SleepType = decltype(&::Sleep); 67 | using AllocConsoleType = decltype(&::AllocConsole); 68 | using GetStdHandleType = decltype(&::GetStdHandle); 69 | using WriteConsoleAType = decltype(&::WriteConsoleA); 70 | DWORD Tid; 71 | void* ThreadEntryPoint; 72 | CreateThreadType CreateThread; 73 | CloseHandleType CloseHandle; 74 | SleepType Sleep; 75 | AllocConsoleType AllocConsole; 76 | GetStdHandleType GetStdHandle; 77 | WriteConsoleAType WriteConsoleA; 78 | }; 79 | 80 | struct REMOTE_MEMORY_BLOCK { 81 | REMOTE_THREAD_CONTEXT Parameter; 82 | std::uint8_t Code[PAGE_SIZE - sizeof(REMOTE_THREAD_CONTEXT)]; 83 | }; 84 | static_assert(sizeof(REMOTE_MEMORY_BLOCK) == PAGE_SIZE, "Size check"); 85 | 86 | //////////////////////////////////////////////////////////////////////////////// 87 | // 88 | // prototypes 89 | // 90 | 91 | bool AppMain(const std::vector& Args); 92 | 93 | void Usage(); 94 | 95 | HANDLE Open32BitProcess(DWORD ProcessId); 96 | 97 | REMOTE_THREAD_CONTEXT MakeRemoteThreadContext(void* ThreadEntryPoint); 98 | std::shared_ptr InstallCodeWithVirtualAllocEx( 99 | HANDLE ProcessHandle); 100 | 101 | std::shared_ptr InstallCodeWithZwMapViewOfSection( 102 | HANDLE ProcessHandle); 103 | 104 | DWORD ExecuteCodeWithCreateRemoteThread(HANDLE ProcessHandle, 105 | REMOTE_MEMORY_BLOCK* RemoteMemory); 106 | 107 | DWORD ExecuteCodeWithSetThreadContext(HANDLE ProcessHandle, 108 | REMOTE_MEMORY_BLOCK* RemoteMemory); 109 | 110 | DWORD WINAPI RemoteCode(REMOTE_THREAD_CONTEXT* Context); 111 | 112 | void RemoteCodeAsm(); 113 | 114 | void RemoteCodeZZZ(); 115 | 116 | //////////////////////////////////////////////////////////////////////////////// 117 | // 118 | // variables 119 | // 120 | 121 | //////////////////////////////////////////////////////////////////////////////// 122 | // 123 | // implementations 124 | // 125 | 126 | int _tmain(int argc, _TCHAR* argv[]) { 127 | auto result = EXIT_FAILURE; 128 | try { 129 | std::vector args; 130 | for (auto i = 0; i < argc; ++i) { 131 | args.push_back(argv[i]); 132 | } 133 | if (AppMain(args)) { 134 | result = EXIT_SUCCESS; 135 | } 136 | } catch (std::exception& e) { 137 | std::cout << e.what() << std::endl; 138 | } catch (...) { 139 | std::cout << "An unhandled exception raised." << std::endl; 140 | } 141 | return result; 142 | } 143 | 144 | // Main 145 | bool AppMain(const std::vector& Args) { 146 | if (Args.size() != 4) { 147 | Usage(); 148 | return true; 149 | } 150 | 151 | const auto pid = std::stoul(Args[1]); 152 | const auto installationMethod = std::wstring(Args[2]); 153 | const auto executionMethod = std::wstring(Args[3]); 154 | 155 | std::shared_ptr(*installer)(HANDLE) = nullptr; 156 | if (installationMethod == L"alloc") { 157 | installer = InstallCodeWithVirtualAllocEx; 158 | } else if (installationMethod == L"section") { 159 | installer = InstallCodeWithZwMapViewOfSection; 160 | } 161 | DWORD (*executer)(HANDLE, REMOTE_MEMORY_BLOCK*) = nullptr; 162 | if (executionMethod == L"remote") { 163 | executer = ExecuteCodeWithCreateRemoteThread; 164 | } else if (executionMethod == L"context") { 165 | executer = ExecuteCodeWithSetThreadContext; 166 | } 167 | 168 | if (!installer || !executer) { 169 | Usage(); 170 | return false; 171 | } 172 | 173 | const auto processHandle = 174 | stdexp::make_unique_resource(Open32BitProcess(pid), &::CloseHandle); 175 | if (!processHandle) { 176 | return false; 177 | } 178 | 179 | // Inject code 180 | const auto remoteMemory = installer(processHandle.get()); 181 | if (!remoteMemory) { 182 | return false; 183 | } 184 | std::cout << "Remote Address : " << std::hex << remoteMemory.get() 185 | << std::endl; 186 | 187 | // Execute code 188 | const auto remoteThreadId = executer(processHandle.get(), remoteMemory.get()); 189 | if (!remoteThreadId) { 190 | return false; 191 | } 192 | 193 | std::cout << "Remote Thread ID : " << std::dec << remoteThreadId << std::endl; 194 | 195 | // Do not execute a destructor for remoteMemory that will release remote code 196 | ::ExitProcess(EXIT_SUCCESS); 197 | } 198 | 199 | // Prints usage 200 | void Usage() { 201 | std::cout << "Usage:\n" 202 | " >this.exe \n" 203 | "\n" 204 | " PID:\n" 205 | " Target's process ID. The target must be 32bit process.\n" 206 | "\n" 207 | " installation_method:\n" 208 | " alloc - use VirtualAllocEx\n" 209 | " section - use ZwMapViewOfSection\n" 210 | "\n" 211 | " execution_method:\n" 212 | " remote - use CreateRemoteThread\n" 213 | " context - use SetThreadContext\n" << std::endl; 214 | } 215 | 216 | // Returns a full access handle to a 32bit process, or nullptr. 217 | HANDLE Open32BitProcess(DWORD ProcessId) { 218 | const auto processHandle = 219 | ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 220 | if (!processHandle) { 221 | return nullptr; 222 | } 223 | BOOL amIwow64 = FALSE; 224 | BOOL isTargetWow64 = FALSE; 225 | if (!::IsWow64Process(::GetCurrentProcess(), &amIwow64) || 226 | !::IsWow64Process(processHandle, &isTargetWow64) || 227 | (amIwow64 && !isTargetWow64)) { 228 | ::CloseHandle(processHandle); 229 | return nullptr; 230 | } 231 | return processHandle; 232 | } 233 | 234 | // Returns a remote thread context. 235 | REMOTE_THREAD_CONTEXT MakeRemoteThreadContext(void* ThreadEntryPoint) { 236 | const auto kernel32 = ::GetModuleHandle(TEXT("kernel32")); 237 | return { 238 | 0, ThreadEntryPoint, 239 | reinterpret_cast( 240 | ::GetProcAddress(kernel32, "CreateThread")), 241 | reinterpret_cast( 242 | ::GetProcAddress(kernel32, "CloseHandle")), 243 | reinterpret_cast( 244 | ::GetProcAddress(kernel32, "Sleep")), 245 | reinterpret_cast( 246 | ::GetProcAddress(kernel32, "AllocConsole")), 247 | reinterpret_cast( 248 | ::GetProcAddress(kernel32, "GetStdHandle")), 249 | reinterpret_cast( 250 | ::GetProcAddress(kernel32, "WriteConsoleA")), 251 | }; 252 | } 253 | 254 | // Install code into a remote process using VirtualAllocEx. 255 | std::shared_ptr InstallCodeWithVirtualAllocEx( 256 | HANDLE ProcessHandle) { 257 | const auto remoteMemory = std::shared_ptr( 258 | reinterpret_cast( 259 | ::VirtualAllocEx(ProcessHandle, nullptr, PAGE_SIZE, MEM_COMMIT, 260 | PAGE_EXECUTE_READWRITE)), 261 | [ProcessHandle](REMOTE_MEMORY_BLOCK* p) { 262 | ::VirtualFreeEx(ProcessHandle, p, 0, MEM_RELEASE); 263 | }); 264 | if (!remoteMemory) { 265 | return nullptr; 266 | } 267 | 268 | const auto context = MakeRemoteThreadContext(&remoteMemory->Code); 269 | if (!::WriteProcessMemory(ProcessHandle, &remoteMemory->Parameter, &context, 270 | sizeof(context), nullptr)) { 271 | return nullptr; 272 | } 273 | 274 | const auto codeSize = reinterpret_cast(&::RemoteCodeZZZ) - 275 | reinterpret_cast(&::RemoteCode); 276 | if (!::WriteProcessMemory(ProcessHandle, &remoteMemory->Code, &::RemoteCode, 277 | codeSize, nullptr)) { 278 | return nullptr; 279 | } 280 | return remoteMemory; 281 | } 282 | 283 | // Install code into a remote process using ZwMapViewOfSection. 284 | std::shared_ptr InstallCodeWithZwMapViewOfSection( 285 | HANDLE ProcessHandle) { 286 | const auto ntdll = ::GetModuleHandle(TEXT("ntdll")); 287 | const auto ZwCreateSection = 288 | reinterpret_cast( 289 | ::GetProcAddress(ntdll, "ZwCreateSection")); 290 | const auto ZwMapViewOfSection = 291 | reinterpret_cast( 292 | ::GetProcAddress(ntdll, "ZwMapViewOfSection")); 293 | const auto ZwUnmapViewOfSection = 294 | reinterpret_cast( 295 | ::GetProcAddress(ntdll, "ZwUnmapViewOfSection")); 296 | const auto ZwClose = reinterpret_cast( 297 | ::GetProcAddress(ntdll, "ZwClose")); 298 | 299 | HANDLE sectionHandle = nullptr; 300 | LARGE_INTEGER sectionSize = {PAGE_SIZE}; 301 | auto status = 302 | ZwCreateSection(§ionHandle, SECTION_ALL_ACCESS, nullptr, §ionSize, 303 | PAGE_EXECUTE_READWRITE, SEC_COMMIT, nullptr); 304 | if (!NT_SUCCESS(status)) { 305 | return nullptr; 306 | } 307 | const auto sectionHandleCleaner = 308 | stdexp::make_scope_exit([&]() { ZwClose(sectionHandle); }); 309 | 310 | SIZE_T viewSize = 0; 311 | void* viewAddress = nullptr; 312 | status = ZwMapViewOfSection(sectionHandle, ::GetCurrentProcess(), 313 | &viewAddress, 0, PAGE_SIZE, nullptr, &viewSize, 314 | ntdll::ViewUnmap, 0, PAGE_READWRITE); 315 | if (!NT_SUCCESS(status)) { 316 | return nullptr; 317 | } 318 | const auto viewCleaner = stdexp::make_scope_exit( 319 | [&]() { ZwUnmapViewOfSection(::GetCurrentProcess(), viewAddress); }); 320 | 321 | SIZE_T remoteViewSize = 0; 322 | void* remoteViewAddress = nullptr; 323 | status = ZwMapViewOfSection(sectionHandle, ProcessHandle, &remoteViewAddress, 324 | 0, PAGE_SIZE, nullptr, &remoteViewSize, 325 | ntdll::ViewUnmap, 0, PAGE_EXECUTE_READWRITE); 326 | if (!NT_SUCCESS(status)) { 327 | return nullptr; 328 | } 329 | const auto remoteMemory = 330 | reinterpret_cast(remoteViewAddress); 331 | 332 | const auto memory = reinterpret_cast(viewAddress); 333 | memory->Parameter = MakeRemoteThreadContext(&remoteMemory->Code); 334 | 335 | const auto codeSize = reinterpret_cast(&::RemoteCodeZZZ) - 336 | reinterpret_cast(&::RemoteCode); 337 | memcpy(&memory->Code, &::RemoteCode, codeSize); 338 | 339 | return std::shared_ptr( 340 | remoteMemory, 341 | [=](REMOTE_MEMORY_BLOCK* p) { ZwUnmapViewOfSection(ProcessHandle, p); }); 342 | } 343 | 344 | // Execute code in a remote process using CreateRemoteThread. 345 | DWORD ExecuteCodeWithCreateRemoteThread(HANDLE ProcessHandle, 346 | REMOTE_MEMORY_BLOCK* RemoteMemory) { 347 | auto remoteThreadId = 0ul; 348 | const auto threadHandle = stdexp::make_unique_resource( 349 | ::CreateRemoteThread( 350 | ProcessHandle, nullptr, 0, 351 | reinterpret_cast(&RemoteMemory->Code), 352 | &RemoteMemory->Parameter, 0, &remoteThreadId), 353 | &::CloseHandle); 354 | if (!threadHandle) { 355 | return 0; 356 | } 357 | return remoteThreadId; 358 | } 359 | 360 | // Execute code in a remote process using SetThreadContext. 361 | DWORD ExecuteCodeWithSetThreadContext(HANDLE ProcessHandle, 362 | REMOTE_MEMORY_BLOCK* RemoteMemory) { 363 | const auto ntdll = ::GetModuleHandle(TEXT("ntdll")); 364 | const auto ZwSuspendProcess = 365 | reinterpret_cast( 366 | ::GetProcAddress(ntdll, "ZwSuspendProcess")); 367 | const auto ZwResumeProcess = 368 | reinterpret_cast( 369 | ::GetProcAddress(ntdll, "ZwResumeProcess")); 370 | 371 | auto status = ZwSuspendProcess(ProcessHandle); 372 | if (!NT_SUCCESS(status)) { 373 | return 0; 374 | } 375 | const auto processResumer = 376 | stdexp::make_scope_exit([=]() { ZwResumeProcess(ProcessHandle); }); 377 | 378 | const auto snapshot = stdexp::make_unique_resource( 379 | ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0), &::CloseHandle); 380 | if (snapshot == INVALID_HANDLE_VALUE) { 381 | return 0; 382 | } 383 | 384 | // Get the first thread's ID on the target process. That is less than optimal 385 | // because it is not guaranteed that the thread is active or can be activated 386 | // by the call of InvalidateRect later. 387 | auto targetThreadId = 0ul; 388 | THREADENTRY32 th32 = {sizeof(th32)}; 389 | for (::Thread32First(snapshot.get(), &th32); 390 | ::Thread32Next(snapshot.get(), &th32); 391 | /**/) { 392 | if (::GetProcessId(ProcessHandle) == th32.th32OwnerProcessID) { 393 | targetThreadId = th32.th32ThreadID; 394 | break; 395 | } 396 | } 397 | if (targetThreadId == 0) { 398 | return 0; 399 | } 400 | 401 | const auto threadHandle = stdexp::make_unique_resource( 402 | ::OpenThread(THREAD_ALL_ACCESS, FALSE, targetThreadId), &::CloseHandle); 403 | if (!threadHandle) { 404 | return 0; 405 | } 406 | 407 | CONTEXT oldThreadContext = {CONTEXT_FULL}; 408 | if (!::GetThreadContext(threadHandle.get(), &oldThreadContext)) { 409 | return 0; 410 | } 411 | 412 | const auto offsetToRemoteMemory = 413 | reinterpret_cast(RemoteCodeAsm) - 414 | reinterpret_cast(RemoteCode); 415 | const auto remoteCodeAsmAddr = (RemoteMemory->Code + offsetToRemoteMemory); 416 | auto newThreadContext = oldThreadContext; 417 | 418 | // Do not use volatile registers as it is not guaranteed that a value you set 419 | // in a volatile register is preserved until your code (EIP) is get executed. 420 | newThreadContext.Edi = reinterpret_cast(&RemoteMemory->Parameter); 421 | newThreadContext.Eip = reinterpret_cast(remoteCodeAsmAddr); 422 | if (!::SetThreadContext(threadHandle.get(), &newThreadContext)) { 423 | return 0; 424 | } 425 | 426 | status = ZwResumeProcess(ProcessHandle); 427 | ::InvalidateRect(nullptr, nullptr, TRUE); // An attempt to active the thread 428 | 429 | SIZE_T readBytes = 0; 430 | auto remoteThreadId = 0ul; 431 | for (;;) { 432 | // Wait a remote thread's ID is set 433 | ::Sleep(100); 434 | if (!::ReadProcessMemory(ProcessHandle, &RemoteMemory->Parameter.Tid, 435 | &remoteThreadId, sizeof(remoteThreadId), 436 | &readBytes)) { 437 | return 0; 438 | } 439 | if (remoteThreadId != 0) { 440 | break; 441 | } 442 | ::Sleep(900); 443 | std::cout << "Waiting for the thread get executed." << std::endl; 444 | } 445 | 446 | // An error on a CreateThread call 447 | if (remoteThreadId == 0xdeaddead) { 448 | return 0; 449 | } 450 | 451 | if (::SuspendThread(threadHandle.get()) == -1) { 452 | return 0; 453 | } 454 | CONTEXT currentThreadContext = {CONTEXT_FULL}; 455 | if (!::GetThreadContext(threadHandle.get(), ¤tThreadContext)) { 456 | return 0; 457 | } 458 | if (!::SetThreadContext(threadHandle.get(), &oldThreadContext)) { 459 | return 0; 460 | } 461 | if (::ResumeThread(threadHandle.get()) == -1) { 462 | return 0; 463 | } 464 | return remoteThreadId; 465 | } 466 | 467 | // Following code is injected to a remote process 468 | #pragma optimize("", off) 469 | #pragma check_stack(off) 470 | #pragma runtime_checks("", off) 471 | 472 | // Hi! 473 | DWORD WINAPI RemoteCode(REMOTE_THREAD_CONTEXT* Context) { 474 | Context->AllocConsole(); 475 | const auto out = Context->GetStdHandle(STD_OUTPUT_HANDLE); 476 | for (; /*ever*/;) { 477 | const char MESSAGE[] = { 478 | 'H', 'i', '\n', 479 | }; 480 | auto written = 0ul; 481 | Context->WriteConsoleA(out, MESSAGE, sizeof(MESSAGE), &written, nullptr); 482 | Context->Sleep(1000); 483 | } 484 | } 485 | 486 | // Used for remote executing using a section. It creates a thread executing 487 | // RemoteCode. 488 | __declspec(naked) void RemoteCodeAsm() { 489 | __asm { 490 | nop 491 | xor eax, eax 492 | mov ecx, [edi + 4] ; ThreadEntry 493 | mov edx, [edi + 8] ; CreateThread 494 | 495 | push edi ; lpThreadId 496 | push eax ; dwCreationFlags 497 | push edi ; lpParameter 498 | push ecx ; lpStartAddress 499 | push eax ; dwStackSize 500 | push eax ; lpThreadAttributes 501 | call edx ; CreateThread 502 | 503 | test eax, eax 504 | jnz Close 505 | mov dword ptr [edi], 0xdeaddead ; *Tid = 0xdeaddead 506 | jmp EndlessLoop 507 | 508 | Close: 509 | push eax 510 | mov edx, [edi + 12] ; CloseHandle 511 | call ecx ; CloseHandle 512 | 513 | EndlessLoop: 514 | jmp EndlessLoop 515 | int 3 516 | } 517 | } 518 | 519 | void RemoteCodeZZZ() {} 520 | #pragma runtime_checks("", restore) 521 | #pragma check_stack() 522 | #pragma optimize("", on) 523 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/TestInjector.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {D90F53FD-D478-4111-9F00-4B75B91589FD} 15 | Win32Proj 16 | TestInjector 17 | 18 | 19 | 20 | Application 21 | true 22 | v120 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v120 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | Use 51 | Level4 52 | Disabled 53 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 54 | 55 | 56 | Console 57 | true 58 | 59 | 60 | 61 | 62 | Level4 63 | Use 64 | MaxSpeed 65 | true 66 | true 67 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 68 | false 69 | 70 | 71 | Console 72 | true 73 | true 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Create 86 | Create 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/TestInjector.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8c672a16-fde0-44d2-997b-a18daf299ca9} 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files\ScopedResource 29 | 30 | 31 | Header Files\ScopedResource 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // injector.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ScopedResource/unique_resource.h" 24 | #include "ScopedResource/scope_exit.h" 25 | 26 | namespace stdexp = std::experimental; 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | // 30 | // macro utilities 31 | // 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // 35 | // constants and macros 36 | // 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | // 40 | // types 41 | // 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | // 45 | // prototypes 46 | // 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | // 50 | // variables 51 | // 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | // 55 | // implementations 56 | // 57 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/TestInjector/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, 6 | // include WinSDKVer.h and 7 | // set the _WIN32_WINNT macro to the platform you wish to support before 8 | // including SDKDDKVer.h. 9 | 10 | #include 11 | #define _WIN32_WINNT _WIN32_WINNT_WINXP 12 | #include 13 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | del *.sdf *.sdf *.opensdf 3 | del /a:h *.suo 4 | rmdir /s /q .vs 5 | rmdir /s /q ipch 6 | rmdir /s /q Debug 7 | rmdir /s /q Release 8 | rmdir /s /q Win7Debug 9 | rmdir /s /q Win7Release 10 | rmdir /s /q Win8.1Debug 11 | rmdir /s /q Win8.1Release 12 | rmdir /s /q x64 13 | rmdir /s /q TestInjector\Debug 14 | rmdir /s /q TestInjector\Release 15 | rmdir /s /q RemoteWriteMonitor\Win7Debug 16 | rmdir /s /q RemoteWriteMonitor\Win7Release 17 | rmdir /s /q RemoteWriteMonitor\Win8.1Debug 18 | rmdir /s /q RemoteWriteMonitor\Win8.1Release 19 | rmdir /s /q RemoteWriteMonitor\x64 20 | rmdir /s /q bin_RemoteWriteMonitor 21 | del /s *.aps 22 | pause 23 | -------------------------------------------------------------------------------- /RemoteWriteMonitor/make_release_folder.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | del /s *.pdb *.cer *.inf 3 | 4 | :: Arrange the x86 folder 5 | rmdir /s /q _x86 6 | mkdir _x86 7 | move Win7Release _x86 8 | move Win8.1Release _x86 9 | 10 | :: Arrange the x64 folder 11 | rmdir /s /q _x64 12 | mkdir _x64 13 | move x64\Win7Release _x64 14 | move x64\Win8.1Release _x64 15 | 16 | :: Arrange the bin_Scavenger folder 17 | rmdir /s /q bin_RemoteWriteMonitor 18 | mkdir bin_RemoteWriteMonitor 19 | move _x86 bin_RemoteWriteMonitor\x86 20 | move _x64 bin_RemoteWriteMonitor\x64 21 | move Release\TestInjector.exe bin_RemoteWriteMonitor\ 22 | pause 23 | -------------------------------------------------------------------------------- /img/injector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/RemoteWriteMonitor/e4003535f31f9aad0a9b853fc54d16ccda74020f/img/injector.png --------------------------------------------------------------------------------