├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── README.md ├── UnhookMe.sln ├── UnhookMe ├── PE.cpp ├── PE.h ├── UnhookMe.cpp ├── UnhookMe.vcxproj ├── UnhookMe.vcxproj.filters ├── UnhookMe.vcxproj.user ├── hexdump.hpp ├── resolver.cpp ├── resolver.h └── usings.h ├── apimonitor.gif └── x64 └── Release └── UnhookMe.exe /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | Mariusz Banach (mgeeky, @mariuszbit, mb@binary-offensive.com). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mariusz Banach (mgeeky, ) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## UnhookMe - Dynamically unhooking imports resolver 2 | 3 | In the era of intrusive AVs and EDRs that introduce hot-patches to the running processes for their enhanced optics requirements, modern adversaries must have a robust tool to slide through these watchguards. The propsed implementation of dynamic imports resolver that would be capable of unhooking used functions in-the-fly is yet another step towards strengthening adversary resilience efforts. 4 | 5 | The solution I'm proposing here is to switch from using linker-resolved WinAPI imports, staying visibile in compiled executable's PE headers (Import Address Table specifically) to favor fully-dynamic approach insisting on resolving imports only in a dynamic fashion. Such dynamical resolver can be equipped with unhooking logic happening in the background, without any sort of guidance from the operator's side. 6 | 7 | 8 | --- 9 | 10 | ## Simplest usage example 11 | 12 | This is how you can ensure to call `MessageBoxW` unhooked, unmonitored: 13 | 14 | ```c++ 15 | RESOLVE(user32, MessageBoxW); 16 | _MessageBoxW(0, L"Look Ma! I'm unhooked!", L"Third - Unhooked", 0); 17 | ``` 18 | 19 | All the magic happens within `RESOLVE` macrodefintion, that constructs `ImportResolver` object named `_MessageBoxW`. 20 | 21 | --- 22 | 23 | ## Showcase 24 | 25 | ![Unhookme showcase animation](https://raw.githubusercontent.com/mgeeky/UnhookMe/master/apimonitor.gif) 26 | 27 | 28 | Here's how `UnhookMe` example works: 29 | 30 | 1. It presents us with the first `MessageBoxW` that is not subject for hooking 31 | 2. Then we hook `MessageBoxW` prologue ourselves to make it always return 0 without displaying it's message 32 | 3. Finally, we resolve `MessageBoxW` dynamically using the `UnhookingImportResolver` resolver, which will detect 33 | applied prologue patches and restore original bytes, effectively unhooking `MessageBoxW` functionality. 34 | 35 | In the meantime of popping message boxes, these are the loglines printed to console's stdout: 36 | 37 | ``` 38 | [~] Resolved symbol kernel32.dll!CreateFileA 39 | [~] Resolved symbol kernel32.dll!ReadProcessMemory 40 | [~] Resolved symbol kernel32.dll!MapViewOfFile 41 | [~] Resolved symbol kernel32.dll!VirtualProtectEx 42 | [#] Found trampoline hook in symbol: MessageBoxW . Restored original bytes from file. 43 | [~] Resolved symbol user32.dll!MessageBoxW 44 | ``` 45 | 46 | --- 47 | 48 | ## How to use it? 49 | 50 | There are in total 5 C++ source code/header files that your solution need to include. However your main program file needs to include only two required headers, as detailed below. 51 | 52 | * `resolver.h` - header containing most of the `UnhookingImportResolver` implementation and handy macrodefinitions 53 | * `resolver.cpp` - source code with global options defined 54 | * `usings.h` - a one big and nasty header file containing tens of `using` type definitions for commonly used WinAPIs 55 | * `PE.cpp` - custom PE parser source code file 56 | * `PE.h` - custom PE parser header file 57 | 58 | 59 | ### Required headers 60 | 61 | Your program will require only two headers being included: 62 | 63 | ```c++ 64 | #include "usings.h" 65 | #include "resolver.h" 66 | ``` 67 | 68 | ### Global options 69 | 70 | There are couple of global options that can be changed affecting the way in which Resolver works or reports it's activity. These are defined in the very beginning of **`resolver.cpp`** file: 71 | 72 | Resolver global options: 73 | 74 | - **`globalQuietOption`** - set to true if you don't want to have any sort of output 75 | - **`globalVerboseOption`** - set to true if you want to have detailed verbose output 76 | - **`globalAntiSplicingOption`** - unhook resolved functions if they're hooked. 77 | - **`globalLogFilePath`** - where to redirect output log lines. If empty, pick stdout. 78 | 79 | ```c++ 80 | bool globalQuietOption = false; 81 | bool globalVerboseOption = true; 82 | bool globalAntiSplicingOption = true; 83 | 84 | wchar_t globalLogFilePath[MAX_PATH] = L""; 85 | ``` 86 | 87 | ### Custom API type specification 88 | 89 | In order to use Resolver a function pointer type must be first declared with `using` statement of strict form: 90 | 91 | ```c++ 92 | using fn_FunctionName = ReturnType WINAPI ( 93 | ParamType1 paramName1, 94 | ..., 95 | ParamTypeN paramNameN, 96 | ); 97 | ``` 98 | 99 | This repository comes with **`usings.h`** header file containing predefined using types for tens of popular Windows APIs. 100 | 101 | The _FunctionName_ will correspond to the WinAPI that we want to have ImportResolver resolve and that function pointer must be marked as having WINAPI call convention ( `__stdcall` on x86 and `__fastcall` on x64). The _ReturnType_ must precede `WINAPI` type modifier. 102 | 103 | 104 | ### Function resolution and usage 105 | 106 | Having function pointer type defined like specified above, we will be able to use it in the following manner: 107 | 108 | ```c++ 109 | RESOLVE(libraryName, FunctionName); 110 | ReturnType output = _FunctionName(param1, ..., paramN); 111 | ``` 112 | 113 | The macro `RESOLVE` takes care of instantiating `ImportResolver` templated object and adjust specifed library's name. 114 | 115 | Resolver introduces several more Macrodefinitions offering easy to use in various circumstances constructor invocation: 116 | 117 | ```c++ 118 | #define RESOLVE(mod, func) RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, ::globalAntiSplicingOption) 119 | #define RESOLVE_NO_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, false) 120 | 121 | #define RESOLVE_VERBOSE_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, true, true) 122 | #define RESOLVE_VERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, true, false) 123 | #define RESOLVE_NOVERBOSE_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, true) 124 | #define RESOLVE_NOVERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, false) 125 | ``` 126 | 127 | Resolver's constructor: 128 | 129 | ```c++ 130 | template 131 | ImportResolver( 132 | std::string dllName, 133 | std::string funcName, 134 | bool _verbose = false, 135 | bool _unhook = false, 136 | bool *_wasItHooked = nullptr 137 | ) 138 | ``` 139 | 140 | --- 141 | 142 | ## How does it work? 143 | 144 | The underlaying resolver leverages custom PE headers parser, that processes every referenced DLL module to map their exports and verify that module's PE headers integrity as well as integrity of referenced function's stub bytes. 145 | 146 | The idea is following: 147 | 148 | 1) Firstly we issue `LoadLibrary` to load referenced by the user library (the one specified as first parameter for `RESOLVE` macro) if it could not be reached through `GetModuleHandle`. 149 | 150 | 2) Then we process loaded/referenced library's PE headers, map its exports, retrieve array of exports addresses as well as compute these addresses ourselves for cross-verification. 151 | 152 | 3) If address of a routine defined in DLL's Export Address Table doesn't correspond to what we would expect, the export is considered EAT hooked. The same goes if our Executable Import Address Table (IAT) entry for that function was altered and no longer points to the correct spot in DLL's code section - then the function is considered to be IAT hooked. 153 | 154 | 4) Assuming no hooks were found so far, we fetch first N bytes of the function's prologue and compare them to what's in DLL's file stored in disk. If there is miscrepancy between bytes fetched from memory and from file - we consider function was inline patched (hot-patched). 155 | 156 | 5) If the function was considered hooked - we return original export's address (the one we computed ourselves) and/or unhook the entry. If there were patch bytes in place, we'll restore them. 157 | 158 | 6) Finally, in order to optimize resolver's performance impact - we cache all of the loaded modules imagebases and resolved functions addresses and return them from a cache (being `std::map` ) during subsequent hits. 159 | 160 | 161 | Among the problems such dynamically-unhooking resolver faced are the issues with traversing forwarded APIs (a DLL may contain Export thunk saying that this function is not implemented in this module, but it is in another one) - which although this implementation has support for, sometimes it brokes its traversal logic. 162 | 163 | 164 | 165 | --- 166 | 167 | ### ☕ Show Support ☕ 168 | 169 | This and other projects are outcome of sleepless nights and **plenty of hard work**. If you like what I do and appreciate that I always give back to the community, 170 | [Consider buying me a coffee](https://github.com/sponsors/mgeeky) _(or better a beer)_ just to say thank you! 💪 171 | 172 | --- 173 | 174 | ## Author 175 | 176 | ``` 177 | Mariusz Banach / mgeeky, 21 178 | 179 | (https://github.com/mgeeky) 180 | ``` 181 | -------------------------------------------------------------------------------- /UnhookMe.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.1082 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnhookMe", "UnhookMe\UnhookMe.vcxproj", "{3DA2F1A6-6B47-4065-B384-DB39A5D49287}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Debug|x64.ActiveCfg = Debug|x64 17 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Debug|x64.Build.0 = Debug|x64 18 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Debug|x86.ActiveCfg = Debug|Win32 19 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Debug|x86.Build.0 = Debug|Win32 20 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Release|x64.ActiveCfg = Release|x64 21 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Release|x64.Build.0 = Release|x64 22 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Release|x86.ActiveCfg = Release|Win32 23 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {E6391BC9-0C73-4BC1-91A0-730D98CB750D} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /UnhookMe/PE.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "usings.h" 20 | 21 | #define RESOLVE_NO_UNHOOK(a, b) \ 22 | auto _ ## b = reinterpret_cast(::GetProcAddress(::LoadLibraryA(#a), #b)); 23 | 24 | /////////////////////////////////// 25 | 26 | #define PE_MAX_SECTIONS_COUNT 96 // according to Microsoft "pecoff_v8.doc" 27 | // and "The Art of computer virus research 28 | // and defense" by Peter Szor 29 | #define PE_MAX_ELFANEW_VALUE 0x400 30 | #define PE_MAX_SECTION_NAME_LEN 9 31 | #define PE_MAX_RELOCS_LIMIT 0x3000 32 | #define PE_MAX_BASE_RELOCATIONS 0x400 33 | #define PE_MAX_IAT_SIZE 0x100000 34 | #define PE_MAX_IAT_DESCRIPTORS 0x100 35 | #define PE_MAX_IAT_THUNKS 0x2000 36 | #define PE_MAX_EXPORTS_NUMBER 0x2000 37 | 38 | // Errors 39 | 40 | #define ERROR_FILE_IS_COMPRESSED 0x80001 // File is probably compressed 41 | #define ERROR_IAT_UNACCESSIBLE 0x80002 // IAT is unaccessible 42 | #define ERROR_INVALID_MAGIC 0x80003 // DOS_HEADER.eMagic is not "MZ" 43 | #define ERROR_INVALID_PE ERROR_INVALID_MAGIC 44 | #define ERROR_INVALID_SIGNATURE 0x80004 // NT_HEADERS.Signature is not "PE" 45 | #define ERROR_HEAP_CORRUPTED 0x80005 // Error while allocating memory at the Heap 46 | #define ERROR_READ_LESS_THAN_SHOULD 0x80006 // Read less bytes than should read 47 | #define ERROR_WRITE_LESS_THAN_SHOULD 0x80007 // Write less bytes than should write 48 | #define ERROR_EAT_UNACCESSIBLE 0x80008 // EAT is unaccessible 49 | #define ERROR_IAT_CORRUPTED 0x80009 // IAT is corrupted 50 | #define ERROR_EAT_CORRUPTED 0x8000A // EAT is corrupted 51 | #define ERROR_OPENED_FOR_READ_ONLY 0x8000B // File was opened for read only access. 52 | #define ERROR_PE_HEADERS_CORRUPTED 0x8000C 53 | #define ERROR_NO_RELOCS 0x8000D 54 | #define ERROR_RELOCS_CORRUPTED 0x8000E 55 | #define ERROR_RESOURCES_CORRUPTED 0x8000F 56 | #define ERROR_NO_RESOURCES 0x80010 57 | #define ERROR_IAT_TOO_BIG 0x80011 58 | #define ERROR_TOO_MANY_EXPORTS 0x80012 59 | #define ERROR_TOO_MANY_RELOCS 0x80013 60 | 61 | 62 | typedef struct _LDR_MODULE { 63 | LIST_ENTRY InLoadOrderModuleList; 64 | LIST_ENTRY InMemoryOrderModuleList; 65 | LIST_ENTRY InInitializationOrderModuleList; 66 | void* BaseAddress; 67 | void* EntryPoint; 68 | ULONG SizeOfImage; 69 | UNICODE_STRING FullDllName; 70 | UNICODE_STRING BaseDllName; 71 | ULONG Flags; 72 | SHORT LoadCount; 73 | SHORT TlsIndex; 74 | HANDLE SectionHandle; 75 | ULONG CheckSum; 76 | ULONG TimeDateStamp; 77 | } LDR_MODULE, *PLDR_MODULE; 78 | 79 | struct IMPORTED_FUNCTION 80 | { 81 | IMPORTED_FUNCTION() { memset((void*)szFunction, 0, sizeof(szFunction)); } 82 | 83 | unsigned uImpDescriptorIndex; // Index of import descriptor (index in vector) 84 | unsigned uImportIndex; // Index of import inside import descriptor 85 | char szFunction[65]; 86 | 87 | ULONGLONG dwPtrValueVA; // Value of pointer to this thunk 88 | ULONGLONG dwPtrValueRVA; // Value of pointer to this thunk 89 | DWORD dwHint; // Hint 90 | WORD wOrdinal; 91 | DWORD dwThunkRVA; // RVA address of this Thunk in file (not of value) 92 | }; 93 | 94 | 95 | // Class describing each exported thunk 96 | 97 | struct EXPORTED_FUNCTION 98 | { 99 | EXPORTED_FUNCTION() { memset((void*)szFunction, 0, sizeof(szFunction)); } 100 | 101 | bool bIsOrdinal; // Specifies wheter function is exported by ordinal 102 | // instead of by name 103 | unsigned uExportIndex; // Export thunk index. 104 | union { 105 | char szFunction[65]; 106 | DWORD dwOrdinal; 107 | }; 108 | 109 | bool bIsForwarded; // Specifies wheter exported thunk is forwarded 110 | char szForwarder[256]; 111 | 112 | ULONGLONG dwPtrValue; // Value of pointer to this thunk 113 | DWORD dwPtrValueRVA; // Value of pointer to this thunk 114 | WORD wOrdinal; // Ordinal 115 | DWORD dwThunkRVA; // RVA address of this Thunk in file (not of value) 116 | }; 117 | 118 | 119 | // Class describing each import descriptor 120 | 121 | struct __IMAGE_IMPORT_DESCRIPTOR 122 | { 123 | IMAGE_IMPORT_DESCRIPTOR d; 124 | char szName[128]; 125 | std::vector< IMPORTED_FUNCTION> vImports; 126 | }; 127 | 128 | struct __IMAGE_EXPORT_DIRECTORY 129 | { 130 | IMAGE_EXPORT_DIRECTORY d; 131 | char szName[128]; 132 | }; 133 | 134 | struct __IMAGE_SECTION_HEADER 135 | { 136 | IMAGE_SECTION_HEADER s; 137 | char szSectionName[PE_MAX_SECTION_NAME_LEN]; 138 | }; 139 | 140 | struct __IMAGE_RELOC_ENTRY 141 | { 142 | WORD offset : 12; 143 | WORD type : 4; 144 | }; 145 | 146 | struct __IMAGE_RELOCATION 147 | { 148 | IMAGE_BASE_RELOCATION baseRelocation; 149 | std::vector<__IMAGE_RELOC_ENTRY> relocs; 150 | }; 151 | 152 | 153 | //////////////////////////////// 154 | 155 | class PE 156 | { 157 | class _MY_IMAGE_OPTIONAL_HEADER; 158 | 159 | public: 160 | 161 | struct HookTrampolineBuffers 162 | { 163 | // (Input) Buffer containing bytes that should be restored while unhooking. 164 | BYTE* originalBytes; 165 | DWORD originalBytesSize; 166 | 167 | // (Output) Buffer that will receive bytes present prior to trampoline installation/restoring. 168 | BYTE* previousBytes; 169 | DWORD previousBytesSize; 170 | }; 171 | 172 | enum class AccessMethod 173 | { 174 | Arbitrary = 0, 175 | File_Begin, 176 | File_Current, 177 | File_End 178 | }; 179 | 180 | enum class AnalysisType 181 | { 182 | None, 183 | File, 184 | MappedModule, 185 | Memory, 186 | Dump 187 | }; 188 | 189 | bool hasImports; 190 | bool hasExports; 191 | bool hasRelocs; 192 | bool bReadOnly; 193 | bool bIsValidPE; 194 | 195 | AnalysisType analysisType; 196 | 197 | std::string szFileName; 198 | BY_HANDLE_FILE_INFORMATION bhFileInformation; 199 | 200 | size_t sizeOfFile; // actual size of file 201 | 202 | // PE Headers 203 | IMAGE_DOS_HEADER imgDosHdr; 204 | IMAGE_NT_HEADERS32 imgNtHdrs32; 205 | IMAGE_NT_HEADERS64 imgNtHdrs64; 206 | 207 | __IMAGE_EXPORT_DIRECTORY imgExportDirectory; // If this module provides 208 | // EAT, then this structure 209 | // will be filled correspondly 210 | 211 | size_t numOfNewSections; // Number of added sections 212 | // (by CreateSection method) 213 | 214 | // DOS header & stub 215 | std::vector lpDOSStub; // DOS STUB 216 | 217 | // Vectors 218 | std::vector<__IMAGE_SECTION_HEADER> vSections; 219 | std::vector< IMPORTED_FUNCTION> vImports; 220 | std::vector<__IMAGE_IMPORT_DESCRIPTOR> vImportDescriptors; 221 | std::vector< EXPORTED_FUNCTION> vExports; 222 | std::vector<__IMAGE_RELOCATION> vRelocations; 223 | 224 | // Address of mapped memory area 225 | LPBYTE lpMapOfFile; 226 | bool bUseRVAInsteadOfRAW; // Used for analysis of a previously mapped 227 | // dump file. 228 | HANDLE hFileHandle; 229 | 230 | // Process analysis specific variables 231 | bool bPreferBaseAddressThanImageBase; 232 | // If parsing PIC injected shellcode, its allocation 233 | // base address may be different than its expected ImageBase. 234 | // In such scenario, prefer allocation base over its ImageBase. 235 | bool bMemoryAnalysis; // PE interface is performing living 236 | // process/module analysis (memory). 237 | // This not flags actual memory 238 | // analysing, but in fact it's 239 | // switch used in recognizing 240 | // Process Memory operations. 241 | 242 | DWORD dwPID; // PID of the currently analysed process. 243 | 244 | 245 | ////////////////////////////// MEMBER METHODS //////////////////////////////////////////// 246 | 247 | ~PE(); 248 | 249 | // Implicit constructor. After you have to call PE::LoadFile member method to analyse an image 250 | PE() { _initVars(); } 251 | 252 | // Explicit constructor. After initialization instantly runs PE image analysis 253 | PE(const std::string& _szFileName, bool bRunAnalysis = false) : szFileName(_szFileName) 254 | { 255 | _initVars(); 256 | if (bRunAnalysis == true) { 257 | _bIsFileMapped = false; 258 | LoadFile(); 259 | } 260 | } 261 | 262 | void close(); 263 | 264 | 265 | //========= Address / offset conversions 266 | 267 | size_t RVA2RAW(size_t dwRVA, bool bForce = false) const; 268 | 269 | // Returns conversion from RVA to RAW. 270 | // If we set bForce to true, it will omit 271 | // usage of this->bUseRVAInsteadOfRAW variable 272 | DWORD RAW2RVA(size_t dwRAW) const; 273 | DWORD VA2RVA32(DWORD dwVA) const { return dwVA - GetIB32(); } 274 | DWORD RVA2VA32(DWORD dwRVA) const { return dwRVA + GetIB32(); } 275 | ULONGLONG VA2RVA64(ULONGLONG dwVA) const { return dwVA - GetIB64(); } 276 | ULONGLONG RVA2VA64(ULONGLONG dwRVA) const { return dwRVA + GetIB64(); } 277 | 278 | 279 | //========= Getting info 280 | 281 | DWORD GetEP() const { return bIs86 ? imgNtHdrs32.OptionalHeader.AddressOfEntryPoint : imgNtHdrs64.OptionalHeader.AddressOfEntryPoint; } 282 | ULONGLONG GetImageBase() const { return bIs86 ? GetIB32() : GetIB64(); } 283 | size_t GetSectionsCount() const { return vSections.size(); } 284 | __IMAGE_SECTION_HEADER& GetSection(size_t u) { return vSections[u]; } 285 | __IMAGE_SECTION_HEADER& GetLastSection() { return vSections.back(); } 286 | 287 | bool isArch86() const { return bIs86; } 288 | bool HasOverlay() const { return _bHasOverlay; } 289 | 290 | 291 | //========= Checking errors 292 | 293 | DWORD GetError() const { return _dwLastError; } 294 | DWORD GetErrorLine() const { return _dwErrorLine; } 295 | std::wstring GetErrorFunc() const { std::wstring func(_szErrorFunction.begin(), _szErrorFunction.end()); return func; } 296 | std::wstring GetErrorString() const; 297 | 298 | bool operator!() const { return (this->GetError() != 0); } 299 | void SetError(DWORD dwErrCode) { SetLastError(dwErrCode); _dwLastError = dwErrCode; } 300 | 301 | 302 | //=========== Analysis methods ============ 303 | 304 | // Simple file reading & writing (and of course parsing) 305 | bool AnalyseFile(const std::wstring& _szFileName, bool readOnly, bool _bIsValidPEImage = true) 306 | { 307 | std::string _name(_szFileName.begin(), _szFileName.end()); 308 | _filePath = _szFileName; 309 | return AnalyseFile(_name, readOnly, _bIsValidPEImage); 310 | } 311 | 312 | bool AnalyseFile(const std::string& _szFileName, bool readOnly, bool _bIsValidPEImage = true) 313 | { 314 | this->szFileName = _szFileName; 315 | this->_bIsFileMapped = false; 316 | this->bUseRVAInsteadOfRAW = false; 317 | this->bReadOnly = readOnly; 318 | this->bIsValidPE = _bIsValidPEImage; 319 | this->analysisType = AnalysisType::File; 320 | _filePath = std::wstring(_szFileName.begin(), _szFileName.end()); 321 | 322 | return PE::LoadFile(); 323 | } 324 | 325 | // Another type of analysis. This performs analysis from dump file which is aligned and 326 | // divided to sections. This means, that analysis must be RVA-based 327 | // and make file reading & writing on every I/O. 328 | // e.g. dump file may be a dumped process memory. 329 | // Simple file reading & writing (and of course parsing) 330 | bool AnalyseDump(const std::wstring& _szDump, bool readOnly) 331 | { 332 | std::string _name(_szDump.begin(), _szDump.end()); 333 | return AnalyseDump(_name, readOnly); 334 | } 335 | 336 | bool AnalyseDump(const std::string& _szDump, bool readOnly) 337 | { 338 | this->bUseRVAInsteadOfRAW = this->bIsValidPE = true; 339 | this->_bIsFileMapped = false; 340 | this->bReadOnly = readOnly; 341 | this->analysisType = AnalysisType::Dump; 342 | 343 | szFileName = _szDump; 344 | 345 | return PE::LoadFile(); 346 | } 347 | 348 | // Analyses current process memory treating input dwAddress as a base of 349 | // mapped image. This address should point to the mapped address of valid PE 350 | // file inside current process memory. 351 | // If analysed memory region is not a MEM_MAPPED or MEM_IMAGE, it may be PIC shellcode. 352 | // In such case, prefer dwAddress over expected ImageBase while calculating RVAs/VAs. 353 | // use isMapped=True if you plan to analyse memory mapped(or ordinarly loaded) PE module such as EXE/DLL, 354 | // or isMapped=False for PIC shellcode acting as a Reflective DLL injected one or PE-to-shellcode converted. 355 | // If adjustMemoryProtections is true, will iterate over memory pool and set R/RW pages wherever no Read/RW is applied. 356 | bool AnalyseMemory(DWORD dwPID, LPBYTE dwAddress, size_t dwSize, bool readOnly, bool isMapped, bool adjustMemoryProtections = false); 357 | 358 | // Below methods performs module analysis from specified process memory. 359 | // This works by reading process memory and parsing/analysing it. 360 | // If adjustMemoryProtections is true, will iterate over memory pool and set R/RW pages wherever no Read/RW is applied. 361 | bool AnalyseProcessModule(DWORD dwPID, HMODULE hModule, bool readOnly, bool adjustMemoryProtections = false); 362 | 363 | // This method performs process module analysis. Actually, it opens process, 364 | // enumerates process modules and compares it with the szModule name. Afterwards, 365 | // it sets module handle and launches analysis. By specifying szModule to nullptr user can 366 | // perform dwPID process analysis instead of one of it's modules. 367 | // If adjustMemoryProtections is true, will iterate over memory pool and set R/RW pages wherever no Read/RW is applied. 368 | bool AnalyseProcessModule(DWORD dwPID, const std::wstring& szModule, bool readOnly, bool adjustMemoryProtections = false) 369 | { 370 | std::string n(szModule.begin(), szModule.end()); 371 | return AnalyseProcessModule(dwPID, n, readOnly, adjustMemoryProtections); 372 | } 373 | 374 | bool AnalyseProcessModule(DWORD dwPID, const std::string& szModule, bool readOnly, bool adjustMemoryProtections = false); 375 | 376 | 377 | // Simple wrapper to _AnalyseProcessModule for quick process analysis 378 | bool AnalyseProcess(DWORD dwPID, bool readOnly) { return this->AnalyseProcessModule(dwPID, "", readOnly); } 379 | 380 | bool ApplyAllRelocs(ULONGLONG newImageBase = 0); 381 | bool ApplyRelocsInBuffer(ULONGLONG newBase, ULONGLONG bufferRVA, uint8_t *buffer, size_t sizeOfBuffer, ULONGLONG oldImageBase = 0); 382 | 383 | // This function actually opens the file, gathers headers from file, performs IAT parsing, and 384 | // if possible performs EAT parsing. Whole PE image analysis is beginning there. 385 | bool LoadFile(); 386 | 387 | bool getImport(const char* functionName, IMPORTED_FUNCTION* func); 388 | bool getExport(const char* exportFunction, EXPORTED_FUNCTION* func); 389 | 390 | // I/O - read/writes opened file/process (PE::_hFileHandle) and returns 391 | bool ReadBytes(LPVOID, size_t dwSize, size_t dwOffset = 0, enum class AccessMethod method = AccessMethod::File_Current, bool dontRestoreFilePointer = false); 392 | bool WriteBytes(LPVOID, size_t dwSize, size_t dwOffset = 0, enum class AccessMethod method = AccessMethod::File_Current, bool dontRestoreFilePointer = false); 393 | 394 | 395 | std::vector ReadOverlay(); 396 | std::vector ReadSection(const __IMAGE_SECTION_HEADER& section); 397 | 398 | // Writes PE headers back to the mapped file/memory. 399 | bool UpdateHeaders(); 400 | 401 | DWORD InsertShellcode(uint8_t *shellcode, size_t sizeOfShellcode, const std::string& szSectionName = ".extra", BYTE* whereToReturn = 0); 402 | 403 | std::vector getJumpPayload(BYTE* whereToJump); 404 | 405 | // This method hooks IAT/EAT routine by swapping original IAT/EAT thunk address with input hook address 406 | // Returns: 0 if szImportThunk/szExportThunk has not been found, -1 if there has 407 | // occured an error during WriteBytes, or non-zero if function succeed, and this value will 408 | // be previous thunk EntryPoint address. 409 | ULONGLONG HookIAT(const std::string& szImportThunk, ULONGLONG hookedVA); 410 | DWORD HookEAT(const std::string& szExportThunk, DWORD hookedRVA); 411 | bool HookTrampoline(bool installHook, LPVOID address, LPVOID jumpAddress, HookTrampolineBuffers* buffers = NULL); 412 | 413 | 414 | // Creates image section and appends it to the PE::pSectionHdrs table 415 | __IMAGE_SECTION_HEADER CreateSection(DWORD dwSizeOfSection, DWORD dwDesiredAccess, const std::string& szNameOfSection); 416 | __IMAGE_SECTION_HEADER RemoveSection(size_t index); 417 | 418 | static int64_t LECharTo64bitNum(char a[]) 419 | { 420 | int64_t n = 0; 421 | n = (((int64_t)a[7] << 56) & 0xFF00000000000000U) 422 | | (((int64_t)a[6] << 48) & 0x00FF000000000000U) 423 | | (((int64_t)a[5] << 40) & 0x0000FF0000000000U) 424 | | (((int64_t)a[4] << 32) & 0x000000FF00000000U) 425 | | ((a[3] << 24) & 0x00000000FF000000U) 426 | | ((a[2] << 16) & 0x0000000000FF0000U) 427 | | ((a[1] << 8) & 0x000000000000FF00U) 428 | | (a[0] & 0x00000000000000FFU); 429 | return n; 430 | } 431 | 432 | static int32_t LECharTo32bitNum(char a[]) 433 | { 434 | int32_t n = 0; 435 | n = (((int32_t)a[3] << 24) & 0xFF000000) 436 | | (((int32_t)a[2] << 16) & 0x00FF0000) 437 | | (((int32_t)a[1] << 8) & 0x0000FF00) 438 | | (((int64_t)a[0] << 0) & 0x000000FF); 439 | return n; 440 | } 441 | 442 | static void convert64ToLECharArray(uint8_t *arr, uint64_t a) 443 | { 444 | size_t i = 0; 445 | for (i = 7; i > 0; i--) 446 | { 447 | arr[i] = (uint8_t)((((uint64_t)a) >> (56 - (8 * i))) & 0xFFu); 448 | } 449 | } 450 | 451 | static void convert32ToLECharArray(uint8_t *arr, uint32_t a) 452 | { 453 | size_t i = 0; 454 | for (i = 3; i > 0; i--) 455 | { 456 | arr[i] = (uint8_t)((((uint32_t)a) >> (24 - (8 * i))) & 0xFFu); 457 | } 458 | } 459 | 460 | private: 461 | // ========================================== 462 | 463 | void _initVars() 464 | { 465 | _bHasOverlay = _bIsFileMapped = _bIsIATFilled = bUseRVAInsteadOfRAW = bMemoryAnalysis = _selfProcessAnalysis = _bAutoMapOfFile = false; 466 | numOfNewSections = sizeOfFile = _dwCurrentOffset = _dwLastError = dwPID = 0; 467 | _hMapOfFile = hFileHandle = (HANDLE)INVALID_HANDLE_VALUE; 468 | lpMapOfFile = nullptr; 469 | analysisType = AnalysisType::None; 470 | if(!lpDOSStub.empty()) lpDOSStub.clear(); 471 | } 472 | 473 | // More detailed SetError version 474 | void _SetError(DWORD dwErrCode, int iLine, const char* szFunc) 475 | { 476 | if (this->_dwLastError != 0) return; 477 | this->SetError(dwErrCode); 478 | this->_dwErrorLine = (DWORD)iLine; 479 | this->_szErrorFunction = std::string(szFunc); 480 | } 481 | 482 | void adjustOptionalHeader(); 483 | 484 | std::string& trimQuote(std::string& szPath) const; // Trims from file path quote chars '"' 485 | 486 | bool ReadEntireModuleSafely(LPVOID lpBuffer, size_t dwSize, size_t dwOffset); 487 | 488 | void AddNewSection(size_t sizeOfSection, DWORD flags, const std::string& szSectionName); 489 | bool AppendShellcode(BYTE* whereToReturn, uint8_t *shellcode, size_t sizeOfShellcode, __IMAGE_SECTION_HEADER *imgNewSection); 490 | 491 | DWORD GetSafeSectionSize(const __IMAGE_SECTION_HEADER& sect) const; 492 | 493 | bool _OpenFile(); 494 | 495 | void SetFileMapped(LPBYTE _lpMapOfFile) 496 | { 497 | lpMapOfFile = _lpMapOfFile; 498 | _bIsFileMapped = true; 499 | _bAutoMapOfFile = false; 500 | } 501 | 502 | // Simple CreateFileMappingA and MapViewOfFile function 503 | LPBYTE MapFile(); 504 | 505 | // Function fills IAT in mapped memory (with GetProcAddr addresses 506 | // if dwAddresses hasn't been specified, or with addresses from dwAddresses table. 507 | bool FillIAT(DWORD *dwAddresses = nullptr, DWORD dwElements = 0); 508 | 509 | bool ParseIAT(DWORD dwAddressOfIAT = 0); // Function parses IAT (from input address, or if 510 | // not specified - from DataDirectory[1] ) 511 | bool ParseEAT(DWORD dwAddressOfEAT = 0); // Function parses EAT (from input address, or if 512 | // not specified - from DataDirectory[0] ) 513 | 514 | bool ParseRelocs(); 515 | bool ParseResources(); 516 | 517 | // Returns char intepretation of input, or '.' if it is not printable 518 | inline char _HexChar(int c); 519 | 520 | DWORD GetIB32() const; 521 | ULONGLONG GetIB64() const; 522 | 523 | bool changeProtection(LPVOID address, size_t size, DWORD newProtection, LPDWORD oldProtection); 524 | 525 | bool verifyAddressBounds(uintptr_t address, bool relative = true); 526 | bool verifyAddressBounds(uintptr_t address, size_t upperBoundary, bool relative = true); 527 | bool verifyAddressBounds(uintptr_t address, size_t lowerBoundary, size_t upperBoundary, bool relative = true); 528 | 529 | std::vector 530 | collectProcessMemoryMap(); 531 | 532 | bool _LoadFile(); 533 | bool accomodateMemoryPagesForAccess( 534 | bool accomodateOrRestore, HMODULE hModule, std::vector& origMemoryMap); 535 | 536 | 537 | /************************************/ 538 | 539 | std::wstring _filePath; 540 | 541 | bool bIs86; 542 | 543 | size_t ptrSize; 544 | DWORD _dwLastError; // PE interface last error 545 | // may be as well return of the 546 | // GetLastError() 547 | DWORD _dwErrorLine; // Line in code where error occured 548 | std::string _szErrorFunction; // Function name where error occured 549 | bool _bIsIATFilled; // Specifies wheter IAT has been 550 | // filled 551 | 552 | size_t _dwCurrentOffset; // During process analysis 553 | // we cannot use SetFilePointer 554 | // routine to seek inside process 555 | // memory, so that's how we obey it 556 | intptr_t _moduleStartPos; 557 | HANDLE _hMapOfFile; 558 | bool _bIsFileMapped; 559 | bool _bAutoMapOfFile; 560 | bool _selfProcessAnalysis; 561 | bool _bHasOverlay; 562 | bool adjustMemoryProtections; 563 | 564 | }; 565 | -------------------------------------------------------------------------------- /UnhookMe/UnhookMe.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an example program presenting capabilities of the UnhookingImportResolver implementation. 3 | * The Resolver exposes an easy to use macrodefition allowing it's user to safely dynamically resolve 4 | * their needed imports, without worrying about any IAT/EAT/hot-patch hooks potentially introduced by 5 | * AVs/EDRs. 6 | * 7 | * The program logic is following: 8 | * 1. It presents us with the first MessageBoxW that is not subject for hooking 9 | * 2. Then we hook MessageBoxW prologue ourselves to make it always return 0 without displaying it's message 10 | * 3. Finally, we resolve MessageBoxW dynamically using the UnhookingImportResolver resolver, which will detect 11 | * applied prologue patches and restore original bytes, effectively unhooking MessageBoxW functionality. 12 | * 13 | * Compile with: 14 | * /std:c++17 15 | * 16 | * Tested on both x86 and x64. 17 | * 18 | * Author: 19 | * Mariusz Banach / mgeeky (@mariuszbit) 20 | **/ 21 | 22 | #include 23 | 24 | // 25 | // These are the only two required include statements your program will need. 26 | // 27 | #include "usings.h" 28 | #include "resolver.h" 29 | 30 | 31 | // optional include 32 | #include "hexdump.hpp" 33 | 34 | 35 | #ifndef _WIN64 36 | #error "This resolver example was designed only for x64 architectures." 37 | #endif 38 | 39 | 40 | bool displayImportedFunction(const unsigned char*); 41 | 42 | int main() 43 | { 44 | // 45 | // Here we Patch MessageBoxW's prologue stub to make it immediately return with 0, preventing 46 | // malware from popping up those nasty messages! 47 | // 48 | unsigned char* ptr = reinterpret_cast(MessageBoxW); 49 | 50 | std::wcout << L"1. Starting UnhookMe" << std::endl; 51 | 52 | displayImportedFunction(ptr); 53 | 54 | std::wcout << std::endl << L"Function's stub bytes:" << std::endl << getHexdump(ptr, 16) << std::endl; 55 | std::wcout << L"3. 1st MessageBox - not inspected (the one that may be monitored by EDRs/AVs)." << std::endl; 56 | 57 | MessageBoxW(0, L"This message box is subject for dynamic inspection.", L"First - Not inspected", 0); 58 | 59 | // 60 | // Step 1: Make MessageBoxW's stub RWX 61 | // 62 | DWORD old, old2; 63 | if (!VirtualProtect(ptr, 10, PAGE_EXECUTE_READWRITE, &old)) { 64 | return 0; 65 | } 66 | 67 | // xor rax, rax 68 | // ret 69 | std::wcout << L"4. Patching MessageBoxW's stub with (xor rax, rax; ret / 48 31 c0 c3)" << std::endl; 70 | unsigned char patch[] = { 0x48, 0x31, 0xC0, 0xC3 }; 71 | 72 | // 73 | // Imagine instead of simple "return 0" hot patch, the Cylance EDR introduces here 74 | // their API inspection logic trampoline. We'll be able to easily wipe it out 75 | // 76 | memcpy(ptr, patch, _countof(patch)); 77 | VirtualProtect(ptr, 10, old, &old2); 78 | 79 | std::wcout << getHexdump(ptr, 16) << std::endl; 80 | 81 | 82 | // 83 | // Step 2: Calling patched (instrumented, hooked) MessageBoxW - this will fail as we just patched 84 | // function's prologue stub preventing any subsequent function calls 85 | // 86 | std::wcout << L"6. 2nd MessageBox - the one which stub was patched with a return 0 trampoline (it won't pop up)" << std::endl; 87 | 88 | MessageBoxW(0, L"This message box will never have chance to shine :(", L"Second - Hooked", 0); 89 | 90 | 91 | // 92 | // Step 3: As a fight back strategy we resolve MessageBoxW address dynamically here and let the resolver 93 | // Do it's thing to safely unhook our lovely routine. 94 | // 95 | 96 | std::wcout << L"7. Resolving MessageBoxW's address dynamically, unhooking it along the way" << std::endl; 97 | 98 | RESOLVE(user32, MessageBoxW); 99 | unsigned char* ptr2 = reinterpret_cast(_MessageBoxW.getAddress()); 100 | 101 | std::wcout << std::endl << L"8. 3rd MessageBox - the one that is unpatched, unhooked, invisible to user-mode API monitoring" << std::endl; 102 | std::wcout << L"9. Restored MessageBoxW stub bytes:" << std::endl << getHexdump(ptr, 16) << std::endl; 103 | 104 | if (ptr != ptr2) 105 | { 106 | std::wcout << L"10. MessageBoxW address: Hooked (0x" << std::hex << ptr << L"), Unhooked: (0x" << std::hex << ptr2 << L")" << std::endl; 107 | } 108 | else 109 | { 110 | std::wcout << L"10. MessageBoxW address: (0x" << std::hex << ptr << L") - was never really hooked, only trampolined." << std::endl; 111 | } 112 | 113 | _MessageBoxW(0, L"Look Ma! I'm unhooked!", L"Third - Unhooked", 0); 114 | 115 | // 116 | // From now on, MessageBoxW will remain unhooked. 117 | // 118 | 119 | return 0; 120 | } 121 | 122 | bool displayImportedFunction(const unsigned char* ptr) 123 | { 124 | PE pe; 125 | if (!pe.AnalyseProcess(0, true) || pe.GetError() != 0) 126 | { 127 | std::wcout << L"[!] Could not analyse own process memory. Error: " << pe.GetErrorString() << std::endl;; 128 | return false; 129 | } 130 | 131 | size_t num = 0; 132 | auto msgbox = std::find_if(pe.vImports.begin(), pe.vImports.end(), [&](const IMPORTED_FUNCTION& imp) { 133 | return !strcmp(imp.szFunction, "MessageBoxW"); 134 | }); 135 | 136 | if (msgbox == pe.vImports.end()) { 137 | std::wcout << L"[!] Could not find imported MessageBoxW thunk! Imports: " << pe.vImports.size() << std::endl; 138 | return false; 139 | } 140 | 141 | std::wcout << L"\t- Imported function hint: 0x" << std::dec << msgbox->dwHint << std::endl; 142 | std::wcout << L"\t- Imported function address: 0x" << std::hex << msgbox->dwPtrValueVA << std::endl; 143 | std::wcout << L"\t- Imported function RVA: 0x" << std::hex << msgbox->dwPtrValueRVA << std::endl; 144 | std::wcout << L"\t- Imported function thunk address: 0x" << std::hex << msgbox->dwThunkRVA << std::endl; 145 | std::wcout << L"\t- Hexdump of first 16 bytes from imported function's thunk:" << std::endl 146 | << getHexdump(reinterpret_cast(pe.RVA2VA64(msgbox->dwThunkRVA)), 16); 147 | 148 | return true; 149 | } -------------------------------------------------------------------------------- /UnhookMe/UnhookMe.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {3DA2F1A6-6B47-4065-B384-DB39A5D49287} 24 | Win32Proj 25 | UnhookMe 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | stdcpp17 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | true 108 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | true 110 | stdcpp17 111 | 112 | 113 | Console 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | Level3 122 | MaxSpeed 123 | true 124 | true 125 | true 126 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | stdcpp17 129 | 130 | 131 | Console 132 | true 133 | true 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | Level3 142 | MaxSpeed 143 | true 144 | true 145 | true 146 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 147 | true 148 | stdcpp17 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /UnhookMe/UnhookMe.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /UnhookMe/UnhookMe.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /UnhookMe/hexdump.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEXDUMP_HPP 2 | #define HEXDUMP_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | struct CustomHexdump 10 | { 11 | CustomHexdump(void* data, size_t length) : 12 | mData(static_cast(data)), mLength(length) { } 13 | const unsigned char* mData; 14 | const size_t mLength; 15 | }; 16 | 17 | template 18 | std::ostream& operator<<(std::ostream& out, const CustomHexdump& dump) 19 | { 20 | out.fill('0'); 21 | for (size_t i = 0; i < dump.mLength; i += RowSize) 22 | { 23 | out << "0x" << std::setw(6) << std::hex << i << ": "; 24 | for (size_t j = 0; j < RowSize; ++j) 25 | { 26 | if (i + j < dump.mLength) 27 | { 28 | out << std::hex << std::setw(2) << static_cast(dump.mData[i + j]) << " "; 29 | } 30 | else 31 | { 32 | out << " "; 33 | } 34 | } 35 | 36 | out << " "; 37 | if (ShowAscii) 38 | { 39 | for (size_t j = 0; j < RowSize; ++j) 40 | { 41 | if (i + j < dump.mLength) 42 | { 43 | if (std::isprint(dump.mData[i + j])) 44 | { 45 | out << static_cast(dump.mData[i + j]); 46 | } 47 | else 48 | { 49 | out << "."; 50 | } 51 | } 52 | } 53 | } 54 | out << std::endl; 55 | } 56 | return out; 57 | } 58 | 59 | typedef CustomHexdump<16, true> Hexdump; 60 | 61 | template 62 | std::wstring getHexdump(T* data, size_t length) 63 | { 64 | std::ostringstream oss; 65 | oss << Hexdump(data, length); 66 | 67 | auto s = oss.str(); 68 | return std::wstring(s.begin(), s.end()); 69 | } 70 | 71 | #endif // HEXDUMP_HPP -------------------------------------------------------------------------------- /UnhookMe/resolver.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "resolver.h" 3 | 4 | 5 | namespace UnhookingImportResolver 6 | { 7 | // 8 | // Resolver global options: 9 | // 10 | // globalQuietOption - set to true if you don't want to have any sort of output 11 | // globalVerboseOption - set to true if you want to have detailed verbose output 12 | // globalAntiSplicingOption - unhook resolved functions if they're hooked. 13 | // globalLogFilePath - where to redirect output log lines. If empty, pick stdout. 14 | // 15 | 16 | bool globalQuietOption = true; 17 | bool globalVerboseOption = false; 18 | bool globalAntiSplicingOption = true; 19 | 20 | ImportResolverCache UnhookingImportResolver::globalResolverCache; 21 | 22 | void die() 23 | { 24 | ::ExitProcess(0); 25 | } 26 | 27 | std::wstring adjustPath(const std::wstring& szPath) 28 | { 29 | auto out = _adjustPath(szPath); 30 | if (!out.empty()) return out; 31 | 32 | out = _adjustPath(szPath + OBFI(L".exe")); 33 | if (!out.empty()) return out; 34 | 35 | out = _adjustPath(szPath + OBFI(L".dll")); 36 | if (!out.empty()) return out; 37 | 38 | out = _adjustPath(OBFI(L"..\\") + szPath); 39 | if (!out.empty()) return out; 40 | 41 | out = _adjustPath(OBFI(L"..\\") + szPath + OBFI(L".exe")); 42 | if (!out.empty()) return out; 43 | 44 | out = _adjustPath(OBFI(L"..\\") + szPath + OBFI(L".dll")); 45 | if (!out.empty()) return out; 46 | 47 | die(); 48 | return L""; 49 | } 50 | 51 | std::wstring _adjustPath(const std::wstring& szPath) 52 | { 53 | // 54 | // Can't RESOLVE GetFileAttributesW and ExpandEnvironmentStringsW as we're unable to 55 | // #include Resolver here. This would lead to cyclic dependency and wouldn't build. 56 | // 57 | 58 | DWORD dwAttrib = GetFileAttributesW(szPath.c_str()); 59 | 60 | bool exists = (dwAttrib != INVALID_FILE_ATTRIBUTES && 61 | !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 62 | 63 | if (exists) 64 | { 65 | return std::wstring(szPath); 66 | } 67 | else 68 | { 69 | wchar_t path[1024] = { 0 }; 70 | ExpandEnvironmentStringsW(OBF_WSTR(L"%SystemRoot%").c_str(), path, _countof(path)); 71 | 72 | auto newPath = std::wstring(path) + OBF_WSTR(L"\\System32\\") + szPath; 73 | 74 | dwAttrib = GetFileAttributesW(newPath.c_str()); 75 | exists = (dwAttrib != INVALID_FILE_ATTRIBUTES && 76 | !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 77 | 78 | if (!exists) 79 | { 80 | return {}; 81 | } 82 | 83 | return newPath; 84 | } 85 | } 86 | 87 | std::string adjustPathA(const std::string& szPath) 88 | { 89 | static std::map cachedAdjustedPaths; 90 | if (cachedAdjustedPaths.count(szPath) != 0) 91 | { 92 | return cachedAdjustedPaths[szPath]; 93 | } 94 | 95 | std::wstring b(szPath.begin(), szPath.end()); 96 | auto a = adjustPath(b); 97 | 98 | cachedAdjustedPaths[szPath] = std::string(a.begin(), a.end()); 99 | return cachedAdjustedPaths[szPath]; 100 | } 101 | } -------------------------------------------------------------------------------- /UnhookMe/resolver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "PE.h" 12 | #include "usings.h" 13 | 14 | #pragma warning(disable: 6387) 15 | #pragma warning(disable: 4005) 16 | 17 | // 18 | // Helper macrodefintions to easily plug ADVobfuscator/MetaString.h 19 | // 20 | 21 | 22 | #define OBF(x) x 23 | #define OBFI(x) x 24 | #define OBF_ASCII(x) x 25 | #define OBFI_ASCII(x) x 26 | #define ADV_OBF(x) x 27 | #define ADV_OBF_W(x) x 28 | #define OBF_WSTR(x) std::wstring(x) 29 | #define OBF_STR(x) std::string(x) 30 | 31 | /* 32 | In order to use Resolver a function pointer type must be first declared 33 | with "using" statement of strict form: 34 | 35 | using fn_FunctionName = ReturnType WINAPI ( 36 | ParamType1 paramName1, 37 | ..., 38 | ParamTypeN paramNameN, 39 | ); 40 | 41 | The FunctionName will correspond to the WinAPI that we want to have ImportResolver resolve 42 | and that function pointer must be marked as having WINAPI call convention (__stdcall on 43 | x86 and __fastcall on x64). The ReturnType must precede "WINAPI" type modifier. 44 | 45 | Having function pointer type defined like specified above, we will be able to use it in 46 | the following manner: 47 | 48 | RESOLVE(libraryName, FunctionName); 49 | ReturnType output = _FunctionName(param1, ..., paramN); 50 | 51 | The macro `RESOLVE` takes care of instantiating ImportResolver templated object, 52 | adjust given library's name. 53 | 54 | Resolver's constructor: 55 | 56 | template 57 | ImportResolver( 58 | std::string dllName, 59 | std::string funcName, 60 | bool _unhook = false, 61 | bool *_wasItHooked = nullptr 62 | ) 63 | */ 64 | 65 | // 66 | // Macrodefinition Parameters: 67 | // - mod: library name exporting specified function 68 | // - func: function to import 69 | // - unhook: whether to do function anti-splicing/unhooking if it was hooked 70 | // 71 | #define RESOLVE_PARAMETERIZED(mod, func, unhook) \ 72 | static auto _ ## func = UnhookingImportResolver::ImportResolver( \ 73 | UnhookingImportResolver::adjustPathA(OBFI_ASCII(#mod)), OBFI_ASCII(#func), \ 74 | unhook); 75 | 76 | #define RESOLVE(mod, func) RESOLVE_PARAMETERIZED(mod, func, true) 77 | #define RESOLVE_NO_UNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false) 78 | 79 | 80 | namespace UnhookingImportResolver 81 | { 82 | template 83 | class ImportResolver {}; 84 | 85 | template 86 | bool stringicompare(const T& a, const T& b) 87 | { 88 | if (a.length() != b.length()) return false; 89 | 90 | return std::equal( 91 | a.begin(), 92 | a.end(), 93 | b.begin(), 94 | [](const unsigned short& a, const unsigned short& b) 95 | { 96 | return (std::tolower(a) == std::tolower(b)); 97 | } 98 | ); 99 | } 100 | 101 | template 102 | struct ImportResolverCache 103 | { 104 | ImportResolverCache() : cachedResolvedImports{}, cachedModuleBases{} {}; 105 | 106 | std::map functionResolutionCount; 107 | std::map, FARPROC> cachedResolvedImports; 108 | std::map cachedModuleBases; 109 | 110 | T getModuleName(const T& dllName) 111 | { 112 | std::string name = dllName; 113 | std::string suffix = ".dll"; 114 | 115 | if (0 != name.compare(name.size() - suffix.size(), suffix.size(), suffix)) name += suffix; 116 | 117 | static std::map cachedNames; 118 | if (cachedNames.count(name) != 0) 119 | { 120 | return cachedNames[name]; 121 | } 122 | 123 | const auto lastBackslash = name.rfind(static_cast('\\')); 124 | if (lastBackslash != T::npos) 125 | { 126 | cachedNames[name] = T(name, lastBackslash + 1); 127 | return cachedNames[name]; 128 | } 129 | 130 | return name; 131 | } 132 | 133 | FARPROC getCached(const T& dllName, const T& funcName) 134 | { 135 | const auto name = getModuleName(dllName); 136 | const auto tmp = std::make_pair(name, funcName); 137 | if (cachedResolvedImports.count(tmp) != 0) 138 | { 139 | functionResolutionCount[funcName]++; 140 | return cachedResolvedImports[tmp]; 141 | } 142 | 143 | return nullptr; 144 | } 145 | 146 | void setCachedFunction(const T& dllName, const T& funcName, FARPROC func) 147 | { 148 | const auto name = getModuleName(dllName); 149 | cachedResolvedImports[std::make_pair(name, funcName)] = func; 150 | functionResolutionCount[funcName] = 1; 151 | } 152 | 153 | HINSTANCE getModule(const T& dllName) 154 | { 155 | const auto name = getModuleName(dllName); 156 | if (cachedModuleBases.count(name) != 0) 157 | { 158 | return cachedModuleBases[name]; 159 | } 160 | 161 | cachedModuleBases[name] = ::GetModuleHandleA(dllName.c_str()); 162 | return cachedModuleBases[name]; 163 | } 164 | 165 | void invalidateModule(const T& dllName) 166 | { 167 | auto name = getModuleName(dllName); 168 | if (cachedModuleBases.count(name) != 0) 169 | { 170 | cachedModuleBases[name] = nullptr; 171 | cachedModuleBases.erase(name); 172 | } 173 | 174 | std::vector funcs; 175 | for (auto const& moduleFuncPair : cachedResolvedImports) 176 | { 177 | std::string mod = moduleFuncPair.first.first; 178 | if(stringicompare(mod, name)) funcs.push_back(moduleFuncPair.first.second); 179 | } 180 | 181 | for (auto const& func : funcs) { 182 | invalidateFunction(name, func); 183 | } 184 | } 185 | 186 | void invalidateFunction(const T& dllName, const T& funcName) 187 | { 188 | const auto name = getModuleName(dllName); 189 | const auto tmp = std::make_pair(name, funcName); 190 | if (cachedResolvedImports.count(tmp) != 0) 191 | { 192 | cachedResolvedImports[tmp] = nullptr; 193 | cachedResolvedImports.erase(tmp); 194 | } 195 | } 196 | 197 | void setCachedModuleBase(const T& dllName, HINSTANCE mod) 198 | { 199 | const auto name = getModuleName(dllName); 200 | cachedModuleBases[name] = mod; 201 | } 202 | }; 203 | 204 | 205 | // 206 | // ======================================================================================= 207 | // 208 | 209 | template 210 | std::vector split(const T& s, T seperator) 211 | { 212 | std::vector output; 213 | size_t prev_pos = 0, pos = 0; 214 | 215 | while ((pos = s.find(seperator, pos)) != T::npos) 216 | { 217 | T substring(s.substr(prev_pos, pos - prev_pos)); 218 | output.push_back(substring); 219 | prev_pos = ++pos; 220 | } 221 | 222 | output.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word 223 | return output; 224 | } 225 | 226 | std::wstring adjustPath( 227 | const std::wstring& szPath 228 | ); 229 | 230 | std::wstring _adjustPath( 231 | const std::wstring& szPath 232 | ); 233 | 234 | std::string adjustPathA( 235 | const std::string& szPath 236 | ); 237 | 238 | void die(); 239 | 240 | 241 | // 242 | // ======================================================================================= 243 | // 244 | 245 | 246 | // Cannot make this ImportResolver's a class member, since ImportResolver is a template and gets various 247 | // instantiations per each function pointer type. 248 | extern ImportResolverCache globalResolverCache; 249 | 250 | template 251 | class ImportResolver 252 | { 253 | public: 254 | 255 | auto getAddress() const { return resolvedFuncAddress; } 256 | auto getModule() const { return hModule; } 257 | auto getDllName() const { return dllName; } 258 | auto getDllNameShort() const { return dllNameShort; } 259 | auto getFuncName() const { return funcName; } 260 | 261 | //auto operator()(Args... args) { return call(args...); } 262 | //auto call(Args... args) 263 | 264 | Ret operator()(Args... args) 265 | { 266 | auto test = ::GetModuleHandleA(dllName.c_str()); 267 | if (test == nullptr) 268 | { 269 | this->hModule = ::LoadLibraryA(dllName.c_str()); 270 | globalResolverCache.setCachedModuleBase(dllName, this->hModule); 271 | } 272 | 273 | //typedef Ret(WINAPI* funcType)(Args...); 274 | //funcType func = reinterpret_cast(resolvedFuncAddress); 275 | 276 | auto func = reinterpret_cast>(resolvedFuncAddress); 277 | return func(args...); 278 | } 279 | 280 | ImportResolver( 281 | std::string dllName, 282 | std::string funcName, 283 | bool _unhook = false, 284 | bool* _wasItHooked = nullptr 285 | ) 286 | : unhook(_unhook), wasItHooked(_wasItHooked), 287 | hModule(nullptr), dllNameShort("") 288 | { 289 | std::transform(dllName.begin(), dllName.end(), dllName.begin(), 290 | [](unsigned char c) { return std::tolower(c); }); 291 | 292 | this->dllName = dllName; 293 | this->funcName = funcName; 294 | this->dllNameShort = split(dllName, std::string("\\")).back(); 295 | 296 | FARPROC cached = globalResolverCache.getCached(dllName, funcName); 297 | if (cached != nullptr) 298 | { 299 | try 300 | { 301 | const char* ptr = reinterpret_cast(cached); 302 | for (size_t i = 0; i < 32; ptr[i++]); 303 | 304 | resolvedFuncAddress = cached; 305 | return; 306 | } 307 | catch (...) 308 | { 309 | globalResolverCache.invalidateFunction(dllName, funcName); 310 | cached = nullptr; 311 | } 312 | } 313 | 314 | this->hModule = globalResolverCache.getModule(dllName); 315 | try 316 | { 317 | const char* ptr = reinterpret_cast(this->hModule); 318 | for (size_t i = 0; i < 32; ptr[i++]); 319 | } 320 | catch (...) 321 | { 322 | this->hModule = nullptr; 323 | globalResolverCache.invalidateModule(dllName); 324 | } 325 | 326 | if (IsProcessWow64()) 327 | { 328 | // BUG: Hours of debugging led me to conclusion, that Resolver currently doesn't play very well 329 | // on Wow64 processes (x86 processes running on top of x64 OS). 330 | // 331 | // TODO: Until I get it sorted, need to remove unhooking logic for Wow64 :-( 332 | this->unhook = false; 333 | } 334 | 335 | assertLibraryLoaded(); 336 | 337 | // 338 | // GlobalAddAtomA despite being exported by Kernel32.dll requries user32.dll to be preloaded & initialized 339 | // in the first place to work fine, as originally experienced by Alex Ionescu: 340 | // http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2004-03/0851.html 341 | // and revisited here: 342 | // https://stackoverflow.com/questions/3577077/globaladdatom-returns-0-and-getlasterror-0x5-on-win7-works-on-xp 343 | // We are in position to apply hot-fix for that here 344 | // 345 | if (!funcName.rfind(OBFI_ASCII("GlobalAddAtom"), 0) || !funcName.rfind(OBFI_ASCII("GlobalGetAtomName"))) 346 | { 347 | const auto u = std::string(OBFI_ASCII("user32.dll")); 348 | globalResolverCache.setCachedModuleBase(u, ::LoadLibraryA(u.c_str())); 349 | } 350 | 351 | if (this->hModule == nullptr) { 352 | die(); 353 | } 354 | 355 | FARPROC func; 356 | this->funcOriginal = ::GetProcAddress(this->hModule, funcName.c_str()); 357 | 358 | if (unhook) 359 | { 360 | func = manualExportLookup(); 361 | 362 | // 363 | // Assert module is still loaded. There were cases where for instance advapi32.dll got 364 | // unloaded after resolving ReadProcMemory/VirtualQueryEx/others, leading to a crash. 365 | // 366 | auto prevModule = this->hModule; 367 | uintptr_t offset = reinterpret_cast(funcOriginal) 368 | - reinterpret_cast(this->hModule); 369 | 370 | assertLibraryLoaded(); 371 | 372 | if (this->hModule != prevModule) 373 | { 374 | auto a = (uintptr_t)(this->hModule); 375 | auto b = (uintptr_t)(offset); 376 | func = FARPROC(a + b); 377 | } 378 | } 379 | else 380 | { 381 | func = funcOriginal; 382 | } 383 | 384 | if (!func) { 385 | // 386 | // DID YOU SPECIFY CORRECT DLL LIBRARY NAME THAT EXPORTS GIVEN FUNCTION? 387 | // 388 | // This error may indicate that you were trying to resolve a function from a library that doesn't export it, like: 389 | // 390 | // RESOLVE(kernel32, MessageBoxA); // kernel32 doesn't export MessageBoxA, there should be user32 instead. 391 | // 392 | 393 | //die(); 394 | 395 | func = funcOriginal; 396 | if (!func) 397 | { 398 | auto mod = GetModuleHandleA(dllName.c_str()); 399 | if (!mod) 400 | { 401 | mod = ::LoadLibraryA(dllName.c_str()); 402 | } 403 | 404 | if (mod) 405 | { 406 | func = ::GetProcAddress(mod, funcName.c_str()); 407 | } 408 | 409 | if (!func) 410 | { 411 | die(); 412 | } 413 | } 414 | 415 | unhook = false; 416 | } 417 | 418 | if (unhook) 419 | { 420 | if (!checkIsAddressAvailable((uintptr_t)func)) 421 | { 422 | func = funcOriginal; 423 | unhook = false; 424 | } 425 | 426 | if (!unhookImport(func)) 427 | { 428 | } 429 | } 430 | 431 | globalResolverCache.setCachedFunction(dllName, funcName, func); 432 | resolvedFuncAddress = func; 433 | } 434 | 435 | bool checkIsAddressAvailable(uintptr_t address) 436 | { 437 | static auto _VirtualQuery = reinterpret_cast(nullptr); 438 | 439 | if (!_VirtualQuery) 440 | { 441 | _VirtualQuery = reinterpret_cast(::GetProcAddress( 442 | GetModuleHandleW(L"kernel32.dll"), 443 | OBFI_ASCII("VirtualQuery") 444 | )); 445 | } 446 | 447 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 448 | 449 | LPCVOID addr = (LPCVOID)address; 450 | 451 | if (_VirtualQuery != NULL && _VirtualQuery(addr, &mbi, sizeof(mbi)) != sizeof(mbi) 452 | || mbi.State == MEM_FREE || mbi.RegionSize < 0x1000 453 | || (mbi.Type != MEM_IMAGE && mbi.Type != MEM_MAPPED)) 454 | { 455 | return false; 456 | } 457 | 458 | return true; 459 | } 460 | 461 | void assertLibraryLoaded() 462 | { 463 | static const char* kernel = "kernel32.dll"; 464 | static const char* ntdll = "ntdll.dll"; 465 | 466 | auto dllNamePtr = strrchr(dllName.c_str(), '\\'); 467 | if (dllNamePtr == nullptr) dllNamePtr = dllName.c_str(); 468 | else dllNamePtr++; 469 | 470 | if (this->hModule != nullptr && (strcmp(dllNamePtr, kernel) != 0 && strcmp(dllNamePtr, ntdll) != 0)) 471 | { 472 | // Check if library is still loaded (despite having it's cached ImageBase) 473 | if (!checkIsAddressAvailable((uintptr_t)this->hModule)) 474 | { 475 | // If not, invalidate the cache entry and reload it. 476 | this->hModule = nullptr; 477 | } 478 | } 479 | 480 | if (!this->hModule) 481 | { 482 | bool loaded = false; 483 | 484 | { 485 | static auto _CreateToolhelp32Snapshot = reinterpret_cast(nullptr); 486 | if (!_CreateToolhelp32Snapshot) 487 | { 488 | _CreateToolhelp32Snapshot = reinterpret_cast(::GetProcAddress( 489 | GetModuleHandleW(L"kernel32.dll"), 490 | OBFI_ASCII("CreateToolhelp32Snapshot") 491 | )); 492 | } 493 | 494 | static auto _Module32FirstW = reinterpret_cast(nullptr); 495 | if (!_Module32FirstW) 496 | { 497 | _Module32FirstW = reinterpret_cast(::GetProcAddress( 498 | GetModuleHandleW(L"kernel32.dll"), 499 | OBFI_ASCII("Module32FirstW") 500 | )); 501 | } 502 | 503 | static auto _Module32NextW = reinterpret_cast(nullptr); 504 | if (!_Module32NextW) 505 | { 506 | _Module32NextW = reinterpret_cast(::GetProcAddress( 507 | GetModuleHandleW(L"kernel32.dll"), 508 | OBFI_ASCII("Module32NextW") 509 | )); 510 | } 511 | 512 | HANDLE hSnap = _CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); 513 | if (hSnap == nullptr || hSnap == (HANDLE)INVALID_HANDLE_VALUE) 514 | { 515 | return; 516 | } 517 | 518 | MODULEENTRY32W me32 = { 0 }; 519 | me32.dwSize = sizeof(MODULEENTRY32W); 520 | 521 | if (!_Module32FirstW(hSnap, &me32)) 522 | { 523 | CloseHandle(hSnap); 524 | return; 525 | } 526 | 527 | std::wstring wdllName(dllName.begin(), dllName.end()); 528 | 529 | do 530 | { 531 | auto modBase = reinterpret_cast(me32.modBaseAddr); 532 | auto hModulePtr = reinterpret_cast(hModule); 533 | 534 | if (stringicompare(std::wstring(me32.szExePath), wdllName)) 535 | { 536 | loaded = true; 537 | size_t a = (size_t)me32.modBaseAddr; 538 | size_t b = (size_t)this->hModule; 539 | 540 | if (a != b) 541 | { 542 | this->hModule = (HINSTANCE)me32.modBaseAddr; 543 | break; 544 | } 545 | } 546 | 547 | if (!_Module32NextW(hSnap, &me32)) 548 | { 549 | if (::GetLastError() != ERROR_NO_MORE_FILES) 550 | { 551 | return; 552 | } 553 | 554 | break; 555 | } 556 | } while (true); 557 | 558 | CloseHandle(hSnap); 559 | } 560 | 561 | if (!loaded) 562 | { 563 | this->hModule = ::LoadLibraryA(dllName.c_str()); 564 | 565 | globalResolverCache.setCachedModuleBase(dllName, this->hModule); 566 | } 567 | } 568 | } 569 | 570 | private: 571 | 572 | FARPROC funcOriginal; 573 | FARPROC resolvedFuncAddress; 574 | HINSTANCE hModule; 575 | std::string dllName; 576 | std::string dllNameShort; 577 | std::string funcName; 578 | 579 | bool unhook; 580 | bool* wasItHooked; 581 | 582 | inline std::wstring str_to_wstr(std::string input) 583 | { 584 | std::wstring out(input.begin(), input.end()); 585 | wchar_t tmp[128] = { 0 }; 586 | wcscpy_s(tmp, out.c_str()); 587 | return OBFI(tmp); 588 | } 589 | 590 | FARPROC manualExportLookup() 591 | { 592 | PE peModule; 593 | bool res = peModule.AnalyseProcessModule(0, hModule, true, true); 594 | 595 | if (!res) 596 | { 597 | return nullptr; 598 | } 599 | 600 | EXPORTED_FUNCTION exportEntry; 601 | 602 | if (!peModule.getExport(this->funcName.c_str(), &exportEntry)) 603 | { 604 | return nullptr; 605 | } 606 | 607 | auto resolved = reinterpret_cast(hModule) + exportEntry.dwPtrValueRVA; 608 | if (exportEntry.bIsForwarded) 609 | { 610 | auto fwd = std::string(exportEntry.szForwarder); 611 | 612 | std::string moduleFwd(split(std::string(exportEntry.szForwarder), std::string(".")).front()); 613 | moduleFwd += OBFI_ASCII(".dll"); 614 | 615 | auto importDesc = std::find_if( 616 | peModule.vImportDescriptors.begin(), 617 | peModule.vImportDescriptors.end(), 618 | [&moduleFwd](const __IMAGE_IMPORT_DESCRIPTOR& f) { 619 | return (!strcmp(f.szName, moduleFwd.c_str())); 620 | } 621 | ); 622 | 623 | if (importDesc == peModule.vImportDescriptors.end()) 624 | { 625 | return nullptr; 626 | } 627 | 628 | IMPORTED_FUNCTION fwdImport; 629 | if (!peModule.getImport(this->funcName.c_str(), &fwdImport)) 630 | { 631 | return nullptr; 632 | } 633 | 634 | auto path = split(dllName, std::string("\\")); 635 | path.pop_back(); 636 | 637 | this->dllName = ""; 638 | for (auto p : path) 639 | { 640 | this->dllName += p + "\\"; 641 | } 642 | 643 | if (moduleFwd.find(OBFI_ASCII("api-ms-win-core-"), 0) == 0) 644 | { 645 | auto _dllNameShort = this->dllNameShort; 646 | auto _dllName = this->dllName; 647 | auto _hModule = this->hModule; 648 | 649 | this->dllNameShort = OBFI_ASCII("kernelbase.dll"); 650 | this->dllName = this->dllNameShort; 651 | this->hModule = LoadLibraryA(this->dllNameShort.c_str()); 652 | 653 | auto out = manualExportLookup(); 654 | if (!out) 655 | { 656 | this->dllNameShort = _dllNameShort; 657 | this->dllName = _dllName + _dllNameShort; 658 | this->hModule = _hModule; 659 | unhook = false; 660 | 661 | return ::GetProcAddress(_hModule, this->funcName.c_str()); 662 | } 663 | 664 | return out; 665 | } 666 | else 667 | { 668 | auto _dllNameShort = this->dllNameShort; 669 | auto _dllName = this->dllName; 670 | auto _hModule = this->hModule; 671 | 672 | this->dllNameShort = moduleFwd; 673 | this->dllName += this->dllNameShort; 674 | this->hModule = LoadLibraryA(this->dllNameShort.c_str()); 675 | 676 | auto out = manualExportLookup(); 677 | if (!out) 678 | { 679 | this->dllNameShort = _dllNameShort; 680 | this->dllName = _dllName + _dllNameShort; 681 | this->hModule = _hModule; 682 | unhook = false; 683 | 684 | return ::GetProcAddress(_hModule, this->funcName.c_str()); 685 | } 686 | 687 | return out; 688 | } 689 | } 690 | else 691 | { 692 | IMPORTED_FUNCTION importEntry; 693 | 694 | if (peModule.getImport(this->funcName.c_str(), &importEntry)) 695 | { 696 | // Probably there is API SET redirection involved, example: 697 | // kernel32!CreateFileW -> jmp -> kernelbase.dll!CreateFileW 698 | 699 | if (importEntry.uImpDescriptorIndex >= 0 && importEntry.uImpDescriptorIndex < peModule.vImportDescriptors.size()) 700 | { 701 | auto importDescriptor = peModule.vImportDescriptors[importEntry.uImpDescriptorIndex]; 702 | auto moduleFwd = std::string(importDescriptor.szName); 703 | 704 | auto path = split(dllName, std::string("\\")); 705 | path.pop_back(); 706 | 707 | this->dllName = ""; 708 | for (auto p : path) 709 | { 710 | this->dllName += p + "\\"; 711 | } 712 | 713 | if (moduleFwd.find(OBFI_ASCII("api-ms-win-core-"), 0) == 0) 714 | { 715 | auto _dllNameShort = this->dllNameShort; 716 | auto _dllName = this->dllName; 717 | auto _hModule = this->hModule; 718 | 719 | this->dllNameShort = OBFI_ASCII("kernelbase.dll"); 720 | this->dllName += this->dllNameShort; 721 | this->hModule = LoadLibraryA(this->dllNameShort.c_str()); 722 | 723 | auto out = manualExportLookup(); 724 | if (!out) 725 | { 726 | this->dllNameShort = _dllNameShort; 727 | this->dllName = _dllName + _dllNameShort; 728 | this->hModule = _hModule; 729 | unhook = false; 730 | 731 | return ::GetProcAddress(_hModule, this->funcName.c_str()); 732 | } 733 | 734 | return out; 735 | } 736 | } 737 | } 738 | } 739 | 740 | return reinterpret_cast(resolved); 741 | } 742 | 743 | bool IsProcessWow64() 744 | { 745 | typedef BOOL(WINAPI* typeIsWow64Process) (HANDLE, PBOOL); 746 | auto pIsWow64Process = (typeIsWow64Process)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); 747 | 748 | static BOOL bIsWow64 = FALSE; 749 | static BOOL once = false; 750 | 751 | if (once) return bIsWow64; 752 | 753 | if (pIsWow64Process != nullptr) 754 | { 755 | if (!pIsWow64Process(GetCurrentProcess(), &bIsWow64)) 756 | { 757 | //return false; 758 | } 759 | } 760 | 761 | once = true; 762 | return bIsWow64; 763 | } 764 | 765 | 766 | bool flipPageGuards(bool disable, uintptr_t hModule, std::vector* guardedAllocs) 767 | { 768 | #ifdef _DEBUG 769 | // DEBUG!!! 770 | return true; 771 | #endif 772 | 773 | if (hModule == 0) hModule = (uintptr_t)GetModuleHandle(NULL); 774 | 775 | PIMAGE_DOS_HEADER dosHdr = (PIMAGE_DOS_HEADER)hModule; 776 | PIMAGE_NT_HEADERS32 ntHdr = (PIMAGE_NT_HEADERS32)((uintptr_t)dosHdr->e_lfanew + hModule); 777 | 778 | DWORD sizeOfImage = 0; 779 | if (ntHdr->FileHeader.Machine & IMAGE_FILE_MACHINE_I386) 780 | { 781 | sizeOfImage = ntHdr->OptionalHeader.SizeOfImage; 782 | } 783 | else 784 | { 785 | PIMAGE_NT_HEADERS64 ntHdr = (PIMAGE_NT_HEADERS64)((uintptr_t)dosHdr->e_lfanew + hModule); 786 | sizeOfImage = ntHdr->OptionalHeader.SizeOfImage; 787 | } 788 | 789 | uint8_t* address = 0; 790 | const size_t MaxSize = (sizeof(ULONG_PTR) == 4) ? ((1ULL << 31) - 1) : ((1ULL << 63) - 1); 791 | 792 | if (disable) 793 | { 794 | while (reinterpret_cast(address) < MaxSize) 795 | { 796 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 797 | if (!VirtualQuery(address, &mbi, sizeof(mbi))) 798 | { 799 | break; 800 | } 801 | 802 | if ((uintptr_t)mbi.BaseAddress >= (uintptr_t)hModule 803 | && (uintptr_t)((uintptr_t)mbi.BaseAddress + mbi.RegionSize) < (uintptr_t)((uintptr_t)hModule + sizeOfImage)) 804 | { 805 | if (mbi.Protect & PAGE_GUARD) 806 | { 807 | DWORD oldProtect = 0; 808 | DWORD newProtect = mbi.Protect & (~PAGE_GUARD); 809 | 810 | VirtualProtect(mbi.BaseAddress, mbi.RegionSize, newProtect, &oldProtect); 811 | if (guardedAllocs != nullptr) guardedAllocs->push_back(mbi); 812 | } 813 | } 814 | 815 | address += mbi.RegionSize; 816 | } 817 | } 818 | else 819 | { 820 | if (guardedAllocs != nullptr && guardedAllocs->size() > 0) 821 | { 822 | for (const auto& mbi : *guardedAllocs) 823 | { 824 | DWORD oldProtect = 0; 825 | VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &oldProtect); 826 | } 827 | } 828 | } 829 | 830 | return true; 831 | } 832 | 833 | bool unhookImport(FARPROC funcAddress) 834 | { 835 | if (!this->unhook) return true; 836 | 837 | const size_t Max_Bytes_Of_Function_To_Check = 32; 838 | 839 | uint8_t currentImportStub[Max_Bytes_Of_Function_To_Check] = { 0 }; 840 | uint8_t inFileImportStub[Max_Bytes_Of_Function_To_Check] = { 0 }; 841 | 842 | memcpy(currentImportStub, funcAddress, sizeof(currentImportStub)); 843 | 844 | PE peLibraryFile; 845 | if (!peLibraryFile.AnalyseFile(dllName, true)) 846 | { 847 | return false; 848 | } 849 | 850 | if (!peLibraryFile.ApplyAllRelocs((ULONGLONG)this->hModule)) 851 | { 852 | return false; 853 | } 854 | 855 | EXPORTED_FUNCTION exportEntry; 856 | 857 | if (peLibraryFile.getExport(this->funcName.c_str(), &exportEntry)) 858 | { 859 | DWORD funcAddr = 0; 860 | if (!peLibraryFile.ReadBytes(&funcAddr, sizeof(DWORD), exportEntry.dwThunkRVA, PE::AccessMethod::File_Begin)) 861 | { 862 | return false; 863 | } 864 | 865 | if (!peLibraryFile.ReadBytes(inFileImportStub, sizeof(inFileImportStub), peLibraryFile.RVA2RAW(funcAddr), PE::AccessMethod::File_Begin)) 866 | { 867 | return false; 868 | } 869 | } 870 | else 871 | { 872 | return false; 873 | } 874 | 875 | // Step 1: Inspect current process' IAT entry 876 | { 877 | std::vector flippedPages; 878 | if (!flipPageGuards(true, 0, &flippedPages)) 879 | { 880 | return false; 881 | } 882 | 883 | PE currProcess; 884 | if (!currProcess.AnalyseProcess(0, false)) 885 | { 886 | flipPageGuards(false, 0, &flippedPages); 887 | return false; 888 | } 889 | 890 | IMPORTED_FUNCTION importedFunc; 891 | 892 | if (currProcess.getImport(this->funcName.c_str(), &importedFunc)) 893 | { 894 | const DWORD origExportRVA = exportEntry.dwPtrValueRVA; 895 | const DWORD currThunkRVA = static_cast(reinterpret_cast(funcAddress) - reinterpret_cast(hModule)); 896 | 897 | if (origExportRVA != currThunkRVA) 898 | { 899 | if (wasItHooked != nullptr) 900 | { 901 | *wasItHooked = true; 902 | } 903 | 904 | // IAT hijacked 905 | const ULONGLONG restore = reinterpret_cast(hModule) + origExportRVA; 906 | 907 | currProcess.HookIAT(funcName, restore); 908 | } 909 | } 910 | else 911 | { 912 | // Possibly we're not importing this function explicitly, that's fine. 913 | } 914 | 915 | if (!flipPageGuards(false, 0, &flippedPages)) 916 | { 917 | return false; 918 | } 919 | } 920 | 921 | // Step 2: Check for hijacked EAT entries. 922 | { 923 | std::vector flippedPages; 924 | if (!flipPageGuards(true, (uintptr_t)hModule, &flippedPages)) 925 | { 926 | return false; 927 | } 928 | 929 | PE mappedLib; 930 | if (!mappedLib.AnalyseProcessModule(0, hModule, false, true)) 931 | { 932 | flipPageGuards(false, (uintptr_t)hModule, &flippedPages); 933 | return false; 934 | } 935 | 936 | EXPORTED_FUNCTION inMemoryExportEntry; 937 | 938 | if (mappedLib.getExport(funcName.c_str(), &inMemoryExportEntry)) 939 | { 940 | auto addr = static_cast(mappedLib.RVA2RAW(inMemoryExportEntry.dwThunkRVA)); 941 | const DWORD origExportRVA = exportEntry.dwPtrValueRVA; 942 | //const DWORD currThunkRVA = static_cast(reinterpret_cast(funcAddress) - reinterpret_cast(hModule)); 943 | const DWORD currThunkRVA = inMemoryExportEntry.dwPtrValueRVA; 944 | 945 | if (origExportRVA != currThunkRVA && !inMemoryExportEntry.bIsForwarded) 946 | { 947 | if (wasItHooked != nullptr) 948 | { 949 | *wasItHooked = true; 950 | } 951 | 952 | // EAT hijacked 953 | const DWORD restore = origExportRVA; 954 | mappedLib.HookEAT(funcName, restore); 955 | } 956 | } 957 | else 958 | { 959 | flipPageGuards(false, (uintptr_t)hModule, &flippedPages); 960 | return false; 961 | } 962 | 963 | if (!flipPageGuards(false, (uintptr_t)hModule, &flippedPages)) 964 | { 965 | return false; 966 | } 967 | } 968 | 969 | // Step 3: Check for hooked import's stub. 970 | if (memcmp(currentImportStub, inFileImportStub, Max_Bytes_Of_Function_To_Check) != 0) 971 | { 972 | if (wasItHooked != nullptr) 973 | { 974 | *wasItHooked = true; 975 | } 976 | 977 | DWORD old, old2; 978 | if (VirtualProtect(funcAddress, Max_Bytes_Of_Function_To_Check, PAGE_EXECUTE_READWRITE, &old)) 979 | { 980 | //if (peLibraryFile.ApplyRelocsInBuffer(reinterpret_cast(hModule), 981 | // exportEntry.dwThunkRVA, inFileImportStub, Max_Bytes_Of_Function_To_Check)) 982 | { 983 | for (size_t u = 0; u < Max_Bytes_Of_Function_To_Check; u++) 984 | { 985 | if (currentImportStub[u] != inFileImportStub[u]) 986 | { 987 | reinterpret_cast(funcAddress)[u] = inFileImportStub[u]; 988 | } 989 | } 990 | } 991 | 992 | return VirtualProtect(funcAddress, Max_Bytes_Of_Function_To_Check, old, &old2); 993 | } 994 | } 995 | 996 | if (wasItHooked != nullptr) 997 | { 998 | *wasItHooked = false; 999 | } 1000 | 1001 | return true; 1002 | } 1003 | }; 1004 | } -------------------------------------------------------------------------------- /UnhookMe/usings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace { 18 | 19 | // 20 | // This is how we specify API type definition for use with RESOLVE(...) 21 | // 22 | using fn_MessageBoxW = int WINAPI( 23 | HWND hWnd, 24 | LPCWSTR lpText, 25 | LPCWSTR lpCaption, 26 | UINT uType 27 | ); 28 | 29 | using fn_NtOpenProcess = NTSTATUS NTAPI( 30 | PHANDLE ProcessHandle, 31 | ACCESS_MASK DesiredAccess, 32 | POBJECT_ATTRIBUTES ObjectAttributes, 33 | CLIENT_ID* ClientId 34 | ); 35 | 36 | using fn_InitializeProcThreadAttributeList = BOOL NTAPI( 37 | LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, 38 | DWORD dwAttributeCount, 39 | DWORD dwFlags, 40 | PSIZE_T lpSize 41 | ); 42 | 43 | using fn_UpdateProcThreadAttribute = BOOL NTAPI( 44 | LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, 45 | DWORD dwFlags, 46 | DWORD_PTR Attribute, 47 | PVOID lpValue, 48 | SIZE_T cbSize, 49 | PVOID lpPreviousValue, 50 | PSIZE_T lpReturnSize 51 | ); 52 | 53 | using fn_NtUnmapViewOfSection = NTSTATUS NTAPI( 54 | HANDLE ProcessHandle, 55 | PVOID BaseAddress 56 | ); 57 | 58 | using fn_NtQueryInformationProcess = NTSTATUS NTAPI( 59 | HANDLE ProcessHandle, 60 | DWORD ProcessInformationClass, 61 | PVOID ProcessInformation, 62 | DWORD ProcessInformationLength, 63 | PDWORD ReturnLength 64 | ); 65 | 66 | using fn_NtQuerySystemInformation = NTSTATUS NTAPI( 67 | IN SYSTEM_INFORMATION_CLASS SystemInformationClass, 68 | OUT PVOID SystemInformation, 69 | IN ULONG SystemInformationLength, 70 | OUT PULONG ReturnLength OPTIONAL 71 | ); 72 | 73 | using fn_RtlCreateUserThread = NTSTATUS NTAPI( 74 | HANDLE ProcessHandle, 75 | PSECURITY_DESCRIPTOR SecurityDescriptor, 76 | BOOLEAN CreateSuspended, 77 | ULONG StackZeroBits, 78 | SIZE_T StackReserve, 79 | SIZE_T StackCommit, 80 | PTHREAD_START_ROUTINE StartAddress, 81 | PVOID Parameter, 82 | PHANDLE ThreadHandle, 83 | CLIENT_ID* ClientId 84 | ); 85 | 86 | using fn_NtQueueApcThreadEx = NTSTATUS NTAPI( 87 | HANDLE ThreadHandle, 88 | HANDLE UserApcReserveHandle, 89 | PVOID ApcRoutine, 90 | PVOID ApcRoutineContext, 91 | PVOID ApcStatusBlock, 92 | PVOID ApcReserved 93 | ); 94 | 95 | using fn_CreateEventW = HANDLE WINAPI( 96 | LPSECURITY_ATTRIBUTES lpEventAttributes, 97 | BOOL bManualReset, 98 | BOOL bInitialState, 99 | LPCWSTR lpName 100 | ); 101 | 102 | using fn_Sleep = void WINAPI( 103 | DWORD dwMilliseconds 104 | ); 105 | 106 | using fn_RegisterServiceCtrlHandlerW = SERVICE_STATUS_HANDLE WINAPI( 107 | LPCWSTR lpServiceName, 108 | LPHANDLER_FUNCTION lpHandlerProc 109 | ); 110 | 111 | using fn_SetServiceStatus = BOOL WINAPI( 112 | SERVICE_STATUS_HANDLE hServiceStatus, 113 | LPSERVICE_STATUS lpServiceStatus 114 | ); 115 | 116 | using fn_WriteFile = BOOL WINAPI( 117 | HANDLE hFile, 118 | LPCVOID lpBuffer, 119 | DWORD nNumberOfBytesToWrite, 120 | LPDWORD lpNumberOfBytesWritten, 121 | LPOVERLAPPED lpOverlapped 122 | ); 123 | 124 | using fn_SetFilePointer = DWORD WINAPI( 125 | HANDLE hFile, 126 | LONG lDistanceToMove, 127 | PLONG lpDistanceToMoveHigh, 128 | DWORD dwMoveMethod 129 | ); 130 | 131 | using fn_ZwSetInformationThread = NTSTATUS NTAPI( 132 | HANDLE ThreadHandle, 133 | LONG ThreadInformationClass, 134 | PVOID ThreadInformation, 135 | ULONG ThreadInformationLength 136 | ); 137 | 138 | using fn_OpenProcess = HANDLE WINAPI( 139 | DWORD dwDesiredAccess, 140 | BOOL bInheritHandle, 141 | DWORD dwProcessId 142 | ); 143 | 144 | using fn_VirtualProtectEx = BOOL WINAPI( 145 | HANDLE hProcess, 146 | LPVOID lpAddress, 147 | SIZE_T dwSize, 148 | DWORD flNewProtect, 149 | PDWORD lpflOldProtect 150 | ); 151 | 152 | using fn_QueueUserAPC = DWORD WINAPI( 153 | PVOID pfnAPC, 154 | HANDLE hThread, 155 | ULONG_PTR dwData 156 | ); 157 | 158 | using fn_ReadProcessMemory = BOOL WINAPI( 159 | HANDLE hProcess, 160 | LPCVOID lpBaseAddress, 161 | LPVOID lpBuffer, 162 | SIZE_T nSize, 163 | SIZE_T *lpNumberOfBytesRead 164 | ); 165 | 166 | using fn_NtReadVirtualMemory = NTSTATUS NTAPI( 167 | IN HANDLE ProcessHandle, 168 | IN PVOID BaseAddress, 169 | OUT PVOID Buffer, 170 | IN ULONG NumberOfBytesToRead, 171 | OUT PULONG NumberOfBytesReaded OPTIONAL 172 | ); 173 | 174 | using fn_NtWriteVirtualMemory = NTSTATUS NTAPI( 175 | IN HANDLE ProcessHandle, 176 | IN PVOID BaseAddress, 177 | IN PVOID Buffer, 178 | IN ULONG NumberOfBytesToWrite, 179 | OUT PULONG NumberOfBytesWritten OPTIONAL 180 | ); 181 | 182 | using fn_WriteProcessMemory = BOOL WINAPI( 183 | HANDLE hProcess, 184 | LPVOID lpBaseAddress, 185 | LPCVOID lpBuffer, 186 | SIZE_T nSize, 187 | SIZE_T *lpNumberOfBytesWritten 188 | ); 189 | 190 | using fn_NtGetContextThread = BOOL NTAPI( 191 | HANDLE hThread, 192 | LPCONTEXT lpContext 193 | ); 194 | 195 | using fn_NtSetContextThread = BOOL NTAPI( 196 | HANDLE hThread, 197 | LPCONTEXT lpContext 198 | ); 199 | 200 | using fn_GetThreadContext = BOOL WINAPI( 201 | HANDLE hThread, 202 | LPCONTEXT lpContext 203 | ); 204 | 205 | using fn_GetUserNameW = BOOL WINAPI( 206 | LPWSTR lpBuffer, 207 | LPDWORD pcbBuffer 208 | ); 209 | 210 | using fn_GetComputerNameW = BOOL WINAPI( 211 | LPWSTR lpBuffer, 212 | LPDWORD nSize 213 | ); 214 | 215 | using fn_SetThreadContext = BOOL WINAPI( 216 | HANDLE hThread, 217 | LPCONTEXT lpContext 218 | ); 219 | 220 | using fn_CreateToolhelp32Snapshot = HANDLE WINAPI( 221 | DWORD dwFlags, 222 | DWORD th32ProcessID 223 | ); 224 | 225 | using fn_Process32FirstW = BOOL WINAPI( 226 | HANDLE hSnapshot, 227 | LPPROCESSENTRY32W lppe 228 | ); 229 | 230 | using fn_Process32NextW = BOOL WINAPI( 231 | HANDLE hSnapshot, 232 | LPPROCESSENTRY32W lppe 233 | ); 234 | 235 | using fn_OpenThreadToken = BOOL WINAPI( 236 | HANDLE ThreadHandle, 237 | DWORD DesiredAccess, 238 | BOOL OpenAsSelf, 239 | PHANDLE TokenHandle 240 | ); 241 | 242 | using fn_CreateRemoteThread = HANDLE WINAPI( 243 | HANDLE hProcess, 244 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 245 | SIZE_T dwStackSize, 246 | LPTHREAD_START_ROUTINE lpStartAddress, 247 | LPVOID lpParameter, 248 | DWORD dwCreationFlags, 249 | LPDWORD lpThreadId 250 | ); 251 | 252 | using fn_CreateThread = HANDLE WINAPI( 253 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 254 | SIZE_T dwStackSize, 255 | LPTHREAD_START_ROUTINE lpStartAddress, 256 | LPVOID lpParameter, 257 | DWORD dwCreationFlags, 258 | LPDWORD lpThreadId 259 | ); 260 | 261 | using fn_VirtualAllocEx = LPVOID WINAPI( 262 | HANDLE hProcess, 263 | LPVOID lpAddress, 264 | SIZE_T dwSize, 265 | DWORD flAllocationType, 266 | DWORD flProtect 267 | ); 268 | 269 | using fn_CreateFileMappingA = HANDLE WINAPI( 270 | HANDLE hFile, 271 | LPSECURITY_ATTRIBUTES lpFileMappingAttributes, 272 | DWORD flProtect, 273 | DWORD dwMaximumSizeHigh, 274 | DWORD dwMaximumSizeLow, 275 | LPCSTR lpName 276 | ); 277 | 278 | using fn_CreateFileMappingW = HANDLE WINAPI( 279 | HANDLE hFile, 280 | LPSECURITY_ATTRIBUTES lpFileMappingAttributes, 281 | DWORD flProtect, 282 | DWORD dwMaximumSizeHigh, 283 | DWORD dwMaximumSizeLow, 284 | LPCWSTR lpName 285 | ); 286 | 287 | using fn_MapViewOfFile = LPVOID WINAPI( 288 | HANDLE hFileMappingObject, 289 | DWORD dwDesiredAccess, 290 | DWORD dwFileOffsetHigh, 291 | DWORD dwFileOffsetLow, 292 | SIZE_T dwNumberOfBytesToMap 293 | ); 294 | 295 | using fn_VirtualAlloc = LPVOID WINAPI( 296 | LPVOID lpAddress, 297 | SIZE_T dwSize, 298 | DWORD flAllocationType, 299 | DWORD flProtect 300 | ); 301 | 302 | using fn_LookupPrivilegeValueW = BOOL WINAPI( 303 | LPCWSTR lpSystemName, 304 | LPCWSTR lpName, 305 | PLUID lpLuid 306 | ); 307 | 308 | using fn_AdjustTokenPrivileges = BOOL WINAPI( 309 | HANDLE TokenHandle, 310 | BOOL DisableAllPrivileges, 311 | PTOKEN_PRIVILEGES NewState, 312 | DWORD BufferLength, 313 | PTOKEN_PRIVILEGES PreviousState, 314 | PDWORD ReturnLength 315 | ); 316 | 317 | using fn_CreateProcessW = BOOL WINAPI( 318 | LPCWSTR lpApplicationName, 319 | LPWSTR lpCommandLine, 320 | LPSECURITY_ATTRIBUTES lpProcessAttributes, 321 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 322 | BOOL bInheritHandles, 323 | DWORD dwCreationFlags, 324 | LPVOID lpEnvironment, 325 | LPCWSTR lpCurrentDirectory, 326 | LPSTARTUPINFOW lpStartupInfo, 327 | LPPROCESS_INFORMATION lpProcessInformation 328 | ); 329 | 330 | using fn_OpenProcessToken = BOOL WINAPI( 331 | HANDLE ProcessHandle, 332 | DWORD DesiredAccess, 333 | PHANDLE TokenHandle 334 | ); 335 | 336 | using fn_GetTokenInformation = BOOL WINAPI( 337 | HANDLE TokenHandle, 338 | DWORD TokenInformationClass, 339 | LPVOID TokenInformation, 340 | DWORD TokenInformationLength, 341 | PDWORD ReturnLength 342 | ); 343 | 344 | using fn_NtAllocateVirtualMemory = NTSTATUS NTAPI( 345 | HANDLE ProcessHandle, 346 | PVOID *BaseAddress, 347 | ULONG_PTR ZeroBits, 348 | PSIZE_T RegionSize, 349 | ULONG AllocationType, 350 | ULONG Protect 351 | ); 352 | 353 | using fn_VirtualAllocExNuma = LPVOID WINAPI( 354 | HANDLE hProcess, 355 | LPVOID lpAddress, 356 | SIZE_T dwSize, 357 | DWORD flAllocationType, 358 | DWORD flProtect, 359 | DWORD nndPreferred 360 | ); 361 | 362 | using fn_VirtualFreeEx = BOOL WINAPI( 363 | HANDLE hProcess, 364 | LPVOID lpAddress, 365 | SIZE_T dwSize, 366 | DWORD dwFreeType 367 | ); 368 | 369 | using fn_ZwCreateSection = NTSTATUS NTAPI( 370 | PHANDLE SectionHandle, 371 | ACCESS_MASK DesiredAccess, 372 | POBJECT_ATTRIBUTES ObjectAttributes, 373 | PLARGE_INTEGER MaximumSize, 374 | ULONG SectionPageProtection, 375 | ULONG AllocationAttributes, 376 | HANDLE FileHandle 377 | ); 378 | 379 | using fn_NtMapViewOfSection = NTSTATUS NTAPI( 380 | HANDLE SectionHandle, 381 | HANDLE ProcessHandle, 382 | PVOID *BaseAddress, 383 | ULONG_PTR ZeroBits, 384 | SIZE_T CommitSize, 385 | PLARGE_INTEGER SectionOffset, 386 | PSIZE_T ViewSize, 387 | DWORD InheritDisposition, 388 | ULONG AllocationType, 389 | ULONG Win32Protect 390 | ); 391 | 392 | //SOURCE: http://processhacker.sourceforge.net/doc/ntpsapi_8h_source.html 393 | #define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001 394 | #define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002 // ? 395 | #define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004 396 | #define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010 // ? 397 | #define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020 // ? 398 | #define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080 399 | 400 | using fn_ZwCreateThreadEx = NTSTATUS NTAPI( 401 | PHANDLE ThreadHandle, 402 | ACCESS_MASK DesiredAccess, 403 | POBJECT_ATTRIBUTES ObjectAttributes, 404 | HANDLE ProcessHandle, 405 | PVOID StartRoutine, 406 | PVOID Argument, 407 | ULONG CreateFlags, 408 | ULONG_PTR ZeroBits, 409 | SIZE_T StackSize, 410 | SIZE_T MaximumStackSize, 411 | PVOID AttributeList 412 | ); 413 | 414 | using fn_ZwUnmapViewOfSection = NTSTATUS NTAPI( 415 | HANDLE ProcessHandle, 416 | PVOID BaseAddress 417 | ); 418 | 419 | using fn_ZwClose = NTSTATUS NTAPI( 420 | HANDLE Handle 421 | ); 422 | 423 | using fn_NtProtectVirtualMemory = NTSTATUS NTAPI( 424 | HANDLE ProcessHandle, 425 | PVOID *BaseAddress, 426 | PULONG NumberOfBytesToProtect, 427 | ULONG NewAccessProtection, 428 | PULONG OldAccessProtection 429 | ); 430 | 431 | using fn_NtOpenThread = NTSTATUS WINAPI( 432 | _Out_ PHANDLE ThreadHandle, 433 | _In_ ACCESS_MASK DesiredAccess, 434 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 435 | _In_ CLIENT_ID *ClientId 436 | ); 437 | 438 | using fn_OpenThread = HANDLE WINAPI( 439 | DWORD dwDesiredAccess, 440 | BOOL bInheritHandle, 441 | DWORD dwThreadId 442 | ); 443 | 444 | using fn_Thread32First = BOOL WINAPI( 445 | HANDLE hSnapshot, 446 | LPTHREADENTRY32 lpte 447 | ); 448 | 449 | using fn_Thread32Next = BOOL WINAPI( 450 | HANDLE hSnapshot, 451 | LPTHREADENTRY32 lpte 452 | ); 453 | 454 | using fn_EnumChildWindows = BOOL WINAPI( 455 | HWND hWndParent, 456 | WNDENUMPROC lpEnumFunc, 457 | LPARAM lParam 458 | ); 459 | 460 | using fn_EnumPropsExW = int WINAPI( 461 | HWND hWnd, 462 | PROPENUMPROCEXW lpEnumFunc, 463 | LPARAM lParam 464 | ); 465 | 466 | using fn_EnumWindows = BOOL WINAPI( 467 | WNDENUMPROC lpEnumFunc, 468 | LPARAM lParam 469 | ); 470 | 471 | using fn_GetPropW = HANDLE WINAPI( 472 | HWND hWnd, 473 | LPCWSTR lpString 474 | ); 475 | 476 | using fn_SetPropW = BOOL WINAPI( 477 | HWND hWnd, 478 | LPCWSTR lpString, 479 | HANDLE hData 480 | ); 481 | 482 | using fn_UnmapViewOfFile = BOOL WINAPI( 483 | LPCVOID lpBaseAddress 484 | ); 485 | 486 | using fn_NtQueryInformationProcess = NTSTATUS NTAPI( 487 | HANDLE ProcessHandle, 488 | DWORD ProcessInformationClass, 489 | PVOID ProcessInformation, 490 | DWORD ProcessInformationLength, 491 | PDWORD ReturnLength 492 | ); 493 | 494 | using fn_CreateFileA = HANDLE WINAPI( 495 | LPCSTR lpFileName, 496 | DWORD dwDesiredAccess, 497 | DWORD dwShareMode, 498 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 499 | DWORD dwCreationDisposition, 500 | DWORD dwFlagsAndAttributes, 501 | HANDLE hTemplateFile 502 | ); 503 | 504 | using fn_CreateToolhelp32Snapshot = HANDLE WINAPI( 505 | DWORD dwFlags, 506 | DWORD th32ProcessID 507 | ); 508 | 509 | using fn_Module32FirstW = BOOL WINAPI( 510 | HANDLE hSnapshot, 511 | LPMODULEENTRY32W lppe 512 | ); 513 | 514 | using fn_Module32NextW = BOOL WINAPI( 515 | HANDLE hSnapshot, 516 | LPMODULEENTRY32W lppe 517 | ); 518 | 519 | using fn_OpenProcess = HANDLE WINAPI( 520 | DWORD dwDesiredAccess, 521 | BOOL bInheritHandle, 522 | DWORD dwProcessId 523 | ); 524 | 525 | using fn_MapViewOfFile = LPVOID WINAPI( 526 | HANDLE hFileMappingObject, 527 | DWORD dwDesiredAccess, 528 | DWORD dwFileOffsetHigh, 529 | DWORD dwFileOffsetLow, 530 | SIZE_T dwNumberOfBytesToMap 531 | ); 532 | 533 | using fn_VirtualProtectEx = BOOL WINAPI( 534 | HANDLE hProcess, 535 | LPVOID lpAddress, 536 | SIZE_T dwSize, 537 | DWORD flNewProtect, 538 | PDWORD lpflOldProtect 539 | ); 540 | 541 | using fn_ReadProcessMemory = BOOL WINAPI( 542 | HANDLE hProcess, 543 | LPCVOID lpBaseAddress, 544 | LPVOID lpBuffer, 545 | SIZE_T nSize, 546 | SIZE_T *lpNumberOfBytesRead 547 | ); 548 | 549 | using fn_WriteProcessMemory = BOOL WINAPI( 550 | HANDLE hProcess, 551 | LPVOID lpBaseAddress, 552 | LPCVOID lpBuffer, 553 | SIZE_T nSize, 554 | SIZE_T *lpNumberOfBytesWritten 555 | ); 556 | 557 | using fn_VirtualQueryEx = SIZE_T WINAPI( 558 | HANDLE hProcess, 559 | LPCVOID lpAddress, 560 | PMEMORY_BASIC_INFORMATION lpBuffer, 561 | SIZE_T dwLength 562 | ); 563 | 564 | using fn_NtUnmapViewOfSection = NTSTATUS NTAPI( 565 | HANDLE ProcessHandle, 566 | PVOID BaseAddress 567 | ); 568 | 569 | using fn_CreateEventW = HANDLE WINAPI( 570 | LPSECURITY_ATTRIBUTES lpEventAttributes, 571 | BOOL bManualReset, 572 | BOOL bInitialState, 573 | LPCWSTR lpName 574 | ); 575 | 576 | using fn_SetEvent = BOOL WINAPI( 577 | HANDLE hEvent 578 | ); 579 | 580 | typedef enum _SE_OBJECT_TYPE { 581 | SE_UNKNOWN_OBJECT_TYPE, 582 | SE_FILE_OBJECT, 583 | SE_SERVICE, 584 | SE_PRINTER, 585 | SE_REGISTRY_KEY, 586 | SE_LMSHARE, 587 | SE_KERNEL_OBJECT, 588 | SE_WINDOW_OBJECT, 589 | SE_DS_OBJECT, 590 | SE_DS_OBJECT_ALL, 591 | SE_PROVIDER_DEFINED_OBJECT, 592 | SE_WMIGUID_OBJECT, 593 | SE_REGISTRY_WOW64_32KEY, 594 | SE_REGISTRY_WOW64_64KEY 595 | } SE_OBJECT_TYPE; 596 | 597 | using fn_SetSecurityInfo = DWORD WINAPI( 598 | HANDLE handle, 599 | SE_OBJECT_TYPE ObjectType, 600 | SECURITY_INFORMATION SecurityInfo, 601 | PSID psidOwner, 602 | PSID psidGroup, 603 | PACL pDacl, 604 | PACL pSacl 605 | ); 606 | 607 | using fn_GlobalAddAtomA = ATOM WINAPI( 608 | LPCSTR lpString 609 | ); 610 | 611 | using fn_GlobalGetAtomNameA = UINT WINAPI( 612 | ATOM nAtom, 613 | LPSTR lpBuffer, 614 | int nSize 615 | ); 616 | 617 | using fn_NtResumeThread = NTSTATUS NTAPI( 618 | HANDLE ThreadHandle, 619 | PULONG SuspendCount 620 | ); 621 | 622 | using fn_NtSuspendThread = NTSTATUS NTAPI( 623 | HANDLE ThreadHandle, 624 | PULONG SuspendCount 625 | ); 626 | 627 | using fn_NtDelayExecution = NTSTATUS NTAPI( 628 | BOOLEAN Alertable, 629 | PLARGE_INTEGER DelayInterval 630 | ); 631 | 632 | using fn_ObtainUserAgentString = HRESULT WINAPI( 633 | DWORD dwOption, 634 | LPCSTR pcszUAOut, 635 | DWORD* cbSize 636 | ); 637 | 638 | using fn_VirtualQuery = SIZE_T WINAPI( 639 | LPCVOID lpAddress, 640 | PMEMORY_BASIC_INFORMATION lpBuffer, 641 | SIZE_T dwLength 642 | ); 643 | 644 | using fn_InternetOpenW = HANDLE WINAPI( 645 | LPCWSTR lpszAgent, 646 | DWORD dwAccessType, 647 | LPCWSTR lpszProxy, 648 | LPCWSTR lpszProxyBypass, 649 | DWORD dwFlags 650 | ); 651 | 652 | using fn_InternetConnectW = HANDLE WINAPI( 653 | HANDLE hInternet, 654 | LPCWSTR lpszServerName, 655 | WORD nServerPort, 656 | LPCWSTR lpszUserName, 657 | LPCWSTR lpszPassword, 658 | DWORD dwService, 659 | DWORD dwFlags, 660 | DWORD_PTR dwContext 661 | ); 662 | 663 | using fn_HttpOpenRequestW = HANDLE WINAPI( 664 | HANDLE hConnect, 665 | LPCWSTR lpszVerb, 666 | LPCWSTR lpszObjectName, 667 | LPCWSTR lpszVersion, 668 | LPCWSTR lpszReferrer, 669 | LPCWSTR *lplpszAcceptTypes, 670 | DWORD dwFlags, 671 | DWORD_PTR dwContext 672 | ); 673 | 674 | using fn_InternetCrackUrlW = BOOL WINAPI( 675 | LPCWSTR lpszUrl, 676 | DWORD dwUrlLength, 677 | DWORD dwFlags, 678 | LPURL_COMPONENTSW lpUrlComponents 679 | ); 680 | 681 | using fn_HttpSendRequestW = BOOL WINAPI( 682 | HANDLE hRequest, 683 | LPCWSTR lpszHeaders, 684 | DWORD dwHeadersLength, 685 | LPVOID lpOptional, 686 | DWORD dwOptionalLength 687 | ); 688 | 689 | using fn_InternetQueryDataAvailable = BOOL WINAPI( 690 | HINTERNET hFile, 691 | LPDWORD lpdwNumberOfBytesAvailable, 692 | DWORD dwFlags, 693 | DWORD_PTR dwContext 694 | ); 695 | 696 | using fn_InternetReadFile = BOOL WINAPI( 697 | HANDLE hFile, 698 | LPVOID lpBuffer, 699 | DWORD dwNumberOfBytesToRead, 700 | LPDWORD lpdwNumberOfBytesRead 701 | ); 702 | 703 | using fn_InternetGetLastResponseInfoW = BOOL WINAPI( 704 | LPDWORD lpdwError, 705 | LPWSTR lpszBuffer, 706 | LPDWORD lpdwBufferLength 707 | ); 708 | 709 | using fn_InternetCloseHandle = BOOL WINAPI( 710 | HANDLE hInternet 711 | ); 712 | 713 | using fn_HttpQueryInfoW = BOOL WINAPI( 714 | HANDLE hRequest, 715 | DWORD dwInfoLevel, 716 | LPVOID lpBuffer, 717 | LPDWORD lpdwBufferLength, 718 | LPDWORD lpdwIndex 719 | ); 720 | 721 | using fn_CoInitializeEx = HRESULT WINAPI( 722 | LPVOID pvReserved, 723 | DWORD dwCoInit 724 | ); 725 | 726 | using fn_CoInitializeSecurity = HRESULT WINAPI( 727 | PSECURITY_DESCRIPTOR pSecDesc, 728 | LONG cAuthSvc, 729 | SOLE_AUTHENTICATION_SERVICE *asAuthSvc, 730 | void *pReserved1, 731 | DWORD dwAuthnLevel, 732 | DWORD dwImpLevel, 733 | void *pAuthList, 734 | DWORD dwCapabilities, 735 | void *pReserved3 736 | ); 737 | 738 | using fn_CoCreateInstance = HRESULT WINAPI( 739 | REFCLSID rclsid, 740 | LPUNKNOWN pUnkOuter, 741 | DWORD dwClsContext, 742 | REFIID riid, 743 | LPVOID *ppv 744 | ); 745 | 746 | using fn_SysAllocString = BSTR WINAPI( 747 | const OLECHAR *psz 748 | ); 749 | 750 | using fn_SysFreeString = BSTR WINAPI( 751 | const OLECHAR *psz 752 | ); 753 | 754 | using fn_CoUninitialize = void WINAPI(); 755 | 756 | using fn_CoSetProxyBlanket = HRESULT WINAPI( 757 | IUnknown *pProxy, 758 | DWORD dwAuthnSvc, 759 | DWORD dwAuthzSvc, 760 | OLECHAR *pServerPrincName, 761 | DWORD dwAuthnLevel, 762 | DWORD dwImpLevel, 763 | RPC_AUTH_IDENTITY_HANDLE pAuthInfo, 764 | DWORD dwCapabilities 765 | ); 766 | 767 | using fn_CLSIDFromString = HRESULT WINAPI( 768 | LPCOLESTR lpsz, 769 | LPCLSID pclsid 770 | ); 771 | 772 | using fn_NetGetJoinInformation = NET_API_STATUS WINAPI( 773 | LPCWSTR lpServer, 774 | LPWSTR *lpNameBuffer, 775 | PNETSETUP_JOIN_STATUS BufferType 776 | ); 777 | 778 | using fn_DsRoleGetPrimaryDomainInformation = DWORD WINAPI( 779 | IN LPCWSTR lpServer, 780 | IN DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel, 781 | OUT PBYTE *Buffer 782 | ); 783 | 784 | using fn_DsRoleFreeMemory = void WINAPI( 785 | IN PVOID Buffer 786 | ); 787 | 788 | using fn_CommandLineToArgvW = LPWSTR * WINAPI( 789 | LPCWSTR lpCmdLine, 790 | int *pNumArgs 791 | ); 792 | 793 | using fn_GetParent = HWND WINAPI( 794 | HWND hWnd 795 | ); 796 | 797 | using fn_GetWindowThreadProcessId = DWORD WINAPI( 798 | HWND hWnd, 799 | LPDWORD lpdwProcessId 800 | ); 801 | 802 | using fn_GetClassNameW = int WINAPI( 803 | HWND hWnd, 804 | LPWSTR lpClassName, 805 | int nMaxCount 806 | ); 807 | 808 | using fn_PostMessageW = BOOL WINAPI( 809 | HWND hWnd, 810 | UINT Msg, 811 | WPARAM wParam, 812 | LPARAM lParam 813 | ); 814 | 815 | using fn_GetCursorPos = BOOL WINAPI( 816 | LPPOINT lpPoint 817 | ); 818 | 819 | using fn_ImpersonateSelf = BOOL WINAPI( 820 | SECURITY_IMPERSONATION_LEVEL ImpersonationLevel 821 | ); 822 | 823 | using fn_GetSidSubAuthority = PDWORD WINAPI( 824 | PSID pSid, 825 | DWORD nSubAuthority 826 | ); 827 | 828 | using fn_GetSidSubAuthorityCount = PUCHAR WINAPI( 829 | PSID pSid 830 | ); 831 | 832 | using fn_LookupAccountSidW = BOOL WINAPI( 833 | LPCWSTR lpSystemName, 834 | PSID Sid, 835 | LPWSTR Name, 836 | LPDWORD cchName, 837 | LPWSTR ReferencedDomainName, 838 | LPDWORD cchReferencedDomainName, 839 | PSID_NAME_USE peUse 840 | ); 841 | 842 | using fn_GetSystemInfo = void WINAPI( 843 | LPSYSTEM_INFO lpSystemInfo 844 | ); 845 | 846 | using fn_GlobalMemoryStatusEx = BOOL WINAPI( 847 | LPMEMORYSTATUSEX lpBuffer 848 | ); 849 | 850 | using fn_GetPhysicallyInstalledSystemMemory = BOOL WINAPI( 851 | PULONGLONG TotalMemoryInKilobytes 852 | ); 853 | 854 | using fn_QueryPerformanceCounter = BOOL WINAPI( 855 | LARGE_INTEGER *lpPerformanceCount 856 | ); 857 | 858 | using fn_QueryPerformanceFrequency = BOOL WINAPI( 859 | LARGE_INTEGER *lpPerformanceCount 860 | ); 861 | 862 | using fn_SetProcessMitigationPolicy = BOOL WINAPI( 863 | PROCESS_MITIGATION_POLICY MitigationPolicy, 864 | PVOID lpBuffer, 865 | SIZE_T dwLength 866 | ); 867 | 868 | using fn_GetThreadTimes = BOOL WINAPI( 869 | HANDLE hThread, 870 | LPFILETIME lpCreationTime, 871 | LPFILETIME lpExitTime, 872 | LPFILETIME lpKernelTime, 873 | LPFILETIME lpUserTime 874 | ); 875 | 876 | using fn_PeekNamedPipe = BOOL WINAPI( 877 | HANDLE hNamedPipe, 878 | LPVOID lpBuffer, 879 | DWORD nBufferSize, 880 | LPDWORD lpBytesRead, 881 | LPDWORD lpTotalBytesAvail, 882 | LPDWORD lpBytesLeftThisMessage 883 | ); 884 | 885 | using fn_CreatePipe = BOOL WINAPI( 886 | PHANDLE hReadPipe, 887 | PHANDLE hWritePipe, 888 | LPSECURITY_ATTRIBUTES lpPipeAttributes, 889 | DWORD nSize 890 | ); 891 | 892 | using fn_GetComputerNameW = BOOL WINAPI( 893 | LPWSTR lpBuffer, 894 | LPDWORD nSize 895 | ); 896 | 897 | using fn_GetComputerNameExW = BOOL WINAPI( 898 | COMPUTER_NAME_FORMAT NameType, 899 | LPWSTR lpBuffer, 900 | LPDWORD nSize 901 | ); 902 | 903 | using fn_QueryFullProcessImageNameA = BOOL WINAPI( 904 | HANDLE hProcess, 905 | DWORD dwFlags, 906 | LPSTR lpExeName, 907 | PDWORD lpdwSize 908 | ); 909 | 910 | using fn_RegOpenKeyExW = LSTATUS WINAPI( 911 | HKEY hKey, 912 | LPCWSTR lpSubKey, 913 | DWORD ulOptions, 914 | REGSAM samDesired, 915 | PHKEY phkResult 916 | ); 917 | 918 | using fn_RegEnumKeyExW = LSTATUS WINAPI( 919 | HKEY hKey, 920 | DWORD dwIndex, 921 | LPWSTR lpName, 922 | LPDWORD lpcchName, 923 | LPDWORD lpReserved, 924 | LPWSTR lpClass, 925 | LPDWORD lpcchClass, 926 | PFILETIME lpftLastWriteTime 927 | ); 928 | 929 | using fn_RegCloseKey = LSTATUS WINAPI( 930 | HKEY hKey 931 | ); 932 | 933 | using fn_RegEnumValueW = LSTATUS WINAPI( 934 | HKEY hKey, 935 | DWORD dwIndex, 936 | LPWSTR lpValueName, 937 | LPDWORD lpcchValueName, 938 | LPDWORD lpReserved, 939 | LPDWORD lpType, 940 | LPBYTE lpData, 941 | LPDWORD lpcbData 942 | ); 943 | 944 | using fn_FindFirstFileW = HANDLE WINAPI( 945 | LPCWSTR lpFileName, 946 | LPWIN32_FIND_DATAW lpFindFileData 947 | ); 948 | 949 | using fn_FindNextFileW = BOOL WINAPI( 950 | HANDLE hFindFile, 951 | LPWIN32_FIND_DATAW lpFindFileData 952 | ); 953 | 954 | using fn_IsNativeVhdBoot = BOOL WINAPI( 955 | PBOOL NativeVhdBoot 956 | ); 957 | 958 | using fn_SetupDiGetClassDevsW = HDEVINFO WINAPI( 959 | const GUID* ClassGuid, 960 | PCWSTR Enumerator, 961 | HWND hwndParent, 962 | DWORD Flags 963 | ); 964 | 965 | using fn_SetupDiEnumDeviceInfo = BOOL WINAPI( 966 | HDEVINFO DeviceInfoSet, 967 | DWORD MemberIndex, 968 | PSP_DEVINFO_DATA DeviceInfoData 969 | ); 970 | 971 | using fn_SetupDiGetDeviceRegistryPropertyW = BOOL WINAPI( 972 | HDEVINFO DeviceInfoSet, 973 | PSP_DEVINFO_DATA DeviceInfoData, 974 | DWORD Property, 975 | PDWORD PropertyRegDataType, 976 | PBYTE PropertyBuffer, 977 | DWORD PropertyBufferSize, 978 | PDWORD RequiredSize 979 | ); 980 | 981 | using fn_CreateFileW = HANDLE WINAPI( 982 | LPCWSTR lpFileName, 983 | DWORD dwDesiredAccess, 984 | DWORD dwShareMode, 985 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 986 | DWORD dwCreationDisposition, 987 | DWORD dwFlagsAndAttributes, 988 | HANDLE hTemplateFile 989 | ); 990 | 991 | using fn_NtCreateFile = NTSTATUS WINAPI( 992 | OUT PHANDLE FileHandle, 993 | IN ACCESS_MASK DesiredAccess, 994 | IN POBJECT_ATTRIBUTES ObjectAttributes, 995 | OUT PIO_STATUS_BLOCK IoStatusBlock, 996 | IN PLARGE_INTEGER AllocationSize, 997 | IN ULONG FileAttributes, 998 | IN ULONG ShareAccess, 999 | IN ULONG CreateDisposition, 1000 | IN ULONG CreateOptions, 1001 | IN PVOID EaBuffer, 1002 | IN ULONG EaLength 1003 | ); 1004 | 1005 | using fn_RtlInitUnicodeString = void WINAPI( 1006 | PUNICODE_STRING DestinationString, 1007 | PCWSTR SourceString 1008 | ); 1009 | 1010 | using fn_RtlSecureZeroMemory = PVOID WINAPI( 1011 | PVOID ptr, 1012 | SIZE_T cnt 1013 | ); 1014 | 1015 | using fn_GetNativeSystemInfo = void WINAPI( 1016 | LPSYSTEM_INFO lpSystemInfo 1017 | ); 1018 | 1019 | using fn_IsWow64Process = BOOL WINAPI( 1020 | HANDLE hProcess, 1021 | PBOOL Wow64Process 1022 | ); 1023 | 1024 | using fn_SetThreadPriorityBoost = BOOL WINAPI( 1025 | HANDLE hThread, 1026 | BOOL bDisablePriorityBoost 1027 | ); 1028 | 1029 | using fn_SetThreadPriority = BOOL WINAPI( 1030 | HANDLE hThread, 1031 | int nPriority 1032 | ); 1033 | 1034 | using fn_GetThreadPriority = int WINAPI( 1035 | HANDLE hThread 1036 | ); 1037 | 1038 | using fn_NtTestAlert = NTSTATUS WINAPI( 1039 | ); 1040 | 1041 | using fn_GetFileSizeEx = BOOL WINAPI( 1042 | HANDLE hFile, 1043 | PLARGE_INTEGER lpFileSize 1044 | ); 1045 | 1046 | using fn_ReadFile = BOOL WINAPI( 1047 | HANDLE hFile, 1048 | LPVOID lpBuffer, 1049 | DWORD nNumberOfBytesToRead, 1050 | LPDWORD lpNumberOfBytesRead, 1051 | LPOVERLAPPED lpOverlapped 1052 | ); 1053 | 1054 | using fn_VirtualProtect = BOOL WINAPI( 1055 | LPVOID lpAddress, 1056 | SIZE_T dwSize, 1057 | DWORD flNewProtect, 1058 | PDWORD lpflOldProtect 1059 | ); 1060 | 1061 | using fn_TerminateProcess = BOOL WINAPI( 1062 | HANDLE hProcess, 1063 | UINT uExitCode 1064 | ); 1065 | 1066 | using fn_QueryFullProcessImageNameW = BOOL WINAPI( 1067 | HANDLE hProcess, 1068 | DWORD dwFlags, 1069 | LPWSTR lpExeName, 1070 | PDWORD lpdwSize 1071 | ); 1072 | 1073 | using fn_SleepEx = DWORD WINAPI( 1074 | DWORD dwMilliseconds, 1075 | BOOL bAlertable 1076 | ); 1077 | 1078 | using fn_GetFileAttributesW = DWORD WINAPI( 1079 | LPCWSTR lpFileName 1080 | ); 1081 | 1082 | using fn_DeviceIoControl = BOOL WINAPI( 1083 | HANDLE hDevice, 1084 | DWORD dwIoControlCode, 1085 | LPVOID lpInBuffer, 1086 | DWORD nInBufferSize, 1087 | LPVOID lpOutBuffer, 1088 | DWORD nOutBufferSize, 1089 | LPDWORD lpBytesReturned, 1090 | LPOVERLAPPED lpOverlapped 1091 | ); 1092 | 1093 | using fn_CreateThread = HANDLE WINAPI( 1094 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 1095 | SIZE_T dwStackSize, 1096 | LPTHREAD_START_ROUTINE lpStartAddress, 1097 | __drv_aliasesMem LPVOID lpParameter, 1098 | DWORD dwCreationFlags, 1099 | LPDWORD lpThreadId 1100 | ); 1101 | 1102 | using fn_EtwEventWrite = ULONG WINAPI( 1103 | HANDLE RegHandle, 1104 | void* EventDescriptor, 1105 | ULONG UserDataCount, 1106 | void* UserData 1107 | ); 1108 | 1109 | using fn_ExpandEnvironmentStringsW = DWORD WINAPI( 1110 | const wchar_t* lpSrc, 1111 | wchar_t* lpDst, 1112 | DWORD nSize 1113 | ); 1114 | 1115 | using fn_CryptAcquireContextW = BOOL WINAPI( 1116 | HCRYPTPROV* phProv, 1117 | LPCWSTR szContainer, 1118 | LPCWSTR szProvider, 1119 | DWORD dwProvType, 1120 | DWORD dwFlags 1121 | ); 1122 | 1123 | using fn_CryptAcquireContextA = BOOL WINAPI( 1124 | HCRYPTPROV* phProv, 1125 | LPCSTR szContainer, 1126 | LPCSTR szProvider, 1127 | DWORD dwProvType, 1128 | DWORD dwFlags 1129 | ); 1130 | 1131 | using fn_CryptImportKey = BOOL WINAPI( 1132 | HCRYPTPROV hProv, 1133 | const BYTE* pbData, 1134 | DWORD dwDataLen, 1135 | HCRYPTKEY hPubKey, 1136 | DWORD dwFlags, 1137 | HCRYPTKEY* phKey 1138 | ); 1139 | 1140 | using fn_CryptSetKeyParam = BOOL WINAPI( 1141 | HCRYPTKEY hKey, 1142 | DWORD dwParam, 1143 | const BYTE* pbData, 1144 | DWORD dwFlags 1145 | ); 1146 | 1147 | using fn_CryptEncrypt = BOOL WINAPI( 1148 | HCRYPTKEY hKey, 1149 | HCRYPTHASH hHash, 1150 | BOOL Final, 1151 | DWORD dwFlags, 1152 | BYTE* pbData, 1153 | DWORD* pdwDataLen, 1154 | DWORD dwBufLen 1155 | ); 1156 | 1157 | using fn_CryptDecrypt = BOOL WINAPI( 1158 | HCRYPTKEY hKey, 1159 | HCRYPTHASH hHash, 1160 | BOOL Final, 1161 | DWORD dwFlags, 1162 | BYTE* pbData, 1163 | DWORD* pdwDataLen 1164 | ); 1165 | 1166 | using fn_CryptDestroyKey = BOOL WINAPI( 1167 | HCRYPTKEY hKey 1168 | ); 1169 | 1170 | using fn_CryptReleaseContext = BOOL WINAPI( 1171 | HCRYPTPROV hProv, 1172 | DWORD dwFlags 1173 | ); 1174 | 1175 | using fn_RtlGetCompressionWorkSpaceSize = NTSTATUS WINAPI( 1176 | USHORT CompressionFormatAndEngine, 1177 | PULONG CompressBufferWorkSpaceSize, 1178 | PULONG CompressFragmentWorkSpaceSize 1179 | ); 1180 | 1181 | using fn_RtlCompressBuffer = NTSTATUS WINAPI( 1182 | USHORT CompressionFormatAndEngine, 1183 | PUCHAR UncompressedBuffer, 1184 | ULONG UncompressedBufferSize, 1185 | PUCHAR CompressedBuffer, 1186 | ULONG CompressedBufferSize, 1187 | ULONG UncompressedChunkSize, 1188 | PULONG FinalCompressedSize, 1189 | PVOID WorkSpace 1190 | ); 1191 | 1192 | using fn_RtlDecompressBuffer = NTSTATUS WINAPI( 1193 | USHORT CompressionFormat, 1194 | PUCHAR UncompressedBuffer, 1195 | ULONG UncompressedBufferSize, 1196 | PUCHAR CompressedBuffer, 1197 | ULONG CompressedBufferSize, 1198 | PULONG FinalUncompressedSize 1199 | ); 1200 | 1201 | using fn_RtlDecompressBufferEx = NTSTATUS WINAPI( 1202 | USHORT CompressionFormat, 1203 | PUCHAR UncompressedBuffer, 1204 | ULONG UncompressedBufferSize, 1205 | PUCHAR CompressedBuffer, 1206 | ULONG CompressedBufferSize, 1207 | PULONG FinalUncompressedSize, 1208 | PVOID WorkSpace 1209 | ); 1210 | 1211 | using fn_CreateDecompressor = BOOL WINAPI( 1212 | DWORD Algorithm, 1213 | PCOMPRESS_ALLOCATION_ROUTINES AllocationRoutines, 1214 | PDECOMPRESSOR_HANDLE DecompressorHandle 1215 | ); 1216 | 1217 | using fn_Decompress = BOOL WINAPI( 1218 | DECOMPRESSOR_HANDLE DecompressorHandle, 1219 | LPCVOID CompressedData, 1220 | SIZE_T CompressedDataSize, 1221 | PVOID UncompressedBuffer, 1222 | SIZE_T UncompressedBufferSize, 1223 | PSIZE_T UncompressedDataSize 1224 | ); 1225 | 1226 | using fn_CloseDecompressor = BOOL WINAPI( 1227 | DECOMPRESSOR_HANDLE DecompressorHandle 1228 | ); 1229 | } 1230 | -------------------------------------------------------------------------------- /apimonitor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mgeeky/UnhookMe/ff8dc75967381e6bdfb6820cca564ee5250cce58/apimonitor.gif -------------------------------------------------------------------------------- /x64/Release/UnhookMe.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mgeeky/UnhookMe/ff8dc75967381e6bdfb6820cca564ee5250cce58/x64/Release/UnhookMe.exe --------------------------------------------------------------------------------