Revert "UnitTestFrameworkPkg/Library/SubhookLib: Removed subhook from submodules."

This reverts commit 036165f251.
This commit is contained in:
Mikhail Krichanov 2024-11-05 10:55:04 +03:00 committed by MikhailKrichanov
parent 036165f251
commit 90c9d6b09a
26 changed files with 6 additions and 1863 deletions

3
.gitmodules vendored
View File

@ -23,6 +23,9 @@
[submodule "UnitTestFrameworkPkg/Library/GoogleTestLib/googletest"] [submodule "UnitTestFrameworkPkg/Library/GoogleTestLib/googletest"]
path = UnitTestFrameworkPkg/Library/GoogleTestLib/googletest path = UnitTestFrameworkPkg/Library/GoogleTestLib/googletest
url = https://github.com/google/googletest.git url = https://github.com/google/googletest.git
[submodule "UnitTestFrameworkPkg/Library/SubhookLib/subhook"]
path = UnitTestFrameworkPkg/Library/SubhookLib/subhook
url = https://github.com/Zeex/subhook.git
[submodule "MdePkg/Library/BaseFdtLib/libfdt"] [submodule "MdePkg/Library/BaseFdtLib/libfdt"]
path = MdePkg/Library/BaseFdtLib/libfdt path = MdePkg/Library/BaseFdtLib/libfdt
url = https://github.com/devicetree-org/pylibfdt.git url = https://github.com/devicetree-org/pylibfdt.git

View File

