├── .github └── workflows │ └── release.yml ├── .gitignore ├── .releaserc.yml ├── CMakeLists.txt ├── README.md ├── dump.h ├── hook.cpp ├── jni.h ├── jni_x86.h ├── library.def ├── mhook ├── .github │ └── stale.yml ├── .gitignore ├── CMakeLists.txt ├── COPYING ├── README.md ├── appveyor.yml ├── disasm-lib │ ├── cpu.c │ ├── cpu.h │ ├── disasm.c │ ├── disasm.h │ ├── disasm_x86.c │ ├── disasm_x86.h │ ├── disasm_x86_tables.h │ └── misc.h ├── mhook-lib │ ├── mhook.cpp │ └── mhook.h └── mhook-test │ ├── mhook-test.cpp │ ├── stdafx.cpp │ └── stdafx.h ├── parser.h └── proxy.h /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | token: ${{ secrets.API_GITHUB_TOKEN }} 18 | 19 | - name: Build 20 | run: | 21 | mkdir build 22 | cd build 23 | cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" 24 | cmake --build . 25 | 26 | - name: Action For Semantic Release 27 | uses: cycjimmy/semantic-release-action@v2.4.1 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.API_GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/clion+all,cmake,c 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=clion+all,cmake,c 4 | 5 | ### C ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | ### CLion+all ### 60 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 61 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 62 | 63 | # User-specific stuff 64 | .idea/**/workspace.xml 65 | .idea/**/tasks.xml 66 | .idea/**/usage.statistics.xml 67 | .idea/**/dictionaries 68 | .idea/**/shelf 69 | 70 | # Generated files 71 | .idea/**/contentModel.xml 72 | 73 | # Sensitive or high-churn files 74 | .idea/**/dataSources/ 75 | .idea/**/dataSources.ids 76 | .idea/**/dataSources.local.xml 77 | .idea/**/sqlDataSources.xml 78 | .idea/**/dynamic.xml 79 | .idea/**/uiDesigner.xml 80 | .idea/**/dbnavigator.xml 81 | 82 | # Gradle 83 | .idea/**/gradle.xml 84 | .idea/**/libraries 85 | 86 | # Gradle and Maven with auto-import 87 | # When using Gradle or Maven with auto-import, you should exclude module files, 88 | # since they will be recreated, and may cause churn. Uncomment if using 89 | # auto-import. 90 | # .idea/artifacts 91 | # .idea/compiler.xml 92 | # .idea/jarRepositories.xml 93 | # .idea/modules.xml 94 | # .idea/*.iml 95 | # .idea/modules 96 | # *.iml 97 | # *.ipr 98 | 99 | # CMake 100 | cmake-build-*/ 101 | 102 | # Mongo Explorer plugin 103 | .idea/**/mongoSettings.xml 104 | 105 | # File-based project format 106 | *.iws 107 | 108 | # IntelliJ 109 | out/ 110 | 111 | # mpeltonen/sbt-idea plugin 112 | .idea_modules/ 113 | 114 | # JIRA plugin 115 | atlassian-ide-plugin.xml 116 | 117 | # Cursive Clojure plugin 118 | .idea/replstate.xml 119 | 120 | # Crashlytics plugin (for Android Studio and IntelliJ) 121 | com_crashlytics_export_strings.xml 122 | crashlytics.properties 123 | crashlytics-build.properties 124 | fabric.properties 125 | 126 | # Editor-based Rest Client 127 | .idea/httpRequests 128 | 129 | # Android studio 3.1+ serialized cache file 130 | .idea/caches/build_file_checksums.ser 131 | 132 | ### CLion+all Patch ### 133 | # Ignores the whole .idea folder and all .iml files 134 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 135 | 136 | .idea/ 137 | 138 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 139 | 140 | *.iml 141 | modules.xml 142 | .idea/misc.xml 143 | *.ipr 144 | 145 | # Sonarlint plugin 146 | .idea/sonarlint 147 | 148 | ### CMake ### 149 | CMakeLists.txt.user 150 | CMakeCache.txt 151 | CMakeFiles 152 | CMakeScripts 153 | Testing 154 | Makefile 155 | cmake_install.cmake 156 | install_manifest.txt 157 | compile_commands.json 158 | CTestTestfile.cmake 159 | _deps 160 | 161 | ### CMake Patch ### 162 | # External projects 163 | *-prefix/ 164 | 165 | # End of https://www.toptal.com/developers/gitignore/api/clion+all,cmake,c 166 | -------------------------------------------------------------------------------- /.releaserc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | branches: 3 | - master 4 | plugins: 5 | - "@semantic-release/commit-analyzer" 6 | - "@semantic-release/release-notes-generator" 7 | - # 8 | - "@semantic-release/github" 9 | - successComment: false 10 | failComment: false 11 | assets: 12 | - path: build/version.dll 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(version) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 7 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 8 | 9 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 10 | set(CMAKE_STATIC_LIBRARY_PREFIX "") 11 | 12 | # https://stackoverflow.com/questions/18138635/mingw-exe-requires-a-few-gcc-dlls-regardless-of-the-code 13 | add_link_options(-static -static-libgcc -static-libstdc++) 14 | 15 | add_library(version SHARED hook.cpp library.def proxy.h jni.h jni_x86.h parser.h dump.h) 16 | link_libraries(ws2_32) 17 | 18 | add_subdirectory(mhook) 19 | target_compile_options(mhook PUBLIC -w) 20 | target_link_libraries(version mhook) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JVM Dump Proxy 2 | > A proxy DLL for Windows to dump JVM classes at JNI level. 3 | 4 | ## Introduction 5 | Some Java programs use [reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html) to hide their code by loading and executing classes dynamically. You can [dump them from memory](https://github.com/hengyunabc/dumpclass), but what if they are unloaded right after they execute? You could edit `rt.jar` and place a hook on various reflection methods. But what if the program uses the native class loading methods directly, bypassing any bytecode-level hooks? 6 | 7 | This project aims to be a universal solution to all your dumping needs. By hooking at the lowest reliably accessible JNI level, it will dump all classes as they are being loaded. 8 | 9 | ## Limitations 10 | - x64 only 11 | - Only hooks `DefineClass*`. It is possible to bypass this method by implementing your own class loader [in JNI](https://stackoverflow.com/questions/3735233/encrypted-class-files-with-decryption-handled-by-a-native-library). 12 | 13 | ## Usage 14 | Download the latest [release](https://github.com/ViRb3/jvm-dump-proxy/releases). Place `version.dll` in your Java `bin` directory, next to `java.exe`. You may want to use a separate Java installation so you don't affect all processes. On your desktop, create a new directory called `JVMDUMP`. 15 | 16 | When you run any program with the modified Java installation, you will see a message box with the hooking result. Once you press `OK`, all loaded classes will be saved under the directory on your desktop. 17 | 18 | ## FAQ 19 | - Q: I am getting `Error 2` \ 20 | A: `JVMDUMP` is not accessible on your desktop 21 | - Q: But duplicate classes? \ 22 | A: They will be appended with a number, nothing will be overwritten 23 | 24 | ## Technical details 25 | ### proxy.h, library.def 26 | A simple proxy DLL implementation to inject into the JVM painlessly and reliably. For more information, check the [references](#References). 27 | ### hook.cpp 28 | Installs the hooks that redirect methods to our code. Powered by [mhook](https://github.com/apriorit/mhook). 29 | ### dump.h 30 | The class dumping logic. 31 | ### parser.h 32 | A fast and simple Java class parser. It will parse the class name of each hooked byte buffer and use it to save the file under the appropriate name. 33 | 34 | ## Compilation 35 | - mingw-w64 7.0.0+ 36 | - CMake 3.16+ 37 | 38 | ## References 39 | - If you would like to learn more about the proxy DLL technique, check out the base project [PerfectProxyDLL](https://github.com/ViRb3/PerfectProxyDLL) 40 | - Control flow of [findClass](https://stackoverflow.com/questions/3544614/how-is-the-control-flow-to-findclass-of) 41 | - JDK [Classloader.c](https://github.com/corretto/corretto-8/blob/release-8.202.32.1/src/jdk/src/share/native/java/lang/ClassLoader.c) 42 | - JDK [jvm.cpp](https://github.com/corretto/corretto-8/blob/release-8.202.32.1/src/hotspot/src/share/vm/prims/jvm.cpp) -------------------------------------------------------------------------------- /dump.h: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_DUMP_H 2 | #define VERSION_DUMP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "parser.h" 9 | 10 | using namespace std; 11 | 12 | mutex dumpMutex; 13 | 14 | bool fileExists(const wstring &name) { 15 | if (FILE *file = _wfopen(name.c_str(), L"r")) { 16 | fclose(file); 17 | return true; 18 | } else { 19 | return false; 20 | } 21 | } 22 | 23 | bool getNextFreeFilePath(wstring &path, const char *name) { 24 | path.resize(MAX_PATH); 25 | if (!SHGetSpecialFolderPathW(HWND_DESKTOP, path.data(), CSIDL_DESKTOP, FALSE)) { 26 | return false; 27 | } 28 | path.resize(wcslen(path.data())); 29 | 30 | wstring fixedName(MAX_PATH, '\x00'); 31 | mbstowcs(fixedName.data(), name, strlen(name)); 32 | PathCleanupSpec(nullptr, fixedName.data()); 33 | fixedName.resize(wcslen(fixedName.data())); 34 | 35 | int initialLen = path.length(); 36 | int i = 0; 37 | do { 38 | path.resize(initialLen); 39 | path += L"\\JVMDUMP\\"; 40 | path += fixedName; 41 | if (i != 0) { 42 | path += to_wstring(i); 43 | } 44 | path += L".class"; 45 | i++; 46 | } while (fileExists(path)); 47 | return true; 48 | } 49 | 50 | void DoDump(const char *buf, int len) { 51 | if (!buf || len < 1) { 52 | return; 53 | } 54 | lock_guard lock(dumpMutex); 55 | FILE *fp; 56 | wstring path; 57 | string className = GetJavaClassName(buf); 58 | if (getNextFreeFilePath(path, className.c_str())) { 59 | fp = _wfopen(path.c_str(), L"wb"); 60 | int written = fwrite(buf, sizeof(char), len, fp); 61 | if (written == 0) { 62 | MessageBox(nullptr, "Error 2", "Error", MB_OK); 63 | } else if (written != sizeof(char) * len) { 64 | MessageBox(nullptr, "Error 3", "Error", MB_OK); 65 | } 66 | fclose(fp); 67 | } else { 68 | MessageBox(nullptr, "Error 1", "Error", MB_OK); 69 | } 70 | } 71 | 72 | #endif //VERSION_DUMP_H 73 | -------------------------------------------------------------------------------- /hook.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mhook/mhook-lib/mhook.h" 3 | #include "jni.h" 4 | #include "proxy.h" 5 | #include "dump.h" 6 | 7 | using namespace std; 8 | 9 | typedef jclass JNICALL (*sig_JVM_DefineClass)(JNIEnv *env, const char *name, jobject loader, 10 | const jbyte *buf, jsize len, jobject pd); 11 | typedef jclass JNICALL(*sig_JVM_DefineClassWithSource)(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, 12 | jsize len, jobject pd, const char *source); 13 | typedef jclass JNICALL (*sig_JVM_DefineClassWithSourceCond)(JNIEnv *env, const char *name, 14 | jobject loader, const jbyte *buf, jsize len, jobject pd, 15 | const char *source, jboolean verify); 16 | 17 | sig_JVM_DefineClass orig_JVM_DefineClass = NULL; 18 | sig_JVM_DefineClassWithSource orig_JVM_DefineClassWithSource = NULL; 19 | sig_JVM_DefineClassWithSourceCond orig_JVM_DefineClassWithSourceCond = NULL; 20 | 21 | jclass JNICALL detour_JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, 22 | const jbyte *buf, jsize len, jobject pd) { 23 | DoDump((char *) buf, len); 24 | return orig_JVM_DefineClass(env, name, loader, buf, len, pd); 25 | } 26 | 27 | jclass JNICALL detour_JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, 28 | jsize len, jobject pd, const char *source) { 29 | DoDump((char *) buf, len); 30 | return orig_JVM_DefineClassWithSource(env, name, loader, buf, len, pd, source); 31 | } 32 | 33 | jclass JNICALL detour_JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, 34 | jsize len, jobject pd, const char *source, jboolean verify) { 35 | DoDump((char *) buf, len); 36 | return orig_JVM_DefineClassWithSourceCond(env, name, loader, buf, len, pd, source, verify); 37 | } 38 | 39 | bool doHook() { 40 | HMODULE hJvm = LoadLibrary("jvm.dll"); 41 | if (!hJvm) { 42 | return FALSE; 43 | } 44 | orig_JVM_DefineClass = (sig_JVM_DefineClass) GetProcAddress(hJvm, "JVM_DefineClass"); 45 | if (!orig_JVM_DefineClass) { 46 | return FALSE; 47 | } 48 | orig_JVM_DefineClassWithSource = (sig_JVM_DefineClassWithSource) GetProcAddress(hJvm, "JVM_DefineClassWithSource"); 49 | if (!orig_JVM_DefineClassWithSource) { 50 | return FALSE; 51 | } 52 | orig_JVM_DefineClassWithSourceCond = (sig_JVM_DefineClassWithSourceCond) GetProcAddress(hJvm, 53 | "JVM_DefineClassWithSourceCond"); 54 | if (!orig_JVM_DefineClassWithSourceCond) { 55 | return FALSE; 56 | } 57 | 58 | HOOK_INFO hooks[] = {{(PVOID *) &orig_JVM_DefineClass, (PVOID) detour_JVM_DefineClass}, 59 | {(PVOID *) &orig_JVM_DefineClassWithSource, (PVOID) detour_JVM_DefineClassWithSource}, 60 | {(PVOID *) &orig_JVM_DefineClassWithSourceCond, (PVOID) detour_JVM_DefineClassWithSourceCond}}; 61 | return Mhook_SetHookEx(hooks, 3) == 3; 62 | } 63 | 64 | bool WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { 65 | if (fdwReason == DLL_PROCESS_ATTACH) { 66 | SourceInit(); 67 | if (doHook()) { 68 | MessageBox(NULL, "Hooks initialized.", "Success", MB_OK); 69 | } else { 70 | MessageBox(NULL, "Something went wrong.", "Error", MB_OK); 71 | } 72 | } 73 | return TRUE; 74 | } -------------------------------------------------------------------------------- /jni_x86.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | #ifndef _JAVASOFT_JNI_MD_H_ 27 | #define _JAVASOFT_JNI_MD_H_ 28 | 29 | #if defined(SOLARIS) || defined(LINUX) || defined(_ALLBSD_SOURCE) 30 | 31 | 32 | // Note: please do not change these without also changing jni_md.h in the JDK 33 | // repository 34 | #ifndef __has_attribute 35 | #define __has_attribute(x) 0 36 | #endif 37 | #if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) 38 | #define JNIEXPORT __attribute__((visibility("default"))) 39 | #define JNIIMPORT __attribute__((visibility("default"))) 40 | #else 41 | #define JNIEXPORT 42 | #define JNIIMPORT 43 | #endif 44 | 45 | #define JNICALL 46 | typedef int jint; 47 | #if defined(_LP64) 48 | typedef long jlong; 49 | #else 50 | typedef long long jlong; 51 | #endif 52 | 53 | #else 54 | #define JNIEXPORT __declspec(dllexport) 55 | #define JNIIMPORT __declspec(dllimport) 56 | #define JNICALL __stdcall 57 | 58 | typedef int jint; 59 | typedef __int64 jlong; 60 | #endif 61 | 62 | typedef signed char jbyte; 63 | 64 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 65 | -------------------------------------------------------------------------------- /library.def: -------------------------------------------------------------------------------- 1 | LIBRARY "VERSION" 2 | EXPORTS 3 | 4 | GetFileVersionInfoA = _GetFileVersionInfoA 5 | GetFileVersionInfoByHandle = _GetFileVersionInfoByHandle 6 | GetFileVersionInfoExA = _GetFileVersionInfoExA 7 | GetFileVersionInfoExW = _GetFileVersionInfoExW 8 | GetFileVersionInfoSizeA = _GetFileVersionInfoSizeA 9 | GetFileVersionInfoSizeExA = _GetFileVersionInfoSizeExA 10 | GetFileVersionInfoSizeExW = _GetFileVersionInfoSizeExW 11 | GetFileVersionInfoSizeW = _GetFileVersionInfoSizeW 12 | GetFileVersionInfoW = _GetFileVersionInfoW 13 | VerFindFileA = _VerFindFileA 14 | VerFindFileW = _VerFindFileW 15 | VerInstallFileA = _VerInstallFileA 16 | VerInstallFileW = _VerInstallFileW 17 | VerLanguageNameA = _VerLanguageNameA 18 | VerLanguageNameW = _VerLanguageNameW 19 | VerQueryValueA = _VerQueryValueA 20 | VerQueryValueW = _VerQueryValueW -------------------------------------------------------------------------------- /mhook/.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 50 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /mhook/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build32 3 | /build64 -------------------------------------------------------------------------------- /mhook/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(mhook) 3 | 4 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 5 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake") 6 | 7 | add_definitions(-DNO_SANITY_CHECKS -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN) 8 | 9 | file(GLOB DisasmSrc disasm-lib/*.c disasm-lib/*.h) 10 | file(GLOB MhookSrc mhook-lib/*.cpp mhook-lib/*.h) 11 | 12 | source_group("disasm-lib" FILES ${DisasmSrc}) 13 | source_group("mhook-lib" FILES ${MhookSrc}) 14 | 15 | add_library(mhook STATIC ${DisasmSrc} ${MhookSrc}) 16 | target_include_directories(mhook INTERFACE .) 17 | 18 | option(BUILD_MHOOK_TEST "Building test exe for mhook" OFF) 19 | 20 | if(BUILD_MHOOK_TEST) 21 | add_executable(mhook-test 22 | mhook-test/mhook-test.cpp 23 | mhook-test/stdafx.cpp 24 | mhook-test/stdafx.h 25 | ) 26 | set_target_properties(mhook-test PROPERTIES COMPILE_FLAGS "/Yustdafx.h") 27 | set_source_files_properties(mhook-test/stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h") 28 | target_link_libraries(mhook-test mhook) 29 | endif(BUILD_MHOOK_TEST) 30 | 31 | include(GNUInstallDirs) 32 | 33 | install(TARGETS mhook 34 | ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" 35 | LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}") 36 | install(FILES mhook-lib/mhook.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/mhook-lib") -------------------------------------------------------------------------------- /mhook/COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2018, Apriorit 4 | Copyright (c) 2007-2014, Marton Anka 5 | Portions Copyright (c) 2007, Matt Conover 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /mhook/README.md: -------------------------------------------------------------------------------- 1 | # Mhook - a Windows API hooking library [![Build status](https://ci.appveyor.com/api/projects/status/qieg4d47uqv00we0/branch/master?svg=true)](https://ci.appveyor.com/project/apriorit/mhook/branch/master) 2 | 3 | - [Introduction](#introduction) 4 | - [How to use](#how-to-use) 5 | - [License](#license) 6 | - [Version history](#version-history) 7 | - [Acknowledgements](#acknowledgements) 8 | 9 | # Introduction 10 | This library was created as a free alternative to [Microsoft Detours](http://research.microsoft.com/sn/detours). It is originally developed by Marton Anka and currently is supported and developed by [Apriorit](https://www.apriorit.com/). 11 | 12 | # How to use 13 | ```C++ 14 | // Include a header 15 | #include 16 | 17 | // Save the original function 18 | typedef ULONG (WINAPI* _NtClose)(IN HANDLE Handle); 19 | _NtClose TrueNtClose = (_NtClose)GetProcAddress(GetModuleHandle(L"ntdll"), "NtClose"); 20 | 21 | // Declare your function that will be handle a hook: 22 | ULONG WINAPI HookNtClose(HANDLE hHandle) 23 | { 24 | printf("***** Call to NtClose(0x%p)\n", hHandle); 25 | return TrueNtClose(hHandle); 26 | } 27 | 28 | //... 29 | 30 | // Set the hook 31 | BOOL isHookSet = Mhook_SetHook((PVOID*)&TrueNtClose, HookNtClose); 32 | 33 | //... 34 | 35 | // After finishing using the hook – remove it 36 | Mhook_Unhook((PVOID*)&TrueNtClose); 37 | 38 | ``` 39 | 40 | You can also set a bunch of hooks in one call: 41 | ```C++ 42 | HOOK_INFO hooks[] = 43 | { 44 | { (PVOID*)&TrueNtOpenProcess, HookNtOpenProcess }, 45 | { (PVOID*)&TrueSelectObject, HookSelectobject }, 46 | { (PVOID*)&Truegetaddrinfo, Hookgetaddrinfo }, 47 | { (PVOID*)&TrueHeapAlloc, HookHeapAlloc }, 48 | { (PVOID*)&TrueNtClose, HookNtClose } 49 | }; 50 | 51 | int numberOfSetHooks = Mhook_SetHookEx(hooks, 5); 52 | 53 | //... 54 | 55 | // Removing hooks 56 | int numberOfRemovedHooks = Mhook_UnhookEx(hooks, 5); 57 | ``` 58 | 59 | That way of setting multiple hooks is also much better in performance. 60 | 61 | # License 62 | Mhook is freely distributed under an [MIT license](https://choosealicense.com/licenses/mit/). 63 | 64 | # Version history 65 | 66 | ## Version 2.5.1 (30 March 2018) 67 | - Fix #1: VirtualAlloc hooking reports anomaly 68 | - New #2: Add integration to vcpkg package 69 | - New #3: Add AppVeyor CI 70 | - Fix #4: Add ability to hook functions with call in first 5 bytes 71 | 72 | ## Version 2.5 (20 Oct 2017) 73 | - 10x performance boost 74 | - CMake build system 75 | - Change tabs to spaces 76 | - Ability to hook functions with `je`/`jne` in the first 5 bytes 77 | - Fix hook recursion 78 | - Other fixes 79 | 80 | ## Version 2.4 (05 Mar 2014, the last from the original author) 81 | - A number of improvements: hot patch location (mov edi, edi) handling, support for REX-prefixed EIP-relative jumps on x64, removal of compile-time limit on the number of hooks 82 | 83 | ## Version 2.3 (15 Jan 2012) 84 | - A bugfix that allows hooking more API functions 85 | 86 | ## Version 2.2 (27 Jun 2008) 87 | - Support for instructions using IP-relative addressing 88 | 89 | ## Version 2.1 (15 Oct 2007) 90 | - Fixes 91 | 92 | ## Version 2.0 (08 Jul 2007) 93 | - Built-in disassembler 94 | 95 | ## Version 1.0 (24 Jun 2007) 96 | - Original release 97 | 98 | # Acknowledgements 99 | Mhook contains a disassembler that is a stripped-down version of the excellent tDisasm package by Matt Conover. Thank you Matt! tDisasm comes with a BSD-style license and re-releasig a derivative of it under the MIT license has been confirmed to be OK by its author. 100 | 101 | Alexandr Filenkov submitted bugfixes in Sept-2007. Michael Syrovatsky submitted fixes for IP-relative addressing in Jun-2008. Andrey Kubyshev submitted a bugfix in Jul-2011 and Jan-2013. John McDonald enabled unlimited hooks. Kasper Brandt provided a fix for hot patch function prologues. 102 | -------------------------------------------------------------------------------- /mhook/appveyor.yml: -------------------------------------------------------------------------------- 1 | configuration: 2 | - Debug 3 | - Release 4 | 5 | environment: 6 | matrix: 7 | - generator: "Visual Studio 10 2010 Win64" 8 | - generator: "Visual Studio 10 2010" 9 | - generator: "Visual Studio 14 2015 Win64" 10 | - generator: "Visual Studio 14 2015" 11 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 12 | generator: "Visual Studio 15 2017 Win64" 13 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 14 | generator: "Visual Studio 15 2017" 15 | 16 | matrix: 17 | fast_finish: true 18 | 19 | shallow_clone: true 20 | 21 | before_build: 22 | - cmake . -Bbuild -G"%generator%" 23 | 24 | build_script: 25 | - set CMAKE_BUILD_FLAGS=--config %configuration% -- /m /v:m 26 | - cmake --build build %CMAKE_BUILD_FLAGS% 27 | -------------------------------------------------------------------------------- /mhook/disasm-lib/cpu.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2003, Matt Conover (mconover@gmail.com) 2 | #include "cpu.h" 3 | #include 4 | 5 | // NOTE: this assumes default scenarios (i.e., we assume CS/DS/ES/SS and flat 6 | // and all have a base of 0 and limit of 0xffffffff, we don't try to verify 7 | // that in the GDT) 8 | // 9 | // TODO: use inline assembly to get selector for segment 10 | // Segment = x86 segment register (SEG_ES = 0, SEG_CS = 1, ...) 11 | BYTE *GetAbsoluteAddressFromSegment(BYTE Segment, DWORD Offset) 12 | { 13 | switch (Segment) 14 | { 15 | // Windows uses a flat address space (except FS for x86 and GS for x64) 16 | case 0: // SEG_ES 17 | case 1: // SEG_CS 18 | case 2: // SEG_SS 19 | case 3: // SEG_DS 20 | return (BYTE *)(DWORD_PTR)Offset; 21 | case 4: // SEG_FS 22 | case 5: // SEG_GS 23 | return (BYTE *)(DWORD_PTR)Offset; 24 | // Note: we're really supposed to do this, but get_teb is not implemented 25 | // in this bastardized version of the disassembler. 26 | // return (BYTE *)get_teb() + Offset; 27 | default: 28 | assert(0); 29 | return (BYTE *)(DWORD_PTR)Offset; 30 | } 31 | } 32 | 33 | // This is an GDT/LDT selector (pGDT+Selector) 34 | BYTE *GetAbsoluteAddressFromSelector(WORD Selector, DWORD Offset) 35 | { 36 | DESCRIPTOR_ENTRY Entry; 37 | GATE_ENTRY *Gate; 38 | ULONG_PTR Base; 39 | 40 | assert(Selector < 0x10000); 41 | if (!GetThreadSelectorEntry(GetCurrentThread(), Selector, (LDT_ENTRY *)&Entry)) return NULL; 42 | if (!Entry.Present) return NULL; 43 | if (Entry.System) 44 | { 45 | Base = 0; 46 | #ifdef _WIN64 47 | Base |= (ULONG_PTR)Entry.HighOffset64 << 32; 48 | #endif 49 | Base |= Entry.BaseHi << 24; 50 | Base |= Entry.BaseMid << 16; 51 | Base |= Entry.BaseLow; 52 | } 53 | else 54 | { 55 | switch (Entry.Type) 56 | { 57 | case 1: // 16-bit TSS (available) 58 | case 2: // LDT 59 | case 3: // 16-bit TSS (busy) 60 | case 9: // 32-bit TSS (available) 61 | case 11: // 32-bit TSS (busy) 62 | Base = 0; 63 | #ifdef _WIN64 64 | Base |= (ULONG_PTR)Entry.HighOffset64 << 32; 65 | #endif 66 | Base |= Entry.BaseHi << 24; 67 | Base |= Entry.BaseMid << 16; 68 | Base |= Entry.BaseLow; 69 | break; 70 | 71 | case 4: // 16-bit call gate 72 | case 5: // task gate 73 | case 6: // 16-bit interrupt gate 74 | case 7: // 16-bit task gate 75 | case 12: // 32-bit call gate 76 | case 14: // 32-bit interrupt gate 77 | case 15: // 32-bit trap gate 78 | Gate = (GATE_ENTRY *)&Entry; 79 | #ifdef _WIN64 80 | Base = ((ULONG_PTR)Gate->HighOffset64 << 32) | (Gate->HighOffset << 16) | Gate->LowOffset; 81 | #else 82 | Base = (Gate->HighOffset << 16) | Gate->LowOffset; 83 | #endif 84 | assert(!Offset); Offset = 0; 85 | break; 86 | default: 87 | assert(0); 88 | return NULL; 89 | } 90 | } 91 | return (BYTE *)Base + Offset; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /mhook/disasm-lib/cpu.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2003, Matt Conover (mconover@gmail.com) 2 | #ifndef CPU_H 3 | #define CPU_H 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "misc.h" 10 | 11 | // Windows SDK assumes default alignment. 12 | #pragma pack(push,1) 13 | 14 | //////////////////////////////////////////////////////// 15 | // System descriptors 16 | //////////////////////////////////////////////////////// 17 | 18 | #define GDT_NULL 0 19 | #define GDT_R0_CODE 0x08 20 | #define GDT_R0_DATA 0x10 21 | #define GDT_R3_CODE 0x18 22 | #define GDT_R3_DATA 0x20 23 | #define GDT_TSS 0x28 24 | #define GDT_PCR 0x30 25 | #define GDT_R3_TEB 0x38 26 | #define GDT_VDM 0x40 27 | #define GDT_LDT 0x48 28 | #define GDT_DOUBLEFAULT_TSS 0x50 29 | #define GDT_NMI_TSS 0x58 30 | 31 | // 16-bit GDT entries: 32 | // TODO: #define GDT_ABIOS_UNKNOWN 0x60 (22F30-32F2F) 33 | #define GDT_ABIOS_VIDEO 0x68 34 | #define GDT_ABIOS_GDT 0x70 // descriptor describing ABIOS GDT itself 35 | #define GDT_ABIOS_NTOS 0x78 // first 64K of NTOSKRNL 36 | #define GDT_ABIOS_CDA 0xE8 // common data area 37 | #define GDT_ABIOS_CODE 0xF0 // KiI386AbiosCall 38 | #define GDT_ABIOS_STACK 0xF8 39 | 40 | #define SELECTOR_RPL_MASK 0x03 // bits 0-1 41 | #define SELECTOR_LDT 0x04 // bit 2 42 | 43 | // for data selectors 44 | #define DATA_ACCESS_MASK (1<<0) 45 | #define DATA_WRITE_ENABLE_MASK (1<<1) 46 | #define DATA_EXPAND_DOWN_MASK (1<<2) 47 | 48 | // for code selectors 49 | #define CODE_ACCESS_MASK (1<<0) 50 | #define CODE_READ_MASK (1<<1) 51 | #define CODE_CONFORMING_MASK (1<<2) 52 | #define CODE_FLAG (1<<3) 53 | 54 | #define TASK_GATE 5 55 | #define INTERRUPT_GATE 6 56 | #define TRAP_GATE 7 57 | 58 | typedef struct _IDT_ENTRY 59 | { 60 | USHORT LowOffset; 61 | USHORT Selector; 62 | UCHAR Ignored : 5; 63 | UCHAR Zero : 3; 64 | UCHAR Type : 3; 65 | UCHAR Is32Bit : 1; 66 | UCHAR Ignored2 : 1; 67 | UCHAR DPL : 2; 68 | UCHAR Present : 1; 69 | USHORT HighOffset; 70 | #ifdef _WIN64 71 | ULONG HighOffset64; 72 | ULONG Reserved; 73 | #endif 74 | } IDT_ENTRY, TRAP_GATE_ENTRY; 75 | 76 | typedef struct _CALL_GATE_ENTRY 77 | { 78 | USHORT LowOffset; 79 | USHORT Selector; 80 | UCHAR ParameterCount: 4; 81 | UCHAR Ignored : 3; 82 | UCHAR Type : 5; 83 | UCHAR DPL : 2; 84 | UCHAR Present : 1; 85 | USHORT HighOffset; 86 | #ifdef _WIN64 87 | ULONG HighOffset64; 88 | ULONG Reserved; 89 | #endif 90 | } CALL_GATE_ENTRY; 91 | 92 | typedef struct _TASK_GATE_ENTRY 93 | { 94 | USHORT Ignored; 95 | USHORT Selector; 96 | UCHAR Ignored2 : 5; 97 | UCHAR Zero : 3; 98 | UCHAR Type : 5; 99 | UCHAR DPL : 2; 100 | UCHAR Present : 1; 101 | USHORT Ignored3; 102 | } TASK_GATE_ENTRY; 103 | 104 | typedef struct _DESCRIPTOR_ENTRY 105 | { 106 | USHORT LimitLow; 107 | USHORT BaseLow; 108 | UCHAR BaseMid; 109 | UCHAR Type : 4; // 10EWA (code), E=ExpandDown, W=Writable, A=Accessed 110 | // 11CRA (data), C=Conforming, R=Readable, A=Accessed 111 | UCHAR System : 1; // if 1 then it is a gate or LDT 112 | UCHAR DPL : 2; // descriptor privilege level; 113 | // for data selectors, MAX(CPL, RPL) must be <= DPL to access (or else GP# fault) 114 | // for non-conforming code selectors (without callgate), MAX(CPL, RPL) must be <= DPL to access (or else GP# fault) 115 | // for conforming code selectors, MAX(CPL, RPL) must be >= DPL (i.e., CPL 0-2 cannot access if DPL is 3) 116 | // for non-conforming code selectors (with call gate), DPL indicates lowest privilege allowed to access gate 117 | UCHAR Present : 1; 118 | UCHAR LimitHigh : 4; 119 | UCHAR Available: 1; // aka AVL 120 | UCHAR Reserved : 1; 121 | UCHAR Is32Bit : 1; // aka B flag 122 | UCHAR Granularity : 1; // aka G flag 123 | UCHAR BaseHi : 8; 124 | #ifdef _WIN64 125 | ULONG HighOffset64; 126 | ULONG Reserved2; 127 | #endif 128 | } DESCRIPTOR_ENTRY; 129 | 130 | typedef struct _GATE_ENTRY 131 | { 132 | USHORT LowOffset; 133 | UCHAR Skip; 134 | UCHAR Type : 5; 135 | UCHAR DPL : 2; 136 | UCHAR Present : 1; 137 | USHORT HighOffset; 138 | #ifdef _WIN64 139 | ULONG HighOffset64; 140 | ULONG Reserved; 141 | #endif 142 | } GATE_ENTRY; 143 | 144 | // TODO: update for X64 145 | typedef struct _PTE_ENTRY 146 | { 147 | ULONG Present : 1; 148 | ULONG Write : 1; 149 | ULONG Owner : 1; // E.g., user mode or supervisor mode 150 | ULONG WriteThrough : 1; 151 | ULONG CacheDisable : 1; 152 | ULONG Accessed : 1; 153 | ULONG Dirty : 1; 154 | ULONG PAT : 1; 155 | ULONG Global : 1; 156 | ULONG CopyOnWrite : 1; 157 | ULONG Prototype : 1; 158 | ULONG Transition : 1; 159 | ULONG Address : 20; 160 | } PTE_ENTRY; 161 | 162 | // TODO: update for X64 163 | typedef struct _PDE_ENTRY 164 | { 165 | ULONG Present : 1; 166 | ULONG Write : 1; 167 | ULONG Owner : 1; 168 | ULONG WriteThrough : 1; 169 | ULONG CacheDisable : 1; 170 | ULONG Accessed : 1; 171 | ULONG Reserved1 : 1; 172 | ULONG PageSize : 1; 173 | ULONG Global : 1; 174 | ULONG Reserved : 3; 175 | ULONG Address : 20; 176 | } PDE_ENTRY; 177 | 178 | // TODO: update for X64 179 | typedef struct _IO_ACCESS_MAP 180 | { 181 | UCHAR DirectionMap[32]; 182 | UCHAR IoMap[8196]; 183 | } IO_ACCESS_MAP; 184 | 185 | #define MIN_TSS_SIZE FIELD_OFFSET(TSS_ENTRY, IoMaps) 186 | // TODO: update for X64 187 | typedef struct _TSS_ENTRY 188 | { 189 | USHORT Backlink; 190 | USHORT Reserved0; 191 | ULONG Esp0; 192 | USHORT Ss0; 193 | USHORT Reserved1; 194 | ULONG NotUsed1[4]; 195 | ULONG CR3; 196 | ULONG Eip; 197 | ULONG NotUsed2[9]; 198 | USHORT Es; 199 | USHORT Reserved2; 200 | USHORT Cs; 201 | USHORT Reserved3; 202 | USHORT Ss; 203 | USHORT Reserved4; 204 | USHORT Ds; 205 | USHORT Reserved5; 206 | USHORT Fs; 207 | USHORT Reserved6; 208 | USHORT Gs; 209 | USHORT Reserved7; 210 | USHORT LDT; 211 | USHORT Reserved8; 212 | USHORT Flags; 213 | USHORT IoMapBase; 214 | IO_ACCESS_MAP IoMaps[1]; 215 | UCHAR IntDirectionMap[32]; 216 | } TSS_ENTRY; 217 | 218 | // TODO: update for X64 219 | typedef struct _TSS16_ENTRY 220 | { 221 | USHORT Backlink; 222 | USHORT Sp0; 223 | USHORT Ss0; 224 | USHORT Sp1; 225 | USHORT Ss1; 226 | USHORT Sp2; 227 | USHORT Ss3; 228 | USHORT Ip; 229 | USHORT Flags; 230 | USHORT Ax; 231 | USHORT Cx; 232 | USHORT Dx; 233 | USHORT Bx; 234 | USHORT Sp; 235 | USHORT Bp; 236 | USHORT Si; 237 | USHORT Di; 238 | USHORT Es; 239 | USHORT Cs; 240 | USHORT Ss; 241 | USHORT Ds; 242 | USHORT LDT; 243 | } TSS16_ENTRY; 244 | 245 | // TODO: update for X64 246 | typedef struct _GDT_ENTRY 247 | { 248 | USHORT LimitLow; 249 | USHORT BaseLow; 250 | union { 251 | struct { 252 | UCHAR BaseMid; 253 | UCHAR Flags1; 254 | UCHAR Flags2; 255 | UCHAR BaseHi; 256 | } Bytes; 257 | struct { 258 | ULONG BaseMid : 8; 259 | ULONG Type : 5; 260 | ULONG Dpl : 2; 261 | ULONG Pres : 1; 262 | ULONG LimitHi : 4; 263 | ULONG Sys : 1; 264 | ULONG Reserved_0 : 1; 265 | ULONG Default_Big : 1; 266 | ULONG Granularity : 1; 267 | ULONG BaseHi : 8; 268 | } Bits; 269 | } HighWord; 270 | } GDT_ENTRY; 271 | 272 | BYTE *GetAbsoluteAddressFromSegment(BYTE Segment, DWORD Offset); 273 | BYTE *GetAbsoluteAddressFromSelector(WORD Selector, DWORD Offset); 274 | 275 | #pragma pack(pop) 276 | #ifdef __cplusplus 277 | } 278 | #endif 279 | #endif // CPU_H 280 | -------------------------------------------------------------------------------- /mhook/disasm-lib/disasm.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004, Matt Conover (mconover@gmail.com) 2 | #undef NDEBUG 3 | #include 4 | #include 5 | #include "disasm.h" 6 | 7 | #ifdef NO_SANITY_CHECKS 8 | #define NDEBUG 9 | #undef assert 10 | #define assert(x) 11 | #endif 12 | 13 | ////////////////////////////////////////////////////////////////////// 14 | // Global variables 15 | ////////////////////////////////////////////////////////////////////// 16 | 17 | ARCHITECTURE_FORMAT SupportedArchitectures[] = 18 | { 19 | { ARCH_X86, &X86 }, 20 | { ARCH_X86_16, &X86 }, 21 | { ARCH_X64, &X86 }, 22 | { ARCH_UNKNOWN, NULL } 23 | }; 24 | 25 | typedef struct _DISASM_ARG_INFO 26 | { 27 | INSTRUCTION *MatchedInstruction; 28 | BOOL MatchPrefix; 29 | U8 *Opcode; 30 | U32 OpcodeLength; 31 | INSTRUCTION_TYPE InstructionType; 32 | U32 Count; 33 | } DISASM_ARG_INFO; 34 | 35 | ////////////////////////////////////////////////////////////////////// 36 | // Function prototypes 37 | ////////////////////////////////////////////////////////////////////// 38 | 39 | BOOL InitInstruction(INSTRUCTION *Instruction, DISASSEMBLER *Disassembler); 40 | static struct _ARCHITECTURE_FORMAT *GetArchitectureFormat(ARCHITECTURE_TYPE Type); 41 | 42 | ////////////////////////////////////////////////////////////////////// 43 | // Disassembler setup 44 | ////////////////////////////////////////////////////////////////////// 45 | 46 | BOOL InitDisassembler(DISASSEMBLER *Disassembler, ARCHITECTURE_TYPE Architecture) 47 | { 48 | ARCHITECTURE_FORMAT *ArchFormat; 49 | 50 | memset(Disassembler, 0, sizeof(DISASSEMBLER)); 51 | Disassembler->Initialized = DISASSEMBLER_INITIALIZED; 52 | 53 | ArchFormat = GetArchitectureFormat(Architecture); 54 | if (!ArchFormat) { assert(0); return FALSE; } 55 | Disassembler->ArchType = ArchFormat->Type; 56 | Disassembler->Functions = ArchFormat->Functions; 57 | return TRUE; 58 | } 59 | 60 | void CloseDisassembler(DISASSEMBLER *Disassembler) 61 | { 62 | memset(Disassembler, 0, sizeof(DISASSEMBLER)); 63 | } 64 | 65 | ////////////////////////////////////////////////////////////////////// 66 | // Instruction setup 67 | ////////////////////////////////////////////////////////////////////// 68 | 69 | BOOL InitInstruction(INSTRUCTION *Instruction, DISASSEMBLER *Disassembler) 70 | { 71 | memset(Instruction, 0, sizeof(INSTRUCTION)); 72 | Instruction->Initialized = INSTRUCTION_INITIALIZED; 73 | Instruction->Disassembler = Disassembler; 74 | memset(Instruction->String, ' ', MAX_OPCODE_DESCRIPTION-1); 75 | Instruction->String[MAX_OPCODE_DESCRIPTION-1] = '\0'; 76 | return TRUE; 77 | } 78 | 79 | // If Decode = FALSE, only the following fields are valid: 80 | // Instruction->Length, Instruction->Address, Instruction->Prefixes, Instruction->PrefixCount, 81 | // Instruction->OpcodeBytes, Instruction->Instruction->OpcodeLength, Instruction->Groups, 82 | // Instruction->Type, Instruction->OperandCount 83 | // 84 | // If Disassemble = TRUE, then Instruction->String is valid (also requires Decode = TRUE) 85 | // 86 | // WARNING: This will overwrite the previously obtained instruction 87 | INSTRUCTION *GetInstruction(DISASSEMBLER *Disassembler, U64 VirtualAddress, U8 *Address, U32 Flags) 88 | { 89 | if (Disassembler->Initialized != DISASSEMBLER_INITIALIZED) { assert(0); return NULL; } 90 | assert(Address); 91 | InitInstruction(&Disassembler->Instruction, Disassembler); 92 | Disassembler->Instruction.Address = Address; 93 | Disassembler->Instruction.VirtualAddressDelta = VirtualAddress - (U64)Address; 94 | if (!Disassembler->Functions->GetInstruction(&Disassembler->Instruction, Address, Flags)) 95 | { 96 | assert(Disassembler->Instruction.Address == Address); 97 | assert(Disassembler->Instruction.Length < MAX_INSTRUCTION_LENGTH); 98 | 99 | // Save the address that failed, in case the lower-level disassembler didn't 100 | Disassembler->Instruction.Address = Address; 101 | Disassembler->Instruction.ErrorOccurred = TRUE; 102 | return NULL; 103 | } 104 | return &Disassembler->Instruction; 105 | } 106 | 107 | /////////////////////////////////////////////////////////////////////////// 108 | // Miscellaneous 109 | /////////////////////////////////////////////////////////////////////////// 110 | 111 | static ARCHITECTURE_FORMAT *GetArchitectureFormat(ARCHITECTURE_TYPE Type) 112 | { 113 | ARCHITECTURE_FORMAT *Format; 114 | for (Format = SupportedArchitectures; Format->Type != ARCH_UNKNOWN; Format++) 115 | { 116 | if (Format->Type == Type) return Format; 117 | } 118 | 119 | assert(0); 120 | return NULL; 121 | } 122 | 123 | -------------------------------------------------------------------------------- /mhook/disasm-lib/disasm.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004, Matt Conover (mconover@gmail.com) 2 | // 3 | // WARNING: 4 | // I wouldn't recommend changing any flags like OP_*, ITYPE_*, or *_MASK 5 | // aside from those marked as UNUSED. This is because the flags parts of 6 | // the flags are architecture independent and other are left to specific 7 | // architectures to define, so unless you understand the relationships 8 | // between them, I would leave them as is. 9 | 10 | #ifndef DISASM_H 11 | #define DISASM_H 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | #include 16 | #include 17 | #include "misc.h" 18 | 19 | typedef signed char S8; 20 | typedef unsigned char U8; 21 | typedef signed short S16; 22 | typedef unsigned short U16; 23 | typedef signed long S32; 24 | typedef unsigned long U32; 25 | typedef LONG64 S64; 26 | typedef ULONG64 U64; 27 | 28 | #ifdef SPEEDY 29 | // On Visual Studio 6, making the internal functions inline makes compiling take forever 30 | #define INTERNAL static _inline 31 | #define INLINE _inline 32 | #else 33 | #define INTERNAL static 34 | #define INLINE 35 | #endif 36 | 37 | #define VALID_INSTRUCTION(i) ((i) && !((i)->ErrorOccurred)) 38 | #define NEXT_INSTRUCTION(i) ((i)->Address + (i)->Length) 39 | #define DISASM_ARCH_TYPE(dis) ((dis)->ArchType) 40 | #define INS_ARCH_TYPE(ins) DISASM_ARCH_TYPE((ins)->Disassembler) 41 | 42 | // NOTE: these should be as big set to the maximum of the supported architectures 43 | #define MAX_PREFIX_LENGTH 15 44 | #define MAX_OPERAND_COUNT 3 45 | #define MAX_INSTRUCTION_LENGTH 25 46 | #define MAX_OPCODE_LENGTH 3 47 | #define MAX_OPCODE_DESCRIPTION 256 48 | 49 | ///////////////////////////////////////////////////////////////////// 50 | // Code branch 51 | ///////////////////////////////////////////////////////////////////// 52 | 53 | #define MAX_CODE_REFERENCE_COUNT 3 54 | 55 | typedef struct _CODE_BRANCH 56 | { 57 | U64 Addresses[MAX_CODE_REFERENCE_COUNT]; // NULL if multiple to addresses 58 | U32 Count; 59 | U8 IsLoop : 1; 60 | U8 IsCall : 1; // branch if false 61 | U8 IsIndirect : 1; // call/jmp [Address] 62 | U8 AddressOffset: 5; 63 | struct _INSTRUCTION_OPERAND *Operand; // the operand containg the address 64 | } CODE_BRANCH; 65 | 66 | ///////////////////////////////////////////////////////////////////// 67 | // Data references 68 | ///////////////////////////////////////////////////////////////////// 69 | 70 | #define MAX_DATA_REFERENCE_COUNT 3 71 | 72 | typedef struct _DATA_REFERENCE 73 | { 74 | U64 Addresses[MAX_DATA_REFERENCE_COUNT]; // NULL if multiple to addresses 75 | U32 Count; 76 | ULONG_PTR DataSize; 77 | struct _INSTRUCTION_OPERAND *Operand; // the operand containg the address 78 | } DATA_REFERENCE; 79 | 80 | //////////////////////////////////////////////////////////////////// 81 | // Instruction 82 | ///////////////////////////////////////////////////////////////////// 83 | 84 | // 85 | // Instruction types (bits 0-7) 86 | // Instruction groups (bits 8-26) 87 | // 88 | #define ITYPE_EXEC_OFFSET (1<<8) 89 | #define ITYPE_ARITH_OFFSET (1<<9) 90 | #define ITYPE_LOGIC_OFFSET (1<<10) 91 | #define ITYPE_STACK_OFFSET (1<<11) 92 | #define ITYPE_TESTCOND_OFFSET (1<<12) 93 | #define ITYPE_LOAD_OFFSET (1<<13) 94 | #define ITYPE_ARRAY_OFFSET (1<<14) 95 | #define ITYPE_BIT_OFFSET (1<<15) 96 | #define ITYPE_FLAG_OFFSET (1<<16) 97 | #define ITYPE_FPU_OFFSET (1<<17) 98 | #define ITYPE_TRAPS_OFFSET (1<<18) 99 | #define ITYPE_SYSTEM_OFFSET (1<<19) 100 | #define ITYPE_OTHER_OFFSET (1<<20) 101 | #define ITYPE_UNUSED1_OFFSET (1<<21) 102 | #define ITYPE_UNUSED2_OFFSET (1<<22) 103 | #define ITYPE_UNUSED3_OFFSET (1<<23) 104 | #define ITYPE_UNUSED4_OFFSET (1<<24) 105 | #define ITYPE_UNUSED5_OFFSET (1<<25) 106 | #define ITYPE_UNUSED6_OFFSET (1<<26) 107 | #define ITYPE_EXT_UNUSED1 (1<<27) 108 | #define ITYPE_EXT_UNUSED2 (1<<28) 109 | #define ITYPE_EXT_UNUSED3 (1<<29) 110 | #define ITYPE_EXT_UNUSED4 (1<<30) 111 | #define ITYPE_EXT_UNUSED5 (1<<31) 112 | 113 | // 114 | // X86-specific flags (bits 27-31) 115 | // 116 | 117 | #define ITYPE_EXT_64 ITYPE_EXT_UNUSED1 // Use index 1 if in 64-bit mode and 0 otherwise 118 | #define ITYPE_EXT_MODRM ITYPE_EXT_UNUSED2 // ModRM byte may extend the opcode 119 | #define ITYPE_EXT_SUFFIX ITYPE_EXT_UNUSED3 // byte after ModRM/SIB/displacement is the third opcode 120 | #define ITYPE_EXT_PREFIX ITYPE_EXT_UNUSED4 // prefix 121 | #define ITYPE_EXT_FPU ITYPE_EXT_UNUSED5 // FPU instructions require special handling 122 | 123 | #define ITYPE_3DNOW_OFFSET ITYPE_UNUSED1_OFFSET 124 | #define ITYPE_MMX_OFFSET ITYPE_UNUSED2_OFFSET 125 | #define ITYPE_SSE_OFFSET ITYPE_UNUSED3_OFFSET 126 | #define ITYPE_SSE2_OFFSET ITYPE_UNUSED4_OFFSET 127 | #define ITYPE_SSE3_OFFSET ITYPE_UNUSED5_OFFSET 128 | 129 | // 130 | // Instruction types 131 | // 132 | 133 | #define ITYPE_TYPE_MASK 0x7FFFFFFF 134 | #define ITYPE_GROUP_MASK 0x7FFFFF00 135 | 136 | typedef enum _INSTRUCTION_TYPE 137 | { 138 | // ITYPE_EXEC group 139 | ITYPE_EXEC = ITYPE_EXEC_OFFSET, 140 | ITYPE_BRANCH, 141 | ITYPE_BRANCHCC, // conditional (not necessarily just flags) 142 | ITYPE_CALL, 143 | ITYPE_CALLCC, // conditional (not necessarily just flags) 144 | ITYPE_RET, 145 | ITYPE_LOOPCC, 146 | 147 | // ITYPE_ARITH group 148 | ITYPE_ARITH = ITYPE_ARITH_OFFSET, 149 | ITYPE_XCHGADD, 150 | ITYPE_ADD, 151 | ITYPE_SUB, 152 | ITYPE_MUL, 153 | ITYPE_DIV, 154 | ITYPE_INC, 155 | ITYPE_DEC, 156 | ITYPE_SHL, 157 | ITYPE_SHR, 158 | ITYPE_ROL, 159 | ITYPE_ROR, 160 | 161 | // ITYPE_LOGIC group 162 | ITYPE_LOGIC=ITYPE_LOGIC_OFFSET, 163 | ITYPE_AND, 164 | ITYPE_OR, 165 | ITYPE_XOR, 166 | ITYPE_NOT, 167 | ITYPE_NEG, 168 | 169 | // ITYPE_STACK group 170 | ITYPE_STACK=ITYPE_STACK_OFFSET, 171 | ITYPE_PUSH, 172 | ITYPE_POP, 173 | ITYPE_PUSHA, 174 | ITYPE_POPA, 175 | ITYPE_PUSHF, 176 | ITYPE_POPF, 177 | ITYPE_ENTER, 178 | ITYPE_LEAVE, 179 | 180 | // ITYPE_TESTCOND group 181 | ITYPE_TESTCOND=ITYPE_TESTCOND_OFFSET, 182 | ITYPE_TEST, 183 | ITYPE_CMP, 184 | 185 | // ITYPE_LOAD group 186 | ITYPE_LOAD=ITYPE_LOAD_OFFSET, 187 | ITYPE_MOV, 188 | ITYPE_MOVCC, // conditional 189 | ITYPE_LEA, 190 | ITYPE_XCHG, 191 | ITYPE_XCHGCC, // conditional 192 | 193 | // ITYPE_ARRAY group 194 | ITYPE_ARRAY=ITYPE_ARRAY_OFFSET, 195 | ITYPE_STRCMP, 196 | ITYPE_STRLOAD, 197 | ITYPE_STRMOV, 198 | ITYPE_STRSTOR, 199 | ITYPE_XLAT, 200 | 201 | // ITYPE_BIT group 202 | ITYPE_BIT=ITYPE_BIT_OFFSET, 203 | ITYPE_BITTEST, 204 | ITYPE_BITSET, 205 | ITYPE_BITCLR, 206 | 207 | // ITYPE_FLAG group 208 | // PF = parify flag 209 | // ZF = zero flag 210 | // OF = overflow flag 211 | // DF = direction flag 212 | // SF = sign flag 213 | ITYPE_FLAG=ITYPE_FLAG_OFFSET, 214 | // clear 215 | ITYPE_CLEARCF, 216 | ITYPE_CLEARZF, 217 | ITYPE_CLEAROF, 218 | ITYPE_CLEARDF, 219 | ITYPE_CLEARSF, 220 | ITYPE_CLEARPF, 221 | // set 222 | ITYPE_SETCF, 223 | ITYPE_SETZF, 224 | ITYPE_SETOF, 225 | ITYPE_SETDF, 226 | ITYPE_SETSF, 227 | ITYPE_SETPF, 228 | // toggle 229 | ITYPE_TOGCF, 230 | ITYPE_TOGZF, 231 | ITYPE_TOGOF, 232 | ITYPE_TOGDF, 233 | ITYPE_TOGSF, 234 | ITYPE_TOGPF, 235 | 236 | // ITYPE_FPU group 237 | ITYPE_FPU=ITYPE_FPU_OFFSET, 238 | ITYPE_FADD, 239 | ITYPE_FSUB, 240 | ITYPE_FMUL, 241 | ITYPE_FDIV, 242 | ITYPE_FCOMP, 243 | ITYPE_FEXCH, 244 | ITYPE_FLOAD, 245 | ITYPE_FLOADENV, 246 | ITYPE_FSTORE, 247 | ITYPE_FSTOREENV, 248 | ITYPE_FSAVE, 249 | ITYPE_FRESTORE, 250 | ITYPE_FMOVCC, 251 | 252 | ITYPE_UNUSED1=ITYPE_UNUSED1_OFFSET, 253 | ITYPE_UNUSED2=ITYPE_UNUSED2_OFFSET, 254 | ITYPE_UNUSED3=ITYPE_UNUSED3_OFFSET, 255 | 256 | // ITYPE_MMX group 257 | ITYPE_MMX=ITYPE_MMX_OFFSET, 258 | ITYPE_MMX_MOV, 259 | ITYPE_MMX_ADD, 260 | ITYPE_MMX_SUB, 261 | ITYPE_MMX_MUL, 262 | ITYPE_MMX_DIV, 263 | ITYPE_MMX_AND, 264 | ITYPE_MMX_OR, 265 | ITYPE_MMX_XOR, 266 | ITYPE_MMX_CMP, 267 | 268 | // ITYPE_SSE group 269 | ITYPE_SSE=ITYPE_SSE_OFFSET, 270 | ITYPE_SSE_MOV, 271 | ITYPE_SSE_ADD, 272 | ITYPE_SSE_SUB, 273 | ITYPE_SSE_MUL, 274 | ITYPE_SSE_DIV, 275 | ITYPE_SSE_AND, 276 | ITYPE_SSE_OR, 277 | ITYPE_SSE_XOR, 278 | ITYPE_SSE_CMP, 279 | 280 | // ITYPE_SSE2 group 281 | ITYPE_SSE2=ITYPE_SSE2_OFFSET, 282 | ITYPE_SSE2_MOV, 283 | ITYPE_SSE2_ADD, 284 | ITYPE_SSE2_SUB, 285 | ITYPE_SSE2_MUL, 286 | ITYPE_SSE2_DIV, 287 | ITYPE_SSE2_AND, 288 | ITYPE_SSE2_OR, 289 | ITYPE_SSE2_XOR, 290 | ITYPE_SSE2_CMP, 291 | 292 | // ITYPE_SSE3 group 293 | ITYPE_SSE3=ITYPE_SSE3_OFFSET, 294 | ITYPE_SSE3_MOV, 295 | ITYPE_SSE3_ADD, 296 | ITYPE_SSE3_SUB, 297 | ITYPE_SSE3_MUL, 298 | ITYPE_SSE3_DIV, 299 | ITYPE_SSE3_AND, 300 | ITYPE_SSE3_OR, 301 | ITYPE_SSE3_XOR, 302 | ITYPE_SSE3_CMP, 303 | 304 | // ITYPE_3DNOW group 305 | ITYPE_3DNOW=ITYPE_3DNOW_OFFSET, 306 | ITYPE_3DNOW_ADD, 307 | ITYPE_3DNOW_SUB, 308 | ITYPE_3DNOW_MUL, 309 | ITYPE_3DNOW_DIV, 310 | ITYPE_3DNOW_CMP, 311 | ITYPE_3DNOW_XCHG, 312 | 313 | // ITYPE_TRAP 314 | ITYPE_TRAPS=ITYPE_TRAPS_OFFSET, 315 | ITYPE_TRAP, // generate trap 316 | ITYPE_TRAPCC, // conditional trap gen 317 | ITYPE_TRAPRET, // return from trap 318 | ITYPE_BOUNDS, // gen bounds trap 319 | ITYPE_DEBUG, // gen breakpoint trap 320 | ITYPE_TRACE, // gen single step trap 321 | ITYPE_INVALID, // gen invalid instruction 322 | ITYPE_OFLOW, // gen overflow trap 323 | 324 | // ITYPE_SYSTEM group 325 | ITYPE_SYSTEM=ITYPE_SYSTEM_OFFSET, 326 | ITYPE_HALT, // halt machine 327 | ITYPE_IN, // input form port 328 | ITYPE_OUT, // output to port 329 | ITYPE_CPUID, // identify cpu 330 | ITYPE_SETIF, // allow interrupts 331 | ITYPE_CLEARIF, // block interrupts 332 | ITYPE_SYSCALL, 333 | ITYPE_SYSCALLRET, 334 | 335 | // ITYPE_OTHER group 336 | ITYPE_OTHER = ITYPE_OTHER_OFFSET, 337 | ITYPE_NOP, 338 | ITYPE_BCDCONV, // convert to/from BCD 339 | ITYPE_SZCONV // convert size of operand 340 | } INSTRUCTION_TYPE; 341 | 342 | // 343 | // Operand flags 344 | // 345 | 346 | // Type = bits 0-6 (these are mutually exclusive -- bits 0-6 will always be a power of 2)) 347 | #define OPTYPE_NONE 0x00 348 | #define OPTYPE_IMM 0x01 // immediate value 349 | #define OPTYPE_OFFSET 0x02 // relative offset 350 | #define OPTYPE_FLOAT 0x03 // floating point 351 | #define OPTYPE_BCD 0x04 352 | #define OPTYPE_STRING 0x05 353 | #define OPTYPE_SPECIAL 0x06 354 | #define OPTYPE_MASK 0x7F 355 | 356 | // Flags = bits 7-23 (these can be combinations) 357 | // These are used in the X86 opcode table 358 | #define OP_REG (1<<7) // 0x80 359 | #define OP_SIGNED (1<<8) 360 | #define OP_SYS (1<<9) // parameter is an index into some system structure 361 | #define OP_CONDR (1<<10) 362 | #define OP_CONDW (1<<11) 363 | #define OP_UNUSED (1<<12) 364 | #define OP_SRC (1<<13) // operand is source operand 365 | #define OP_DST (1<<14) // operand is destination operand 366 | #define OP_EXEC (1<<15) // operand is executed 367 | 368 | #define OP_CONDE OP_CONDR 369 | #define OP_COND_EXEC (OP_CONDE|OP_EXEC) // executed only if the pre-conditions are met 370 | #define OP_COND_SRC (OP_CONDR|OP_SRC) // set only if pre-conditions are met 371 | #define OP_COND_DST (OP_CONDW|OP_DST) // set only if pre-conditions are met 372 | #define OP_COND (OP_CONDR|OP_CONDW) 373 | 374 | // Bits 16-31 are available for use outside of the opcode table, but they can only 375 | // be used in INSTRUCTION_OPERAND.Flags, they may conflit with the architecture specific 376 | // operands. For example, bits 16-31 are used in X86 for AMODE_* and OPTYPE_* 377 | #define OP_ADDRESS (1<<16) 378 | #define OP_LOCAL (1<<17) 379 | #define OP_PARAM (1<<18) 380 | #define OP_GLOBAL (1<<19) 381 | #define OP_FAR (1<<20) 382 | #define OP_IPREL (1<<21) 383 | 384 | // 385 | // X86-specific flags (bits 27-31) 386 | // 387 | #define OP_MSR (OP_SYS|OP_UNUSED) 388 | 389 | // 390 | // Other architecture flags 391 | // 392 | #define OP_DELAY OP_UNUSED // delayed instruction (e.g., delayed branch that executes after the next instruction) 393 | 394 | ///////////////////////////////////////////////////////////////////// 395 | // Architectures 396 | ///////////////////////////////////////////////////////////////////// 397 | 398 | typedef enum _ARCHITECTURE_TYPE 399 | { 400 | ARCH_UNKNOWN=0, 401 | 402 | // x86-based 403 | ARCH_X86, // 32-bit x86 404 | ARCH_X86_16, // 16-bit x86 405 | ARCH_X64, // AMD64 and Intel EMD64 406 | 407 | // everything else 408 | ARCH_ALPHA, 409 | ARCH_ARM, 410 | ARCH_DOTNET, 411 | ARCH_EFI, 412 | ARCH_IA64, 413 | ARCH_M68K, 414 | ARCH_MIPS, 415 | ARCH_PPC, 416 | ARCH_SH3, 417 | ARCH_SH4, 418 | ARCH_SPARC, 419 | ARCH_THUMB 420 | 421 | } ARCHITECTURE_TYPE; 422 | 423 | typedef BOOL (*INIT_INSTRUCTION)(struct _INSTRUCTION *Instruction); 424 | typedef void (*DUMP_INSTRUCTION)(struct _INSTRUCTION *Instruction, BOOL ShowBytes, BOOL Verbose); 425 | typedef BOOL (*GET_INSTRUCTION)(struct _INSTRUCTION *Instruction, U8 *Address, U32 Flags); 426 | typedef U8 *(*FIND_FUNCTION_BY_PROLOGUE)(struct _INSTRUCTION *Instruction, U8 *StartAddress, U8 *EndAddress, U32 Flags); 427 | 428 | typedef struct _ARCHITECTURE_FORMAT_FUNCTIONS 429 | { 430 | INIT_INSTRUCTION InitInstruction; 431 | DUMP_INSTRUCTION DumpInstruction; 432 | GET_INSTRUCTION GetInstruction; 433 | FIND_FUNCTION_BY_PROLOGUE FindFunctionByPrologue; 434 | } ARCHITECTURE_FORMAT_FUNCTIONS; 435 | 436 | typedef struct _ARCHITECTURE_FORMAT 437 | { 438 | ARCHITECTURE_TYPE Type; 439 | ARCHITECTURE_FORMAT_FUNCTIONS *Functions; 440 | } ARCHITECTURE_FORMAT; 441 | 442 | #define DISASSEMBLER_INITIALIZED 0x1234566F 443 | #define INSTRUCTION_INITIALIZED 0x1234567F 444 | 445 | #include "disasm_x86.h" 446 | 447 | typedef struct DECLSPEC_ALIGN(16) _S128 448 | { 449 | U64 Low; 450 | S64 High; 451 | } S128; 452 | typedef struct DECLSPEC_ALIGN(16) _U128 453 | { 454 | U64 Low; 455 | U64 High; 456 | } U128; 457 | 458 | typedef struct _INSTRUCTION_OPERAND 459 | { 460 | U32 Flags; 461 | U8 Type : 6; 462 | U8 Unused : 2; 463 | U16 Length; 464 | 465 | 466 | // If non-NULL, this indicates the target address of the instruction (e.g., a branch or 467 | // a displacement with no base register). However, this address is only reliable if the 468 | // image is mapped correctly (e.g., the executable is mapped as an image and fixups have 469 | // been applied if it is not at its preferred image base). 470 | // 471 | // If disassembling a 16-bit DOS application, TargetAddress is in the context of 472 | // X86Instruction->Segment. For example, if TargetAddress is the address of a code branch, 473 | // it is in the CS segment (unless X86Instruction->HasSegmentOverridePrefix is set). If 474 | // TargetAddress is a data pointer, it is in the DS segment (unless 475 | // X86Instruction->HasSegmentOverridePrefix is set) 476 | U64 TargetAddress; 477 | U32 Register; 478 | 479 | union 480 | { 481 | // All 8/16/32-bit operands are extended to 64-bits automatically 482 | // If you want to downcast, check whether Flags & OP_SIGNED is set 483 | // Like this: 484 | // U32 GetOperand32(OPERAND *Operand) 485 | // { 486 | // if (Operand->Flags & OP_SIGNED) return (S32)Operand->Value_S64; 487 | // else return (U32)Operand->Value_U64; 488 | //} 489 | U64 Value_U64; 490 | S64 Value_S64; 491 | U128 Value_U128; 492 | U128 Float128; 493 | U8 Float80[80]; 494 | U8 BCD[10]; 495 | }; 496 | } INSTRUCTION_OPERAND; 497 | 498 | typedef struct _INSTRUCTION 499 | { 500 | U32 Initialized; 501 | struct _DISASSEMBLER *Disassembler; 502 | 503 | char String[MAX_OPCODE_DESCRIPTION]; 504 | U8 StringIndex; 505 | U64 VirtualAddressDelta; 506 | 507 | U32 Groups; // ITYPE_EXEC, ITYPE_ARITH, etc. -- NOTE groups can be OR'd together 508 | INSTRUCTION_TYPE Type; // ITYPE_ADD, ITYPE_RET, etc. -- NOTE there is only one possible type 509 | 510 | U8 *Address; 511 | U8 *OpcodeAddress; 512 | U32 Length; 513 | 514 | U8 Prefixes[MAX_PREFIX_LENGTH]; 515 | U32 PrefixCount; 516 | 517 | U8 LastOpcode; // last byte of opcode 518 | U8 OpcodeBytes[MAX_OPCODE_LENGTH]; 519 | U32 OpcodeLength; // excludes any operands and prefixes 520 | 521 | INSTRUCTION_OPERAND Operands[MAX_OPERAND_COUNT]; 522 | U32 OperandCount; 523 | 524 | X86_INSTRUCTION X86; 525 | 526 | DATA_REFERENCE DataSrc; 527 | DATA_REFERENCE DataDst; 528 | CODE_BRANCH CodeBranch; 529 | 530 | // Direction depends on which direction the stack grows 531 | // For example, on x86 a push results in StackChange < 0 since the stack grows down 532 | // This is only relevant if (Group & ITYPE_STACK) is true 533 | // 534 | // If Groups & ITYPE_STACK is set but StackChange = 0, it means that the change 535 | // couldn't be determined (non-constant) 536 | LONG StackChange; 537 | 538 | // Used to assist in debugging 539 | // If set, the current instruction is doing something that requires special handling 540 | // For example, popf can cause tracing to be disabled 541 | 542 | U8 StringAligned : 1; // internal only 543 | U8 NeedsEmulation : 1; // instruction does something that re 544 | U8 Repeat : 1; // instruction repeats until some condition is met (e.g., REP prefix on X86) 545 | U8 ErrorOccurred : 1; // set if instruction is invalid 546 | U8 AnomalyOccurred : 1; // set if instruction is anomalous 547 | U8 LastInstruction : 1; // tells the iterator callback it is the last instruction 548 | U8 CodeBlockFirst: 1; 549 | U8 CodeBlockLast : 1; 550 | } INSTRUCTION; 551 | 552 | typedef struct _DISASSEMBLER 553 | { 554 | U32 Initialized; 555 | ARCHITECTURE_TYPE ArchType; 556 | ARCHITECTURE_FORMAT_FUNCTIONS *Functions; 557 | INSTRUCTION Instruction; 558 | U32 Stage1Count; // GetInstruction called 559 | U32 Stage2Count; // Opcode fully decoded 560 | U32 Stage3CountNoDecode; // made it through all checks when DISASM_DECODE is not set 561 | U32 Stage3CountWithDecode; // made it through all checks when DISASM_DECODE is set 562 | } DISASSEMBLER; 563 | 564 | #define DISASM_DISASSEMBLE (1<<1) 565 | #define DISASM_DECODE (1<<2) 566 | #define DISASM_SUPPRESSERRORS (1<<3) 567 | #define DISASM_SHOWFLAGS (1<<4) 568 | #define DISASM_ALIGNOUTPUT (1<<5) 569 | #define DISASM_DISASSEMBLE_MASK (DISASM_ALIGNOUTPUT|DISASM_SHOWBYTES|DISASM_DISASSEMBLE) 570 | 571 | BOOL InitDisassembler(DISASSEMBLER *Disassembler, ARCHITECTURE_TYPE Architecture); 572 | void CloseDisassembler(DISASSEMBLER *Disassembler); 573 | INSTRUCTION *GetInstruction(DISASSEMBLER *Disassembler, U64 VirtualAddress, U8 *Address, U32 Flags); 574 | 575 | #ifdef __cplusplus 576 | } 577 | #endif 578 | #endif // DISASM_H 579 | -------------------------------------------------------------------------------- /mhook/disasm-lib/disasm_x86.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2004, Matt Conover (mconover@gmail.com) 2 | #ifndef X86_DISASM_H 3 | #define X86_DISASM_H 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | // NOTE: the processor may actually accept less than this amount (officially 15) 9 | // #define AMD64_MAX_INSTRUCTION_LEN 15 // theoretical max 25=5+2+1+1+8+8 10 | #define AMD64_MAX_PREFIX_LENGTH 5 // 4 legacy + 1 rex 11 | #define AMD64_MAX_ADDRESS_LENGTH 18 // modrm + sib + 8 byte displacement + 8 byte immediate value 12 | 13 | // NOTE: the processor may actually accept less than this amount (officially 15) 14 | #define X86_MAX_INSTRUCTION_LEN 15 // theoretical 16=4+2+1+1+4+4 15 | #define X86_MAX_PREFIX_LENGTH 4 16 | #define X86_MAX_OPCODE_LENGTH 3 // third byte is either a suffix or prefix 17 | #define X86_MAX_ADDRESS_LENGTH 10 // modrm + sib + 4 byte displacement + 4 byte immediate value 18 | #define X86_MAX_OPERANDS 3 19 | 20 | #define X86_PREFIX(a) ((a)->MnemonicFlags == ITYPE_EXT_PREFIX) 21 | #define X86_SPECIAL_EXTENSION(a) ((a)->MnemonicFlags & (ITYPE_EXT_MODRM|ITYPE_EXT_FPU|ITYPE_EXT_SUFFIX|ITYPE_EXT_64)) 22 | #define X86_EXTENDED_OPCODE(a) ((a)->Table) 23 | #define X86_INVALID(a) (!(a)->MnemonicFlags && !(a)->Table) 24 | #define X86_OPERAND_COUNT(a) ((a)->OperandFlags[0] ? ((a)->OperandFlags[1] ? ((a)->OperandFlags[2] ? 3 : 2) : 1) : 0) 25 | #define X86_GET_CATEGORY(p) ((p)->MnemonicFlags & ITYPE_GROUP_MASK) 26 | #define X86_GET_TYPE(p) ((p)->MnemonicFlags & ITYPE_TYPE_MASK) 27 | 28 | // Various instructions being specially decoded 29 | #define X86_TWO_BYTE_OPCODE 0x0f 30 | #define PREFIX_SEGMENT_OVERRIDE_ES 0x26 31 | #define PREFIX_SEGMENT_OVERRIDE_CS 0x2e 32 | #define PREFIX_BRANCH_NOT_TAKEN 0x2e // used only with conditional jumps 33 | #define PREFIX_SEGMENT_OVERRIDE_SS 0x36 34 | #define PREFIX_SEGMENT_OVERRIDE_DS 0x3e 35 | #define PREFIX_BRANCH_TAKEN 0x3e // used only with conditional jumps 36 | #define PREFIX_SEGMENT_OVERRIDE_FS 0x64 37 | #define PREFIX_SEGMENT_OVERRIDE_GS 0x65 38 | #define PREFIX_OPERAND_SIZE 0x66 39 | #define PREFIX_ADDRESS_SIZE 0x67 40 | #define PREFIX_LOCK 0xf0 41 | #define PREFIX_REPNE 0xf2 42 | #define PREFIX_REP 0xf3 43 | 44 | ////////////////////////////////////////////////////////////////// 45 | // Implicit operand handling 46 | ////////////////////////////////////////////////////////////////// 47 | 48 | #define X86_AMODE_MASK 0x00FF0000 // bits 16-23 (AMODE_*) 49 | #define X86_OPFLAGS_MASK 0x0000FF80 // bits 7-15 (OPTYPE_*) 50 | #define X86_OPTYPE_MASK 0xFF0000FF // bits 0-7 (OPTYPE_* below + OP_REG) and 24-31 (OPTYPE_* above) 51 | 52 | #define OPTYPE_0 0x01 53 | #define OPTYPE_1 0x02 54 | #define OPTYPE_FF 0x03 55 | //... 56 | #define OPTYPE_CS 0x10 57 | #define OPTYPE_DS 0x11 58 | #define OPTYPE_ES 0x12 59 | #define OPTYPE_FS 0x13 60 | #define OPTYPE_GS 0x14 61 | #define OPTYPE_SS 0x15 62 | #define OPTYPE_CR0 0x16 63 | #define OPTYPE_TSC 0x17 // time stamp counter 64 | //... 65 | #define OPTYPE_FLAGS 0x20 66 | #define OPTYPE_xFLAGS 0x21 // RFLAGS/EFLAGS (depending on operand size) 67 | #define OPTYPE_xCX_HI_xBX_LO 0x22 // represented by 2 registers CX:BX or ECX:EBX (depending on operand size) 68 | #define OPTYPE_xDX_HI_xAX_LO 0x23 // DX:AX or EDX:EAX (depending on operand size) 69 | #define OPTYPE_EDX_HI_EAX_LO 0x24 // DX:AX or EDX:EAX (depending on operand size) 70 | #define OPTYPE_EDX_ECX_EBX_EAX 0x25 // all registers are set 71 | //... 72 | #define OPTYPE_STx 0x30 73 | #define OPTYPE_ST0 0x31 74 | #define OPTYPE_ST1 0x32 75 | #define OPTYPE_FPU_STATUS 0x33 76 | #define OPTYPE_FPU_CONTROL 0x34 77 | #define OPTYPE_FPU_TAG 0x35 78 | #define OPTYPE_FLDZ 0x36 // 0 79 | #define OPTYPE_FLD1 0x37 // 1 80 | #define OPTYPE_FLDPI 0x38 // pi 81 | #define OPTYPE_FLDL2T 0x39 // lg 10 82 | #define OPTYPE_FLDL2E 0x3A // lg e 83 | #define OPTYPE_FLDLG2 0x3B // log_10 2 84 | #define OPTYPE_FLDLN2 0x3C // log_e 2 85 | //... 86 | #define OPTYPE_CS_MSR 0x40 87 | #define OPTYPE_EIP_MSR 0x41 88 | #define OPTYPE_ESP_MSR 0x42 89 | #define OPTYPE_KERNELBASE_MSR 0x43 90 | #define OPTYPE_FMASK_MSR 0x44 91 | #define OPTYPE_STAR_MSR 0x45 92 | #define OPTYPE_CSTAR_MSR 0x46 // 32-bit mode 93 | #define OPTYPE_LSTAR_MSR 0x47 // 64-bit mode 94 | 95 | 96 | // NOTE: OPTYPES >= 0x80 reserved for registers (OP_REG+XX) 97 | #define OPTYPE_REG_AL OP_REG+0x01 98 | #define OPTYPE_REG_CL OP_REG+0x02 99 | #define OPTYPE_REG_AH OP_REG+0x03 100 | #define OPTYPE_REG_AX OP_REG+0x04 101 | #define OPTYPE_REG_DX OP_REG+0x05 102 | #define OPTYPE_REG_ECX OP_REG+0x06 103 | #define OPTYPE_REG8 OP_REG+0x07 104 | 105 | // If address size is 2, use BP 106 | // If address size is 4, use EBP 107 | // If address size is 8, use RBP 108 | #define OPTYPE_REG_xBP OP_REG+0x08 109 | 110 | // If address size is 2, use BP 111 | // If address size is 4, use EBP 112 | // If address size is 8, use RBP 113 | #define OPTYPE_REG_xSP OP_REG+0x09 114 | 115 | // If operand size is 2, take 8-bit register 116 | // If operand size is 4, take 16-bit register 117 | // If operand size is 8, take 32-bit register 118 | #define OPTYPE_REG_xAX_SMALL OP_REG+0x0a 119 | 120 | // If operand size is 2, take 16-bit register 121 | // If operand size is 4, take 32-bit register 122 | // If operand size is 8, take 64-bit register 123 | #define OPTYPE_REG_xAX_BIG OP_REG+0x0b 124 | 125 | typedef enum _CPU_TYPE 126 | { 127 | CPU_UNKNOWN=0, 128 | 129 | /////////////////////////////////////// 130 | // 1st generation 131 | /////////////////////////////////////// 132 | // 1978 133 | //CPU_8086 = 1MB address limit, 16-bit registers 134 | // 1982 135 | //CPU_i186 136 | 137 | /////////////////////////////////////// 138 | // 2nd generation 139 | /////////////////////////////////////// 140 | // 1982 141 | //CPU_I286 // 16MB limit, 16-bit registers, added protected mode 142 | CPU_I287, // CPU_I286 + math coprocessor 143 | 144 | /////////////////////////////////////// 145 | // 3rd generation 146 | /////////////////////////////////////// 147 | // 1985 148 | CPU_I386, // 32-bit registers, 4GB memory limit 149 | // 1988 150 | CPU_I387, // CPU_I386 + math coprocessor 151 | 152 | /////////////////////////////////////// 153 | // 4th generation (1989) 154 | /////////////////////////////////////// 155 | CPU_I486, 156 | 157 | /////////////////////////////////////// 158 | // 5th generation 159 | /////////////////////////////////////// 160 | // 1993 161 | CPU_PENTIUM, // superscalar architecture 162 | // 1997 163 | //CPU_PENTIUM_MMX 164 | 165 | /////////////////////////////////////// 166 | // 6th generation (1995) 167 | /////////////////////////////////////// 168 | CPU_PENTIUM_PRO, // P6 architecture, no MMX, out-of-order execution, speculative execution 169 | //CPU_CYRIX_6X86, 170 | //CPU_AMD_K5 // RISC processor 171 | // 1997 172 | CPU_PENTIUM2, // Pentium Pro architecture + MMX 173 | //CPU_AMD_K6, 174 | //CPU_CYRIX_6X86MX, // Cyrix 6x86 + MMX 175 | // 1998 176 | CPU_AMD_K6_2, // added 3DNow! (MMX) 177 | // 1999 178 | // CPU_AMD_K6_3 // added SSE 179 | 180 | /////////////////////////////////////// 181 | // 7th generation 182 | /////////////////////////////////////// 183 | // 1999 184 | CPU_PENTIUM3, // introduced SSE 185 | // CPU_AMD_K7 // aka Athlon 186 | // 2000 187 | CPU_PENTIUM4, // introduced SSE2 and hyperthreading 188 | 189 | // 2004? 2005? 190 | CPU_PRESCOTT, // introduced SSE3 191 | 192 | /////////////////////////////////////// 193 | // 8th generation (X86-64) 194 | // IA32 instruction set with 64-bit extensions, >4GB RAM 195 | /////////////////////////////////////// 196 | 197 | // 2003 198 | CPU_AMD64, // includes Athlon 64 and Opteron aka X86-64 199 | 200 | // 2004? 201 | //CPU_EMD64 // Intel's version of AMD64 202 | CPU_IA64 // aka Itanium: new instruction set -- adds JMPE to IA32 mode to return to IA64 native code 203 | 204 | } CPU_TYPE; 205 | 206 | ////////////////////////////////////////////////////////////////// 207 | // Conditions (these can be OR'd) 208 | ////////////////////////////////////////////////////////////////// 209 | 210 | // Used for Flags.Preconditions 211 | #define COND_O (1<<0) // overflow (signed) 212 | #define COND_C (1<<1) // below (unsigned) 213 | #define COND_Z (1<<2) // equal (unsigned) 214 | #define COND_S (1<<3) // sign set (signed) 215 | #define COND_P (1<<4) // parity even 216 | #define COND_BE (1<<5) // CF or ZF is set (unsigned) 217 | #define COND_L (1<<6) // (SF && !OF) || (OF && !SF) 218 | #define COND_LE (1<<7) // ZF || (SF && !OF) || (OF && !SF) (signed) 219 | #define COND_NO (1<<8) // !O 220 | #define COND_NC (1<<9) // !C (not below, above or equal to) 221 | #define COND_NZ (1<<10) // !Z (not equal) 222 | #define COND_NS (1<<11) // !S 223 | #define COND_NP (1<<12) // !P (parity odd) 224 | #define COND_NL (1<<13) // (!SF && !OF) || (SF && OF) 225 | #define COND_G (1<<14) // !ZF && ((!SF && !OF) || (SF && OF)) 226 | #define COND_D (1<<15) // DF 227 | #define COND_REG_xCX_BIG_Z (1<<16) // CX/ECX/RCX (depending on address size) == 0 228 | #define COND_REG_xCX_BIG_NZ (1<<17) // CX/ECX/RCX (depending on address size) != 0 229 | #define COND_OP1_EQ_OP2 (1<<18) 230 | #define COND_OP1_EQ_OP3 (1<<19) 231 | #define COND_B COND_C 232 | #define COND_NAE COND_C 233 | #define COND_E COND_Z 234 | #define COND_NA COND_BE 235 | #define COND_PE COND_P 236 | #define COND_U COND_P 237 | #define COND_NGE COND_L 238 | #define COND_NG COND_LE 239 | #define COND_PO COND_NP 240 | #define COND_NU COND_NP 241 | #define COND_NE COND_NZ 242 | #define COND_NB COND_NC 243 | #define COND_AE COND_NC 244 | #define COND_NE COND_NZ 245 | #define COND_A (COND_NC|COND_NZ) 246 | #define COND_NBE COND_A 247 | #define COND_GE COND_NL 248 | #define COND_NLE COND_G 249 | 250 | // Used for Opcode.FlagsChanged 251 | #define FLAG_CF_SET (1<<0) 252 | #define FLAG_DF_SET (1<<1) 253 | #define FLAG_IF_SET (1<<2) 254 | #define FLAG_SET_MASK (FLAG_CF_SET|FLAG_DF_SET|FLAG_IF_SET) 255 | 256 | #define FLAG_SF_CLR (1<<3) 257 | #define FLAG_ZF_CLR (1<<4) 258 | #define FLAG_AF_CLR (1<<5) 259 | #define FLAG_CF_CLR (1<<6) 260 | #define FLAG_DF_CLR (1<<7) 261 | #define FLAG_IF_CLR (1<<8) 262 | #define FLAG_OF_CLR (1<<9) 263 | #define FPU_C0_CLR (1<<19) 264 | #define FPU_C1_CLR (1<<20) 265 | #define FPU_C2_CLR (1<<21) 266 | #define FPU_C3_CLR (1<<22) 267 | #define FPU_ALL_CLR (FPU_C0_CLR|FPU_C1_CLR|FPU_C2_CLR|FPU_C3_CLR) 268 | #define FLAG_CLR_MASK (FLAG_SF_CLR|FLAG_ZF_CLR|FLAG_AF_CLR|FLAG_CF_CLR|FLAG_DF_CLR|FLAG_IF_CLR|FLAG_OF_CLR|FPU_ALL_CLR) 269 | 270 | #define FLAG_OF_MOD (1<<10) 271 | #define FLAG_SF_MOD (1<<11) 272 | #define FLAG_ZF_MOD (1<<12) 273 | #define FLAG_AF_MOD (1<<13) 274 | #define FLAG_PF_MOD (1<<14) 275 | #define FLAG_CF_MOD (1<<15) 276 | #define FLAG_DF_MOD (1<<16) 277 | #define FLAG_IF_MOD (1<<17) 278 | #define FLAG_ALL_MOD (FLAG_OF_MOD|FLAG_SF_MOD|FLAG_ZF_MOD|FLAG_AF_MOD|FLAG_PF_MOD|FLAG_CF_MOD|FLAG_DF_MOD|FLAG_IF_MOD) 279 | #define FLAG_COMMON_MOD (FLAG_OF_MOD|FLAG_SF_MOD|FLAG_ZF_MOD|FLAG_AF_MOD|FLAG_PF_MOD|FLAG_CF_MOD) 280 | #define FPU_C0_MOD (1<<23) 281 | #define FPU_C1_MOD (1<<24) 282 | #define FPU_C2_MOD (1<<25) 283 | #define FPU_C3_MOD (1<<26) 284 | #define FPU_ALL_MOD (FPU_C0_MOD|FPU_C1_MOD|FPU_C2_MOD|FPU_C3_MOD) 285 | #define FLAG_MOD_MASK (FLAG_ALL_MOD|FPU_ALL_MOD) 286 | 287 | #define FLAG_CF_TOG (1<<18) 288 | #define FLAG_TOG_MASK FLAG_CF_TOG 289 | 290 | // Used for Opcode.ResultsIfTrue and Opcode.ResultsIfFalse 291 | #define OP1_DST (1<<0) 292 | #define OP2_DST (1<<1) 293 | #define OP3_DST (1<<2) 294 | #define OP1_SRC (1<<3) 295 | #define OP2_SRC (1<<4) 296 | #define OP3_SRC (1<<5) 297 | #define FPU_STACK_INC (1<<6) 298 | #define FPU_STACK_INC2 (1<<7) 299 | #define FPU_STACK_DEC (1<<8) 300 | #define SERIALIZE_WRITE (1<<9) 301 | #define SERIALIZE_READ (1<<10) 302 | #define xCX_DEC (1<<11) 303 | #define xCX_REP_DEC (1<<12) 304 | #define xDI_DEC (1<<13) 305 | #define xDI_INC (1<<14) 306 | #define xSI_DEC (1<<15) 307 | #define xSI_INC (1<<16) 308 | #define xDI_DECx (1<<17) 309 | #define xDI_INCx (1<<18) 310 | #define xSI_DECx (1<<19) 311 | #define xSI_INCx (1<<20) 312 | #define FPU_STACK_PUSH FPU_STACK_DEC 313 | #define FPU_STACK_POP FPU_STACK_INC 314 | #define FPU_STACK_POP2 FPU_STACK_INC2 315 | #define SERIALIZE_ALL (SERIALIZE_WRITE|SERIALIZE_READ) 316 | 317 | #define X86_SEGMENT_OFFSET 0x00 318 | #define X86_TEST_OFFSET 0x10 319 | #define X86_CONTROL_OFFSET 0x20 320 | #define X86_DEBUG_OFFSET 0x30 321 | #define X86_FPU_OFFSET 0x40 322 | #define X86_MMX_OFFSET 0x50 323 | #define X86_XMM_OFFSET 0x60 324 | #define X86_8BIT_OFFSET 0x70 325 | #define X86_16BIT_OFFSET 0x80 326 | #define X86_32BIT_OFFSET 0x90 327 | #define AMD64_8BIT_OFFSET 0xA0 328 | #define AMD64_16BIT_OFFSET 0xB0 329 | #define AMD64_32BIT_OFFSET 0xC0 330 | #define AMD64_64BIT_OFFSET 0xD0 331 | 332 | typedef enum _X86_REGISTER 333 | { 334 | // Segments 335 | X86_SEG_ES = X86_SEGMENT_OFFSET, 336 | X86_SEG_CS, 337 | X86_SEG_SS, 338 | X86_SEG_DS, 339 | X86_SEG_FS, 340 | X86_SEG_GS, 341 | 342 | // Miscellaneous 343 | X86_REG_FLAGS, 344 | X86_REG_EFLAGS, 345 | AMD64_REG_RFLAGS, 346 | X86_REG_IP, 347 | X86_REG_EIP, 348 | AMD64_REG_RIP, 349 | 350 | // Test registers 351 | X86_REG_TR0 = X86_TEST_OFFSET, 352 | X86_REG_TR1, 353 | X86_REG_TR2, 354 | X86_REG_TR3, 355 | X86_REG_TR4, 356 | X86_REG_TR5, 357 | X86_REG_TR6, 358 | X86_REG_TR7, 359 | X86_REG_TR8, 360 | X86_REG_TR9, 361 | X86_REG_TR10, 362 | X86_REG_TR11, 363 | X86_REG_TR12, 364 | X86_REG_TR13, 365 | X86_REG_TR14, 366 | X86_REG_TR15, 367 | 368 | // Control registers 369 | X86_REG_CR0=X86_CONTROL_OFFSET, 370 | X86_REG_CR1, 371 | X86_REG_CR2, 372 | X86_REG_CR3, 373 | X86_REG_CR4, 374 | X86_REG_CR5, 375 | X86_REG_CR6, 376 | X86_REG_CR7, 377 | X86_REG_CR8, 378 | X86_REG_CR9, 379 | X86_REG_CR10, 380 | X86_REG_CR11, 381 | X86_REG_CR12, 382 | X86_REG_CR13, 383 | X86_REG_CR14, 384 | X86_REG_CR15, 385 | 386 | // Debug registers 387 | X86_REG_DR0=X86_DEBUG_OFFSET, 388 | X86_REG_DR1, 389 | X86_REG_DR2, 390 | X86_REG_DR3, 391 | X86_REG_DR4, 392 | X86_REG_DR5, 393 | X86_REG_DR6, 394 | X86_REG_DR7, 395 | X86_REG_DR8, 396 | X86_REG_DR9, 397 | X86_REG_DR10, 398 | X86_REG_DR11, 399 | X86_REG_DR12, 400 | X86_REG_DR13, 401 | X86_REG_DR14, 402 | X86_REG_DR15, 403 | 404 | // FPU registers 405 | X86_REG_ST0=X86_FPU_OFFSET, 406 | X86_REG_ST1, 407 | X86_REG_ST2, 408 | X86_REG_ST3, 409 | X86_REG_ST4, 410 | X86_REG_ST5, 411 | X86_REG_ST6, 412 | X86_REG_ST7, 413 | 414 | // MMX registers 415 | X86_REG_MM0=X86_MMX_OFFSET, 416 | X86_REG_MM1, 417 | X86_REG_MM2, 418 | X86_REG_MM3, 419 | X86_REG_MM4, 420 | X86_REG_MM5, 421 | X86_REG_MM6, 422 | X86_REG_MM7, 423 | 424 | // XMM registers 425 | X86_REG_XMM0=X86_XMM_OFFSET, 426 | X86_REG_XMM1, 427 | X86_REG_XMM2, 428 | X86_REG_XMM3, 429 | X86_REG_XMM4, 430 | X86_REG_XMM5, 431 | X86_REG_XMM6, 432 | X86_REG_XMM7, 433 | 434 | // 8-bit registers 435 | X86_REG_AL=X86_8BIT_OFFSET, 436 | X86_REG_CL, 437 | X86_REG_DL, 438 | X86_REG_BL, 439 | X86_REG_AH, 440 | X86_REG_CH, 441 | X86_REG_DH, 442 | X86_REG_BH, 443 | 444 | // 16-bit registers 445 | X86_REG_AX=X86_16BIT_OFFSET, 446 | X86_REG_CX, 447 | X86_REG_DX, 448 | X86_REG_BX, 449 | X86_REG_SP, 450 | X86_REG_BP, 451 | X86_REG_SI, 452 | X86_REG_DI, 453 | 454 | // 32-bit registers 455 | X86_REG_EAX=X86_32BIT_OFFSET, 456 | X86_REG_ECX, 457 | X86_REG_EDX, 458 | X86_REG_EBX, 459 | X86_REG_ESP, 460 | X86_REG_EBP, 461 | X86_REG_ESI, 462 | X86_REG_EDI, 463 | 464 | // AMD64 8-bit registers 465 | AMD64_REG_AL=AMD64_8BIT_OFFSET, 466 | AMD64_REG_CL, 467 | AMD64_REG_DL, 468 | AMD64_REG_BL, 469 | AMD64_REG_SPL, 470 | AMD64_REG_BPL, 471 | AMD64_REG_SIL, 472 | AMD64_REG_DIL, 473 | AMD64_REG_R8B, 474 | AMD64_REG_R9B, 475 | AMD64_REG_R10B, 476 | AMD64_REG_R11B, 477 | AMD64_REG_R12B, 478 | AMD64_REG_R13B, 479 | AMD64_REG_R14B, 480 | AMD64_REG_R15B, 481 | 482 | // AMD64 16-bit registers 483 | AMD64_REG_AX=AMD64_16BIT_OFFSET, 484 | AMD64_REG_CX, 485 | AMD64_REG_DX, 486 | AMD64_REG_BX, 487 | AMD64_REG_SP, 488 | AMD64_REG_BP, 489 | AMD64_REG_SI, 490 | AMD64_REG_DI, 491 | AMD64_REG_R8W, 492 | AMD64_REG_R9W, 493 | AMD64_REG_R10W, 494 | AMD64_REG_R11W, 495 | AMD64_REG_R12W, 496 | AMD64_REG_R13W, 497 | AMD64_REG_R14W, 498 | AMD64_REG_R15W, 499 | 500 | // AMD64 32-bit registers 501 | AMD64_REG_EAX=AMD64_32BIT_OFFSET, 502 | AMD64_REG_ECX, 503 | AMD64_REG_EDX, 504 | AMD64_REG_EBX, 505 | AMD64_REG_ESP, 506 | AMD64_REG_EBP, 507 | AMD64_REG_ESI, 508 | AMD64_REG_EDI, 509 | AMD64_REG_R8D, 510 | AMD64_REG_R9D, 511 | AMD64_REG_R10D, 512 | AMD64_REG_R11D, 513 | AMD64_REG_R12D, 514 | AMD64_REG_R13D, 515 | AMD64_REG_R14D, 516 | AMD64_REG_R15D, 517 | 518 | // AMD64 64-bit registers 519 | AMD64_REG_RAX=AMD64_64BIT_OFFSET, 520 | AMD64_REG_RCX, 521 | AMD64_REG_RDX, 522 | AMD64_REG_RBX, 523 | AMD64_REG_RSP, 524 | AMD64_REG_RBP, 525 | AMD64_REG_RSI, 526 | AMD64_REG_RDI, 527 | AMD64_REG_R8, 528 | AMD64_REG_R9, 529 | AMD64_REG_R10, 530 | AMD64_REG_R11, 531 | AMD64_REG_R12, 532 | AMD64_REG_R13, 533 | AMD64_REG_R14, 534 | AMD64_REG_R15 535 | } X86_REGISTER; 536 | 537 | typedef enum _X86_TEST_REGISTER 538 | { 539 | REG_TR0=0, 540 | REG_TR1, 541 | REG_TR2, 542 | REG_TR3, 543 | REG_TR4, 544 | REG_TR5, 545 | REG_TR6, 546 | REG_TR7, 547 | REG_TR8, 548 | REG_TR9, 549 | REG_TR10, 550 | REG_TR11, 551 | REG_TR12, 552 | REG_TR13, 553 | REG_TR14, 554 | REG_TR15 555 | } X86_TEST_REGISTER; 556 | 557 | typedef enum _X86_CONTROL_REGISTER 558 | { 559 | REG_CR0, 560 | REG_CR1, 561 | REG_CR2, 562 | REG_CR3, 563 | REG_CR4, 564 | REG_CR5, 565 | REG_CR6, 566 | REG_CR7, 567 | REG_CR8, 568 | REG_CR9, 569 | REG_CR10, 570 | REG_CR11, 571 | REG_CR12, 572 | REG_CR13, 573 | REG_CR14, 574 | REG_CR15 575 | } X86_CONTROL_REGISTER; 576 | 577 | typedef enum _X86_DEBUG_REGISTER 578 | { 579 | REG_DR0, 580 | REG_DR1, 581 | REG_DR2, 582 | REG_DR3, 583 | REG_DR4, 584 | REG_DR5, 585 | REG_DR6, 586 | REG_DR7, 587 | REG_DR8, 588 | REG_DR9, 589 | REG_DR10, 590 | REG_DR11, 591 | REG_DR12, 592 | REG_DR13, 593 | REG_DR14, 594 | REG_DR15 595 | } X86_DEBUG_REGISTER; 596 | 597 | typedef enum _X86_MMX_REGISTER 598 | { 599 | REG_MM0=0, 600 | REG_MM1=1, 601 | REG_MM2=2, 602 | REG_MM3=3, 603 | REG_MM4=4, 604 | REG_MM5=5, 605 | REG_MM6=6, 606 | REG_MM7=7 607 | } X86_MMX_REGISTER; 608 | 609 | typedef enum _X86_SSE_REGISTER 610 | { 611 | REG_XMM0=0, 612 | REG_XMM1=1, 613 | REG_XMM2=2, 614 | REG_XMM3=3, 615 | REG_XMM4=4, 616 | REG_XMM5=5, 617 | REG_XMM6=6, 618 | REG_XMM7=7 619 | } X86_SSE_REGISTER; 620 | 621 | typedef enum _X86_FPU_REGISTER 622 | { 623 | REG_ST0=0, 624 | REG_ST1=1, 625 | REG_ST2=2, 626 | REG_ST3=3, 627 | REG_ST4=4, 628 | REG_ST5=5, 629 | REG_ST6=6, 630 | REG_ST7=7 631 | } X86_FPU_REGISTER; 632 | 633 | typedef enum _X86_8BIT_REGISTER 634 | { 635 | REG_AL = 0, 636 | REG_CL = 1, 637 | REG_DL = 2, 638 | REG_BL = 3, 639 | REG_AH = 4, 640 | REG_CH = 5, 641 | REG_DH = 6, 642 | REG_BH = 7 643 | } X86_8BIT_REGISTER; 644 | 645 | typedef enum _X86_16BIT_REGISTER 646 | { 647 | REG_AX = 0, 648 | REG_CX = 1, 649 | REG_DX = 2, 650 | REG_BX = 3, 651 | REG_SP = 4, 652 | REG_BP = 5, 653 | REG_SI = 6, 654 | REG_DI = 7 655 | } X86_16BIT_REGISTER; 656 | 657 | typedef enum _X86_32BIT_REGISTER 658 | { 659 | REG_EAX = 0, 660 | REG_ECX = 1, 661 | REG_EDX = 2, 662 | REG_EBX = 3, 663 | REG_ESP = 4, 664 | REG_EBP = 5, 665 | REG_ESI = 6, 666 | REG_EDI = 7 667 | } X86_32BIT_REGISTER; 668 | 669 | typedef enum _X86_SEGMENT 670 | { 671 | SEG_ES = 0, 672 | SEG_CS = 1, 673 | SEG_SS = 2, 674 | SEG_DS = 3, 675 | SEG_FS = 4, 676 | SEG_GS = 5, 677 | SEG_MAX = 6 678 | } X86_SEGMENT; 679 | 680 | extern char *X86_Registers[]; 681 | 682 | #pragma pack(push,1) 683 | typedef struct _MODRM 684 | { 685 | U8 mod : 2; 686 | U8 reg : 3; 687 | U8 rm : 3; 688 | } MODRM; 689 | typedef struct _SIB 690 | { 691 | U8 scale : 2; 692 | U8 index : 3; 693 | U8 base : 3; 694 | } SIB; 695 | typedef struct _REX 696 | { 697 | U8 unused : 4; // bits 4,5,6,7 698 | U8 w : 1; // bit 3 699 | U8 r : 1; // bit 2 700 | U8 x : 1; // bit 1 701 | U8 b : 1; // bit 0 702 | } REX; 703 | typedef struct _REX_MODRM 704 | { 705 | U8 reg : 4; 706 | U8 rm : 4; 707 | } REX_MODRM; 708 | typedef struct _REX_SIB 709 | { 710 | U8 index : 4; 711 | U8 base : 4; 712 | } REX_SIB; 713 | #pragma pack(pop) 714 | 715 | // 716 | // Properties: 717 | // If an operand is OP_COND_EXEC, it means that it is executed only if the pre-conditions are met. 718 | // 719 | // If if an instruction has one or more OP_COND_DST operands, then the actions are determined by 720 | // whether the Opcode.Preconditions are met or not. If all the COND_* flags in Opcode.Preconditions 721 | // are true, then the results are determined by ResultsIfTrue. If the preconditions are not met, then 722 | // the results are determined by ResultsIfFalse. 723 | // 724 | // If Preconditions == NOCOND, then results in ResultsIfTrue are unconditional and ResultsIfFalse 725 | // is ignored 726 | // 727 | typedef struct _X86_OPCODE 728 | { 729 | struct _X86_OPCODE *Table; 730 | CPU_TYPE CPU; // minimum CPU (starting with i386) 731 | U32 MnemonicFlags; 732 | char Mnemonic[X86_MAX_INSTRUCTION_LEN+1]; 733 | U32 OperandFlags[X86_MAX_OPERANDS]; 734 | U32 Preconditions; 735 | U32 FlagsChanged; // changes in flags 736 | U32 ResultsIfTrue; // results if Preconditions are met 737 | U32 ResultsIfFalse; // results if Preconditions are not met 738 | } X86_OPCODE; 739 | 740 | typedef struct _X86_INSTRUCTION 741 | { 742 | struct _INSTRUCTION *Instruction; // the generic instruction format representing this instruction 743 | 744 | X86_OPCODE Opcode; 745 | 746 | U8 sib_b; 747 | U8 modrm_b; 748 | MODRM modrm; 749 | SIB sib; 750 | U8 rex_b; 751 | REX rex; 752 | REX_MODRM rex_modrm; 753 | REX_SIB rex_sib; 754 | 755 | X86_SEGMENT DstSegment; 756 | union 757 | { 758 | X86_SEGMENT Segment; 759 | DWORD Selector; 760 | }; 761 | 762 | // NOTE: these are for internal use, use Instruction->Operands[] 763 | // 764 | // If DstRegAddressing or SrcRegAddressing = TRUE then BaseRegister is the base register 765 | // It is the operand represented by SIBOperand 766 | // 767 | // The operand indices of the destination operands is in DstOpIndex[0 to DstOpCount-1] 768 | // The operand indices of the source operands is in SrcOpIndex[0 to SrcOpCount-1] 769 | // 770 | // These are used both for instructions like xadd/xchg (where both operands are source/destination) 771 | // and to represent implicit registers (e.g., cmpxchg) 772 | 773 | U8 SrcOpIndex[3]; 774 | U8 DstOpIndex[3]; 775 | 776 | // Addressing mode: 777 | // If DstRegAddressing = TRUE, then these apply to DstReg 778 | // If SrcRegAddressing = TRUE, then this applies to SrcReg[AddressIndex] 779 | // If both are false, then SrcReg and DstReg are not addresses 780 | X86_REGISTER BaseRegister; 781 | X86_REGISTER IndexRegister; 782 | 783 | U8 Scale; 784 | U8 HasDefault64Operand : 1; 785 | U8 HasOperandSizePrefix : 1; 786 | U8 HasAddressSizePrefix : 1; 787 | U8 HasSegmentOverridePrefix : 1; 788 | U8 HasLockPrefix : 1; 789 | U8 HasRepeatWhileEqualPrefix : 1; 790 | U8 HasRepeatWhileNotEqualPrefix : 1; 791 | U8 HasBranchTakenPrefix : 1; 792 | U8 HasBranchNotTakenPrefix : 1; 793 | U8 HasDstAddressing : 1; 794 | U8 HasSrcAddressing : 1; 795 | U8 HasModRM : 1; 796 | U8 HasBaseRegister : 1; 797 | U8 HasIndexRegister : 1; 798 | U8 HasFullDisplacement : 1; 799 | U8 HasDstSegment : 1; // used for ins/cmps/scas/movs/etc which have 2 segments 800 | U8 DstAddressIndex : 2; // DstOpIndex[DstAddressIndex] 801 | U8 SrcAddressIndex : 2; // SrcOpIndex[SrcAddressIndex] 802 | U8 DstOpCount : 2; 803 | U8 SrcOpCount : 2; 804 | U8 OperandSize : 4; 805 | U8 AddressSize : 4; 806 | U8 Relative : 1; 807 | U8 HasSelector : 1; // segment is actually a selector 808 | U8 Group : 5; 809 | 810 | S64 Displacement; 811 | 812 | } X86_INSTRUCTION; 813 | 814 | //////////////////////////////////////////////////////////////////////////////////// 815 | // Exported functions 816 | //////////////////////////////////////////////////////////////////////////////////// 817 | 818 | extern ARCHITECTURE_FORMAT_FUNCTIONS X86; 819 | 820 | // Instruction setup 821 | BOOL X86_InitInstruction(struct _INSTRUCTION *Instruction); 822 | void X86_CloseInstruction(struct _INSTRUCTION *Instruction); 823 | 824 | // Instruction translator 825 | BOOL X86_TranslateInstruction(struct _INSTRUCTION *Instruction, BOOL Verbose); 826 | 827 | // Instruction decoder 828 | BOOL X86_GetInstruction(struct _INSTRUCTION *Instruction, U8 *Address, DWORD Flags); 829 | 830 | // Function finding 831 | U8 *X86_FindFunctionByPrologue(struct _INSTRUCTION *Instruction, U8 *StartAddress, U8 *EndAddress, DWORD Flags); 832 | 833 | #ifdef __cplusplus 834 | } 835 | #endif 836 | #endif // X86_DISASM_H 837 | 838 | -------------------------------------------------------------------------------- /mhook/disasm-lib/misc.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002, Matt Conover (mconover@gmail.com) 2 | #ifndef MISC_H 3 | #define MISC_H 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 14 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 15 | 16 | // NOTE: start is inclusive, end is exclusive (as in start <= x < end) 17 | #define IS_IN_RANGE(x, s, e) \ 18 | ( \ 19 | ((ULONG_PTR)(x) == (ULONG_PTR)(s) && (ULONG_PTR)(x) == (ULONG_PTR)(e)) || \ 20 | ((ULONG_PTR)(x) >= (ULONG_PTR)(s) && (ULONG_PTR)(x) < (ULONG_PTR)(e)) \ 21 | ) 22 | 23 | #if _MSC_VER >= 1400 24 | #pragma warning(disable:4996) 25 | #endif 26 | 27 | #if defined(_WIN64) 28 | #define VALID_ADDRESS_MAX 0x7FFEFFFFFFFFFFFF // Win64 specific 29 | typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; 30 | #else 31 | #define VALID_ADDRESS_MAX 0x7FFEFFFF // Win32 specific 32 | typedef unsigned long ULONG_PTR, *PULONG_PTR; 33 | #endif 34 | 35 | #ifndef DECLSPEC_ALIGN 36 | #if (_MSC_VER >= 1300) && !defined(MIDL_PASS) 37 | #define DECLSPEC_ALIGN(x) __declspec(align(x)) 38 | #else 39 | #define DECLSPEC_ALIGN(x) 40 | #endif 41 | #endif 42 | 43 | #define VALID_ADDRESS_MIN 0x10000 // Win32 specific 44 | #define IS_VALID_ADDRESS(a) IS_IN_RANGE(a, VALID_ADDRESS_MIN, VALID_ADDRESS_MAX+1) 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif // MISC_H 50 | -------------------------------------------------------------------------------- /mhook/mhook-lib/mhook.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2007-2008, Marton Anka 2 | // 3 | //Permission is hereby granted, free of charge, to any person obtaining a 4 | //copy of this software and associated documentation files (the "Software"), 5 | //to deal in the Software without restriction, including without limitation 6 | //the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | //and/or sell copies of the Software, and to permit persons to whom the 8 | //Software is furnished to do so, subject to the following conditions: 9 | // 10 | //The above copyright notice and this permission notice shall be included 11 | //in all copies or substantial portions of the Software. 12 | // 13 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | //OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | //THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | //FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | //IN THE SOFTWARE. 20 | 21 | #include 22 | #include 23 | #include 24 | #include "mhook.h" 25 | #include "../disasm-lib/disasm.h" 26 | 27 | //========================================================================= 28 | #ifndef GOOD_HANDLE 29 | #define GOOD_HANDLE(a) ((a!=INVALID_HANDLE_VALUE)&&(a!=NULL)) 30 | #endif 31 | 32 | //========================================================================= 33 | #ifndef gle 34 | #define gle GetLastError 35 | #endif 36 | 37 | //========================================================================= 38 | #ifndef ODPRINTF 39 | 40 | #ifdef _DEBUG 41 | #define ODPRINTF(a) odprintf a 42 | #else 43 | #define ODPRINTF(a) 44 | #endif 45 | 46 | inline void __cdecl odprintf(PCWSTR format, ...) 47 | { 48 | va_list args; 49 | va_start(args, format); 50 | int len = _vscwprintf(format, args); 51 | if (len > 0) 52 | { 53 | len += (1 + 2); 54 | PWSTR buf = (PWSTR) malloc(sizeof(WCHAR)*len); 55 | if (buf) 56 | { 57 | len = vswprintf_s(buf, len, format, args); 58 | if (len > 0) 59 | { 60 | while (len && iswspace(buf[len-1])) len--; 61 | buf[len++] = L'\r'; 62 | buf[len++] = L'\n'; 63 | buf[len] = 0; 64 | OutputDebugStringW(buf); 65 | } 66 | free(buf); 67 | } 68 | va_end(args); 69 | } 70 | } 71 | 72 | #endif //#ifndef ODPRINTF 73 | 74 | //========================================================================= 75 | #define MHOOKS_MAX_CODE_BYTES 32 76 | #define MHOOKS_MAX_RIPS 4 77 | 78 | //========================================================================= 79 | // The trampoline structure - stores every bit of info about a hook 80 | struct MHOOKS_TRAMPOLINE 81 | { 82 | PBYTE pSystemFunction; // the original system function 83 | DWORD cbOverwrittenCode; // number of bytes overwritten by the jump 84 | PBYTE pHookFunction; // the hook function that we provide 85 | BYTE codeJumpToHookFunction[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that jumps to the hook function 86 | BYTE codeTrampoline[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that holds the first few 87 | // bytes from the system function and a jump to the remainder 88 | // in the original location 89 | BYTE codeUntouched[MHOOKS_MAX_CODE_BYTES]; // placeholder for unmodified original code 90 | // (we patch IP-relative addressing) 91 | MHOOKS_TRAMPOLINE* pPrevTrampoline; // When in the free list, thess are pointers to the prev and next entry. 92 | MHOOKS_TRAMPOLINE* pNextTrampoline; // When not in the free list, this is a pointer to the prev and next trampoline in use. 93 | }; 94 | 95 | //========================================================================= 96 | // The patch data structures - store info about rip-relative instructions 97 | // during hook placement 98 | struct MHOOKS_RIPINFO 99 | { 100 | DWORD dwOffset; 101 | S64 nDisplacement; 102 | }; 103 | 104 | struct MHOOKS_PATCHDATA 105 | { 106 | S64 nLimitUp; 107 | S64 nLimitDown; 108 | DWORD nRipCnt; 109 | MHOOKS_RIPINFO rips[MHOOKS_MAX_RIPS]; 110 | }; 111 | 112 | //========================================================================= 113 | // Hook context contains info about one hook 114 | struct HOOK_CONTEXT 115 | { 116 | PVOID pSystemFunction; 117 | PVOID pHookFunction; 118 | DWORD dwInstructionLength; 119 | MHOOKS_TRAMPOLINE* pTrampoline; 120 | 121 | MHOOKS_PATCHDATA patchdata; 122 | 123 | bool needPatchJump; 124 | bool needPatchCall; 125 | }; 126 | 127 | //========================================================================= 128 | // Global vars 129 | static bool g_bVarsInitialized = false; 130 | static CRITICAL_SECTION g_cs; 131 | static MHOOKS_TRAMPOLINE* g_pHooks = NULL; 132 | static MHOOKS_TRAMPOLINE* g_pFreeList = NULL; 133 | static HANDLE* g_hThreadHandles = NULL; 134 | static DWORD g_nThreadHandles = 0; 135 | #define MHOOK_JMPSIZE 5 136 | #define MHOOK_MINALLOCSIZE 4096 137 | 138 | //========================================================================= 139 | // ntdll definitions 140 | 141 | typedef LONG NTSTATUS; 142 | 143 | typedef LONG KPRIORITY; 144 | 145 | typedef enum _SYSTEM_INFORMATION_CLASS 146 | { 147 | SystemBasicInformation = 0, 148 | SystemPerformanceInformation = 2, 149 | SystemTimeOfDayInformation = 3, 150 | SystemProcessInformation = 5, 151 | SystemProcessorPerformanceInformation = 8, 152 | SystemHandleInformation = 16, 153 | SystemInterruptInformation = 23, 154 | SystemExceptionInformation = 33, 155 | SystemRegistryQuotaInformation = 37, 156 | SystemLookasideInformation = 45, 157 | SystemProcessIdInformation = 0x58 158 | } SYSTEM_INFORMATION_CLASS; 159 | 160 | typedef enum _KWAIT_REASON 161 | { 162 | Executive, 163 | FreePage, 164 | PageIn, 165 | PoolAllocation, 166 | DelayExecution, 167 | Suspended, 168 | UserRequest, 169 | WrExecutive, 170 | WrFreePage, 171 | WrPageIn, 172 | WrPoolAllocation, 173 | WrDelayExecution, 174 | WrSuspended, 175 | WrUserRequest, 176 | WrEventPair, 177 | WrQueue, 178 | WrLpcReceive, 179 | WrLpcReply, 180 | WrVirtualMemory, 181 | WrPageOut, 182 | WrRendezvous, 183 | Spare2, 184 | Spare3, 185 | Spare4, 186 | Spare5, 187 | Spare6, 188 | WrKernel, 189 | MaximumWaitReason 190 | } KWAIT_REASON, *PKWAIT_REASON; 191 | 192 | typedef struct _CLIENT_ID 193 | { 194 | HANDLE UniqueProcess; 195 | HANDLE UniqueThread; 196 | } CLIENT_ID, *PCLIENT_ID; 197 | 198 | typedef struct _SYSTEM_THREAD_INFORMATION 199 | { 200 | LARGE_INTEGER KernelTime; 201 | LARGE_INTEGER UserTime; 202 | LARGE_INTEGER CreateTime; 203 | ULONG WaitTime; 204 | PVOID StartAddress; 205 | CLIENT_ID ClientId; 206 | KPRIORITY Priority; 207 | LONG BasePriority; 208 | ULONG ContextSwitches; 209 | ULONG ThreadState; 210 | KWAIT_REASON WaitReason; 211 | } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; 212 | 213 | typedef struct _UNICODE_STRING 214 | { 215 | USHORT Length; 216 | USHORT MaximumLength; 217 | PWSTR Buffer; 218 | } UNICODE_STRING; 219 | 220 | typedef struct _SYSTEM_PROCESS_INFORMATION 221 | { 222 | ULONG uNext; 223 | ULONG uThreadCount; 224 | LARGE_INTEGER WorkingSetPrivateSize; // since VISTA 225 | ULONG HardFaultCount; // since WIN7 226 | ULONG NumberOfThreadsHighWatermark; // since WIN7 227 | ULONGLONG CycleTime; // since WIN7 228 | LARGE_INTEGER CreateTime; 229 | LARGE_INTEGER UserTime; 230 | LARGE_INTEGER KernelTime; 231 | UNICODE_STRING ImageName; 232 | KPRIORITY BasePriority; 233 | HANDLE uUniqueProcessId; 234 | HANDLE InheritedFromUniqueProcessId; 235 | ULONG HandleCount; 236 | ULONG SessionId; 237 | ULONG_PTR UniqueProcessKey; // since VISTA (requires SystemExtendedProcessInformation) 238 | SIZE_T PeakVirtualSize; 239 | SIZE_T VirtualSize; 240 | ULONG PageFaultCount; 241 | SIZE_T PeakWorkingSetSize; 242 | SIZE_T WorkingSetSize; 243 | SIZE_T QuotaPeakPagedPoolUsage; 244 | SIZE_T QuotaPagedPoolUsage; 245 | SIZE_T QuotaPeakNonPagedPoolUsage; 246 | SIZE_T QuotaNonPagedPoolUsage; 247 | SIZE_T PagefileUsage; 248 | SIZE_T PeakPagefileUsage; 249 | SIZE_T PrivatePageCount; 250 | LARGE_INTEGER ReadOperationCount; 251 | LARGE_INTEGER WriteOperationCount; 252 | LARGE_INTEGER OtherOperationCount; 253 | LARGE_INTEGER ReadTransferCount; 254 | LARGE_INTEGER WriteTransferCount; 255 | LARGE_INTEGER OtherTransferCount; 256 | SYSTEM_THREAD_INFORMATION Threads[1]; 257 | } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; 258 | 259 | //========================================================================= 260 | // ZwQuerySystemInformation definitions 261 | typedef NTSTATUS(NTAPI* PZwQuerySystemInformation)( 262 | SYSTEM_INFORMATION_CLASS SystemInformationClass, 263 | __inout PVOID SystemInformation, 264 | ULONG SystemInformationLength, 265 | __out_opt PULONG ReturnLength 266 | ); 267 | 268 | #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) 269 | static PZwQuerySystemInformation fnZwQuerySystemInformation = reinterpret_cast(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQuerySystemInformation")); 270 | 271 | //========================================================================= 272 | // Internal function: 273 | // 274 | // Remove the trampoline from the specified list, updating the head pointer 275 | // if necessary. 276 | //========================================================================= 277 | static VOID ListRemove(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) 278 | { 279 | if (pNode->pPrevTrampoline) 280 | { 281 | pNode->pPrevTrampoline->pNextTrampoline = pNode->pNextTrampoline; 282 | } 283 | 284 | if (pNode->pNextTrampoline) 285 | { 286 | pNode->pNextTrampoline->pPrevTrampoline = pNode->pPrevTrampoline; 287 | } 288 | 289 | if ((*pListHead) == pNode) 290 | { 291 | (*pListHead) = pNode->pNextTrampoline; 292 | if (*pListHead != NULL) 293 | { 294 | assert((*pListHead)->pPrevTrampoline == NULL); 295 | } 296 | } 297 | 298 | pNode->pPrevTrampoline = NULL; 299 | pNode->pNextTrampoline = NULL; 300 | } 301 | 302 | //========================================================================= 303 | // Internal function: 304 | // 305 | // Prepend the trampoline from the specified list and update the head pointer. 306 | //========================================================================= 307 | static VOID ListPrepend(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) 308 | { 309 | pNode->pPrevTrampoline = NULL; 310 | pNode->pNextTrampoline = (*pListHead); 311 | if ((*pListHead)) 312 | { 313 | (*pListHead)->pPrevTrampoline = pNode; 314 | } 315 | (*pListHead) = pNode; 316 | } 317 | 318 | //========================================================================= 319 | // Internal function: 320 | // 321 | // For iteration over the list 322 | //========================================================================= 323 | static MHOOKS_TRAMPOLINE* ListNext(MHOOKS_TRAMPOLINE* pNode) 324 | { 325 | return pNode && pNode->pNextTrampoline ? pNode->pNextTrampoline : NULL; 326 | } 327 | 328 | //========================================================================= 329 | static VOID EnterCritSec() 330 | { 331 | if (!g_bVarsInitialized) 332 | { 333 | InitializeCriticalSection(&g_cs); 334 | g_bVarsInitialized = true; 335 | } 336 | EnterCriticalSection(&g_cs); 337 | } 338 | 339 | //========================================================================= 340 | static VOID LeaveCritSec() 341 | { 342 | LeaveCriticalSection(&g_cs); 343 | } 344 | 345 | //========================================================================= 346 | // Internal function: 347 | // 348 | // Skip over jumps that lead to the real function. Gets around import 349 | // jump tables, etc. 350 | //========================================================================= 351 | static PBYTE SkipJumps(PBYTE pbCode) 352 | { 353 | PBYTE pbOrgCode = pbCode; 354 | #ifdef _M_IX86_X64 355 | #ifdef _M_IX86 356 | //mov edi,edi: hot patch point 357 | if (pbCode[0] == 0x8b && pbCode[1] == 0xff) 358 | pbCode += 2; 359 | // push ebp; mov ebp, esp; pop ebp; 360 | // "collapsed" stackframe generated by MSVC 361 | if (pbCode[0] == 0x55 && pbCode[1] == 0x8b && pbCode[2] == 0xec && pbCode[3] == 0x5d) 362 | pbCode += 4; 363 | #endif 364 | if (pbCode[0] == 0xff && pbCode[1] == 0x25) 365 | { 366 | #ifdef _M_IX86 367 | // on x86 we have an absolute pointer... 368 | PBYTE pbTarget = *(PBYTE *)&pbCode[2]; 369 | // ... that shows us an absolute pointer. 370 | return SkipJumps(*(PBYTE *)pbTarget); 371 | #elif defined _M_X64 372 | // on x64 we have a 32-bit offset... 373 | INT32 lOffset = *(INT32 *)&pbCode[2]; 374 | // ... that shows us an absolute pointer 375 | return SkipJumps(*(PBYTE*)(pbCode + 6 + lOffset)); 376 | } 377 | else if (pbCode[0] == 0x48 && pbCode[1] == 0xff && pbCode[2] == 0x25) 378 | { 379 | // or we can have the same with a REX prefix 380 | INT32 lOffset = *(INT32 *)&pbCode[3]; 381 | // ... that shows us an absolute pointer 382 | return SkipJumps(*(PBYTE*)(pbCode + 7 + lOffset)); 383 | #endif 384 | } 385 | else if (pbCode[0] == 0xe9) 386 | { 387 | // here the behavior is identical, we have... 388 | // ...a 32-bit offset to the destination. 389 | return SkipJumps(pbCode + 5 + *(INT32 *)&pbCode[1]); 390 | } 391 | else if (pbCode[0] == 0xeb) 392 | { 393 | // and finally an 8-bit offset to the destination 394 | return SkipJumps(pbCode + 2 + *(CHAR *)&pbCode[1]); 395 | } 396 | #else 397 | #error unsupported platform 398 | #endif 399 | return pbOrgCode; 400 | } 401 | 402 | //========================================================================= 403 | // Internal function: 404 | // 405 | // Writes code at pbCode that jumps to pbJumpTo. Will attempt to do this 406 | // in as few bytes as possible. Important on x64 where the long jump 407 | // (0xff 0x25 ....) can take up 14 bytes. 408 | //========================================================================= 409 | static PBYTE EmitJump(PBYTE pbCode, PBYTE pbJumpTo) 410 | { 411 | #ifdef _M_IX86_X64 412 | PBYTE pbJumpFrom = pbCode + 5; 413 | SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo - pbJumpFrom; 414 | ODPRINTF((L"mhooks: EmitJump: Jumping from %p to %p, diff is %p", pbJumpFrom, pbJumpTo, cbDiff)); 415 | if (cbDiff <= 0x7fff0000) 416 | { 417 | pbCode[0] = 0xe9; 418 | pbCode += 1; 419 | *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom); 420 | pbCode += sizeof(DWORD); 421 | } 422 | else 423 | { 424 | pbCode[0] = 0xff; 425 | pbCode[1] = 0x25; 426 | pbCode += 2; 427 | #ifdef _M_IX86 428 | // on x86 we write an absolute address (just behind the instruction) 429 | *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbCode + sizeof(DWORD)); 430 | #elif defined _M_X64 431 | // on x64 we write the relative address of the same location 432 | *((PDWORD)pbCode) = (DWORD)0; 433 | #endif 434 | pbCode += sizeof(DWORD); 435 | *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo); 436 | pbCode += sizeof(DWORD_PTR); 437 | } 438 | #else 439 | #error unsupported platform 440 | #endif 441 | return pbCode; 442 | } 443 | 444 | //========================================================================= 445 | // Internal function: 446 | // 447 | // Round down to the next multiple of rndDown 448 | //========================================================================= 449 | static size_t RoundDown(size_t addr, size_t rndDown) 450 | { 451 | return (addr / rndDown) * rndDown; 452 | } 453 | 454 | //========================================================================= 455 | // Internal function: 456 | // 457 | // Will attempt allocate a block of memory within the specified range, as 458 | // near as possible to the specified function. 459 | //========================================================================= 460 | static MHOOKS_TRAMPOLINE* BlockAlloc(PBYTE pSystemFunction, PBYTE pbLower, PBYTE pbUpper) 461 | { 462 | SYSTEM_INFO sSysInfo = {0}; 463 | ::GetSystemInfo(&sSysInfo); 464 | 465 | // Always allocate in bulk, in case the system actually has a smaller allocation granularity than MINALLOCSIZE. 466 | const ptrdiff_t cAllocSize = MAX(sSysInfo.dwAllocationGranularity, MHOOK_MINALLOCSIZE); 467 | 468 | MHOOKS_TRAMPOLINE* pRetVal = NULL; 469 | PBYTE pModuleGuess = (PBYTE) RoundDown((size_t)pSystemFunction, cAllocSize); 470 | int loopCount = 0; 471 | for (PBYTE pbAlloc = pModuleGuess; pbLower < pbAlloc && pbAlloc < pbUpper; ++loopCount) 472 | { 473 | // determine current state 474 | MEMORY_BASIC_INFORMATION mbi; 475 | ODPRINTF((L"mhooks: BlockAlloc: Looking at address %p", pbAlloc)); 476 | if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi))) 477 | break; 478 | // free & large enough? 479 | if (mbi.State == MEM_FREE && mbi.RegionSize >= (unsigned)cAllocSize) 480 | { 481 | // and then try to allocate it 482 | pRetVal = (MHOOKS_TRAMPOLINE*)VirtualAlloc(pbAlloc, cAllocSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); 483 | if (pRetVal) 484 | { 485 | size_t trampolineCount = cAllocSize / sizeof(MHOOKS_TRAMPOLINE); 486 | ODPRINTF((L"mhooks: BlockAlloc: Allocated block at %p as %d trampolines", pRetVal, trampolineCount)); 487 | 488 | pRetVal[0].pPrevTrampoline = NULL; 489 | pRetVal[0].pNextTrampoline = &pRetVal[1]; 490 | 491 | // prepare them by having them point down the line at the next entry. 492 | for (size_t s = 1; s < trampolineCount; ++s) 493 | { 494 | pRetVal[s].pPrevTrampoline = &pRetVal[s - 1]; 495 | pRetVal[s].pNextTrampoline = &pRetVal[s + 1]; 496 | } 497 | 498 | // last entry points to the current head of the free list 499 | pRetVal[trampolineCount - 1].pNextTrampoline = g_pFreeList; 500 | if (g_pFreeList) 501 | { 502 | g_pFreeList->pPrevTrampoline = &pRetVal[trampolineCount - 1]; 503 | } 504 | break; 505 | } 506 | } 507 | 508 | // This is a spiral, should be -1, 1, -2, 2, -3, 3, etc. (* cAllocSize) 509 | ptrdiff_t bytesToOffset = (cAllocSize * (loopCount + 1) * ((loopCount % 2 == 0) ? -1 : 1)); 510 | pbAlloc = pbAlloc + bytesToOffset; 511 | } 512 | 513 | return pRetVal; 514 | } 515 | 516 | //========================================================================= 517 | // Internal function: 518 | // 519 | // Will try to allocate a big block of memory inside the required range. 520 | //========================================================================= 521 | static MHOOKS_TRAMPOLINE* FindTrampolineInRange(PBYTE pLower, PBYTE pUpper) 522 | { 523 | if (!g_pFreeList) 524 | { 525 | return NULL; 526 | } 527 | 528 | // This is a standard free list, except we're doubly linked to deal with soem return shenanigans. 529 | MHOOKS_TRAMPOLINE* curEntry = g_pFreeList; 530 | while (curEntry) 531 | { 532 | if ((MHOOKS_TRAMPOLINE*) pLower < curEntry && curEntry < (MHOOKS_TRAMPOLINE*) pUpper) 533 | { 534 | ListRemove(&g_pFreeList, curEntry); 535 | 536 | return curEntry; 537 | } 538 | 539 | curEntry = curEntry->pNextTrampoline; 540 | } 541 | 542 | return NULL; 543 | } 544 | 545 | //========================================================================= 546 | // Internal function: 547 | // 548 | // Will try to allocate the trampoline structure within 2 gigabytes of 549 | // the target function. 550 | //========================================================================= 551 | static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) 552 | { 553 | 554 | MHOOKS_TRAMPOLINE* pTrampoline = NULL; 555 | 556 | // determine lower and upper bounds for the allocation locations. 557 | // in the basic scenario this is +/- 2GB but IP-relative instructions 558 | // found in the original code may require a smaller window. 559 | PBYTE pLower = pSystemFunction + nLimitUp; 560 | pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ? 561 | (PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000); 562 | PBYTE pUpper = pSystemFunction + nLimitDown; 563 | pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ? 564 | (PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000; 565 | ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper)); 566 | 567 | // try to find a trampoline in the specified range 568 | pTrampoline = FindTrampolineInRange(pLower, pUpper); 569 | if (!pTrampoline) 570 | { 571 | // if it we can't find it, then we need to allocate a new block and 572 | // try again. Just fail if that doesn't work 573 | g_pFreeList = BlockAlloc(pSystemFunction, pLower, pUpper); 574 | pTrampoline = FindTrampolineInRange(pLower, pUpper); 575 | } 576 | 577 | // found and allocated a trampoline? 578 | if (pTrampoline) 579 | { 580 | ListPrepend(&g_pHooks, pTrampoline); 581 | } 582 | 583 | return pTrampoline; 584 | } 585 | 586 | //========================================================================= 587 | // Internal function: 588 | // 589 | // Return the internal trampoline structure that belongs to a hooked function. 590 | //========================================================================= 591 | static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) 592 | { 593 | MHOOKS_TRAMPOLINE* pCurrent = g_pHooks; 594 | 595 | while (pCurrent) 596 | { 597 | if ((PBYTE)&(pCurrent->codeTrampoline) == pHookedFunction) 598 | { 599 | return pCurrent; 600 | } 601 | 602 | pCurrent = pCurrent->pNextTrampoline; 603 | } 604 | 605 | return NULL; 606 | } 607 | 608 | //========================================================================= 609 | // Internal function: 610 | // 611 | // Free a trampoline structure. 612 | //========================================================================= 613 | static VOID TrampolineFree(MHOOKS_TRAMPOLINE* pTrampoline, bool bNeverUsed) 614 | { 615 | ListRemove(&g_pHooks, pTrampoline); 616 | 617 | // If a thread could feasinbly have some of our trampoline code 618 | // on its stack and we yank the region from underneath it then it will 619 | // surely crash upon returning. So instead of freeing the 620 | // memory we just let it leak. Ugly, but safe. 621 | if (bNeverUsed) 622 | { 623 | ListPrepend(&g_pFreeList, pTrampoline); 624 | } 625 | } 626 | 627 | static bool VerifyThreadContext(PBYTE pIp, HOOK_CONTEXT* hookCtx, int hookCount) 628 | { 629 | for (int i = 0; i < hookCount; i++) 630 | { 631 | if (pIp >= (PBYTE)hookCtx[i].pSystemFunction && pIp < ((PBYTE)hookCtx[i].pSystemFunction + hookCtx[i].dwInstructionLength)) 632 | { 633 | return false; 634 | } 635 | } 636 | 637 | return true; 638 | } 639 | 640 | //========================================================================= 641 | // Internal function: 642 | // 643 | // Suspend a given thread and try to make sure that its instruction 644 | // pointer is not in the given range. 645 | //========================================================================= 646 | //========================================================================= 647 | static HANDLE SuspendOneThread(DWORD dwThreadId, HOOK_CONTEXT* hookCtx, int hookCount) 648 | { 649 | // open the thread 650 | HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, false, dwThreadId); 651 | 652 | if (GOOD_HANDLE(hThread)) 653 | { 654 | // attempt suspension 655 | DWORD dwSuspendCount = SuspendThread(hThread); 656 | if (dwSuspendCount != -1) 657 | { 658 | // see where the IP is 659 | CONTEXT ctx; 660 | ctx.ContextFlags = CONTEXT_CONTROL; 661 | int nTries = 0; 662 | while (GetThreadContext(hThread, &ctx)) 663 | { 664 | #ifdef _M_IX86 665 | PBYTE pIp = (PBYTE)(DWORD_PTR)ctx.Eip; 666 | #elif defined _M_X64 667 | PBYTE pIp = (PBYTE)(DWORD_PTR)ctx.Rip; 668 | #endif 669 | if (!VerifyThreadContext(pIp, hookCtx, hookCount)) 670 | { 671 | if (nTries < 3) 672 | { 673 | // oops - we should try to get the instruction pointer out of here. 674 | ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE", dwThreadId, pIp)); 675 | ResumeThread(hThread); 676 | Sleep(100); 677 | SuspendThread(hThread); 678 | nTries++; 679 | } 680 | else 681 | { 682 | // we gave it all we could. (this will probably never 683 | // happen - unless the thread has already been suspended 684 | // to begin with) 685 | ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE - CAN'T FIX", dwThreadId, pIp)); 686 | ResumeThread(hThread); 687 | CloseHandle(hThread); 688 | hThread = NULL; 689 | break; 690 | } 691 | } 692 | else 693 | { 694 | // success, the IP is not conflicting 695 | ODPRINTF((L"mhooks: SuspendOneThread: Successfully suspended thread %d - IP is at %p", dwThreadId, pIp)); 696 | break; 697 | } 698 | } 699 | } 700 | else 701 | { 702 | // couldn't suspend 703 | CloseHandle(hThread); 704 | hThread = NULL; 705 | } 706 | } 707 | 708 | return hThread; 709 | } 710 | 711 | //========================================================================= 712 | // Internal function: 713 | // 714 | // Free memory allocated for processes snapshot 715 | //========================================================================= 716 | static VOID CloseProcessSnapshot(VOID* snapshotContext) 717 | { 718 | free(snapshotContext); 719 | } 720 | 721 | //========================================================================= 722 | // Internal function: 723 | // 724 | // Resumes all previously suspended threads in the current process. 725 | //========================================================================= 726 | static VOID ResumeOtherThreads() 727 | { 728 | // make sure things go as fast as possible 729 | INT nOriginalPriority = GetThreadPriority(GetCurrentThread()); 730 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 731 | // go through our list 732 | for (DWORD i=0; iuUniqueProcessId == (HANDLE)processId) 795 | { 796 | break; 797 | } 798 | 799 | if (currentProcess->uNext == 0) 800 | { 801 | currentProcess = NULL; 802 | } 803 | else 804 | { 805 | currentProcess = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)currentProcess) + currentProcess->uNext); 806 | } 807 | } 808 | 809 | return currentProcess; 810 | } 811 | 812 | //========================================================================= 813 | // Internal function: 814 | // 815 | // Get current process snapshot and process info 816 | // 817 | //========================================================================= 818 | static bool GetCurrentProcessSnapshot(PVOID* snapshot, PSYSTEM_PROCESS_INFORMATION* procInfo) 819 | { 820 | // get a view of the threads in the system 821 | 822 | if (!CreateProcessSnapshot(snapshot)) 823 | { 824 | ODPRINTF((L"mhooks: can't get process snapshot!")); 825 | return false; 826 | } 827 | 828 | DWORD pid = GetCurrentProcessId(); 829 | 830 | *procInfo = FindProcess(*snapshot, pid); 831 | return true; 832 | } 833 | 834 | //========================================================================= 835 | // Internal function: 836 | // 837 | // Suspend all threads in this process while trying to make sure that their 838 | // instruction pointer is not in the given range. 839 | //========================================================================= 840 | static bool SuspendOtherThreads(HOOK_CONTEXT* hookCtx, int hookCount, PSYSTEM_PROCESS_INFORMATION procInfo) 841 | { 842 | bool bRet = false; 843 | // make sure we're the most important thread in the process 844 | INT nOriginalPriority = GetThreadPriority(GetCurrentThread()); 845 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 846 | 847 | DWORD pid = GetCurrentProcessId(); 848 | DWORD tid = GetCurrentThreadId(); 849 | 850 | // count threads in this process (except for ourselves) 851 | DWORD nThreadsInProcess = 0; 852 | 853 | if (procInfo->uThreadCount != 0) 854 | { 855 | nThreadsInProcess = procInfo->uThreadCount - 1; 856 | } 857 | 858 | ODPRINTF((L"mhooks: [%d:%d] SuspendOtherThreads: counted %d other threads", pid, tid, nThreadsInProcess)); 859 | 860 | if (nThreadsInProcess) 861 | { 862 | // alloc buffer for the handles we really suspended 863 | g_hThreadHandles = (HANDLE*)malloc(nThreadsInProcess * sizeof(HANDLE)); 864 | 865 | if (g_hThreadHandles) 866 | { 867 | ZeroMemory(g_hThreadHandles, nThreadsInProcess * sizeof(HANDLE)); 868 | DWORD nCurrentThread = 0; 869 | bool bFailed = false; 870 | 871 | // go through every thread 872 | for (ULONG threadIdx = 0; threadIdx < procInfo->uThreadCount; threadIdx++) 873 | { 874 | DWORD threadId = static_cast(reinterpret_cast(procInfo->Threads[threadIdx].ClientId.UniqueThread)); 875 | 876 | if (threadId != tid) 877 | { 878 | // attempt to suspend it 879 | g_hThreadHandles[nCurrentThread] = SuspendOneThread(threadId, hookCtx, hookCount); 880 | 881 | if (GOOD_HANDLE(g_hThreadHandles[nCurrentThread])) 882 | { 883 | ODPRINTF((L"mhooks: [%d:%d] SuspendOtherThreads: successfully suspended %d", pid, tid, threadId)); 884 | nCurrentThread++; 885 | } 886 | else 887 | { 888 | ODPRINTF((L"mhooks: [%d:%d] SuspendOtherThreads: error while suspending thread %d: %d", pid, tid, threadId, gle())); 889 | // TODO: this might not be the wisest choice 890 | // but we can choose to ignore failures on 891 | // thread suspension. It's pretty unlikely that 892 | // we'll fail - and even if we do, the chances 893 | // of a thread's IP being in the wrong place 894 | // is pretty small. 895 | // bFailed = true; 896 | } 897 | } 898 | } 899 | 900 | g_nThreadHandles = nCurrentThread; 901 | bRet = !bFailed; 902 | } 903 | } 904 | 905 | //TODO: we might want to have another pass to make sure all threads 906 | // in the current process (including those that might have been 907 | // created since we took the original snapshot) have been 908 | // suspended. 909 | 910 | if (!bRet && nThreadsInProcess != 0) 911 | { 912 | ODPRINTF((L"mhooks: [%d:%d] SuspendOtherThreads: Had a problem (or not running multithreaded), resuming all threads.", pid, tid)); 913 | ResumeOtherThreads(); 914 | } 915 | 916 | SetThreadPriority(GetCurrentThread(), nOriginalPriority); 917 | 918 | return bRet; 919 | } 920 | 921 | //========================================================================= 922 | // if IP-relative addressing has been detected, fix up the code so the 923 | // offset points to the original location 924 | static void FixupIPRelativeAddressing(PBYTE pbNew, PBYTE pbOriginal, MHOOKS_PATCHDATA* pdata) 925 | { 926 | #if defined _M_X64 927 | S64 diff = pbNew - pbOriginal; 928 | for (DWORD i = 0; i < pdata->nRipCnt; i++) 929 | { 930 | DWORD dwNewDisplacement = (DWORD)(pdata->rips[i].nDisplacement - diff); 931 | ODPRINTF((L"mhooks: fixing up RIP instruction operand for code at 0x%p: " 932 | L"old displacement: 0x%8.8x, new displacement: 0x%8.8x", 933 | pbNew + pdata->rips[i].dwOffset, 934 | (DWORD)pdata->rips[i].nDisplacement, 935 | dwNewDisplacement)); 936 | *(PDWORD)(pbNew + pdata->rips[i].dwOffset) = dwNewDisplacement; 937 | } 938 | #endif 939 | } 940 | 941 | //========================================================================= 942 | // Examine the machine code at the target function's entry point, and 943 | // skip bytes in a way that we'll always end on an instruction boundary. 944 | // We also detect branches and subroutine calls (as well as returns) 945 | // at which point disassembly must stop. 946 | // Finally, detect and collect information on IP-relative instructions 947 | // that we can patch. 948 | static DWORD DisassembleAndSkip(PVOID pFunction, DWORD dwMinLen, MHOOKS_PATCHDATA* pdata) 949 | { 950 | DWORD dwRet = 0; 951 | pdata->nLimitDown = 0; 952 | pdata->nLimitUp = 0; 953 | pdata->nRipCnt = 0; 954 | #ifdef _M_IX86 955 | ARCHITECTURE_TYPE arch = ARCH_X86; 956 | #elif defined _M_X64 957 | ARCHITECTURE_TYPE arch = ARCH_X64; 958 | #else 959 | #error unsupported platform 960 | #endif 961 | DISASSEMBLER dis; 962 | if (InitDisassembler(&dis, arch)) 963 | { 964 | INSTRUCTION* pins = NULL; 965 | U8* pLoc = (U8*)pFunction; 966 | DWORD dwFlags = DISASM_DECODE | DISASM_DISASSEMBLE | DISASM_ALIGNOUTPUT; 967 | 968 | ODPRINTF((L"mhooks: DisassembleAndSkip: Disassembling %p", pLoc)); 969 | while ( (dwRet < dwMinLen) && (pins = GetInstruction(&dis, (ULONG_PTR)pLoc, pLoc, dwFlags)) ) 970 | { 971 | ODPRINTF((L"mhooks: DisassembleAndSkip: %p:(0x%2.2x) %s", pLoc, pins->Length, pins->String)); 972 | if (pins->Type == ITYPE_RET ) break; 973 | if (pins->Type == ITYPE_BRANCHCC) break; 974 | if (pins->Type == ITYPE_CALLCC) break; 975 | 976 | #if defined _M_X64 977 | bool bProcessRip = false; 978 | // jmp to rip+imm32 979 | if ((pins->Type == ITYPE_BRANCH) && (pins->OperandCount == 1) && (pins->X86.Relative) && (pins->X86.BaseRegister == AMD64_REG_RIP) && (pins->Operands[0].Flags & OP_IPREL)) 980 | { 981 | // rip-addressing "jmp [rip+imm32]" 982 | ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc + 3))); 983 | bProcessRip = true; 984 | } 985 | 986 | // mov or lea to register from rip+imm32 987 | else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && 988 | (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && 989 | (pins->Operands[1].Flags & OP_IPREL) && (pins->Operands[1].Register == AMD64_REG_RIP)) 990 | { 991 | // rip-addressing "mov reg, [rip+imm32]" 992 | ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc+3))); 993 | bProcessRip = true; 994 | } 995 | // mov or lea to rip+imm32 from register 996 | else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && 997 | (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && 998 | (pins->Operands[0].Flags & OP_IPREL) && (pins->Operands[0].Register == AMD64_REG_RIP)) 999 | { 1000 | // rip-addressing "mov [rip+imm32], reg" 1001 | ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 0, pins->X86.Displacement, *(PDWORD)(pLoc+3))); 1002 | bProcessRip = true; 1003 | } 1004 | else if ( (pins->OperandCount >= 1) && (pins->Operands[0].Flags & OP_IPREL) ) 1005 | { 1006 | // unsupported rip-addressing 1007 | ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 0)); 1008 | // dump instruction bytes to the debug output 1009 | for (DWORD i=0; iLength; i++) 1010 | { 1011 | ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); 1012 | } 1013 | break; 1014 | } 1015 | else if ( (pins->OperandCount >= 2) && (pins->Operands[1].Flags & OP_IPREL) ) 1016 | { 1017 | // unsupported rip-addressing 1018 | ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 1)); 1019 | // dump instruction bytes to the debug output 1020 | for (DWORD i=0; iLength; i++) 1021 | { 1022 | ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); 1023 | } 1024 | break; 1025 | } 1026 | else if ( (pins->OperandCount >= 3) && (pins->Operands[2].Flags & OP_IPREL) ) 1027 | { 1028 | // unsupported rip-addressing 1029 | ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 2)); 1030 | // dump instruction bytes to the debug output 1031 | for (DWORD i=0; iLength; i++) 1032 | { 1033 | ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); 1034 | } 1035 | break; 1036 | } 1037 | // follow through with RIP-processing if needed 1038 | if (bProcessRip) 1039 | { 1040 | // calculate displacement relative to function start 1041 | S64 nAdjustedDisplacement = pins->X86.Displacement + (pLoc - (U8*)pFunction); 1042 | // store displacement values furthest from zero (both positive and negative) 1043 | if (nAdjustedDisplacement < pdata->nLimitDown) 1044 | pdata->nLimitDown = nAdjustedDisplacement; 1045 | if (nAdjustedDisplacement > pdata->nLimitUp) 1046 | pdata->nLimitUp = nAdjustedDisplacement; 1047 | // store patch info 1048 | if (pdata->nRipCnt < MHOOKS_MAX_RIPS) 1049 | { 1050 | pdata->rips[pdata->nRipCnt].dwOffset = dwRet + 3; 1051 | pdata->rips[pdata->nRipCnt].nDisplacement = pins->X86.Displacement; 1052 | pdata->nRipCnt++; 1053 | } 1054 | else 1055 | { 1056 | // no room for patch info, stop disassembly 1057 | break; 1058 | } 1059 | } 1060 | #endif 1061 | 1062 | dwRet += pins->Length; 1063 | pLoc += pins->Length; 1064 | } 1065 | 1066 | CloseDisassembler(&dis); 1067 | } 1068 | 1069 | return dwRet; 1070 | } 1071 | 1072 | static bool IsInstructionPresentInFirstFiveByte(PVOID pFunction, INSTRUCTION_TYPE type) 1073 | { 1074 | DWORD dwRet = 0; 1075 | 1076 | #ifdef _M_IX86 1077 | ARCHITECTURE_TYPE arch = ARCH_X86; 1078 | #elif defined _M_X64 1079 | ARCHITECTURE_TYPE arch = ARCH_X64; 1080 | #else 1081 | #error unsupported platform 1082 | #endif 1083 | DISASSEMBLER dis; 1084 | if (InitDisassembler(&dis, arch)) 1085 | { 1086 | INSTRUCTION* pins = NULL; 1087 | U8* pLoc = (U8*)pFunction; 1088 | DWORD dwFlags = DISASM_DECODE | DISASM_DISASSEMBLE | DISASM_ALIGNOUTPUT; 1089 | 1090 | while ((dwRet < MHOOK_JMPSIZE) && (pins = GetInstruction(&dis, (ULONG_PTR)pLoc, pLoc, dwFlags))) 1091 | { 1092 | if (pins->Type == type) 1093 | { 1094 | return true; 1095 | } 1096 | 1097 | dwRet += pins->Length; 1098 | pLoc += pins->Length; 1099 | } 1100 | 1101 | CloseDisassembler(&dis); 1102 | } 1103 | 1104 | return false; 1105 | } 1106 | 1107 | static PBYTE PatchRelative(PBYTE pCodeTrampoline, PVOID pSystemFunction) 1108 | { 1109 | DWORD dwRet = 0; 1110 | 1111 | #ifdef _M_IX86 1112 | ARCHITECTURE_TYPE arch = ARCH_X86; 1113 | #elif defined _M_X64 1114 | ARCHITECTURE_TYPE arch = ARCH_X64; 1115 | #else 1116 | #error unsupported platform 1117 | #endif 1118 | DISASSEMBLER dis; 1119 | if (InitDisassembler(&dis, arch)) 1120 | { 1121 | INSTRUCTION* pins = NULL; 1122 | U8* pLoc = (U8*)pCodeTrampoline; 1123 | DWORD dwFlags = DISASM_DECODE | DISASM_DISASSEMBLE | DISASM_ALIGNOUTPUT; 1124 | 1125 | while ((dwRet < MHOOK_JMPSIZE) && (pins = GetInstruction(&dis, (ULONG_PTR)pLoc, pLoc, dwFlags))) 1126 | { 1127 | if (pins->Type == ITYPE_BRANCHCC) 1128 | { 1129 | // we will patch only near jump je/jz for now 1130 | if (pins->OpcodeLength == 1 && (pins->OpcodeBytes[0] == 0x74 || pins->OpcodeBytes[0] == 0x75)) 1131 | { 1132 | // save old offset from current position to jump destination 1133 | U8 oldOffset = *(pLoc + pins->OpcodeLength); 1134 | // write je opcode with rel32 address in codeTrampoline block 1135 | *pLoc = 0x0f; 1136 | *(pLoc + pins->OpcodeLength) = pins->OpcodeBytes[0] + 0x10; 1137 | 1138 | // Calculating offset from codeTrampoline to jump label in original function 1139 | 1140 | // get address of original jump destination 1141 | ULONG_PTR jumpDestinationAddress = reinterpret_cast(pSystemFunction); 1142 | // oldOffset is from the pLoc + pins->OpcodeLength address, so add it 1143 | jumpDestinationAddress += oldOffset + pins->OpcodeLength; 1144 | 1145 | // current address is from the pLoc + 2 (je rel32 opcode is 2-bytes length), so add it 1146 | const DWORD kJERel32OpcodeLength = 2; 1147 | ULONG_PTR currentAddress = reinterpret_cast(pLoc + kJERel32OpcodeLength); 1148 | 1149 | // take the offset that we should add to current address to reach original jump destination 1150 | LONG newOffset = static_cast(jumpDestinationAddress - currentAddress); 1151 | assert(currentAddress + newOffset == jumpDestinationAddress); 1152 | 1153 | memcpy(pLoc + kJERel32OpcodeLength, &newOffset, sizeof(newOffset)); 1154 | 1155 | return pLoc + kJERel32OpcodeLength + sizeof(newOffset); 1156 | } 1157 | } 1158 | 1159 | if (pins->Type == ITYPE_CALL) 1160 | { 1161 | // we will patch CALL relative32 1162 | if (pins->OpcodeLength == 1 && pins->OpcodeBytes[0] == 0xE8) 1163 | { 1164 | // call rel32 address is relative to the next instruction start address 1165 | // reinterpret_cast(pSystemFunction) is the original function address 1166 | // (pLoc - pCodeTrampoline) for current offset of call from start of the function, 1167 | // pins->Length - full legth of instruction and operand address 1168 | ULONG_PTR oldStartAddress = (pLoc - pCodeTrampoline) + reinterpret_cast(pSystemFunction)+pins->Length; 1169 | // offset from the next instruction address 1170 | LONG oldOffset = *(reinterpret_cast(pins->Operands[0].BCD)); 1171 | // target function address 1172 | ULONG_PTR destination = oldStartAddress + oldOffset; 1173 | 1174 | // now calculate new start address and new offset 1175 | ULONG_PTR newStartAddress = reinterpret_cast(pins->Address) + pins->Length; 1176 | LONG newOffset = static_cast(destination - newStartAddress); 1177 | 1178 | // save new offset to the trampoline code 1179 | *reinterpret_cast(pLoc + pins->OpcodeLength) = newOffset; 1180 | 1181 | return pLoc + pins->OpcodeLength + sizeof(newOffset); 1182 | } 1183 | } 1184 | 1185 | dwRet += pins->Length; 1186 | pLoc += pins->Length; 1187 | } 1188 | 1189 | CloseDisassembler(&dis); 1190 | } 1191 | 1192 | return pCodeTrampoline; 1193 | } 1194 | 1195 | static bool FindSystemFunction(HOOK_CONTEXT* hookCtx, int fromIdx, int toIdx, PVOID pSystemFunction) 1196 | { 1197 | for (int idx = fromIdx; idx < toIdx; idx++) 1198 | { 1199 | if (hookCtx[idx].pSystemFunction == pSystemFunction) 1200 | { 1201 | return true; 1202 | } 1203 | } 1204 | 1205 | return false; 1206 | } 1207 | 1208 | //========================================================================= 1209 | int Mhook_SetHookEx(HOOK_INFO* hooks, int hookCount) 1210 | { 1211 | int hooksSet = 0; 1212 | 1213 | HOOK_CONTEXT* hookCtx = (HOOK_CONTEXT*)malloc(hookCount * sizeof(HOOK_CONTEXT)); 1214 | 1215 | if (hookCtx == NULL) 1216 | { 1217 | // return error status 1218 | ODPRINTF((L"mhooks: can't allocate buffer!")); 1219 | 1220 | return hooksSet; 1221 | } 1222 | 1223 | EnterCritSec(); 1224 | 1225 | for (int idx = 0; idx < hookCount; idx++) 1226 | { 1227 | hookCtx[idx].pSystemFunction = *hooks[idx].ppSystemFunction; 1228 | hookCtx[idx].pHookFunction = hooks[idx].pHookFunction; 1229 | hookCtx[idx].pTrampoline = NULL; 1230 | hookCtx[idx].dwInstructionLength = 0; 1231 | memset(&hookCtx[idx].patchdata, 0, sizeof(MHOOKS_PATCHDATA)); 1232 | hookCtx[idx].needPatchJump = false; 1233 | hookCtx[idx].needPatchCall = false; 1234 | 1235 | ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", hookCtx[idx].pSystemFunction, hookCtx[idx].pHookFunction)); 1236 | 1237 | // find the real functions (jump over jump tables, if any) 1238 | hookCtx[idx].pSystemFunction = SkipJumps((PBYTE)hookCtx[idx].pSystemFunction); 1239 | hookCtx[idx].pHookFunction = SkipJumps((PBYTE)hookCtx[idx].pHookFunction); 1240 | 1241 | if (FindSystemFunction(hookCtx, 0, idx, hookCtx[idx].pSystemFunction)) 1242 | { 1243 | // Same system function found. Skip it. 1244 | 1245 | // It is not an error. 1246 | // This case is possible when two system functions from different DLLs are stubs and they are redirected to the one internal implementation. 1247 | // We are going to hook first instance and skip other. 1248 | 1249 | ODPRINTF((L"mhooks: Mhook_SetHook: already hooked: %p", hookCtx[idx].pSystemFunction)); 1250 | 1251 | hookCtx[idx].pTrampoline = NULL; 1252 | } 1253 | else 1254 | { 1255 | ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", hookCtx[idx].pSystemFunction, hookCtx[idx].pHookFunction)); 1256 | 1257 | // figure out the length of the overwrite zone 1258 | hookCtx[idx].dwInstructionLength = DisassembleAndSkip(hookCtx[idx].pSystemFunction, MHOOK_JMPSIZE, &hookCtx[idx].patchdata); 1259 | 1260 | hookCtx[idx].needPatchJump = IsInstructionPresentInFirstFiveByte(hookCtx[idx].pSystemFunction, ITYPE_BRANCHCC); 1261 | hookCtx[idx].needPatchCall = IsInstructionPresentInFirstFiveByte(hookCtx[idx].pSystemFunction, ITYPE_CALL); 1262 | 1263 | if (hookCtx[idx].dwInstructionLength >= MHOOK_JMPSIZE && !(hookCtx[idx].needPatchJump && hookCtx[idx].needPatchCall)) 1264 | { 1265 | ODPRINTF((L"mhooks: Mhook_SetHook: disassembly signals %d bytes", hookCtx[idx].dwInstructionLength)); 1266 | 1267 | // allocate a trampoline structure (TODO: it is pretty wasteful to get 1268 | // VirtualAlloc to grab chunks of memory smaller than 100 bytes) 1269 | hookCtx[idx].pTrampoline = TrampolineAlloc((PBYTE)hookCtx[idx].pSystemFunction, hookCtx[idx].patchdata.nLimitUp, hookCtx[idx].patchdata.nLimitDown); 1270 | } 1271 | else 1272 | { 1273 | // error - skip hook 1274 | ODPRINTF((L"mhooks: error! disassembly signals %d bytes (unacceptable)", hookCtx[idx].dwInstructionLength)); 1275 | } 1276 | } 1277 | } 1278 | 1279 | VOID* procEnumerationCtx = NULL; 1280 | PSYSTEM_PROCESS_INFORMATION procInfo = NULL; 1281 | 1282 | if (GetCurrentProcessSnapshot(&procEnumerationCtx, &procInfo)) 1283 | { 1284 | // suspend threads 1285 | SuspendOtherThreads(hookCtx, hookCount, procInfo); 1286 | 1287 | // returns pseudo-handle, no need to CloseHandle() for it 1288 | HANDLE currentProcessHandle = GetCurrentProcess(); 1289 | 1290 | // the next code is same to the Mhook_SetHook. Differences are only in using hookCtx[i] 1291 | for (int i = 0; i < hookCount; i++) 1292 | { 1293 | if (hookCtx[i].pTrampoline) 1294 | { 1295 | ODPRINTF((L"mhooks: Mhook_SetHook: allocated structure at %p", hookCtx[i].pTrampoline)); 1296 | DWORD dwOldProtectSystemFunction = 0; 1297 | DWORD dwOldProtectTrampolineFunction = 0; 1298 | 1299 | // set the system function to PAGE_EXECUTE_READWRITE 1300 | if (VirtualProtect(hookCtx[i].pSystemFunction, hookCtx[i].dwInstructionLength, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) 1301 | { 1302 | ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on system function")); 1303 | 1304 | // mark our trampoline buffer to PAGE_EXECUTE_READWRITE 1305 | if (VirtualProtect(hookCtx[i].pTrampoline, sizeof(MHOOKS_TRAMPOLINE), PAGE_EXECUTE_READWRITE, &dwOldProtectTrampolineFunction)) 1306 | { 1307 | ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on trampoline structure")); 1308 | 1309 | // create our trampoline function 1310 | PBYTE pbCode = hookCtx[i].pTrampoline->codeTrampoline; 1311 | 1312 | // save original code.. 1313 | for (DWORD k = 0; k < hookCtx[i].dwInstructionLength; k++) 1314 | { 1315 | hookCtx[i].pTrampoline->codeUntouched[k] = pbCode[k] = ((PBYTE)hookCtx[i].pSystemFunction)[k]; 1316 | } 1317 | 1318 | if (hookCtx[i].needPatchJump || hookCtx[i].needPatchCall) 1319 | { 1320 | pbCode = PatchRelative(pbCode, hookCtx[i].pSystemFunction); 1321 | } 1322 | else 1323 | { 1324 | pbCode += hookCtx[i].dwInstructionLength; 1325 | } 1326 | 1327 | // plus a jump to the continuation in the original location 1328 | pbCode = EmitJump(pbCode, ((PBYTE)hookCtx[i].pSystemFunction) + hookCtx[i].dwInstructionLength); 1329 | ODPRINTF((L"mhooks: Mhook_SetHook: updated the trampoline")); 1330 | 1331 | // fix up any IP-relative addressing in the code 1332 | FixupIPRelativeAddressing(hookCtx[i].pTrampoline->codeTrampoline, (PBYTE)hookCtx[i].pSystemFunction, &hookCtx[i].patchdata); 1333 | 1334 | DWORD_PTR dwDistance = (PBYTE)hookCtx[i].pHookFunction < (PBYTE)hookCtx[i].pSystemFunction ? 1335 | (PBYTE)hookCtx[i].pSystemFunction - (PBYTE)hookCtx[i].pHookFunction : (PBYTE)hookCtx[i].pHookFunction - (PBYTE)hookCtx[i].pSystemFunction; 1336 | 1337 | if (dwDistance > 0x7fff0000) 1338 | { 1339 | // create a stub that jumps to the replacement function. 1340 | // we need this because jumping from the API to the hook directly 1341 | // will be a long jump, which is 14 bytes on x64, and we want to 1342 | // avoid that - the API may or may not have room for such stuff. 1343 | // (remember, we only have 5 bytes guaranteed in the API.) 1344 | // on the other hand we do have room, and the trampoline will always be 1345 | // within +/- 2GB of the API, so we do the long jump in there. 1346 | // the API will jump to the "reverse trampoline" which 1347 | // will jump to the user's hook code. 1348 | pbCode = hookCtx[i].pTrampoline->codeJumpToHookFunction; 1349 | pbCode = EmitJump(pbCode, (PBYTE)hookCtx[i].pHookFunction); 1350 | ODPRINTF((L"mhooks: Mhook_SetHook: created reverse trampoline")); 1351 | FlushInstructionCache(GetCurrentProcess(), hookCtx[i].pTrampoline->codeJumpToHookFunction, 1352 | pbCode - hookCtx[i].pTrampoline->codeJumpToHookFunction); 1353 | 1354 | // update the API itself 1355 | pbCode = (PBYTE)hookCtx[i].pSystemFunction; 1356 | pbCode = EmitJump(pbCode, hookCtx[i].pTrampoline->codeJumpToHookFunction); 1357 | } 1358 | else 1359 | { 1360 | // the jump will be at most 5 bytes so we can do it directly 1361 | // update the API itself 1362 | pbCode = (PBYTE)hookCtx[i].pSystemFunction; 1363 | pbCode = EmitJump(pbCode, (PBYTE)hookCtx[i].pHookFunction); 1364 | } 1365 | 1366 | // update data members 1367 | hookCtx[i].pTrampoline->cbOverwrittenCode = hookCtx[i].dwInstructionLength; 1368 | hookCtx[i].pTrampoline->pSystemFunction = (PBYTE)hookCtx[i].pSystemFunction; 1369 | hookCtx[i].pTrampoline->pHookFunction = (PBYTE)hookCtx[i].pHookFunction; 1370 | 1371 | // update pointer here for ability to hook system functions follows 1372 | if (hookCtx[i].pTrampoline->pSystemFunction) 1373 | { 1374 | // this is what the application will use as the entry point 1375 | // to the "original" unhooked function. 1376 | *hooks[i].ppSystemFunction = hookCtx[i].pTrampoline->codeTrampoline; 1377 | } 1378 | 1379 | // flush instruction cache and restore original protection 1380 | FlushInstructionCache(currentProcessHandle, hookCtx[i].pTrampoline->codeTrampoline, hookCtx[i].dwInstructionLength); 1381 | VirtualProtect(hookCtx[i].pTrampoline, sizeof(MHOOKS_TRAMPOLINE), dwOldProtectTrampolineFunction, &dwOldProtectTrampolineFunction); 1382 | } 1383 | else 1384 | { 1385 | ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtect 2: %d", gle())); 1386 | } 1387 | 1388 | // flush instruction cache and restore original protection 1389 | FlushInstructionCache(currentProcessHandle, hookCtx[i].pSystemFunction, hookCtx[i].dwInstructionLength); 1390 | VirtualProtect(hookCtx[i].pSystemFunction, hookCtx[i].dwInstructionLength, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); 1391 | } 1392 | else 1393 | { 1394 | ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtect 1: %d", gle())); 1395 | } 1396 | 1397 | if (hookCtx[i].pTrampoline->pSystemFunction) 1398 | { 1399 | hooksSet++; 1400 | // setting the entry point is moved upper for ability to hook some internal system functions 1401 | ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!")); 1402 | } 1403 | else 1404 | { 1405 | // if we failed discard the trampoline (forcing VirtualFree) 1406 | TrampolineFree(hookCtx[i].pTrampoline, true); 1407 | hookCtx[i].pTrampoline = NULL; 1408 | } 1409 | } 1410 | } 1411 | 1412 | // resume threads 1413 | ResumeOtherThreads(); 1414 | 1415 | CloseProcessSnapshot(procEnumerationCtx); 1416 | } 1417 | 1418 | free(hookCtx); 1419 | 1420 | LeaveCritSec(); 1421 | 1422 | return hooksSet; 1423 | } 1424 | 1425 | //========================================================================= 1426 | BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction) 1427 | { 1428 | HOOK_INFO hook = { ppSystemFunction, pHookFunction }; 1429 | return Mhook_SetHookEx(&hook, 1) == 1; 1430 | } 1431 | 1432 | //========================================================================= 1433 | int Mhook_UnhookEx(PVOID** hooks, int hookCount) 1434 | { 1435 | ODPRINTF((L"mhooks: Mhook_UnhookEx: %d hooks to unhook", hookCount)); 1436 | int result = 0; 1437 | 1438 | HOOK_CONTEXT* hookCtx = (HOOK_CONTEXT*)malloc(hookCount * sizeof(HOOK_CONTEXT)); 1439 | if (hookCtx == NULL) 1440 | { 1441 | // return error status 1442 | ODPRINTF((L"mhooks: Mhook_UnhookEx: can't allocate buffer!")); 1443 | 1444 | return result; 1445 | } 1446 | 1447 | EnterCritSec(); 1448 | 1449 | for (int idx = 0; idx < hookCount; idx++) 1450 | { 1451 | hookCtx[idx].pSystemFunction = *hooks[idx]; 1452 | // get the trampoline structure that corresponds to our function 1453 | hookCtx[idx].pTrampoline = TrampolineGet((PBYTE)hookCtx[idx].pSystemFunction); 1454 | 1455 | if (!hookCtx[idx].pTrampoline) 1456 | { 1457 | continue; 1458 | } 1459 | 1460 | ODPRINTF((L"mhooks: Mhook_UnhookEx: found struct at %p", hookCtx[idx].pTrampoline)); 1461 | 1462 | hookCtx[idx].dwInstructionLength = hookCtx[idx].pTrampoline->cbOverwrittenCode; 1463 | } 1464 | 1465 | VOID* procEnumerationCtx = NULL; 1466 | PSYSTEM_PROCESS_INFORMATION procInfo = NULL; 1467 | 1468 | if (GetCurrentProcessSnapshot(&procEnumerationCtx, &procInfo)) 1469 | { 1470 | // make sure nobody's executing code where we're about to overwrite a few bytes 1471 | SuspendOtherThreads(hookCtx, hookCount, procInfo); 1472 | 1473 | for (int idx = 0; idx < hookCount; idx++) 1474 | { 1475 | if (!hookCtx[idx].pTrampoline) 1476 | { 1477 | continue; 1478 | } 1479 | 1480 | DWORD dwOldProtectSystemFunction = 0; 1481 | // make memory writable 1482 | if (VirtualProtect(hookCtx[idx].pTrampoline->pSystemFunction, hookCtx[idx].pTrampoline->cbOverwrittenCode, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) 1483 | { 1484 | ODPRINTF((L"mhooks: Mhook_UnhookEx: readwrite set on system function")); 1485 | PBYTE pbCode = (PBYTE)hookCtx[idx].pTrampoline->pSystemFunction; 1486 | for (DWORD i = 0; i < hookCtx[idx].pTrampoline->cbOverwrittenCode; i++) 1487 | { 1488 | pbCode[i] = hookCtx[idx].pTrampoline->codeUntouched[i]; 1489 | } 1490 | 1491 | // flush instruction cache and make memory unwritable 1492 | FlushInstructionCache(GetCurrentProcess(), hookCtx[idx].pTrampoline->pSystemFunction, hookCtx[idx].pTrampoline->cbOverwrittenCode); 1493 | VirtualProtect(hookCtx[idx].pTrampoline->pSystemFunction, hookCtx[idx].pTrampoline->cbOverwrittenCode, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); 1494 | 1495 | // return the original function pointer 1496 | *hooks[idx] = hookCtx[idx].pTrampoline->pSystemFunction; 1497 | result += 1; 1498 | 1499 | ODPRINTF((L"mhooks: Mhook_UnhookEx: sysfunc: %p", *hooks[idx])); 1500 | 1501 | // free the trampoline while not really discarding it from memory 1502 | TrampolineFree(hookCtx[idx].pTrampoline, false); 1503 | ODPRINTF((L"mhooks: Mhook_UnhookEx: unhook successful")); 1504 | } 1505 | else 1506 | { 1507 | ODPRINTF((L"mhooks: Mhook_UnhookEx: failed VirtualProtect 1: %d", gle())); 1508 | } 1509 | } 1510 | 1511 | // make the other guys runnable 1512 | ResumeOtherThreads(); 1513 | 1514 | CloseProcessSnapshot(procEnumerationCtx); 1515 | } 1516 | 1517 | free(hookCtx); 1518 | 1519 | LeaveCritSec(); 1520 | 1521 | return result; 1522 | } 1523 | 1524 | //========================================================================= 1525 | BOOL Mhook_Unhook(PVOID *ppHookedFunction) 1526 | { 1527 | return Mhook_UnhookEx(&ppHookedFunction, 1) == 1; 1528 | } 1529 | -------------------------------------------------------------------------------- /mhook/mhook-lib/mhook.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2007-2008, Marton Anka 2 | // 3 | //Permission is hereby granted, free of charge, to any person obtaining a 4 | //copy of this software and associated documentation files (the "Software"), 5 | //to deal in the Software without restriction, including without limitation 6 | //the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | //and/or sell copies of the Software, and to permit persons to whom the 8 | //Software is furnished to do so, subject to the following conditions: 9 | // 10 | //The above copyright notice and this permission notice shall be included 11 | //in all copies or substantial portions of the Software. 12 | // 13 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | //OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | //THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | //FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | //IN THE SOFTWARE. 20 | 21 | #ifdef _M_IX86 22 | #define _M_IX86_X64 23 | #elif defined _M_X64 24 | #define _M_IX86_X64 25 | #endif 26 | 27 | struct HOOK_INFO 28 | { 29 | PVOID *ppSystemFunction; // pointer to pointer to function to be hooked 30 | PVOID pHookFunction; // hook function 31 | }; 32 | 33 | // returns number of successfully set hooks 34 | int Mhook_SetHookEx(struct HOOK_INFO* hooks, int hookCount); 35 | BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction); 36 | int Mhook_UnhookEx(PVOID** hooks, int hookCount); 37 | BOOL Mhook_Unhook(PVOID *ppHookedFunction); 38 | -------------------------------------------------------------------------------- /mhook/mhook-test/mhook-test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2007-2008, Marton Anka 2 | // 3 | //Permission is hereby granted, free of charge, to any person obtaining a 4 | //copy of this software and associated documentation files (the "Software"), 5 | //to deal in the Software without restriction, including without limitation 6 | //the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | //and/or sell copies of the Software, and to permit persons to whom the 8 | //Software is furnished to do so, subject to the following conditions: 9 | // 10 | //The above copyright notice and this permission notice shall be included 11 | //in all copies or substantial portions of the Software. 12 | // 13 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | //OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | //THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | //FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | //IN THE SOFTWARE. 20 | 21 | #include "stdafx.h" 22 | #include "mhook-lib/mhook.h" 23 | 24 | //========================================================================= 25 | // Define _NtOpenProcess so we can dynamically bind to the function 26 | // 27 | typedef struct _CLIENT_ID 28 | { 29 | DWORD_PTR UniqueProcess; 30 | DWORD_PTR UniqueThread; 31 | } CLIENT_ID, *PCLIENT_ID; 32 | 33 | typedef ULONG (WINAPI* _NtOpenProcess)(OUT PHANDLE ProcessHandle, 34 | IN ACCESS_MASK AccessMask, IN PVOID ObjectAttributes, 35 | IN PCLIENT_ID ClientId ); 36 | 37 | //========================================================================= 38 | // Define _SelectObject so we can dynamically bind to the function 39 | typedef HGDIOBJ (WINAPI* _SelectObject)(HDC hdc, HGDIOBJ hgdiobj); 40 | 41 | //========================================================================= 42 | // Define _getaddrinfo so we can dynamically bind to the function 43 | typedef int (WSAAPI* _getaddrinfo)(const char* nodename, const char* servname, const struct addrinfo* hints, struct addrinfo** res); 44 | 45 | //========================================================================= 46 | // Define _HeapAlloc so we can dynamically bind to the function 47 | typedef LPVOID (WINAPI *_HeapAlloc)(HANDLE, DWORD, SIZE_T); 48 | 49 | //========================================================================= 50 | // Define _NtClose so we can dynamically bind to the function 51 | typedef ULONG (WINAPI* _NtClose)(IN HANDLE Handle); 52 | 53 | //========================================================================= 54 | // Get the current (original) address to the functions to be hooked 55 | // 56 | _NtOpenProcess TrueNtOpenProcess = (_NtOpenProcess) 57 | GetProcAddress(GetModuleHandle(L"ntdll"), "NtOpenProcess"); 58 | 59 | _SelectObject TrueSelectObject = (_SelectObject) 60 | GetProcAddress(GetModuleHandle(L"gdi32"), "SelectObject"); 61 | 62 | _getaddrinfo Truegetaddrinfo = (_getaddrinfo)GetProcAddress(GetModuleHandle(L"ws2_32"), "getaddrinfo"); 63 | 64 | _HeapAlloc TrueHeapAlloc = (_HeapAlloc)GetProcAddress(GetModuleHandle(L"kernel32"), "HeapAlloc"); 65 | 66 | _NtClose TrueNtClose = (_NtClose)GetProcAddress(GetModuleHandle(L"ntdll"), "NtClose"); 67 | 68 | //========================================================================= 69 | // This is the function that will replace NtOpenProcess once the hook 70 | // is in place 71 | // 72 | ULONG WINAPI HookNtOpenProcess(OUT PHANDLE ProcessHandle, 73 | IN ACCESS_MASK AccessMask, 74 | IN PVOID ObjectAttributes, 75 | IN PCLIENT_ID ClientId) 76 | { 77 | printf("***** Call to open process %Id\n", ClientId->UniqueProcess); 78 | return TrueNtOpenProcess(ProcessHandle, AccessMask, 79 | ObjectAttributes, ClientId); 80 | } 81 | 82 | //========================================================================= 83 | // This is the function that will replace SelectObject once the hook 84 | // is in place 85 | // 86 | HGDIOBJ WINAPI HookSelectobject(HDC hdc, HGDIOBJ hgdiobj) 87 | { 88 | printf("***** Call to SelectObject(0x%p, 0x%p)\n", hdc, hgdiobj); 89 | return TrueSelectObject(hdc, hgdiobj); 90 | } 91 | 92 | //========================================================================= 93 | // This is the function that will replace SelectObject once the hook 94 | // is in place 95 | // 96 | int WSAAPI Hookgetaddrinfo(const char* nodename, const char* servname, const struct addrinfo* hints, struct addrinfo** res) 97 | { 98 | printf("***** Call to getaddrinfo(0x%p, 0x%p, 0x%p, 0x%p)\n", nodename, servname, hints, res); 99 | return Truegetaddrinfo(nodename, servname, hints, res); 100 | } 101 | 102 | //========================================================================= 103 | // This is the function that will replace HeapAlloc once the hook 104 | // is in place 105 | // 106 | LPVOID WINAPI HookHeapAlloc(HANDLE a_Handle, DWORD a_Bla, SIZE_T a_Bla2) 107 | { 108 | printf("***** Call to HeapAlloc(0x%p, %u, 0x%Id)\n", a_Handle, a_Bla, a_Bla2); 109 | return TrueHeapAlloc(a_Handle, a_Bla, a_Bla2); 110 | } 111 | 112 | //========================================================================= 113 | // This is the function that will replace NtClose once the hook 114 | // is in place 115 | // 116 | ULONG WINAPI HookNtClose(HANDLE hHandle) 117 | { 118 | printf("***** Call to NtClose(0x%p)\n", hHandle); 119 | return TrueNtClose(hHandle); 120 | } 121 | 122 | //========================================================================= 123 | // This is where the work gets done. 124 | // 125 | int wmain(int argc, WCHAR* argv[]) 126 | { 127 | HANDLE hProc = NULL; 128 | 129 | // Set the hook 130 | if (Mhook_SetHook((PVOID*)&TrueNtOpenProcess, (PVOID) HookNtOpenProcess)) 131 | { 132 | // Now call OpenProcess and observe NtOpenProcess being redirected 133 | // under the hood. 134 | hProc = OpenProcess(PROCESS_ALL_ACCESS, 135 | FALSE, GetCurrentProcessId()); 136 | if (hProc) 137 | { 138 | printf("Successfully opened self: %p\n", hProc); 139 | CloseHandle(hProc); 140 | } 141 | else 142 | { 143 | printf("Could not open self: %d\n", GetLastError()); 144 | } 145 | // Remove the hook 146 | Mhook_Unhook((PVOID*)&TrueNtOpenProcess); 147 | } 148 | 149 | // Call OpenProces again - this time there won't be a redirection as 150 | // the hook has bee removed. 151 | hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); 152 | if (hProc) 153 | { 154 | printf("Successfully opened self: %p\n", hProc); 155 | CloseHandle(hProc); 156 | } 157 | else 158 | { 159 | printf("Could not open self: %d\n", GetLastError()); 160 | } 161 | 162 | // Test another hook, this time in SelectObject 163 | // (SelectObject is interesting in that on XP x64, the second instruction 164 | // in the trampoline uses IP-relative addressing and we need to do some 165 | // extra work under the hood to make things work properly. This really 166 | // is more of a test case rather than a demo.) 167 | printf("Testing SelectObject.\n"); 168 | if (Mhook_SetHook((PVOID*)&TrueSelectObject, (PVOID) HookSelectobject)) 169 | { 170 | // error checking omitted for brevity. doesn't matter much 171 | // in this context anyway. 172 | HDC hdc = GetDC(NULL); 173 | HDC hdcMem = CreateCompatibleDC(hdc); 174 | HBITMAP hbm = CreateCompatibleBitmap(hdc, 32, 32); 175 | HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); 176 | SelectObject(hdcMem, hbmOld); 177 | DeleteObject(hbm); 178 | DeleteDC(hdcMem); 179 | ReleaseDC(NULL, hdc); 180 | // Remove the hook 181 | Mhook_Unhook((PVOID*)&TrueSelectObject); 182 | } 183 | 184 | printf("Testing getaddrinfo.\n"); 185 | if (Mhook_SetHook((PVOID*)&Truegetaddrinfo, (PVOID) Hookgetaddrinfo)) 186 | { 187 | // error checking omitted for brevity. doesn't matter much 188 | // in this context anyway. 189 | WSADATA wd = {0}; 190 | WSAStartup(MAKEWORD(2, 2), &wd); 191 | const char* ip = "localhost"; 192 | struct addrinfo aiHints; 193 | struct addrinfo *res = NULL; 194 | memset(&aiHints, 0, sizeof(aiHints)); 195 | aiHints.ai_family = PF_UNSPEC; 196 | aiHints.ai_socktype = SOCK_STREAM; 197 | if (getaddrinfo(ip, NULL, &aiHints, &res)) 198 | { 199 | printf("getaddrinfo failed\n"); 200 | } 201 | else 202 | { 203 | int n = 0; 204 | while(res) 205 | { 206 | res = res->ai_next; 207 | n++; 208 | } 209 | printf("got %d addresses\n", n); 210 | } 211 | WSACleanup(); 212 | // Remove the hook 213 | Mhook_Unhook((PVOID*)&Truegetaddrinfo); 214 | } 215 | 216 | printf("Testing HeapAlloc.\n"); 217 | if (Mhook_SetHook((PVOID*)&TrueHeapAlloc, (PVOID) HookHeapAlloc)) 218 | { 219 | free(malloc(10)); 220 | // Remove the hook 221 | Mhook_Unhook((PVOID*)&TrueHeapAlloc); 222 | } 223 | 224 | printf("Testing NtClose.\n"); 225 | if (Mhook_SetHook((PVOID*)&TrueNtClose, (PVOID) HookNtClose)) 226 | { 227 | CloseHandle(NULL); 228 | // Remove the hook 229 | Mhook_Unhook((PVOID*)&TrueNtClose); 230 | } 231 | 232 | return 0; 233 | } 234 | 235 | -------------------------------------------------------------------------------- /mhook/mhook-test/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // mhook-test.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /mhook/mhook-test/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. 9 | #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. 10 | #endif 11 | 12 | //for the getaddrinfo test 13 | #include 14 | #pragma comment(lib, "ws2_32") 15 | 16 | #include 17 | #include 18 | #include 19 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_PARSER_H 2 | #define VERSION_PARSER_H 3 | 4 | #include 5 | #include 6 | #include // link with "ws2_32" 7 | 8 | using namespace std; 9 | 10 | // 11 | // Simple Java Class Parser 12 | // Supported: Up to Java 14 13 | // 14 | // Reference: 15 | // https://docs.oracle.com/javase/specs/jvms/se14/html/jvms-4.html#jvms-4.4 16 | // 17 | 18 | // Disable structure alignment as we will cast bytes directly into them, and we want them to be accurate. 19 | #pragma pack(push, 1) 20 | 21 | // Java values are big endian, so we need to create wrappers to reverse them. 22 | #pragma region Big endian wrappers 23 | 24 | class be_uint16_t { 25 | public: 26 | be_uint16_t() : be_val_(0) {} 27 | 28 | be_uint16_t(const uint16_t &val) : be_val_(htons(val)) {} 29 | 30 | operator uint16_t() const { return ntohs(be_val_); } 31 | 32 | private: 33 | uint16_t be_val_; 34 | }; 35 | 36 | class be_uint32_t { 37 | public: 38 | be_uint32_t() : be_val_(0) {} 39 | 40 | be_uint32_t(const uint32_t &val) : be_val_(htonl(val)) {} 41 | 42 | operator uint32_t() const { return ntohl(be_val_); } 43 | 44 | private: 45 | uint32_t be_val_; 46 | }; 47 | 48 | #pragma endregion 49 | 50 | #pragma region Java structs 51 | 52 | typedef be_uint32_t u4; 53 | typedef be_uint16_t u2; 54 | typedef unsigned char u1; 55 | 56 | struct CONSTANT_Class_info { 57 | u1 tag; 58 | u2 name_index; 59 | }; 60 | 61 | struct CONSTANT_Utf8_info { 62 | u1 tag; 63 | u2 length; 64 | u1 bytes[]; 65 | }; 66 | 67 | struct ClassHeader { 68 | u4 magic; 69 | u2 minor_version; 70 | u2 major_version; 71 | u2 constant_pool_count; 72 | }; 73 | 74 | struct ClassMiddle { 75 | u2 access_flags; 76 | u2 this_class; 77 | u2 super_class; 78 | }; 79 | 80 | enum Constant_pool { 81 | CONSTANT_Class = 7, 82 | CONSTANT_Fieldref = 9, 83 | CONSTANT_Methodref = 10, 84 | CONSTANT_InterfaceMethodref = 11, 85 | CONSTANT_String = 8, 86 | CONSTANT_Integer = 3, 87 | CONSTANT_Float = 4, 88 | CONSTANT_Long = 5, 89 | CONSTANT_Double = 6, 90 | CONSTANT_NameAndType = 12, 91 | CONSTANT_Utf8 = 1, 92 | CONSTANT_MethodHandle = 15, 93 | CONSTANT_MethodType = 16, 94 | CONSTANT_Dynamic = 17, 95 | CONSTANT_InvokeDynamic = 18, 96 | CONSTANT_Module = 19, 97 | CONSTANT_Package = 20, 98 | }; 99 | 100 | #pragma endregion 101 | 102 | #pragma pack(pop) 103 | 104 | const u4 javaMagic = *(u4 *) ("\xCA\xFE\xBA\xBE"); 105 | const int classHeaderSize = sizeof(ClassHeader); 106 | 107 | // Advances the pointer into a Java class buffer to the next constant structure. 108 | bool nextConstant(int &i, const char *&pc, std::vector &constant_pool) { 109 | constant_pool.push_back(pc); 110 | u1 tag = *(u1 *) pc; 111 | switch (tag) { 112 | case CONSTANT_Class: 113 | case CONSTANT_String: 114 | case CONSTANT_MethodType: 115 | case CONSTANT_Module: 116 | case CONSTANT_Package: 117 | pc += 3; 118 | break; 119 | case CONSTANT_Fieldref: 120 | case CONSTANT_Methodref: 121 | case CONSTANT_InterfaceMethodref: 122 | case CONSTANT_NameAndType: 123 | case CONSTANT_Dynamic: 124 | case CONSTANT_InvokeDynamic: 125 | case CONSTANT_Integer: 126 | case CONSTANT_Float: 127 | pc += 5; 128 | break; 129 | case CONSTANT_Long: 130 | case CONSTANT_Double: 131 | // All 8-byte constants take up two entries in the constant_pool table of the class file. 132 | constant_pool.push_back(nullptr); 133 | i++; 134 | pc += 9; 135 | break; 136 | case CONSTANT_Utf8: { 137 | pc += 3 + *(u2 *) (pc + 1); 138 | break; 139 | } 140 | case CONSTANT_MethodHandle: 141 | pc += 4; 142 | break; 143 | default: 144 | return false; 145 | } 146 | return true; 147 | } 148 | 149 | // Returns the class name parsed from the class bytes. 150 | string GetJavaClassName(const char *classBuf) { 151 | auto header = (ClassHeader *) classBuf; 152 | if (header->magic != javaMagic) { 153 | return string("M_ERROR"); 154 | } 155 | 156 | const char *pc = classBuf + classHeaderSize; 157 | std::vector constant_pool(header->constant_pool_count); 158 | constant_pool.resize(0); 159 | 160 | for (int i = 0; i < header->constant_pool_count - 1; i++) { 161 | if (!nextConstant(i, pc, constant_pool)) { 162 | return string("F_ERROR"); 163 | } 164 | } 165 | 166 | auto middle = (ClassMiddle *) pc; 167 | auto constClass = (CONSTANT_Class_info *) constant_pool.at(middle->this_class - 1); 168 | auto constUtf8 = (CONSTANT_Utf8_info *) constant_pool.at(constClass->name_index - 1); 169 | 170 | auto result = string(constUtf8->length + 1, '\x00'); 171 | memcpy(result.data(), constUtf8->bytes, constUtf8->length); 172 | return result; 173 | } 174 | 175 | #endif //VERSION_PARSER_H 176 | -------------------------------------------------------------------------------- /proxy.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef VERSION_PROXY_H 4 | #define VERSION_PROXY_H 5 | 6 | #define WRAPPER_GENFUNC(name) \ 7 | FARPROC orig_##name; \ 8 | extern "C" __declspec(naked) void _##name() \ 9 | { \ 10 | asm("jmp *orig_"#name); \ 11 | } 12 | 13 | WRAPPER_GENFUNC(GetFileVersionInfoA) 14 | WRAPPER_GENFUNC(GetFileVersionInfoByHandle) 15 | WRAPPER_GENFUNC(GetFileVersionInfoExW) 16 | WRAPPER_GENFUNC(GetFileVersionInfoExA) 17 | WRAPPER_GENFUNC(GetFileVersionInfoSizeA) 18 | WRAPPER_GENFUNC(GetFileVersionInfoSizeExA) 19 | WRAPPER_GENFUNC(GetFileVersionInfoSizeExW) 20 | WRAPPER_GENFUNC(GetFileVersionInfoSizeW) 21 | WRAPPER_GENFUNC(GetFileVersionInfoW) 22 | WRAPPER_GENFUNC(VerFindFileA) 23 | WRAPPER_GENFUNC(VerFindFileW) 24 | WRAPPER_GENFUNC(VerInstallFileA) 25 | WRAPPER_GENFUNC(VerInstallFileW) 26 | WRAPPER_GENFUNC(VerLanguageNameA) 27 | WRAPPER_GENFUNC(VerLanguageNameW) 28 | WRAPPER_GENFUNC(VerQueryValueA) 29 | WRAPPER_GENFUNC(VerQueryValueW) 30 | 31 | #define WRAPPER_FUNC(name) orig_##name = GetProcAddress(hOriginalDll, #name); 32 | 33 | void SourceInit() { 34 | char source[MAX_PATH]; 35 | GetSystemDirectory(source, MAX_PATH); 36 | strcat_s(source, sizeof source, "\\version.dll"); 37 | HMODULE hOriginalDll = LoadLibrary(source); 38 | 39 | WRAPPER_FUNC(GetFileVersionInfoA); 40 | WRAPPER_FUNC(GetFileVersionInfoByHandle); 41 | WRAPPER_FUNC(GetFileVersionInfoExW); 42 | WRAPPER_FUNC(GetFileVersionInfoExA); 43 | WRAPPER_FUNC(GetFileVersionInfoSizeA); 44 | WRAPPER_FUNC(GetFileVersionInfoSizeExW); 45 | WRAPPER_FUNC(GetFileVersionInfoSizeExA); 46 | WRAPPER_FUNC(GetFileVersionInfoSizeW); 47 | WRAPPER_FUNC(GetFileVersionInfoW); 48 | WRAPPER_FUNC(VerFindFileA); 49 | WRAPPER_FUNC(VerFindFileW); 50 | WRAPPER_FUNC(VerInstallFileA); 51 | WRAPPER_FUNC(VerInstallFileW); 52 | WRAPPER_FUNC(VerLanguageNameA); 53 | WRAPPER_FUNC(VerLanguageNameW); 54 | WRAPPER_FUNC(VerQueryValueA); 55 | WRAPPER_FUNC(VerQueryValueW); 56 | } 57 | 58 | #endif //VERSION_PROXY_H --------------------------------------------------------------------------------