diff --git a/archive/adspro15/DirectInput.txt b/archive/adspro15/DirectInput.txt new file mode 100644 index 00000000..e00ee83e --- /dev/null +++ b/archive/adspro15/DirectInput.txt @@ -0,0 +1,2065 @@ +Project Path: arc_adspro15_DirectInput_238y0ipn + +Source Tree: + +```txt +arc_adspro15_DirectInput_238y0ipn +├── LICENSE +├── Project +│ └── DirectInput.sln +├── README +└── Source + ├── DirectInput + │ ├── DirectInput.cpp + │ ├── DirectInput.h + │ ├── DirectInput.inf + │ ├── DirectInput.vcxproj + │ ├── DirectInput.vcxproj.filters + │ ├── DirectInput.vcxproj.user + │ ├── WPPTrace.h + │ ├── main.cpp + │ ├── stdafx.cpp + │ └── stdafx.h + └── DirectInputU + ├── DirectInput.cpp + ├── DirectInput.h + ├── DirectInputU.vcxproj + ├── DirectInputU.vcxproj.filters + ├── DirectInputU.vcxproj.user + ├── main.cpp + ├── stdafx.cpp + └── stdafx.h + +``` + +`LICENSE`: + +``` + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +``` + +`Project/DirectInput.sln`: + +```sln + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2016 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectInput", "..\Source\DirectInput\DirectInput.vcxproj", "{B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectInputU", "..\Source\DirectInputU\DirectInputU.vcxproj", "{97D5C996-7016-421F-9B53-3089F9C18B2D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM.ActiveCfg = Debug|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM.Build.0 = Debug|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM.Deploy.0 = Debug|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM64.Build.0 = Debug|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x64.ActiveCfg = Debug|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x64.Build.0 = Debug|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x64.Deploy.0 = Debug|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x86.ActiveCfg = Debug|Win32 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x86.Build.0 = Debug|Win32 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Debug|x86.Deploy.0 = Debug|Win32 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM.ActiveCfg = Release|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM.Build.0 = Release|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM.Deploy.0 = Release|ARM + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM64.ActiveCfg = Release|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM64.Build.0 = Release|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|ARM64.Deploy.0 = Release|ARM64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x64.ActiveCfg = Release|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x64.Build.0 = Release|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x64.Deploy.0 = Release|x64 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x86.ActiveCfg = Release|Win32 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x86.Build.0 = Release|Win32 + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2}.Release|x86.Deploy.0 = Release|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|ARM.ActiveCfg = Debug|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|ARM64.ActiveCfg = Debug|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|x64.ActiveCfg = Debug|x64 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|x64.Build.0 = Debug|x64 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|x86.ActiveCfg = Debug|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Debug|x86.Build.0 = Debug|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|ARM.ActiveCfg = Release|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|ARM64.ActiveCfg = Release|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|x64.ActiveCfg = Release|x64 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|x64.Build.0 = Release|x64 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|x86.ActiveCfg = Release|Win32 + {97D5C996-7016-421F-9B53-3089F9C18B2D}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2D9B3278-CEC2-42EE-BD66-6C532C0914A0} + EndGlobalSection +EndGlobal + +``` + +`README`: + +``` +# DirectInput + +VS2017 (15.8), C++17 +WDK 10.0.17134 +SDK 10.0.17137 + +Simulate SendInput with ClassService. + +``` + +`Source/DirectInput/DirectInput.cpp`: + +```cpp +#include "stdafx.h" +#include "DirectInput.tmh" + +#include "DirectInput.h" + +namespace wdk +{ + extern"C" + { + extern POBJECT_TYPE* IoDriverObjectType; + + NTSTATUS ObReferenceObjectByName( + __in PUNICODE_STRING ObjectName, + __in ULONG Attributes, + __in_opt PACCESS_STATE AccessState, + __in_opt ACCESS_MASK DesiredAccess, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode, + __inout_opt PVOID ParseContext, + __out PVOID * Object); + + } +} + +namespace DirectInput +{ + struct ClassServiceData : public CONNECT_DATA + { + enum Classes: UINT32 + { + Keyboard, + Mouse, + Maximum + }; + + auto Initialize(Classes aClass) -> NTSTATUS; + + auto CallService(PVOID aInputData, PVOID aInputDataEnd, UINT32* aConsumed) + -> NTSTATUS; + }; + + static ClassServiceData s_ClassServices[ClassServiceData::Classes::Maximum]{}; + + ////////////////////////////////////////////////////////////////////////// + + auto ClassServiceData::Initialize(Classes aClass) + -> NTSTATUS + { +# pragma prefast(push) +# pragma prefast(disable: 28175) + + constexpr auto KeyboardHid = LR"(\Driver\kbdhid)"; + constexpr auto MouseHid = LR"(\Driver\mouhid)"; + constexpr auto PS2Port = LR"(\Driver\i8042prt)"; + constexpr auto KeyboardClass= LR"(\Driver\kbdclass)"; + constexpr auto MouseClass = LR"(\Driver\mouclass)"; + + NTSTATUS vStatus = STATUS_SUCCESS; + + PDRIVER_OBJECT vDeviceDriver = nullptr; + PDRIVER_OBJECT vClassDriver = nullptr; + for (;;) + { + auto vObjectName = UNICODE_STRING{}; + auto vDeviceName = static_cast(nullptr); + auto vClassName = static_cast(nullptr); + + if (Classes::Keyboard == aClass) + { + vDeviceName = KeyboardHid; + vClassName = KeyboardClass; + } + else if (Classes::Mouse == aClass) + { + vDeviceName = MouseHid; + vClassName = MouseClass; + } + + RtlInitUnicodeString(&vObjectName, vDeviceName); + vStatus = wdk::ObReferenceObjectByName( + &vObjectName, OBJ_CASE_INSENSITIVE, + nullptr, + FILE_ANY_ACCESS, + *wdk::IoDriverObjectType, + KernelMode, + nullptr, + (PVOID*)&vDeviceDriver); + if (!NT_SUCCESS(vStatus)) + { + if (STATUS_OBJECT_NAME_NOT_FOUND != vStatus) + { + break; + } + + vDeviceName = PS2Port; + RtlInitUnicodeString(&vObjectName, vDeviceName); + vStatus = wdk::ObReferenceObjectByName( + &vObjectName, OBJ_CASE_INSENSITIVE, + nullptr, + FILE_ANY_ACCESS, + *wdk::IoDriverObjectType, + KernelMode, + nullptr, + (PVOID*)&vDeviceDriver); + if (!NT_SUCCESS(vStatus)) + { + break; + } + } + + RtlInitUnicodeString(&vObjectName, vClassName); + vStatus = wdk::ObReferenceObjectByName( + &vObjectName, OBJ_CASE_INSENSITIVE, + nullptr, + FILE_ANY_ACCESS, + *wdk::IoDriverObjectType, + KernelMode, + nullptr, + (PVOID*)&vClassDriver); + if (!NT_SUCCESS(vStatus)) + { + break; + } + auto vClassDriverStart = (PVOID)(vClassDriver->DriverStart); + auto vClassDriverEnd = (PVOID)((SIZE_T)vClassDriverStart + vClassDriver->DriverSize); + + vStatus = STATUS_NOT_FOUND; + for (auto vDeviceDevice = vDeviceDriver->DeviceObject; vDeviceDevice; vDeviceDevice = vDeviceDevice->NextDevice) + { + auto vDeviceExtBytes = (intptr_t)vDeviceDevice->DeviceObjectExtension - (intptr_t)vDeviceDevice->DeviceExtension; + if (vDeviceExtBytes < 0) + { + continue; + } + auto vDeviceExtPtrCount = vDeviceExtBytes / sizeof(void*) - 1; + auto vDeviceExt = static_cast(vDeviceDevice->DeviceExtension); + + for (auto vClassDevice = vClassDriver->DeviceObject; vClassDevice; vClassDevice = vClassDevice->NextDevice) + { + for (auto i = 0u; i < vDeviceExtPtrCount; ++i) + { + if (vDeviceExt[i] == vClassDevice && + vDeviceExt[i + 1] > vClassDriverStart && + vDeviceExt[i + 1] < vClassDriverEnd) + { + ClassDeviceObject = vClassDevice; + ClassService = vDeviceExt[i + 1]; + + vStatus = STATUS_SUCCESS; + break; + } + } + if (NT_SUCCESS(vStatus)) + { + break; + } + } + if (NT_SUCCESS(vStatus)) + { + break; + } + } + if (!NT_SUCCESS(vStatus)) + { + break; + } + + break; + } + if (vClassDriver) ObDereferenceObject(vClassDriver), vClassDriver = nullptr; + if (vDeviceDriver) ObDereferenceObject(vDeviceDriver), vDeviceDriver = nullptr; + + return vStatus; + +# pragma prefast(pop) + } + + auto ClassServiceData::CallService(PVOID aInputData, PVOID aInputDataEnd, UINT32* aConsumed) + -> NTSTATUS + { + struct DPCContext + { + KDPC Dpc; + KEVENT Event; + PVOID Callback; + PDEVICE_OBJECT Device; + PVOID InputData; + PVOID InputDataEnd; + ULONG Consumed; + }; + + NTSTATUS vStatus = STATUS_SUCCESS; + + auto vContext = static_cast(nullptr); + for (;;) + { + vContext = (DPCContext*)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCContext), 'ItiD'); + if (nullptr == vContext) + { + vStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + vContext->Callback = ClassService; + vContext->Device = ClassDeviceObject; + vContext->InputData = aInputData; + vContext->InputDataEnd = aInputDataEnd; + vContext->Consumed = 0; + + KeInitializeEvent(&vContext->Event, NotificationEvent, FALSE); + KeInitializeDpc( + &vContext->Dpc, + [](PKDPC /*aDpc*/, PVOID aContext, PVOID /*aSysArg1*/, PVOID /*aSysArg2*/) + ->void + { + auto vContext = static_cast(aContext); + + static_cast(vContext->Callback)( + vContext->Device, + vContext->InputData, + vContext->InputDataEnd, + &vContext->Consumed); + + KeSetEvent(&vContext->Event, IO_KEYBOARD_INCREMENT, FALSE); + }, vContext); + + if (!KeInsertQueueDpc(&vContext->Dpc, nullptr, nullptr)) + { + vStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + vStatus = KeWaitForSingleObject(&vContext->Event, Executive, KernelMode, FALSE, nullptr); + if (!NT_SUCCESS(vStatus)) + { + break; + } + + if (aConsumed) *aConsumed = vContext->Consumed; + break; + } + if (vContext) ExFreePoolWithTag(vContext, 'ItiD'), vContext = nullptr; + + return vStatus; + } + + auto Initialize() + -> NTSTATUS + { + NTSTATUS vStatus = STATUS_SUCCESS; + + for (auto i = 0u; i < ClassServiceData::Maximum; ++i) + { + auto& vServiceData = s_ClassServices[i]; + + vStatus = vServiceData.Initialize(ClassServiceData::Classes(i)); + if (!NT_SUCCESS(vStatus)) + { + break; + } + + TraceError("ClassDevice : %p", vServiceData.ClassDeviceObject); + TraceError("ClassService: %p", vServiceData.ClassService); + } + + return vStatus; + } + + template + static auto MouseInput($MOUSEINPUT& aInput) + -> NTSTATUS + { + NTSTATUS vStatus = STATUS_SUCCESS; + auto& vService = s_ClassServices[ClassServiceData::Mouse]; + + for (;;) + { + auto vInput = MOUSE_INPUT_DATA{}; + vInput.LastX = aInput.dx; + vInput.LastY = aInput.dy; + vInput.ButtonData = static_cast(aInput.mouseData); + + if (MOUSEEVENTF_ABSOLUTE & aInput.dwFlags) + { + vInput.Flags |= MOUSE_MOVE_ABSOLUTE; + } + if (MOUSEEVENTF_VIRTUALDESK & aInput.dwFlags) + { + vInput.Flags |= MOUSE_VIRTUAL_DESKTOP; + } + if (MOUSEEVENTF_MOVE_NOCOALESCE & aInput.dwFlags) + { + vInput.Flags |= MOUSE_MOVE_NOCOALESCE; + } + + if (MOUSEEVENTF_WHEEL & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_WHEEL; + } + if (MOUSEEVENTF_HWHEEL & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_HWHEEL; + } + + if (MOUSEEVENTF_LEFTDOWN & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN; + } + if (MOUSEEVENTF_LEFTUP & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_LEFT_BUTTON_UP; + } + if (MOUSEEVENTF_RIGHTDOWN & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN; + } + if (MOUSEEVENTF_RIGHTUP & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP; + } + if (MOUSEEVENTF_MIDDLEDOWN & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN; + } + if (MOUSEEVENTF_MIDDLEUP & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP; + } + + if (MOUSEEVENTF_XDOWN & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_BUTTON_4_DOWN; + } + if (MOUSEEVENTF_XUP & aInput.dwFlags) + { + vInput.ButtonFlags |= MOUSE_BUTTON_4_UP; + } + + vStatus = vService.CallService(&vInput, &vInput + 1, nullptr); + break; + } + + return vStatus; + } + + template + static auto KeyboardInput($KEYBDINPUT& aInput) + -> NTSTATUS + { + NTSTATUS vStatus = STATUS_SUCCESS; + auto& vService = s_ClassServices[ClassServiceData::Keyboard]; + + for (;;) + { + if (!(KEYEVENTF_SCANCODE & aInput.dwFlags)) + { + vStatus = STATUS_NOT_IMPLEMENTED; + break; + } + if (KEYEVENTF_UNICODE & aInput.dwFlags) + { + vStatus = STATUS_NOT_IMPLEMENTED; + break; + } + + auto vInput = KEYBOARD_INPUT_DATA{}; + vInput.MakeCode = aInput.wScan; + + if (KEYEVENTF_KEYUP & aInput.dwFlags) + { + vInput.Flags |= KEY_BREAK; + } + if (KEYEVENTF_EXTENDEDKEY & aInput.dwFlags) + { + vInput.Flags |= KEY_E0; + } + + vStatus = vService.CallService(&vInput, &vInput + 1, nullptr); + break; + } + + return vStatus; + } + + auto SendInput(UINT32 aInputCount, LPINPUT aInputs, UINT32 aInputBytes, UINT32* aConsumed) + -> NTSTATUS + { + NTSTATUS vStatus = STATUS_SUCCESS; + + auto vSendCount = 0u; + if (sizeof(INPUT32) == aInputBytes) + { + auto vInputs = reinterpret_cast(aInputs); + for (auto i = 0u; i < aInputCount; ++i) + { + auto& vInput = vInputs[i]; + if (INPUT_MOUSE == vInput.type) + vStatus = MouseInput(vInput.mi); + else if (INPUT_KEYBOARD == vInput.type) + vStatus = KeyboardInput(vInput.ki); + + if (!NT_SUCCESS(vStatus)) + { + break; + } + + ++vSendCount; + } + } + else if (sizeof(INPUT64) == aInputBytes) + { + auto vInputs = reinterpret_cast(aInputs); + for (auto i = 0u; i < aInputCount; ++i) + { + auto& vInput = vInputs[i]; + if (INPUT_MOUSE == vInput.type) + vStatus = MouseInput(vInput.mi); + else if (INPUT_KEYBOARD == vInput.type) + vStatus = KeyboardInput(vInput.ki); + + if (!NT_SUCCESS(vStatus)) + { + break; + } + + ++vSendCount; + } + } + else + { + vStatus = STATUS_INVALID_PARAMETER; + } + + if (aConsumed) *aConsumed = vSendCount; + + return vStatus; + } + +} + +``` + +`Source/DirectInput/DirectInput.h`: + +```h +#pragma once + +////////////////////////////////////////////////////////////////////////// +// Mouse + +#define MOUSEEVENTF_MOVE 0x0001 /* mouse move */ +#define MOUSEEVENTF_LEFTDOWN 0x0002 /* left button down */ +#define MOUSEEVENTF_LEFTUP 0x0004 /* left button up */ +#define MOUSEEVENTF_RIGHTDOWN 0x0008 /* right button down */ +#define MOUSEEVENTF_RIGHTUP 0x0010 /* right button up */ +#define MOUSEEVENTF_MIDDLEDOWN 0x0020 /* middle button down */ +#define MOUSEEVENTF_MIDDLEUP 0x0040 /* middle button up */ +#define MOUSEEVENTF_XDOWN 0x0080 /* x button down */ +#define MOUSEEVENTF_XUP 0x0100 /* x button down */ +#define MOUSEEVENTF_WHEEL 0x0800 /* wheel button rolled */ +#define MOUSEEVENTF_HWHEEL 0x1000 /* hwheel button rolled */ +#define MOUSEEVENTF_MOVE_NOCOALESCE 0x2000 /* do not coalesce mouse moves */ +#define MOUSEEVENTF_VIRTUALDESK 0x4000 /* map to entire virtual desktop */ +#define MOUSEEVENTF_ABSOLUTE 0x8000 /* absolute move */ + +template +struct $MOUSEINPUT +{ + LONG dx; + LONG dy; + ULONG mouseData; + ULONG dwFlags; + ULONG time; + T dwExtraInfo; +}; + +using MOUSEINPUT = $MOUSEINPUT<>; +using PMOUSEINPUT = MOUSEINPUT *; +using LPMOUSEINPUT = MOUSEINPUT *; + +using MOUSEINPUT32 = $MOUSEINPUT; +using PMOUSEINPUT32 = MOUSEINPUT32 *; +using LPMOUSEINPUT32= MOUSEINPUT32 *; + +using MOUSEINPUT64 = $MOUSEINPUT; +using PMOUSEINPUT64 = MOUSEINPUT64 *; +using LPMOUSEINPUT64= MOUSEINPUT64 *; + +////////////////////////////////////////////////////////////////////////// +// Keyboard + +#define KEYEVENTF_EXTENDEDKEY 0x0001 +#define KEYEVENTF_KEYUP 0x0002 +#define KEYEVENTF_UNICODE 0x0004 +#define KEYEVENTF_SCANCODE 0x0008 + +template +struct $KEYBDINPUT { + USHORT wVk; + USHORT wScan; + ULONG dwFlags; + ULONG time; + T dwExtraInfo; +}; + +using KEYBDINPUT = $KEYBDINPUT<>; +using PKEYBDINPUT = KEYBDINPUT * ; +using LPKEYBDINPUT = KEYBDINPUT * ; + +using KEYBDINPUT32 = $KEYBDINPUT; +using PKEYBDINPUT32 = KEYBDINPUT32 * ; +using LPKEYBDINPUT32= KEYBDINPUT32 * ; + +using KEYBDINPUT64 = $KEYBDINPUT; +using PKEYBDINPUT64 = KEYBDINPUT64 * ; +using LPKEYBDINPUT64= KEYBDINPUT64 * ; + +////////////////////////////////////////////////////////////////////////// +// Other + +typedef struct tagHARDWAREINPUT { + ULONG uMsg; + USHORT wParamL; + USHORT wParamH; +} HARDWAREINPUT, *PHARDWAREINPUT, FAR* LPHARDWAREINPUT; + +#define INPUT_MOUSE 0 +#define INPUT_KEYBOARD 1 +#define INPUT_HARDWARE 2 + +template +struct $INPUT { + ULONG type; + + union + { + $MOUSEINPUT mi; + $KEYBDINPUT ki; + HARDWAREINPUT hi; + }; +}; + +using INPUT = $INPUT<>; +using PINPUT = INPUT * ; +using LPINPUT = INPUT * ; + +using INPUT32 = $INPUT; +using PINPUT32 = INPUT32 * ; +using LPINPUT32 = INPUT32 * ; + +using INPUT64 = $INPUT; +using PINPUT64 = INPUT64 * ; +using LPINPUT64 = INPUT64 * ; + +namespace DirectInput +{ + + auto Initialize() + -> NTSTATUS; + + auto SendInput(UINT32 aInputCount, LPINPUT aInputs, UINT32 aInputBytes, UINT32* aConsumed) + -> NTSTATUS; +} + +``` + +`Source/DirectInput/DirectInput.inf`: + +```inf +; +; DirectInput.inf +; + +[Version] +Signature = "$WINDOWS NT$" +Class = AntiVirus +ClassGuid = {b1d1a169-c54f-4379-81db-bee7d88d7454} +Provider = %ManufacturerName% +CatalogFile = %DriverName%.cat +DriverVer = 09/02/2018 + +;************************************* + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId%,,,"" + +[SourceDisksFiles] +DirectInput.sys = 1,, +DirectInput.x86.sys = 1,, +DirectInput.x64.sys = 1,, + +[Files.Copy.NTx86] +DirectInput.sys,DirectInput.x86.sys,,0x00004020 ; COPYFLG_IN_USE_RENAME | COPYFLG_NO_VERSION_DIALOG + +[Files.Copy.NTamd64] +DirectInput.sys,DirectInput.x64.sys,,0x00004020 + +[Files.Del] +DirectInput.sys + +;************************************* + +[DefaultInstall.NTx86] +CopyFiles=Files.Copy.NTx86 + +[DefaultInstall.NTamd64] +CopyFiles=Files.Copy.NTamd64 + +[DefaultInstall.NTx86.Services] +AddService = %ServiceName%,%ServicInstFlags%, ServiceInstall + +[DefaultInstall.NTamd64.Services] +AddService = %ServiceName%,%ServicInstFlags%, ServiceInstall + +[DefaultUninstall.NTx86] +DelFiles = Files.Del + +[DefaultUninstall.NTamd64] +DelFiles = Files.Del + +[DefaultUninstall.NTx86.Services] +DelService = %ServiceName%,0x200 + +[DefaultUninstall.NTamd64.Services] +DelService = %ServiceName%,0x200 + +;************************************* + +[ServiceInstall] +DisplayName = %ServiceName% +Description = %ServiceDesc% +ServiceBinary = %12%\%DriverName%.sys +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; 0 = SERVICE_BOOT_START + ; 1 = SERVICE_SYSTEM_START + ; 2 = SERVICE_AUTO_START + ; 3 = SERVICE_DEMAND_START + ; 4 = SERVICE_DISABLED +ErrorControl = 1 ; SERVICE_ERROR_NORMAL + +;************************************* + +[Strings] +ServicInstFlags = 0x00000000 + +ManufacturerName = "Idea" +DriverName = "DirectInput" +ServiceDesc = "Mouse and keyboard input directly" +ServiceName = "DirectInput" +DiskId = "DirectInput Device Installation Disk" + +``` + +`Source/DirectInput/DirectInput.vcxproj`: + +```vcxproj + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + + {B73F04C6-EBC6-4637-9F32-1DDF00EE2EA2} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + Win32 + DirectInput + $(LatestTargetPlatformVersion) + + + + Windows7 + true + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + + + Windows7 + false + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + + + Windows7 + true + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + + + Windows7 + false + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + + DbgengKernelDebugger + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(TargetName.Replace(' ','')).$(PlatformTarget) + ..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverMinimumRules.ruleset + true + + + DbgengKernelDebugger + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(TargetName.Replace(' ','')).$(PlatformTarget) + ..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverMinimumRules.ruleset + true + + + DbgengKernelDebugger + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(TargetName.Replace(' ','')).$(PlatformTarget) + ..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverMinimumRules.ruleset + true + + + DbgengKernelDebugger + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(TargetName.Replace(' ','')).$(PlatformTarget) + ..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\CodeAnalysis\DriverMinimumRules.ruleset + true + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + true + true + + + WPPTrace.h + false + true + stdcpp17 + + + + + true + true + + + WPPTrace.h + true + stdcpp17 + + + + + true + true + + + WPPTrace.h + false + true + stdcpp17 + + + + + true + true + + + WPPTrace.h + true + stdcpp17 + + + + + + + + + + + + + + + + + + + + + + +``` + +`Source/DirectInput/DirectInput.vcxproj.filters`: + +```filters + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;hpp;hxx;hm;inl;inc;xsd + + + + + + + + Src + + + Src + + + Src + + + + + Src + + + Src + + + Src + + + +``` + +`Source/DirectInput/DirectInput.vcxproj.user`: + +```user + + + + +``` + +`Source/DirectInput/WPPTrace.h`: + +```h +/*++ + +Module Name: + + Trace.h + +Abstract: + + Header file for the debug tracing related function defintions and macros. + +Environment: + + Kernel mode + +--*/ + +#pragma once +#include + +// +// Define the tracing flags. +// +// Tracing GUID - 7CA5906D-C7EE-4BCB-A179-C4609D13A9BC +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + WPPTraceGuid, (7CA5906D,C7EE,4BCB,A179,C4609D13A9BC), \ + WPP_DEFINE_BIT(WPP_ANY_FLAGS) \ + WPP_DEFINE_BIT(WPP_FUNC_TRACE) \ + ) + +#define WPP_FLAGS_LEVEL_LOGGER(flags, level) \ + WPP_LEVEL_LOGGER(flags) + +#define WPP_FLAGS_LEVEL_ENABLED(flags, level) \ + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) + +#define WPP_LEVEL_FLAGS_LOGGER(level, flags) \ + WPP_LEVEL_LOGGER(flags) + +#define WPP_LEVEL_FLAGS_ENABLED(level, flags) \ + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) + +// +// PRE macro: The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +// +#define WPP_FLAGS_NT_PRE(FLAGS, NT) {if (!NT_SUCCESS(NT)) { + +// +// POST macro +// The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +#define WPP_FLAGS_NT_POST(FLAGS, NT) ;}} + +// +// The two macros below are for checking if the event should be logged and for +// choosing the logger handle to use when calling the ETW trace API +// +#define WPP_FLAGS_NT_ENABLED(FLAGS, NT) WPP_FLAG_ENABLED(FLAGS) +#define WPP_FLAGS_NT_LOGGER(FLAGS, NT) WPP_FLAG_LOGGER(FLAGS) + +// Map the null flags used by Entry/Exit to a function called FuncTrace +#define WPP__ENABLED() WPP_LEVEL_ENABLED(WPP_FUNC_TRACE) +#define WPP__LOGGER() WPP_LEVEL_LOGGER(WPP_FUNC_TRACE) + +#define WPP_NT_ENABLED(NT) WPP_LEVEL_ENABLED(WPP_FUNC_TRACE) +#define WPP_NT_LOGGER(NT) WPP_LEVEL_LOGGER(WPP_FUNC_TRACE) + +// +// WPP orders static parameters before dynamic parameters. To support the Trace function +// defined below which sets FLAGS=MYDRIVER_ALL_INFO, a custom macro must be defined to +// reorder the arguments to what the .tpl configuration file expects. +// +#define WPP_RECORDER_FLAGS_LEVEL_ARGS(flags, level) WPP_RECORDER_LEVEL_FLAGS_ARGS(level, flags) +#define WPP_RECORDER_FLAGS_LEVEL_FILTER(flags, level) WPP_RECORDER_LEVEL_FLAGS_FILTER(level, flags) + +// MACRO: TraceReturn +// Configuration block that defines trace macro. It uses the PRE/POST macros to include +// code as part of the trace macro expansion. TRACE_MACRO is equivalent to the code below: +// +// {if (Status != STATUS_SUCCESS){ // This is the code in the PRE macro +// Trace(TRACE_LEVEL_ERROR, "%!FUNC! Return = %!STATUS!", Status) +// ;}} // This is the code in the POST macro +// +// +// USEPREFIX statement: Defines a format string prefix to be used when logging the event, +// below the STDPREFIX is used. The first value is the trace function name with out parenthesis +// and the second value is the format string to be used. +// +// USESUFFIX statement: Defines a suffix format string that gets logged with the event. +// +// FUNC statement: Defines the name and signature of the trace function. The function defined +// below takes one argument, no format string, and predefines the flag equal to FLAG_ONE. +// +// +//begin_wpp config +//USEPREFIX (TraceReturn, "%!STDPREFIX!"); +//FUNC TraceReturn{FLAGS=WPP_ANY_FLAGS}(NT); +//USESUFFIX (TraceReturn, "%!FUNC! Return=%!STATUS!", NT); +//end_wpp + +// MACRO: TraceBegin +// +//begin_wpp config +//FUNC TraceEntry(); +//FUNC TraceExit(NT); +//USESUFFIX (TraceEntry, "Entry to %!FUNC!"); +//USESUFFIX (TraceExit, "%!FUNC! Return=%!STATUS!", NT); +//end_wpp + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC TraceEvents (LEVEL, FLAGS, MSG, ...); +// FUNC Trace{FLAGS=WPP_ANY_FLAGS} (LEVEL, MSG, ...); +// FUNC TraceCritical{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_CRITICAL} (MSG, ...); +// FUNC TraceFatal{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_FATAL} (MSG, ...); +// FUNC TraceError{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_ERROR} (MSG, ...); +// FUNC TraceWarn{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_WARNING} (MSG, ...); +// FUNC TraceInfo{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_INFORMATION} (MSG, ...); +// FUNC TraceVerbose{FLAGS=WPP_ANY_FLAGS, LEVEL=TRACE_LEVEL_VERBOSE} (MSG, ...); +// end_wpp +// + + +``` + +`Source/DirectInput/main.cpp`: + +```cpp +#include "stdafx.h" +#include "main.tmh" + +#include "DirectInput.h" + +////////////////////////////////////////////////////////////////////////// + +static constexpr wchar_t s_DevName[] = LR"(\Device\{ED0EB7DC-9BD5-4DD8-8B5F-24BBFBF31CF0})"; +static constexpr wchar_t s_SymName[] = LR"(\DosDevices\Global\{ED0EB7DC-9BD5-4DD8-8B5F-24BBFBF31CF0})"; + +enum class IoCode : UINT32 +{ + BeginCode = 0x800, + + SendInput = CTL_CODE(FILE_DEVICE_UNKNOWN, BeginCode + 1, METHOD_BUFFERED, FILE_ANY_ACCESS), +}; + +struct SendInputArgs +{ + UINT32 InputCount; + UINT32 InputBytes; + UINT64 Inputs; +}; + +////////////////////////////////////////////////////////////////////////// + +extern"C" +{ + DRIVER_INITIALIZE DriverEntry; +} + +static PDEVICE_OBJECT s_Nothing = nullptr; + +////////////////////////////////////////////////////////////////////////// + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +static auto DeviceCreateClose( + PDEVICE_OBJECT /*aDeviceObject*/, + PIRP aIrp) + -> NTSTATUS +{ + aIrp->IoStatus.Status = STATUS_SUCCESS; + aIrp->IoStatus.Information = 0; + + return IoCompleteRequest(aIrp, IO_NO_INCREMENT), STATUS_SUCCESS; +} + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +static auto DeviceControl( + PDEVICE_OBJECT /*aDeviceObject*/, + PIRP aIrp) + -> NTSTATUS +{ + NTSTATUS vStatus = STATUS_SUCCESS; + ULONG_PTR vReturnInfo = 0u; + + auto vIrp = IoGetCurrentIrpStackLocation(aIrp); + auto vInBytes = vIrp->Parameters.DeviceIoControl.InputBufferLength; + auto vOutBytes = vIrp->Parameters.DeviceIoControl.OutputBufferLength; + + switch (IoCode(vIrp->Parameters.DeviceIoControl.IoControlCode)) + { + case IoCode::SendInput: + { + if (sizeof(SendInputArgs) != vInBytes || sizeof(UINT32) != vOutBytes) + { + vStatus = STATUS_INVALID_PARAMETER; + break; + } + + auto vInBuffer = aIrp->AssociatedIrp.SystemBuffer; + auto vOutBuffer = aIrp->AssociatedIrp.SystemBuffer; + + auto vInputData = static_cast(vInBuffer); + auto vSendCount = static_cast(vOutBuffer); + + __try + { + ProbeForRead((void*)vInputData->Inputs, vInputData->InputBytes * vInputData->InputCount, sizeof(UINT8)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + vStatus = GetExceptionCode(); + break; + } + + auto vMdl = IoAllocateMdl((void*)vInputData->Inputs, vInputData->InputBytes * vInputData->InputCount, FALSE, TRUE, NULL); + if (!vMdl) + { + vStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + __try + { + MmProbeAndLockPages(vMdl, UserMode, IoReadAccess); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + vStatus = GetExceptionCode(); + + IoFreeMdl(vMdl), vMdl = nullptr; + break; + } + + auto vInputs = MmGetSystemAddressForMdlSafe(vMdl, NormalPagePriority | MdlMappingNoExecute); + if (nullptr == vInputs) + { + MmUnlockPages(vMdl); + IoFreeMdl(vMdl), vMdl = nullptr; + + vStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + vStatus = DirectInput::SendInput(vInputData->InputCount, (LPINPUT)vInputs, vInputData->InputBytes, vSendCount); + + MmUnlockPages(vMdl); + IoFreeMdl(vMdl), vMdl = nullptr; + + vReturnInfo = sizeof(*vSendCount); + break; + } + default: + { + vStatus = STATUS_NOT_IMPLEMENTED; + break; + } + } + + aIrp->IoStatus.Status = vStatus; + aIrp->IoStatus.Information = vReturnInfo; + + return IoCompleteRequest(aIrp, IO_NO_INCREMENT), vStatus; +} + +static auto DriverUnload(PDRIVER_OBJECT aDriverObject) +-> void +{ + constexpr UNICODE_STRING cSymName = RTL_CONSTANT_STRING(s_SymName); + IoDeleteSymbolicLink(const_cast(&cSymName)); + + if (s_Nothing) + { + IoDeleteDevice(s_Nothing), s_Nothing = nullptr; + } + + WPP_CLEANUP(aDriverObject); +} + +extern"C" +auto DriverEntry( + PDRIVER_OBJECT aDriverObject, + PUNICODE_STRING aRegistryPath) + -> NTSTATUS +{ + NTSTATUS vStatus = STATUS_SUCCESS; + + for (;;) + { + WPP_INIT_TRACING(aDriverObject, aRegistryPath); + + vStatus = DirectInput::Initialize(); + if (!NT_SUCCESS(vStatus)) + { + break; + } + + constexpr UNICODE_STRING cDevName = RTL_CONSTANT_STRING(s_DevName); + constexpr UNICODE_STRING cSymName = RTL_CONSTANT_STRING(s_SymName); + + vStatus = IoCreateDevice( + aDriverObject, + 0, + const_cast(&cDevName), + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &s_Nothing); + if (!NT_SUCCESS(vStatus)) + { + break; + } + + vStatus = IoCreateSymbolicLink( + const_cast(&cSymName), + const_cast(&cDevName)); + if (!NT_SUCCESS(vStatus)) + { + break; + } + s_Nothing->Flags |= DO_BUFFERED_IO; + s_Nothing->Flags &= ~DO_DEVICE_INITIALIZING; + + aDriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreateClose; + aDriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceCreateClose; + aDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl; + + aDriverObject->DriverUnload = DriverUnload; + break; + } + TraceReturn(vStatus); + + if (!NT_SUCCESS(vStatus)) + { + DriverUnload(aDriverObject); + } + + return vStatus; +} + +``` + +`Source/DirectInput/stdafx.cpp`: + +```cpp +#include "stdafx.h" + +``` + +`Source/DirectInput/stdafx.h`: + +```h +#pragma once + +#include +#include +#include + +#define NTSTRSAFE_NO_CCH_FUNCTIONS 1 +#include + +#include "WPPTrace.h" + +``` + +`Source/DirectInputU/DirectInput.cpp`: + +```cpp +#include "stdafx.h" +#include "DirectInput.h" + +namespace DirectInput +{ + ////////////////////////////////////////////////////////////////////////// + + static constexpr wchar_t s_SymName[] = LR"(\\.\{ED0EB7DC-9BD5-4DD8-8B5F-24BBFBF31CF0})"; + + enum class IoCode : UINT32 + { + BeginCode = 0x800, + + SendInput = CTL_CODE(FILE_DEVICE_UNKNOWN, BeginCode + 1, METHOD_BUFFERED, FILE_ANY_ACCESS), + }; + + struct SendInputArgs + { + UINT32 InputCount; + UINT32 InputBytes; + UINT64 Inputs; + }; + + ////////////////////////////////////////////////////////////////////////// + + auto Initialize() + ->HRESULT + { + return E_NOTIMPL; + } + + static auto SendInputCode(UINT32 aInputCount, LPINPUT aInputs, UINT32 aInputBytes) + -> UINT + { + UINT32 vDosCode = NOERROR; + UINT32 vSendCount = 0u; + + HANDLE vDevice = nullptr; + for (;;) + { + vDevice = CreateFile( + s_SymName, + FILE_ANY_ACCESS, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_DEVICE, + nullptr); + if (INVALID_HANDLE_VALUE == vDevice) + { + vDevice = nullptr; + + vDosCode = GetLastError(); + break; + } + + auto vArgs = SendInputArgs { aInputCount, aInputBytes, (UINT64)aInputs }; + + auto vReturnBytes = 0ul; + if (!DeviceIoControl( + vDevice, + (UINT32)IoCode::SendInput, + &vArgs, sizeof(vArgs), + &vSendCount, sizeof(vSendCount), + &vReturnBytes, + nullptr)) + { + vDosCode = GetLastError(); + break; + } + + break; + } + if (vDevice) CloseHandle(vDevice), vDevice = nullptr; + + return SetLastError(vDosCode), vSendCount; + } + + auto SendInput(UINT32 aInputCount, LPINPUT aInputs, UINT32 aInputBytes) + -> UINT + { + for (auto i = 0u; i < aInputCount; ++i) + { + auto& vInput = aInputs[i]; + if (INPUT_KEYBOARD == vInput.type) + { + if (KEYEVENTF_SCANCODE & vInput.ki.dwFlags) + { + break; + } + + vInput.ki.wScan = MapVirtualKey(vInput.ki.wVk, MAPVK_VK_TO_VSC); + vInput.ki.dwFlags |= KEYEVENTF_SCANCODE; + + switch (vInput.ki.wVk) + { + case VK_INSERT: + case VK_DELETE: + case VK_HOME: + case VK_END: + case VK_PRIOR: //Page Up + case VK_NEXT: //Page Down + + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + + case VK_DIVIDE: + + case VK_LWIN: + case VK_RCONTROL: + case VK_RWIN: + case VK_RMENU: //ALT + vInput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + break; + } + } + } + + return SendInputCode(aInputCount, aInputs, aInputBytes); + } + +} + +``` + +`Source/DirectInputU/DirectInput.h`: + +```h +#pragma once + +namespace DirectInput +{ + + auto Initialize() + -> HRESULT; + + auto SendInput(UINT32 aCount, LPINPUT aInputs, UINT32 aBytes) + -> UINT; +} + +``` + +`Source/DirectInputU/DirectInputU.vcxproj`: + +```vcxproj + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {97D5C996-7016-421F-9B53-3089F9C18B2D} + Win32Proj + DirectInputU + 10.0.17134.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(ProjectName).$(PlatformTarget) + NativeRecommendedRules.ruleset + true + + + true + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(ProjectName).$(PlatformTarget) + NativeRecommendedRules.ruleset + true + + + false + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(ProjectName).$(PlatformTarget) + NativeRecommendedRules.ruleset + true + + + false + $(SolutionDir)..\Output\$(Configuration)\ + $(ProjectName).dir\$(Configuration)\$(PlatformTarget)\ + $(ProjectName).$(PlatformTarget) + NativeRecommendedRules.ruleset + true + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + stdcpp17 + true + MultiThreadedDebug + + + Console + true + + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + stdcpp17 + true + MultiThreadedDebug + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + true + MultiThreaded + + + Console + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + true + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + Create + Create + Create + Create + + + + + + +``` + +`Source/DirectInputU/DirectInputU.vcxproj.filters`: + +```filters + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + + + Src + + + Src + + + Src + + + + + Src + + + Src + + + +``` + +`Source/DirectInputU/DirectInputU.vcxproj.user`: + +```user + + + + +``` + +`Source/DirectInputU/main.cpp`: + +```cpp +#include "stdafx.h" +#include "DirectInput.h" + +int main() +{ + HRESULT hr = S_OK; + + (void)getchar(); + for (;;) + { + auto vSendCount = 0; + + INPUT vInputs[2]{}; + + vInputs[0].type = INPUT_KEYBOARD; + vInputs[0].ki.wVk = VK_LWIN; + vInputs[0].ki.dwFlags = 0; + + vInputs[1].type = INPUT_KEYBOARD; + vInputs[1].ki.wVk = VK_TAB; + vInputs[1].ki.dwFlags = 0; + + vSendCount = DirectInput::SendInput(_countof(vInputs), vInputs, sizeof(vInputs[0])); + + Sleep(3000); + + vInputs[0].type = INPUT_KEYBOARD; + vInputs[0].ki.wVk = VK_TAB; + vInputs[0].ki.dwFlags = KEYEVENTF_KEYUP; + + vInputs[1].type = INPUT_KEYBOARD; + vInputs[1].ki.wVk = VK_LWIN; + vInputs[1].ki.dwFlags = KEYEVENTF_KEYUP; + + vSendCount = DirectInput::SendInput(_countof(vInputs), vInputs, sizeof(vInputs[0])); + + break; + } + + return hr; +} + +``` + +`Source/DirectInputU/stdafx.cpp`: + +```cpp +#include "stdafx.h" + +``` + +`Source/DirectInputU/stdafx.h`: + +```h +#pragma once + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN 1 +#include +#include + +``` \ No newline at end of file diff --git a/archive/blackhades00/PareidoliaTriggerbot.txt b/archive/blackhades00/PareidoliaTriggerbot.txt new file mode 100644 index 00000000..94ffce87 --- /dev/null +++ b/archive/blackhades00/PareidoliaTriggerbot.txt @@ -0,0 +1,10566 @@ +Project Path: arc_blackhades00_PareidoliaTriggerbot_t58kk7cr + +Source Tree: + +```txt +arc_blackhades00_PareidoliaTriggerbot_t58kk7cr +├── Common +│ ├── config.h +│ └── ioctl.h +├── LICENSE +├── MouClassInputInjection +├── PareidoliaCL +│ ├── LICENSE +│ ├── PareidoliaCL.vcxproj +│ ├── PareidoliaCL.vcxproj.filters +│ ├── README.md +│ ├── console.cpp +│ ├── console.h +│ ├── debug.h +│ ├── disassembler.cpp +│ ├── disassembler.h +│ ├── driver.cpp +│ ├── driver.h +│ ├── keyboard.h +│ ├── log.cpp +│ ├── log.h +│ ├── main.cpp +│ ├── memory_util.cpp +│ ├── memory_util.h +│ ├── ntdll.h +│ ├── overwatch.cpp +│ ├── overwatch.h +│ ├── pattern.cpp +│ ├── pattern.h +│ ├── pe.cpp +│ ├── pe.h +│ ├── process_util.cpp +│ ├── process_util.h +│ ├── random.cpp +│ ├── random.h +│ ├── time_util.cpp +│ ├── time_util.h +│ ├── triggerbot.cpp +│ └── triggerbot.h +├── PareidoliaTriggerbot +│ ├── LICENSE +│ ├── PareidoliaTriggerbot.vcxproj +│ ├── PareidoliaTriggerbot.vcxproj.filters +│ ├── debug.h +│ ├── driver.cpp +│ ├── log.h +│ ├── nt.h +│ ├── object_util.cpp +│ ├── object_util.h +│ ├── pareidolia_triggerbot.cpp +│ ├── pareidolia_triggerbot.h +│ ├── process_access_manager.cpp +│ ├── process_access_manager.h +│ ├── process_util.cpp +│ ├── process_util.h +│ ├── stealth.cpp +│ ├── stealth.h +│ ├── system_util.cpp +│ └── system_util.h +├── PareidoliaTriggerbot.sln +├── README.md +├── VivienneVMM +└── hde + ├── LICENSE + ├── hde.h + ├── hde32.cpp + ├── hde32.h + ├── hde64.cpp + ├── hde64.h + ├── hde_stdint.h + ├── table32.h + └── table64.h + +``` + +`Common/config.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#if defined(_KERNEL_MODE) +#include +#else +#include +#endif + +#include "../VivienneVMM/common/arch_x64.h" + +//============================================================================= +// Names +//============================================================================= +/* + The symbolic link names for the DEVICE_OBJECTs created by the driver. + + NOTE Users should use unique names because these symbolic links can be used + as a detection vector by anti-cheat software. +*/ +#define CFG_DEVICE_NAME_PAREIDOLIA_TRIGGERBOT_U L"pareidolia" +#define CFG_DEVICE_NAME_MOUCLASS_INPUT_INJECTION_U L"mouii" +#define CFG_DEVICE_NAME_VIVIENNE_VMM_U L"vivienne" + +/* + The VivienneVMM ULONG-sized signature returned by cpuid. + + NOTE Users should use a unique name because this value can be used as a + detection vector by anti-cheat software. +*/ +#define CFG_VIVIENNE_VMM_SIGNATURE ((ULONG)'prdl') + +//============================================================================= +// Access Protection +//============================================================================= +/* + Prevent processes from opening user mode handles to the client process. +*/ +#define CFG_ENABLE_PROCESS_ACCESS_PROTECTION + +#if defined(_KERNEL_MODE) +//============================================================================= +// Log +//============================================================================= +/* + The NT file path used for the VivienneVMM log file. + + NOTE Users should use a unique name because this string can be used as a + detection vector by anti-cheat software. +*/ +#define CFG_VIVIENNE_VMM_LOG_FILE_NT_PATH_U \ + (L"\\SystemRoot\\" CFG_DEVICE_NAME_PAREIDOLIA_TRIGGERBOT_U ".log") + +//============================================================================= +// Stealth +//============================================================================= +/* + Unlink the PareidoliaTriggerbot DRIVER_OBJECT from nt!PsLoadedModuleList so + that the driver does not appear in the loaded driver list. + + WARNING This option is only supported on Windows 7. nt!PsLoadedModuleList + is protected by PatchGuard on Windows 8+. +*/ +#define CFG_UNLINK_DRIVER_OBJECT + +#else +//============================================================================= +// Keybinds +//============================================================================= +/* + Exit the client. +*/ +#define CFG_KEY_EXIT_CLIENT (VK_F11) +#define CFG_KEY_EXIT_CLIENT_TEXT "F11" + +/* + Get the Overwatch context for a fully loaded Overwatch process. +*/ +#define CFG_KEY_GET_OVERWATCH_CONTEXT (VK_RETURN) +#define CFG_KEY_GET_OVERWATCH_CONTEXT_TEXT "ENTER" + +/* + Initialize a new triggerbot round context. + + This keybind issues a VivienneVMM Capture Execution Context Register (CECR) + request to obtain the player's trace state address. The player must be + scoped as the Widowmaker class while the request is active. +*/ +#define CFG_KEY_INITIALIZE_NEW_ROUND (VK_F5) + +/* + Decrease / increase the duration of the CECR request when initializing a + new triggerbot round context. +*/ +#define CFG_KEY_DECREASE_NEW_ROUND_REQUEST_DURATION (VK_F8) +#define CFG_KEY_INCREASE_NEW_ROUND_REQUEST_DURATION (VK_F9) + +/* + Enable / disable the triggerbot. +*/ +#define CFG_KEY_TOGGLE_TRIGGERBOT (VK_MBUTTON) + +//============================================================================= +// Client +//============================================================================= +/* + The duration in milliseconds between active process queries during client + initialization. +*/ +#define CFG_PROCESS_QUERY_INTERVAL_MS 3000 + +//============================================================================= +// Overwatch +//============================================================================= +/* + Use hard-coded values for the 'TRACE_STATE_INSTRUCTION' object instead of + resolving it at runtime. +*/ +///#define CFG_USE_FIXED_TRACE_STATE_INSTRUCTION + +#if defined(CFG_USE_FIXED_TRACE_STATE_INSTRUCTION) +#define CFG_TRACE_STATE_INSTRUCTION_RELATIVE_ADDRESS 0x00B8C6F3 +#define CFG_TRACE_STATE_INSTRUCTION_REGISTER REGISTER_RDI +#define CFG_TRACE_STATE_INSTRUCTION_DISPLACEMENT 0x000001FC +#endif + +//============================================================================= +// Triggerbot +//============================================================================= +/* + The duration in milliseconds between tick loop iterations when the + triggerbot is active. +*/ +#define CFG_TB_TICK_LOOP_INTERVAL_ACTIVE_MS 1 + +/* + The duration in milliseconds between tick loop iterations when the + triggerbot is inactive. +*/ +#define CFG_TB_TICK_LOOP_INTERVAL_INACTIVE_MS 1000 + +/* + The default duration in milliseconds of the CECR request when initializing + a new triggerbot round context. This value can be dynamically adjusted + using the CFG_KEY_DECREASE_NEW_ROUND_REQUEST_DURATION and + CFG_KEY_INCREASE_NEW_ROUND_REQUEST_DURATION keybinds. + + NOTE This value is bounded by the range: [50, 10000] +*/ +#define CFG_TB_NEW_ROUND_REQUEST_DURATION_MS 2000 + +/* + The amount of time in milliseconds added to or subtracted from the duration + of the new round CECR request. + + See: CFG_KEY_DECREASE_NEW_ROUND_REQUEST_DURATION +*/ +#define CFG_TB_NEW_ROUND_REQUEST_DURATION_STEP_MS 1000 + +/* + The range of time in microseconds for the randomly generated delay between + trigger activation and trigger release. + + NOTE These default time values were obtained by observing the average delay + between mouse button click and release. Users should update these values to + reflect the average delay for their mouse. + + See: https://github.com/changeofpace/MouHidInputHook +*/ +#define CFG_TB_TRIGGER_RELEASE_DELAY_MIN_US 32000 +#define CFG_TB_TRIGGER_RELEASE_DELAY_MAX_US 55000 + +C_ASSERT(CFG_TB_TRIGGER_RELEASE_DELAY_MIN_US < ULONG_MAX); +C_ASSERT(CFG_TB_TRIGGER_RELEASE_DELAY_MAX_US < ULONG_MAX); + +/* + The range of time in microseconds for the randomly generated delay after + trigger release. +*/ +#define CFG_TB_TRIGGER_COOLDOWN_DELAY_MIN_US 550000 +#define CFG_TB_TRIGGER_COOLDOWN_DELAY_MAX_US 750000 + +C_ASSERT(CFG_TB_TRIGGER_COOLDOWN_DELAY_MIN_US < ULONG_MAX); +C_ASSERT(CFG_TB_TRIGGER_COOLDOWN_DELAY_MAX_US < ULONG_MAX); + +/* + Print the time statistics for each trigger activation. +*/ +///#define CFG_TB_PRINT_TRIGGER_TICK_DATA + +/* + Disable input injection during trigger activation. + + NOTE This option is intended to be used with CFG_TB_PRINT_TRIGGER_TICK_DATA + to debug the trigger release and trigger cooldown settings. +*/ +///#define CFG_TB_DISABLE_INPUT_INJECTION + +/* + Print the value of the local player's trace state variable for each trigger + activation event. +*/ +///#define CFG_TB_PRINT_TRACE_STATE_ON_TRIGGER_ACTIVATION + +#endif + +``` + +`Common/ioctl.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#if defined(_KERNEL_MODE) +#include +#else +#include +#endif + +#include + +#include "../Common/config.h" + +//============================================================================= +// Names +//============================================================================= +#define PAREIDOLIA_DRIVER_NAME_U CFG_DEVICE_NAME_PAREIDOLIA_TRIGGERBOT_U +#define PAREIDOLIA_LOCAL_DEVICE_PATH_U (L"\\\\.\\" PAREIDOLIA_DRIVER_NAME_U) +#define PAREIDOLIA_NT_DEVICE_NAME_U \ + (L"\\Device\\" PAREIDOLIA_DRIVER_NAME_U) +#define PAREIDOLIA_SYMBOLIC_LINK_NAME_U \ + (L"\\DosDevices\\" PAREIDOLIA_DRIVER_NAME_U) + +//============================================================================= +// Ioctls +//============================================================================= +#define FILE_DEVICE_PAREIDOLIA_TRIGGERBOT 41499ul + +#define IOCTL_RESTRICT_PROCESS_ACCESS \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 3900, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_DERESTRICT_PROCESS_ACCESS \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 3901, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_GET_PROCESS_IMAGE_BASE \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 4000, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_GET_PROCESS_IMAGE_FILE_PATH_SIZE \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 4001, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_GET_PROCESS_IMAGE_FILE_PATH \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 4002, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_READ_VIRTUAL_MEMORY \ + CTL_CODE( \ + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, \ + 4100, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +//============================================================================= +// IOCTL_RESTRICT_PROCESS_ACCESS +//============================================================================= +typedef struct _RESTRICT_PROCESS_ACCESS_REQUEST { + ULONG_PTR ProcessId; +} RESTRICT_PROCESS_ACCESS_REQUEST, *PRESTRICT_PROCESS_ACCESS_REQUEST; + +//============================================================================= +// IOCTL_DERESTRICT_PROCESS_ACCESS +//============================================================================= +typedef struct _DERESTRICT_PROCESS_ACCESS_REQUEST { + ULONG_PTR ProcessId; +} DERESTRICT_PROCESS_ACCESS_REQUEST, *PDERESTRICT_PROCESS_ACCESS_REQUEST; + +//============================================================================= +// IOCTL_GET_PROCESS_IMAGE_BASE +//============================================================================= +typedef struct _GET_PROCESS_IMAGE_BASE_REQUEST { + ULONG_PTR ProcessId; +} GET_PROCESS_IMAGE_BASE_REQUEST, *PGET_PROCESS_IMAGE_BASE_REQUEST; + +typedef struct _GET_PROCESS_IMAGE_BASE_REPLY { + ULONG_PTR ImageBase; +} GET_PROCESS_IMAGE_BASE_REPLY, *PGET_PROCESS_IMAGE_BASE_REPLY; + +//============================================================================= +// IOCTL_GET_PROCESS_IMAGE_FILE_PATH_SIZE +//============================================================================= +typedef struct _GET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST { + ULONG_PTR ProcessId; +} GET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST, +*PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST; + +typedef struct _GET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY { + ULONG Size; +} GET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY, +*PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY; + +//============================================================================= +// IOCTL_GET_PROCESS_IMAGE_FILE_PATH +//============================================================================= +typedef struct _GET_PROCESS_IMAGE_FILE_PATH_REQUEST { + ULONG_PTR ProcessId; +} GET_PROCESS_IMAGE_FILE_PATH_REQUEST, *PGET_PROCESS_IMAGE_FILE_PATH_REQUEST; + +typedef struct _GET_PROCESS_IMAGE_FILE_PATH_REPLY { + WCHAR NtFilePath[ANYSIZE_ARRAY]; +} GET_PROCESS_IMAGE_FILE_PATH_REPLY, *PGET_PROCESS_IMAGE_FILE_PATH_REPLY; + +//============================================================================= +// IOCTL_READ_VIRTUAL_MEMORY +//============================================================================= +typedef struct _READ_VIRTUAL_MEMORY_REQUEST { + ULONG_PTR ProcessId; + ULONG_PTR Address; + ULONG Size; +} READ_VIRTUAL_MEMORY_REQUEST, *PREAD_VIRTUAL_MEMORY_REQUEST; + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2019 changeofpace + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`PareidoliaCL/LICENSE`: + +``` +MIT License + +Copyright (c) 2019 changeofpace + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`PareidoliaCL/PareidoliaCL.vcxproj`: + +```vcxproj + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7} + PareidoliaCL + 10.0.17763.0 + + + + Application + true + v141 + Unicode + false + + + Application + false + v141 + true + Unicode + false + + + + + + + + + + + + + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + Level4 + Disabled + true + true + ProgramDatabase + false + MultiThreadedDebug + true + 6066; + + + ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + + + + + Level4 + MaxSpeed + true + true + true + true + true + 6066; + + + true + true + ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2bd23a46-4121-4890-ae9a-6bb1e195c71f} + + + {8eaad2dc-8830-4c01-acfd-cc1e2f567ab2} + + + + + + +``` + +`PareidoliaCL/PareidoliaCL.vcxproj.filters`: + +```filters + + + + + {1701b7f9-134e-49dc-b813-2ad16b795bd4} + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {8a7fd2b7-116a-4161-a436-48420a8b4138} + + + {2f62c391-6741-41f3-b31f-f4f3ff8e849a} + + + {031858e2-9c84-4e2d-acc9-e5b2925a2547} + + + {32d7dfaa-b5a3-4810-8be5-2d434249a3da} + + + {c72711ac-fe9d-4fd0-8047-af945fd9578b} + + + + + PareidoliaCL\Source Files + + + hde\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + PareidoliaCL\Source Files + + + + + hde\Header Files + + + hde\Header Files + + + hde\Header Files + + + hde\Header Files + + + MouiiCL + + + MouiiCL + + + VivienneCL + + + VivienneCL + + + VivienneCL + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + PareidoliaCL\Header Files + + + +``` + +`PareidoliaCL/README.md`: + +```md +# PareidoliaCL + +**PareidoliaCL** is the user mode client for the **PareidoliaTriggerbot** driver. + +## Usage + +In the following sections, words with the *CFG_KEY_* prefix refer to keybinds defined in the [config file](../Common/config.h/). + +### Initialization + +1. Load the **PareidoliaTriggerbot** driver. + +2. Start the **PareidoliaCL** (client) executable. + +3. Wait for the client to display the following line: + + ``` + Waiting for Overwatch process. (Press CFG_KEY_EXIT_CLIENT_TEXT to exit client) + ``` + +4. Start the Overwatch executable. + +5. Wait for the client to detect the Overwatch process and display the following lines: + + ``` + Found Overwatch process. (ProcessId = OVERWATCH_PROCESS_ID) + Press CFG_KEY_GET_OVERWATCH_CONTEXT_TEXT when Overwatch is at the main menu... + ``` + +5. Wait for the Overwatch process to load to the main menu. + +6. Press the CFG_KEY_GET_OVERWATCH_CONTEXT_TEXT keybind to capture the Overwatch context. + +7. Wait for the client to display the following line: + + ``` + Starting TB. + ``` + +8. The triggerbot remains in the tick loop until the user presses the CFG_KEY_EXIT_CLIENT keybind to exit the client. + +Users must start the client before the Overwatch process so that the client can enable process access protection for itself to prevent Overwatch from reading its virtual address space. + +### New Round Initialization + +The triggerbot must be initialized for each new game round using the following steps: + +1. Press the CFG_KEY_INITIALIZE_NEW_ROUND keybind to initiate the VivienneVMM capture execution context register (CECR) request. + +2. Press the Widowmaker 'zoom' keybind and remain scoped until the VivienneVMM CECR request completes. + +3. There are four possible states depending on the result of the VivienneVMM CECR request: + + i. **CECR FAILURE A**: The driver failed to service the request. Restart from step (1). + + ii. **CECR FAILURE B**: The request returned zero matches. This failure occurs when the user misses the CECR timing window by executing step (2) too early or too late. Users can adjust this timing window by increasing or decreasing the duration of the CECR request using the CFG_KEY_DECREASE_NEW_ROUND_SAMPLE_DURATION and CFG_KEY_INCREASE_NEW_ROUND_SAMPLE_DURATION keybinds. Restart from step (1). + + iii. **CECR FAILURE C**: The request returned multiple matches. This failure occurs when another user in the game has caused the trace state instruction to be executed during the CECR request. i.e., The CECR request has the trace state address for multiple players and cannot determine which trace state address corresponds to the local player. Wait a few seconds and restart from step (1). + + iv. **CECR SUCCESS**: The triggerbot was successfully initialized for the round. Press the CFG_KEY_TOGGLE_TRIGGERBOT to enable the triggerbot. + +4. Verify that the triggerbot was initialized with the local player's trace state address using the following steps: + + i. Toggle the triggerbot to the active state. + + ii. Zoom, wait for 100% charge, and place the crosshair over a dynamic, non-player entity (e.g., a spawn door, the lid of a trashcan, the payload). The triggerbot should detect the entity under the crosshair and inject a left mouse click. If this does not occur then restart from step (1). + +## Warnings + +The client is intended to be used for one Overwatch process. i.e., The user should exit the client after the Overwatch process terminates. Behavior is undefined if the user attempts to use the client for multiple Overwatch processes. + +### Desynchronization + +The triggerbot may become desynchronized and exhibit strange behavior (e.g., injecting mouse clicks every second whether the local player is scoped or not). Users can fix this by following the steps to initialize a new round. + +## Limitations + +### Driver Certificate + +The PareidoliaTriggerbot project does not contain a certificate issued by a trusted root authority. Users must enable test signing in order to load the PareidoliaTriggerbot driver. + +## Notes + +* The debug configuration uses the multi-threaded debug runtime library to reduce library requirements in virtual machines. + +``` + +`PareidoliaCL/console.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "console.h" + +#include "debug.h" +#include "log.h" + + +//============================================================================= +// Constants +//============================================================================= +#define INPUT_EVENT_BUFFER_SIZE 64 + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _CONSOLE_DEVICE { + HANDLE DeviceHandle; + BOOL RestoreMode; + DWORD OriginalMode; +} CONSOLE_DEVICE, *PCONSOLE_DEVICE; + +typedef struct _CONSOLE_CONTEXT { + CONSOLE_DEVICE InputDevice; +} CONSOLE_CONTEXT, *PCONSOLE_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static CONSOLE_CONTEXT g_ConContext = {}; + + +//============================================================================= +// Private Prototypes +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +static +BOOL +ConpInitializeDevice( + _In_ DWORD StandardDeviceHandle, + _Out_ PCONSOLE_DEVICE pConsoleDevice +); + +static +VOID +ConpTerminateDevice( + _In_ PCONSOLE_DEVICE pConsoleDevice +); + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +ConInitialization() +{ + CONSOLE_DEVICE InputDevice = {}; + BOOL status = TRUE; + + status = ConpInitializeDevice(STD_INPUT_HANDLE, &InputDevice); + if (!status) + { + goto exit; + } + + // + // Initialize the global context. + // + RtlCopyMemory( + &g_ConContext.InputDevice, + &InputDevice, + sizeof(CONSOLE_DEVICE)); + +exit: + return status; +} + + +VOID +ConTermination() +{ + ConpTerminateDevice(&g_ConContext.InputDevice); +} + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +ConWaitForInputKey( + WORD VirtualKeyCode +) +/*++ + +Description: + + Process all console device input events until the specified virtual key is + issued by the user. + +Parameters: + + VirtualKeyCode - The virtual key code to wait for. + +Remarks: + + This routine blocks the calling thread until the specified virtual key is + processed. + +--*/ +{ + INPUT_RECORD InputEvents[INPUT_EVENT_BUFFER_SIZE] = {}; + DWORD nEventsRead = 0; + DWORD i = 0; + BOOL fKeyProcessed = FALSE; + BOOL status = TRUE; + + for (;;) + { + status = ReadConsoleInputW( + g_ConContext.InputDevice.DeviceHandle, + InputEvents, + ARRAYSIZE(InputEvents), + &nEventsRead); + if (!status) + { + ERR_PRINT("ReadConsoleInputW failed: %u", GetLastError()); + goto exit; + } + + for (i = 0; i < nEventsRead; ++i) + { + if (KEY_EVENT != InputEvents[i].EventType) + { + continue; + } + + if (InputEvents[i].Event.KeyEvent.wVirtualKeyCode == + VirtualKeyCode) + { + fKeyProcessed = TRUE; + break; + } + } + + if (fKeyProcessed) + { + break; + } + } + +exit: + return status; +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +static +BOOL +ConpInitializeDevice( + DWORD StandardDeviceHandle, + PCONSOLE_DEVICE pConsoleDevice +) +{ + HANDLE DeviceHandle = NULL; + DWORD Mode = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(pConsoleDevice, sizeof(*pConsoleDevice)); + + DeviceHandle = GetStdHandle(StandardDeviceHandle); + if (INVALID_HANDLE_VALUE == DeviceHandle || !DeviceHandle) + { + ERR_PRINT("GetStdHandle failed: %u", GetLastError()); + status = FALSE; + goto exit; + } + + status = GetConsoleMode(DeviceHandle, &Mode); + if (!status) + { + ERR_PRINT("GetConsoleMode failed: %u", GetLastError()); + goto exit; + } + + // + // Set out parameters. + // + pConsoleDevice->DeviceHandle = DeviceHandle; + pConsoleDevice->RestoreMode = FALSE; + pConsoleDevice->OriginalMode = Mode; + +exit: + return status; +} + + +_Use_decl_annotations_ +static +VOID +ConpTerminateDevice( + PCONSOLE_DEVICE pConsoleDevice +) +{ + VERIFY(FlushConsoleInputBuffer(pConsoleDevice->DeviceHandle)); + + if (pConsoleDevice->RestoreMode) + { + VERIFY(SetConsoleMode( + pConsoleDevice->DeviceHandle, + pConsoleDevice->OriginalMode)); + } +} + +``` + +`PareidoliaCL/console.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Meta Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +ConInitialization(); + +VOID +ConTermination(); + +//============================================================================= +// Public Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +ConWaitForInputKey( + _In_ WORD VirtualKeyCode +); + +``` + +`PareidoliaCL/debug.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#include + +#include "log.h" + +/*++ + +Macro Name: + + DEBUG_BREAK + +Macro Description: + + A software breakpoint which is only executed if a user mode debugger is + attached to the executing process. + +--*/ +#if defined(_DEBUG) +#define DEBUG_BREAK \ + if (IsDebuggerPresent()) \ + { \ + DebugBreak(); \ + } +#else +#define DEBUG_BREAK +#endif + +/*++ + +Macro Name: + + VERIFY + +Macro Description: + + A validation macro which ASSERTs in debug build configurations and logs + failures in release build configurations. + +--*/ +#if defined(VERIFY) +#error "Unexpected identifier conflict. (VERIFY)" +#endif + +#if defined(_DEBUG) +#define VERIFY(Expression) (_ASSERT(Expression)) +#else +#define VERIFY(Expression) \ + if (!(Expression)) \ + { \ + ERR_PRINT("\'" #Expression "\' failed."); \ + } +#endif + +``` + +`PareidoliaCL/disassembler.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "disassembler.h" + +#include "log.h" + +#include "../hde/hde.h" + + +//============================================================================= +// Constants +//============================================================================= +/* + See 'Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte' in the Intel + instruction manual. +*/ +static CONST X64_REGISTER g_ModRmEffectiveAddressTable[4][8] = { + // + // Mod: 00 + // + { + REGISTER_RAX, REGISTER_RCX, REGISTER_RDX, REGISTER_RBX, + REGISTER_INVALID, REGISTER_INVALID, REGISTER_RSI, REGISTER_RDI + }, + + // + // Mod: 01 + // + { + REGISTER_RAX, REGISTER_RCX, REGISTER_RDX, REGISTER_RBX, + REGISTER_INVALID, REGISTER_RBP, REGISTER_RSI, REGISTER_RDI + }, + + // + // Mod: 10 + // + { + REGISTER_RAX, REGISTER_RCX, REGISTER_RDX, REGISTER_RBX, + REGISTER_INVALID, REGISTER_RBP, REGISTER_RSI, REGISTER_RDI + }, + + // + // Mod: 11 + // + { + REGISTER_RAX, REGISTER_RCX, REGISTER_RDX, REGISTER_RBX, + REGISTER_RSP, REGISTER_RBP, REGISTER_RSI, REGISTER_RDI + }, +}; + +static CONST X64_REGISTER g_ModRmSecondOperandTable32[8] = { + REGISTER_RAX, + REGISTER_RCX, + REGISTER_RDX, + REGISTER_RBX, + REGISTER_RSP, + REGISTER_RBP, + REGISTER_RSI, + REGISTER_RDI +}; + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +DsmParseModRmOperands( + MODRM_BYTE ModRm, + PX64_REGISTER pFirstOperand, + PX64_REGISTER pSecondOperand +) +/*++ + +Description: + + Return the two registers used for the specified ModRM byte. + +Parameters: + + ModRm - The ModRM byte to be parsed. + + pFirstOperand - Returns the first operand for the specified ModRM byte. + + pSecondOperand - Returns the second operand for the specified ModRM byte. + +Remarks: + + Returns FALSE if the specified ModRM byte requires a SIB byte. + +--*/ +{ + X64_REGISTER FirstOperand = {}; + X64_REGISTER SecondOperand = {}; + BOOL status = TRUE; + + // + // Zero out parameters + // + *pFirstOperand = {}; + *pSecondOperand = {}; + + FirstOperand = g_ModRmEffectiveAddressTable[ModRm.Mod][ModRm.RM]; + if (REGISTER_INVALID == FirstOperand) + { + ERR_PRINT("Unhandled first operand element. (Mod = %u, RM = %u)", + ModRm.Mod, + ModRm.RM); + status = FALSE; + goto exit; + } + + SecondOperand = g_ModRmSecondOperandTable32[ModRm.Reg]; + if (REGISTER_INVALID == SecondOperand) + { + ERR_PRINT("Unhandled second operand element. (Reg = %u)", ModRm.Reg); + status = FALSE; + goto exit; + } + + // + // Set out parameters + // + *pFirstOperand = FirstOperand; + *pSecondOperand = SecondOperand; + +exit: + return status; +} + + +_Use_decl_annotations_ +PCSTR +DsmRegisterToString( + X64_REGISTER Register +) +{ + switch (Register) + { + case REGISTER_RIP: return "rip"; + case REGISTER_RAX: return "rax"; + case REGISTER_RCX: return "rcx"; + case REGISTER_RDX: return "rdx"; + case REGISTER_RDI: return "rdi"; + case REGISTER_RSI: return "rsi"; + case REGISTER_RBX: return "rbx"; + case REGISTER_RBP: return "rbp"; + case REGISTER_RSP: return "rsp"; + case REGISTER_R8: return "r8"; + case REGISTER_R9: return "r9"; + case REGISTER_R10: return "r10"; + case REGISTER_R11: return "r11"; + case REGISTER_R12: return "r12"; + case REGISTER_R13: return "r13"; + case REGISTER_R14: return "r14"; + case REGISTER_R15: return "r15"; + default: + break; + } + + return "(INVALID)"; +} + + +_Use_decl_annotations_ +VOID +DsmPrintInstruction( + PHDE_DISASSEMBLY pDisassembly +) +{ + INF_PRINT("Disassembly:"); + INF_PRINT(" len: %hhu", pDisassembly->len); + INF_PRINT(" p_rep: 0x%02hhX", pDisassembly->p_rep); + INF_PRINT(" p_lock: 0x%02hhX", pDisassembly->p_lock); + INF_PRINT(" p_seg: 0x%02hhX", pDisassembly->p_seg); + INF_PRINT(" p_66: 0x%02hhX", pDisassembly->p_66); + INF_PRINT(" p_67: 0x%02hhX", pDisassembly->p_67); + INF_PRINT(" rex: 0x%02hhX", pDisassembly->rex); + INF_PRINT(" rex_w: 0x%02hhX", pDisassembly->rex_w); + INF_PRINT(" rex_r: 0x%02hhX", pDisassembly->rex_r); + INF_PRINT(" rex_x: 0x%02hhX", pDisassembly->rex_x); + INF_PRINT(" rex_b: 0x%02hhX", pDisassembly->rex_b); + INF_PRINT(" opcode: 0x%02hhX", pDisassembly->opcode); + INF_PRINT(" opcode2: 0x%02hhX", pDisassembly->opcode2); + INF_PRINT(" modrm: 0x%02hhX", pDisassembly->modrm); + INF_PRINT(" modrm_mod: 0x%02hhX", pDisassembly->modrm_mod); + INF_PRINT(" modrm_reg: 0x%02hhX", pDisassembly->modrm_reg); + INF_PRINT(" modrm_rm: 0x%02hhX", pDisassembly->modrm_rm); + INF_PRINT(" sib: 0x%02hhX", pDisassembly->sib); + INF_PRINT(" sib_scale: 0x%02hhX", pDisassembly->sib_scale); + INF_PRINT(" sib_index: 0x%02hhX", pDisassembly->sib_index); + INF_PRINT(" sib_base: 0x%02hhX", pDisassembly->sib_base); + INF_PRINT(" imm:"); + INF_PRINT(" imm8: 0x%02hhX", pDisassembly->imm.imm8); + INF_PRINT(" imm16: 0x%04hX", pDisassembly->imm.imm16); + INF_PRINT(" imm32: 0x%08X", pDisassembly->imm.imm32); + INF_PRINT(" imm64: 0x%016llX", pDisassembly->imm.imm64); + INF_PRINT(" disp:"); + INF_PRINT(" disp8: 0x%02hhX", pDisassembly->disp.disp8); + INF_PRINT(" disp16: 0x%04hX", pDisassembly->disp.disp16); + INF_PRINT(" disp32: 0x%08X", pDisassembly->disp.disp32); + INF_PRINT(" flags: 0x%08X", pDisassembly->flags); +} + +``` + +`PareidoliaCL/disassembler.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#pragma once + +#include + +#include "../VivienneVMM/common/arch_x64.h" + +#include "../hde/hde.h" + +//============================================================================= +// Public Types +//============================================================================= +#pragma warning(push) +#pragma warning(disable : 4201) // Nonstandard extension: nameless struct/union +typedef union _MODRM_BYTE { + struct { + UCHAR RM : 3; + UCHAR Reg : 3; + UCHAR Mod : 2; + }; + UCHAR Value; +} MODRM_BYTE, *PMODRM_BYTE; +#pragma warning(pop) + +C_ASSERT(sizeof(MODRM_BYTE) == sizeof(UCHAR)); + +//============================================================================= +// Public Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +DsmParseModRmOperands( + _In_ MODRM_BYTE ModRm, + _Out_ PX64_REGISTER pFirstOperand, + _Out_ PX64_REGISTER pSecondOperand +); + +PCSTR +DsmRegisterToString( + _In_ X64_REGISTER Register +); + +VOID +DsmPrintInstruction( + _In_ PHDE_DISASSEMBLY pDisassembly +); + +``` + +`PareidoliaCL/driver.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "driver.h" + +#include "debug.h" +#include "ntdll.h" + +#include "../Common/ioctl.h" + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _DRIVER_CONTEXT { + HANDLE DeviceHandle; +} DRIVER_CONTEXT, *PDRIVER_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static DRIVER_CONTEXT g_DriverContext = {}; + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +PareidoliaIoInitialization() +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + BOOL status = TRUE; + + hDevice = CreateFileW( + PAREIDOLIA_LOCAL_DEVICE_PATH_U, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (INVALID_HANDLE_VALUE == hDevice) + { + status = FALSE; + goto exit; + } + + // + // Initialize the global context. + // + g_DriverContext.DeviceHandle = hDevice; + +exit: + if (!status) + { + if (INVALID_HANDLE_VALUE != hDevice) + { + VERIFY(CloseHandle(hDevice)); + } + } + + return status; +} + + +VOID +PareidoliaIoTermination() +{ + VERIFY(CloseHandle(g_DriverContext.DeviceHandle)); +} + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +PareidoliaIoRestrictProcessAccess( + ULONG_PTR ProcessId +) +{ + RESTRICT_PROCESS_ACCESS_REQUEST Request = {}; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_RESTRICT_PROCESS_ACCESS, + &Request, + sizeof(Request), + NULL, + 0, + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +PareidoliaIoDerestrictProcessAccess( + ULONG_PTR ProcessId +) +{ + DERESTRICT_PROCESS_ACCESS_REQUEST Request = {}; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_DERESTRICT_PROCESS_ACCESS, + &Request, + sizeof(Request), + NULL, + 0, + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +PareidoliaIoGetProcessImageBase( + ULONG_PTR ProcessId, + PULONG_PTR pImageBase +) +{ + GET_PROCESS_IMAGE_BASE_REQUEST Request = {}; + GET_PROCESS_IMAGE_BASE_REPLY Reply = {}; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pImageBase = 0; + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_GET_PROCESS_IMAGE_BASE, + &Request, + sizeof(Request), + &Reply, + sizeof(Reply), + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + + // + // Set out parameters. + // + *pImageBase = Reply.ImageBase; + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +PareidoliaIoGetProcessImageFilePathSize( + ULONG_PTR ProcessId, + PULONG pcbSize +) +{ + GET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST Request = {}; + GET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY Reply = {}; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pcbSize = 0; + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_GET_PROCESS_IMAGE_FILE_PATH_SIZE, + &Request, + sizeof(Request), + &Reply, + sizeof(Reply), + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + + // + // Set out parameters. + // + *pcbSize = Reply.Size; + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +PareidoliaIoGetProcessImageFilePath( + ULONG_PTR ProcessId, + PWCHAR pwzImageFilePath, + ULONG cbImageFilePath +) +{ + GET_PROCESS_IMAGE_FILE_PATH_REQUEST Request = {}; + PGET_PROCESS_IMAGE_FILE_PATH_REPLY pReply = NULL; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(pwzImageFilePath, cbImageFilePath); + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + + // + // Initialize the reply. + // + pReply = (PGET_PROCESS_IMAGE_FILE_PATH_REPLY)pwzImageFilePath; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_GET_PROCESS_IMAGE_FILE_PATH, + &Request, + sizeof(Request), + pReply, + cbImageFilePath, + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +PareidoliaIoReadVirtualMemory( + ULONG_PTR ProcessId, + ULONG_PTR Address, + PVOID pBuffer, + ULONG cbBuffer +) +{ + READ_VIRTUAL_MEMORY_REQUEST Request = {}; + DWORD cbReturned = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(pBuffer, cbBuffer); + + // + // Initialize the request. + // + Request.ProcessId = ProcessId; + Request.Address = Address; + Request.Size = cbBuffer; + + status = DeviceIoControl( + g_DriverContext.DeviceHandle, + IOCTL_READ_VIRTUAL_MEMORY, + &Request, + sizeof(Request), + pBuffer, + cbBuffer, + &cbReturned, + NULL); + if (!status) + { + goto exit; + } + +exit: + return status; +} + +``` + +`PareidoliaCL/driver.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#include "../Common/ioctl.h" + +//============================================================================= +// Meta Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoInitialization(); + +VOID +PareidoliaIoTermination(); + +//============================================================================= +// Public Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoRestrictProcessAccess( + _In_ ULONG_PTR ProcessId +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoDerestrictProcessAccess( + _In_ ULONG_PTR ProcessId +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoGetProcessImageBase( + _In_ ULONG_PTR ProcessId, + _Out_ PULONG_PTR pImageBase +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoGetProcessImageFilePathSize( + _In_ ULONG_PTR ProcessId, + _Out_ PULONG pcbSize +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoGetProcessImageFilePath( + _In_ ULONG_PTR ProcessId, + _Out_writes_bytes_(cbImageFilePath) PWCHAR pwzImageFilePath, + _In_ ULONG cbImageFilePath +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PareidoliaIoReadVirtualMemory( + _In_ ULONG_PTR ProcessId, + _In_ ULONG_PTR Address, + _Out_writes_bytes_(cbBuffer) PVOID pBuffer, + _In_ ULONG cbBuffer +); + +``` + +`PareidoliaCL/keyboard.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#define IS_KEY_DOWN_ASYNC(VirtualKey) \ + ((BOOLEAN)(GetAsyncKeyState(VirtualKey) & 0x8001)) + +``` + +`PareidoliaCL/log.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "log.h" + +#define NTSTRSAFE_NO_CB_FUNCTIONS + +#include +#include + +#include "debug.h" +#include "ntdll.h" + + +//============================================================================= +// Constants +//============================================================================= +#define OUTPUT_BUFFER_CCH_MAX 512 +#define TIME_BUFFER_CCH 25 +#define MESSAGE_BUFFER_CCH_MAX \ + (OUTPUT_BUFFER_CCH_MAX - TIME_BUFFER_CCH - 80) + +#define VALID_CONFIG_OUTPUT_MASK \ + (LOG_CONFIG_STDOUT | LOG_CONFIG_DEBUGGER | LOG_CONFIG_TIMESTAMP_PREFIX) + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _LOG_CONTEXT { + ULONG Config; +} LOG_CONTEXT, *PLOG_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static LOG_CONTEXT g_LogContext = {}; + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +LogInitialization( + ULONG Config +) +{ + BOOL status = TRUE; + + if ((~VALID_CONFIG_OUTPUT_MASK) & Config) + { + SetLastError(ERROR_INVALID_PARAMETER); + status = FALSE; + goto exit; + } + + // + // Initialize the global context. + // + g_LogContext.Config = Config; + +exit: + return status; +} + + +_Use_decl_annotations_ +HRESULT +LogPrintDirect( + PCSTR pszMessage +) +{ + int printstatus = 0; + HRESULT hresult = S_OK; + + if (LOG_CONFIG_DEBUGGER & g_LogContext.Config) + { + OutputDebugStringA(pszMessage); + } + + if (LOG_CONFIG_STDOUT & g_LogContext.Config) + { + printstatus = printf("%s", pszMessage); + if (0 > printstatus) + { + hresult = E_FAIL; + DEBUG_BREAK; + goto exit; + } + } + +exit: + return hresult; +} + + +_Use_decl_annotations_ +HRESULT +LogPrint( + LOG_LEVEL Level, + ULONG Options, + PCSTR pszFormat, + ... +) +{ + va_list VarArgs = {}; + CHAR szMessageBuffer[MESSAGE_BUFFER_CCH_MAX] = {}; + CHAR szTimeBuffer[TIME_BUFFER_CCH] = {}; + SYSTEMTIME LocalTime = {}; + BOOL fAppendCrLf = FALSE; + CHAR szOutputBuffer[OUTPUT_BUFFER_CCH_MAX] = {}; + int printstatus = 0; + HRESULT hresult = S_OK; + + UNREFERENCED_PARAMETER(Level); + + va_start(VarArgs, pszFormat); + hresult = StringCchVPrintfA( + szMessageBuffer, + RTL_NUMBER_OF(szMessageBuffer), + pszFormat, + VarArgs); + va_end(VarArgs); + if (FAILED(hresult)) + { + DEBUG_BREAK; + goto exit; + } + + if (LOG_CONFIG_TIMESTAMP_PREFIX & g_LogContext.Config) + { + GetLocalTime(&LocalTime); + + hresult = StringCchPrintfA( + szTimeBuffer, + RTL_NUMBER_OF(szTimeBuffer), + "%02hd:%02hd:%02hd.%03hd ", + LocalTime.wHour, + LocalTime.wMinute, + LocalTime.wSecond, + LocalTime.wMilliseconds); + if (FAILED(hresult)) + { + DEBUG_BREAK; + goto exit; + } + } + + if (LOG_OPTION_APPEND_CRLF & Options) + { + fAppendCrLf = TRUE; + } + + hresult = StringCchPrintfA( + szOutputBuffer, + RTL_NUMBER_OF(szOutputBuffer), + "%s%s%s", + szTimeBuffer, + szMessageBuffer, + fAppendCrLf ? "\r\n" : ""); + if (FAILED(hresult)) + { + DEBUG_BREAK; + goto exit; + } + + if (LOG_CONFIG_DEBUGGER & g_LogContext.Config) + { + OutputDebugStringA(szOutputBuffer); + } + + if (LOG_CONFIG_STDOUT & g_LogContext.Config) + { + printstatus = printf("%s", szOutputBuffer); + if (0 > printstatus) + { + hresult = E_FAIL; + DEBUG_BREAK; + goto exit; + } + } + +exit: + return hresult; +} + +``` + +`PareidoliaCL/log.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Constants +//============================================================================= +#define LOG_CONFIG_STDOUT 0x00000001 +#define LOG_CONFIG_DEBUGGER 0x00000002 +#define LOG_CONFIG_TIMESTAMP_PREFIX 0x00000004 + +#define LOG_OPTION_APPEND_CRLF 0x00000001 + +//============================================================================= +// Enumerations +//============================================================================= +typedef enum _LOG_LEVEL { + LogLevelDebug, + LogLevelInfo, + LogLevelWarning, + LogLevelError, +} LOG_LEVEL, *PLOG_LEVEL; + +//============================================================================= +// Meta Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +LogInitialization( + _In_ ULONG Config +); + +//============================================================================= +// Public Interface +//============================================================================= +HRESULT +LogPrintDirect( + _In_z_ PCSTR pszMessage +); + +HRESULT +LogPrint( + _In_ LOG_LEVEL Level, + _In_ ULONG Options, + _In_z_ _Printf_format_string_ PCSTR pszFormat, + ... +); + +#if defined(_DEBUG) +#define DBG_PRINT(Format, ...) \ + LogPrint(LogLevelDebug, LOG_OPTION_APPEND_CRLF, (Format), __VA_ARGS__) +#else +// +// Debug level messages are disabled in release builds. +// +#define DBG_PRINT(Format, ...) +#endif + +#define INF_PRINT(Format, ...) \ + LogPrint(LogLevelInfo, LOG_OPTION_APPEND_CRLF, (Format), __VA_ARGS__) + +#define WRN_PRINT(Format, ...) \ + LogPrint( \ + LogLevelWarning, \ + LOG_OPTION_APPEND_CRLF, \ + (Format), \ + __VA_ARGS__) + +#define ERR_PRINT(Format, ...) \ + LogPrint(LogLevelError, LOG_OPTION_APPEND_CRLF, (Format), __VA_ARGS__) + +``` + +`PareidoliaCL/main.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include + +#include "console.h" +#include "debug.h" +#include "driver.h" +#include "keyboard.h" +#include "log.h" +#include "ntdll.h" +#include "overwatch.h" +#include "process_util.h" +#include "random.h" +#include "time_util.h" +#include "triggerbot.h" + +#include "../Common/config.h" + +#include "../MouClassInputInjection/MouiiCL/driver.h" +#include "../MouClassInputInjection/MouiiCL/mouse_input_injection.h" + +#include "../VivienneVMM/VivienneCL/driver_io.h" + + +//============================================================================= +// Private Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +static +BOOL +WaitForOverwatchProcess( + _Out_ POVERWATCH_CONTEXT pOverwatchContext +) +/*++ + +Description: + + This routine performs the following actions: + + 1. Block the calling thread until a single, active Overwatch process + is detected. + + 2. Wait for the user to press the CFG_KEY_GET_OVERWATCH_CONTEXT + keybind. The user should press this key once the Overwatch process + has loaded the main menu to guarantee that the process has finished + unpacking itself. + + 3. Create the Overwatch context for the target Overwatch process. + +Parameters: + + pOverwatchContext - Returns the Overwatch context for the target Overwatch + process. + +Remarks: + + The user can exit the wait loops by pressing the CFG_KEY_EXIT_CLIENT + keybind. + +--*/ +{ + ULONG_PTR ProcessId = 0; + OVERWATCH_CONTEXT OverwatchContext = {}; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(pOverwatchContext, sizeof(*pOverwatchContext)); + + INF_PRINT("Waiting for Overwatch process. (Press %s to exit client)", + CFG_KEY_EXIT_CLIENT_TEXT); + + // + // Search the active process list until a single Overwatch process is + // detected or the user presses the CFG_KEY_EXIT_CLIENT keybind. + // + for (;;) + { + status = + PsuLookupProcessIdByName(OVERWATCH_PROCESS_NAME_U, &ProcessId); + if (status) + { + break; + } + + if (IS_KEY_DOWN_ASYNC(CFG_KEY_EXIT_CLIENT)) + { + INF_PRINT("Exiting client."); + status = FALSE; + goto exit; + } + + Sleep(CFG_PROCESS_QUERY_INTERVAL_MS); + } + + INF_PRINT("Found Overwatch process. (ProcessId = %Iu)", ProcessId); + + INF_PRINT("Press %s when Overwatch is at the main menu...", + CFG_KEY_GET_OVERWATCH_CONTEXT_TEXT); + + status = ConWaitForInputKey(CFG_KEY_GET_OVERWATCH_CONTEXT); + if (!status) + { + ERR_PRINT("ConWaitForInputKey failed."); + goto exit; + } + + status = OwGetOverwatchContext(ProcessId, &OverwatchContext); + if (!status) + { + ERR_PRINT("OwGetOverwatchContext failed."); + goto exit; + } + + // + // Set out parameters. + // + RtlCopyMemory( + pOverwatchContext, + &OverwatchContext, + sizeof(OVERWATCH_CONTEXT)); + +exit: + return status; +} + + +//============================================================================= +// Meta Interface +//============================================================================= +int +wmain( + _In_ int argc, + _In_ wchar_t* argv[] +) +{ + BOOL fConsoleInitialized = FALSE; + BOOL fVivienneIoInitialized = FALSE; + BOOL fPareidoliaIoInitialized = FALSE; +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + BOOL fProcessAccessRestricted = FALSE; +#endif + BOOL fMouiiIoInitialized = FALSE; + OVERWATCH_CONTEXT OverwatchContext = {}; + int mainstatus = EXIT_SUCCESS; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // + // Initialize the client modules. + // + if (!LogInitialization(LOG_CONFIG_STDOUT | LOG_CONFIG_TIMESTAMP_PREFIX)) + { + ERR_PRINT("LogInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + + if (!TmuInitialization()) + { + ERR_PRINT("TmuInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + + if (!RndInitialization()) + { + ERR_PRINT("RndInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + + if (!ConInitialization()) + { + ERR_PRINT("ConInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + // + fConsoleInitialized = TRUE; + + // + // Initialize the client devices. + // + if (!VivienneIoInitialization()) + { + ERR_PRINT("VivienneIoInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + // + fVivienneIoInitialized = TRUE; + + if (!PareidoliaIoInitialization()) + { + ERR_PRINT("PareidoliaIoInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + // + fPareidoliaIoInitialized = TRUE; + +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + if (!PareidoliaIoRestrictProcessAccess(GetCurrentProcessId())) + { + ERR_PRINT("PareidoliaIoRestrictProcessAccess failed: %u", + GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + // + fProcessAccessRestricted = TRUE; +#endif + + if (!MouiiIoInitialization()) + { + ERR_PRINT("MouiiIoInitialization failed: %u", GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + // + fMouiiIoInitialized = TRUE; + + if (!MouInitializeDeviceStackContext(NULL)) + { + ERR_PRINT("MouInitializeDeviceStackContext failed: %u", + GetLastError()); + mainstatus = EXIT_FAILURE; + goto exit; + } + + // + // Initialize the triggerbot. + // + if (!WaitForOverwatchProcess(&OverwatchContext)) + { + ERR_PRINT("WaitForOverwatchProcess failed."); + mainstatus = EXIT_FAILURE; + goto exit; + } + + TrgInitialization(&OverwatchContext); + + // + // Execute the triggerbot tick loop. + // + TrgTickLoop(); + +exit: + if (fMouiiIoInitialized) + { + MouiiIoTermination(); + } + +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + if (fProcessAccessRestricted) + { + VERIFY(PareidoliaIoDerestrictProcessAccess(GetCurrentProcessId())); + } +#endif + + if (fPareidoliaIoInitialized) + { + PareidoliaIoTermination(); + } + + if (fVivienneIoInitialized) + { + VivienneIoTermination(); + } + + if (fConsoleInitialized) + { + ConTermination(); + } + + return mainstatus; +} + +``` + +`PareidoliaCL/memory_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "memory_util.h" + + +_Use_decl_annotations_ +PVOID +MemAllocateHeap( + SIZE_T cbSize +) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSize); +} + + +_Use_decl_annotations_ +BOOL +MemFreeHeap( + PVOID pBuffer +) +{ + return HeapFree(GetProcessHeap(), 0, pBuffer); +} + +``` + +`PareidoliaCL/memory_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_Check_return_ +_Ret_maybenull_ +_Post_writable_byte_size_(cbSize) +DECLSPEC_ALLOCATOR +PVOID +MemAllocateHeap( + _In_ SIZE_T cbSize +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +MemFreeHeap( + __drv_freesMem(Mem) PVOID pBuffer +); + +``` + +`PareidoliaCL/ntdll.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; + +#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) + +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#define STATUS_REVISION_MISMATCH ((NTSTATUS)0xC0000059L) +#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L) +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) + +#ifndef FlagOn +#define FlagOn(_F,_SF) ((_F) & (_SF)) +#endif + +#ifndef BooleanFlagOn +#define BooleanFlagOn(F,SF) ((BOOLEAN)(((F) & (SF)) != 0)) +#endif + +#ifndef SetFlag +#define SetFlag(_F,_SF) ((_F) |= (_SF)) +#endif + +#ifndef ClearFlag +#define ClearFlag(_F,_SF) ((_F) &= ~(_SF)) +#endif + +#define OFFSET_POINTER(Pointer, Offset, Type) \ + ((Type*)(((PUCHAR)(Pointer)) + (Offset))) + +#define OBJ_CASE_INSENSITIVE 0x00000040L + +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ + } + +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemBasicInformation = 0, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemProcessInformation = 5, + SystemProcessorPerformanceInformation = 8, + SystemHandleInformation = 16, + SystemInterruptInformation = 23, + SystemExceptionInformation = 33, + SystemRegistryQuotaInformation = 37, + SystemLookasideInformation = 45, + SystemExtendedHandleInformation = 64, + SystemPolicyInformation = 134, +} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + _Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer; +} UNICODE_STRING, *PUNICODE_STRING;; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef +VOID +(NTAPI *PIO_APC_ROUTINE) ( + _In_ PVOID ApcContext, + _In_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG Reserved + ); + +typedef LONG KPRIORITY; + +typedef struct _SYSTEM_PROCESS_INFORMATION { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + BYTE Reserved1[48]; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE UniqueProcessId; + PVOID Reserved2; + ULONG HandleCount; + ULONG SessionId; + PVOID Reserved3; + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG Reserved4; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + PVOID Reserved5; + SIZE_T QuotaPagedPoolUsage; + PVOID Reserved6; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER Reserved7[6]; +} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; + +EXTERN_C +NTSTATUS +NTAPI +NtOpenFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions +); + +EXTERN_C +NTSTATUS +NTAPI +NtReadFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_writes_bytes_(Length) PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key +); + +EXTERN_C +NTSTATUS +NTAPI +NtClose( + _In_ HANDLE Handle +); + +EXTERN_C +NTSTATUS +NTAPI +NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength +); + +EXTERN_C +VOID +NTAPI +RtlInitUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_opt_ PCWSTR SourceString +); + +EXTERN_C +BOOLEAN +NTAPI +RtlEqualUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive +); + +EXTERN_C +ULONG +NTAPI +RtlRandomEx( + PULONG Seed +); + +EXTERN_C +PIMAGE_NT_HEADERS +NTAPI +RtlImageNtHeader( + _In_ PVOID BaseAddress +); + +``` + +`PareidoliaCL/overwatch.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "overwatch.h" + +#include "debug.h" +#include "disassembler.h" +#include "driver.h" +#include "log.h" +#include "memory_util.h" +#include "ntdll.h" +#include "pattern.h" +#include "pe.h" +#include "process_util.h" + +#include "../Common/config.h" + +#include "../VivienneVMM/common/arch_x64.h" + +#include "../hde/hde.h" + + +//============================================================================= +// Constants +//============================================================================= +static CHAR g_TraceStateInstructionPattern[] = + "\x89\x87" + "\x00\x00\x00\x00" + "\x0F\x29\x87" + "\x00\x00\x00\x00" + "\x0F\x29\x87" + "\x00\x00\x00\x00"; + +static CHAR g_TraceStateInstructionMask[] = "xx????xxx????xxx????"; + +C_ASSERT( + ARRAYSIZE(g_TraceStateInstructionPattern) == + ARRAYSIZE(g_TraceStateInstructionMask)); + +#define TRACE_STATE_INSTRUCTION_MASK_WILDCARD '?' + +#define TRACE_STATE_INSTRUCTION_DISASSEMBLY_FLAGS (F_MODRM | F_DISP32) + +#define TRACE_STATE_INSTRUCTION_OPCODE 0x89 + +#define TRACE_STATE_FILTER_FLAG_ENEMY_ENTITY (1 << 31) +#define TRACE_STATE_FILTER_FLAG_DYNAMIC_NONPLAYER_ENTITY (1 << 30) + + +//============================================================================= +// Private Prototypes +//============================================================================= +#if defined(CFG_USE_FIXED_TRACE_STATE_INSTRUCTION) +_Check_return_ +_Success_(return != FALSE) +static +BOOL +OwpGetTraceStateInstructionFixed( + _In_ ULONG_PTR ImageBase, + _Out_ PTRACE_STATE_INSTRUCTION pTraceStateInstruction +); + +#else + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +OwpGetTraceStateInstructionDynamic( + _In_ ULONG_PTR ProcessId, + _In_ ULONG_PTR ImageBase, + _Out_ PTRACE_STATE_INSTRUCTION pTraceStateInstruction +); +#endif + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +OwpReadPeHeaderFromDisk( + _In_ ULONG_PTR ProcessId, + _Outptr_result_nullonfailure_ PVOID* ppPeHeader, + _Out_ PULONG pcbPeHeader +); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +OwpFindRemoteTraceStateInstructionAddress( + _In_ ULONG_PTR ProcessId, + _In_ ULONG_PTR ImageBase, + _In_ PVOID pPeHeader, + _Out_ PULONG_PTR pRemoteInstruction +); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +OwpDisassembleTraceStateInstruction( + _In_ ULONG_PTR ProcessId, + _In_ ULONG_PTR RemoteInstruction, + _Out_ PTRACE_STATE_INSTRUCTION pTraceStateInstruction +); + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +OwGetOverwatchContext( + ULONG_PTR ProcessId, + POVERWATCH_CONTEXT pContext +) +{ + ULONG_PTR ImageBase = 0; + TRACE_STATE_INSTRUCTION TraceStateInstruction = {}; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(pContext, sizeof(*pContext)); + + DBG_PRINT("Getting Overwatch context."); + + status = PareidoliaIoGetProcessImageBase(ProcessId, &ImageBase); + if (!status) + { + ERR_PRINT("PareidoliaIoGetProcessImageBase failed: %u", + GetLastError()); + goto exit; + } + + DBG_PRINT("ImageBase: %p", ImageBase); + +#if defined(CFG_USE_FIXED_TRACE_STATE_INSTRUCTION) + status = OwpGetTraceStateInstructionFixed(&TraceStateInstruction); + if (!status) + { + ERR_PRINT("OwpGetTraceStateInstructionFixed failed."); + goto exit; + } +#else + status = OwpGetTraceStateInstructionDynamic( + ProcessId, + ImageBase, + &TraceStateInstruction); + if (!status) + { + ERR_PRINT("OwpGetTraceStateInstructionDynamic failed."); + goto exit; + } +#endif + + DBG_PRINT("Trace state instruction:"); + DBG_PRINT(" Address: %p", TraceStateInstruction.Address); + DBG_PRINT(" Register: %s (%d)", + DsmRegisterToString(TraceStateInstruction.Register), + TraceStateInstruction.Register); + DBG_PRINT(" Displacement: 0x%X", TraceStateInstruction.Displacement); + + // + // Set out parameters. + // + pContext->ProcessId = ProcessId; + pContext->ImageBase = ImageBase; + RtlCopyMemory( + &pContext->TraceStateInstruction, + &TraceStateInstruction, + sizeof(TRACE_STATE_INSTRUCTION)); + +exit: + return status; +} + + +_Use_decl_annotations_ +BOOL +OwIsTracingEnemyPlayerEntity( + WIDOWMAKER_TRACE_STATE TraceState +) +/*++ + +Description: + + Analyze the trace state value to determine if it corresponds to an enemy + player entity. + +Parameters: + + TraceState - The trace state to be analyzed. + +Remarks: + + The current strategy applies a mask to the trace state value to filter + some of the dynamic, non-player entity values from the set of nonzero trace + state values. + + This mask was created by analyzing the relationship between entity types + and the trace state values they generate (when the local player is scoped, + fully charged, and the crosshair is over that entity type). See the + CFG_TB_PRINT_TRACE_STATE_VALUE_ON_TRIGGER_ACTIVATION config setting. + + TODO BUGBUG The mask occasionally filters out enemy McCree entities. + +--*/ +{ + return + FlagOn(TraceState, TRACE_STATE_FILTER_FLAG_ENEMY_ENTITY) && + !FlagOn(TraceState, TRACE_STATE_FILTER_FLAG_DYNAMIC_NONPLAYER_ENTITY); +} + + +//============================================================================= +// Private Interface +//============================================================================= +#if defined(CFG_USE_FIXED_TRACE_STATE_INSTRUCTION) + +_Use_decl_annotations_ +static +BOOL +OwpGetTraceStateInstructionFixed( + ULONG_PTR ImageBase, + PTRACE_STATE_INSTRUCTION pTraceStateInstruction +) +/*++ + +Description: + + Returns the hard-coded trace state instruction. + +Parameters: + + pTraceStateInstruction - Returns the hard-coded trace state instruction. + +Remarks: + + This routine is an alternative to OwpGetTraceStateInstructionDynamic. Use + this routine if an Overwatch patch breaks the trace state instruction + signature. + +--*/ +{ + DBG_PRINT("Getting trace state instruction. (Fixed)"); + + pTraceStateInstruction->Address = + ImageBase + CFG_TRACE_STATE_INSTRUCTION_RELATIVE_ADDRESS; + pTraceStateInstruction->Register = CFG_TRACE_STATE_INSTRUCTION_REGISTER; + pTraceStateInstruction->Displacement = + CFG_TRACE_STATE_INSTRUCTION_DISPLACEMENT; + + return TRUE; +} + +#else + +_Use_decl_annotations_ +static +BOOL +OwpGetTraceStateInstructionDynamic( + ULONG_PTR ProcessId, + ULONG_PTR ImageBase, + PTRACE_STATE_INSTRUCTION pTraceStateInstruction +) +/*++ + +Description: + + Search the text section of the target Overwatch process for the trace state + instruction. + +Parameters: + + ProcessId - The process id of the target Overwatch process. + + ImageBase - The image base of the target Overwatch process. + + pTraceStateInstruction - Returns the disassembly information for the trace + state instruction in the target Overwatch process. + +--*/ +{ + PVOID pPeHeader = NULL; + ULONG cbPeHeader = 0; + ULONG cbImageSize = 0; + ULONG_PTR RemoteInstruction = NULL; + TRACE_STATE_INSTRUCTION TraceStateInstruction = {}; + BOOL status = TRUE; + + // + // Zero out parameters. + // + RtlSecureZeroMemory( + pTraceStateInstruction, + sizeof(*pTraceStateInstruction)); + + DBG_PRINT("Getting trace state instruction. (Dynamic)"); + + status = OwpReadPeHeaderFromDisk(ProcessId, &pPeHeader, &cbPeHeader); + if (!status) + { + ERR_PRINT("OwpReadPeHeaderFromDisk failed."); + goto exit; + } + + cbImageSize = PeGetImageSize(pPeHeader); + if (!cbImageSize) + { + ERR_PRINT("Unexpected image size."); + goto exit; + } + + DBG_PRINT("ImageSize: 0x%X", cbImageSize); + + status = OwpFindRemoteTraceStateInstructionAddress( + ProcessId, + ImageBase, + pPeHeader, + &RemoteInstruction); + if (!status) + { + ERR_PRINT("OwpFindRemoteTraceStateInstructionAddress failed."); + goto exit; + } + + status = OwpDisassembleTraceStateInstruction( + ProcessId, + RemoteInstruction, + &TraceStateInstruction); + if (!status) + { + ERR_PRINT("OwpDisassembleTraceStateInstruction failed."); + goto exit; + } + + // + // Set out parameters. + // + RtlCopyMemory( + pTraceStateInstruction, + &TraceStateInstruction, + sizeof(TRACE_STATE_INSTRUCTION)); + +exit: + if (pPeHeader) + { + VERIFY(MemFreeHeap(pPeHeader)); + } + + return status; +} + +#endif + + +_Use_decl_annotations_ +static +BOOL +OwpReadPeHeaderFromDisk( + ULONG_PTR ProcessId, + PVOID* ppPeHeader, + PULONG pcbPeHeader +) +/*++ + +Description: + + Read the PE header of the Overwatch image file on disk. + +Parameters: + + ProcessId - The process id of the target Overwatch process. + + ppPeHeader - Returns a pointer to an allocated buffer which contains the + contents of the PE header from the Overwatch image file on disk. + + pcbPeHeader - Returns the size of the allocated buffer. + +Remarks: + + If successful, the caller must free the returned buffer by calling + MemFreeHeap. + + We must read the PE header from the image file on disk because the + Overwatch process intentionally destroys its mapped PE header during the + unpacking phase. + +--*/ +{ + PWCHAR pwzImageFilePath = NULL; + ULONG cbImageFilePath = 0; + PVOID pPeHeader = NULL; + ULONG cbPeHeader = 0; + UNICODE_STRING usImageFilePath = {}; + ULONG FileOptions = 0; + HANDLE FileHandle = NULL; + OBJECT_ATTRIBUTES ObjectAttributes = {}; + IO_STATUS_BLOCK IoStatusBlock = {}; + BOOL fFileOpened = FALSE; + PIMAGE_NT_HEADERS pNtHeaders = NULL; + NTSTATUS ntstatus = STATUS_SUCCESS; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *ppPeHeader = NULL; + *pcbPeHeader = 0; + + status = PsuGetProcessImageFilePath( + ProcessId, + &pwzImageFilePath, + &cbImageFilePath); + if (!status) + { + ERR_PRINT("PsuGetProcessImageFilePath failed."); + goto exit; + } + + DBG_PRINT("ImageFilePath: %ls", pwzImageFilePath); + + cbPeHeader = PAGE_SIZE; + + pPeHeader = MemAllocateHeap(cbPeHeader); + if (!pPeHeader) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + RtlInitUnicodeString(&usImageFilePath, pwzImageFilePath); + + InitializeObjectAttributes( + &ObjectAttributes, + &usImageFilePath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + FileOptions = + FILE_SEQUENTIAL_ONLY | + FILE_SYNCHRONOUS_IO_NONALERT | + FILE_NON_DIRECTORY_FILE; + + ntstatus = NtOpenFile( + &FileHandle, + FILE_READ_DATA | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ, + FileOptions); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("NtOpenFile failed: 0x%X", ntstatus); + status = FALSE; + goto exit; + } + // + fFileOpened = TRUE; + + IoStatusBlock = {}; + + ntstatus = NtReadFile( + FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + pPeHeader, + cbPeHeader, + NULL, + NULL); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("NtReadFile failed: 0x%X", ntstatus); + status = FALSE; + goto exit; + } + // + if (IoStatusBlock.Information > ULONG_MAX || + (ULONG)IoStatusBlock.Information != cbPeHeader) + { + ERR_PRINT("NtReadFile failed. (Unexpected read size)"); + status = FALSE; + goto exit; + } + + pNtHeaders = RtlImageNtHeader(pPeHeader); + if (!pNtHeaders) + { + ERR_PRINT("RtlImageNtHeader failed. (Unexpected)"); + status = FALSE; + goto exit; + } + + if (pNtHeaders->OptionalHeader.SizeOfHeaders > cbPeHeader) + { + ERR_PRINT("Unexpected SizeOfHeaders: 0x%X", + pNtHeaders->OptionalHeader.SizeOfHeaders); + status = FALSE; + goto exit; + } + + // + // Set out parameters. + // + *ppPeHeader = pPeHeader; + *pcbPeHeader = cbPeHeader; + +exit: + if (fFileOpened) + { + VERIFY(NT_SUCCESS(NtClose(FileHandle))); + } + + if (!status) + { + if (pPeHeader) + { + VERIFY(MemFreeHeap(pPeHeader)); + } + } + + if (pwzImageFilePath) + { + VERIFY(MemFreeHeap(pwzImageFilePath)); + } + + return status; +} + + +_Use_decl_annotations_ +static +BOOL +OwpFindRemoteTraceStateInstructionAddress( + ULONG_PTR ProcessId, + ULONG_PTR ImageBase, + PVOID pPeHeader, + PULONG_PTR pRemoteInstruction +) +/*++ + +Description: + + Search the text section of the target Overwatch process for the trace state + instruction. + +Parameters: + + ProcessId - The process id of the target Overwatch process. + + ImageBase - The image base of the target Overwatch process. + + pPeHeader - Pointer to a buffer which contains the contents of the PE + header from the Overwatch image file on disk. + + pRemoteInstruction - Returns the virtual address of the trace state + instruction in the target Overwatch process. + +Remarks: + + This routine fails if the instruction is less than HDE_INSTRUCTION_SIZE_MAX + bytes from the end of the remote text section. + +--*/ +{ + PIMAGE_SECTION_HEADER* ppExecutableSections = NULL; + ULONG nExecutableSections = 0; + ULONG_PTR TextSectionRemote = 0; + ULONG cbTextSection = 0; + PVOID pTextSectionLocal = NULL; + ULONG_PTR InstructionLocal = 0; + ULONG_PTR InstructionRemote = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pRemoteInstruction = 0; + + DBG_PRINT("Finding remote trace state instruction address."); + + status = PeGetExecutableSections( + pPeHeader, + &ppExecutableSections, + &nExecutableSections); + if (!status) + { + ERR_PRINT("PeGetExecutableSections failed: %u", GetLastError()); + goto exit; + } + // + if (1 != nExecutableSections) + { + ERR_PRINT("Unexpected number of executable PE sections: %u", + nExecutableSections); + status = FALSE; + goto exit; + } + + // + // Calculate the virtual address of the text section in the remote + // Overwatch process. + // + TextSectionRemote = ImageBase + ppExecutableSections[0]->VirtualAddress; + cbTextSection = ppExecutableSections[0]->Misc.VirtualSize; + + DBG_PRINT("TextSectionRemote: %p (0x%X)", + TextSectionRemote, + cbTextSection); + + pTextSectionLocal = MemAllocateHeap(cbTextSection); + if (!pTextSectionLocal) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + // + // Read the contents of the remote text section. + // + status = PareidoliaIoReadVirtualMemory( + ProcessId, + TextSectionRemote, + pTextSectionLocal, + cbTextSection); + if (!status) + { + ERR_PRINT( + "PareidoliaIoReadVirtualMemory failed: %u" + " (Address = %p, Size = 0x%X)", + GetLastError(), + TextSectionRemote, + cbTextSection); + goto exit; + } + + // + // Search the local copy of the text section for the trace state + // instruction signature. + // + status = PtnFindPatternUnique( + pTextSectionLocal, + cbTextSection, + g_TraceStateInstructionPattern, + g_TraceStateInstructionMask, + ARRAYSIZE(g_TraceStateInstructionPattern), + TRACE_STATE_INSTRUCTION_MASK_WILDCARD, + &InstructionLocal); + if (!status) + { + ERR_PRINT("Failed to find trace state instruction."); + goto exit; + } + + // + // Calculate the virtual address of the trace state instruction in the + // remote Overwatch process. + // + InstructionRemote = + TextSectionRemote + InstructionLocal - (ULONG_PTR)pTextSectionLocal; + + // + // Verify that the remote trace state instruction is in the remote text + // section. + // + if (InstructionRemote + HDE_INSTRUCTION_SIZE_MAX <= InstructionRemote || + InstructionRemote < TextSectionRemote || + InstructionRemote + HDE_INSTRUCTION_SIZE_MAX >= + TextSectionRemote + cbTextSection) + { + ERR_PRINT( + "Unexpected remote trace state instruction address." + " (Address = %p, TextSection = %p - %p)", + InstructionRemote, + TextSectionRemote, + TextSectionRemote + cbTextSection); + status = FALSE; + goto exit; + } + + DBG_PRINT("Found remote trace state instruction: %p (Rva = 0x%08IX)", + InstructionRemote, + InstructionRemote - ImageBase); + + // + // Set out parameters. + // + *pRemoteInstruction = InstructionRemote; + +exit: + if (pTextSectionLocal) + { + VERIFY(MemFreeHeap(pTextSectionLocal)); + } + + if (ppExecutableSections) + { + VERIFY(MemFreeHeap(ppExecutableSections)); + } + + return status; +} + + +_Use_decl_annotations_ +static +BOOL +OwpDisassembleTraceStateInstruction( + ULONG_PTR ProcessId, + ULONG_PTR RemoteInstruction, + PTRACE_STATE_INSTRUCTION pTraceStateInstruction +) +/*++ + +Description: + + Initialize the disassembly information for the trace state instruction in + the target Overwatch process. + +Parameters: + + ProcessId - The process id of the target Overwatch process. + + RemoteInstruction - The virtual address of the trace state instruction in + the target Overwatch process. + + pTraceStateInstruction - Returns the disassembly information for the trace + state instruction in the target Overwatch process. + +Remarks: + + This routine validates the remote trace state instruction. + +--*/ +{ + PVOID pBuffer = NULL; + ULONG cbBuffer = 0; + HDE_DISASSEMBLY Disassembly = {}; + UINT cbInstruction = 0; + MODRM_BYTE ModRm = {}; + X64_REGISTER FirstOperand = {}; + X64_REGISTER SecondOperand = {}; + BOOL status = TRUE; + + // + // Zero out parameters + // + RtlSecureZeroMemory( + pTraceStateInstruction, + sizeof(*pTraceStateInstruction)); + + cbBuffer = HDE_INSTRUCTION_SIZE_MAX; + + pBuffer = MemAllocateHeap(cbBuffer); + if (!pBuffer) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + // + // Read the remote trace state instruction. + // + status = PareidoliaIoReadVirtualMemory( + ProcessId, + RemoteInstruction, + pBuffer, + cbBuffer); + if (!status) + { + ERR_PRINT( + "PareidoliaIoReadVirtualMemory failed: %u" + " (Address = %p, Size = 0x%X)", + GetLastError(), + RemoteInstruction, + cbBuffer); + goto exit; + } + + // + // Validate the instruction's disassembly information. + // + cbInstruction = HDE_DISASM(pBuffer, &Disassembly); + if (!cbInstruction) + { + ERR_PRINT("HDE_DISASM failed."); + status = FALSE; + goto exit; + } + // + if (F_ERROR & Disassembly.flags) + { + ERR_PRINT("HDE_DISASM failed. (Flags = 0x%08X)", Disassembly.flags); + status = FALSE; + goto exit; + } + + if (TRACE_STATE_INSTRUCTION_OPCODE != Disassembly.opcode) + { + ERR_PRINT( + "Unexpected disassembly. (OpCode = %02hhX, Expected = %02hhX)", + Disassembly.opcode, + TRACE_STATE_INSTRUCTION_OPCODE); + status = FALSE; + goto exit; + } + + if (TRACE_STATE_INSTRUCTION_DISASSEMBLY_FLAGS != + (TRACE_STATE_INSTRUCTION_DISASSEMBLY_FLAGS & Disassembly.flags)) + { + ERR_PRINT("Unexpected disassembly. (Flags = 0x%08X)", + Disassembly.flags); + status = FALSE; + goto exit; + } + + ModRm.Value = Disassembly.modrm; + + status = DsmParseModRmOperands(ModRm, &FirstOperand, &SecondOperand); + if (!status) + { + ERR_PRINT("DsmParseModRmOperands failed."); + goto exit; + } + + // + // Set out parameters + // + pTraceStateInstruction->Address = RemoteInstruction; + pTraceStateInstruction->Register = FirstOperand; + pTraceStateInstruction->Displacement = Disassembly.disp.disp32; + +exit: + if (pBuffer) + { + VERIFY(MemFreeHeap(pBuffer)); + } + + return status; +} + +``` + +`PareidoliaCL/overwatch.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#include "../VivienneVMM/common/arch_x64.h" + +//============================================================================= +// Constants +//============================================================================= +#define OVERWATCH_PROCESS_NAME_U L"Overwatch.exe" + +//============================================================================= +// Public Types +//============================================================================= +/*++ + +Name: + + WIDOWMAKER_TRACE_STATE + +Description: + + The Widowmaker trace state is a ULONG-sized variable which has a nonzero + value when: + + 1. There is a Widowmaker player in a match. + + 2. The Widowmaker player has the Widow's Kiss sniper rifle equipped. + + 3. The Widowmaker player is scoped (i.e., zoomed in) and the Widow's + Kiss is fully charged. + + 4. The Widowmaker player's crosshair is over an enemy player entity or + a dynamic, non-player entity (e.g., the payload, a closed spawn + door, or the lid of a trash can). + + This variable exists as a field in the trace state object type. The + following diagram depicts the trace state elements inside the Overwatch + virtual address space: + + + Low Memory + +====================+ + | | Trace State Object + | | ~ + | | +=============+ + | | | | Trace State + |--------------------| |-------------| ~ + | Dynamic Allocation | -----> | Trace State | -----> [0, MAXULONG] + |--------------------| | Variable | + | | |-------------| + | | | | + | | +=============+ + | | + | | + | | + | | + | | + +====================+ + High Memory + + + The game engine maintains one trace state object for each Widowmaker player + in a match. A trace state object is created each time a non-Widowmaker + player swaps to the Widowmaker hero. The object is destroyed when the + player swaps to a non-Widowmaker hero, the round ends, or the player leaves + the match. + + NOTE We do not fully understand the purpose of the trace state variable. We + refer to this concept as the 'trace state' because the variable behaves as + if it were the hit detection result of the game engine running a trace ray + whose origin vector is the Widowmaker player's crosshair. + +--*/ +typedef ULONG WIDOWMAKER_TRACE_STATE, *PWIDOWMAKER_TRACE_STATE; + +/*++ + +Name: + + TRACE_STATE_INSTRUCTION + +Description: + + The address and disassembly information of an instruction in the remote + Overwatch process which accesses a player's Widowmaker trace state variable + when that player presses their 'zoom' keybind. + +Fields: + + Address - The virtual address of the instruction in the remote Overwatch + process. + + Register - The destination register in the instruction. This register + contains the base address of the trace state object when the + instruction is executed. + + Displacement - The field offset of the trace state variable in the trace + state object type. + +Remarks: + + The trace state mechanic provides all of the functionality required to + write a triggerbot. In order to use this mechanic we need to be able to + locate the address of our (local player) trace state object inside the + remote Overwatch process. This is nontrivial for the following reasons: + + 1. The address of the target trace state object changes when the object + is destroyed and recreated. + + 2. We cannot hook code in the Overwatch process because the anti-cheat + frequently scans for code patches. + + 3. Overwatch's code and data are significantly obfuscated. + + We can reliably recover the address of our trace state object using the + VivienneVMM Capture Execution Context Register (CECR) request. Our target + instruction is executed whenever a Widowmaker player presses their 'zoom' + keybind. The following is the annotated assembly of the target instruction: + + Platform: Windows 7 x64 + File Name: Overwatch.exe + Version: 1.41.1.0 - 63372 + SHA256: + + 9d079af7df9ed332f32d303c1eec0aab886f300dc79489019b8bbae682cbdb99 + + Assembly: + + 89 87 FC 01 00 00 mov [rdi+1FCh], eax + + rdi = Pointer to the base address of a trace state object. + 1FC = The field offset of the trace state variable. + eax = The new trace state value. + + NOTE We found this instruction by scanning Overwatch's virtual memory for + values which changed when the local player was scoped / not scoped. + + NOTE We do not fully understand the purpose of the trace state instruction + or its containing function. + +--*/ +typedef struct _TRACE_STATE_INSTRUCTION { + ULONG_PTR Address; + X64_REGISTER Register; + ULONG Displacement; +} TRACE_STATE_INSTRUCTION, *PTRACE_STATE_INSTRUCTION; + +typedef struct _OVERWATCH_CONTEXT { + ULONG_PTR ProcessId; + ULONG_PTR ImageBase; + TRACE_STATE_INSTRUCTION TraceStateInstruction; +} OVERWATCH_CONTEXT, *POVERWATCH_CONTEXT; + +//============================================================================= +// Public Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +OwGetOverwatchContext( + _In_ ULONG_PTR ProcessId, + _Out_ POVERWATCH_CONTEXT pContext +); + + +_Check_return_ +BOOL +OwIsTracingEnemyPlayerEntity( + _In_ WIDOWMAKER_TRACE_STATE TraceState +); + +``` + +`PareidoliaCL/pattern.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "pattern.h" + +#include "log.h" +#include "ntdll.h" + + +_Use_decl_annotations_ +BOOL +PtnFindPatternUnique( + PVOID pBaseAddress, + SIZE_T cbSearchSize, + PCHAR pszPattern, + PCHAR pszMask, + SIZE_T cchPattern, + CHAR Wildcard, + PULONG_PTR pAddress +) +/*++ + +Description: + + Search the specified region of memory for a unique series of bytes which + match the specified pattern and mask. + +Parameters: + + pBaseAddress - The base address of the region of memory to be searched. + + cbSearchSize - The size of the region of memory to be searched. + + pszPattern - A null-terminated string representing the desired series of + bytes to be matched. + + pszMask - A null-terminated string that specifies the indices of the + wildcard characters in the pattern string. + + cchPattern - The number of characters, including the null-terminator, in + the pattern string and the mask string. + + WildCard - The wildcard character used in the mask string. + + pAddress - Returns the address of the matched series of bytes. + +Remarks: + + This routine returns FALSE if multiple matches are found. + +--*/ +{ + SIZE_T cchCompare = 0; + PCHAR pCursor = NULL; + PCHAR pEndAddress = NULL; + SIZE_T i = 0; + PVOID pCandidate = NULL; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pAddress = 0; + + DBG_PRINT("Searching for unique pattern. (Address = %p, Size = 0x%IX)", + pBaseAddress, + cbSearchSize); + + // + // We do not match the null-terminator when searching. + // + cchCompare = cchPattern - 1; + if (!cchCompare || MAXSIZE_T == cchCompare) + { + ERR_PRINT("Invalid pattern length."); + status = FALSE; + goto exit; + } + + pEndAddress = (PCHAR)((ULONG_PTR)pBaseAddress + cbSearchSize); + + for (pCursor = (PCHAR)pBaseAddress; + (ULONG_PTR)pCursor + cchCompare <= (ULONG_PTR)pEndAddress; + pCursor++) + { + for (i = 0; i < cchCompare; i++) + { + if (pszMask[i] == Wildcard) + { + continue; + } + + if (pCursor[i] != pszPattern[i]) + { + break; + } + } + // + if (i != cchCompare) + { + continue; + } + + // + // Verify that we only find one match in the search region. + // + if (pCandidate) + { + ERR_PRINT("Pattern collision. (%p, %p)", pCandidate, pCursor); + status = FALSE; + goto exit; + } + + // + // Set the current address as our candidate. + // + pCandidate = pCursor; + } + // + if (!pCandidate) + { + ERR_PRINT("Failed to find pattern."); + status = FALSE; + goto exit; + } + + // + // Set out parameters. + // + *pAddress = (ULONG_PTR)pCandidate; + +exit: + return status; +} + +``` + +`PareidoliaCL/pattern.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_Check_return_ +_Success_(return != FALSE) +BOOL +PtnFindPatternUnique( + _In_ PVOID pBaseAddress, + _In_ SIZE_T cbSearchSize, + _In_z_ PCHAR pszPattern, + _In_z_ PCHAR pszMask, + _In_ SIZE_T cchPattern, + _In_ CHAR Wildcard, + _Out_ PULONG_PTR pAddress +); + +``` + +`PareidoliaCL/pe.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "pe.h" + +#include "debug.h" +#include "memory_util.h" +#include "ntdll.h" + + +_Use_decl_annotations_ +ULONG +PeGetImageSize( + PVOID pImageBase +) +{ + PIMAGE_NT_HEADERS pNtHeaders = NULL; + ULONG cbImageSize = 0; + + pNtHeaders = RtlImageNtHeader(pImageBase); + if (!pNtHeaders) + { + SetLastError(ERROR_BAD_FORMAT); + goto exit; + } + + cbImageSize = pNtHeaders->OptionalHeader.SizeOfImage; + +exit: + return cbImageSize; +} + + +_Use_decl_annotations_ +BOOL +PeGetSectionsByCharacteristics( + PVOID pImageBase, + ULONG Characteristics, + PIMAGE_SECTION_HEADER** pppSectionHeaders, + PULONG pnSectionHeaders +) +/*++ + +Routine Description: + + Returns the image section header pointer of each section in the image with + the specified characteristics. + +Parameters: + + pImageBase - The base address of the target image. + + Characteristics - A bitmask of image section characteristics to match + against. + + pppSectionHeaders - Returns a pointer to an allocated array of image + section header pointers for sections with the specified + characteristics. + + pnSectionHeaders - Returns the number of elements in the allocated array. + +Remarks: + + If successful, the caller must free the returned array by calling + MemFreeHeap. + +--*/ +{ + PIMAGE_NT_HEADERS pNtHeaders = NULL; + PIMAGE_SECTION_HEADER pSectionHeader = NULL; + USHORT i = 0; + USHORT nSectionHeaders = 0; + SIZE_T cbSectionHeaders = 0; + USHORT j = 0; + PIMAGE_SECTION_HEADER* ppSectionHeaders = NULL; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pppSectionHeaders = NULL; + *pnSectionHeaders = 0; + + pNtHeaders = RtlImageNtHeader(pImageBase); + if (!pNtHeaders) + { + SetLastError(ERROR_BAD_FORMAT); + status = FALSE; + goto exit; + } + + // + // Determine the number of sections which have the specified + // characteristics. + // + pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); + + for (i = 0; + i < pNtHeaders->FileHeader.NumberOfSections; + ++i, ++pSectionHeader) + { + if (pSectionHeader->Characteristics & Characteristics) + { + nSectionHeaders++; + } + } + // + if (!nSectionHeaders) + { + SetLastError(ERROR_NOT_FOUND); + status = FALSE; + goto exit; + } + + // + // Allocate and initialize the returned array. + // + cbSectionHeaders = nSectionHeaders * sizeof(*ppSectionHeaders); + + ppSectionHeaders = + (PIMAGE_SECTION_HEADER*)MemAllocateHeap(cbSectionHeaders); + if (!ppSectionHeaders) + { + SetLastError(ERROR_NO_SYSTEM_RESOURCES); + status = FALSE; + goto exit; + } + + pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); + + for (i = 0, j = 0; + i < pNtHeaders->FileHeader.NumberOfSections; + ++i, ++pSectionHeader) + { + if (!(pSectionHeader->Characteristics & Characteristics)) + { + continue; + } + + if (j < nSectionHeaders) + { + ppSectionHeaders[j] = pSectionHeader; + } + + j++; + } + // + if (j != nSectionHeaders) + { + SetLastError(ERROR_INVALID_DATA); + status = FALSE; + goto exit; + } + + // + // Set out parameters. + // + *pppSectionHeaders = ppSectionHeaders; + *pnSectionHeaders = nSectionHeaders; + +exit: + if (!status) + { + if (ppSectionHeaders) + { + VERIFY(MemFreeHeap(ppSectionHeaders)); + } + } + + return status; +} + + +_Use_decl_annotations_ +BOOL +PeGetExecutableSections( + PVOID pImageBase, + PIMAGE_SECTION_HEADER** pppSectionHeaders, + PULONG pnSectionHeaders +) +{ + return PeGetSectionsByCharacteristics( + pImageBase, + IMAGE_SCN_MEM_EXECUTE, + pppSectionHeaders, + pnSectionHeaders); +} + +``` + +`PareidoliaCL/pe.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_Check_return_ +_Success_(return != 0) +ULONG +PeGetImageSize( + _In_ PVOID pImageBase +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PeGetSectionsByCharacteristics( + _In_ PVOID pImageBase, + _In_ ULONG Characteristics, + _Outptr_result_nullonfailure_ PIMAGE_SECTION_HEADER** pppSectionHeaders, + _Out_ PULONG pnSectionHeaders +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PeGetExecutableSections( + _In_ PVOID pImageBase, + _Outptr_result_nullonfailure_ PIMAGE_SECTION_HEADER** pppSectionHeaders, + _Out_ PULONG pnSectionHeaders +); + +``` + +`PareidoliaCL/process_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "process_util.h" + +#include "debug.h" +#include "driver.h" +#include "log.h" +#include "memory_util.h" +#include "ntdll.h" + + +_Use_decl_annotations_ +BOOL +PsuLookupProcessIdsByName( + PCWSTR pwzProcessName, + PULONG_PTR* ppProcessIds, + PSIZE_T pnProcessIds +) +/*++ + +Routine Description: + + Returns an array of process ids for all processes whose names match the + specified process name. + +Parameters: + + pwzProcessName - The target process name. + + ppProcessIds - Returns a pointer to an allocated array of process ids for + all processes whose names match the specified process name. + + pnProcessIds - Returns the number of elements in the allocated array. + +Remarks: + + If successful, the caller must free the returned array by calling + MemFreeHeap. + +--*/ +{ + PSYSTEM_PROCESS_INFORMATION pSystemProcessInfo = NULL; + ULONG cbSystemProcessInfo = NULL; + UNICODE_STRING usProcessName = {}; + PSYSTEM_PROCESS_INFORMATION pEntry = NULL; + BOOL fHasMoreEntries = FALSE; + SIZE_T nProcessIds = 0; + PULONG_PTR pProcessIds = NULL; + SIZE_T i = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *ppProcessIds = NULL; + *pnProcessIds = 0; + + for (cbSystemProcessInfo = sizeof(*pSystemProcessInfo);;) + { + pSystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)MemAllocateHeap( + cbSystemProcessInfo); + if (!pSystemProcessInfo) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + ntstatus = NtQuerySystemInformation( + SystemProcessInformation, + pSystemProcessInfo, + cbSystemProcessInfo, + &cbSystemProcessInfo); + if (NT_SUCCESS(ntstatus)) + { + break; + } + else if (STATUS_INFO_LENGTH_MISMATCH != ntstatus) + { + ERR_PRINT("NtQuerySystemInformation failed: 0x%X", ntstatus); + status = FALSE; + goto exit; + } + + if (!MemFreeHeap(pSystemProcessInfo)) + { + ERR_PRINT("MemFreeHeap failed: %u", GetLastError()); + status = FALSE; + goto exit; + } + } + + RtlInitUnicodeString(&usProcessName, pwzProcessName); + + // + // Determine the number of processes which have the specified process name. + // + fHasMoreEntries = TRUE; + + for (pEntry = pSystemProcessInfo; + fHasMoreEntries; + pEntry = OFFSET_POINTER( + pEntry, + pEntry->NextEntryOffset, + SYSTEM_PROCESS_INFORMATION)) + { + if (!pEntry->NextEntryOffset) + { + fHasMoreEntries = FALSE; + } + + if (RtlEqualUnicodeString(&usProcessName, &pEntry->ImageName, TRUE)) + { + nProcessIds++; + } + } + // + if (!nProcessIds) + { + SetLastError(ERROR_NOT_FOUND); + status = FALSE; + goto exit; + } + + // + // Allocate and fill the returned buffer. + // + pProcessIds = + (PULONG_PTR)MemAllocateHeap(sizeof(*pProcessIds) * nProcessIds); + if (!pProcessIds) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + fHasMoreEntries = TRUE; + + for (pEntry = pSystemProcessInfo; + fHasMoreEntries; + pEntry = OFFSET_POINTER( + pEntry, + pEntry->NextEntryOffset, + SYSTEM_PROCESS_INFORMATION)) + { + if (!pEntry->NextEntryOffset) + { + fHasMoreEntries = FALSE; + } + + if (RtlEqualUnicodeString(&usProcessName, &pEntry->ImageName, TRUE)) + { + pProcessIds[i] = (ULONG_PTR)pEntry->UniqueProcessId; + i++; + } + } + + // + // Set out parameters. + // + *ppProcessIds = pProcessIds; + *pnProcessIds = nProcessIds; + +exit: + if (!status) + { + if (pProcessIds) + { + VERIFY(MemFreeHeap(pProcessIds)); + } + } + + if (pSystemProcessInfo) + { + VERIFY(MemFreeHeap(pSystemProcessInfo)); + } + + return status; +} + + +_Use_decl_annotations_ +BOOL +PsuLookupProcessIdByName( + PCWSTR pwzProcessName, + PULONG_PTR pProcessId +) +{ + PULONG_PTR pProcessIds = NULL; + SIZE_T nProcessIds = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pProcessId = 0; + + status = + PsuLookupProcessIdsByName(pwzProcessName, &pProcessIds, &nProcessIds); + if (!status) + { + goto exit; + } + // + if (1 != nProcessIds) + { + ERR_PRINT("Process name collision. (ProcessName = %ls)", + pwzProcessName); + SetLastError(ERROR_TOO_MANY_NAMES); + status = FALSE; + goto exit; + } + + // + // Set out parameters. + // + *pProcessId = *pProcessIds; + +exit: + if (pProcessIds) + { + VERIFY(MemFreeHeap(pProcessIds)); + } + + return status; +} + + +_Use_decl_annotations_ +BOOL +PsuGetProcessImageFilePath( + ULONG_PTR ProcessId, + PWCHAR* ppwzImageFilePath, + PULONG pcbImageFilePath +) +/*++ + +Description: + + Returns the NT file path of the image backing the target process. + +Parameters: + + ProcessId - The process id of the target process. + + ppwzImageFilePath - Returns a pointer to an allocated buffer for the NT + file path of the image backing the target process. + + pcbImageFilePath - Returns the size of the allocated buffer. + +Remarks: + + If successful, the caller must free the returned buffer by calling + MemFreeHeap. + +--*/ +{ + ULONG cbImageFilePath = 0; + PWCHAR pwzImageFilePath = NULL; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *ppwzImageFilePath = NULL; + *pcbImageFilePath = 0; + + status = + PareidoliaIoGetProcessImageFilePathSize(ProcessId, &cbImageFilePath); + if (!status) + { + ERR_PRINT("PareidoliaIoGetProcessImageFilePathSize failed: %u", + GetLastError()); + goto exit; + } + + pwzImageFilePath = (PWCHAR)MemAllocateHeap(cbImageFilePath); + if (!pwzImageFilePath) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + status = PareidoliaIoGetProcessImageFilePath( + ProcessId, + pwzImageFilePath, + cbImageFilePath); + if (!status) + { + ERR_PRINT("PareidoliaIoGetProcessImageFilePath failed: %u", + GetLastError()); + goto exit; + } + + // + // Set out parameters. + // + *ppwzImageFilePath = pwzImageFilePath; + *pcbImageFilePath = cbImageFilePath; + +exit: + if (!status) + { + if (pwzImageFilePath) + { + VERIFY(MemFreeHeap(pwzImageFilePath)); + } + } + + return status; +} + +``` + +`PareidoliaCL/process_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_Check_return_ +_Success_(return != FALSE) +BOOL +PsuLookupProcessIdsByName( + _In_z_ PCWSTR pwzProcessName, + _Outptr_result_nullonfailure_ PULONG_PTR* ppProcessIds, + _Out_ PSIZE_T pnProcessIds +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PsuLookupProcessIdByName( + _In_z_ PCWSTR pwzProcessName, + _Out_ PULONG_PTR pProcessId +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +PsuGetProcessImageFilePath( + _In_ ULONG_PTR ProcessId, + _Outptr_result_nullonfailure_ PWCHAR* ppwzImageFilePath, + _Out_ PULONG pcbImageFilePath +); + +``` + +`PareidoliaCL/random.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "random.h" + +#include "ntdll.h" + + +//============================================================================= +// Macros +//============================================================================= +#define SYSTEM_TIME_MONTH_MASK 0x001F +#define SYSTEM_TIME_DAY_MASK 0x000F + +#define PERFORMANCE_COUNTER_MASK 0x000000000000000000000000007FFFFF + + +//============================================================================= +// Private Types +//============================================================================= +#pragma warning(push) +#pragma warning(disable : 4201) // Nonstandard extension: nameless struct/union +typedef union _WEAK_SEED { + struct { + ULONG Month : 5; + ULONG Day : 4; + ULONG TimeStamp : 23; + }; + ULONG Value; +} WEAK_SEED, *PWEAK_SEED; +#pragma warning(pop) + +C_ASSERT(sizeof(WEAK_SEED) == sizeof(ULONG)); + +typedef struct _RANDOM_CONTEXT { + ULONG Seed; +} RANDOM_CONTEXT, *PRANDOM_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static RANDOM_CONTEXT g_RndContext = {}; + + +//============================================================================= +// Private Prototypes +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +static +BOOL +RndpGenerateSeedWeak( + _Out_ PULONG pSeed +); + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +RndInitialization() +{ + ULONG Seed = 0; + BOOL status = TRUE; + + status = RndpGenerateSeedWeak(&Seed); + if (!status) + { + goto exit; + } + + // + // Initialize the global context. + // + g_RndContext.Seed = Seed; + +exit: + return status; +} + + +//============================================================================= +// Public Interface +//============================================================================= +ULONG +RndUlong() +{ + return RtlRandomEx(&g_RndContext.Seed); +} + + +_Use_decl_annotations_ +ULONG +RndUlongInRange( + ULONG Minimum, + ULONG Maximum +) +{ + ULONG Base = 0; + ULONG Value = 0; + + Base = RndUlong(); + Value = Base % (Maximum - Minimum + 1) + Minimum; + + return Value; +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +static +BOOL +RndpGenerateSeedWeak( + PULONG pSeed +) +{ + LARGE_INTEGER PerformanceCounter = {}; + SYSTEMTIME SystemTime = {}; + WEAK_SEED Seed = {}; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pSeed = 0; + + status = QueryPerformanceCounter(&PerformanceCounter); + if (!status) + { + goto exit; + } + + GetSystemTime(&SystemTime); + + Seed.Month = SystemTime.wMonth & SYSTEM_TIME_MONTH_MASK; + Seed.Day = SystemTime.wDay & SYSTEM_TIME_DAY_MASK; + Seed.TimeStamp = PerformanceCounter.QuadPart & PERFORMANCE_COUNTER_MASK; + + // + // Set out parameters. + // + *pSeed = Seed.Value; + +exit: + return status; +} + +``` + +`PareidoliaCL/random.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Meta Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +RndInitialization(); + +//============================================================================= +// Public Interface +//============================================================================= +ULONG +RndUlong(); + +ULONG +RndUlongInRange( + _In_ ULONG Minimum, + _In_ ULONG Maximum +); + +``` + +`PareidoliaCL/time_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "time_util.h" + +#include "ntdll.h" + + +//============================================================================= +// Constants +//============================================================================= +#define SECOND_IN_MICROSECONDS 1000000 + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _TIME_CONTEXT { + LARGE_INTEGER PerformanceFrequency; +} TIME_CONTEXT, *PTIME_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static TIME_CONTEXT g_TimeContext = {}; + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +BOOL +TmuInitialization() +{ + LARGE_INTEGER PerformanceFrequency = {}; + LARGE_INTEGER PerformanceCounter = {}; + BOOL status = TRUE; + + status = QueryPerformanceFrequency(&PerformanceFrequency); + if (!status) + { + goto exit; + } + + // + // Verify that we can query the performance counter. + // + status = QueryPerformanceCounter(&PerformanceCounter); + if (!status) + { + goto exit; + } + + // + // Initialize the global context. + // + g_TimeContext.PerformanceFrequency = PerformanceFrequency; + +exit: + return status; +} + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +LONGLONG +TmuMicrosecondsToTicks( + LONGLONG Microseconds +) +{ + LONGLONG Ticks = 0; + + Ticks = Microseconds * g_TimeContext.PerformanceFrequency.QuadPart; + Ticks /= SECOND_IN_MICROSECONDS; + + return Ticks; +} + + +_Use_decl_annotations_ +LONGLONG +TmuTicksToMicroseconds( + LONGLONG Ticks +) +{ + LONGLONG Microseconds = 0; + + Microseconds = Ticks * SECOND_IN_MICROSECONDS; + Microseconds /= g_TimeContext.PerformanceFrequency.QuadPart; + + return Microseconds; +} + + +_Use_decl_annotations_ +BOOL +TmuStallExecution( + ULONG Microseconds +) +/*++ + +Description: + + Execute a spinloop for a specified time interval. + +Parameters: + + Microseconds - The number of microseconds to stall. + +Remarks: + + This routine does not prevent the executing thread from being preempted. + +--*/ +{ + LONGLONG NumberOfTicks = 0; + LARGE_INTEGER InitialTime = {}; + LONGLONG BreakTick = {}; + LARGE_INTEGER CurrentTime = {}; + BOOL status = TRUE; + + NumberOfTicks = TmuMicrosecondsToTicks(Microseconds); + + if (!QueryPerformanceCounter(&InitialTime)) + { + status = FALSE; + goto exit; + } + + BreakTick = InitialTime.QuadPart + NumberOfTicks; + + if (BreakTick < InitialTime.QuadPart) + { + status = FALSE; + goto exit; + } + + do + { + YieldProcessor(); + + if (!QueryPerformanceCounter(&CurrentTime)) + { + status = FALSE; + goto exit; + } + + if (CurrentTime.QuadPart < InitialTime.QuadPart) + { + status = FALSE; + goto exit; + } + } + while (CurrentTime.QuadPart < BreakTick); + +exit: + return status; +} + +``` + +`PareidoliaCL/time_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Meta Interface +//============================================================================= +_Check_return_ +_Success_(return != FALSE) +BOOL +TmuInitialization(); + +//============================================================================= +// Public Interface +//============================================================================= +LONGLONG +TmuMicrosecondsToTicks( + _In_ LONGLONG Microseconds +); + +LONGLONG +TmuTicksToMicroseconds( + _In_ LONGLONG Ticks +); + +_Check_return_ +_Success_(return != FALSE) +BOOL +TmuStallExecution( + _In_ ULONG Microseconds +); + +``` + +`PareidoliaCL/triggerbot.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "triggerbot.h" + +#include "debug.h" +#include "driver.h" +#include "keyboard.h" +#include "log.h" +#include "memory_util.h" +#include "ntdll.h" +#include "random.h" +#include "time_util.h" + +#include "../Common/config.h" + +#include "../MouClassInputInjection/MouiiCL/mouse_input_injection.h" + +#include "../VivienneVMM/common/arch_x64.h" +#include "../VivienneVMM/common/driver_io_types.h" + +#include "../VivienneVMM/VivienneCL/driver_io.h" + + +//============================================================================= +// Constants +//============================================================================= +/* + The minimum and maximum duration in milliseconds for the new round CECR + request. +*/ +#define NEW_ROUND_REQUEST_DURATION_MIN_MS 50 +#define NEW_ROUND_REQUEST_DURATION_MAX_MS 10000 + +/* + The new round CECR request buffer size. +*/ +#define NUMBER_OF_PLAYERS_MAX 12 + +#define NEW_ROUND_REQUEST_BUFFER_SIZE \ + (UFIELD_OFFSET(CEC_REGISTER_VALUES, Values[NUMBER_OF_PLAYERS_MAX])) + +/* + The index of the debug address register used by the new round CECR request. +*/ +#define NEW_ROUND_REQUEST_DEBUG_ADDRESS_REGISTER 1 + +C_ASSERT( + 0 <= NEW_ROUND_REQUEST_DEBUG_ADDRESS_REGISTER && + NEW_ROUND_REQUEST_DEBUG_ADDRESS_REGISTER <= DAR_COUNT); + + +//============================================================================= +// Private Types +//============================================================================= +/*++ + +Name: + + GAME_ROUND_CONTEXT + +Description: + + The trace state information for the local player. + +Parameters: + + TraceStateAddress - The virtual address of the local player's trace state + variable in the target (remote) Overwatch process. + + NumberOfTriggerEvents - The number of trigger activation events for the + game round. + +Remarks: + + The game round context must be initialized each time the local player + spawns as the Widowmaker hero for the first time since their last hero + swap. The following scenarios are examples of when the user must initialize + a new context: + + - The player joins a ranked matchmaking game and selects the Widowmaker + hero during 'Setup Time'. The context should be initialized when + the player spawns after the setup time expires. + + - The player swaps from the McCree hero to the Widowmaker hero. The + context should be initialized when the player spawns as the + Widowmaker hero. + + The context remains valid until the local player leaves the match, spawns + as a non-Widowmaker hero, or changes teams. + +--*/ +typedef struct _GAME_ROUND_CONTEXT { + ULONG_PTR TraceStateAddress; + SIZE_T NumberOfTriggerEvents; +} GAME_ROUND_CONTEXT, *PGAME_ROUND_CONTEXT; + +typedef struct _TRIGGERBOT_CONTEXT { + OVERWATCH_CONTEXT OverwatchContext; + ULONG NewRoundRequestDuration; + BOOL Active; + GAME_ROUND_CONTEXT RoundContext; +} TRIGGERBOT_CONTEXT, *PTRIGGERBOT_CONTEXT; + + +//============================================================================= +// Module Globals +//============================================================================= +static TRIGGERBOT_CONTEXT g_Triggerbot = {}; + + +//============================================================================= +// Private Prototypes +//============================================================================= +static +VOID +TrgpAdjustNewRoundRequestDuration( + _In_ BOOL fIncreaseDuration +); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +TrgpInitializeNewRound(); + +static +VOID +TrgpResetRound(); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +TrgpToggle(); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +TrgpReadTraceState( + _Out_ PWIDOWMAKER_TRACE_STATE pTraceState +); + +_Check_return_ +_Success_(return != FALSE) +static +BOOL +TrgpActivateTrigger(); + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +VOID +TrgInitialization( + POVERWATCH_CONTEXT pOverwatchContext +) +/*++ + +Description: + + Initialize the global triggerbot context for the target Overwatch context. + +Parameters: + + pOverwatchContext - The target Overwatch context. + +Remarks: + + The triggerbot context is valid until the target Overwatch process + terminates. + +--*/ +{ + RtlCopyMemory( + &g_Triggerbot.OverwatchContext, + pOverwatchContext, + sizeof(OVERWATCH_CONTEXT)); + + g_Triggerbot.NewRoundRequestDuration = + CFG_TB_NEW_ROUND_REQUEST_DURATION_MS; +} + + +//============================================================================= +// Public Interface +//============================================================================= +VOID +TrgTickLoop() +/*++ + +Description: + + The triggerbot tick loop. + +Remarks: + + The tick loop executes until the user presses the CFG_KEY_EXIT_CLIENT + keybind. + + The tick loop is designed to enforce the following states: + + Valid States: + + - TraceStateAddress valid and Triggerbot active. + - TraceStateAddress valid and Triggerbot inactive. + - TraceStateAddress invalid and Triggerbot inactive. + + Invalid States: + + - TraceStateAddress invalid and Triggerbot active. + + The following diagram depicts a simplified overview of the tick loop: + + .-----------------------. + | | + v | + +===============================+ | + | Process user input | | + +===============================+ | + | | + v | + .-------------------------------. | + | Is the round context valid? |--No-->+ + '-------------------------------' | + | | + Yes | + | | + v | + .-------------------------------. | + | Is the triggerbot enabled? |--No-->+ + '-------------------------------' | + | | + Yes | + | | + v | + +===============================+ | + | Read Widowmaker Trace State | | + +===============================+ | + | | + v | + .-------------------------------. | + | Is the player's crosshair |--No-->+ + | over an enemy player? | | + '-------------------------------' | + | | + Yes | + | | + v | + +===============================+ | + | Activate trigger | | + +===============================+ | + | | + v | + +===============================+ | + | Trigger cooldown | | + +===============================+ | + | | + '-----------------------' + +--*/ +{ + WIDOWMAKER_TRACE_STATE TraceState = 0; + + INF_PRINT("Starting TB."); + + for (;;) + { + _ASSERTE(!( + g_Triggerbot.Active && + !g_Triggerbot.RoundContext.TraceStateAddress)); + + // + // Choose the sleep duration based on the active state so that we use + // less cycles when the triggerbot is inactive. + // + if (g_Triggerbot.Active) + { + Sleep(CFG_TB_TICK_LOOP_INTERVAL_ACTIVE_MS); + } + else + { + Sleep(CFG_TB_TICK_LOOP_INTERVAL_INACTIVE_MS); + } + + // + // Process user input. + // + if (IS_KEY_DOWN_ASYNC(CFG_KEY_EXIT_CLIENT)) + { + INF_PRINT("Exiting TB."); + goto exit; + } + + if (IS_KEY_DOWN_ASYNC(CFG_KEY_DECREASE_NEW_ROUND_REQUEST_DURATION)) + { + TrgpAdjustNewRoundRequestDuration(FALSE); + } + + if (IS_KEY_DOWN_ASYNC(CFG_KEY_INCREASE_NEW_ROUND_REQUEST_DURATION)) + { + TrgpAdjustNewRoundRequestDuration(TRUE); + } + + if (IS_KEY_DOWN_ASYNC(CFG_KEY_INITIALIZE_NEW_ROUND)) + { + if (!TrgpInitializeNewRound()) + { + WRN_PRINT("Failed to initialize a new round context."); + continue; + } + } + + if (IS_KEY_DOWN_ASYNC(CFG_KEY_TOGGLE_TRIGGERBOT)) + { + if (!TrgpToggle()) + { + continue; + } + } + + // + // End this iteration if the round context is invalid. + // + if (!g_Triggerbot.RoundContext.TraceStateAddress) + { + continue; + } + + // + // End this iteration if the triggerbot is inactive. + // + if (!g_Triggerbot.Active) + { + continue; + } + + // + // Determine if we should trigger. + // + if (!TrgpReadTraceState(&TraceState)) + { + // + // If we fail to read the trace state then the user most likely + // exited the match. Reset the round context and end this + // iteration. + // + TrgpResetRound(); + + continue; + } + + if (!OwIsTracingEnemyPlayerEntity(TraceState)) + { + continue; + } + + // + // Trigger. + // + if (!TrgpActivateTrigger()) + { + ERR_PRINT("Activate trigger failed. Exiting."); + goto exit; + } + +#if defined(CFG_TB_PRINT_TRACE_STATE_ON_TRIGGER_ACTIVATION) + // + // NOTE We print the trace state value after the trigger activation + // event so that the print routine does not delay the event. + // + INF_PRINT("Trigger activated. (TraceState = 0x%08X)", TraceState); +#endif + } + +exit: + return; +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +static +VOID +TrgpAdjustNewRoundRequestDuration( + BOOL fIncreaseDuration +) +/*++ + +Description: + + Adjust the duration of the CECR request used to initialize a new round + context. + +Parameters: + + fIncreaseDuration - Specifies whether to increase or decrease the duration. + +Remarks: + + The duration is incremented or decremented by the step value. See the + CFG_TB_NEW_ROUND_REQUEST_DURATION_STEP_MS config setting. + + The duration is clamped to the range: + + [NEW_ROUND_REQUEST_DURATION_MIN_MS, NEW_ROUND_REQUEST_DURATION_MAX_MS] + +--*/ +{ + ULONG PreviousDuration = 0; + ULONG NewDuration = 0; + + PreviousDuration = g_Triggerbot.NewRoundRequestDuration; + + // + // Calculate and clamp the new duration. + // + if (fIncreaseDuration) + { + NewDuration = + g_Triggerbot.NewRoundRequestDuration + + CFG_TB_NEW_ROUND_REQUEST_DURATION_STEP_MS; + + if (NEW_ROUND_REQUEST_DURATION_MAX_MS < NewDuration || + NewDuration < PreviousDuration) + { + NewDuration = NEW_ROUND_REQUEST_DURATION_MAX_MS; + } + } + else + { + NewDuration = + g_Triggerbot.NewRoundRequestDuration - + CFG_TB_NEW_ROUND_REQUEST_DURATION_STEP_MS; + + if (NEW_ROUND_REQUEST_DURATION_MIN_MS > NewDuration || + NewDuration > PreviousDuration) + { + NewDuration = NEW_ROUND_REQUEST_DURATION_MIN_MS; + } + } + + _ASSERTE(NEW_ROUND_REQUEST_DURATION_MIN_MS <= NewDuration); + _ASSERTE(NEW_ROUND_REQUEST_DURATION_MAX_MS >= NewDuration); + + // + // Update the global triggerbot context. + // + g_Triggerbot.NewRoundRequestDuration = NewDuration; + + if (g_Triggerbot.NewRoundRequestDuration != PreviousDuration) + { + INF_PRINT("New round request duration: %u ms (Previous %u)", + g_Triggerbot.NewRoundRequestDuration, + PreviousDuration); + } + else + { + INF_PRINT("Failed to update new round request duration. (Limit: %u)", + g_Triggerbot.NewRoundRequestDuration); + } +} + + +_Use_decl_annotations_ +static +BOOL +TrgpInitializeNewRound() +/*++ + +Description: + + Attempt to resolve the remote virtual address of the local player's trace + state variable by issuing a Vivienne CECR request for the trace state + instruction. + +Remarks: + + NOTE The user must press their Overwatch 'zoom' keybind to exercise the + trace state instruction while the request is active. + + NOTE The CECR request may return zero, one, or multiple values: + + If the request returns zero values then the user missed the execution + timing window. + + If the request returns more than one value then an unknown event caused + the instruction to be executed for two different trace state objects. + This may occur when there are multiple Widowmaker players in a match. + In this scenario the user should wait a few seconds then try to + initialize a new round context. See the + CFG_TB_NEW_ROUND_REQUEST_DURATION_MS config setting. + + If the request returns one value then the returned value is most likely + the remote virtual address of the local player's trace state object. + The user should verify this by enabling the triggerbot and aiming their + crosshair over a dynamic, non-player entity (e.g., the payload, a + closed spawn door, or the lid of a trash can). The triggerbot should + trigger as soon as the local player's Widow's Kiss is fully charged. If + the triggerbot does not trigger or behaves erratically then the user + should initialize a new round context. + +--*/ +{ + PCEC_REGISTER_VALUES pRegisterValues = NULL; + ULONG cbRegisterValues = 0; + BOOL status = TRUE; + + // + // Reset the current round context and disable the triggerbot. + // + TrgpResetRound(); + + INF_PRINT("Initializing a new round context."); + + // + // Allocate the captured context buffer. + // + cbRegisterValues = NEW_ROUND_REQUEST_BUFFER_SIZE; + + pRegisterValues = (PCEC_REGISTER_VALUES)MemAllocateHeap(cbRegisterValues); + if (!pRegisterValues) + { + ERR_PRINT("MemAllocateHeap failed."); + status = FALSE; + goto exit; + } + + INF_PRINT("Issuing CECR request. (Duration = %u ms)", + g_Triggerbot.NewRoundRequestDuration); + + // + // Issue a CECR request for the remote trace state instruction. + // + status = VivienneIoCaptureRegisterValues( + g_Triggerbot.OverwatchContext.ProcessId, + NEW_ROUND_REQUEST_DEBUG_ADDRESS_REGISTER, + g_Triggerbot.OverwatchContext.TraceStateInstruction.Address, + HWBP_TYPE::Execute, + HWBP_SIZE::Byte, + g_Triggerbot.OverwatchContext.TraceStateInstruction.Register, + g_Triggerbot.NewRoundRequestDuration, + pRegisterValues, + cbRegisterValues); + if (!status) + { + ERR_PRINT( + "[CECR FAILURE A] VivienneIoCaptureRegisterValues failed: %u", + GetLastError()); + goto exit; + } + // + if (!pRegisterValues->NumberOfValues) + { + ERR_PRINT("[CECR FAILURE B] Failed to find a trace state address."); + status = FALSE; + goto exit; + } + // + if (1 != pRegisterValues->NumberOfValues) + { + ERR_PRINT( + "[CECR FAILURE C] Found multiple trace state addresses." + " (Found %u)", + pRegisterValues->NumberOfValues); + status = FALSE; + goto exit; + } + + // + // Update the round context. + // + g_Triggerbot.RoundContext.TraceStateAddress = + pRegisterValues->Values[0] + + g_Triggerbot.OverwatchContext.TraceStateInstruction.Displacement; + + DBG_PRINT("TraceStateAddress: %p", + g_Triggerbot.RoundContext.TraceStateAddress); + + INF_PRINT("[CECR SUCCESS] Round initialized. (Ready to trigger)"); + +exit: + if (pRegisterValues) + { + VERIFY(MemFreeHeap(pRegisterValues)); + } + + return status; +} + + +static +VOID +TrgpResetRound() +/*++ + +Description: + + Reset the round context and disable the triggerbot. + +--*/ +{ + INF_PRINT("Resetting round context. (NumberOfTriggerEvents = %Iu)", + g_Triggerbot.RoundContext.NumberOfTriggerEvents); + + RtlSecureZeroMemory( + &g_Triggerbot.RoundContext, + sizeof(g_Triggerbot.RoundContext)); + + // + // Disable the triggerbot. + // + if (g_Triggerbot.Active) + { + g_Triggerbot.Active = FALSE; + + INF_PRINT("TB: Inactive"); + } +} + + +_Use_decl_annotations_ +static +BOOL +TrgpToggle() +/*++ + +Description: + + Toggle the triggerbot active state. + +--*/ +{ + BOOL status = TRUE; + + // + // Prevent the user from enabling the triggerbot if the round context is + // invalid. + // + if (!g_Triggerbot.RoundContext.TraceStateAddress) + { + WRN_PRINT("Initialize a new round context before enabling TB."); + status = FALSE; + goto exit; + } + + // + // Update the triggerbot active state. + // + g_Triggerbot.Active = !g_Triggerbot.Active; + + INF_PRINT("TB: %s", g_Triggerbot.Active ? "Enabled" : "Disabled"); + +exit: + return status; +} + + +_Use_decl_annotations_ +static +BOOL +TrgpReadTraceState( + PWIDOWMAKER_TRACE_STATE pTraceState +) +/*++ + +Description: + + Read the ULONG value at the remote virtual address of the local player's + trace state variable. + +Parameters: + + pTraceState - Returns the trace state value. + +Remarks: + + On failure, the caller must invalidate the current round context. + +--*/ +{ + WIDOWMAKER_TRACE_STATE TraceState = 0; + BOOL status = TRUE; + + // + // Zero out parameters. + // + *pTraceState = 0; + + status = PareidoliaIoReadVirtualMemory( + g_Triggerbot.OverwatchContext.ProcessId, + g_Triggerbot.RoundContext.TraceStateAddress, + &TraceState, + sizeof(TraceState)); + if (!status) + { + ERR_PRINT("PareidoliaIoReadVirtualMemory failed: %u (Address = %p)", + GetLastError(), + g_Triggerbot.RoundContext.TraceStateAddress); + goto exit; + } + + // + // Set out parameters. + // + *pTraceState = TraceState; + +exit: + return status; +} + + +_Use_decl_annotations_ +static +BOOL +TrgpActivateTrigger() +/*++ + +Description: + + Execute a trigger activation event by injecting a left mouse button click + and release. + +Remarks: + + If this routine fails then the client or driver has encountered a critical + error, and the caller must exit the tick loop. + +--*/ +{ +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + LARGE_INTEGER ClickTime = {}; + LARGE_INTEGER ReleaseTime = {}; + LARGE_INTEGER FinishTime = {}; +#endif + ULONG ReleaseDelay = 0; + ULONG CooldownDelay = 0; + BOOL status = TRUE; + +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + QueryPerformanceCounter(&ClickTime); +#endif + +#if !defined(CFG_TB_DISABLE_INPUT_INJECTION) + // + // We inject the left mouse button click immediately to reduce the latency + // between when we determine that we should trigger and when the trigger + // activation event occurs. + // + status = MouInjectButtonInput( + g_Triggerbot.OverwatchContext.ProcessId, + MOUSE_LEFT_BUTTON_DOWN, + 0); + if (!status) + { + ERR_PRINT("MouInjectButtonInput failed: %u", GetLastError()); + goto exit; + } +#endif + +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + QueryPerformanceCounter(&ClickTime); +#endif + + // + // Generate the duration of the release delay. + // + ReleaseDelay = RndUlongInRange( + CFG_TB_TRIGGER_RELEASE_DELAY_MIN_US, + CFG_TB_TRIGGER_RELEASE_DELAY_MAX_US); + + // + // Stall execution until the release delay expires. + // + status = TmuStallExecution(ReleaseDelay); + if (!status) + { + ERR_PRINT("TmuStallExecution failed. (Release)"); + goto exit; + } + +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + QueryPerformanceCounter(&ReleaseTime); +#endif + +#if !defined(CFG_TB_DISABLE_INPUT_INJECTION) + // + // Inject the left mouse button release. + // + status = MouInjectButtonInput( + g_Triggerbot.OverwatchContext.ProcessId, + MOUSE_LEFT_BUTTON_UP, + 0); + if (!status) + { + ERR_PRINT("MouInjectButtonInput failed: %u", GetLastError()); + goto exit; + } +#endif + +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + QueryPerformanceCounter(&ReleaseTime); +#endif + + // + // Generate the duration of the cooldown delay. + // + CooldownDelay = RndUlongInRange( + CFG_TB_TRIGGER_COOLDOWN_DELAY_MIN_US, + CFG_TB_TRIGGER_COOLDOWN_DELAY_MAX_US); + + // + // Stall execution until the cooldown delay expires. + // + status = TmuStallExecution(CooldownDelay); + if (!status) + { + ERR_PRINT("TmuStallExecution failed. (Cooldown)"); + goto exit; + } + +#if defined(CFG_TB_PRINT_TRIGGER_TICK_DATA) + QueryPerformanceCounter(&FinishTime); + + // + // NOTE We print debug tick times at the end of the function so that the + // print routines do not impact the trigger timing window. + // + INF_PRINT("Trigger activated:"); + INF_PRINT(" Click %016I64X", ClickTime.QuadPart); + INF_PRINT(" Release %016I64X (%016I64Xus)", + ReleaseTime.QuadPart, + TmuTicksToMicroseconds(ReleaseTime.QuadPart - ClickTime.QuadPart)); + INF_PRINT(" Cooldown %016I64X (%016I64Xus)", + FinishTime.QuadPart, + TmuTicksToMicroseconds(FinishTime.QuadPart - ReleaseTime.QuadPart)); + INF_PRINT(" Total %016I64X (%016I64Xus)", + FinishTime.QuadPart - ClickTime.QuadPart, + TmuTicksToMicroseconds(FinishTime.QuadPart - ClickTime.QuadPart)); +#endif + + // + // Update the current round statistics. + // + g_Triggerbot.RoundContext.NumberOfTriggerEvents++; + +exit: + return status; +} + +``` + +`PareidoliaCL/triggerbot.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#include "overwatch.h" + +//============================================================================= +// Meta Interface +//============================================================================= +VOID +TrgInitialization( + _In_ POVERWATCH_CONTEXT pOverwatchContext +); + +//============================================================================= +// Public Interface +//============================================================================= +VOID +TrgTickLoop(); + +``` + +`PareidoliaTriggerbot.sln`: + +```sln + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.779 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PareidoliaTriggerbot", "PareidoliaTriggerbot\PareidoliaTriggerbot.vcxproj", "{CF897E67-92A8-4D2A-89BE-F0F20956BB2A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PareidoliaCL", "PareidoliaCL\PareidoliaCL.vcxproj", "{8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouClassInputInjection", "MouClassInputInjection\MouClassInputInjection\MouClassInputInjection.vcxproj", "{EF63C19B-04B6-453D-A8F8-098840055017}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouiiCL", "MouClassInputInjection\MouiiCL\MouiiCL.vcxproj", "{2BD23A46-4121-4890-AE9A-6BB1E195C71F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VivienneCL", "VivienneVMM\VivienneCL\VivienneCL.vcxproj", "{8EAAD2DC-8830-4C01-ACFD-CC1E2F567AB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VivienneVMM", "VivienneVMM\VivienneVMM\VivienneVMM.vcxproj", "{914E9EBE-76FF-456A-999C-AC55D05890EB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Debug|x64.ActiveCfg = Debug|x64 + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Debug|x64.Build.0 = Debug|x64 + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Debug|x64.Deploy.0 = Debug|x64 + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Release|x64.ActiveCfg = Release|x64 + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Release|x64.Build.0 = Release|x64 + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A}.Release|x64.Deploy.0 = Release|x64 + {8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7}.Debug|x64.ActiveCfg = Debug|x64 + {8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7}.Debug|x64.Build.0 = Debug|x64 + {8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7}.Release|x64.ActiveCfg = Release|x64 + {8496E9A2-1B64-4F8B-9ECE-3FBBE61AA3A7}.Release|x64.Build.0 = Release|x64 + {EF63C19B-04B6-453D-A8F8-098840055017}.Debug|x64.ActiveCfg = Debug Library|x64 + {EF63C19B-04B6-453D-A8F8-098840055017}.Debug|x64.Build.0 = Debug Library|x64 + {EF63C19B-04B6-453D-A8F8-098840055017}.Release|x64.ActiveCfg = Release Library|x64 + {EF63C19B-04B6-453D-A8F8-098840055017}.Release|x64.Build.0 = Release Library|x64 + {2BD23A46-4121-4890-AE9A-6BB1E195C71F}.Debug|x64.ActiveCfg = Debug Library|x64 + {2BD23A46-4121-4890-AE9A-6BB1E195C71F}.Debug|x64.Build.0 = Debug Library|x64 + {2BD23A46-4121-4890-AE9A-6BB1E195C71F}.Release|x64.ActiveCfg = Release Library|x64 + {2BD23A46-4121-4890-AE9A-6BB1E195C71F}.Release|x64.Build.0 = Release Library|x64 + {8EAAD2DC-8830-4C01-ACFD-CC1E2F567AB2}.Debug|x64.ActiveCfg = Debug Library|x64 + {8EAAD2DC-8830-4C01-ACFD-CC1E2F567AB2}.Debug|x64.Build.0 = Debug Library|x64 + {8EAAD2DC-8830-4C01-ACFD-CC1E2F567AB2}.Release|x64.ActiveCfg = Release Library|x64 + {8EAAD2DC-8830-4C01-ACFD-CC1E2F567AB2}.Release|x64.Build.0 = Release Library|x64 + {914E9EBE-76FF-456A-999C-AC55D05890EB}.Debug|x64.ActiveCfg = Debug Library|x64 + {914E9EBE-76FF-456A-999C-AC55D05890EB}.Debug|x64.Build.0 = Debug Library|x64 + {914E9EBE-76FF-456A-999C-AC55D05890EB}.Release|x64.ActiveCfg = Release Library|x64 + {914E9EBE-76FF-456A-999C-AC55D05890EB}.Release|x64.Build.0 = Release Library|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1C610BEB-29E6-4CA3-8064-703DC8FFF564} + EndGlobalSection +EndGlobal + +``` + +`PareidoliaTriggerbot/LICENSE`: + +``` +MIT License + +Copyright (c) 2019 changeofpace + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`PareidoliaTriggerbot/PareidoliaTriggerbot.vcxproj`: + +```vcxproj + + + + + Debug + x64 + + + Release + x64 + + + + {CF897E67-92A8-4D2A-89BE-F0F20956BB2A} + {dd38f7fc-d7bd-488b-9242-7d8754cde80d} + v4.5 + 12.0 + Debug + PareidoliaTriggerbot + $(LatestTargetPlatformVersion) + + + + Windows7 + true + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + false + + + Windows7 + false + WindowsKernelModeDriver10.0 + Driver + WDM + Desktop + false + + + + + + + + + + + DbgengKernelDebugger + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration)\ + pareidolia + + + DbgengKernelDebugger + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration)\ + pareidolia + + + + 4748;28751;28175;%(DisableSpecificWarnings) + + + + + 4603;4627;4986;4987;4996;28751;28175;%(DisableSpecificWarnings) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {ef63c19b-04b6-453d-a8f8-098840055017} + + + {914e9ebe-76ff-456a-999c-ac55d05890eb} + + + + + + +``` + +`PareidoliaTriggerbot/PareidoliaTriggerbot.vcxproj.filters`: + +```filters + + + + + {cffb03ca-d654-4bb4-a275-42d69f6315cc} + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {58629cad-f414-4e60-9e8d-3fd627e52f2e} + + + {10af24fe-58f1-403c-b9ae-7bce5b5316b3} + + + {8e25171f-8220-4914-9e14-47b52f2b3438} + + + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + PareidoliaTriggerbot\Source Files + + + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + PareidoliaTriggerbot\Header Files + + + HyperPlatform + + + VivienneVMM + + + MouClassInputInjection + + + MouClassInputInjection + + + VivienneVMM + + + PareidoliaTriggerbot\Header Files + + + VivienneVMM + + + +``` + +`PareidoliaTriggerbot/debug.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +#include "log.h" + +/*++ + +Name: + + DEBUG_BREAK + +Description: + + A software breakpoint which is only executed if all of the following + conditions are true: + + 1. Debug build configuration. + + 2. Debugging was enabled on the machine at boot time. + + 3. A kernel debugger is currently attached to the machine. + +--*/ +#if defined(DBG) +#define DEBUG_BREAK \ + if (!(KD_DEBUGGER_NOT_PRESENT)) \ + { \ + DbgBreakPoint(); \ + } +#else +#define DEBUG_BREAK +#endif + +/*++ + +Name: + + VERIFY + +Description: + + A validation macro which ASSERTs in debug build configurations and logs + failures in release build configurations. + +Remarks: + + NT_VERIFY and RTL_SOFT_ASSERT are similar utility macros. + +--*/ +#if defined(VERIFY) +#error "Unexpected identifier conflict. (VERIFY)" +#endif + +#if defined(DBG) +#define VERIFY(NtExpression) (NT_ASSERT(NT_SUCCESS(NtExpression))) +#else +#define VERIFY(NtExpression) \ +{ \ + NTSTATUS Verify_NtStatus_ = (NtExpression); \ + if (!NT_SUCCESS(Verify_NtStatus_)) \ + { \ + ERR_PRINT("\'" #NtExpression "\' failed: 0x%X", Verify_NtStatus_); \ + } \ +} +#endif + +``` + +`PareidoliaTriggerbot/driver.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include + +#include "debug.h" +#include "log.h" +#include "nt.h" +#include "object_util.h" +#include "pareidolia_triggerbot.h" +#include "stealth.h" +#include "system_util.h" + +#include "../Common/config.h" +#include "../Common/ioctl.h" + +#include "../MouClassInputInjection/Common/ioctl.h" +#include "../MouClassInputInjection/MouClassInputInjection/driver.h" + +#include "../VivienneVMM/common/driver_io_types.h" +#include "../VivienneVMM/VivienneVMM/vivienne.h" +#include "../VivienneVMM/VivienneVMM/HyperPlatform/HyperPlatform/driver.h" + + +//============================================================================= +// Private Prototypes +//============================================================================= +EXTERN_C +DRIVER_INITIALIZE +DriverEntry; + +EXTERN_C +static +DRIVER_UNLOAD +DriverUnload; + +_Dispatch_type_(IRP_MJ_CREATE) +EXTERN_C +static +DRIVER_DISPATCH +DispatchCreate; + +_Dispatch_type_(IRP_MJ_CLOSE) +EXTERN_C +static +DRIVER_DISPATCH +DispatchClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +EXTERN_C +static +DRIVER_DISPATCH +DispatchDeviceControl; + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +DriverEntry( + PDRIVER_OBJECT pDriverObject, + PUNICODE_STRING pRegistryPath +) +{ + BOOLEAN fVivienneVmmLoaded = FALSE; + BOOLEAN fPareidoliaTriggerbotLoaded = FALSE; + BOOLEAN fMouClassInputInjectionLoaded = FALSE; + NTSTATUS ntstatus = STATUS_SUCCESS; + + DBG_PRINT("Loading %ls.", PAREIDOLIA_NT_DEVICE_NAME_U); + + // + // Modify our driver object so that we can register privileged callbacks. + // + // NOTE This is only required if the driver is not signed. + // + SysSetSignedKernelImageFlag(pDriverObject); + + // + // Load the driver subsystems. + // + // NOTE Each subsystem will create its own named device object and modify + // fields in the driver object. + // + // NOTE We must load the VivienneVMM subsystem first because the other + // subsystems use its log module. + // + ntstatus = HyperPlatformDriverEntry(pDriverObject, pRegistryPath); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("HyperPlatformDriverEntry failed: 0x%X", ntstatus); + goto exit; + } + // + fVivienneVmmLoaded = TRUE; + + ntstatus = PareidoliaTriggerbotDriverEntry(pDriverObject, pRegistryPath); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PareidoliaTriggerbotDriverEntry failed: 0x%X", ntstatus); + goto exit; + } + // + fPareidoliaTriggerbotLoaded = TRUE; + + ntstatus = MouClassInputInjectionDriverEntry(pDriverObject, pRegistryPath); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("MouClassInputInjectionDriverEntry failed: 0x%X", ntstatus); + goto exit; + } + // + fMouClassInputInjectionLoaded = TRUE; + + // + // Set the driver dispatch routines after the subsystems are loaded so that + // all IO requests are sent to the forward routines. + // + pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; + pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; + pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + DispatchDeviceControl; + pDriverObject->DriverUnload = DriverUnload; + +#if defined(CFG_UNLINK_DRIVER_OBJECT) + // + // Unlink the driver object from the loaded modules list. + // + // NOTE We must unlink the driver object after the subsystems are loaded + // because privileged callback registration requires our driver to be in + // the loaded modules list. + // + ntstatus = StlIsUnlinkDriverObjectSupported(); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("StlIsUnlinkDriverObjectSupported failed: 0x%X", ntstatus); + goto exit; + } + + ntstatus = StlUnlinkDriverObject(pDriverObject); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("StlUnlinkDriverObject failed: 0x%X", ntstatus); + goto exit; + } +#endif + + DBG_PRINT("%ls loaded.", PAREIDOLIA_NT_DEVICE_NAME_U); + +exit: + if (!NT_SUCCESS(ntstatus)) + { + if (fMouClassInputInjectionLoaded) + { + MouClassInputInjectionDriverUnload(pDriverObject); + } + + if (fPareidoliaTriggerbotLoaded) + { + PareidoliaTriggerbotDriverUnload(pDriverObject); + } + + if (fVivienneVmmLoaded) + { + HyperPlatformDriverUnload(pDriverObject); + } + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +static +VOID +DriverUnload( + PDRIVER_OBJECT pDriverObject +) +{ + UNICODE_STRING usSymbolicLinkName = {}; + + DBG_PRINT("Unloading %ls.", PAREIDOLIA_NT_DEVICE_NAME_U); + + // + // Unload the driver subsystems in reverse-load order. + // + MouClassInputInjectionDriverUnload(pDriverObject); + PareidoliaTriggerbotDriverUnload(pDriverObject); + HyperPlatformDriverUnload(pDriverObject); + + DBG_PRINT("%ls unloaded.", PAREIDOLIA_NT_DEVICE_NAME_U); +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +static +NTSTATUS +DispatchCreate( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + DBG_PRINT("Processing IRP_MJ_CREATE."); + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +EXTERN_C +static +NTSTATUS +DispatchClose( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; + UNICODE_STRING VivienneVmmDeviceName = {}; + UNICODE_STRING PareidoliaTriggerbotDeviceName = {}; + UNICODE_STRING MouClassInputInjectionDeviceName = {}; + NTSTATUS ntstatus = STATUS_SUCCESS; + + DBG_PRINT("Processing IRP_MJ_CLOSE."); + + // + // Query the name of the device object to be closed. + // + ntstatus = ObuQueryNameString(pDeviceObject, &pObjectNameInfo); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("ObuQueryNameString failed: 0x%X (DeviceObject = %p)", + ntstatus, + pDeviceObject); + goto exit; + } + + DBG_PRINT("Closing device object. (Name = %wZ)", &pObjectNameInfo->Name); + + // + // Forward the close request to the subsystem whose device object name + // matches the name of the device object to be closed. + // + VivienneVmmDeviceName = RTL_CONSTANT_STRING( + NT_PATH_PREFIX_DEVICE CFG_DEVICE_NAME_VIVIENNE_VMM_U); + + PareidoliaTriggerbotDeviceName = RTL_CONSTANT_STRING( + NT_PATH_PREFIX_DEVICE CFG_DEVICE_NAME_PAREIDOLIA_TRIGGERBOT_U); + + MouClassInputInjectionDeviceName = RTL_CONSTANT_STRING( + NT_PATH_PREFIX_DEVICE CFG_DEVICE_NAME_MOUCLASS_INPUT_INJECTION_U); + + if (RtlEqualUnicodeString( + &pObjectNameInfo->Name, + &VivienneVmmDeviceName, + TRUE)) + { + VERIFY(VivienneVmmDispatchClose(pDeviceObject, pIrp)); + } + else if (RtlEqualUnicodeString( + &pObjectNameInfo->Name, + &PareidoliaTriggerbotDeviceName, + TRUE)) + { + VERIFY(PareidoliaTriggerbotDispatchClose(pDeviceObject, pIrp)); + } + else if (RtlEqualUnicodeString( + &pObjectNameInfo->Name, + &MouClassInputInjectionDeviceName, + TRUE)) + { + VERIFY(MouClassInputInjectionDispatchClose(pDeviceObject, pIrp)); + } + else + { + ERR_PRINT( + "Unexpected device object name. (DeviceObject = %p, Name = %wZ)", + pDeviceObject, + &pObjectNameInfo->Name); + + ntstatus = STATUS_INTERNAL_ERROR; + + // + // NOTE We only complete the irp in the unhandled case because each + // subsystem is responsible for completing every irp they receive. + // + pIrp->IoStatus.Status = ntstatus; + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + DEBUG_BREAK; + + goto exit; + } + +exit: + if (pObjectNameInfo) + { + ExFreePool(pObjectNameInfo); + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +static +NTSTATUS +DispatchDeviceControl( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + PIO_STACK_LOCATION pIrpStack = NULL; + ULONG IoControlCode = 0; + ULONG DeviceType = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + IoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; + + DeviceType = DEVICE_TYPE_FROM_CTL_CODE(IoControlCode); + + // + // Forward the request based on the device type of the ioctl code. + // + switch (DeviceType) + { + case FILE_DEVICE_VVMM: + ntstatus = VivienneVmmDispatchDeviceControl(pDeviceObject, pIrp); + if (!NT_SUCCESS(ntstatus)) + { + goto exit; + } + + break; + + case FILE_DEVICE_PAREIDOLIA_TRIGGERBOT: + ntstatus = PareidoliaTriggerbotDispatchDeviceControl( + pDeviceObject, + pIrp); + if (!NT_SUCCESS(ntstatus)) + { + goto exit; + } + + break; + + case FILE_DEVICE_MOUCLASS_INPUT_INJECTION: + ntstatus = MouClassInputInjectionDispatchDeviceControl( + pDeviceObject, + pIrp); + if (!NT_SUCCESS(ntstatus)) + { + goto exit; + } + + break; + + default: + ERR_PRINT( + "Unhandled device type." + " (DeviceType = %u, IoControlCode = 0x%X)", + DeviceType, + IoControlCode); + + ntstatus = STATUS_UNSUCCESSFUL; + + // + // NOTE We only complete the irp in the unhandled case because each + // subsystem is responsible for completing every irp they receive. + // + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = ntstatus; + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + goto exit; + } + +exit: + return ntstatus; +} + +``` + +`PareidoliaTriggerbot/log.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include "../VivienneVMM/VivienneVMM/log.h" + +``` + +`PareidoliaTriggerbot/nt.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Constants +//============================================================================= +#define LDRP_SIGNED_KERNEL_IMAGE 0x00000020 + +//============================================================================= +// Names +//============================================================================= +#define NT_PATH_PREFIX_DEVICE L"\\Device\\" + +//============================================================================= +// Types +//============================================================================= +#pragma warning(push) +#pragma warning(disable : 4201) // Nonstandard extension: nameless struct/union +typedef struct _LDR_DATA_TABLE_ENTRY { + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + USHORT LoadCount; + USHORT TlsIndex; + union { + LIST_ENTRY HashLinks; + struct { + PVOID SectionPointer; + ULONG CheckSum; + }; + }; + union { + ULONG TimeDateStamp; + PVOID LoadedImports; + }; + PVOID EntryPointActivationContext; + PVOID PatchInformation; + LIST_ENTRY ForwarderLinks; + LIST_ENTRY ServiceTagLinks; + LIST_ENTRY StaticLinks; + PVOID ContextInformation; + ULONG_PTR OriginalBase; + LARGE_INTEGER LoadTime; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; +#pragma warning(pop) + +//============================================================================= +// Process Interface +//============================================================================= +EXTERN_C +PVOID +NTAPI +PsGetProcessSectionBaseAddress( + _In_ PEPROCESS Process +); + +EXTERN_C +NTSTATUS +NTAPI +PsReferenceProcessFilePointer( + _In_ PEPROCESS Process, + _Out_ PVOID* FilePointer +); + +EXTERN_C +NTSTATUS +NTAPI +PsAcquireProcessExitSynchronization( + _In_ PEPROCESS Process +); + +EXTERN_C +VOID +NTAPI +PsReleaseProcessExitSynchronization( + _In_ PEPROCESS Process +); + +``` + +`PareidoliaTriggerbot/object_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "object_util.h" + +#include "log.h" +#include "nt.h" + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +ObuQueryNameString( + PVOID pObject, + POBJECT_NAME_INFORMATION* ppObjectNameInfo +) +/*++ + +Routine Description: + + This function is a convenience wrapper for ObQueryNameString. + +Parameters: + + pObject - Pointer to the object to be queried. + + ppObjectNameInfo - Returns a pointer to an allocated buffer for the object + name information for the specified object. If the object is unnamed + then the unicode string object in the returned buffer is zeroed. The + buffer is allocated from the NonPaged pool. + +Remarks: + + If successful, the caller must free the returned object name information + buffer by calling ExFreePool. + +--*/ +{ + POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; + ULONG cbReturnLength = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + // + // Zero out parameters. + // + *ppObjectNameInfo = NULL; + + ntstatus = ObQueryNameString(pObject, NULL, 0, &cbReturnLength); + if (STATUS_INFO_LENGTH_MISMATCH != ntstatus) + { + ERR_PRINT("ObQueryNameString failed: 0x%X (Unexpected)", ntstatus); + ntstatus = STATUS_UNSUCCESSFUL; + goto exit; + } + + pObjectNameInfo = (POBJECT_NAME_INFORMATION)ExAllocatePool( + NonPagedPool, + cbReturnLength); + if (!pObjectNameInfo) + { + ntstatus = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + // + RtlSecureZeroMemory(pObjectNameInfo, cbReturnLength); + + ntstatus = ObQueryNameString( + pObject, + pObjectNameInfo, + cbReturnLength, + &cbReturnLength); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("ObQueryNameString failed: 0x%X", ntstatus); + goto exit; + } + + // + // Set out parameters. + // + *ppObjectNameInfo = pObjectNameInfo; + +exit: + if (!NT_SUCCESS(ntstatus)) + { + if (pObjectNameInfo) + { + ExFreePool(pObjectNameInfo); + } + } + + return ntstatus; +} + +``` + +`PareidoliaTriggerbot/object_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +ObuQueryNameString( + _In_ PVOID pObject, + _Outptr_result_nullonfailure_ POBJECT_NAME_INFORMATION* ppObjectNameInfo +); + +``` + +`PareidoliaTriggerbot/pareidolia_triggerbot.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "pareidolia_triggerbot.h" + +#include "debug.h" +#include "log.h" +#include "process_access_manager.h" +#include "process_util.h" + +#include "../Common/config.h" +#include "../Common/ioctl.h" + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PareidoliaTriggerbotDriverEntry( + PDRIVER_OBJECT pDriverObject, + PUNICODE_STRING pRegistryPath +) +{ + PDEVICE_OBJECT pDeviceObject = NULL; + UNICODE_STRING usDeviceName = {}; + UNICODE_STRING usSymbolicLinkName = {}; + BOOLEAN fSymbolicLinkCreated = FALSE; +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + BOOLEAN fPamLoaded = FALSE; +#endif + NTSTATUS ntstatus = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pRegistryPath); + + DBG_PRINT("Loading %ls. (Subsystem)", PAREIDOLIA_NT_DEVICE_NAME_U); + + usDeviceName = RTL_CONSTANT_STRING(PAREIDOLIA_NT_DEVICE_NAME_U); + + ntstatus = IoCreateDevice( + pDriverObject, + 0, + &usDeviceName, + FILE_DEVICE_PAREIDOLIA_TRIGGERBOT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &pDeviceObject); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("IoCreateDevice failed: 0x%X", ntstatus); + goto exit; + } + // + pDriverObject->MajorFunction[IRP_MJ_CREATE] = + PareidoliaTriggerbotDispatchCreate; + pDriverObject->MajorFunction[IRP_MJ_CLOSE] = + PareidoliaTriggerbotDispatchClose; + pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + PareidoliaTriggerbotDispatchDeviceControl; + pDriverObject->DriverUnload = PareidoliaTriggerbotDriverUnload; + + // + // Create a symbolic link for the user mode client. + // + usSymbolicLinkName = RTL_CONSTANT_STRING(PAREIDOLIA_SYMBOLIC_LINK_NAME_U); + + ntstatus = IoCreateSymbolicLink(&usSymbolicLinkName, &usDeviceName); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("IoCreateSymbolicLink failed: 0x%X", ntstatus); + goto exit; + } + // + fSymbolicLinkCreated = TRUE; + + // + // Load the driver modules. + // +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + ntstatus = PamDriverEntry(); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PamDriverEntry failed: 0x%X", ntstatus); + goto exit; + } + // + fPamLoaded = TRUE; +#endif + + DBG_PRINT("%ls loaded. (Subsystem)", PAREIDOLIA_NT_DEVICE_NAME_U); + +exit: + if (!NT_SUCCESS(ntstatus)) + { +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + if (fPamLoaded) + { + PamDriverUnload(); + } +#endif + + if (fSymbolicLinkCreated) + { + VERIFY(IoDeleteSymbolicLink(&usSymbolicLinkName)); + } + + if (pDeviceObject) + { + IoDeleteDevice(pDeviceObject); + } + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +VOID +PareidoliaTriggerbotDriverUnload( + PDRIVER_OBJECT pDriverObject +) +{ + UNICODE_STRING usSymbolicLinkName = {}; + + DBG_PRINT("Unloading %ls. (Subsystem)", PAREIDOLIA_NT_DEVICE_NAME_U); + + // + // Unload the driver modules. + // +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + PamDriverUnload(); +#endif + + // + // Release driver resources. + // + usSymbolicLinkName = RTL_CONSTANT_STRING(PAREIDOLIA_SYMBOLIC_LINK_NAME_U); + + VERIFY(IoDeleteSymbolicLink(&usSymbolicLinkName)); + + if (pDriverObject->DeviceObject) + { + IoDeleteDevice(pDriverObject->DeviceObject); + } + + DBG_PRINT("%ls unloaded. (Subsystem)", PAREIDOLIA_NT_DEVICE_NAME_U); +} + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PareidoliaTriggerbotDispatchCreate( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + DBG_PRINT("Processing IRP_MJ_CREATE."); + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PareidoliaTriggerbotDispatchClose( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + + DBG_PRINT("Processing IRP_MJ_CLOSE."); + + // + // Remove access protection from all protected processes. + // + PamReset(); + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PareidoliaTriggerbotDispatchDeviceControl( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp +) +{ + PIO_STACK_LOCATION pIrpStack = NULL; + PVOID pSystemBuffer = NULL; + ULONG cbInput = 0; + ULONG cbOutput = 0; +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + PRESTRICT_PROCESS_ACCESS_REQUEST pRestrictProcessAccessRequest = NULL; + PDERESTRICT_PROCESS_ACCESS_REQUEST pDerestrictProcessAccessRequest = NULL; +#endif + PGET_PROCESS_IMAGE_BASE_REQUEST pGetProcessImageBaseRequest = NULL; + PGET_PROCESS_IMAGE_BASE_REPLY pGetProcessImageBaseReply = NULL; + PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST + pGetProcessImageFilePathSizeRequest = NULL; + PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY + pGetProcessImageFilePathSizeReply = NULL; + PGET_PROCESS_IMAGE_FILE_PATH_REQUEST + pGetProcessImageFilePathRequest = NULL; + PGET_PROCESS_IMAGE_FILE_PATH_REPLY pGetProcessImageFilePathReply = NULL; + ULONG cbRequired = 0; + PREAD_VIRTUAL_MEMORY_REQUEST pReadVirtualMemoryRequest = NULL; + ULONG cbRead = 0; + ULONG_PTR Information = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + pSystemBuffer = pIrp->AssociatedIrp.SystemBuffer; + cbInput = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; + cbOutput = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + UNREFERENCED_PARAMETER(pDeviceObject); + + switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) + { +#if defined(CFG_ENABLE_PROCESS_ACCESS_PROTECTION) + case IOCTL_RESTRICT_PROCESS_ACCESS: + DBG_PRINT("Processing IOCTL_RESTRICT_PROCESS_ACCESS."); + + pRestrictProcessAccessRequest = + (PRESTRICT_PROCESS_ACCESS_REQUEST)pSystemBuffer; + if (!pRestrictProcessAccessRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pRestrictProcessAccessRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PamRestrictAccess( + (HANDLE)pRestrictProcessAccessRequest->ProcessId); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PamRestrictAccess failed: 0x%X", ntstatus); + goto exit; + } + + break; + + case IOCTL_DERESTRICT_PROCESS_ACCESS: + DBG_PRINT("Processing IOCTL_DERESTRICT_PROCESS_ACCESS."); + + pDerestrictProcessAccessRequest = + (PDERESTRICT_PROCESS_ACCESS_REQUEST)pSystemBuffer; + if (!pDerestrictProcessAccessRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pDerestrictProcessAccessRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PamDerestrictAccess( + (HANDLE)pDerestrictProcessAccessRequest->ProcessId); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PamDerestrictAccess failed: 0x%X", ntstatus); + goto exit; + } + + break; +#endif + + case IOCTL_GET_PROCESS_IMAGE_BASE: + DBG_PRINT("Processing IOCTL_GET_PROCESS_IMAGE_BASE."); + + pGetProcessImageBaseRequest = + (PGET_PROCESS_IMAGE_BASE_REQUEST)pSystemBuffer; + if (!pGetProcessImageBaseRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageBaseRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + pGetProcessImageBaseReply = + (PGET_PROCESS_IMAGE_BASE_REPLY)pSystemBuffer; + if (!pGetProcessImageBaseReply) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageBaseReply) != cbOutput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PsuGetImageBase( + (HANDLE)pGetProcessImageBaseRequest->ProcessId, + &pGetProcessImageBaseReply->ImageBase); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsuGetImageBase failed: 0x%X", ntstatus); + goto exit; + } + + Information = sizeof(*pGetProcessImageBaseReply); + + break; + + case IOCTL_GET_PROCESS_IMAGE_FILE_PATH_SIZE: + DBG_PRINT("Processing IOCTL_GET_PROCESS_IMAGE_FILE_PATH_SIZE."); + + pGetProcessImageFilePathSizeRequest = + (PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REQUEST)pSystemBuffer; + if (!pGetProcessImageFilePathSizeRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageFilePathSizeRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + pGetProcessImageFilePathSizeReply = + (PGET_PROCESS_IMAGE_FILE_PATH_SIZE_REPLY)pSystemBuffer; + if (!pGetProcessImageFilePathSizeReply) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageFilePathSizeReply) != cbOutput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PsuGetImageFilePath( + (HANDLE)pGetProcessImageFilePathSizeRequest->ProcessId, + NULL, + 0, + &pGetProcessImageFilePathSizeReply->Size); + if (STATUS_INFO_LENGTH_MISMATCH != ntstatus) + { + ERR_PRINT("PsuGetImageFilePath failed: 0x%X (Unexpected)", + ntstatus); + goto exit; + } + + Information = sizeof(*pGetProcessImageFilePathSizeReply); + + ntstatus = STATUS_SUCCESS; + + break; + + case IOCTL_GET_PROCESS_IMAGE_FILE_PATH: + DBG_PRINT("Processing IOCTL_GET_PROCESS_IMAGE_FILE_PATH."); + + pGetProcessImageFilePathRequest = + (PGET_PROCESS_IMAGE_FILE_PATH_REQUEST)pSystemBuffer; + if (!pGetProcessImageFilePathRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageFilePathRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + pGetProcessImageFilePathReply = + (PGET_PROCESS_IMAGE_FILE_PATH_REPLY)pSystemBuffer; + if (!pGetProcessImageFilePathReply) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pGetProcessImageFilePathReply) > cbOutput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PsuGetImageFilePath( + (HANDLE)pGetProcessImageFilePathRequest->ProcessId, + (PWCHAR)&pGetProcessImageFilePathReply->NtFilePath, + cbOutput, + &cbRequired); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsuGetImageFilePath failed: 0x%X", ntstatus); + goto exit; + } + + Information = cbRequired; + + break; + + case IOCTL_READ_VIRTUAL_MEMORY: + DBG_PRINT("Processing IOCTL_READ_VIRTUAL_MEMORY."); + + pReadVirtualMemoryRequest = + (PREAD_VIRTUAL_MEMORY_REQUEST)pSystemBuffer; + if (!pReadVirtualMemoryRequest) + { + ntstatus = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (sizeof(*pReadVirtualMemoryRequest) != cbInput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + if (pReadVirtualMemoryRequest->Size > cbOutput) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + ntstatus = PsuReadVirtualMemory( + (HANDLE)pReadVirtualMemoryRequest->ProcessId, + pReadVirtualMemoryRequest->Address, + pSystemBuffer, + pReadVirtualMemoryRequest->Size, + &cbRead); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsuReadVirtualMemory failed: 0x%X", ntstatus); + goto exit; + } + + Information = cbRead; + + break; + + default: + ERR_PRINT( + "Unhandled IOCTL." + " (MajorFunction = %hhu, MinorFunction = %hhu)", + pIrpStack->MajorFunction, + pIrpStack->MinorFunction); + ntstatus = STATUS_UNSUCCESSFUL; + goto exit; + } + +exit: + pIrp->IoStatus.Information = Information; + pIrp->IoStatus.Status = ntstatus; + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return ntstatus; +} + +``` + +`PareidoliaTriggerbot/pareidolia_triggerbot.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Meta Interface +//============================================================================= +EXTERN_C +DRIVER_INITIALIZE +PareidoliaTriggerbotDriverEntry; + +EXTERN_C +DRIVER_UNLOAD +PareidoliaTriggerbotDriverUnload; + +//============================================================================= +// Public Interface +//============================================================================= +_Dispatch_type_(IRP_MJ_CREATE) +EXTERN_C +DRIVER_DISPATCH +PareidoliaTriggerbotDispatchCreate; + +_Dispatch_type_(IRP_MJ_CLOSE) +EXTERN_C +DRIVER_DISPATCH +PareidoliaTriggerbotDispatchClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +EXTERN_C +DRIVER_DISPATCH +PareidoliaTriggerbotDispatchDeviceControl; + +``` + +`PareidoliaTriggerbot/process_access_manager.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +Module: + + process access manager + +Description: + + This module implements an interface for restricting user mode handle access + to target processes. + +Remarks: + + This module registers a kernel object callback to prevent user mode handle + access to 'restricted' (protected) processes. The caller must be executing + in a signed image. See SysSetSignedKernelImageFlag. + + WARNING Protecting a process may cause undesirable behavior. e.g., If the + caller restricts access to a process that is executing in a command prompt + then the user will not be able to close the command prompt via the red 'X' + button. See CFG_ENABLE_PROCESS_ACCESS_PROTECTION. + + WARNING This module does not prevent all types of handle access. TODO List + access types. + +--*/ + +#include "process_access_manager.h" + +#include "debug.h" +#include "log.h" + + +//============================================================================= +// Constants +//============================================================================= +#define MODULE_TITLE "Process Access Manager" + +#define OBJECT_CALLBACK_ALTITUDE_U L"maP " + + +//============================================================================= +// Private Enumerations +//============================================================================= +typedef enum _OBJECT_OPERATION_TYPE { + ObjectOperationProcess = 0, + ObjectOperationThread = 1, + MaxObjectOperationType = 2 +} OBJECT_OPERATION_TYPE, *POBJECT_OPERATION_TYPE; + +C_ASSERT(MAXUSHORT >= MaxObjectOperationType); + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _PROTECTED_PROCESS_ENTRY { + LIST_ENTRY ListEntry; + HANDLE ProcessId; +} PROTECTED_PROCESS_ENTRY, *PPROTECTED_PROCESS_ENTRY; + +typedef struct _PROCESS_ACCESS_MANAGER { + POB_CALLBACK_REGISTRATION ObjectCallbackRegistration; + PVOID ObjectCallbackRegistrationHandle; + POINTER_ALIGNMENT ERESOURCE Resource; + _Guarded_by_(Resource) LIST_ENTRY ProtectedProcessListHead; +} PROCESS_ACCESS_MANAGER, *PPROCESS_ACCESS_MANAGER; + + +//============================================================================= +// Module Globals +//============================================================================= +static PROCESS_ACCESS_MANAGER g_ProcessAccessManager = {}; + + +//============================================================================= +// Private Prototypes +//============================================================================= +EXTERN_C +static +VOID +PampProcessNotifyRoutine( + _In_ HANDLE ParentId, + _In_ HANDLE ProcessId, + _In_ BOOLEAN fCreate +); + +_Check_return_ +EXTERN_C +static +OB_PREOP_CALLBACK_STATUS +PampPreOperationCallback( + _In_ PVOID pRegistrationContext, + _Inout_ POB_PRE_OPERATION_INFORMATION pOperationInformation +); + +EXTERN_C +static +VOID +PampPostOperationCallback( + _In_ PVOID pRegistrationContext, + _In_ POB_POST_OPERATION_INFORMATION pOperationInformation +); + +_Check_return_ +EXTERN_C +static +NTSTATUS +PampCreateObjectCallbackRegistration( + _In_ PVOID pContext, + _Outptr_result_nullonfailure_ POB_CALLBACK_REGISTRATION* ppRegistration +); + +_Requires_exclusive_lock_held_(g_ProcessAccessManager.Resource) +_Check_return_ +EXTERN_C +static +PPROTECTED_PROCESS_ENTRY +PampGetProtectedProcess( + _In_ HANDLE ProcessId +); + +_Requires_lock_not_held_(g_ProcessAccessManager.Resource) +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +static +VOID +PampFreeProtectedProcessList(); + + +//============================================================================= +// Meta Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PamDriverEntry() +{ + BOOLEAN fResourceInitialized = FALSE; + BOOLEAN fProcessNotifyCallbackRegistered = FALSE; + POB_CALLBACK_REGISTRATION pObjectCallbackRegistration = NULL; + PVOID ObjectCallbackRegistrationHandle = NULL; + BOOLEAN fObjectCallbacksRegistered = FALSE; + NTSTATUS ntstatus = STATUS_SUCCESS; + + DBG_PRINT("Loading %s.", MODULE_TITLE); + + // + // We must initialize the resource before registering callbacks because our + // callbacks use the resource. + // + ntstatus = ExInitializeResourceLite(&g_ProcessAccessManager.Resource); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("ExInitializeResourceLite failed: 0x%X", ntstatus); + goto exit; + } + // + fResourceInitialized = TRUE; + + ntstatus = + PsSetCreateProcessNotifyRoutine(PampProcessNotifyRoutine, FALSE); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsSetCreateProcessNotifyRoutine failed: 0x%X", ntstatus); + goto exit; + } + // + fProcessNotifyCallbackRegistered = TRUE; + + ntstatus = PampCreateObjectCallbackRegistration( + &g_ProcessAccessManager, + &pObjectCallbackRegistration); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PampCreateObjectCallbackRegistration failed: 0x%X", + ntstatus); + goto exit; + } + + ntstatus = ObRegisterCallbacks( + pObjectCallbackRegistration, + &ObjectCallbackRegistrationHandle); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("ObRegisterCallbacks failed: 0x%X", ntstatus); + goto exit; + } + // + fObjectCallbacksRegistered = TRUE; + + // + // Initialize the global context. + // + g_ProcessAccessManager.ObjectCallbackRegistration = + pObjectCallbackRegistration; + g_ProcessAccessManager.ObjectCallbackRegistrationHandle = + ObjectCallbackRegistrationHandle; + InitializeListHead(&g_ProcessAccessManager.ProtectedProcessListHead); + + DBG_PRINT("%s loaded.", MODULE_TITLE); + +exit: + if (!NT_SUCCESS(ntstatus)) + { + if (fObjectCallbacksRegistered) + { + ObUnRegisterCallbacks(ObjectCallbackRegistrationHandle); + } + + if (pObjectCallbackRegistration) + { + ExFreePool(pObjectCallbackRegistration); + } + + if (fProcessNotifyCallbackRegistered) + { + VERIFY(PsSetCreateProcessNotifyRoutine( + PampProcessNotifyRoutine, + TRUE)); + } + + if (fResourceInitialized) + { + VERIFY(ExDeleteResourceLite(&g_ProcessAccessManager.Resource)); + } + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +VOID +PamDriverUnload() +{ + DBG_PRINT("Unloading %s.", MODULE_TITLE); + + PampFreeProtectedProcessList(); + + ObUnRegisterCallbacks( + g_ProcessAccessManager.ObjectCallbackRegistrationHandle); + + ExFreePool(g_ProcessAccessManager.ObjectCallbackRegistration); + + VERIFY(PsSetCreateProcessNotifyRoutine(PampProcessNotifyRoutine, TRUE)); + + VERIFY(ExDeleteResourceLite(&g_ProcessAccessManager.Resource)); + + DBG_PRINT("%s unloaded.", MODULE_TITLE); +} + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PamRestrictAccess( + HANDLE ProcessId +) +/*++ + +Description: + + Register the specified process as a protected process so that user mode + code cannot create or duplicate a handle to the specified process. + +Parameters: + + ProcessId - The process to be registered. + +Remarks: + + WARNING This module does not prevent all types of handle access. TODO List + access types. + +--*/ +{ + PPROTECTED_PROCESS_ENTRY pProtectedProcessEntry = NULL; + NTSTATUS ntstatus = STATUS_SUCCESS; + + ExEnterCriticalRegionAndAcquireResourceExclusive( + &g_ProcessAccessManager.Resource); + + // + // Fail if the specified process is already registered. + // + if (PampGetProtectedProcess(ProcessId)) + { + ntstatus = STATUS_ALREADY_REGISTERED; + goto exit; + } + + // + // Allocate and initialize a new protected process entry. + // + pProtectedProcessEntry = (PPROTECTED_PROCESS_ENTRY)ExAllocatePool( + PagedPool, + sizeof(*pProtectedProcessEntry)); + if (!pProtectedProcessEntry) + { + ntstatus = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + pProtectedProcessEntry->ProcessId = ProcessId; + + // + // Insert the new entry into the protected process list. + // + InsertTailList( + &g_ProcessAccessManager.ProtectedProcessListHead, + &pProtectedProcessEntry->ListEntry); + + INF_PRINT("Restricted process access. (ProcessId = %Iu)", + pProtectedProcessEntry->ProcessId); + +exit: + if (!NT_SUCCESS(ntstatus)) + { + if (pProtectedProcessEntry) + { + ExFreePool(pProtectedProcessEntry); + } + } + + ExReleaseResourceAndLeaveCriticalRegion(&g_ProcessAccessManager.Resource); + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PamDerestrictAccess( + HANDLE ProcessId +) +/*++ + +Description: + + Unregister a protected process. + +Parameters: + + ProcessId - The process to be unregistered. + +--*/ +{ + PPROTECTED_PROCESS_ENTRY pProtectedProcessEntry = NULL; + NTSTATUS ntstatus = STATUS_SUCCESS; + + ExEnterCriticalRegionAndAcquireResourceExclusive( + &g_ProcessAccessManager.Resource); + + // + // Fail if the specified process is not registered. + // + pProtectedProcessEntry = PampGetProtectedProcess(ProcessId); + if (!pProtectedProcessEntry) + { + ntstatus = STATUS_NOT_FOUND; + goto exit; + } + + // + // Remove the entry from the protected process list. + // + RemoveEntryList(&pProtectedProcessEntry->ListEntry); + + INF_PRINT("Derestricted process access. (ProcessId = %Iu)", + pProtectedProcessEntry->ProcessId); + + ExFreePool(pProtectedProcessEntry); + +exit: + ExReleaseResourceAndLeaveCriticalRegion(&g_ProcessAccessManager.Resource); + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +VOID +PamReset() +/*++ + +Description: + + Unregister all protected processes. + +--*/ +{ + INF_PRINT("Resetting %s.", MODULE_TITLE); + + PampFreeProtectedProcessList(); +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +static +VOID +PampProcessNotifyRoutine( + HANDLE ParentId, + HANDLE ProcessId, + BOOLEAN fCreate +) +{ + BOOLEAN fResourceAcquired = FALSE; + PPROTECTED_PROCESS_ENTRY pProtectedProcessEntry = NULL; + + UNREFERENCED_PARAMETER(ParentId); + + // + // Ignore process creation. + // + if (fCreate) + { + goto exit; + } + + ExEnterCriticalRegionAndAcquireResourceExclusive( + &g_ProcessAccessManager.Resource); + fResourceAcquired = TRUE; + + // + // If a registered process is terminating then unregister it. + // + pProtectedProcessEntry = PampGetProtectedProcess(ProcessId); + if (!pProtectedProcessEntry) + { + goto exit; + } + + RemoveEntryList(&pProtectedProcessEntry->ListEntry); + + INF_PRINT("Derestricted process access. (ProcessId = %Iu)", + pProtectedProcessEntry->ProcessId); + + ExFreePool(pProtectedProcessEntry); + +exit: + if (fResourceAcquired) + { + ExReleaseResourceAndLeaveCriticalRegion( + &g_ProcessAccessManager.Resource); + } +} + + +_Use_decl_annotations_ +EXTERN_C +static +OB_PREOP_CALLBACK_STATUS +PampPreOperationCallback( + PVOID pRegistrationContext, + POB_PRE_OPERATION_INFORMATION pOperationInformation +) +{ + PPROCESS_ACCESS_MANAGER pProcessAccessManager = NULL; + BOOLEAN fResourceAcquired = FALSE; + HANDLE ProcessId = NULL; + POB_PRE_CREATE_HANDLE_INFORMATION pCreateHandleInfo = NULL; + POB_PRE_DUPLICATE_HANDLE_INFORMATION pDuplicateHandleInfo = NULL; + PACCESS_MASK pDesiredAccess = NULL; + ACCESS_MASK OriginalDesiredAccess = NULL; + OB_PREOP_CALLBACK_STATUS status = OB_PREOP_SUCCESS; + + // + // Always allow the requested access for kernel handles. + // + if (pOperationInformation->KernelHandle) + { + goto exit; + } + + // + // Determine the effective process id for the request. + // + if (pOperationInformation->ObjectType == (*PsProcessType)) + { + ProcessId = PsGetProcessId((PEPROCESS)pOperationInformation->Object); + } + else if (pOperationInformation->ObjectType == (*PsThreadType)) + { + ProcessId = + PsGetThreadProcessId((PETHREAD)pOperationInformation->Object); + } + else + { + ERR_PRINT("Unexpected object type. (ObjectType = %p)", + pOperationInformation->ObjectType); + DEBUG_BREAK; + goto exit; + } + + pProcessAccessManager = (PPROCESS_ACCESS_MANAGER)pRegistrationContext; + + ExEnterCriticalRegionAndAcquireResourceExclusive( + &pProcessAccessManager->Resource); + fResourceAcquired = TRUE; + + // + // Allow the requested access if the process is not protected. + // + if (!PampGetProtectedProcess(ProcessId)) + { + goto exit; + } + + switch (pOperationInformation->Operation) + { + case OB_OPERATION_HANDLE_CREATE: + pCreateHandleInfo = + &pOperationInformation->Parameters->CreateHandleInformation; + pDesiredAccess = &pCreateHandleInfo->DesiredAccess; + OriginalDesiredAccess = pCreateHandleInfo->OriginalDesiredAccess; + break; + + case OB_OPERATION_HANDLE_DUPLICATE: + pDuplicateHandleInfo = + &pOperationInformation->Parameters->DuplicateHandleInformation; + pDesiredAccess = &pDuplicateHandleInfo->DesiredAccess; + OriginalDesiredAccess = + pDuplicateHandleInfo->OriginalDesiredAccess; + break; + + default: + ERR_PRINT("Unexpected operation. (Operation = %p)", + pOperationInformation->Operation); + DEBUG_BREAK; + goto exit; + } + + // + // Prevent the requested access. + // + *pDesiredAccess = 0; + +exit: + if (fResourceAcquired) + { + ExReleaseResourceAndLeaveCriticalRegion( + &pProcessAccessManager->Resource); + } + + return status; +} + + +_Use_decl_annotations_ +EXTERN_C +static +VOID +PampPostOperationCallback( + PVOID pRegistrationContext, + POB_POST_OPERATION_INFORMATION pOperationInformation +) +{ + // + // TODO Verify that we prevented access to protected processes. + // + UNREFERENCED_PARAMETER(pRegistrationContext); + UNREFERENCED_PARAMETER(pOperationInformation); +} + + +_Use_decl_annotations_ +EXTERN_C +static +NTSTATUS +PampCreateObjectCallbackRegistration( + PVOID pContext, + POB_CALLBACK_REGISTRATION* ppRegistration +) +/*++ + +Description: + + Allocate and initialize an object callback registration object. + +Parameters: + + pContext - Pointer to the registration context. + + ppRegistration - Returns an initialized object callback registration + object. This object is allocated from the NonPaged pool. + +Remarks: + + If successful, the caller must free the returned registration object by + calling ExFreePool. + +--*/ +{ + USHORT nOperationEntries = 0; + SIZE_T cbCallbackRegistration = 0; + POB_CALLBACK_REGISTRATION pCallbackRegistration = NULL; + POB_OPERATION_REGISTRATION pOperationRegistration = NULL; + NTSTATUS ntstatus = STATUS_SUCCESS; + + // + // Zero out parameters. + // + RtlSecureZeroMemory(ppRegistration, sizeof(*ppRegistration)); + + DBG_PRINT("Creating object callback registration."); + + // + // NOTE The OB_CALLBACK_REGISTRATION object and its + // OB_OPERATION_REGISTRATION array must be contiguous. + // + nOperationEntries = MaxObjectOperationType; + + cbCallbackRegistration = + sizeof(*pCallbackRegistration) + + (sizeof(OB_OPERATION_REGISTRATION) * nOperationEntries); + + pCallbackRegistration = (POB_CALLBACK_REGISTRATION)ExAllocatePool( + NonPagedPool, + cbCallbackRegistration); + if (!pCallbackRegistration) + { + ntstatus = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + pOperationRegistration = (POB_OPERATION_REGISTRATION)( + (ULONG_PTR)pCallbackRegistration + sizeof(*pCallbackRegistration)); + + // + // Initialize the process object operation entry. + // + pOperationRegistration[ObjectOperationProcess].ObjectType = PsProcessType; + pOperationRegistration[ObjectOperationProcess].Operations = + OB_OPERATION_HANDLE_CREATE | + OB_OPERATION_HANDLE_DUPLICATE; + pOperationRegistration[ObjectOperationProcess].PreOperation = + PampPreOperationCallback; + pOperationRegistration[ObjectOperationProcess].PostOperation = + PampPostOperationCallback; + + // + // Initialize the thread object operation entry. + // + pOperationRegistration[ObjectOperationThread].ObjectType = PsThreadType; + pOperationRegistration[ObjectOperationThread].Operations = + OB_OPERATION_HANDLE_CREATE | + OB_OPERATION_HANDLE_DUPLICATE; + pOperationRegistration[ObjectOperationThread].PreOperation = + PampPreOperationCallback; + pOperationRegistration[ObjectOperationThread].PostOperation = + PampPostOperationCallback; + + // + // Initialize the callback registration object. + // + pCallbackRegistration->Version = OB_FLT_REGISTRATION_VERSION; + pCallbackRegistration->OperationRegistrationCount = nOperationEntries; + pCallbackRegistration->Altitude = + RTL_CONSTANT_STRING(OBJECT_CALLBACK_ALTITUDE_U); + pCallbackRegistration->RegistrationContext = pContext; + pCallbackRegistration->OperationRegistration = pOperationRegistration; + + // + // Set out parameters. + // + *ppRegistration = pCallbackRegistration; + +exit: + if (!NT_SUCCESS(ntstatus)) + { + if (pCallbackRegistration) + { + ExFreePool(pCallbackRegistration); + } + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +static +PPROTECTED_PROCESS_ENTRY +PampGetProtectedProcess( + HANDLE ProcessId +) +{ + PLIST_ENTRY pListEntry = NULL; + PPROTECTED_PROCESS_ENTRY pEntry = NULL; + PPROTECTED_PROCESS_ENTRY pProtectedProcessEntry = NULL; + + for (pListEntry = g_ProcessAccessManager.ProtectedProcessListHead.Flink; + pListEntry != &g_ProcessAccessManager.ProtectedProcessListHead; + pListEntry = pListEntry->Flink) + { + pEntry = + CONTAINING_RECORD(pListEntry, PROTECTED_PROCESS_ENTRY, ListEntry); + + if (pEntry->ProcessId == ProcessId) + { + pProtectedProcessEntry = pEntry; + break; + } + } + + return pProtectedProcessEntry; +} + + +_Use_decl_annotations_ +EXTERN_C +static +VOID +PampFreeProtectedProcessList() +{ + PLIST_ENTRY pListHead = NULL; + PLIST_ENTRY pListEntry = NULL; + PPROTECTED_PROCESS_ENTRY pEntry = NULL; + + pListHead = &g_ProcessAccessManager.ProtectedProcessListHead; + + ExEnterCriticalRegionAndAcquireResourceExclusive( + &g_ProcessAccessManager.Resource); + + while (!IsListEmpty(pListHead)) + { + pListEntry = RemoveHeadList(pListHead); + pEntry = + CONTAINING_RECORD(pListEntry, PROTECTED_PROCESS_ENTRY, ListEntry); + + INF_PRINT("Derestricted process access. (ProcessId = %Iu)", + pEntry->ProcessId); + + ExFreePool(pEntry); + } + + ExReleaseResourceAndLeaveCriticalRegion(&g_ProcessAccessManager.Resource); +} + +``` + +`PareidoliaTriggerbot/process_access_manager.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +//============================================================================= +// Meta Interface +//============================================================================= +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +EXTERN_C +NTSTATUS +PamDriverEntry(); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +EXTERN_C +VOID +PamDriverUnload(); + +//============================================================================= +// Public Interface +//============================================================================= +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +PamRestrictAccess( + _In_ HANDLE ProcessId +); + +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +PamDerestrictAccess( + _In_ HANDLE ProcessId +); + +_IRQL_requires_max_(APC_LEVEL) +EXTERN_C +VOID +PamReset(); + +``` + +`PareidoliaTriggerbot/process_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "process_util.h" + +#include "log.h" +#include "nt.h" +#include "object_util.h" + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PsuGetImageBase( + HANDLE ProcessId, + PULONG_PTR pImageBase +) +{ + PEPROCESS pProcess = NULL; + BOOLEAN fHasProcessReference = FALSE; + PVOID pSectionBase = NULL; + NTSTATUS ntstatus = STATUS_SUCCESS; + + // + // Zero out parameters. + // + *pImageBase = 0; + + ntstatus = PsLookupProcessByProcessId(ProcessId, &pProcess); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsLookupProcessByProcessId failed: 0x%X", ntstatus); + goto exit; + } + // + fHasProcessReference = TRUE; + + pSectionBase = PsGetProcessSectionBaseAddress(pProcess); + if (!pSectionBase) + { + ERR_PRINT("Unexpected process section base. (ProcessId = %Iu)", + ProcessId); + ntstatus = STATUS_UNSUCCESSFUL; + goto exit; + } + + // + // Set out parameters. + // + *pImageBase = (ULONG_PTR)pSectionBase; + +exit: + if (fHasProcessReference) + { + ObDereferenceObject(pProcess); + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PsuGetImageFilePath( + HANDLE ProcessId, + PWCHAR pwzImageFilePath, + ULONG cbImageFilePath, + PULONG pcbRequired +) +/*++ + +Description: + + Get the NT file path for the image backing the target process. + +Parameters: + + ProcessId - The process id of the target process. + + pwzImageFilePath - Optionally returns the NT file path for the image + backing the target process. + + cbImageFilePath - The size of the provided output buffer. + + pcbRequired - Optionally returns the size of NT file path. + +Return Value: + + STATUS_INFO_LENGTH_MISMATCH - The provided buffer is not large enough to + store the NT file path. + +--*/ +{ + PEPROCESS pProcess = NULL; + BOOLEAN fHasProcessReference = FALSE; + PFILE_OBJECT pFileObject = NULL; + BOOLEAN fHasFileObjectReference = FALSE; + POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; + ULONG cbRequired = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + // + // Zero out parameters. + // + if (ARGUMENT_PRESENT(pwzImageFilePath) && cbImageFilePath) + { + RtlSecureZeroMemory(pwzImageFilePath, cbImageFilePath); + } + + if (ARGUMENT_PRESENT(pcbRequired)) + { + *pcbRequired = 0; + } + + ntstatus = PsLookupProcessByProcessId(ProcessId, &pProcess); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsLookupProcessByProcessId failed: 0x%X", ntstatus); + goto exit; + } + // + fHasProcessReference = TRUE; + + // + // NOTE This routine acquires process exit synchronization. + // + ntstatus = PsReferenceProcessFilePointer(pProcess, (PVOID*)&pFileObject); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsReferenceProcessFilePointer failed: 0x%X", ntstatus); + goto exit; + } + // + fHasFileObjectReference = TRUE; + + ntstatus = ObuQueryNameString(pFileObject, &pObjectNameInfo); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("ObuQueryNameString failed: 0x%X", ntstatus); + goto exit; + } + + // + // NOTE The 'MaximumLength' field includes the null-terminator. + // + cbRequired = pObjectNameInfo->Name.MaximumLength; + + // + // Set the required size parameter. + // + if (ARGUMENT_PRESENT(pcbRequired)) + { + *pcbRequired = cbRequired; + } + + // + // Fail if the file object is unnamed. + // + if (!pObjectNameInfo->Name.Length || + !pObjectNameInfo->Name.MaximumLength || + !pObjectNameInfo->Name.Buffer) + { + ERR_PRINT("Process file object is unnamed. (ProcessId = %Iu)", + ProcessId); + ntstatus = STATUS_OBJECT_NAME_NOT_FOUND; + goto exit; + } + + // + // Fail if the specified buffer is too small. + // + if (cbRequired > cbImageFilePath) + { + ntstatus = STATUS_INFO_LENGTH_MISMATCH; + goto exit; + } + + // + // Set out parameters. + // + RtlCopyMemory(pwzImageFilePath, pObjectNameInfo->Name.Buffer, cbRequired); + +exit: + if (pObjectNameInfo) + { + ExFreePool(pObjectNameInfo); + } + + if (fHasFileObjectReference) + { + ObDereferenceObject(pFileObject); + } + + if (fHasProcessReference) + { + ObDereferenceObject(pProcess); + } + + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +PsuReadVirtualMemory( + HANDLE ProcessId, + ULONG_PTR Address, + PVOID pBuffer, + ULONG cbBuffer, + PULONG pcbRead +) +{ + PEPROCESS pProcess = NULL; + BOOLEAN fHasProcessReference = FALSE; + BOOLEAN fHasProcessExitSynchronization = FALSE; + KAPC_STATE ApcState = {}; + NTSTATUS ntstatus = STATUS_SUCCESS; + + // + // Zero out parameters. + // + if (ARGUMENT_PRESENT(pcbRead)) + { + *pcbRead = 0; + } + + // + // Fail if the target address range is outside of user space. + // + if (Address + cbBuffer < Address || + Address + cbBuffer > (ULONG_PTR)MmHighestUserAddress || + Address + cbBuffer > (ULONG_PTR)MmHighestUserAddress) + { + ntstatus = STATUS_ACCESS_VIOLATION; + goto exit; + } + + ntstatus = PsLookupProcessByProcessId(ProcessId, &pProcess); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsLookupProcessByProcessId failed: 0x%X", ntstatus); + goto exit; + } + // + fHasProcessReference = TRUE; + + ntstatus = PsAcquireProcessExitSynchronization(pProcess); + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("PsAcquireProcessExitSynchronization failed: 0x%X", + ntstatus); + goto exit; + } + // + fHasProcessExitSynchronization = TRUE; + + __try + { + __try + { + KeStackAttachProcess(pProcess, &ApcState); + RtlCopyMemory(pBuffer, (PVOID)Address, cbBuffer); + } + __finally + { + KeUnstackDetachProcess(&ApcState); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + ERR_PRINT("Unexpected exception: 0x%X", GetExceptionCode()); + ntstatus = STATUS_UNHANDLED_EXCEPTION; + goto exit; + } + + // + // Set out parameters. + // + if (ARGUMENT_PRESENT(pcbRead)) + { + *pcbRead = cbBuffer; + } + +exit: + if (fHasProcessExitSynchronization) + { + PsReleaseProcessExitSynchronization(pProcess); + } + + if (fHasProcessReference) + { + ObDereferenceObject(pProcess); + } + + return ntstatus; +} + +``` + +`PareidoliaTriggerbot/process_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +PsuGetImageBase( + _In_ HANDLE ProcessId, + _Out_ PULONG_PTR pImageBase +); + +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +PsuGetImageFilePath( + _In_ HANDLE ProcessId, + _Out_writes_bytes_opt_(cbImageFilePath) PWCHAR pwzImageFilePath, + _In_ ULONG cbImageFilePath, + _Out_opt_ PULONG pcbRequired +); + +_IRQL_requires_max_(APC_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +PsuReadVirtualMemory( + _In_ HANDLE ProcessId, + _In_ ULONG_PTR Address, + _Out_writes_(cbBuffer) PVOID pBuffer, + _In_ ULONG cbBuffer, + _Out_opt_ PULONG pcbRead +); + +``` + +`PareidoliaTriggerbot/stealth.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "stealth.h" + +#include "log.h" +#include "nt.h" + + +//============================================================================= +// Private Types +//============================================================================= +typedef struct _UNLINK_DRIVER_OBJECT_CONTEXT { + NTSTATUS NtStatus; + PDRIVER_OBJECT DriverObject; + volatile POINTER_ALIGNMENT BOOLEAN OperationCompleted; + _Interlocked_ volatile POINTER_ALIGNMENT ULONG TargetProcessorNumber; + _Interlocked_ volatile POINTER_ALIGNMENT LONG NumberOfActiveProcessors; +} UNLINK_DRIVER_OBJECT_CONTEXT, *PUNLINK_DRIVER_OBJECT_CONTEXT; + + +//============================================================================= +// Private Prototypes +//============================================================================= +EXTERN_C +static +KIPI_BROADCAST_WORKER +StlpUnlinkDriverObjectIpiCallback; + +_Check_return_ +EXTERN_C +static +BOOLEAN +StlpIsListValid( + _In_ PLIST_ENTRY pListHead +); + + +//============================================================================= +// Public Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +StlIsUnlinkDriverObjectSupported() +/*++ + +Description: + + Query support status for unlinking a DRIVER_OBJECT from + nt!PsLoadedModuleList. + +Remarks: + + WARNING This is only supported on Windows 7. nt!PsLoadedModuleList is + protected by PatchGuard on Windows 8+. + +--*/ +{ + RTL_OSVERSIONINFOEXW VersionInfo = {}; + ULONG TypeMask = 0; + ULONGLONG ConditionMask = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); + + VersionInfo.dwMajorVersion = 6; + VersionInfo.dwMinorVersion = 1; + VersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT; + VersionInfo.wProductType = VER_NT_WORKSTATION; + + TypeMask = + VER_MAJORVERSION | + VER_MINORVERSION | + VER_PLATFORMID | + VER_PRODUCT_TYPE; + + VER_SET_CONDITION(ConditionMask, VER_MAJORVERSION, VER_EQUAL); + VER_SET_CONDITION(ConditionMask, VER_MINORVERSION, VER_EQUAL); + VER_SET_CONDITION(ConditionMask, VER_PLATFORMID, VER_EQUAL); + VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL); + + ntstatus = RtlVerifyVersionInfo(&VersionInfo, TypeMask, ConditionMask); + if (!NT_SUCCESS(ntstatus)) + { + goto exit; + } + +exit: + return ntstatus; +} + + +_Use_decl_annotations_ +EXTERN_C +NTSTATUS +StlUnlinkDriverObject( + PDRIVER_OBJECT pDriverObject +) +/*++ + +Description: + + Unlink the DRIVER_OBJECT from nt!PsLoadedModuleList so that the driver does + not appear in the loaded driver list. + +Parameters: + + pDriverObject - Pointer to the driver object to be unlinked. + +Remarks: + + We attempt to unlink the driver object in the context of an IPI callback + because the ERESOURCE (nt!PsLoadedModuleResource) guarding + nt!PsLoadedModuleList is not exported by ntoskrnl. + + NOTE The loaded driver list can be queried using NtQuerySystemInformation + with the SystemModuleInformation and SystemModuleInformationEx information + classes. + +--*/ +{ + PUNLINK_DRIVER_OBJECT_CONTEXT pContext = NULL; + ULONG nProcessors = 0; + NTSTATUS ntstatus = STATUS_SUCCESS; + + DBG_PRINT("Unlinking driver object. (DriverObject = %p)", pDriverObject); + + // + // Allocate the IPI callback context. + // + pContext = (PUNLINK_DRIVER_OBJECT_CONTEXT)ExAllocatePool( + NonPagedPool, + sizeof(*pContext)); + if (!pContext) + { + ntstatus = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + // + // Initialize the IPI callback context. + // + nProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS); + + NT_ASSERT(nProcessors); + + pContext->NtStatus = STATUS_INTERNAL_ERROR; + pContext->DriverObject = pDriverObject; + pContext->OperationCompleted = FALSE; + pContext->TargetProcessorNumber = KeGetCurrentProcessorNumberEx(NULL); + pContext->NumberOfActiveProcessors = nProcessors - 1; + + // + // Execute the IPI callback. + // + KeIpiGenericCall(StlpUnlinkDriverObjectIpiCallback, (ULONG_PTR)pContext); + + ntstatus = pContext->NtStatus; + + if (!NT_SUCCESS(ntstatus)) + { + ERR_PRINT("StlpUnlinkDriverObjectIpiCallback failed: 0x%X", ntstatus); + goto exit; + } + +exit: + if (pContext) + { + ExFreePool(pContext); + } + + return ntstatus; +} + + +//============================================================================= +// Private Interface +//============================================================================= +_Use_decl_annotations_ +EXTERN_C +static +ULONG_PTR +StlpUnlinkDriverObjectIpiCallback( + ULONG_PTR Argument +) +/*++ + +Description: + + Attempt to safely unlink a DRIVER_OBJECT from nt!PsLoadedModuleList without + acquiring nt!PsLoadedModuleResource. + +Parameters: + + Argument - Pointer to the callback context. + +Remarks: + + This routine implements a synchronization barrier so that only one + processor in the system attempts to unlink the target DRIVER_OBJECT at + IPI_LEVEL irql. + +--*/ +{ + PUNLINK_DRIVER_OBJECT_CONTEXT pContext = NULL; + PLDR_DATA_TABLE_ENTRY pLdrDataTableEntry = NULL; + + pContext = (PUNLINK_DRIVER_OBJECT_CONTEXT)Argument; + + // + // Trap all processors except the target processor in a spin loop until + // the target processor finishes the operation. + // + if (KeGetCurrentProcessorNumberEx(NULL) != pContext->TargetProcessorNumber) + { + InterlockedDecrement(&pContext->NumberOfActiveProcessors); + + while (!pContext->OperationCompleted) + { + YieldProcessor(); + } + + goto exit; + } + + // + // The target processor falls through to perform the unlink operation. + // + pLdrDataTableEntry = + (PLDR_DATA_TABLE_ENTRY)pContext->DriverObject->DriverSection; + + // + // Wait until all of the other processors are trapped in the spin loop. + // + while (pContext->NumberOfActiveProcessors) + { + YieldProcessor(); + } + + // + // Walk nt!PsLoadedModuleList to determine if we can unlink the target + // driver object without corrupting the loaded module list. + // + // We can safely walk the loaded module list without acquiring + // nt!PsLoadedModuleResource because: + // + // 1. All processors are synchronized at IPI_LEVEL irql. + // + // 2. Every processor except the target processor is trapped in the + // spin loop above. + // + // 3. ERESOURCE locks can only be (safely) acquired inside a critical + // region at irql <= APC_LEVEL. + // + if (StlpIsListValid(&pLdrDataTableEntry->InLoadOrderLinks)) + { + // + // Unlink the driver object. + // + RemoveEntryList(&pLdrDataTableEntry->InLoadOrderLinks); + + // + // The flink and blink pointers of the unlinked driver object's + // InLoadOrderLinks field still point to entries in + // nt!PsLoadedModuleList. Reinitialize this list entry so that + // ntoskrnl does not corrupt nt!PsLoadedModuleList when the driver + // object is unlinked (again) inside nt!MmUnloadSystemImage. + // + InitializeListHead(&pLdrDataTableEntry->InLoadOrderLinks); + + pContext->NtStatus = STATUS_SUCCESS; + } + else + { + pContext->NtStatus = STATUS_UNSUCCESSFUL; + } + + // + // Update the callback completion state so that the other processors exit + // the spin loop. + // + pContext->OperationCompleted = TRUE; + +exit: + return 0; +} + + +_Use_decl_annotations_ +EXTERN_C +static +BOOLEAN +StlpIsListValid( + PLIST_ENTRY pListHead +) +{ + PLIST_ENTRY pEntry = NULL; + BOOLEAN status = TRUE; + + for (pEntry = pListHead->Flink; + pEntry != pListHead; + pEntry = pEntry->Flink) + { + if (pEntry->Flink->Blink != pEntry || + pEntry->Blink->Flink != pEntry) + { + status = FALSE; + goto exit; + } + } + +exit: + return status; +} + +``` + +`PareidoliaTriggerbot/stealth.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +EXTERN_C +NTSTATUS +StlIsUnlinkDriverObjectSupported(); + +_Check_return_ +EXTERN_C +NTSTATUS +StlUnlinkDriverObject( + _Inout_ PDRIVER_OBJECT pDriverObject +); + +``` + +`PareidoliaTriggerbot/system_util.cpp`: + +```cpp +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#include "system_util.h" + +#include "nt.h" + + +_Use_decl_annotations_ +EXTERN_C +VOID +SysSetSignedKernelImageFlag( + PDRIVER_OBJECT pDriverObject +) +/*++ + +Description: + + Set the 'signed kernel image' flag in the loader data table entry for the + specified driver object. + +Parameters: + + pDriverObject - The target driver object. + +Remarks: + + Certain NT callbacks (e.g., object callbacks) require the calling driver to + have the undocumented 'LDRP_SIGNED_KERNEL_IMAGE' flag set. + +--*/ +{ + PLDR_DATA_TABLE_ENTRY pEntry = NULL; + + pEntry = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; + + pEntry->Flags |= LDRP_SIGNED_KERNEL_IMAGE; +} + +``` + +`PareidoliaTriggerbot/system_util.h`: + +```h +/*++ + +Copyright (c) 2019 changeofpace. All rights reserved. + +Use of this source code is governed by the MIT license. See the 'LICENSE' file +for more information. + +--*/ + +#pragma once + +#include + +EXTERN_C +VOID +SysSetSignedKernelImageFlag( + _Inout_ PDRIVER_OBJECT pDriverObject +); + +``` + +`README.md`: + +```md +# PareidoliaTriggerbot + +PareidoliaTriggerbot is a hypervisor-based, external Widowmaker triggerbot which uses the [VivienneVMM](https://github.com/changeofpace/VivienneVMM) and [MouClassInputInjection](https://github.com/changeofpace/MouClassInputInjection) projects to bypass the Overwatch Anti-Cheat. + +This hack has remained undetected since its initial completion in early 2018. + +[![PareidoliaTriggerbot Clip](https://img.youtube.com/vi/XqaSmmckqps/maxresdefault.jpg)](https://www.youtube.com/watch?v=XqaSmmckqps "PareidoliaTriggerbot Clip") + +## Usage + +See the [PareidoliaCL README](./PareidoliaCL/README.md). + +## Implementation + +PareidoliaTriggerbot is composed of a user mode client application and a kernel mode driver. The client contains the triggerbot logic, and the driver provides support services for the client (e.g., reading process memory without a handle) via a device interface. + +### Triggerbot Logic + +The client executes an infinite tick loop to update the triggerbot state machine. The following diagram depicts a simplified overview of the tick loop: + + .-----------------------. + | | + v | + +===============================+ | + | Process user input | | + +===============================+ | + | | + v | + .-------------------------------. | + | Is the round context valid? |--No-->+ + '-------------------------------' | + | | + Yes | + | | + v | + .-------------------------------. | + | Is the triggerbot enabled? |--No-->+ + '-------------------------------' | + | | + Yes | + | | + v | + +===============================+ | + | Read Widowmaker Trace State | | + +===============================+ | + | | + v | + .-------------------------------. | + | Is the player's crosshair |--No-->+ + | over an enemy player? | | + '-------------------------------' | + | | + Yes | + | | + v | + +===============================+ | + | Activate trigger | | + +===============================+ | + | | + v | + +===============================+ | + | Trigger cooldown | | + +===============================+ | + | | + '-----------------------' + +### Widowmaker Trace State + +The Widowmaker trace state is a ULONG-sized variable which has a nonzero value when: + +1. There is a Widowmaker player in a match. + +2. The Widowmaker player has the Widow's Kiss sniper rifle equipped. + +3. The Widowmaker player is scoped (i.e., zoomed in) and the Widow's Kiss is fully charged. + +4. The Widowmaker player's crosshair is over an enemy player entity or a dynamic, non-player entity (e.g., the payload, a closed spawn door, or the lid of a trash can). + +This variable exists as a field in the trace state object type. The following diagram depicts the trace state elements inside the Overwatch virtual address space: + + + Low Memory + +====================+ + | | Trace State Object + | | ~ + | | +=============+ + | | | | Trace State + |--------------------| |-------------| ~ + | Dynamic Allocation | -----> | Trace State | -----> [0, MAXULONG] + |--------------------| | Variable | + | | |-------------| + | | | | + | | +=============+ + | | + | | + | | + | | + | | + +====================+ + High Memory + + +The game engine maintains one trace state object for each Widowmaker player in a match. A trace state object is created each time a non-Widowmaker player swaps to the Widowmaker hero. The object is destroyed when the player swaps to a non-Widowmaker hero, the round ends, or the player leaves the match. + +NOTE We do not fully understand the purpose of the trace state variable. We refer to this concept as the 'trace state' because the variable behaves as if it were the hit detection result of the game engine running a trace ray whose origin vector is the Widowmaker player's crosshair. + +### Widowmaker Trace State Instruction + +The trace state mechanic provides all of the functionality required to write a triggerbot. In order to use this mechanic we need to be able to locate the address of our (local player) trace state object inside the remote Overwatch process. This is nontrivial for the following reasons: + +1. The address of the target trace state object changes when the object is destroyed and recreated. + +2. We cannot hook code in the Overwatch process because the anti-cheat frequently scans for code patches. + +3. Overwatch's code and data are significantly obfuscated. + +We can reliably recover the address of our trace state object using the VivienneVMM [Capture Execution Context Register](https://github.com/changeofpace/VivienneVMM#capture-execution-context) request. Our target instruction is executed whenever a Widowmaker player presses their 'zoom' keybind. The following is the annotated assembly of the target instruction: + + Platform: Windows 7 x64 + File Name: Overwatch.exe + Version: 1.41.1.0 - 63372 + SHA256: + + 9d079af7df9ed332f32d303c1eec0aab886f300dc79489019b8bbae682cbdb99 + + Assembly: + + 89 87 FC 01 00 00 mov [rdi+1FCh], eax + + rdi = Pointer to the base address of a trace state object. + 1FC = The field offset of the trace state variable. + eax = The new trace state value. + +NOTE We found this instruction by scanning Overwatch's virtual memory for values which changed when the local player was scoped / not scoped. + +NOTE We do not fully understand the purpose of the trace state instruction or its containing function. + +### Anti-Cheat Bypass + +PareidoliaTriggerbot passively avoids detection using the following strategies: + +1. The client interacts with the target Overwatch process using the driver interface. i.e., The client does not open any handles to the target Overwatch process. + +2. The client uses the MouClassInputInjection project for stealthy mouse input injection. + +3. The driver registers kernel object callbacks to prevent the target Overwatch process from reading the client's virtual address space. + +4. The triggerbot simulates realistic mouse clicks by waiting for a dynamic amount of time before injecting the mouse release event. This release delay is a pseudo-random number bounded by parameters in the [config file](./Common/config.h). Users can use the [MouHidInputHook](https://github.com/changeofpace/MouHidInputHook) project to obtain the average delay for their mouse. + +## Projects + +This repository uses the [MouClassInputInjection](https://github.com/changeofpace/MouClassInputInjection) and [VivienneVMM](https://github.com/changeofpace/VivienneVMM) projects as git submodules. The client and driver projects in these submodules are configured to produce static libraries. The PareidoliaTriggerbot projects link against these libraries for mouse input injection and capture execution context register requests. + +## PareidoliaCL + +The user mode triggerbot client. + +## PareidoliaTriggerbot + +The kernel mode triggerbot driver. + +## MouClassInputInjection + +A kernel mode static library which implements mouse input injection. This project is a submodule. + +## MouiiCL + +A user mode static library which provides the client interface for mouse input injection. This project is a submodule. + +## VivienneCL + +A user mode static library which provides the VivienneVMM interface for Capture Execution Context Register requests. This project is a submodule. + +## VivienneVMM + +A kernel mode static library which implements the VivienneVMM interface. This project is a submodule. + +## Warnings + +### Names + +The projects in this repository use several globally visible names which can potentially be used as a detection vector by anti-cheat software. These names include: + +* The symbolic link for the PareidoliaTriggerbot device object. +* The symbolic link for the MouClassInputInjection device object. +* The symbolic link for the VivienneVMM device object. +* The VivienneVMM signature returned by the cpuid instruction. +* The VivienneVMM log file name. + +Users should modify these names and values by editing the [config file](./Common/config.h). + +### Signature Scanning + +The **PareidoliaCL.exe** and **PareidoliaTriggerbot.sys** files on disk are subject to signature scans (even with the process access protection module enabled). Users should pack and/or encrypt the **PareidoliaCL.exe** file and delete the **PareidoliaTriggerbot.sys** file after the driver is loaded to avoid this detection vector. + +### Mouse Input Injection + +The **PareidoliaTriggerbot** driver does not prevent successive **MOUSE_LEFT_BUTTON_DOWN** or **MOUSE_LEFT_BUTTON_UP** mouse input packets. Normally, a physical mouse will never generate two **MOUSE_LEFT_BUTTON_DOWN** packets or two **MOUSE_LEFT_BUTTON_UP** packets in a row because each physical click action has a press and release event. Since the PareidoliaTriggerbot driver injects mouse input packets it is possible for the following series of events to occur: + +1. **PareidoliaCL** decides to trigger. +2. **PareidoliaCL** sends an IO request to the driver to inject a left-click input packet. +3. **PareidoliaTriggerbot** processes the IO request and injects a **MOUSE_LEFT_BUTTON_DOWN** packet. +4. The user physically presses the left mouse button. + +In this scenario, steps (3) and (4) result in two successive **MOUSE_LEFT_BUTTON_DOWN** packets in the input data stream. Anti-cheat software can potentially use this unnatural series of events as a detection vector by monitoring mouse input events via Windows input hooks. + +Users can mitigate this scenario by not clicking while the local player is scoped and the triggerbot is active. + +## Limitations + +### Process Access Manager + +The Process Access Protection module may cause undesirable side effects. e.g., The user will be unable to close the command prompt process that is hosting the client by clicking the red 'X' button. This feature can be disabled by modifying the config file. + +### Driver Certificate + +The PareidoliaTriggerbot project does not contain a certificate issued by a trusted root authority. Users must enable test signing in order to load the PareidoliaTriggerbot driver. + +## Disclaimer + +This project is intended for educational purposes. Use at your own risk. + +## Notes + +* All projects in this repository were developed and tested on Windows 7 x64 SP1. +* All binaries are PatchGuard safe on Windows 7. + +``` + +`hde/LICENSE`: + +``` +The source code contained in this directory is Copyright (c) 2008-2009, +Vyacheslav Patkov. It has been modified for integration with the parent +project. + +=============================================================================== + +License agreement + +Hacker Disassembler Engine 32 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================== + +License agreement + +Hacker Disassembler Engine 64 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +`hde/hde.h`: + +```h +#pragma once + +#if defined(_WIN64) +#include "hde64.h" + +typedef hde64s HDE_DISASSEMBLY, *PHDE_DISASSEMBLY; + +#define HDE_DISASM hde64_disasm +#else +#include "hde32.h" + +typedef hde32s HDE_DISASSEMBLY, *PHDE_DISASSEMBLY; + +#define HDE_DISASM hde32_disasm +#endif + +// +// The maximum amount of bytes the disassembler will read from the code buffer +// before failing. +// +// NOTE This number was taken from the 'Hacker Disassembler Engine 64 C 0.04 +// FINAL' manual and has not been verified. +// +#define HDE_INSTRUCTION_SIZE_MAX 26 + +``` + +`hde/hde32.cpp`: + +```cpp +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include "hde32.h" + +#include + +#include "table32.h" + +#pragma warning(push) +#pragma warning(disable:4701) + +unsigned int hde32_disasm(const void *code, hde32s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; + + __stosb((unsigned char*)hs, 0, sizeof(hde32s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (pref & PRE_66) { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } else { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } + } + + if (cflags & C_IMM16) { + if (hs->flags & F_IMM32) { + hs->flags |= F_IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else if (hs->flags & F_IMM16) { + hs->flags |= F_2IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + } + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#pragma warning(pop) // 4701 + +``` + +`hde/hde32.h`: + +```h +/* + * Hacker Disassembler Engine 32 + * Copyright (c) 2006-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde32.h: C/C++ header file + * + */ + +#pragma once + +#include "hde_stdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_DISP8 0x00000020 +#define F_DISP16 0x00000040 +#define F_DISP32 0x00000080 +#define F_RELATIVE 0x00000100 +#define F_2IMM16 0x00000800 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_ANY 0x3f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde32s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde32_disasm(const void *code, hde32s *hs); + +#ifdef __cplusplus +} +#endif + +``` + +`hde/hde64.cpp`: + +```cpp +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include "hde64.h" + +#include + +#include "table64.h" + +#pragma warning(push) +#pragma warning(disable:4706) + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c = 0, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + __stosb((unsigned char*)hs, 0, sizeof(hde64s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { +#pragma warning(suppress : 26451) // Arithmetic overflow. + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#pragma warning(pop) // 4706 + +``` + +`hde/hde64.h`: + +```h +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#pragma once + +#include "hde_stdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +``` + +`hde/hde_stdint.h`: + +```h +#pragma once + +#include + +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; + +``` + +`hde/table32.h`: + +```h +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#pragma once + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xf1 +#define DELTA_FPU_MODRM 0xf8 +#define DELTA_PREFIXES 0x130 +#define DELTA_OP_LOCK_OK 0x1a1 +#define DELTA_OP2_LOCK_OK 0x1b9 +#define DELTA_OP_ONLY_MEM 0x1cb +#define DELTA_OP2_ONLY_MEM 0x1da + +unsigned char hde32_table[] = { + 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, + 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, + 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, + 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, + 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, + 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, + 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, + 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, + 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, + 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, + 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, + 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, + 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, + 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, + 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, + 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, + 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, + 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, + 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, + 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, + 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, + 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, + 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, + 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, + 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, + 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, + 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, + 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, + 0xe7,0x08,0x00,0xf0,0x02,0x00 +}; + +``` + +`hde/table64.h`: + +```h +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#pragma once + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; + +``` \ No newline at end of file diff --git a/archive/ekknod/MouseClassServiceCallbackMeme.txt b/archive/ekknod/MouseClassServiceCallbackMeme.txt new file mode 100644 index 00000000..31d19a09 --- /dev/null +++ b/archive/ekknod/MouseClassServiceCallbackMeme.txt @@ -0,0 +1,987 @@ +Project Path: arc_ekknod_MouseClassServiceCallbackMeme_o3t7b__9 + +Source Tree: + +```txt +arc_ekknod_MouseClassServiceCallbackMeme_o3t7b__9 +├── MouseClassServiceCallbackMeme.sln +├── MouseClassServiceCallbackMeme.vcxproj +├── MouseClassServiceCallbackMeme.vcxproj.filters +├── MouseClassServiceCallbackMeme.vcxproj.user +├── README.md +├── main.c +└── mouse.asm + +``` + +`MouseClassServiceCallbackMeme.sln`: + +```sln + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32630.192 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseClassServiceCallbackMeme", "MouseClassServiceCallbackMeme.vcxproj", "{D2002713-76DD-4F66-921E-42F9405372B1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.Build.0 = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.ActiveCfg = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.Build.0 = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.Deploy.0 = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.ActiveCfg = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.Build.0 = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.Deploy.0 = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.ActiveCfg = Release|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.Build.0 = Release|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AB3EBD45-1882-414A-A597-95D80D86AD83} + EndGlobalSection +EndGlobal + +``` + +`MouseClassServiceCallbackMeme.vcxproj`: + +```vcxproj + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {D2002713-76DD-4F66-921E-42F9405372B1} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + MouseClassServiceCallbackMeme + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + false + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + + + sha256 + + + DriverEntry + + + + + sha256 + + + + + sha256 + + + + + + + + + + + Document + + + + + +``` + +`MouseClassServiceCallbackMeme.vcxproj.filters`: + +```filters + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Source Files + + + + + Source Files + + + +``` + +`MouseClassServiceCallbackMeme.vcxproj.user`: + +```user + + + + true + + +``` + +`README.md`: + +```md +# MouseClassServiceCallbackMeme +Calling "own" MouseClassServiceCallback + +``` + +`main.c`: + +```c +#include + +/* + * Old project, what can be used for mouse input manipulation +*/ + +typedef int BOOL; +typedef unsigned int DWORD; +typedef ULONG_PTR QWORD; + +#pragma warning(disable : 4201) +typedef struct _MOUSE_INPUT_DATA { + USHORT UnitId; + USHORT Flags; + union { + ULONG Buttons; + struct { + USHORT ButtonFlags; + USHORT ButtonData; + }; + }; + ULONG RawButtons; + LONG LastX; + LONG LastY; + ULONG ExtraInformation; +} MOUSE_INPUT_DATA, *PMOUSE_INPUT_DATA; + +typedef VOID +(*MouseClassServiceCallbackFn)( + PDEVICE_OBJECT DeviceObject, + PMOUSE_INPUT_DATA InputDataStart, + PMOUSE_INPUT_DATA InputDataEnd, + PULONG InputDataConsumed +); + +typedef struct _MOUSE_OBJECT +{ + PDEVICE_OBJECT mouse_device; + MouseClassServiceCallbackFn service_callback; + BOOL use_mouse; +} MOUSE_OBJECT, * PMOUSE_OBJECT; + + + +MOUSE_OBJECT gMouseObject; +QWORD _KeAcquireSpinLockAtDpcLevel; +QWORD _KeReleaseSpinLockFromDpcLevel; +QWORD _IofCompleteRequest; +QWORD _IoReleaseRemoveLockEx; + + + + +NTSYSCALLAPI +POBJECT_TYPE* IoDriverObjectType; + +VOID MouseClassServiceCallback( + PDEVICE_OBJECT DeviceObject, + PMOUSE_INPUT_DATA InputDataStart, + PMOUSE_INPUT_DATA InputDataEnd, + PULONG InputDataConsumed +); + +NTSYSCALLAPI +NTSTATUS +ObReferenceObjectByName( + __in PUNICODE_STRING ObjectName, + __in ULONG Attributes, + __in_opt PACCESS_STATE AccessState, + __in_opt ACCESS_MASK DesiredAccess, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode, + __inout_opt PVOID ParseContext, + __out PVOID *Object + ); + + +void NtSleep(DWORD milliseconds) +{ + QWORD ms = milliseconds; + ms = (ms * 1000) * 10; + ms = ms * -1; +#ifdef _KERNEL_MODE + KeDelayExecutionThread(KernelMode, 0, (PLARGE_INTEGER)&ms); +#else + NtDelayExecution(0, (PLARGE_INTEGER)&ms); +#endif +} + +void mouse_move(long x, long y, unsigned short button_flags); + +VOID +DriverUnload( + _In_ struct _DRIVER_OBJECT* DriverObject +) +{ + UNREFERENCED_PARAMETER(DriverObject); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] MouseClassServiceCallbackMeme.sys is closed\n"); +} + +QWORD _KeAcquireSpinLockAtDpcLevel; +QWORD _KeReleaseSpinLockFromDpcLevel; +QWORD _IofCompleteRequest; +QWORD _IoReleaseRemoveLockEx; + +NTSTATUS DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath +) +{ + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(RegistryPath); + + /* Microsoft compiler is sometimes retarded, thats why we have to do this non sense */ + /* It would otherwise generate wrapper functions around, and it would cause system BSOD */ + _KeAcquireSpinLockAtDpcLevel = (QWORD)KeAcquireSpinLockAtDpcLevel; + _KeReleaseSpinLockFromDpcLevel = (QWORD)KeReleaseSpinLockFromDpcLevel; + _IofCompleteRequest = (QWORD)IofCompleteRequest; + _IoReleaseRemoveLockEx = (QWORD)IoReleaseRemoveLockEx; + + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] MouseClassServiceCallbackMeme.sys is launched\n"); + DriverObject->DriverUnload = DriverUnload; + + + for (int i = 0; i < 32; i++) { + NtSleep(100); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] Moving mouse\n"); + + mouse_move(0, -10, 0); + } + + + return STATUS_SUCCESS; +} + +BOOL mouse_open(void) +{ + // https://github.com/nbqofficial/norsefire + + if (gMouseObject.use_mouse == 0) { + + UNICODE_STRING class_string; + RtlInitUnicodeString(&class_string, L"\\Driver\\MouClass"); + + + PDRIVER_OBJECT class_driver_object = NULL; + NTSTATUS status = ObReferenceObjectByName(&class_string, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&class_driver_object); + if (!NT_SUCCESS(status)) { + gMouseObject.use_mouse = 0; + return 0; + } + + UNICODE_STRING hid_string; + RtlInitUnicodeString(&hid_string, L"\\Driver\\MouHID"); + + + PDRIVER_OBJECT hid_driver_object = NULL; + + status = ObReferenceObjectByName(&hid_string, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&hid_driver_object); + if (!NT_SUCCESS(status)) + { + if (class_driver_object) { + ObfDereferenceObject(class_driver_object); + } + gMouseObject.use_mouse = 0; + return 0; + } + + PVOID class_driver_base = NULL; + + + PDEVICE_OBJECT hid_device_object = hid_driver_object->DeviceObject; + while (hid_device_object && !gMouseObject.service_callback) + { + PDEVICE_OBJECT class_device_object = class_driver_object->DeviceObject; + while (class_device_object && !gMouseObject.service_callback) + { + if (!class_device_object->NextDevice && !gMouseObject.mouse_device) + { + gMouseObject.mouse_device = class_device_object; + } + + PULONG_PTR device_extension = (PULONG_PTR)hid_device_object->DeviceExtension; + ULONG_PTR device_ext_size = ((ULONG_PTR)hid_device_object->DeviceObjectExtension - (ULONG_PTR)hid_device_object->DeviceExtension) / 4; + class_driver_base = class_driver_object->DriverStart; + for (ULONG_PTR i = 0; i < device_ext_size; i++) + { + if (device_extension[i] == (ULONG_PTR)class_device_object && device_extension[i + 1] > (ULONG_PTR)class_driver_object) + { + gMouseObject.service_callback = (MouseClassServiceCallbackFn)(device_extension[i + 1]); + + break; + } + } + class_device_object = class_device_object->NextDevice; + } + hid_device_object = hid_device_object->AttachedDevice; + } + + if (!gMouseObject.mouse_device) + { + PDEVICE_OBJECT target_device_object = class_driver_object->DeviceObject; + while (target_device_object) + { + if (!target_device_object->NextDevice) + { + gMouseObject.mouse_device = target_device_object; + break; + } + target_device_object = target_device_object->NextDevice; + } + } + + ObfDereferenceObject(class_driver_object); + ObfDereferenceObject(hid_driver_object); + + if (gMouseObject.mouse_device && gMouseObject.service_callback) { + gMouseObject.use_mouse = 1; + } + + } + + return gMouseObject.mouse_device && gMouseObject.service_callback; +} + +#define KeMRaiseIrql(a,b) *(b) = KfRaiseIrql(a) +void mouse_move(long x, long y, unsigned short button_flags) +{ + KIRQL irql; + ULONG input_data; + MOUSE_INPUT_DATA mid = { 0 }; + mid.LastX = x; + mid.LastY = y; + mid.ButtonFlags = button_flags; + if (!mouse_open()) { + return; + } + mid.UnitId = 1; + KeMRaiseIrql(DISPATCH_LEVEL, &irql); + MouseClassServiceCallback(gMouseObject.mouse_device, &mid, (PMOUSE_INPUT_DATA)&mid + 1, &input_data); + KeLowerIrql(irql); +} + +``` + +`mouse.asm`: + +```asm +EXTERNDEF _KeAcquireSpinLockAtDpcLevel:PROC +EXTERNDEF _KeReleaseSpinLockFromDpcLevel:PROC +EXTERNDEF _IofCompleteRequest:PROC +EXTERNDEF _IoReleaseRemoveLockEx:PROC +EXTERNDEF memmove:PROC + +.data +WPP_RECORDER_INITIALIZED dq 0; +WPP_GLOBAL_Control dq 0; +.code + +WPP_RECORDER_SF proc + ret +WPP_RECORDER_SF endp + +MouseClassReadCopyData proc + mov r11,rsp + mov QWORD PTR [r11+8h],rbx + mov QWORD PTR [r11+10h],rbp + mov QWORD PTR [r11+18h],rsi + push rdi + push r12 + push r13 + push r14 + push r15 + sub rsp,50h + inc DWORD PTR [rcx+0a8h] + mov rsi,rdx + mov eax,DWORD PTR [rcx+54h] + mov rdi,rcx + mov r13,QWORD PTR [rdx+0b8h] + lea ebp,[rax+rax*2] + mov ebx,DWORD PTR [r13+8h] + shl ebp,3h + mov edx,DWORD PTR [rdi+88h] + cmp ebp,ebx + cmovae ebp,ebx + sub edx,DWORD PTR [rdi+78h] + add edx,DWORD PTR [rdi+68h] + mov r12d,ebp + cmp ebp,edx + cmovae r12d,edx + mov r14,QWORD PTR [rsi+18h] + mov rdx,QWORD PTR [rdi+78h] + mov rcx,r14 + mov r8d,r12d + mov r15d,r12d + call memmove + add r14,r15 + mov ebx,ebp + sub ebx,r12d + je J1A5 + mov rdx,QWORD PTR [rdi+68h] + mov r8,rbx + mov rcx,r14 + call memmove + mov rcx,QWORD PTR [rdi+68h] + add rcx,rbx + mov QWORD PTR [rdi+78h],rcx + jmp J1B0 +J1A5: + add QWORD PTR [rdi+78h],r15 +J1B0: + mov ebx,ebp + mov rax,0aaaaaaaaaaaaaaabh + mul rbx + shr rdx,4h + sub DWORD PTR [rdi+54h],edx + jne J1FF + mov BYTE PTR [rdi+42h],1h +J1FF: + mov QWORD PTR [rsi+38h],rbx + lea r11,[rsp+50h] + mov rbx,QWORD PTR [r11+30h] + xor eax,eax + mov rsi,QWORD PTR [r11+40h] + mov DWORD PTR [r13+8h],ebp + mov rbp,QWORD PTR [r11+38h] + mov rsp,r11 + pop r15 + pop r14 + pop r13 + pop r12 + pop rdi + ret +MouseClassReadCopyData endp + + + + +MouseClassDequeueRead proc + xor edx,edx + lea r8,[rcx+98h] +J9: + mov rcx,QWORD PTR [r8] + cmp rcx,r8 + je J47 + cmp QWORD PTR [rcx+8h],r8 + jne J4C + mov rax,QWORD PTR [rcx] + cmp QWORD PTR [rax+8h],rcx + jne J4C + mov QWORD PTR [r8],rax + lea rdx,[rcx-0a8h] + mov QWORD PTR [rax+8h],r8 + xor eax,eax + xchg QWORD PTR [rdx+68h],rax + test rax,rax + jne J42 + mov QWORD PTR [rcx+8h],rcx + xor edx,edx + mov QWORD PTR [rcx],rcx +J42: + test rdx,rdx + je J9 +J47: + mov rax,rdx + ret + int 3 +J4C: + mov ecx,3h + int 29h +MouseClassDequeueRead endp + + +MouseClassServiceCallback proc + mov rax,rsp + mov QWORD PTR [rax+8h],rbx + mov QWORD PTR [rax+10h],rsi + mov QWORD PTR [rax+18h],rdi + mov QWORD PTR [rax+20h],r9 + push rbp + push r12 + push r13 + push r14 + push r15 + mov rbp,rsp + sub rsp,70h + mov r13,r9 + mov rbx,r8 + mov r14,rdx + mov r15,rcx + + + + + lea rax, WPP_RECORDER_INITIALIZED + xor esi,esi + cmp WPP_RECORDER_INITIALIZED, rax + jne J61 + mov rcx,QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J61 + mov rcx,QWORD PTR [rcx+40h] + lea r9d,[rsi+32h] + lea r8d,[rsi+3h] + mov dl,5h + call WPP_RECORDER_SF + +J61: + + + mov rdi,QWORD PTR [r15+40h] + sub ebx,r14d + mov r12d,esi + mov DWORD PTR [r13+0h],esi + lea rcx,[rdi+90h] + call QWORD PTR _KeAcquireSpinLockAtDpcLevel + nop DWORD PTR [rax+rax*1+0h] + lea rax,[rbp-10h] + mov rcx,rdi + mov QWORD PTR [rbp-8h],rax + lea rax,[rbp-10h] + mov QWORD PTR [rbp-10h],rax + call MouseClassDequeueRead + mov rsi,rax + xor r9d,r9d + mov rax,0aaaaaaaaaaaaaaabh + test rsi,rsi + je J1aa + mov r13,QWORD PTR [rsi+0b8h] + mov r12d,ebx + mov r8d,DWORD PTR [r13+8h] + cmp ebx,r8d + cmovae r12d,r8d + mul r12 + mov rax,QWORD PTR [rbp+48h] + shr rdx,4h + add DWORD PTR [rax],edx + lea rax, WPP_RECORDER_INITIALIZED + cmp WPP_RECORDER_INITIALIZED,rax + jne J11d + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],r9w + je J11d + mov rax,QWORD PTR [rsi+18h] + mov rcx,QWORD PTR [rcx+40h] + mov QWORD PTR [rsp+50h],rax + mov QWORD PTR [rsp+48h],r14 + mov DWORD PTR [rsp+40h],r8d + mov DWORD PTR [rsp+38h],ebx + mov QWORD PTR [rsp+30h],rsi + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + +J11d: + + mov rax,0fffff78000000014h + mov rax,QWORD PTR [rax] + lea rdx,WPP_RECORDER_INITIALIZED + cmp WPP_RECORDER_INITIALIZED,rdx + jne J15e + mov rcx, QWORD PTR WPP_GLOBAL_Control + mov DWORD PTR [rsp+40h],r12d + mov QWORD PTR [rsp+38h],rax + mov QWORD PTR [rsp+30h],rsi + mov rcx,QWORD PTR [rcx+40h] + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + +J15e: + + mov rcx,QWORD PTR [rsi+18h] + mov r8,r12 + mov rdx,r14 + call memmove + mov QWORD PTR [rsi+38h],r12 + lea rcx,[rbp-10h] + xor r8d,r8d + mov DWORD PTR [rsi+30h],r8d + add rsi,0a8h + mov DWORD PTR [r13+8h],r12d + mov rax,QWORD PTR [rbp-8h] + cmp QWORD PTR [rax],rcx + jne J495 + mov r13,QWORD PTR [rbp+48h] + lea rcx,[rbp-10h] + mov QWORD PTR [rsi],rcx + mov QWORD PTR [rsi+8h],rax + mov QWORD PTR [rax],rsi + mov QWORD PTR [rbp-8h],rsi + +J1aa: + + mov eax,r12d + add r14,rax + sub ebx,r12d + lea r12,WPP_RECORDER_INITIALIZED + xor esi,esi + cmp WPP_RECORDER_INITIALIZED,r12 + jne J1e4 + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J1e4 + mov rcx,QWORD PTR [rcx+40h] + mov DWORD PTR [rsp+30h],ebx + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + +J1e4: + + test ebx,ebx + je J41d + cmp WPP_RECORDER_INITIALIZED,r12 + jne J22f + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J22f + mov eax,DWORD PTR [rdi+54h] + mov r9d,36h + mov rcx,QWORD PTR [rcx+40h] + mov DWORD PTR [rsp+38h],ebx + lea edx,[rax+rax*2] + mov eax,DWORD PTR [rdi+88h] + shl edx,3h + sub eax,edx + mov DWORD PTR [rsp+30h],eax + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + +J22f: + + mov ecx,DWORD PTR [rdi+88h] + cmp ecx,ebx + mov r12d,ecx + cmovae r12d,ebx + sub ecx,DWORD PTR [rdi+70h] + mov ebx,DWORD PTR [rdi+68h] + add ebx,ecx + lea rax,WPP_RECORDER_INITIALIZED + cmp WPP_RECORDER_INITIALIZED,rax + jne J287 + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J287 + mov rcx,QWORD PTR [rcx+40h] + mov r9d,38h + mov DWORD PTR [rsp+38h],ebx + mov DWORD PTR [rsp+30h],r12d + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + lea rax,WPP_RECORDER_INITIALIZED + +J287: + + cmp r12d,ebx + mov esi,r12d + cmovae esi,ebx + cmp WPP_RECORDER_INITIALIZED,rax + jne J2cc + mov rcx, QWORD PTR WPP_GLOBAL_Control + xor eax,eax + cmp WORD PTR [rcx+48h],ax + je J2cc + mov rcx,QWORD PTR [rcx+40h] + lea r9d,[rax+39h] + mov rax,QWORD PTR [rdi+70h] + mov QWORD PTR [rsp+40h],rax + mov QWORD PTR [rsp+38h],r14 + mov DWORD PTR [rsp+30h],esi + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + +J2cc: + + mov rcx,QWORD PTR [rdi+70h] + mov rdx,r14 + mov r8d,esi + mov ebx,esi + call memmove + add QWORD PTR [rdi+70h],rbx + add r14,rbx + mov rdx,QWORD PTR [rdi+68h] + mov eax,DWORD PTR [rdi+88h] + mov rcx,QWORD PTR [rdi+70h] + add rax,rdx + cmp rcx,rax + jb J301 + mov QWORD PTR [rdi+70h],rdx + mov rcx,rdx + +J301: + + mov ebx,r12d + sub ebx,esi + je J362 + lea rdx,WPP_RECORDER_INITIALIZED + mov rax,rcx + cmp WPP_RECORDER_INITIALIZED,rdx + jne J350 + mov rdx, QWORD PTR WPP_GLOBAL_Control + xor r8d,r8d + cmp WORD PTR [rdx+48h],r8w + je J350 + mov QWORD PTR [rsp+40h],rcx + lea r9d,[r8+03ah] + mov rcx,QWORD PTR [rdx+40h] + mov QWORD PTR [rsp+38h],r14 + mov DWORD PTR [rsp+30h],ebx + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + mov rax,QWORD PTR [rdi+70h] + +J350: + + mov r8,rbx + mov rdx,r14 + mov rcx,rax + call memmove + add QWORD PTR [rdi+70h],rbx + +J362: + + mov ecx,r12d + mov rax,0aaaaaaaaaaaaaaabh + mul rcx + shr rdx,4h + add DWORD PTR [rdi+54h],edx + mov ecx,DWORD PTR [r13+0h] + add ecx,edx + mov eax,ecx + mov DWORD PTR [r13+0h],ecx + lea r12,WPP_RECORDER_INITIALIZED + xor esi,esi + cmp WPP_RECORDER_INITIALIZED,r12 + jne J41d + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J41d + mov rcx,QWORD PTR [rcx+40h] + mov DWORD PTR [rsp+48h],eax + mov rax,QWORD PTR [rdi+78h] + mov QWORD PTR [rsp+40h],rax + mov rax,QWORD PTR [rdi+70h] + mov QWORD PTR [rsp+38h],rax + mov eax,DWORD PTR [rdi+54h] + mov DWORD PTR [rsp+30h],eax + mov QWORD PTR [rsp+28h],r15 + call WPP_RECORDER_SF + jmp J41d + +J3d5: + + mov rcx,rdi + call MouseClassDequeueRead + mov rbx,rax + test rax,rax + je J422 + mov rdx,rax + mov rcx,rdi + call MouseClassReadCopyData + mov DWORD PTR [rbx+30h],eax + lea rcx,[rbp-10h] + mov rdx,QWORD PTR [rbp-8h] + lea rax,[rbx+0a8h] + cmp QWORD PTR [rdx],rcx + jne J495 + mov QWORD PTR [rax+8h],rdx + lea rcx,[rbp-10h] + mov QWORD PTR [rax],rcx + mov QWORD PTR [rdx],rax + mov QWORD PTR [rbp-8h],rax + +J41d: + + cmp DWORD PTR [rdi+54h],esi + + + ja J3d5 + +J422: + + lea rcx,[rdi+90h] + call QWORD PTR _KeReleaseSpinLockFromDpcLevel + nop DWORD PTR [rax+rax*1+0h] + +J435: + + mov rbx,QWORD PTR [rbp-10h] + lea rax,[rbp-10h] + cmp rbx,rax + je J49c + lea rax,[rbp-10h] + cmp QWORD PTR [rbx+8h],rax + jne J495 + mov rax,QWORD PTR [rbx] + cmp QWORD PTR [rax+8h],rbx + jne J495 + lea rcx,[rbp-10h] + mov QWORD PTR [rbp-10h],rax + mov QWORD PTR [rax+8h],rcx + mov dl,6h + lea rcx,[rbx-0a8h] + call QWORD PTR _IofCompleteRequest + nop DWORD PTR [rax+rax*1+0h] + lea rcx,[rdi+20h] + mov r8d,20h + lea rdx,[rbx-0a8h] + call QWORD PTR _IoReleaseRemoveLockEx + nop DWORD PTR [rax+rax*1+0h] + jmp J435 + +J495: + + mov ecx,3h + int 29h + +J49C: + + cmp WPP_RECORDER_INITIALIZED,r12 + jne J4c7 + mov rcx, QWORD PTR WPP_GLOBAL_Control + cmp WORD PTR [rcx+48h],si + je J4c7 + mov rcx,QWORD PTR [rcx+40h] + mov r9d,3ch + mov dl,5h + lea r8d,[r9-39h] + call WPP_RECORDER_SF + +J4c7: + + lea r11,[rsp+70h] + mov rbx,QWORD PTR [r11+30h] + mov rsi,QWORD PTR [r11+38h] + mov rdi,QWORD PTR [r11+40h] + mov rsp,r11 + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + ret +MouseClassServiceCallback endp + +end + + +``` \ No newline at end of file diff --git a/archive/ekknod/MouseClassServiceCallbackTrick.txt b/archive/ekknod/MouseClassServiceCallbackTrick.txt new file mode 100644 index 00000000..305df592 --- /dev/null +++ b/archive/ekknod/MouseClassServiceCallbackTrick.txt @@ -0,0 +1,719 @@ +Project Path: arc_ekknod_MouseClassServiceCallbackTrick_p4p1gddl + +Source Tree: + +```txt +arc_ekknod_MouseClassServiceCallbackTrick_p4p1gddl +├── MouseClassServiceCallbackTrick.sln +├── MouseClassServiceCallbackTrick.vcxproj +├── MouseClassServiceCallbackTrick.vcxproj.filters +├── MouseClassServiceCallbackTrick.vcxproj.user +├── README.md +├── main.c +└── mouse.asm + +``` + +`MouseClassServiceCallbackTrick.sln`: + +```sln + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32630.192 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseClassServiceCallbackTrick", "MouseClassServiceCallbackTrick.vcxproj", "{D2002713-76DD-4F66-921E-42F9405372B1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.Build.0 = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.ActiveCfg = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.Build.0 = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Debug|x64.Deploy.0 = Debug|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.ActiveCfg = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.Build.0 = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|ARM64.Deploy.0 = Release|ARM64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.ActiveCfg = Release|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.Build.0 = Release|x64 + {D2002713-76DD-4F66-921E-42F9405372B1}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AB3EBD45-1882-414A-A597-95D80D86AD83} + EndGlobalSection +EndGlobal + +``` + +`MouseClassServiceCallbackTrick.vcxproj`: + +```vcxproj + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {D2002713-76DD-4F66-921E-42F9405372B1} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + MouseClassServiceCallbackMeme + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + false + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + + + sha256 + + + DriverEntry + + + + + sha256 + + + + + sha256 + + + + + + + + + + + Document + + + + + +``` + +`MouseClassServiceCallbackTrick.vcxproj.filters`: + +```filters + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Source Files + + + + + Source Files + + + +``` + +`MouseClassServiceCallbackTrick.vcxproj.user`: + +```user + + + + true + + +``` + +`README.md`: + +```md +# MouseClassServiceCallbackTrick +``` + +`main.c`: + +```c +#include + +/* + * MouseClassServiceCallbackTrick + * + * initializing data, jmps at MouHid_ReadComplete -> + * MouHid_ReadComplete will call MouseClassServiceCallback, what does in the end spoof the _ReturnAddress. + * + * this method was used safely at many platforms since of May 2021 to this day. + * + * the method did get blocked by VGK.sys 19.08.2022, (asdf144) did use it there. + * from what i have heard, he didn't get banned. + * + * maybe they are checking: (PsGetCurrentThreadId() != 0) = block input + * since all mouse DPC is done by system KiIdleLoop, ThreadId (0). + * + * otherwise should be fine, until anti-cheats start to properly validate it. + * + * + * Pros: Anti-Cheat hook does get called, and if they do compare input data to game, it will match. + * Cons: Ballsy move, its about taking risk and hoping anti-cheat is not going to check anything else than _ReturnAddress. + * + */ + +typedef int BOOL; +typedef unsigned int DWORD; +typedef ULONG_PTR QWORD; + +#pragma warning(disable : 4201) +typedef struct _MOUSE_INPUT_DATA { + USHORT UnitId; + USHORT Flags; + union { + ULONG Buttons; + struct { + USHORT ButtonFlags; + USHORT ButtonData; + }; + }; + ULONG RawButtons; + LONG LastX; + LONG LastY; + ULONG ExtraInformation; +} MOUSE_INPUT_DATA, *PMOUSE_INPUT_DATA; + + + +typedef struct _MOUSE_OBJECT +{ + PDEVICE_OBJECT mouse_device; + QWORD service_callback; + BOOL use_mouse; +} MOUSE_OBJECT, * PMOUSE_OBJECT; + + +BOOL mouse_open(void); +MOUSE_OBJECT gMouseObject; + + + +NTSYSCALLAPI +POBJECT_TYPE* IoDriverObjectType; + +NTSYSCALLAPI +NTSTATUS +ObReferenceObjectByName( + __in PUNICODE_STRING ObjectName, + __in ULONG Attributes, + __in_opt PACCESS_STATE AccessState, + __in_opt ACCESS_MASK DesiredAccess, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode, + __inout_opt PVOID ParseContext, + __out PVOID *Object + ); + + +void NtSleep(DWORD milliseconds) +{ + QWORD ms = milliseconds; + ms = (ms * 1000) * 10; + ms = ms * -1; +#ifdef _KERNEL_MODE + KeDelayExecutionThread(KernelMode, 0, (PLARGE_INTEGER)&ms); +#else + NtDelayExecution(0, (PLARGE_INTEGER)&ms); +#endif +} + +void mouse_move(long x, long y, unsigned short button_flags); +static QWORD GetSystemBaseAddress(PDRIVER_OBJECT DriverObject, const unsigned short* driver_name); +static QWORD FindPattern(QWORD module, unsigned char *bMask, char *szMask, QWORD len); + +VOID +DriverUnload( + _In_ struct _DRIVER_OBJECT* DriverObject +) +{ + UNREFERENCED_PARAMETER(DriverObject); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] MouseClassServiceCallbackTrick.sys is closed\n"); +} + +QWORD g_target_routine = 0; + +NTSTATUS DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath +) +{ + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(RegistryPath); + + if (!mouse_open()) + { + return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; + } + + + QWORD base = GetSystemBaseAddress(DriverObject, L"mouhid.sys"); + if (base == 0) + { + return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; + } + + g_target_routine = FindPattern((QWORD)base, (unsigned char*)"\x74\x54", "xx", 2); + if (g_target_routine == 0) + { + return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; + } + g_target_routine += 0x56; + + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] MouseClassServiceCallbackTrick.sys is launched\n"); + DriverObject->DriverUnload = DriverUnload; + + + for (int i = 0; i < 32; i++) { + NtSleep(100); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] Moving mouse\n"); + + mouse_move(-10, -10, 0); + } + + + return STATUS_SUCCESS; +} + +BOOL mouse_open(void) +{ + // https://github.com/nbqofficial/norsefire + + if (gMouseObject.use_mouse == 0) { + + UNICODE_STRING class_string; + RtlInitUnicodeString(&class_string, L"\\Driver\\MouClass"); + + + PDRIVER_OBJECT class_driver_object = NULL; + NTSTATUS status = ObReferenceObjectByName(&class_string, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&class_driver_object); + if (!NT_SUCCESS(status)) { + gMouseObject.use_mouse = 0; + return 0; + } + + UNICODE_STRING hid_string; + RtlInitUnicodeString(&hid_string, L"\\Driver\\MouHID"); + + + PDRIVER_OBJECT hid_driver_object = NULL; + + status = ObReferenceObjectByName(&hid_string, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&hid_driver_object); + if (!NT_SUCCESS(status)) + { + if (class_driver_object) { + ObfDereferenceObject(class_driver_object); + } + gMouseObject.use_mouse = 0; + return 0; + } + + PVOID class_driver_base = NULL; + + + PDEVICE_OBJECT hid_device_object = hid_driver_object->DeviceObject; + while (hid_device_object && !gMouseObject.service_callback) + { + PDEVICE_OBJECT class_device_object = class_driver_object->DeviceObject; + while (class_device_object && !gMouseObject.service_callback) + { + if (!class_device_object->NextDevice && !gMouseObject.mouse_device) + { + gMouseObject.mouse_device = class_device_object; + } + + PULONG_PTR device_extension = (PULONG_PTR)hid_device_object->DeviceExtension; + ULONG_PTR device_ext_size = ((ULONG_PTR)hid_device_object->DeviceObjectExtension - (ULONG_PTR)hid_device_object->DeviceExtension) / 4; + class_driver_base = class_driver_object->DriverStart; + for (ULONG_PTR i = 0; i < device_ext_size; i++) + { + if (device_extension[i] == (ULONG_PTR)class_device_object && device_extension[i + 1] > (ULONG_PTR)class_driver_object) + { + gMouseObject.service_callback = (QWORD)(device_extension[i + 1]); + + break; + } + } + class_device_object = class_device_object->NextDevice; + } + hid_device_object = hid_device_object->AttachedDevice; + } + + if (!gMouseObject.mouse_device) + { + PDEVICE_OBJECT target_device_object = class_driver_object->DeviceObject; + while (target_device_object) + { + if (!target_device_object->NextDevice) + { + gMouseObject.mouse_device = target_device_object; + break; + } + target_device_object = target_device_object->NextDevice; + } + } + + ObfDereferenceObject(class_driver_object); + ObfDereferenceObject(hid_driver_object); + + if (gMouseObject.mouse_device && gMouseObject.service_callback) { + gMouseObject.use_mouse = 1; + } + + } + + return gMouseObject.mouse_device && gMouseObject.service_callback; +} + +VOID MouseClassServiceCallbackTrick(QWORD rdi_buffer, QWORD rbp_buffer, QWORD target_address); +void mouse_move(long x, long y, unsigned short button_flags) +{ + char rdi_buffer[0x500]; + char rbp_buffer[0x100]; + + for (QWORD i = 0; i < 0x500; i++) + rdi_buffer[i] = 0; + + for (QWORD i = 0; i < 0x100; i++) + rbp_buffer[i] = 0; + + MOUSE_INPUT_DATA *mid = (MOUSE_INPUT_DATA*)&rdi_buffer[0x160]; + *(QWORD*)&rdi_buffer[0x178] = (QWORD)(PMOUSE_INPUT_DATA)mid + 1; + + mid->LastX = x; + mid->LastY = y; + mid->ButtonFlags = button_flags; + mid->UnitId = 1; + + *(QWORD*)&rdi_buffer[0xE0] = (QWORD)gMouseObject.mouse_device; + *(QWORD*)&rdi_buffer[0xE8] = (QWORD)gMouseObject.service_callback; + MouseClassServiceCallbackTrick( (QWORD)rdi_buffer, (QWORD)rbp_buffer, (QWORD)g_target_routine ); +} + +#pragma warning(disable : 4201) +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + short LoadCount; + short TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + }; + }; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + }; + PVOID* EntryPointActivationContext; + PVOID PatchInformation; + LIST_ENTRY ForwarderLinks; + LIST_ENTRY ServiceTagLinks; + LIST_ENTRY StaticLinks; +} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; + +static QWORD GetSystemBaseAddress(PDRIVER_OBJECT DriverObject, const unsigned short* driver_name) +{ + PLDR_DATA_TABLE_ENTRY ldr = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection; + for (PLIST_ENTRY pListEntry = ldr->InLoadOrderLinks.Flink; pListEntry != &ldr->InLoadOrderLinks; pListEntry = pListEntry->Flink) + { + PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + if (pEntry->BaseDllName.Buffer && wcscmp(pEntry->BaseDllName.Buffer, driver_name) == 0) { + + return (QWORD)pEntry->DllBase; + } + } + return 0; +} + +typedef unsigned char BYTE; + +static BOOLEAN bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) +{ + for (; *szMask; ++szMask, ++pData, ++bMask) + if ((*szMask == 1 || *szMask == 'x') && *pData != *bMask) + return 0; + return (*szMask) == 0; +} + +static QWORD FindPatternEx(UINT64 dwAddress, QWORD dwLen, BYTE *bMask, char * szMask) +{ + if (dwLen <= 0) + return 0; + for (QWORD i = 0; i < dwLen; i++) + if (bDataCompare((BYTE*)(dwAddress + i), bMask, szMask)) + return (QWORD)(dwAddress + i); + return 0; +} + + +#ifdef _KERNEL_MODE +typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header + unsigned short e_magic; // Magic number + unsigned short e_cblp; // Bytes on last page of file + unsigned short e_cp; // Pages in file + unsigned short e_crlc; // Relocations + unsigned short e_cparhdr; // Size of header in paragraphs + unsigned short e_minalloc; // Minimum extra paragraphs needed + unsigned short e_maxalloc; // Maximum extra paragraphs needed + unsigned short e_ss; // Initial (relative) SS value + unsigned short e_sp; // Initial SP value + unsigned short e_csum; // Checksum + unsigned short e_ip; // Initial IP value + unsigned short e_cs; // Initial (relative) CS value + unsigned short e_lfarlc; // File address of relocation table + unsigned short e_ovno; // Overlay number + unsigned short e_res[4]; // Reserved words + unsigned short e_oemid; // OEM identifier (for e_oeminfo) + unsigned short e_oeminfo; // OEM information; e_oemid specific + unsigned short e_res2[10]; // Reserved words + LONG e_lfanew; // File address of new exe header + } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +typedef struct _IMAGE_FILE_HEADER { + unsigned short Machine; + unsigned short NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + unsigned short SizeOfOptionalHeader; + unsigned short Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + +typedef struct _IMAGE_OPTIONAL_HEADER64 { + short Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + ULONGLONG ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + ULONGLONG SizeOfStackReserve; + ULONGLONG SizeOfStackCommit; + ULONGLONG SizeOfHeapReserve; + ULONGLONG SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; + +typedef struct _IMAGE_NT_HEADERS64 { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[8]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + unsigned short NumberOfRelocations; + unsigned short NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; +#endif + +static QWORD FindPattern(QWORD module, unsigned char *bMask, char *szMask, QWORD len) +{ + ULONG_PTR ret = 0; + PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)module; + PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE*)pidh + pidh->e_lfanew); + PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER)((BYTE*)pinh + sizeof(IMAGE_NT_HEADERS64)); + + for (USHORT sec = 0; sec < pinh->FileHeader.NumberOfSections; sec++) + { + + if ((pish[sec].Characteristics & 0x00000020)) + { + QWORD address = FindPatternEx(pish[sec].VirtualAddress + (ULONG_PTR)(module), + pish[sec].Misc.VirtualSize - len, bMask, szMask); + + if (address) { + ret = address; + + break; + } + } + + } + return ret; +} + + +``` + +`mouse.asm`: + +```asm +.code + +MouseClassServiceCallbackTrick proc + + push rbp + push rbx + push rsi + push rdi + push r12 + push r13 + push r14 + push r15 + + mov rbp, rdx + sub rsp, 58h + mov rdi, rcx + + jmp r8 +MouseClassServiceCallbackTrick endp + +end + + +``` \ No newline at end of file diff --git a/archive/gmh5225/AI-FPS-b00m-h3adsh0t.txt b/archive/gmh5225/AI-FPS-b00m-h3adsh0t.txt new file mode 100644 index 00000000..c2d189b9 --- /dev/null +++ b/archive/gmh5225/AI-FPS-b00m-h3adsh0t.txt @@ -0,0 +1,948 @@ +Project Path: arc_gmh5225_AI-FPS-b00m-h3adsh0t_db6tbh1x + +Source Tree: + +```txt +arc_gmh5225_AI-FPS-b00m-h3adsh0t_db6tbh1x +├── CODE_OF_CONDUCT.md +├── README.md +├── _config.yml +├── azure-pipelines.yml +├── b00m-h3adsh0t +├── b00m-h3adsh0t.cfg +├── calcangle.h +├── gitchbox.cpp +├── glitchbox.h +├── main.cpp +├── memory_searcher.cpp +├── playerdata.h +├── readme-images +│ ├── boom headshot header.png +│ ├── gameplay1.png +│ ├── gameplay2.png +│ ├── logo1.png +│ └── logo2.png +├── test +└── train_game + ├── GAME.cfg + ├── GAME.names + ├── GAME.screenshots + ├── GAME.weights + └── GAME_last.weights + +``` + +`CODE_OF_CONDUCT.md`: + +```md +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at low.lucyy@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq + +``` + +`README.md`: + +```md + +# b00m-h3adsh0t! 🔷 +**Neural Network Configurable Aimbot for First-Person-Shooter Games in C/C++** *Note: Aimbots are cheats and illegal in gaming leagues. This repo is solely for educational purposes only.* + +> **┬┴┬┴┬┴┤ (҂ ` ロ ´)︻デ═一____________________\(º □ º )/ ├┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴** + +
+ + [![Status](https://img.shields.io/badge/status-work--in--progress-success.svg)]() + [![GitHub Issues](https://img.shields.io/github/issues/lucylow/b00m-h3adsh0t.svg)](https://github.com/lucylow/Mrs.Robot/issues) + [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/lucylow/b00m-h3adsh0t.svg)](https://github.com/lucylow/b00m-h3adsh0t/pulls) + [![License](https://img.shields.io/bower/l/bootstrap)]() + +
+ +## Table of Contents 🔷 + +* [Motivation](https://github.com/lucylow/b00m-h3adsh0t#motivation) +* [Aimbot Neural Network](https://github.com/lucylow/b00m-h3adsh0t#aimbot-neural-network-) +* [Neural Network Model Training Recognition](https://github.com/lucylow/b00m-h3adsh0t#neural-network-model-training-recognition-) +* [How a normal aimbot works](https://github.com/lucylow/b00m-h3adsh0t#how-a-normal-aimbot-works-) +* [How b00m-h3adsh0t works](https://github.com/lucylow/b00m-h3adsh0t#how-b00m-h3adsh0t-works-) +* [Client-Server Backend Implementation](https://github.com/lucylow/b00m-h3adsh0t#client-server-backend-implementation-) +* [Security and Efficiency Game Server](https://github.com/lucylow/b00m-h3adsh0t#security-and-efficiency-game-server-) +* [Player Behavior Statistics](https://github.com/lucylow/b00m-h3adsh0t#player-behavior-statistics-) +* [User Privacy](https://github.com/lucylow/b00m-h3adsh0t#user-privacy-) +* [Player Attacks](https://github.com/lucylow/b00m-h3adsh0t#player-attacks-) +* [Glitches and Modifications](https://github.com/lucylow/b00m-h3adsh0t#glitches-and-modifications-) +* [Conclusion](https://github.com/lucylow/b00m-h3adsh0t#conclusion-) +* [References](https://github.com/lucylow/b00m-h3adsh0t#references-) + +## Motivation +* **B00m-h3adsh0t is a game bot software for first-person shooting (FPS) games** where players need to constantly move, think, strategize, and shoot enemies all at once. **Aimbot uses game data to automatically shoot at the heads of energy targets.** + +* **Personal motivation to learn C++ compiler programming language, object oriented programing, and how a FPS game executes on an operating system**. B00m-H3adsh0t is 100% written in C++ with Visual Studio compiler providing a very fast, and efficient framework with scripting support such that the framework uses a consistent object-oriented design + + ![](https://github.com/lucylow/b00m-h3adsh0t/blob/master/readme-images/logo2.png) + + *Image. “Turn off Lucy's b00m-h3adsh0t aimbot you noob K/D ratio hacker!"* + + + + +## Aimbot Neural Network 🔷 + +* **Trained by neural network (NN) with customizable predictions and dynamic speed settings** +* Select which FPS game you will use +* **Engine-Aim with colored models:** + * Hook into the FPS game engine to use actual game data to auto-aim without altering gaming files + * Code won't work by itself because we need a handle to the game + * Modifies memory of RAM half-life runs on + * Gathers information from current game and pixel location + + ![](https://github.com/lucylow/b00m-h3adsh0t/blob/master/readme-images/gameplay1.png) + + *Image. Custom training mode on the aimbot with a range of functionalities* + +* **Custom training mode** + * Leverage neural network to detect objects for object recognition using computer vision algorithms + * Train with range of distances, lights, and angles for best possible recognition + +## Neural Network Model Training Recognition 🔷 + +**Deep Reinforcement Learning** + * Allows bot to learn how to aim by interacting with its unknown 3D environment + * Bot receives a reward if it correctly kills an enemy, hence the name b00m-h3adsh0. If the bot dies, it gets a penalty. + * For each step, bot observes the current states Ot of the environment and decides of an action + * Observes reward signal where the goal of the agent is to find a policy that maximizes the expected sum of discounted rewards + * Game states are partially observable + +**Q-Learning Adaptation** + * Used a Q-Learning adaptation for Deep Learning to train the autonomous agent + * Inputs are screenshots of the fps game (pixels) + * Deep reinforcement learning allows bot to learn game features simultaneously along with minimizing a Q-learning objective + +**Dynamic Bayesian Network** + * Common for aimbot detections in FPS games + * Used for probabilistic modeling and inference in discrete-time + * Implementation options: + * libDAI - A free and open source C++ library for Discrete Approximate Inference in graphical models (C++) + * Mocapy++ (C++) - A toolkit for inference and learning in dynamic Bayesian networks + + +## How a normal aimbot works 🔷 + +* **Aimbot can be easily toggled on and off using the mouse or keyboard** +* Recognizes game objects in a certain range, then aims at the objects using game physics + +* **Memory Searcher with Cheat Engine** + * Understand the memory storage structures within a game + * Searching memory to find the values of the player classes such as player coordinates, health, mouse x,y coordinates, etc. + * Use **Cheat engine** to find addresses (programs that scans memory depending on the search details you give it and returns the memory addresses) + * Base address of "client.dll" (int or DWORD) + * Read and write to the game memory + * Call the functions **ReadProcessMemory (RPM)** and **WriteProcessMemory (WPM)** + * Use multi-level pointers to access information to playerObjectAddress +* **CalcAngle** + * Needed to calculate angle functions for aimbot since everything is based on game coordinates + * Takes two 3D positions in source and distance, and outputs the angle to distance in angles + * Pass in the local player's eye position into src, the target's head in dst, and then set the view angles from angles +* **Call Game Functions** + * For internal hacks where we need to inject DLL + * C++ programs call funtion by address via function pointers + * **Traceline and RayTrace** commonly used in aimbots: + * Draws a line between your player and another player + * Checks if there are objects in the way + * If there are no collisions between you and your target your aimbot should aim and shoot at that target +* **Game Player Detection** + * FPS game memory contains the **(X,Y,Z) coordinates of each player for rendering** + * Aimbot scans memory locations for this information + * **Gain access to two key positions** - the player and enemies coordinates + * Subtracting the two positions as vectors == the vector between the two + * Calculate the **angle from the player's current vector to the desired angle vector** + +* **Aim Automatically** + * Inject information directly to the game + * DLL injection + * **Overwriting current FPS game aim functions** + * Patching in-place the Direct3D or OpenGL DLL + * Examining the **functions calls to draw geometry** + * Insert own geometry functions (for things like wall-hacks or glitches) + * Fine-tune with constants adjusting for any **dynamic data structure moving players** around on you + + +## How b00m-h3adsh0t works 🔷 + +* **Neural Network** + + * Program takes **multiple screenshots** to recognize objects + * Different distances, lights, angles for best possible recognition + * Output - program writes in **cfg file** + * Batch = 1 + * Subdivision = 1 for testing + * Graph of **Training/Validation Set** + * Graph x vs y + * Error Rate vs Number of Iterations in Training Set + +* **Training Depenencies - Trained Files for Games** + + * Use **b00m-h3adsh0t.cfg file** to change the resolution range for object recognition + * Train Files Folder + * Darknet folder/subfolders + * Data or back up + * GAME.names + * GAME.cfg + * GAME_last.weights + * GAME.weights +## Client-Server Backend Implementation 🔷 + +* Computer has to display the gameplay to the user by rendering the whole map and every player in it + +* **Client–Server Model Method** + + * Model instantaneously calculating/sending game results + * **Client sessions run synchronously with aimbot server with user input data** + * Run aimbot purely on game server + * Run server mirrors client gameplay and continuously validates each game state + +* **Modifying Game Rules World Method** + + * Aimbot targets servers with no rule enforcement or data integrity + * **Synchronize all client data with information about all of the other clients** + * Reveals where all the players in the game are via (X,Y,Z) coordinates + * Reveals user game states with information on player names, position, clip ammo, ammo count, health, class, weapons, frame rate and more. + * Data from client will allow player to break game rules, manipulate server, or manipulate other clients + +## Security and Efficiency Game Server 🔷 + +* Server responsible for information security and enforcing game rules + +* **Sending Game World State needed for Immediate Display** + * Results in client lag under bandwidth constraints + +* **Sending the Player the Entire World State** + * Results in faster display for player under the same bandwidth constraints + * Exposes data to interception or manipulation + * Trade-off between security and efficiency + +## Player Behavior Statistics 🔷 +Refer to playerdata.h file + +* **Aimbot Evaluation Metrics** + * Compare human player with b00m-h3eadsh0t agent + * K/D Ratio to compare ratio of kills to deaths + * Single player vs multi-player games + +* **Pattern Detection Systems** + * Scan player's hard drives for known cheat code or programs + * Scan player's system memory for known cheat code or programs + * Labor-intensive to constantly track down cheats and update detection patterns + +* **Anti–Cheat Method** + * Guaranteed to work on all end–user system configurations + * Reduce the amount of false positives + +* **Player Behavior Anomalie Detection** + * Detected by statistically analyzing game events + * Data sent by client to server by statistical detection systems + * Add human element of supervision system (community/admin team looks over player statistics) + + ![](https://github.com/lucylow/b00m-h3adsh0t/blob/master/readme-images/gameplay2.png) + + *Image. Unusual player behavior leads to clientside creating then uploading a gamer report* + +## User Privacy 🔷 + +* **End–users concerned with privacy issues and "Never trust the client" is common saying with game developers** +* VAC (Valve Anti-Cheat) accessing browsing history +* User privacy compromised with packet interception/manipulation +* **Man-in-the-Middle Attack** + * Reverse engineer the network packet formatting + * Security of game circumvented by intercepting or manipulating data in real-time while transit from the client to the server or vice versa + * Performed on client machine itself or via external communication proxy + * Can provide player positions and other useful related information + * Forged packets sent to server to move the player, shoot, or other game actions + +## Player Attacks 🔷 + +* **Select button to attack and enable/disable training mode** +* Custom zooming control with scroll wheel +* Custom crosshairs +* Laser sight +* Trigger bot +* Move speed +* Ammo count +* Player radar +* Name-tag display to detect players +* Auto shoot/rapid fire + * Most fps games limit the rate weapons are fired regardless of how fast a player presses buttons + * Binding the firing button to the scroll wheel of a mouse + * Macro setting that will simulate rapid key presses automatically + * Set aiming speed and shooting delay +* Auto clicker for semi automatic weapons +* Dynamic recoil control + * Remove gun revoil game element + * Control bullet spread + * Correcting for bullet drop + +## Glitches and Modifications 🔷 + +* Wall hacks + * Glitches with game surfaces + * Graphics driver modifications that ignore depth checking + * Draw all objects on the screen +* Reduced flash +* Correcting for ping/lag +* Resolution range +* Pixel memory hack +* Transparent buildings, ceilings, obstacles, and trees + * Remove visual elements of the game + * Ex Replace opengl32.dll with one that would render polygons transparent + +* Display enemy lines +* Extrasensory perception (ESP) + * Display all the enemy positions on the map + * Glowing or lighted players, weapons, and loot. + * See all players at all times and plan ahead before making a kill + * Show all information ex: player names, position, clip ammo, ammo count, health, class, weapons, frame rate and more +## Conclusion 🔷 + +B00m-h3adsh0t! is a single architecture neural network configurable aimbot for first-person shooting (FPS) games. We introduced a method to augment a deep reinforcement q-learning model with high-level game information, and feature implementation. We showed that b00m-h3adsh0t! model is able to outperform built-in bots as well as human players and demonstrated the generalizability of our model to do game glitches and modifications. + + +------- + +## References 🔷 + +* Machine Learning Paper. Aimbot Detection in Online FPS Games Using a Heuristic Method Based on Distribution Comparison Matrix: https://link.springer.com/chapter/10.1007/978-3-642-34500-5_77 +* Exploting supervised learning techniques on game server collecting game data with decision trees, Naive Bayes, random forest, neural networks, and support vector machines. https://ieeexplore.ieee.org/abstract/document/6032016 +* Multiple classificatoin system for neural networks http://ceur-ws.org/Vol-1659/paper7.pdf +* Bayesian Imitation Learning the ROute to Belivable Gamebots. https://www.researchgate.net/profile/Christian_Bauckhage/publication/258510478_Is_Bayesian_imitation_learning_the_route_to_believable_gamebots/links/0c960539de8012b04e000000/Is-Bayesian-imitation-learning-the-route-to-believable-gamebots.pdf +* Towards a Fair n Square Aimbot. Machine Learning techniques for spatio-temporal improvements to aimbots. http://vampire-project.de/files/papers/Bauckhage2004-TAF.pdf +* Server side machine learning classifiers for anti-cheating in games using game logs https://ieeexplore.ieee.org/abstract/document/6633617 +* Bayesian network paper on aimbot behavior detection. http://www.cs.cuhk.edu.hk/~cslui/PUBLICATION/detect_cheat.pdf +* Classifier systems for controlling NPCs in games. https://pdfs.semanticscholar.org/68cf/3f5b16c452b004d986dcbdefa6fc28fa1c9b.pdf +* Game bot detection. Detecting user injections https://dl.acm.org/citation.cfm?id=1653694 +* C++ code for applications of Dynamic Bayesian Network https://github.com/wengjn/MatlabDBN +* DBN++ Data Structures and Algorithms in C++ for Dynamic Bayesian Networks https://github.com/thiagopbueno/dbn-pp +* Paper Dynamic Bayesian Neytworks https://www.cs.ubc.ca/~murphyk/Papers/dbnchapter.pdf +* Paper A Bayesian Model for Plan Recognition in RTS (Real Time Strategy) Games https://www.aaai.org/ocs/index.php/AIIDE/AIIDE11/paper/viewFile/4062/4416 +* Learning to Shoot in First Person Shooter Games by Stabilizing Actions and Clustering Rewards for Reinforcement Learning. https://arxiv.org/pdf/1806.05117.pdf +* CS:GO external hack base https://github.com/NullTerminatorr/NullBase +* FastML. Solve the cheaters problem in Counter Strike, with or without machine learning +http://fastml.com/how-to-solve-the-cheaters-problem-in-counter-strike-with-or-without-machine-learning/ + +``` + +`_config.yml`: + +```yml +theme: jekyll-theme-hacker +``` + +`azure-pipelines.yml`: + +```yml +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: +- master + +pool: + vmImage: 'ubuntu-latest' + +steps: +- script: echo Hello, world! + displayName: 'Run a one-line script' + +- script: | + echo Add other tasks to build, test, and deploy your project. + echo See https://aka.ms/yaml + displayName: 'Run a multi-line script' + +``` + +`b00m-h3adsh0t.cfg`: + +```cfg +// b00m-h3adsh0t.cfg Config File +// Web: https://github.com/lucylow/b00m-h3adsh0t + + +// Autoassign # bots (defaults to 0) +set b00m-h3adsh0t "0" + +// Add # bots to allies (defaults to 0) +set b00m-h3adsh0t_allies "0" + +// Add # bots to axis (defaults to 0) +set b00m-h3adsh0t_axis "0" + +// Bot skill level, value from 0.1 to 1.0 (defaults to 1.0) +set b00m-h3adsh0t_skill "1.0" + +// Set to 0 - Original (slower speed and more walking) or 1 - Faster (Run & Gun) or 2 - Both (defaults to 2) +set b00m-h3adsh0t_playstyle "2" + +// Set to 0 to disable bots choosing a random perk upon spawn. +set b00m-h3adsh0t_useperks "1" + +// Set to 1 to enable bots to use the additional character models such as Capt. Price. (defaults to 0) +b00m-h3adsh0t_modelchoice "0" + +// Set to 0 to stop bots using battlechatter (talking during game) (defaults to 1) +set b00m-h3adsh0t_chatter "1" + +// Set to 1 to disable bots using UAV, Airstrike and Helicopter (defaults to 0) +set b00m-h3adsh0t_dewards "0" + +// Set to 1 and bots will drop grenades/RPGs for the player to pick up and restock their grenade/RPG ammunition (defaults to 0) +set b00m-h3adsh0t_grenadepickup "0" + +// Set to 1 to be instant level 55 and have all weapons unlocked (defaults to 0) +set b00m-h3adsh0t_XPcheat "0" + +// Restarts the listen server after (defaults to 2) +set b00m-h3adsh0t_roundCount "2" + + +// Required to fix bug with UAV, Airstrike and Helicopter. Leave this here. +set b00m-h3adsh0t_mode "normal" + +``` + +`calcangle.h`: + +```h +Vector CAimbot::CalcAngle( Vector& src, Vector& dst ) +{ +Vector vAngle; +Vector delta( (src.X-dst.X), (src.Y-dst.Y), (src.Z-dst.Z) ); +double hyp = sqrt( delta.X*delta.X + delta.Y*delta.Y ); + +vAngle.X = (float)(asinf( (delta.Z + 64.06f) / hyp ) * 57.295779513082f); +vAngle.Y = (float)(atanf( delta.Y / delta.X ) * 57.295779513082f); +vAngle.Z = 0.0f; + +if(delta.X >= 0.0) +vAngle.Y += 180.0f; + +return vAngle; +} + + +``` + +`gitchbox.cpp`: + +```cpp +#include "Drawing.h" + + +void Drawing::DrawBorderBox( int x, int y, int w, int h, int thickness, D3DCOLOR Colour, IDirect3DDevice9 *pDevice) +{ + //Top horiz line + DrawFilledRect( x, y, w, thickness, Colour, pDevice ); + //Left vertical line + DrawFilledRect( x, y, thickness, h, Colour, pDevice ); + //right vertical line + DrawFilledRect( (x + w), y, thickness, h, Colour, pDevice ); + //bottom horiz line + DrawFilledRect( x, y + h, w+thickness, thickness, Colour, pDevice ); +} + + +//We receive the 2-D Coordinates the color and the device we want to use to draw those colors with +void Drawing::DrawFilledRect(int x, int y, int w, int h, D3DCOLOR color, IDirect3DDevice9* dev) +{ + //We create our rectangle to draw on screen + D3DRECT BarRect = { x, y, x + w, y + h }; + //We clear that portion of the screen and display our rectangle + dev->Clear(1, &BarRect, D3DCLEAR_TARGET | D3DCLEAR_TARGET, color, 0, 0); +} + + +void Drawing::Draw_Text(LPCSTR TextToDraw, int x, int y, D3DCOLOR Colour, LPD3DXFONT m_font) +{ + // Create a rectangle to indicate where on the screen it should be drawn + RECT rct = {x- 120, y, x+ 120, y + 15}; + + // Draw some text + m_font->DrawText(NULL, TextToDraw, -1, &rct, DT_NOCLIP, Colour ); +} + + + +``` + +`glitchbox.h`: + +```h +#pragma once + +#include "d3d9.h" + +static class Drawing +{ +public: + static void Draw_Text(LPCSTR TextToDraw, int x, int y, D3DCOLOR Colour, LPD3DXFONT m_font); + static void DrawFilledRect(int x, int y, int w, int h, D3DCOLOR color, IDirect3DDevice9* d3dDevice); + static void DrawBorderBox( int x, int y, int w, int h, int thickness, D3DCOLOR Colour, IDirect3DDevice9 *d3dDevice); +}; + + +``` + +`main.cpp`: + +```cpp +#include +#include +#include "main.h" +#include +#include +using namespace std; + + +CHackProcess fProcess; +using namespace std; + +#define F6_Key 0x75 +#define RIGHT_MOUSE 0x02 + +int NumOfPlayers = 32; + +//Relative offsets: +const DWORD dw_PlayerCountOffs = 0x5EF6BC;//Engine.dll +const DWORD Player_Base = 0x4C6708; +const DWORD dw_m_angRotation = 0x47F1B4; //ViewAngles - find by moving our mouse around, look for changed/unchanged value, or use cl_pdump 1 + +// Entity offsets +const DWORD dw_mTeamOffset = 0x9C; +const DWORD dw_Health = 0x94; +const DWORD dw_Pos = 0x260; + +const DWORD EntityPlayer_Base = 0x4D3904;//Entitylist relative offset +const DWORD EntityLoopDistance = 0x10; //Distance in bytes between each ent + +struct MyPlayer_t +{ + DWORD CLocalPlayer; //Address of our ent + int Team; + int Health; + float Position[3]; + void ReadInformation() + { + //Get address of entity + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(fProcess.__dwordClient + Player_Base), &CLocalPlayer, sizeof(CLocalPlayer), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CLocalPlayer + dw_mTeamOffset), &Team, sizeof(Team), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CLocalPlayer + dw_Health), &Health, sizeof(Health), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CLocalPlayer + dw_Pos), &Position, sizeof(float[3]), 0); + //Get Number of players + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(fProcess.__dwordEngine + dw_PlayerCountOffs), &NumOfPlayers, sizeof(int), 0); + } +}MyPlayer; + +//struct for targets +struct TargetList_t +{ + float Distance; + float AimbotAngle[3]; + + TargetList_t() {} //default contructor + + TargetList_t(float aimbotAngle[], float myCoords[], float enemyCoords[]) + { + Distance = Get3dDistance(myCoords[0], myCoords[1], myCoords[2], + enemyCoords[0], enemyCoords[1], enemyCoords[2]); + + //set aimbot angles for the ent + AimbotAngle[0] = aimbotAngle[0]; + AimbotAngle[1] = aimbotAngle[1]; + AimbotAngle[2] = aimbotAngle[2]; + } + + float Get3dDistance(float myCoordsX, float myCoordsZ, float myCoordsY, + float eNx, float eNz, float eNy) + { + return (float)sqrt( + pow(double(eNx - myCoordsX), 2.0) + + pow(double(eNy - myCoordsY), 2.0) + + pow(double(eNz - myCoordsZ), 2.0)); + } +}; + +//Struct for other players +struct PlayerList_t +{ + DWORD CBaseEntity; + int Team; + int Health; + float Position[3]; + float AimbotAngle[3]; + char Name[39]; + + void ReadInformation(int Player) + { + //Get Address of Entity + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(fProcess.__dwordClient + EntityPlayer_Base + (Player * EntityLoopDistance)), &CBaseEntity, sizeof(DWORD), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CBaseEntity + dw_mTeamOffset), &Team, sizeof(int), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CBaseEntity + dw_Health), &Health, sizeof(int), 0); + ReadProcessMemory(fProcess.__HandleProcess, (BYTE*)(CBaseEntity + dw_Pos), &Position, sizeof(float[3]), 0); + } +}PlayerList[32]; + +// Compare distance when sorting the array of Target Enemies "sort predicate" +struct CompareTargetEnArray +{ + bool operator() (TargetList_t & lhs, TargetList_t & rhs) + { + return lhs.Distance < rhs.Distance; + } +}; + +void CalcAngle(float *src, float *dst, float *angles) +{ + double delta[3] = { (src[0] - dst[0]), (src[1] - dst[1]), (src[2] - dst[2]) }; + double hyp = sqrt(delta[0] * delta[0] + delta[1] * delta[1]); + angles[0] = (float)(asinf(delta[2] / hyp) * 57.295779513082f); + angles[1] = (float)(atanf(delta[1] / delta[0]) * 57.295779513082f); + angles[2] = 0.0f; + + // Normalize angle + if (delta[0] >= 0.0) + { + angles[1] += 180.0f; + } +} + +void Aimbot() +{ + // Declare our target list to define our victims through a dynamic array + TargetList_t* TargetList = new TargetList_t[NumOfPlayers]; + + // Loop through all our players and retrieve their information + int targetLoop = 0; + for (int i = 0; i < NumOfPlayers; i++) + { + PlayerList[i].ReadInformation(i); + + // Skip if they're my teammates. + if (PlayerList[i].Team == MyPlayer.Team) continue; + + // Skip players without health such as bad ents + if (PlayerList[i].Health < 2) continue; + + // PlayerList[i].Position[2] -= 10; + CalcAngle(MyPlayer.Position, PlayerList[i].Position, PlayerList[i].AimbotAngle); + + // Populate array of targets with only good targets + TargetList[targetLoop] = TargetList_t(PlayerList[i].AimbotAngle, MyPlayer.Position, PlayerList[i].Position); + + // Increment to advance the array for the next iteration + targetLoop++; + } + + //Aim only if we have any enemies + if (targetLoop > 0) + { + //SORT ENEMIES ARRAY BY DISTANCE by using our sort predicate + std::sort(TargetList, TargetList + targetLoop, CompareTargetEnArray()); + + //AIM at the closest ent, by default aim at ALL times, if you right click hold it switches it off + if (!GetAsyncKeyState(0x2)) + { + WriteProcessMemory(fProcess.__HandleProcess, (BYTE*)(fProcess.__dwordEngine + dw_m_angRotation), TargetList[0].AimbotAngle, 12, 0); + } + + } + // Reset the loop counter + targetLoop = 0; + + delete[] TargetList; //DELETE OUR ARRAY and clear memory +} + +int main() +{ + fProcess.RunProcess(); // Waiting for CSS...... + cout << "Game found! Running b00m h3adsh0t aimbot." << endl; + + //Exit if the F6 key is pressed + while (!GetAsyncKeyState(F6_Key)) + { + MyPlayer.ReadInformation(); + Aimbot(); + } +} + +``` + +`memory_searcher.cpp`: + +```cpp +DWORD pLocalPlayer = { 0x509B74 }; +DWORD playerObjectAddress = 0; + +// Base address of "client.dll" (int or DWORD) +int ClientDLL = (int)GetModuleHandleA("client.dll"); + +// Find actual EntityBase address +EntityBase = *(int*)(ClientDLL + 0x9D3C6C); + +// Read the memory address of the EntityBase+0x23C4 to find the ID of the entity in the crosshair +int in_cross = *(int*)(EntityBase + 0x23C4); + +// ReadProcessMemory reads memory from a given process +ReadProcessMemory(handleToGame, (LPCVOID)addressToRead, &variableToStoreReadInformation, sizeof(variableToStoreReadInformation), NULL); + +// WriteProcessMemory writes memory to a given process +WriteProcessMemory(handleToGame, (LPVOID)addressToWriteTo, &variableContainingValueToWrite, sizeof(variableContainingValueToWrite), NULL); + + +``` + +`playerdata.h`: + +```h +#pragma once +#include + +// 3D data for each player +// Memory addresses within "PlayerData" + + class Vect3d + { + public: + float x; + float y; + float z; + + Vect3d(float _x, float _y, float _z) + { + x = _x; + y = _y; + z = _z; + } + + Vect3d() + { + } + + float length() + { + return (float)sqrt(x * x + y * y + z * z); + } + + float dotproduct(Vect3d dot) + { + return (x * dot.x + y * dot.y + z * dot.z); + } + + }; + + struct Color + { + public: + short R; + short G; + short B; + + Color() + { + + } + Color(short r, short g, short b) + { + R = r; + G = g; + B = b; + } + + }; + + class PlayerDataVec + { + public: + float xMouse; + float yMouse; + int isValid; + float xPos; + float yPos; + float zPos; + int isAlive; + int clientNum; + Color color; + char name[16]; + int pose; + int team; + bool visible; + int isInGame; + int health; + + Vect3d VecCoords() + { + Vect3d vec(xPos, zPos, yPos); + return vec; + } + }; + +``` + +`train_game/GAME.cfg`: + +```cfg +GAME.cfg + +Output - program writes in cfg file + + Batch = 1 + Subdivision = 1 for testing + +``` + +`train_game/GAME.names`: + +```names +GAME.names + +``` + +`train_game/GAME.screenshots`: + +```screenshots +void GetScreenShot(void) +{ + int x1, y1, x2, y2, w, h; + + // get screen dimensions + x1 = GetSystemMetrics(SM_XVIRTUALSCREEN); + y1 = GetSystemMetrics(SM_YVIRTUALSCREEN); + x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN); + y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN); + w = x2 - x1; + h = y2 - y1; + + // copy screen to bitmap + HDC hScreen = GetDC(NULL); + HDC hDC = CreateCompatibleDC(hScreen); + HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h); + HGDIOBJ old_obj = SelectObject(hDC, hBitmap); + BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY); + + // save bitmap to clipboard + OpenClipboard(NULL); + EmptyClipboard(); + SetClipboardData(CF_BITMAP, hBitmap); + CloseClipboard(); + + // clean up + SelectObject(hDC, old_obj); + DeleteDC(hDC); + ReleaseDC(NULL, hScreen); + DeleteObject(hBitmap); + + // screenshot to jpg and save to stream + image.Attach(hBitmap); + image.Save(stream, Gdiplus::ImageFormatJPEG); + IStream_Size(stream, &liSize); + DWORD len = liSize.LowPart; + IStream_Reset(stream); + buf.resize(len); + IStream_Read(stream, &buf[0], len); + stream->Release(); + + // put image in the file + std::fstream fi; + fi.open(path, std::fstream::binary | std::fstream::out); + fi.write(reinterpret_cast(&buf[0]), buf.size() * sizeof(BYTE)); + fi.close(); +} + +``` + +`train_game/GAME.weights`: + +```weights +GAME.weights + +``` + +`train_game/GAME_last.weights`: + +```weights +GAME_last.weights + +``` \ No newline at end of file