@ -230,6 +230,8 @@ class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManag
"BaseTools/Source/C/BrotliCompress/brotli", False)) "BaseTools/Source/C/BrotliCompress/brotli", False))
rs.append(RequiredSubmodule( rs.append(RequiredSubmodule(
"RedfishPkg/Library/JsonLib/jansson", False)) "RedfishPkg/Library/JsonLib/jansson", False))
rs.append(RequiredSubmodule(
"UnitTestFrameworkPkg/Library/SubhookLib/subhook", False))
rs.append(RequiredSubmodule( rs.append(RequiredSubmodule(
"MdePkg/Library/BaseFdtLib/libfdt", False)) "MdePkg/Library/BaseFdtLib/libfdt", False))
rs.append(RequiredSubmodule( rs.append(RequiredSubmodule(

@ -0,0 +1 @@
Subproject commit 83d4e1ebef3588fae48b69a7352cc21801cb70bc

View File

@ -1,10 +0,0 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -1,30 +0,0 @@
language: c
os:
- linux
- osx
- freebsd
env:
global:
- CTEST_OUTPUT_ON_FAILURE=ON
matrix:
- ARCH=x86
- ARCH=x86_64
addons:
apt:
packages:
- gcc
- g++
- gcc-multilib
- g++-multilib
- cmake
- yasm
before_install:
- if [ $TRAVIS_OS_NAME == osx ]; then brew install yasm; fi
- if [ $TRAVIS_OS_NAME == freebsd ]; then sudo pkg install -y yasm; fi
before_script:
- cmake . -DSUBHOOK_FORCE_32BIT=`([ $ARCH = x86 ] && echo 'ON') || echo 'OFF'`
script:
- make
- make test

View File

@ -1,109 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(subhook VERSION 0.8.2 LANGUAGES C)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(GNUInstallDirs)
macro(subhook_add_option_var name type default_value description)
set(${name}_DEFAULT ${default_value})
if(DEFINED ${name})
set(${name}_DEFAULT ${${name}})
endif()
set(${name} ${${name}_DEFAULT} CACHE ${type} ${description})
endmacro()
subhook_add_option_var(SUBHOOK_STATIC BOOL OFF "Build as a static library")
subhook_add_option_var(SUBHOOK_INSTALL
BOOL ON "Enable installation and packaging of targets/files with CPack")
subhook_add_option_var(SUBHOOK_TESTS BOOL ON "Enable tests")
subhook_add_option_var(SUBHOOK_FORCE_32BIT
BOOL OFF "Configure for compiling 32-bit binaries (on 64-bit systems)")
set(SUBHOOK_HEADERS subhook.h)
set(SUBHOOK_SOURCES subhook.c subhook_private.h subhook_x86.c)
if(WIN32)
list(APPEND SUBHOOK_SOURCES subhook_windows.c)
elseif(UNIX)
list(APPEND SUBHOOK_SOURCES subhook_unix.c)
endif()
if(SUBHOOK_STATIC)
add_library(subhook STATIC ${SUBHOOK_HEADERS} ${SUBHOOK_SOURCES})
target_compile_definitions(subhook PUBLIC SUBHOOK_STATIC)
else()
add_library(subhook SHARED ${SUBHOOK_HEADERS} ${SUBHOOK_SOURCES})
endif()
add_library(subhook::subhook ALIAS subhook)
target_compile_definitions(subhook PUBLIC
SUBHOOK_IMPLEMENTATION
SUBHOOK_SEPARATE_SOURCE_FILES
)
target_include_directories(subhook PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(CMAKE_COMPILER_ID MATCHES GNU OR CMAKE_C_COMPILER_ID MATCHES Clang)
target_compile_options(subhook PRIVATE "-Wall -Wextra")
endif()
if(SUBHOOK_FORCE_32BIT)
if(APPLE)
set_target_properties(subhook PROPERTIES OSX_ARCHITECTURES i386)
endif()
if(UNIX)
target_compile_options(subhook PRIVATE "-m32")
target_link_options(subhook PRIVATE "-m32")
endif()
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(SUBHOOK_INSTALL)
include(CMakePackageConfigHelpers)
install(TARGETS subhook EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${SUBHOOK_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
set(config_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake)
set(version_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake)
set(config_install_destination lib/cmake/${PROJECT_NAME})
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
${config_file}
INSTALL_DESTINATION ${config_install_destination}
)
write_basic_package_version_file(
${version_file}
COMPATIBILITY SameMajorVersion
)
install(FILES ${config_file} ${version_file} DESTINATION ${config_install_destination})
install(
EXPORT ${PROJECT_NAME}Targets
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${config_install_destination}
)
endif()
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
include(CPack)
include(CTest)
if(BUILD_TESTING AND SUBHOOK_TESTS)
enable_testing()
add_subdirectory(tests)
endif()

View File

@ -1,23 +0,0 @@
Copyright (c) 2012-2018 Zeex
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,165 +0,0 @@
[![Build Status][build_status]][build]
[![Build Status - Windows][build_status_win]][build_win]
SubHook is a super-simple hooking library for C and C++ that works on Windows,
Linux and macOS. It supports x86 only (32-bit and 64-bit).
Installation
------------
Easy method:
1. Copy the source and header files to your project and include
[`subhook.c`](subhook.c) in your build.
2. On Windows only: Define `SUBHOOK_STATIC` before including `subhook.h`.
With CMake:
1. Copy the subhook repo to your project tree.
2. Call `add_subdirectory(path/to/subhook)` in your CMakeLists.txt.
3. Optional: configure how the library is built by setting these varaible prior
to `add_subdirectory(...)`:
* `SUBHOOK_STATIC` - Build as static library (`OFF` by default)
* `SUBHOOK_INSTALL` - Enable installation and packaging of targets/files
with CPack (`OFF` by default)
* `SUBHOOK_TESTS` - Enable tests (`ON` by default)
* `SUBHOOK_FORCE_32BIT` - Configure for compiling 32-bit binaries on 64-bit
systems (default is `OFF`)
Use of CMake is not mandatory, the library can be built without it (no extra
build configuration is required).
Examples
--------
In the following examples `foo` is some function or a function pointer that
takes a single argument of type `int` and uses the same calling convention
as `my_foo` (depends on compiler).
### Basic usage
```c
#include <stdio.h>
#include <subhook.h>
subhook_t foo_hook;
void my_foo(int x) {
/* Remove the hook so that you can call the original function. */
subhook_remove(foo_hook);
printf("foo(%d) called\n", x);
foo(x);
/* Install the hook back to intercept further calls. */
subhook_install(foo_hook);
}
int main() {
/* Create a hook that will redirect all foo() calls to to my_foo(). */
foo_hook = subhook_new((void *)foo, (void *)my_foo, 0);
/* Install it. */
subhook_install(foo_hook);
foo(123);
/* Remove the hook and free memory when you're done. */
subhook_remove(foo_hook);
subhook_free(foo_hook);
}
```
### Trampolines
Using trampolines allows you to jump to the original code without removing
and re-installing hooks every time your function gets called.
```c
typedef void (*foo_func)(int x);
void my_foo(int x) {
printf("foo(%d) called\n", x);
/* Call foo() via trampoline. */
((foo_func)subhook_get_trampoline(foo_hook))(x);
}
int main() {
/* Same code as in the previous example. */
}
```
Please note that subhook has a very simple length disassmebler engine (LDE)
that works only with most common prologue instructions like push, mov, call,
etc. When it encounters an unknown instruction subhook_get_trampoline() will
return NULL. You can delegate instruction decoding to a custom disassembler
of your choice via `subhook_set_disasm_handler()`.
### C++
```c++
#include <iostream>
#include <subhook.h>
subhook::Hook foo_hook;
subhook::Hook foo_hook_tr;
typedef void (*foo_func)(int x);
void my_foo(int x) {
// ScopedHookRemove removes the specified hook and automatically re-installs
// it when the object goes out of scope (thanks to C++ destructors).
subhook::ScopedHookRemove remove(&foo_hook);
std::cout << "foo(" << x << ") called" << std::endl;
foo(x + 1);
}
void my_foo_tr(int x) {
std::cout << "foo(" << x << ") called" << std::endl;
// Call the original function via trampoline.
((foo_func)foo_hook_tr.GetTrampoline())(x + 1);
}
int main() {
foo_hook.Install((void *)foo, (void *)my_foo);
foo_hook_tr.Install((void *)foo, (void *)my_foo_tr);
}
```
Known issues
------------
* `subhook_get_trampoline()` may return NULL because only a small subset of
x86 instructions is supported by the disassembler in this library (just
common prologue instructions). As a workaround you can plug in a more
advanced instruction length decoder using `subhook_set_disasm_handler()`.
* If a target function (the function you are hooking) is less than N bytes
in length, for example if it's a short 2-byte jump to a nearby location
(sometimes compilers generate code like this), then you will not be able
to hook it.
N is 5 by default: 1 byte for jmp opcode + 4 bytes for offset. But if you
enable the use of 64-bit offsets in 64-bit mode N becomes 14 (see the
definition of `subhook_jmp64`).
* Some systems protect executable code form being modified at runtime, which
will not allow you to install hooks, or don't allow to mark heap-allocated
memory as executable, which prevents the use of trampolines.
For example, on Fedora you can have such problems because of SELinux (though
you can disable it or exclude your files).
License
-------
Licensed under the 2-clause BSD license.
[build]: https://travis-ci.org/Zeex/subhook
[build_status]: https://travis-ci.org/Zeex/subhook.svg?branch=master
[build_win]: https://ci.appveyor.com/project/Zeex/subhook/branch/master
[build_status_win]: https://ci.appveyor.com/api/projects/status/q5sp0p8ahuqfh8e4/branch/master?svg=true

View File

@ -1,20 +0,0 @@
version: '{build}'
platform:
- x86
- x64
configuration:
- Release
environment:
CTEST_OUTPUT_ON_FAILURE: ON
install:
- choco install -y yasm
before_build:
- if %PLATFORM% == x86 set BUILD_ARCH=Win32
- if %PLATFORM% == x64 set BUILD_ARCH=x64
- cmake . -A %BUILD_ARCH%
build_script:
- cmake --build . --config %CONFIGURATION%
test_script:
- ctest --build-config %CONFIGURATION%

View File

@ -1,3 +0,0 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

View File

@ -1,9 +0,0 @@
include(FindPackageHandleStandardArgs)
find_file(YASM_EXECUTABLE NAMES yasm yasm.exe)
mark_as_advanced(YASM_EXECUTABLE)
find_package_handle_standard_args(Yasm
FOUND_VAR YASM_FOUND
REQUIRED_VARS YASM_EXECUTABLE
)

View File

@ -1,77 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "subhook.h"
#include "subhook_private.h"
subhook_disasm_handler_t subhook_disasm_handler = NULL;
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_src(subhook_t hook) {
if (hook == NULL) {
return NULL;
}
return hook->src;
}
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_dst(subhook_t hook) {
if (hook == NULL) {
return NULL;
}
return hook->dst;
}
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_trampoline(subhook_t hook) {
if (hook == NULL) {
return NULL;
}
return hook->trampoline;
}
SUBHOOK_EXPORT int SUBHOOK_API subhook_is_installed(subhook_t hook) {
if (hook == NULL) {
return false;
}
return hook->installed;
}
SUBHOOK_EXPORT void SUBHOOK_API subhook_set_disasm_handler(
subhook_disasm_handler_t handler) {
subhook_disasm_handler = handler;
}
#ifndef SUBHOOK_SEPARATE_SOURCE_FILES
#if defined SUBHOOK_WINDOWS
#include "subhook_windows.c"
#elif defined SUBHOOK_UNIX
#include "subhook_unix.c"
#endif
#if defined SUBHOOK_X86 || defined SUBHOOK_X86_64
#include "subhook_x86.c"
#endif
#endif

View File

@ -1,283 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SUBHOOK_H
#define SUBHOOK_H
#include <stddef.h>
#if defined _M_IX86 || defined __i386__
#define SUBHOOK_X86
#define SUBHOOK_BITS 32
#elif defined _M_AMD64 || __amd64__
#define SUBHOOK_X86_64
#define SUBHOOK_BITS 64
#else
#error Unsupported architecture
#endif
#if defined _WIN32 || defined __CYGWIN__
#define SUBHOOK_WINDOWS
#elif defined __linux__ \
|| defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
#define SUBHOOK_UNIX
#elif defined __APPLE__
#define SUBHOOK_APPLE
#define SUBHOOK_UNIX
#else
#error Unsupported operating system
#endif
#if !defined SUBHOOK_EXTERN
#if defined __cplusplus
#define SUBHOOK_EXTERN extern "C"
#else
#define SUBHOOK_EXTERN extern
#endif
#endif
#if defined SUBHOOK_STATIC
#define SUBHOOK_API
#define SUBHOOK_EXPORT SUBHOOK_EXTERN
#endif
#if !defined SUBHOOK_API
#if defined SUBHOOK_X86
#if defined SUBHOOK_WINDOWS
#define SUBHOOK_API __cdecl
#elif defined SUBHOOK_UNIX
#define SUBHOOK_API __attribute__((cdecl))
#endif
#else
#define SUBHOOK_API
#endif
#endif
#if !defined SUBHOOK_EXPORT
#if defined SUBHOOK_WINDOWS
#if defined SUBHOOK_IMPLEMENTATION
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __declspec(dllexport)
#else
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __declspec(dllimport)
#endif
#elif defined SUBHOOK_UNIX
#if defined SUBHOOK_IMPLEMENTATION
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __attribute__((visibility("default")))
#else
#define SUBHOOK_EXPORT SUBHOOK_EXTERN
#endif
#endif
#endif
typedef enum subhook_flags {
/* Use the 64-bit jump method on x86-64 (requires more space). */
SUBHOOK_64BIT_OFFSET = 1
} subhook_flags_t;
struct subhook_struct;
typedef struct subhook_struct *subhook_t;
typedef int (SUBHOOK_API *subhook_disasm_handler_t)(
void *src,
int *reloc_op_offset);
SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(
void *src,
void *dst,
subhook_flags_t flags);
SUBHOOK_EXPORT void SUBHOOK_API subhook_free(subhook_t hook);
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_src(subhook_t hook);
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_dst(subhook_t hook);
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_trampoline(subhook_t hook);
SUBHOOK_EXPORT int SUBHOOK_API subhook_install(subhook_t hook);
SUBHOOK_EXPORT int SUBHOOK_API subhook_is_installed(subhook_t hook);
SUBHOOK_EXPORT int SUBHOOK_API subhook_remove(subhook_t hook);
/*
* Reads hook destination address from code.
*
* This function may be useful when you don't know the address or want to
* check whether src is already hooked.
*/
SUBHOOK_EXPORT void *SUBHOOK_API subhook_read_dst(void *src);
/*
* Returns the length of the first instruction in src. You can replace it with
* a custom function via subhook_set_disasm_handler.
*/
SUBHOOK_EXPORT int SUBHOOK_API subhook_disasm(void *src, int *reloc_op_offset);
/*
* Sets a custom disassmbler function to use in place of the default one
* (subhook_disasm).
*
* The default function can recognize only a small subset of x86 instructions
* commonly used in prologues. If it fails in your situation, you might want
* to use a more advanced disassembler library.
*/
SUBHOOK_EXPORT void SUBHOOK_API subhook_set_disasm_handler(
subhook_disasm_handler_t handler);
#ifdef __cplusplus
namespace subhook {
enum HookFlags {
HookNoFlags = 0,
HookFlag64BitOffset = SUBHOOK_64BIT_OFFSET
};
inline HookFlags operator|(HookFlags o1, HookFlags o2) {
return static_cast<HookFlags>(
static_cast<unsigned int>(o1) | static_cast<unsigned int>(o2));
}
inline HookFlags operator&(HookFlags o1, HookFlags o2) {
return static_cast<HookFlags>(
static_cast<unsigned int>(o1) & static_cast<unsigned int>(o2));
}
inline void *ReadHookDst(void *src) {
return subhook_read_dst(src);
}
inline void SetDisasmHandler(subhook_disasm_handler_t handler) {
subhook_set_disasm_handler(handler);
}
class Hook {
public:
Hook() : hook_(NULL) {}
Hook(void *src, void *dst, HookFlags flags = HookNoFlags)
: hook_(subhook_new(src, dst, (subhook_flags_t)flags))
{
}
~Hook() {
subhook_remove(hook_);
subhook_free(hook_);
}
void *GetSrc() const { return subhook_get_src(hook_); }
void *GetDst() const { return subhook_get_dst(hook_); }
void *GetTrampoline() const { return subhook_get_trampoline(hook_); }
bool Install() {
return subhook_install(hook_) == 0;
}
bool Install(void *src,
void *dst,
HookFlags flags = HookNoFlags) {
if (hook_ != NULL) {
subhook_remove(hook_);
subhook_free(hook_);
}
hook_ = subhook_new(src, dst, (subhook_flags_t)flags);
if (hook_ == NULL) {
return false;
}
return Install();
}
bool Remove() {
return subhook_remove(hook_) == 0;
}
bool IsInstalled() const {
return !!subhook_is_installed(hook_);
}
private:
Hook(const Hook &);
void operator=(const Hook &);
private:
subhook_t hook_;
};
class ScopedHookRemove {
public:
ScopedHookRemove(Hook *hook)
: hook_(hook),
removed_(hook_->Remove())
{
}
~ScopedHookRemove() {
if (removed_) {
hook_->Install();
}
}
private:
ScopedHookRemove(const ScopedHookRemove &);
void operator=(const ScopedHookRemove &);
private:
Hook *hook_;
bool removed_;
};
class ScopedHookInstall {
public:
ScopedHookInstall(Hook *hook)
: hook_(hook),
installed_(hook_->Install())
{
}
ScopedHookInstall(Hook *hook,
void *src,
void *dst,
HookFlags flags = HookNoFlags)
: hook_(hook),
installed_(hook_->Install(src, dst, flags))
{
}
~ScopedHookInstall() {
if (installed_) {
hook_->Remove();
}
}
private:
ScopedHookInstall(const ScopedHookInstall &);
void operator=(const ScopedHookInstall &);
private:
Hook *hook_;
bool installed_;
};
} // namespace subhook
#endif /* __cplusplus */
#endif /* SUBHOOK_H */

View File

@ -1,55 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SUBHOOK_PRIVATE_H
#define SUBHOOK_PRIVATE_H
#include <stddef.h>
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
struct subhook_struct {
int installed;
void *src;
void *dst;
subhook_flags_t flags;
void *code;
void *trampoline;
size_t jmp_size;
size_t trampoline_size;
size_t trampoline_len;
};
int subhook_unprotect(void *address, size_t size);
void *subhook_alloc_code(size_t size);
int subhook_free_code(void *address, size_t size);
#endif /* SUBHOOK_PRIVATE_H */

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>
#include "subhook.h"
#ifdef SUBHOOK_APPLE
#include <mach/mach.h>
#endif
#define SUBHOOK_CODE_PROTECT_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
int subhook_unprotect(void *address, size_t size) {
long pagesize;
pagesize = sysconf(_SC_PAGESIZE);
void *aligned_address = (void *)((long)address & ~(pagesize - 1));
// Fix up the length - since we rounded the start address off, if a jump is right at the
// end of a page we could need to unprotect both.
void *end = address + size;
size_t new_size = end - aligned_address;
int error = mprotect(aligned_address, new_size, SUBHOOK_CODE_PROTECT_FLAGS);
#ifdef SUBHOOK_APPLE
if (-1 == error)
{
/* If mprotect fails, try to use VM_PROT_COPY with vm_protect. */
kern_return_t kret = vm_protect(mach_task_self(), (unsigned long)aligned_address, new_size, 0, SUBHOOK_CODE_PROTECT_FLAGS | VM_PROT_COPY);
if (kret != KERN_SUCCESS)
{
error = -1;
}
error = 0;
}
#endif
return error;
}
void *subhook_alloc_code(size_t size) {
void *address;
address = mmap(NULL,
size,
SUBHOOK_CODE_PROTECT_FLAGS,
#if defined MAP_32BIT && !defined __APPLE__
MAP_32BIT |
#endif
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
return address == MAP_FAILED ? NULL : address;
}
int subhook_free_code(void *address, size_t size) {
if (address == NULL) {
return 0;
}
return munmap(address, size);
}

View File

@ -1,55 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <windows.h>
#define SUBHOOK_CODE_PROTECT_FLAGS PAGE_EXECUTE_READWRITE
int subhook_unprotect(void *address, size_t size) {
DWORD old_flags;
BOOL result = VirtualProtect(address,
size,
SUBHOOK_CODE_PROTECT_FLAGS,
&old_flags);
return !result;
}
void *subhook_alloc_code(size_t size) {
return VirtualAlloc(NULL,
size,
MEM_COMMIT | MEM_RESERVE,
SUBHOOK_CODE_PROTECT_FLAGS);
}
int subhook_free_code(void *address, size_t size) {
(void)size;
if (address == NULL) {
return 0;
}
return !VirtualFree(address, 0, MEM_RELEASE);
}

View File

@ -1,583 +0,0 @@
/*
* Copyright (c) 2012-2018 Zeex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "subhook.h"
#include "subhook_private.h"
#ifdef SUBHOOK_WINDOWS
#define INT32_MAX 0x7fffffff
#define INT32_MIN (-INT32_MAX - 1)
typedef unsigned __int8 uint8_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
#ifdef SUBHOOK_X86_64
typedef __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else
typedef __int32 intptr_t;
typedef unsigned __int32 uintptr_t;
#endif
#else
#include <stdint.h>
#endif
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MAX_INSN_LEN 15 /* maximum length of x86 instruction */
#define JMP_OPCODE 0xE9
#define PUSH_OPCODE 0x68
#define MOV_OPCODE 0xC7
#define RET_OPCODE 0xC3
#define JMP64_MOV_MODRM 0x44 /* write to address + 1 byte displacement */
#define JMP64_MOV_SIB 0x24 /* write to [rsp] */
#define JMP64_MOV_OFFSET 0x04
#define CHECK_INT32_OVERFLOW(x) \
((int64_t)(x) < INT32_MIN || ((int64_t)(x)) > INT32_MAX)
#pragma pack(push, 1)
struct subhook_jmp32 {
uint8_t opcode;
int32_t offset;
};
/* Since AMD64 doesn't support 64-bit direct jumps, we'll push the address
* onto the stack, then call RET.
*/
struct subhook_jmp64 {
uint8_t push_opcode;
uint32_t push_addr; /* lower 32-bits of the address to jump to */
uint8_t mov_opcode;
uint8_t mov_modrm;
uint8_t mov_sib;
uint8_t mov_offset;
uint32_t mov_addr; /* upper 32-bits of the address to jump to */
uint8_t ret_opcode;
};
#pragma pack(pop)
extern subhook_disasm_handler_t subhook_disasm_handler;
SUBHOOK_EXPORT int SUBHOOK_API subhook_disasm(void *src, int *reloc_op_offset) {
enum flags {
MODRM = 1,
PLUS_R = 1 << 1,
REG_OPCODE = 1 << 2,
IMM8 = 1 << 3,
IMM16 = 1 << 4,
IMM32 = 1 << 5,
RELOC = 1 << 6
};
static uint8_t prefixes[] = {
0xF0, 0xF2, 0xF3,
0x2E, 0x36, 0x3E, 0x26, 0x64, 0x65,
0x66, /* operand size override */
0x67 /* address size override */
};
struct opcode_info {
uint8_t opcode;
uint8_t reg_opcode;
unsigned int flags;
};
/*
* See the Intel Developer Manual volumes 2a and 2b for more information
* about instruction format and encoding:
*
* https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
*/
static struct opcode_info opcodes[] = {
/* ADD AL, imm8 */ {0x04, 0, IMM8},
/* ADD EAX, imm32 */ {0x05, 0, IMM32},
/* ADD r/m8, imm8 */ {0x80, 0, MODRM | REG_OPCODE | IMM8},
/* ADD r/m32, imm32 */ {0x81, 0, MODRM | REG_OPCODE | IMM32},
/* ADD r/m32, imm8 */ {0x83, 0, MODRM | REG_OPCODE | IMM8},
/* ADD r/m8, r8 */ {0x00, 0, MODRM},
/* ADD r/m32, r32 */ {0x01, 0, MODRM},
/* ADD r8, r/m8 */ {0x02, 0, MODRM},
/* ADD r32, r/m32 */ {0x03, 0, MODRM},
/* AND AL, imm8 */ {0x24, 0, IMM8},
/* AND EAX, imm32 */ {0x25, 0, IMM32},
/* AND r/m8, imm8 */ {0x80, 4, MODRM | REG_OPCODE | IMM8},
/* AND r/m32, imm32 */ {0x81, 4, MODRM | REG_OPCODE | IMM32},
/* AND r/m32, imm8 */ {0x83, 4, MODRM | REG_OPCODE | IMM8},
/* AND r/m8, r8 */ {0x20, 0, MODRM},
/* AND r/m32, r32 */ {0x21, 0, MODRM},
/* AND r8, r/m8 */ {0x22, 0, MODRM},
/* AND r32, r/m32 */ {0x23, 0, MODRM},
/* CALL rel32 */ {0xE8, 0, IMM32 | RELOC},
/* CALL r/m32 */ {0xFF, 2, MODRM | REG_OPCODE},
/* CMP r/m32, imm8 */ {0x83, 7, MODRM | REG_OPCODE | IMM8},
/* CMP r/m32, r32 */ {0x39, 0, MODRM},
/* CMP imm16/32 */ {0x3D, 0, IMM32},
/* DEC r/m32 */ {0xFF, 1, MODRM | REG_OPCODE},
/* DEC r32 */ {0x48, 0, PLUS_R},
/* ENTER imm16, imm8 */ {0xC8, 0, IMM16 | IMM8},
/* FLD m32fp */ {0xD9, 0, MODRM | REG_OPCODE},
/* FLD m64fp */ {0xDD, 0, MODRM | REG_OPCODE},
/* FLD m80fp */ {0xDB, 5, MODRM | REG_OPCODE},
/* INT 3 */ {0xCC, 0, 0},
/* JMP rel32 */ {0xE9, 0, IMM32 | RELOC},
/* JMP r/m32 */ {0xFF, 4, MODRM | REG_OPCODE},
/* LEA r32,m */ {0x8D, 0, MODRM},
/* LEAVE */ {0xC9, 0, 0},
/* MOV r/m8,r8 */ {0x88, 0, MODRM},
/* MOV r/m32,r32 */ {0x89, 0, MODRM},
/* MOV r8,r/m8 */ {0x8A, 0, MODRM},
/* MOV r32,r/m32 */ {0x8B, 0, MODRM},
/* MOV r/m16,Sreg */ {0x8C, 0, MODRM},
/* MOV Sreg,r/m16 */ {0x8E, 0, MODRM},
/* MOV AL,moffs8 */ {0xA0, 0, IMM8},
/* MOV EAX,moffs32 */ {0xA1, 0, IMM32},
/* MOV moffs8,AL */ {0xA2, 0, IMM8},
/* MOV moffs32,EAX */ {0xA3, 0, IMM32},
/* MOV r8, imm8 */ {0xB0, 0, PLUS_R | IMM8},
/* MOV r32, imm32 */ {0xB8, 0, PLUS_R | IMM32},
/* MOV r/m8, imm8 */ {0xC6, 0, MODRM | REG_OPCODE | IMM8},
/* MOV r/m32, imm32 */ {0xC7, 0, MODRM | REG_OPCODE | IMM32},
/* NOP */ {0x90, 0, 0},
/* OR AL, imm8 */ {0x0C, 0, IMM8},
/* OR EAX, imm32 */ {0x0D, 0, IMM32},
/* OR r/m8, imm8 */ {0x80, 1, MODRM | REG_OPCODE | IMM8},
/* OR r/m32, imm32 */ {0x81, 1, MODRM | REG_OPCODE | IMM32},
/* OR r/m32, imm8 */ {0x83, 1, MODRM | REG_OPCODE | IMM8},
/* OR r/m8, r8 */ {0x08, 0, MODRM},
/* OR r/m32, r32 */ {0x09, 0, MODRM},
/* OR r8, r/m8 */ {0x0A, 0, MODRM},
/* OR r32, r/m32 */ {0x0B, 0, MODRM},
/* POP r/m32 */ {0x8F, 0, MODRM | REG_OPCODE},
/* POP r32 */ {0x58, 0, PLUS_R},
/* PUSH r/m32 */ {0xFF, 6, MODRM | REG_OPCODE},
/* PUSH r32 */ {0x50, 0, PLUS_R},
/* PUSH imm8 */ {0x6A, 0, IMM8},
/* PUSH imm32 */ {0x68, 0, IMM32},
/* RET */ {0xC3, 0, 0},
/* RET imm16 */ {0xC2, 0, IMM16},
/* SUB AL, imm8 */ {0x2C, 0, IMM8},
/* SUB EAX, imm32 */ {0x2D, 0, IMM32},
/* SUB r/m8, imm8 */ {0x80, 5, MODRM | REG_OPCODE | IMM8},
/* SUB r/m32, imm32 */ {0x81, 5, MODRM | REG_OPCODE | IMM32},
/* SUB r/m32, imm8 */ {0x83, 5, MODRM | REG_OPCODE | IMM8},
/* SUB r/m8, r8 */ {0x28, 0, MODRM},
/* SUB r/m32, r32 */ {0x29, 0, MODRM},
/* SUB r8, r/m8 */ {0x2A, 0, MODRM},
/* SUB r32, r/m32 */ {0x2B, 0, MODRM},
/* TEST AL, imm8 */ {0xA8, 0, IMM8},
/* TEST EAX, imm32 */ {0xA9, 0, IMM32},
/* TEST r/m8, imm8 */ {0xF6, 0, MODRM | REG_OPCODE | IMM8},
/* TEST r/m32, imm32 */ {0xF7, 0, MODRM | REG_OPCODE | IMM32},
/* TEST r/m8, r8 */ {0x84, 0, MODRM},
/* TEST r/m32, r32 */ {0x85, 0, MODRM},
/* XOR AL, imm8 */ {0x34, 0, IMM8},
/* XOR EAX, imm32 */ {0x35, 0, IMM32},
/* XOR r/m8, imm8 */ {0x80, 6, MODRM | REG_OPCODE | IMM8},
/* XOR r/m32, imm32 */ {0x81, 6, MODRM | REG_OPCODE | IMM32},
/* XOR r/m32, imm8 */ {0x83, 6, MODRM | REG_OPCODE | IMM8},
/* XOR r/m8, r8 */ {0x30, 0, MODRM},
/* XOR r/m32, r32 */ {0x31, 0, MODRM},
/* XOR r8, r/m8 */ {0x32, 0, MODRM},
/* XOR r32, r/m32 */ {0x33, 0, MODRM}
};
uint8_t *code = src;
size_t i;
int len = 0;
int operand_size = 4;
uint8_t opcode = 0;
int found_opcode = false;
for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++) {
if (code[len] == prefixes[i]) {
len++;
if (prefixes[i] == 0x66) {
operand_size = 2;
}
}
}
#ifdef SUBHOOK_X86_64
if ((code[len] & 0xF0) == 0x40) {
/* This is a REX prefix (40H - 4FH). REX prefixes are valid only in
* 64-bit mode.
*/
uint8_t rex = code[len++];
if (rex & 8) {
/* REX.W changes size of immediate operand to 64 bits. */
operand_size = 8;
}
}
#endif
for (i = 0; i < sizeof(opcodes) / sizeof(*opcodes); i++) {
if (code[len] == opcodes[i].opcode) {
if (opcodes[i].flags & REG_OPCODE) {
found_opcode = ((code[len + 1] >> 3) & 7) == opcodes[i].reg_opcode;
} else {
found_opcode = true;
}
}
if ((opcodes[i].flags & PLUS_R)
&& (code[len] & 0xF8) == opcodes[i].opcode) {
found_opcode = true;
}
if (found_opcode) {
opcode = code[len++];
break;
}
}
if (!found_opcode) {
return 0;
}
if (reloc_op_offset != NULL && opcodes[i].flags & RELOC) {
/* Either a call or a jump instruction that uses an absolute or relative
* 32-bit address.
*
* Note: We don't support short (8-bit) offsets at the moment, so the
* caller can assume the operand will be always 4 bytes.
*/
*reloc_op_offset = len;
}
if (opcodes[i].flags & MODRM) {
uint8_t modrm = code[len++]; /* +1 for Mod/RM byte */
uint8_t mod = modrm >> 6;
uint8_t rm = modrm & 0x07;
if (mod != 3 && rm == 4) {
uint8_t sib = code[len++]; /* +1 for SIB byte */
uint8_t base = sib & 0x07;
if (base == 5) {
/* The SIB is followed by a disp32 with no base if the MOD is 00B.
* Otherwise, disp8 or disp32 + [EBP].
*/
if (mod == 1) {
len += 1; /* for disp8 */
} else {
len += 4; /* for disp32 */
}
}
}
#ifdef SUBHOOK_X86_64
if (reloc_op_offset != NULL && mod == 0 && rm == 5) {
/* RIP-relative addressing: target is at [RIP + disp32]. */
*reloc_op_offset = (int32_t)len;
}
#endif
if (mod == 1) {
len += 1; /* for disp8 */
}
if (mod == 2 || (mod == 0 && rm == 5)) {
len += 4; /* for disp32 */
}
}
if (opcodes[i].flags & IMM8) {
len += 1;
}
if (opcodes[i].flags & IMM16) {
len += 2;
}
if (opcodes[i].flags & IMM32) {
len += operand_size;
}
return len;
}
static size_t subhook_get_jmp_size(subhook_flags_t flags) {
#ifdef SUBHOOK_X86_64
if ((flags & SUBHOOK_64BIT_OFFSET) != 0) {
return sizeof(struct subhook_jmp64);
}
#else
(void)flags;
#endif
return sizeof(struct subhook_jmp32);
}
static int subhook_make_jmp32(void *src, void *dst) {
struct subhook_jmp32 *jmp = (struct subhook_jmp32 *)src;
intptr_t src_addr = (intptr_t)src;
intptr_t dst_addr = (intptr_t)dst;
#ifdef SUBHOOK_X86_64
int64_t distance = ABS(src_addr - dst_addr);
#endif
#ifdef SUBHOOK_X86_64
if (CHECK_INT32_OVERFLOW(distance)) {
return -EOVERFLOW;
}
#endif
jmp->opcode = JMP_OPCODE;
jmp->offset = (int32_t)(dst_addr - (src_addr + sizeof(*jmp)));
return 0;
}
#ifdef SUBHOOK_X86_64
static int subhook_make_jmp64(void *src, void *dst) {
struct subhook_jmp64 *jmp = (struct subhook_jmp64 *)src;
jmp->push_opcode = PUSH_OPCODE;
jmp->push_addr = (uint32_t)(uintptr_t)dst; /* truncate */
jmp->mov_opcode = MOV_OPCODE;
jmp->mov_modrm = JMP64_MOV_MODRM;
jmp->mov_sib = JMP64_MOV_SIB;
jmp->mov_offset = JMP64_MOV_OFFSET;
jmp->mov_addr = (uint32_t)(((uintptr_t)dst) >> 32);
jmp->ret_opcode = RET_OPCODE;
return 0;
}
#endif
static int subhook_make_jmp(void *src,
void *dst,
subhook_flags_t flags) {
#ifdef SUBHOOK_X86_64
if ((flags & SUBHOOK_64BIT_OFFSET) != 0) {
return subhook_make_jmp64(src, dst);
}
#else
(void)flags;
#endif
return subhook_make_jmp32(src, dst);
}
static int subhook_make_trampoline(void *trampoline,
void *src,
size_t jmp_size,
size_t *trampoline_len,
subhook_flags_t flags) {
size_t orig_size = 0;
size_t insn_len;
intptr_t trampoline_addr = (intptr_t)trampoline;
intptr_t src_addr = (intptr_t)src;
subhook_disasm_handler_t disasm_handler =
subhook_disasm_handler != NULL ? subhook_disasm_handler : subhook_disasm;
assert(trampoline_len != NULL);
/* Determine how many bytes of original code needs to be copied over
* to the trampoline.
*/
while (orig_size < jmp_size) {
int reloc_op_offset = 0;
insn_len =
disasm_handler((void *)(src_addr + orig_size), &reloc_op_offset);
if (insn_len == 0) {
return -EINVAL;
}
/* Copy this instruction to the trampoline.
*/
memcpy((void *)(trampoline_addr + orig_size),
(void *)(src_addr + orig_size),
insn_len);
/* If the operand is a relative address, such as found in calls or jumps,
* it needs to be relocated because the original code and the trampoline
* reside at different locations in memory.
*/
if (reloc_op_offset > 0) {
/* Calculate how far our trampoline is from the source and change the
* address accordingly.
*/
intptr_t offset = trampoline_addr - src_addr;
#ifdef SUBHOOK_X86_64
if (CHECK_INT32_OVERFLOW(offset)) {
/*
* Oops! It looks like the two locations are too far away from each
* other! This is not going to work...
*/
*trampoline_len = 0;
return -EOVERFLOW;
}
#endif
int32_t *op = (int32_t *)(trampoline_addr + orig_size + reloc_op_offset);
*op -= (int32_t)offset;
}
orig_size += insn_len;
}
*trampoline_len = orig_size + jmp_size;
/* Insert the final jump. It goes back to the original code at
* src + orig_size.
*/
return subhook_make_jmp((void *)(trampoline_addr + orig_size),
(void *)(src_addr + orig_size),
flags);
}
SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
void *dst,
subhook_flags_t flags) {
subhook_t hook;
int error;
hook = calloc(1, sizeof(*hook));
if (hook == NULL) {
return NULL;
}
hook->src = src;
hook->dst = dst;
hook->flags = flags;
hook->jmp_size = subhook_get_jmp_size(hook->flags);
hook->trampoline_size = hook->jmp_size * 2 + MAX_INSN_LEN;
hook->code = malloc(hook->jmp_size);
if (hook->code == NULL) {
goto error_exit;
}
memcpy(hook->code, hook->src, hook->jmp_size);
error = subhook_unprotect(hook->src, hook->jmp_size);
if (error != 0) {
goto error_exit;
}
hook->trampoline = subhook_alloc_code(hook->trampoline_size);
if (hook->trampoline != NULL) {
error = subhook_make_trampoline(hook->trampoline,
hook->src,
hook->jmp_size,
&hook->trampoline_len,
hook->flags);
if (error != 0) {
subhook_free_code(hook->trampoline, hook->trampoline_size);
hook->trampoline = NULL;
hook->trampoline_size = 0;
hook->trampoline_len = 0;
}
}
return hook;
error_exit:
subhook_free_code(hook->trampoline, hook->trampoline_size);
free(hook->code);
free(hook);
return NULL;
}
SUBHOOK_EXPORT void SUBHOOK_API subhook_free(subhook_t hook) {
if (hook == NULL) {
return;
}
subhook_free_code(hook->trampoline, hook->trampoline_size);
free(hook->code);
free(hook);
}
SUBHOOK_EXPORT int SUBHOOK_API subhook_install(subhook_t hook) {
int error;
if (hook == NULL) {
return -EINVAL;
}
if (hook->installed) {
return -EINVAL;
}
error = subhook_make_jmp(hook->src, hook->dst, hook->flags);
if (error >= 0) {
hook->installed = true;
return 0;
}
return error;
}
SUBHOOK_EXPORT int SUBHOOK_API subhook_remove(subhook_t hook) {
if (hook == NULL) {
return -EINVAL;
}
if (!hook->installed) {
return -EINVAL;
}
memcpy(hook->src, hook->code, hook->jmp_size);
hook->installed = 0;
return 0;
}
SUBHOOK_EXPORT void *SUBHOOK_API subhook_read_dst(void *src) {
struct subhook_jmp32 *maybe_jmp32 = (struct subhook_jmp32 *)src;
#ifdef SUBHOOK_X86_64
struct subhook_jmp64 *maybe_jmp64 = (struct subhook_jmp64 *)src;
#endif
if (maybe_jmp32->opcode == JMP_OPCODE) {
return (void *)(
maybe_jmp32->offset + (uintptr_t)src + sizeof(*maybe_jmp32));
}
#ifdef SUBHOOK_X86_64
if (maybe_jmp64->push_opcode == PUSH_OPCODE
&& maybe_jmp64->mov_opcode == MOV_OPCODE
&& maybe_jmp64->mov_modrm == JMP64_MOV_MODRM
&& maybe_jmp64->mov_sib == JMP64_MOV_SIB
&& maybe_jmp64->mov_offset == JMP64_MOV_OFFSET
&& maybe_jmp64->ret_opcode == RET_OPCODE) {
return (void *)(
maybe_jmp64->push_addr & ((uintptr_t)maybe_jmp64->mov_addr << 32));
}
#endif
return NULL;
}

View File

@ -1,103 +0,0 @@
find_package(Yasm REQUIRED)
if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR SUBHOOK_FORCE_32BIT)
set(BITS 32)
else()
set(BITS 64)
endif()
set(asm_file foo_${BITS}.asm)
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT SUBHOOK_FORCE_32BIT)
if(WIN32)
set(asm_file foo_64_win.asm)
elseif(UNIX)
set(asm_file foo_64_unix.asm)
set(asm_obj_file ${asm_file}.o)
endif()
endif()
if(NOT asm_obj_file)
set(asm_obj_file ${asm_file}.obj)
endif()
if(WIN32)
set(asm_format "win${BITS}")
elseif(APPLE)
set(asm_format "macho${BITS}")
else()
set(asm_format "elf${BITS}")
endif()
set(options "-f" "${asm_format}")
if(APPLE OR (WIN32 AND (CMAKE_SIZEOF_VOID_P EQUAL 4 OR SUBHOOK_FORCE_32BIT)))
list(APPEND options "--prefix=_")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND options "-DUSE_PLT")
endif()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${asm_obj_file}"
COMMAND "${YASM_EXECUTABLE}" ${options} "-o"
"${CMAKE_CURRENT_BINARY_DIR}/${asm_obj_file}"
"${CMAKE_CURRENT_SOURCE_DIR}/${asm_file}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${asm_file}"
)
add_executable(subhook_test_exe
test.c
"${CMAKE_CURRENT_BINARY_DIR}/${asm_obj_file}"
)
set_target_properties(subhook_test_exe PROPERTIES OUTPUT_NAME test)
enable_language(CXX)
add_executable(subhook_cxx_test_exe
test.cpp
"${CMAKE_CURRENT_BINARY_DIR}/${asm_obj_file}"
)
set_target_properties(subhook_cxx_test_exe PROPERTIES OUTPUT_NAME test++)
foreach(target subhook_test_exe subhook_cxx_test_exe)
if(SUBHOOK_FORCE_32BIT)
if(APPLE)
set_target_properties(${target} PROPERTIES OSX_ARCHITECTURES i386)
endif()
if(UNIX)
set_property(TARGET ${target} APPEND_STRING PROPERTY
COMPILE_FLAGS " -m32")
set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " -m32")
endif()
endif()
target_link_libraries(${target} subhook)
if(MSVC)
set_property(TARGET ${target}
APPEND_STRING PROPERTY LINK_FLAGS " /INCREMENTAL:NO")
endif()
if(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT SUBHOOK_FORCE_32BIT)
set_property(TARGET ${target} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-no_pie")
endif()
add_test(NAME ${target}_test COMMAND $<TARGET_FILE:${target}>)
set(expected_output
"Testing initial install
foo_hooked\\(\\) called
foo\\(\\) called
Testing re-install
foo_hooked\\(\\) called
foo\\(\\) called
Testing trampoline
foo_hooked_tr\\(\\) called
foo\\(\\) called
")
set_tests_properties(${target}_test PROPERTIES
PASS_REGULAR_EXPRESSION "${expected_output}")
if(WIN32 AND NOT SUBHOOK_STATIC)
set_tests_properties(${target}_test PROPERTIES
ENVIRONMENT PATH=$<TARGET_FILE_DIR:subhook>)
endif()
endforeach()

View File

@ -1,5 +0,0 @@
#include <stdio.h>
extern "C" void foo() {
puts("foo() called");
}

View File

@ -1,31 +0,0 @@
extern puts
global foo
section .rodata
message:
db 'foo() called', 0
section .text
;; Long nop macros for nasm/yasm borrowed from nasm-utils:
;; https://github.com/travisdowns/nasm-utils
%define nop1 nop ; just a nop, included for completeness
%define nop2 db 0x66, 0x90 ; 66 NOP
%define nop3 db 0x0F, 0x1F, 0x00 ; NOP DWORD ptr [EAX]
%define nop4 db 0x0F, 0x1F, 0x40, 0x00 ; NOP DWORD ptr [EAX + 00H]
%define nop5 db 0x0F, 0x1F, 0x44, 0x00, 0x00 ; NOP DWORD ptr [EAX + EAX*1 + 00H]
%define nop6 db 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 ; 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
%define nop7 db 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 ; NOP DWORD ptr [EAX + 00000000H]
%define nop8 db 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 ; NOP DWORD ptr [EAX + EAX*1 + 00000000H]
%define nop9 db 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 ; 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
foo:
push ebp
mov ebp, esp
sub esp, 4 ; align the stack to a 16-byte boundary
push message
call puts
mov esp, ebp
pop ebp
ret

View File

@ -1,24 +0,0 @@
bits 64
extern puts
global foo
section .data
message:
db 'foo() called', 0
section .text
foo:
nop
push rbp
mov rbp, rsp
lea rdi, [rel message]
%ifdef USE_PLT
call puts wrt ..plt
%else
call puts
%endif
pop rbp
ret

View File

@ -1,22 +0,0 @@
bits 64
extern puts
global foo
section .data
message:
db 'foo() called', 0
section .text
foo:
nop
push rbp
mov rbp, rsp
sub rsp, 32
mov rcx, qword message
call puts
add rsp, 32
pop rbp
ret

View File

@ -1,6 +0,0 @@
extern void foo(void);
int main() {
foo();
return 0;
}

View File

@ -1,6 +0,0 @@
extern "C" void foo(void);
int main() {
foo();
return 0;
}

View File

@ -1,82 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <subhook.h>
typedef void (*foo_func_t)(void);
#ifdef SUBHOOK_X86
#if defined SUBHOOK_WINDOWS
#define FOO_CALL __cdecl
#elif defined SUBHOOK_UNIX
#define FOO_CALL __attribute__((cdecl))
#endif
#endif
#ifndef FOO_CALL
#define FOO_CALL
#endif
extern void FOO_CALL foo(void);
foo_func_t foo_tr = NULL;
void foo_hooked(void) {
puts("foo_hooked() called");
}
void foo_hooked_tr(void) {
puts("foo_hooked_tr() called");
foo_tr();
}
int main() {
puts("Testing initial install");
subhook_t foo_hook = subhook_new((void *)foo,
(void *)foo_hooked,
SUBHOOK_64BIT_OFFSET);
if (foo_hook == NULL || subhook_install(foo_hook) < 0) {
puts("Install failed");
return EXIT_FAILURE;
}
foo();
if (subhook_remove(foo_hook) < 0) {
puts("Remove failed");
return EXIT_FAILURE;
}
foo();
puts("Testing re-install");
if (subhook_install(foo_hook) < 0) {
puts("Install failed");
return EXIT_FAILURE;
}
foo();
if (subhook_remove(foo_hook) < 0) {
puts("Remove failed");
return EXIT_FAILURE;
}
foo();
subhook_free(foo_hook);
puts("Testing trampoline");
subhook_t foo_hook_tr = subhook_new((void *)foo,
(void *)foo_hooked_tr,
SUBHOOK_64BIT_OFFSET);
if (subhook_install(foo_hook_tr) < 0) {
puts("Install failed");
return EXIT_FAILURE;
}
foo_tr = (foo_func_t)subhook_get_trampoline(foo_hook_tr);
if (foo_tr == NULL) {
puts("Failed to build trampoline");
return EXIT_FAILURE;
}
foo();
subhook_free(foo_hook_tr);
return EXIT_SUCCESS;
}

View File

@ -1,77 +0,0 @@
#include <cstdlib>
#include <iostream>
#include <subhook.h>
typedef void (*foo_func_t)();
#ifdef SUBHOOK_X86
#if defined SUBHOOK_WINDOWS
#define FOO_CALL __cdecl
#elif defined SUBHOOK_UNIX
#define FOO_CALL __attribute__((cdecl))
#endif
#endif
#ifndef FOO_CALL
#define FOO_CALL
#endif
extern "C" void FOO_CALL foo();
foo_func_t foo_tr = 0;
void foo_hooked() {
std::cout << "foo_hooked() called" << std::endl;;
}
void foo_hooked_tr() {
std::cout << "foo_hooked_tr() called" << std::endl;
foo_tr();
}
int main() {
std::cout << "Testing initial install" << std::endl;
subhook::Hook foo_hook((void *)foo,
(void *)foo_hooked,
subhook::HookFlag64BitOffset);
if (!foo_hook.Install()) {
std::cout << "Install failed" << std::endl;
return EXIT_FAILURE;
}
foo();
if (!foo_hook.Remove()) {
std::cout << "Remove failed" << std::endl;
return EXIT_FAILURE;
}
foo();
std::cout << "Testing re-install" << std::endl;
if (!foo_hook.Install()) {
std::cout << "Install failed" << std::endl;
return EXIT_FAILURE;
}
foo();
if (!foo_hook.Remove()) {
std::cout << "Remove failed" << std::endl;
return EXIT_FAILURE;
}
foo();
std::cout << "Testing trampoline" << std::endl;
subhook::Hook foo_hook_tr((void *)foo,
(void *)foo_hooked_tr,
subhook::HookFlag64BitOffset);
if (!foo_hook_tr.Install()) {
std::cout << "Install failed" << std::endl;
return EXIT_FAILURE;
}
foo_tr = (foo_func_t)foo_hook_tr.GetTrampoline();
if (foo_tr == 0) {
std::cout << "Failed to build trampoline" << std::endl;
return EXIT_FAILURE;
}
foo();
return EXIT_SUCCESS;
}