Imported Upstream version 0.2
Devon Kearns
11 years ago
0 | Vikas Naresh Kumar <[email protected]> | |
1 | Selective Intellect LLC <[email protected]> |
0 | ### hotpatch is a dll injection strategy | |
1 | ### Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
2 | ### All rights reserved. | |
3 | ### | |
4 | ### Redistribution and use in source and binary forms, with or without | |
5 | ### modification, are permitted provided that the following conditions are met: | |
6 | ### | |
7 | ### * Redistributions of source code must retain the above copyright | |
8 | ### notice, this list of conditions and the following disclaimer. | |
9 | ### | |
10 | ### * Redistributions in binary form must reproduce the above copyright | |
11 | ### notice, this list of conditions and the following disclaimer in the | |
12 | ### documentation and/or other materials provided with the distribution. | |
13 | ### | |
14 | ### * Neither the name of Selective Intellect LLC nor the | |
15 | ### names of its contributors may be used to endorse or promote products | |
16 | ### derived from this software without specific prior written permission. | |
17 | ### | |
18 | ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
19 | ### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | ### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | ### DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
22 | ### DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | ### LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | ### ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | ### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
27 | ### SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | ### | |
29 | cmake_minimum_required(VERSION 2.6 FATAL_ERROR) | |
30 | ||
31 | option(DEBUG "In Debug mode" ON) | |
32 | option(USE_ASM "Use Assembler" OFF) | |
33 | if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") | |
34 | set(DEBUG OFF) | |
35 | endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") | |
36 | ||
37 | if (CMAKE_COMPILER_IS_GNUCC) | |
38 | if (ARCH) | |
39 | if (ARCH STREQUAL "x86_64") | |
40 | message(STATUS "Compiling for ${ARCH} 64-bit") | |
41 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") | |
42 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -m64") | |
43 | else (ARCH STREQUAL "x86_64") | |
44 | message(STATUS "Compiling for ${ARCH} 32-bit") | |
45 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") | |
46 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -m32") | |
47 | endif (ARCH STREQUAL "x86_64") | |
48 | endif (ARCH) | |
49 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -pedantic -ansi -posix") | |
50 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -fno-inline") | |
51 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") | |
52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pedantic -ansi -posix") | |
53 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -fno-inline") | |
54 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") | |
55 | endif (CMAKE_COMPILER_IS_GNUCC) | |
56 | include(CTest) | |
57 | include(CheckFunctionExists) | |
58 | include(CheckIncludeFile) | |
59 | include(CheckIncludeFiles) | |
60 | include(CheckSymbolExists) | |
61 | include(CheckTypeSize) | |
62 | if (WIN32 OR APPLE) | |
63 | message(FATAL_ERROR "Windows or Mac OSX is not supported") | |
64 | endif (WIN32 OR APPLE) | |
65 | check_include_file("features.h" HOTPATCH_HAS_FEATURES_H) | |
66 | check_include_file("errno.h" HOTPATCH_HAS_ERRNO_H) | |
67 | check_include_file("stdio.h" HOTPATCH_HAS_STDIO_H) | |
68 | check_include_file("stdlib.h" HOTPATCH_HAS_STDLIB_H) | |
69 | check_include_file("string.h" HOTPATCH_HAS_STRING_H) | |
70 | check_function_exists("strnlen" HOTPATCH_HAS_STRNLEN_FN) | |
71 | check_function_exists("strtok_r" HOTPATCH_HAS_STRTOKR_FN) | |
72 | check_include_file("stddef.h" HOTPATCH_HAS_STDDEF_H) | |
73 | check_include_file("stdint.h" HOTPATCH_HAS_STDINT_H) | |
74 | check_include_file("stdarg.h" HOTPATCH_HAS_STDARG_H) | |
75 | check_include_file("stdbool.h" HOTPATCH_HAS_STDBOOL_H) | |
76 | check_include_file("time.h" HOTPATCH_HAS_TIME_H) | |
77 | check_include_file("sys/time.h" HOTPATCH_HAS_SYS_TIME_H) | |
78 | check_include_file("sys/types.h" HOTPATCH_HAS_SYS_TYPES_H) | |
79 | check_include_file("unistd.h" HOTPATCH_HAS_UNISTD_H) | |
80 | check_include_file("assert.h" HOTPATCH_HAS_ASSERT_H) | |
81 | check_include_file("limits.h" HOTPATCH_HAS_LIMITS_H) | |
82 | if (UNIX AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | |
83 | check_include_file("elf.h" HOTPATCH_HAS_LINUX_ELF_H) | |
84 | check_include_file("dlfcn.h" HOTPATCH_HAS_LINUX_DLFCN_H) | |
85 | if (HOTPATCH_HAS_LINUX_DLFCN_H) | |
86 | set(HOTPATCH_OTHER_LIBS ${HOTPATCH_OTHER_LIBS} dl) | |
87 | endif (HOTPATCH_HAS_LINUX_DLFCN_H) | |
88 | check_include_file("link.h" HOTPATCH_HAS_LINUX_LINK_H) | |
89 | check_include_file("pthread.h" HOTPATCH_HAS_LINUX_PTHREAD_H) | |
90 | if (HOTPATCH_HAS_LINUX_PTHREAD_H) | |
91 | set(HOTPATCH_OTHER_LIBS ${HOTPATCH_OTHER_LIBS} pthread) | |
92 | endif (HOTPATCH_HAS_LINUX_PTHREAD_H) | |
93 | check_include_file("setjmp.h" HOTPATCH_HAS_LINUX_SETJMP_H) | |
94 | check_include_file("signal.h" HOTPATCH_HAS_LINUX_SIGNAL_H) | |
95 | check_include_file("sys/ptrace.h" HOTPATCH_HAS_LINUX_SYS_PTRACE_H) | |
96 | check_include_file("sys/wait.h" HOTPATCH_HAS_LINUX_SYS_WAIT_H) | |
97 | check_include_file("sys/stat.h" HOTPATCH_HAS_LINUX_SYS_STAT_H) | |
98 | check_include_file("fcntl.h" HOTPATCH_HAS_LINUX_FCNTL_H) | |
99 | check_include_file("sys/syscall.h" HOTPATCH_HAS_LINUX_SYS_SYSCALL_H) | |
100 | check_include_files("sys/types.h;sys/user.h" HOTPATCH_HAS_LINUX_SYS_USER_H) | |
101 | check_function_exists("dl_iterate_phdr" HOTPATCH_HAS_LINUX_DLITERPHDR_FN) | |
102 | check_type_size("void *" HOTPATCH_VOIDPTR_SIZE) | |
103 | if (NOT HAVE_HOTPATCH_VOIDPTR_SIZE) | |
104 | message(FATAL_ERROR "(void *) does not seem to be supported.") | |
105 | endif (NOT HAVE_HOTPATCH_VOIDPTR_SIZE) | |
106 | ## Find assemblers | |
107 | if (USE_ASM) | |
108 | include(FindPerl) | |
109 | find_file(HOTPATCH_NASM nasm) | |
110 | if (HOTPATCH_NASM MATCHES "-NOTFOUND") | |
111 | message(STATUS "Found nasm - not found") | |
112 | set(HOTPATCH_HAS_NASM 0) | |
113 | else (HOTPATCH_NASM MATCHES "-NOTFOUND") | |
114 | message(STATUS "Found nasm - ${HOTPATCH_NASM}") | |
115 | set(HOTPATCH_HAS_NASM 1) | |
116 | endif (HOTPATCH_NASM MATCHES "-NOTFOUND") | |
117 | find_file(HOTPATCH_YASM yasm) | |
118 | if (HOTPATCH_YASM MATCHES "-NOTFOUND") | |
119 | message(STATUS "Found yasm - no") | |
120 | set(HOTPATCH_HAS_YASM 1) | |
121 | else (HOTPATCH_YASM MATCHES "-NOTFOUND") | |
122 | message(STATUS "Found yasm - ${HOTPATCH_YASM}") | |
123 | set(HOTPATCH_HAS_YASM 1) | |
124 | endif (HOTPATCH_YASM MATCHES "-NOTFOUND") | |
125 | if (HOTPATCH_HAS_NASM OR HOTPATCH_HAS_YASM) | |
126 | if (HOTPATCH_HAS_NASM) | |
127 | message(STATUS "Using nasm - yes") | |
128 | message(STATUS "Using yasm - no") | |
129 | set(HOTPATCH_ASM ${HOTPATCH_NASM} CACHE INTERNAL "" FORCE) | |
130 | else (HOTPATCH_HAS_NASM) | |
131 | message(STATUS "Using nasm - no") | |
132 | message(STATUS "Using yasm - yes") | |
133 | set(HOTPATCH_ASM ${HOTPATCH_YASM} CACHE INTERNAL "" FORCE) | |
134 | endif (HOTPATCH_HAS_NASM) | |
135 | else (HOTPATCH_HAS_NASM OR HOTPATCH_HAS_YASM) | |
136 | message(FATAL_ERROR "Unable to find nasm or yasm." | |
137 | " Please install nasm or yasm or both and make it available in the" | |
138 | " PATH") | |
139 | endif (HOTPATCH_HAS_NASM OR HOTPATCH_HAS_YASM) | |
140 | set(HOTPATCH_USE_ASM 1) | |
141 | endif (USE_ASM) | |
142 | endif (UNIX AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | |
143 | ||
144 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) | |
145 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) | |
146 | add_subdirectory(include) | |
147 | add_subdirectory(src) | |
148 | add_subdirectory(test) |
0 | Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
1 | All rights reserved. | |
2 | ||
3 | Redistribution and use in source and binary forms, with or without | |
4 | modification, are permitted provided that the following conditions are met: | |
5 | ||
6 | * Redistributions of source code must retain the above copyright | |
7 | notice, this list of conditions and the following disclaimer. | |
8 | ||
9 | * Redistributions in binary form must reproduce the above copyright | |
10 | notice, this list of conditions and the following disclaimer in the | |
11 | documentation and/or other materials provided with the distribution. | |
12 | ||
13 | * Neither the name of Selective Intellect LLC nor the | |
14 | names of its contributors may be used to endorse or promote products | |
15 | derived from this software without specific prior written permission. | |
16 | ||
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
20 | DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | HOTPATCH INSTALLATION | |
1 | ====================== | |
2 | ||
3 | Pre-requisites: | |
4 | ||
5 | 1. CMake: You need to have cmake 2.6 or above installed. | |
6 | 2. Compilers: gcc is needed along with standard headers. | |
7 | 3. GNU Make: make is needed to build the code. | |
8 | ||
9 | ||
10 | How to install: | |
11 | ||
12 | Release build: | |
13 | ||
14 | $ make | |
15 | $ make test | |
16 | $ make install | |
17 | ||
18 | By default make will try to install in /usr/local. If you want your custom | |
19 | directory then you can do the following: | |
20 | ||
21 | $ make install PREFIX=/path/to/install | |
22 | ||
23 | If you're on a 64-bit system and want to compile for 32-bit, you can do the | |
24 | following: | |
25 | ||
26 | $ make ARCH=i386 | |
27 | $ make test ARCH=i386 | |
28 | $ make install ARCH=i386 | |
29 | ||
30 | Debug build: | |
31 | ||
32 | $ make debug | |
33 | $ make testdebug | |
34 | $ make installdebug | |
35 | ||
36 | By default make will try to install in /usr/local. If you want your custom | |
37 | directory then you can do the following: | |
38 | ||
39 | $ make installdebug PREFIX=/path/to/install | |
40 | ||
41 | If you're on a 64-bit system and want to compile for 32-bit, you can do the | |
42 | following: | |
43 | ||
44 | $ make debug ARCH=i386 | |
45 | $ make testdebug ARCH=i386 | |
46 | $ make installdebug ARCH=i386 |
0 | ### hotpatch is a dll injection strategy | |
1 | ### Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
2 | ### All rights reserved. | |
3 | ### | |
4 | ### Redistribution and use in source and binary forms, with or without | |
5 | ### modification, are permitted provided that the following conditions are met: | |
6 | ### | |
7 | ### * Redistributions of source code must retain the above copyright | |
8 | ### notice, this list of conditions and the following disclaimer. | |
9 | ### | |
10 | ### * Redistributions in binary form must reproduce the above copyright | |
11 | ### notice, this list of conditions and the following disclaimer in the | |
12 | ### documentation and/or other materials provided with the distribution. | |
13 | ### | |
14 | ### * Neither the name of Selective Intellect LLC nor the | |
15 | ### names of its contributors may be used to endorse or promote products | |
16 | ### derived from this software without specific prior written permission. | |
17 | ### | |
18 | ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
19 | ### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | ### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | ### DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
22 | ### DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | ### LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | ### ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | ### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
27 | ### SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | ### | |
29 | CMAKE=$(shell which cmake) | |
30 | CTEST=$(shell which ctest) | |
31 | PREFIX?=/usr | |
32 | ARCH=$(shell uname -m) | |
33 | ||
34 | default: release | |
35 | .PHONY: default | |
36 | ||
37 | all: release debug | |
38 | .PHONY: all | |
39 | ||
40 | clean: cleanrelease | |
41 | .PHONY: clean | |
42 | ||
43 | test: testrelease | |
44 | .PHONY: test | |
45 | ||
46 | install: installrelease | |
47 | .PHONY: install | |
48 | ||
49 | release: | |
50 | @mkdir -p Release | |
51 | @cd Release && $(CMAKE) -DARCH=$(ARCH) -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(PREFIX) .. | |
52 | @cd Release && $(MAKE) | |
53 | @echo "Release Build complete" | |
54 | .PHONY: release | |
55 | ||
56 | debug: | |
57 | @mkdir -p Debug | |
58 | @cd Debug && $(CMAKE) -DARCH=$(ARCH) -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$(PREFIX) .. | |
59 | @cd Debug && $(MAKE) | |
60 | @echo "Debug Build complete" | |
61 | .PHONY: debug | |
62 | ||
63 | cleanrelease: | |
64 | @if test -d Release; then cd Release && $(MAKE) clean; fi | |
65 | @if test -d Release; then cd Release && rm -f CMakeCache.txt; fi | |
66 | @if test -d Release; then echo "Release Cleaning complete"; else echo "Nothing to clean"; fi | |
67 | .PHONY: cleanrelease | |
68 | ||
69 | cleandebug: | |
70 | @if test -d Debug; then cd Debug && $(MAKE) clean; fi | |
71 | @if test -d Debug; then cd Debug && rm -f CMakeCache.txt; fi | |
72 | @if test -d Debug; then echo "Debug Cleaning complete"; else echo "Nothing to clean"; fi | |
73 | .PHONY: cleandebug | |
74 | ||
75 | testrelease: release | |
76 | cd Release && $(CTEST) | |
77 | .PHONY: testrelease | |
78 | ||
79 | testdebug: debug | |
80 | cd Debug && $(CTEST) | |
81 | .PHONY: testdebug | |
82 | ||
83 | installrelease: release | |
84 | @cd Release && $(CMAKE) -DARCH=$(ARCH) -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(PREFIX) .. | |
85 | @cd Release && $(MAKE) install | |
86 | @echo "Release installation complete" | |
87 | .PHONY: installrelease | |
88 | ||
89 | installdebug: debug | |
90 | @cd Debug && $(CMAKE) -DARCH=$(ARCH) -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$(PREFIX) .. | |
91 | @cd Debug && $(MAKE) install | |
92 | @echo "Debug installation complete" | |
93 | .PHONY: installdebug | |
94 |
0 | Introduction to Hotpatch | |
1 | ========================= | |
2 | Hotpatch is a library that can be used to dynamically load a shared library | |
3 | (.so) file on Linux from one process into another already running process, | |
4 | without affecting the execution of the target process. The API is a C API, but | |
5 | also supported in C++. | |
6 | ||
7 | The current version is 0.2. | |
8 | ||
9 | The limitations, directions on how to use, and possible uses of hotpatch will be | |
10 | explained in this document. | |
11 | ||
12 | The main idea of hotpatch stems from the fact that in Linux, it is not easy to | |
13 | load a library into another already running process. In Windows, there is an API | |
14 | called CreateRemoteThread() that can load a library into another process very | |
15 | easily with a couple of API calls. Hotpatch makes this functionality available | |
16 | to Linux users and developers, with a single API call. Unlike other available | |
17 | injection libraries, hotpatch restores the execution of the process to its | |
18 | original state. | |
19 | ||
20 | The user can do the following with hotpatch: | |
21 | - load his/her own .so file into an already running process | |
22 | - invoke a custom symbol/function in that .so file | |
23 | - pass arguments to that function as long as it is serialized to the form of a | |
24 | byte buffer and length of the buffer. This shall be explained more later. | |
25 | ||
26 | Hotpatch is available as an API with a header file called "hotpatch.h" and a | |
27 | .so file called "libhotpatch.so", and also a commandline application called | |
28 | "hotpatcher" which can inject .so files into processes via the commandline | |
29 | itself. Hotpatch also comes with a test .so called "libhotpatchtest.so" | |
30 | which can be used via the commandline "hotpatcher" application to test out | |
31 | the working of hotpatch on any system. The "libhotpatchtest.so" has a symbol | |
32 | "mysym" that can be invoked, and it writes to the "/tmp/hotpatchtest.log" file | |
33 | with the timestamp at which the .so file was injected and anything else. | |
34 | ||
35 | Limitations | |
36 | ============ | |
37 | NOTE: Currently if hotpatch is compiled in 64-bit mode, it can inject libraries | |
38 | only in 64-bit processes, and if compiled in 32-bit mode can inject libraries | |
39 | only in 32-bit processes. It cannot inject from a 64-bit to a 32-bit process or | |
40 | from a 32-bit to a 64-bit process. | |
41 | ||
42 | There are some limitations, the main being that the user can inject a library | |
43 | .so file only in a process on which the user has privileges over. For example, | |
44 | as the root user, hotpatch can inject libraries into any process, but as a | |
45 | regular non-root user, hotpatch can inject libraries into only those processes | |
46 | that hotpatch has access to, i.e. the user's processes and any other via sudo | |
47 | privileges. | |
48 | ||
49 | The other limitation is that if the user needs to compile his shared library | |
50 | with the linker options "-fPIC -nostartfiles" so that hotpatch can reliably load | |
51 | the .so file. | |
52 | ||
53 | Another limitation is that injection for a particular .so file can happen only | |
54 | once in the target process. Each library that is injected can be injected only | |
55 | once into the target process. | |
56 | ||
57 | Usage: API | |
58 | =========== | |
59 | ||
60 | The "hotpatch.h" header file needs to be included by the user. There are 3 main | |
61 | API calls that matter. Each of them have to be called in the order as shown | |
62 | below in the sample program. | |
63 | ||
64 | - hotpatch_t *hotpatch_create(pid_t pid, int verbose); | |
65 | ||
66 | This function takes a PID of the target process, and the verbosity level | |
67 | (between 0 to 6), and returns an opaque object which contains further intimate | |
68 | details about the process such as current library mappings, and locations of the | |
69 | important functions needed for hotpatch to do its work. | |
70 | ||
71 | - int hotpatch_inject_library(hotpatch_t *hp, | |
72 | const char *sofile, | |
73 | const char *symbol, | |
74 | const unsigned char *data, | |
75 | size_t datalen, | |
76 | uintptr_t *out_addr, | |
77 | uintptr_t *out_result); | |
78 | ||
79 | This function takes the newly created hotpatch object, along with a path to the | |
80 | shared library in the variable "sofile", the optional function "symbol" to invoke, | |
81 | along with the serialized arguments to the function provided in "data" and | |
82 | "datalen" which are also optional. The return address of where the library was | |
83 | loaded is returned in "out_addr" and the return value of the invocation of | |
84 | "symbol" is returned in "out_result". On success this returns 0 and on failure | |
85 | returns -1. | |
86 | ||
87 | The verbosity levels can be adjusted accordingly from 0 to 6 to see debugging | |
88 | information for investigating errors. | |
89 | ||
90 | The usefulness of the "data" and "datalen" parameters is extremely high. Suppose | |
91 | the user has a custom function they want to invoke, and the arguments of the | |
92 | function is a big struct or a class. The user can then write a wrapper function | |
93 | that takes a serialized buffer of this struct/class along with the length of the | |
94 | buffer and invoke that wrapper function. This wrapper function can then | |
95 | deserialize this buffer into the struct/class as needed and call the actual | |
96 | function that the user really wanted to invoke. This functionality is only | |
97 | available by the API and not by the "hotpatcher" executable. | |
98 | ||
99 | - void hotpatch_destroy(hotpatch_t *hp); | |
100 | ||
101 | This function cleans up memory and resources used by the hotpatch opaque object. | |
102 | ||
103 | Sample Program | |
104 | ============== | |
105 | ||
106 | #include <hotpatch.h> | |
107 | ||
108 | int main(int argc, char **argv) | |
109 | { | |
110 | pid_t pid = argc > 1 ? atoi(argv[1]) : 0; | |
111 | hotpatch_t *hp = hotpatch_create(pid, 1); | |
112 | if (hp) { | |
113 | unsigned char *data = (unsigned char *)"my custom serialized data"; | |
114 | size_t datalen = strlen((char *)data) + 1; | |
115 | uintptr_t result1, result2; | |
116 | hotpatch_inject_library(hp, "libhotpatchtest.so", "mysym", | |
117 | data, datalen, &result1, &result2); | |
118 | hotpatch_destroy(hp); | |
119 | } | |
120 | return 0; | |
121 | } | |
122 | ||
123 | Usage: Hotpatcher | |
124 | ================== | |
125 | ||
126 | The commandline "hotpatcher" can be executed with the "-h" option to see the | |
127 | various options that are supported. | |
128 | ||
129 | A sample execution of "hotpatcher" into the current running shell can be done as | |
130 | below: | |
131 | ||
132 | Let's say the library libhotpatchtest.so is in the current directory. | |
133 | ||
134 | bash> ./hotpatcher -l ./libhotpatchtest.so -s mysym -v1 $$ | |
135 | ||
136 | On success the "/tmp/hotpatchtest.log" file can be checked if it has the | |
137 | timestamp of the injection. | |
138 | ||
139 | Uses of Hotpatch | |
140 | ================= | |
141 | Most uses of hotpatch are related to custom modifications of processes for which | |
142 | the users do not have source code available. | |
143 | ||
144 | - System administrators can use hotpatch to inject their own custom libraries in | |
145 | already running processes and change behavior as per requirement. Some such | |
146 | behavior could be adding a library that creates a thread and heartbeats to a | |
147 | monitoring system. | |
148 | ||
149 | - Many software applications, that are not mission critical, are not built with | |
150 | mechanisms to update their software without having to stop the application and | |
151 | restarting it. Hotpatch can help modify applications to restart and do other | |
152 | fancy tricks without losing the PID and the other states such as file handles of | |
153 | the applications that might be very useful or too risky to let go. | |
154 | ||
155 | - Users can inject a library and then set up RPC service calls for the target | |
156 | application without changing any code. | |
157 | ||
158 | - Users can inject a library and with import table modifications can instrument | |
159 | the target application for things like profiling, reverse engineering and also | |
160 | debugging. This is useful as it does not necessarily need the application to be | |
161 | recompiled and performance numbers can be extracted. The code to do import table | |
162 | modifications is currently outside the scope of hotpatch. | |
163 | ||
164 | - Users can create threads in other processes and make them work like a cluster | |
165 | of processes that they control. | |
166 | ||
167 | - Users can modify another application and make it perform better by doing | |
168 | tricks in the injected code. | |
169 | ||
170 | License & Copyright | |
171 | =================== | |
172 | ||
173 | The license/copyright can be found in the COPYRIGHT document in the source code. | |
174 | ||
175 | ==THE END== |
0 | ||
1 | [------------------------------------------------------------------------] | |
2 | [-- Uninformed Research -- informative information for the uninformed. --] | |
3 | [------------------------------------------------------------------------] | |
4 | [-- Genre : Development --] | |
5 | [-- Name : needle --] | |
6 | [-- Desc : Linux x86 run-time process manipulation --] | |
7 | [-- Url : http://www.uninformed.org/ --] | |
8 | [-- Use : EVILNESS --] | |
9 | [------------------------------------------------------------------------] | |
10 | [-- Author : skape ([email protected]) --] | |
11 | [-- Date : 01/19/2003 --] | |
12 | [------------------------------------------------------------------------] | |
13 | ||
14 | [-- Table of contents: --] | |
15 | ||
16 | 1) Overview | |
17 | 1.1) Topics | |
18 | 1.2) Techniques | |
19 | 1.3) Execution Diversion | |
20 | 2) Memory Allocation | |
21 | 3) Memory Management | |
22 | 4) Library Injection | |
23 | 5) Code Injection | |
24 | 5.1) Forking | |
25 | 5.2) Threading | |
26 | 5.3) Function Trampolines | |
27 | 6) Conclusion | |
28 | 7) References | |
29 | ||
30 | [-- 1) Overview --] | |
31 | ||
32 | So, you want to be evil and modify the image of an executing | |
33 | process? Well, perhaps you've come to the right place. This | |
34 | document deals strictly with some methodologies used to to | |
35 | alter process images under Linux. If you're curious about how | |
36 | to do something similar to the things listed in this document in | |
37 | Windows, please read the ``References`` section. | |
38 | ||
39 | [-- 1.1) Topics --] | |
40 | ||
41 | The following concepts will be discussed in this document as they | |
42 | relate to run-time process manipulation: | |
43 | ||
44 | * Memory Allocation | |
45 | ||
46 | The use of being able to allocate and deallocate memory in a | |
47 | running process from another process has awesome power for | |
48 | such scenarios as execution diversion (the act of diverting | |
49 | a processes execution to your own code), data hiding (the act | |
50 | of hiding data in a process image), and even, in some cases | |
51 | allocating dynamic structures/strings for use within a process | |
52 | for its normal execution. These aren't the only uses, but | |
53 | they're all I could think of right now :). See the | |
54 | ``Memory Allocation`` section for details. | |
55 | ||
56 | * Memory Management | |
57 | ||
58 | The ability to copy arbitrary memory from one process to another | |
59 | at arbitrary addresses allows for flexible manipulation of | |
60 | a given processes memory image. This can be applied to copy | |
61 | strings, functions, integers, everything. See the ``Memory | |
62 | Management`` section for details. | |
63 | ||
64 | * Library Injection | |
65 | ||
66 | The ability to inject arbitrary shared objects into a process | |
67 | allows for getting at symbols that an executable would not | |
68 | normally have as well as allowing an evil-doer such as yourself | |
69 | to inject arbitrary PIC that can reference symbols in | |
70 | an executable without getting in trouble. This alone is | |
71 | extremely powerful. See the ``Library Injection`` section for | |
72 | details. | |
73 | ||
74 | * Code Injection | |
75 | ||
76 | Well, when you get down to it, you just want to execute code | |
77 | in a given process that you define and you want to control | |
78 | when it gets executed. Lucky for you, this is possible AND | |
79 | just as powerful as you'd hoped. This document will cover | |
80 | three types of code injection: | |
81 | ||
82 | 1) Forking | |
83 | ||
84 | The act of causing a process to create a child image | |
85 | and execute arbitrary code. | |
86 | ||
87 | 2) Threading | |
88 | ||
89 | The act of causing a process to create a thread | |
90 | that executes an arbitrary function. | |
91 | ||
92 | 3) Function Trampolines | |
93 | ||
94 | The act of causing a call to a given function to | |
95 | 'trampoline' to arbitrary code and then 'jump' back to | |
96 | the original function. | |
97 | ||
98 | [-- 1.2) Techniques --] | |
99 | ||
100 | As of this document I'm aware of two plausible techniques for | |
101 | altering the image of an executing process: | |
102 | ||
103 | * ptrace | |
104 | ||
105 | Likely the most obvious technique, the ptrace (process trace) API | |
106 | allows for altering of memory, reading of memory, looking and | |
107 | setting registers, as well as single-stepping through a process. | |
108 | The application for these things as it pertains to this document | |
109 | should be obvious. If not, or if you're curious, read the | |
110 | ``References`` section for more details on ptrace. | |
111 | ||
112 | * /proc/[pid]/mem | |
113 | ||
114 | This technique is more limited in the amount of things it can | |
115 | do but is by no means something that should be cast aside. | |
116 | With the ability to read/write a given process's image, one | |
117 | could easily modify the image to do ``Code Injection``. Doing | |
118 | things like memory allocation, management, and library | |
119 | injection via this method are quote a means harder but *NOT* | |
120 | impossible. They would take a decent amount of hackery though. | |
121 | (Theoretical, not proven yet, by me at least.) | |
122 | ||
123 | [-- 1.3) Execution Diversion --] | |
124 | ||
125 | In order to do most of the techniques in this document we need to | |
126 | divert the execution of a running process to code that we control. | |
127 | This presents a few problems off the bat. Where can we safely put | |
128 | the code that we want executed? How could we possibly change the | |
129 | course of execution? How do we restore execution once our code | |
130 | has finished? Well, thankfully, there are answers to these | |
131 | questions, and they're pretty easy to answer. Let's start with | |
132 | the first one. | |
133 | ||
134 | * Where can we safely put the code that we want executed? | |
135 | ||
136 | Well to answer this question you need to have a slight | |
137 | understanding of how the process is laid out and how the flow of | |
138 | execution goes. The basic tools you need in your knowledge base | |
139 | are that executables have symbols, symbols map to vma's that are | |
140 | used to tell the vm where symbols should be located in memory. | |
141 | This is used not only for functions, but also for global variables. | |
142 | With that said, we can tell where code will be in an executable | |
143 | based off processing the ELF image associated with the process. | |
144 | Example: | |
145 | ||
146 | root@rd-linux:~# objdump --syms ./ownme | grep main | |
147 | 08048450 g F .text 00000082 main | |
148 | ||
149 | This tells us that main will be found at 0x08048450 when the | |
150 | program is executing. But what good does this do us? A lot. | |
151 | Considering the main function is the 'gateway' to normal code | |
152 | execution, it's an excellent place to use as a dumping zone for | |
153 | arbitrary code. There are some restrictions, however. The code | |
154 | has some size restrictions. Here's the preamble and some code | |
155 | from main in ./ownme: | |
156 | ||
157 | root@rd-linux:~# objdump --section=.text \ | |
158 | --start-address=0x08048450 --stop-address=0x080484d4 \ | |
159 | -d ./ownme | |
160 | ||
161 | ./ownme: file format elf32-i386 | |
162 | ||
163 | Disassembly of section .text: | |
164 | ||
165 | 08048450 <main>: | |
166 | 8048450: 55 push %ebp | |
167 | 8048451: 89 e5 mov %esp,%ebp | |
168 | 8048453: 83 ec 08 sub $0x8,%esp | |
169 | 8048456: 90 nop | |
170 | 8048457: 90 nop | |
171 | 8048458: 90 nop | |
172 | ... | |
173 | 80484d0: c9 leave | |
174 | 80484d1: c3 ret | |
175 | ||
176 | Granted, main isn't always the entry point, but it's easy to find | |
177 | out what is by the e_entry attribute of the elf header. Now, the | |
178 | reason I say main is a great place to use as a dump zone is because | |
179 | it holds code that will _never be accessed again_. This is the key. | |
180 | There are lots of other places you could use as a dumpzone. For | |
181 | instance, if the application contains a large helper banner, you | |
182 | could put code over the help banner considering the banner wont be | |
183 | printed ever again once the program is executing. Use your | |
184 | imagination, you'll think of lots more. 'main' is the most | |
185 | generic method, since it's guaranteed in every application. | |
186 | ||
187 | Well, now we know where we can safely put code to be executed, but | |
188 | how do we actually execute it? | |
189 | ||
190 | * How could we possibly change the course of execution? | |
191 | ||
192 | In order to change the course of execution in a process you need | |
193 | some working knowledge of ptrace and how the vm traverses an | |
194 | executable. Assuming you have both, read on. On x86 there | |
195 | is a vm register used to hold the vma of the NEXT instruction. | |
196 | Once an instruction finishes, the vm processes the instruction | |
197 | at eip (the vm register) and increments eip by the size of the | |
198 | current instruction. There are some instructions, such as jmp | |
199 | and call which are themselves execution diversion functions | |
200 | that cause eip to be changed to the address specified in the | |
201 | operand. We use this same principal when it comes to changing | |
202 | our course of execution to what we want. | |
203 | ||
204 | Now, let's say that we theoretically put some of our own code | |
205 | at 0x08048450 (the address of main above) using the functionality | |
206 | from the ``Memory Management`` section. In order to have | |
207 | our code get executed (since it would normally never get executed) | |
208 | we use ptrace's PTRACE_SETREGS and PTRACE_GETREGS functionality. | |
209 | These two methods allow a third party process to obtain the | |
210 | registers and set the registers of another process. These | |
211 | registers include eip. In order to change the execution we | |
212 | perform the following steps: | |
213 | ||
214 | 1) call PTRACE_GETREGS to obtain the 'current' set of | |
215 | registers. | |
216 | ||
217 | 2) set eip in the returned set of registers to | |
218 | 0x08048450 (the address of our code). | |
219 | ||
220 | 3) call PTRACE_SETREGS with our modified structure. | |
221 | ||
222 | 4) continue the course of execution. | |
223 | ||
224 | We've now successfully caused our code to be executed, but there's | |
225 | a problem. We injected a small chunk of code that we wanted | |
226 | to be run, but then we wanted the process to return to normal | |
227 | execution. That brings us to the next question. | |
228 | ||
229 | * How do we restore execution once our code has finished? | |
230 | ||
231 | Glad you asked, because this is the most important part. In order | |
232 | to restore execution we need a to modify our injected code just | |
233 | a bit in order to make it easy for us to restore execution. We | |
234 | do this by adding an instruction near the end: | |
235 | ||
236 | int $0x3 | |
237 | ||
238 | This is on Linux (and Windows) to signal an exception or breakpoint | |
239 | to the active debugger. In the case of Linux, it sends a SIGTRAP, | |
240 | which, if the process is being traced will be caught by wait(). | |
241 | ||
242 | Okay, so we've modified our code and let's say it looks something | |
243 | like this: | |
244 | ||
245 | nop | |
246 | nop | |
247 | nop | |
248 | nop | |
249 | nop | |
250 | nop | |
251 | ||
252 | mov $0x1, %eax | |
253 | int $0x3 | |
254 | nop | |
255 | ||
256 | The code is setup with a 6 byte nop pad at the top to make our | |
257 | changing of eip more cleaner (and safer) due to the way the vm | |
258 | reacts to our execution diversion. The movement of 1 into | |
259 | eax is just an example of our arbitrary code. The int $0x3 | |
260 | alerts our attached debugger (ptrace) and the nop is for padding | |
261 | so we can see when we hit the end of our code. | |
262 | ||
263 | Okay, that's a lot of stuff. Let's walk through our modified | |
264 | process of execution now. This assumes you've already injected | |
265 | your code at main (0x08048450): | |
266 | ||
267 | 1) call PTRACE_GETREGS to obtain the 'current' set of | |
268 | registers | |
269 | ||
270 | 2) save these registers in another structure. This is used | |
271 | for restoration. | |
272 | ||
273 | 3) set eip in the returned set of registers to | |
274 | 0x08048450 (the address of our code). | |
275 | ||
276 | 4) call PTRACE_SETREGS with the modified structure. | |
277 | ||
278 | 5) continue execution, but watch for signals with the wait() | |
279 | function. If the wait function returns a signal | |
280 | that is a stop signal: | |
281 | ||
282 | a) call PTRACE_GETREGS and get the current set of registers | |
283 | ||
284 | b) if eip is equal to the size of your injected code - 1 | |
285 | (the location of the nop at the end), you know you've | |
286 | reached the end of your code. go to step 6 at this | |
287 | point. | |
288 | ||
289 | c) otherwise, continue executing. | |
290 | ||
291 | 6) at this point your code has finished. call PTRACE_SETREGS | |
292 | with the saved structure from step 2 and you're finished. | |
293 | you've successfully diverted and reverted execution. | |
294 | ||
295 | That was a mouthful, but it's very important that it's understood. | |
296 | All of the topics in this document emplore this underlying | |
297 | logic to perform their actions. Each one has a 'stub' assembly | |
298 | function that gets injected into a process at main to be executed. | |
299 | This code is meant to be small due to the fact that there are | |
300 | potential size issues. | |
301 | ||
302 | Oh, and another thing, you have full control over every register | |
303 | in this scenario because the registers are restored with | |
304 | PTRACE_SETREGS before the 'normal' execution continues. | |
305 | ||
306 | [-- 2) Memory allocation --] | |
307 | ||
308 | Memory allocation is one of the key features in this documented | |
309 | as all of the sub topics in Execution Diversion are dependant | |
310 | on its functionailty. Memory allocation allows for dynamic | |
311 | memory allocation in another process (duh). The most applicable | |
312 | scenario with regards to this document for such a thing are the | |
313 | storage of arbitrary code in memory without size limitations. | |
314 | This allows one to inject a very large function for execution | |
315 | without having fear that they will overrun into another function | |
316 | or harmful spot. | |
317 | ||
318 | Memory allocation is relatively simple, but understanding how to | |
319 | get from a to b requires a bit of explaining. The first thing we | |
320 | need to do is figure out where malloc will be in a given process | |
321 | image so that we may call into it. If we can figure that out | |
322 | we should be home free considering what we know from section 1.3. | |
323 | ||
324 | Realize that all these steps below can and are easily automated, | |
325 | but for sake of knowing, here they are: | |
326 | ||
327 | 1) Where could malloc possibly be? Well, let's see what | |
328 | our choices are: | |
329 | ||
330 | root@rd-linux:~# ldd ./ownme | |
331 | libc.so.6 => /lib/libc.so.6 (0x40016000) | |
332 | /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) | |
333 | ||
334 | root@rd-linux:~# objdump --dynamic-syms --section=.text \ | |
335 | /lib/libc.so.6 | grep malloc | |
336 | 0006df90 w DF .text 00000235 GLIBC_2.0 malloc | |
337 | root@rd-linux:~# objdump --dynamic-syms --section=.text \ | |
338 | /lib/ld-linux.so.2 | grep malloc | |
339 | 0000c8f0 w DF .text 000000db GLIBC_2.0 malloc | |
340 | ||
341 | Alright, so we've got malloc in both libc and ld-linux. We | |
342 | could probably use either but what about programs that don't | |
343 | use libc? In order to be the most flexible, we should use | |
344 | ld-linux. This also has a positive side effect which is | |
345 | that every elf binary has an 'interpreter', and, it just | |
346 | so happens to ld-linux is that interpreter. | |
347 | ||
348 | 2) Alright, so we know the vma of malloc is at 0x0000c8f0, | |
349 | but that doesn't exactly look like a valid vma. That's | |
350 | because it's not. It's an offset. The actual vma | |
351 | can be calculated by adding the base address from ldd | |
352 | for ld-linux (0x40000000) to the offset (0x0000c8f0) | |
353 | which, in turn produces the full vma 0x4000c8f0. Now | |
354 | we know exactly where malloc is. | |
355 | ||
356 | 3) Cool, so we know where malloc is, now all we need to do is | |
357 | divert execution to some code that calls it and revert back. | |
358 | We also need the return address from malloc though so we | |
359 | know where our newly allocated buffer is at. Fortunately, | |
360 | this is quite easy with PTRACE_GETREGS. eax will hold the | |
361 | return value (cdecl). The code is pretty simple and, | |
362 | considering we control all the registers, we can use | |
363 | them to pass arguments, such as size, into our code | |
364 | at the time of diversion. Here's some code that will, | |
365 | when diverted to with the correctly initialized registers, | |
366 | call malloc and interrupt into the debugger: | |
367 | ||
368 | nop # nop pads | |
369 | nop | |
370 | nop | |
371 | nop | |
372 | nop | |
373 | nop | |
374 | ||
375 | push %ebx # push the size to allocate onto the stack | |
376 | call *%eax # call malloc | |
377 | add $0x4, %esp # restore the stack | |
378 | int $0x3 # breakpoint | |
379 | nop | |
380 | ||
381 | The above code expects the 'size' parameter in ebx and the | |
382 | address of malloc in eax. | |
383 | ||
384 | 4) Alrighty, so now we've executed our code and we're ready | |
385 | to restore the process to normal execution, but wait, | |
386 | we need the address malloc returned. We simply use | |
387 | PTRACE_GETREGS and save eax and we've successfully | |
388 | allocated memory in another process, and we have the | |
389 | address to prove it. | |
390 | ||
391 | The same steps above can be used for deallocating memory, simply | |
392 | s/malloc/free/g and you're set :). | |
393 | ||
394 | [-- 3) Memory management --] | |
395 | ||
396 | I'm only going to briefly cover the concept of copying memory | |
397 | from one process to another as it's sort of out of the scope | |
398 | of this document. If you're more curious, read about memgrep | |
399 | in the ``References`` section. | |
400 | ||
401 | Copying memory from one process to another simply entails the | |
402 | use of PTRACE_POKEDATA which allows for writing 4 bytes of data | |
403 | to a given address inside a process. Not much more is needed | |
404 | to be known from that point on :). | |
405 | ||
406 | [-- 4) Library Injection --] | |
407 | ||
408 | Library injection is very powerful when it comes to using | |
409 | functionality inside a running process that it wasn't meant to | |
410 | be doing. One of the more obvious applications is that of loading | |
411 | a personally developed shared object into a running executable. | |
412 | ||
413 | This one was fun to figure out, so I'll just kind of walk you | |
414 | through the process I took. | |
415 | ||
416 | First thing's first, we need to figure out how to load a library | |
417 | without the binary being linked to libdl. libdl is what provides | |
418 | functions like dlopen(), dlsym(), and dlclose(). The problem is | |
419 | that executables don't link to this library by default. That means | |
420 | we can't do our magic technique of figuring out where dlopen will | |
421 | be in memory because, well, it isn't guaranteed to be there. | |
422 | ||
423 | There's still hope though. dl* functions are mainly just stubs | |
424 | that make calling the underlying API easier. Kind of like how | |
425 | libc makes calling syscalls easier. Since these are just wrappers, | |
426 | there have to be implementers, and indeed, there are. Check this | |
427 | out: | |
428 | ||
429 | root@rd-linux:~# objdump --dynamic-syms /lib/libc.so.6 | \ | |
430 | grep _dl_ | egrep "open|close|sym" | |
431 | 000f7d10 g DF .text 000001ad GLIBC_2.2 _dl_vsym | |
432 | 000f6f10 g DF .text 000006b8 GLIBC_2.0 _dl_close | |
433 | 000f6d80 g DF .text 00000190 GLIBC_2.0 _dl_open | |
434 | 000f7c00 g DF .text 0000010d GLIBC_2.2 _dl_sym | |
435 | ||
436 | Well, isn't it our lucky day? libc.so.6 has _dl_open, _dl_sym, and | |
437 | _dl_close. These look amazingly similar to their dl* wrappers. | |
438 | In fact, they're almost exactly the same. Compare the prototypes: | |
439 | ||
440 | extern void *dlopen (const char *file, int mode); | |
441 | extern void *dlsym (void *handle, const char *name) | |
442 | extern int dlclose (void *handle); | |
443 | ||
444 | To: | |
445 | ||
446 | void *_dl_open (const char *file, int mode, const void *caller); | |
447 | void *_dl_sym (void *handle, const char *name, void *who); | |
448 | void _dl_close (void *_map); | |
449 | ||
450 | Pretty much the same right? Looks very promising. So here's what | |
451 | we know as of now: | |
452 | ||
453 | * We know where the _dl_* symbols will be at in the processes | |
454 | virtual memory. (We can calculate it the same way we did | |
455 | malloc) | |
456 | * We know the prototypes. | |
457 | ||
458 | One thing we don't know is how the functions expect their arguments. | |
459 | One would think they'd be stack based, right? Well, not so. They | |
460 | seem to use a variation of fastcall (like syscalls). Here's a | |
461 | short dump of _dl_open: | |
462 | ||
463 | 000f6d80 <.text+0xdde00> (_dl_open): | |
464 | f6d80: 55 push %ebp | |
465 | f6d81: 89 e5 mov %esp,%ebp | |
466 | f6d83: 83 ec 2c sub $0x2c,%esp | |
467 | f6d86: 57 push %edi | |
468 | f6d87: 56 push %esi | |
469 | f6d88: 53 push %ebx | |
470 | f6d89: e8 00 00 00 00 call 0xf6d8e | |
471 | f6d8e: 5b pop %ebx | |
472 | f6d8f: 81 c3 ba 10 02 00 add $0x210ba,%ebx | |
473 | f6d95: 89 c7 mov %eax,%edi | |
474 | f6d97: 89 d6 mov %edx,%esi | |
475 | f6d99: 89 4d e4 mov %ecx,0xffffffe4(%ebp) | |
476 | f6d9c: f7 c6 03 00 00 00 test $0x3,%esi | |
477 | f6da2: 75 1c jne 0xf6dc0 | |
478 | f6da4: 83 c4 f4 add $0xfffffff4,%esp | |
479 | ||
480 | Looks pretty normal for the most part right? Well, up until 0xf6d95 | |
481 | at least. It's quite odd that it's referencing eax, edx, and ecx | |
482 | which have not been initialized in the context of _dl_open, and then | |
483 | using them and operating on them later in the function. Very strange | |
484 | to say the least. Unless, of course, the arguments are being passed | |
485 | in registers instead of via the stack. Let's look at the source | |
486 | code for _dl_open. | |
487 | ||
488 | void * | |
489 | internal_function | |
490 | _dl_open (const char *file, int mode, const void *caller) | |
491 | { | |
492 | struct dl_open_args args; | |
493 | const char *objname; | |
494 | const char *errstring; | |
495 | int errcode; | |
496 | ||
497 | if ((mode & RTLD_BINDING_MASK) == 0) | |
498 | /* One of the flags must be set. */ | |
499 | _dl_signal_error (EINVAL, file, NULL, | |
500 | N_("invalid mode for dlopen()")); | |
501 | ||
502 | .... | |
503 | ||
504 | } | |
505 | ||
506 | Okay, so we see roughly the first thing it does is do a bitwise and | |
507 | on the mode passed in to make sure it's valid. It does the and | |
508 | with 0x00000003 (RTLD_BINDING_MASK). Do we see any bitwise ands | |
509 | with 0x3 in the disasm? We sure do. At 0xf6d9c a bitwise and is | |
510 | performed between $0x3 and esi. So esi must be where our mode is | |
511 | stored, right? Yes. Let's see where esi is set. Looks like it | |
512 | gets set at 0xf6d97 from edx. Okay, so maybe edx originally | |
513 | contained our mode. Where does edx get set? No where in _dl_open. | |
514 | That means the mode must have been passed in a register, and not on | |
515 | the stack. | |
516 | ||
517 | If you do some more research, you determine that the arguments | |
518 | are passed as such: | |
519 | ||
520 | eax = library name (ex: /lib/libc.so.6) | |
521 | ecx = caller (ex: ./ownme) | |
522 | edx = mode (ex: RTLD_NOW | 0x80000000) | |
523 | ||
524 | Alright, so we know how arguments are passed AND we know the address | |
525 | to call when we want to load a library. From this point things | |
526 | should be pretty obvious. | |
527 | ||
528 | All one need do is allocate space for the library name and the | |
529 | caller in the image using the ``Memory Allocation`` technique. | |
530 | Then copy the library and image using the ``Memory Management`` | |
531 | technique. Then, finally, execute the stub code that loads the | |
532 | library. That code would look something like this: | |
533 | ||
534 | nop # nop pads | |
535 | nop | |
536 | nop | |
537 | nop | |
538 | nop | |
539 | nop | |
540 | ||
541 | call *%edi # call _dl_open | |
542 | int $0x3 # breakpoint | |
543 | nop | |
544 | ||
545 | This code expects the arguments to already be initialized in the | |
546 | proper registers from what we determine above and it expects | |
547 | _dl_open's vma to be in edi. | |
548 | ||
549 | Welp, we've successfully injected a shared object into another | |
550 | processes image. What you do from here is up to the desired | |
551 | outcome. Calling _dl_sym and _dl_close uses the same code as above, | |
552 | but their arguments are as follows: | |
553 | ||
554 | _dl_sym expects: | |
555 | ||
556 | eax = library handle opened by _dl_open | |
557 | edx = symbol name (ex: 'pthread_create') | |
558 | ||
559 | _dl_close expects: | |
560 | ||
561 | eax = library handle opened by _dl_open | |
562 | ||
563 | [-- 5) Code Injection --] | |
564 | ||
565 | I must say we're getting rather hardcore, we can allocate memory, | |
566 | copy memory and load shared objects into arbitrary processes. | |
567 | What more could we possibly want? How about some arbitrary, | |
568 | controlled code execution that isn't limited by size? Sounds | |
569 | spiffy! | |
570 | ||
571 | [-- 5.1) Forking --] | |
572 | ||
573 | Let's say we want to fork a child process inside the context of | |
574 | another process and have it execute an arbitrary function | |
575 | that we've allocated and stored in the processes memory image | |
576 | via the ``Memory Allocation`` and ``Memory Management`` methods. | |
577 | Doing the fork is as simple as writing up some code that will | |
578 | use ``Execution Diversion`` to fork the child and return control | |
579 | to the parent as if nothing happened. An example of forking | |
580 | and executing a supplied function is as follows: | |
581 | ||
582 | nop # nop pads | |
583 | nop | |
584 | nop | |
585 | nop | |
586 | nop | |
587 | nop | |
588 | ||
589 | mov $0x2, %eax # fork syscall | |
590 | int $0x80 # interrupt | |
591 | cmp $0x00, %eax # is the pid stored in eax 0? if so, | |
592 | # we're the child | |
593 | jne fork_finished # since eax wasn't zero, it means we're the | |
594 | # parent. jmp to finished. | |
595 | push %ebx # since we're the child, we push the start | |
596 | # addr | |
597 | call *%edi # then we call the function | |
598 | mov $0x1, %eax # exit the child process | |
599 | int $0x80 # interrupt | |
600 | fork_finished: | |
601 | int $0x3 # we're the parent, we breakpoint. | |
602 | nop | |
603 | ||
604 | This code expects the following registers to be set: | |
605 | ||
606 | ebx = the argument to be passed to the function | |
607 | edi = the vma of the function call in the context of the child. | |
608 | ||
609 | Forking is really as simple as that. Now, one side effect is that | |
610 | if the daemon does not expect fork children (ie, it doesn't call | |
611 | wait()) then your child process will show up as defunct when it | |
612 | exits due to not being cleaned up properly. There are ways around | |
613 | this, though. You could use the ``Execution Diversion`` technique | |
614 | to perform cleanup of exitted children after for the process. | |
615 | ||
616 | [-- 5.2) Threading --] | |
617 | ||
618 | Similar to forking, but different by the fact that a thread runs | |
619 | in the context of the caller and shares memory, threading allows | |
620 | for pretty much the same things that forking does. There are | |
621 | some risks with threading though. For instance, it is _NOT_ safe | |
622 | to create a thread in a process that does not natural thread. This | |
623 | is for multiple reasons -- the most important being that the | |
624 | threading environment is setup at load time (in the case of | |
625 | pthreads). If Linux didn't use some ghetto application-level | |
626 | threading architecture, things wouldn't be so bad. | |
627 | ||
628 | If you really do want to take the risk of creating a thread, | |
629 | the process would be something like this: | |
630 | ||
631 | 1) Inject libpthread.so into the process (``Library Injection``) | |
632 | 2) Find pthread_create's vma in the process | |
633 | (``Library Injection``) | |
634 | 3) Allocate and copy user defined code (``Memory Allocation``) | |
635 | 4) Perform ``Execution Diversion`` on the stub code to | |
636 | create the thread. An example of such code is: | |
637 | ||
638 | nop # nop pads | |
639 | nop | |
640 | nop | |
641 | nop | |
642 | nop | |
643 | nop | |
644 | ||
645 | sub $0x4, %esp # space for the id | |
646 | mov %esp, %ebp # store esp in ebp for pushing | |
647 | push %ebx # push argument | |
648 | push %eax # push function | |
649 | push $0x0 # no attributes | |
650 | push %ebp # push addr to store thread id in | |
651 | call *%edi # call pthread_create | |
652 | add $0x14, %esp # restore stack | |
653 | int $0x3 # breakpoint | |
654 | nop | |
655 | ||
656 | Like I said, threading is dangerous. Know your program before | |
657 | attempting to inject a thread. You will get odd results if | |
658 | you inject a thread into a process that doesn't naturally thread. | |
659 | ||
660 | [-- 5.3) Function Trampolines --] | |
661 | ||
662 | Function trampolines are a great way to transparently hook arbitrary | |
663 | functions in memory. I'll give a brief overview of what a function | |
664 | trampoline is and how it works. | |
665 | ||
666 | The basic jist to how function trampolines work is that they | |
667 | overwrite the first x instructions where the size of the x | |
668 | instructions is at least six bytes. The six bytes come from the | |
669 | fact that on x86 unconditional jumps take up 6 bytes in opcodes. | |
670 | The x instructions are replaced with the jmp instruction that | |
671 | jumps to an address in memory that contains the injected function. | |
672 | This function runs before the actual function runs, and thus, has | |
673 | complete control over whether the actual function even gets called. | |
674 | At the end of the injected function the x instructions are appended | |
675 | as well as a jump back to the original function plus the size of | |
676 | the x instructions. Here's an example: | |
677 | ||
678 | Let's say we want to hook the function 'testFunction' in the | |
679 | executable 'ownme'. | |
680 | ||
681 | root@rd-linux:~# objdump -d ownme --start-addr=0x080484d4 | |
682 | ||
683 | ownme: file format elf32-i386 | |
684 | ||
685 | Disassembly of section .init: | |
686 | Disassembly of section .plt: | |
687 | Disassembly of section .text: | |
688 | ||
689 | 080484d4 <testFunction>: | |
690 | 80484d4: 55 push %ebp | |
691 | 80484d5: 89 e5 mov %esp,%ebp | |
692 | 80484d7: 83 ec 18 sub $0x18,%esp | |
693 | ... | |
694 | 8048500: c9 leave | |
695 | 8048501: c3 ret | |
696 | ||
697 | Well, it looks like the first 3 instructions match our criteria | |
698 | of at least 6 bytes. Let's keep those 6 bytes of opcodes | |
699 | tucked away for now. | |
700 | ||
701 | We need to be smart here. We're going to do a jmp that | |
702 | says jmp to address stored in address x. We're also | |
703 | going to want to restore back to the original place. That means | |
704 | when we allocate our memory we should allocate it in a format like | |
705 | this: | |
706 | ||
707 | [ 4 bytes storing the address of our code ] | |
708 | [ 4 bytes storing the address to jmp back to ] | |
709 | [ X bytes of arbitrary code ] | |
710 | [ X bytes containing the X instructions that we overwrote ] | |
711 | [ 6 bytes for the jump back ] | |
712 | ||
713 | So let's say we want to inject this code and we allocated | |
714 | a buffer in the process of the approriate length which starts | |
715 | at 0x41414140: | |
716 | ||
717 | nop | |
718 | movb $0x1, %al | |
719 | ||
720 | Our actual buffer in memory would look something like this | |
721 | ||
722 | 0x41414140 = 0x41414148 (address of our code) | |
723 | 0x41414144 = 0x080484d8 (address to jmp back to) | |
724 | 0x41414148 = 3 bytes (nop, movb) | |
725 | 0x4141414B = 6 bytes of preamble from testFunction | |
726 | 0x41414152 = jmp *0x41414144 | |
727 | ||
728 | The last step now that we have our code injected is to overwrite | |
729 | the actual preamble (the 6 bytes of testFunction) with the jmp | |
730 | to our code. The assembly would look something like this: | |
731 | ||
732 | jmp *0x41414140 # Jump to the address stored in 0x41414140 | |
733 | ||
734 | Once that's overwritten, we're home free. The flow of | |
735 | execution goes like this: | |
736 | ||
737 | 1) Call to testFunction | |
738 | 2) First instruction of testFunction is: | |
739 | jmp *0x41414140 | |
740 | 3) vm jumps to 0x41414148 an executes: | |
741 | nop | |
742 | movb $0x1, %al | |
743 | push %ebp | |
744 | mov %esp, %ebp | |
745 | sub $0x18, %esp | |
746 | jmp *0x41414144 | |
747 | 4) vm jumps to 0x080484d8 | |
748 | 5) Function executes like normal. | |
749 | ||
750 | That's all there is to it. There are a couple of restrictions | |
751 | when using trampolines: | |
752 | ||
753 | 1) NEVER modify the stack without restoring it before | |
754 | the original functions preamble gets called. Bad | |
755 | things will happen. | |
756 | 2) Becareful what registers you modify. Some functions | |
757 | may use fastcall. | |
758 | ||
759 | For more information on function trampolines, see the ``References`` | |
760 | section. | |
761 | ||
762 | [-- 6) Conclusion --] | |
763 | ||
764 | That about wraps it up. You now have the tools to allocate, | |
765 | copy, inject libraries, create forks, create threads, and | |
766 | install function trampolines. You also have the underlying | |
767 | concept of ``Execution Diversion`` which can be applied across | |
768 | the board to even more things I haven't even thought of yet. | |
769 | ||
770 | [-- 7) References --] | |
771 | ||
772 | * For information about ``Function Trampolines``: | |
773 | ||
774 | http://research.microsoft.com/sn/detours | |
775 |
0 | ### hotpatch is a dll injection strategy | |
1 | ### Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
2 | ### All rights reserved. | |
3 | ### | |
4 | ### Redistribution and use in source and binary forms, with or without | |
5 | ### modification, are permitted provided that the following conditions are met: | |
6 | ### | |
7 | ### * Redistributions of source code must retain the above copyright | |
8 | ### notice, this list of conditions and the following disclaimer. | |
9 | ### | |
10 | ### * Redistributions in binary form must reproduce the above copyright | |
11 | ### notice, this list of conditions and the following disclaimer in the | |
12 | ### documentation and/or other materials provided with the distribution. | |
13 | ### | |
14 | ### * Neither the name of Selective Intellect LLC nor the | |
15 | ### names of its contributors may be used to endorse or promote products | |
16 | ### derived from this software without specific prior written permission. | |
17 | ### | |
18 | ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
19 | ### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | ### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | ### DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
22 | ### DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | ### LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | ### ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | ### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
27 | ### SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | ### | |
29 | configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/hotpatch_config.h @ONLY) | |
30 | install(FILES hotpatch.h DESTINATION include) |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
31 | #ifndef __LIBHOTPATCH_CONFIG_H__ | |
32 | #define __LIBHOTPATCH_CONFIG_H__ | |
33 | ||
34 | #if (defined __GNUC__ && !defined _GNU_SOURCE) | |
35 | #define _GNU_SOURCE 1 | |
36 | #endif | |
37 | ||
38 | #cmakedefine HOTPATCH_HAS_FEATURES_H | |
39 | #ifdef HOTPATCH_HAS_FEATURES_H | |
40 | #include <features.h> | |
41 | #endif | |
42 | ||
43 | #cmakedefine HOTPATCH_HAS_ERRNO_H | |
44 | #ifdef HOTPATCH_HAS_ERRNO_H | |
45 | #include <errno.h> | |
46 | #endif | |
47 | ||
48 | #cmakedefine HOTPATCH_HAS_STDIO_H | |
49 | #ifdef HOTPATCH_HAS_STDIO_H | |
50 | #include <stdio.h> | |
51 | #endif | |
52 | ||
53 | #cmakedefine HOTPATCH_HAS_STDLIB_H | |
54 | #ifdef HOTPATCH_HAS_STDLIB_H | |
55 | #include <stdlib.h> | |
56 | #endif | |
57 | ||
58 | #cmakedefine HOTPATCH_HAS_STRING_H | |
59 | #ifdef HOTPATCH_HAS_STRING_H | |
60 | #include <string.h> | |
61 | #endif | |
62 | ||
63 | #cmakedefine HOTPATCH_HAS_STRNLEN_FN | |
64 | #ifndef HOTPATCH_HAS_STRNLEN_FN | |
65 | size_t hotpatch_strnlen(const char *str, size_t maxlen); | |
66 | #define strnlen(a,b) hotpatch_strnlen(a,b) | |
67 | #endif | |
68 | ||
69 | #cmakedefine HOTPATCH_HAS_STRTOKR_FN | |
70 | #ifndef HOTPATCH_HAS_STRTOKR_FN | |
71 | #define strtok_r(a,b,c) strtok(a,b) | |
72 | #endif | |
73 | ||
74 | #cmakedefine HOTPATCH_HAS_STDDEF_H | |
75 | #ifdef HOTPATCH_HAS_STDDEF_H | |
76 | #include <stddef.h> | |
77 | #endif | |
78 | ||
79 | #cmakedefine HOTPATCH_HAS_LIMITS_H | |
80 | #ifdef HOTPATCH_HAS_LIMITS_H | |
81 | #include <limits.h> | |
82 | #endif | |
83 | ||
84 | #cmakedefine HOTPATCH_HAS_STDINT_H | |
85 | #ifdef HOTPATCH_HAS_STDINT_H | |
86 | #include <stdint.h> | |
87 | #endif | |
88 | ||
89 | #cmakedefine HOTPATCH_HAS_STDARG_H | |
90 | #ifdef HOTPATCH_HAS_STDARG_H | |
91 | #include <stdarg.h> | |
92 | #endif | |
93 | ||
94 | #cmakedefine HOTPATCH_HAS_STDBOOL_H | |
95 | #ifdef HOTPATCH_HAS_STDBOOL_H | |
96 | #include <stdbool.h> | |
97 | #endif | |
98 | ||
99 | #cmakedefine HOTPATCH_HAS_TIME_H | |
100 | #ifdef HOTPATCH_HAS_TIME_H | |
101 | #include <time.h> | |
102 | #endif | |
103 | ||
104 | #cmakedefine HOTPATCH_HAS_SYS_TIME_H | |
105 | #ifdef HOTPATCH_HAS_SYS_TIME_H | |
106 | #include <sys/time.h> | |
107 | #endif | |
108 | ||
109 | #cmakedefine HOTPATCH_HAS_SYS_TYPES_H | |
110 | #ifdef HOTPATCH_HAS_SYS_TYPES_H | |
111 | #include <sys/types.h> | |
112 | #else | |
113 | #ifndef __cplusplus | |
114 | typedef enum { | |
115 | false = 0, | |
116 | true = 1 | |
117 | } bool; | |
118 | #endif | |
119 | #endif | |
120 | ||
121 | #cmakedefine HOTPATCH_HAS_UNISTD_H | |
122 | #ifdef HOTPATCH_HAS_UNISTD_H | |
123 | #include <unistd.h> | |
124 | #endif | |
125 | ||
126 | #cmakedefine HOTPATCH_HAS_ASSERT_H | |
127 | ||
128 | /* for the APPLE */ | |
129 | #cmakedefine HOTPATCH_HAS_MACH_MESSAGE_H | |
130 | #cmakedefine HOTPATCH_HAS_MACH_MACH_H | |
131 | #cmakedefine HOTPATCH_HAS_MACH_TASK_H | |
132 | #cmakedefine HOTPATCH_HAS_MACH_MACHTRAPS_H | |
133 | #cmakedefine HOTPATCH_HAS_MACH_MACHERROR_H | |
134 | #cmakedefine HOTPATCH_HAS_MACH_TASKFORPID_FN | |
135 | ||
136 | /* for the Linux */ | |
137 | #cmakedefine HOTPATCH_HAS_LINUX_ELF_H | |
138 | #ifdef HOTPATCH_HAS_LINUX_ELF_H | |
139 | #include <elf.h> | |
140 | #endif | |
141 | ||
142 | #cmakedefine HOTPATCH_HAS_LINUX_DLFCN_H | |
143 | #ifdef HOTPATCH_HAS_LINUX_DLFCN_H | |
144 | #include <dlfcn.h> | |
145 | #endif | |
146 | ||
147 | #cmakedefine HOTPATCH_HAS_LINUX_LINK_H | |
148 | #ifdef HOTPATCH_HAS_LINUX_LINK_H | |
149 | #include <link.h> | |
150 | #endif | |
151 | ||
152 | #cmakedefine HOTPATCH_HAS_LINUX_PTHREAD_H | |
153 | #ifdef HOTPATCH_HAS_LINUX_PTHREAD_H | |
154 | #include <pthread.h> | |
155 | #endif | |
156 | ||
157 | #cmakedefine HOTPATCH_HAS_LINUX_SETJMP_H | |
158 | #ifdef HOTPATCH_HAS_LINUX_SETJMP_H | |
159 | #include <setjmp.h> | |
160 | #endif | |
161 | ||
162 | #cmakedefine HOTPATCH_HAS_LINUX_SIGNAL_H | |
163 | #ifdef HOTPATCH_HAS_LINUX_SIGNAL_H | |
164 | #include <signal.h> | |
165 | #endif | |
166 | ||
167 | #cmakedefine HOTPATCH_HAS_LINUX_SYS_PTRACE_H | |
168 | #ifdef HOTPATCH_HAS_LINUX_SYS_PTRACE_H | |
169 | #include <sys/ptrace.h> | |
170 | #endif | |
171 | ||
172 | #cmakedefine HOTPATCH_HAS_LINUX_SYS_WAIT_H | |
173 | #ifdef HOTPATCH_HAS_LINUX_SYS_WAIT_H | |
174 | #include <sys/wait.h> | |
175 | #endif | |
176 | ||
177 | #cmakedefine HOTPATCH_HAS_LINUX_SYS_STAT_H | |
178 | #ifdef HOTPATCH_HAS_LINUX_SYS_STAT_H | |
179 | #include <sys/stat.h> | |
180 | #endif | |
181 | ||
182 | #cmakedefine HOTPATCH_HAS_LINUX_FCNTL_H | |
183 | #ifdef HOTPATCH_HAS_LINUX_FCNTL_H | |
184 | #include <fcntl.h> | |
185 | #endif | |
186 | ||
187 | #cmakedefine HOTPATCH_HAS_LINUX_SYS_USER_H | |
188 | #ifdef HOTPATCH_HAS_LINUX_SYS_USER_H | |
189 | #include <sys/types.h> | |
190 | #include <sys/user.h> | |
191 | #endif | |
192 | ||
193 | #cmakedefine HOTPATCH_HAS_LINUX_SYS_SYSCALL_H | |
194 | #ifdef HOTPATCH_HAS_LINUX_SYS_SYSCALL_H | |
195 | #include <sys/syscall.h> | |
196 | #endif | |
197 | ||
198 | #cmakedefine HOTPATCH_HAS_LINUX_DLITERPHDR_FN | |
199 | ||
200 | #define HOTPATCH_VOIDPTR_SIZE @HOTPATCH_VOIDPTR_SIZE@ | |
201 | ||
202 | #cmakedefine HOTPATCH_USE_ASM | |
203 | ||
204 | #if __WORDSIZE == 64 | |
205 | #define LX "%lx" | |
206 | #define LU "%lu" | |
207 | #else | |
208 | #define LX "%x" | |
209 | #define LU "%u" | |
210 | #endif | |
211 | ||
212 | #endif /* __LIBHOTPATCH_CONFIG_H__ */ | |
213 |
0 | /* | |
1 | * hotpatch is a sofile injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #ifndef __LIBHOTPATCH_H__ | |
31 | #define __LIBHOTPATCH_H__ | |
32 | ||
33 | #ifdef __cplusplus | |
34 | extern "C" { | |
35 | #endif /* __cplusplus */ | |
36 | ||
37 | #include <sys/types.h> | |
38 | #include <stdint.h> | |
39 | ||
40 | #define HOTPATCH_MAJOR_VERSION 0 | |
41 | #define HOTPATCH_MINOR_VERSION 2 | |
42 | ||
43 | #ifndef HOTPATCH_LINUX_START | |
44 | #define HOTPATCH_LINUX_START "_start" | |
45 | #endif | |
46 | ||
47 | typedef struct hotpatch_is_opaque hotpatch_t; | |
48 | /* Create the hotpatch object for the running process whose PID is given as an | |
49 | * argument. Returns a pointer to an opaque object that must be freed by | |
50 | * hotpatch_delete() function later to conserve memory. | |
51 | */ | |
52 | hotpatch_t *hotpatch_create(pid_t, int verbosity); | |
53 | /* | |
54 | * delete memory and close all open handles related to the hotpatch'ed process. | |
55 | * This can lead to the hotpatch'ed process to be unstable if not done in the same | |
56 | * thread as create function above. | |
57 | */ | |
58 | void hotpatch_destroy(hotpatch_t *); | |
59 | /* | |
60 | * Inject a shared object into the process and invoke the given symbol without | |
61 | * arguments. No thread will be created by hotpatch. | |
62 | * If the symbol is NULL, then _init() is expected to be in the library. | |
63 | * If data is NULL, no data will be copied over to the other process for the | |
64 | * symbol that is being invoked. If the symbol being invoked is _init(), then | |
65 | * data will be ignored. This data and datalen will be provided as arguments to | |
66 | * the symbol when invoked. | |
67 | * The return address of the dlopen() call can be optionally returned in | |
68 | * the outaddr variable. | |
69 | * The return value from the invocation of the symbol in the process can be | |
70 | * optionally returned in the outres variable. If the symbol is NULL, or if the | |
71 | * symbol returns void, then the return value will be undefined. | |
72 | */ | |
73 | int hotpatch_inject_library(hotpatch_t *, const char *sofile, | |
74 | const char *symbol, | |
75 | const unsigned char *data, size_t datalen, | |
76 | uintptr_t *outaddr, uintptr_t *outres); | |
77 | ||
78 | /* AUXILLIARY FUNCTIONS */ | |
79 | ||
80 | void hotpatch_version(int *major, int *minor); | |
81 | ||
82 | /* finds the symbol in the symbol table of executable and returns the memory | |
83 | * location of it. On a 64-bit system the running process can be 32 or 64 bit, | |
84 | * and hence they both need to be handled correctly or even simultaneously. | |
85 | * Returns not only the location of the symbol but also the type and size | |
86 | */ | |
87 | uintptr_t hotpatch_read_symbol(hotpatch_t *, const char *symbol, int *symtype, | |
88 | size_t *symsize); | |
89 | /* | |
90 | * Get the entry point of the executable in question | |
91 | */ | |
92 | uintptr_t hotpatch_get_entry_point(hotpatch_t *); | |
93 | /* | |
94 | * Attach to the process that you wanted to hotpatch. Returns 0 on success and 1 | |
95 | * on failure. | |
96 | */ | |
97 | int hotpatch_attach(hotpatch_t *); | |
98 | /* | |
99 | * Detach from the process that you wanted to hotpatch. Returns -1 on failure | |
100 | * or if nothing was attached earlier. Returns 0 if detaching succeeded. | |
101 | */ | |
102 | int hotpatch_detach(hotpatch_t *); | |
103 | /* | |
104 | * Sets the execution pointer to point to the address given by the user. | |
105 | * Returns 0 on success and -1 on failure. | |
106 | */ | |
107 | int hotpatch_set_execution_pointer(hotpatch_t *, uintptr_t location); | |
108 | ||
109 | #ifdef __cplusplus | |
110 | } /* end of extern C */ | |
111 | #endif /* __cplusplus */ | |
112 | ||
113 | #endif /* __LIBHOTPATCH_H__ */ |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #ifndef __LIBHOTPATCH_INTERNAL_H__ | |
31 | #define __LIBHOTPATCH_INTERNAL_H__ | |
32 | ||
33 | #include <hotpatch_config.h> | |
34 | ||
35 | #define OS_MAX_BUFFER 512 | |
36 | ||
37 | #undef LOG_ERROR_INVALID_PID | |
38 | #define LOG_ERROR_INVALID_PID(A) do { \ | |
39 | fprintf(stderr, "[%s:%d] Invalid PID: %d\n", __func__, __LINE__, A); \ | |
40 | } while (0) | |
41 | ||
42 | #undef LOG_ERROR_OUT_OF_MEMORY | |
43 | #define LOG_ERROR_OUT_OF_MEMORY do { \ | |
44 | int err = errno; \ | |
45 | fprintf(stderr, "[%s:%d] Out of memory. Error: %s\n", __func__, __LINE__,\ | |
46 | strerror(err)); \ | |
47 | } while (0) | |
48 | ||
49 | #undef LOG_ERROR_FILE_OPEN | |
50 | #define LOG_ERROR_FILE_OPEN(FF) do { \ | |
51 | int err = errno; \ | |
52 | fprintf(stderr, "[%s:%d] File(%s) open error. Error: %s\n", __func__, __LINE__,\ | |
53 | FF, strerror(err)); \ | |
54 | } while (0) | |
55 | ||
56 | #undef LOG_ERROR_FILE_SEEK | |
57 | #define LOG_ERROR_FILE_SEEK do { \ | |
58 | int err = errno; \ | |
59 | fprintf(stderr, "[%s:%d] File seek error. Error: %s\n", __func__, __LINE__,\ | |
60 | strerror(err)); \ | |
61 | } while (0) | |
62 | ||
63 | #undef LOG_ERROR_FILE_READ | |
64 | #define LOG_ERROR_FILE_READ do { \ | |
65 | int err = errno; \ | |
66 | fprintf(stderr, "[%s:%d] File read error. Error: %s\n", __func__, __LINE__,\ | |
67 | strerror(err)); \ | |
68 | } while (0) | |
69 | ||
70 | #undef LOG_ERROR_UNSUPPORTED_PROCESSOR | |
71 | #define LOG_ERROR_UNSUPPORTED_PROCESSOR do { \ | |
72 | fprintf(stderr, \ | |
73 | "[%s:%d] Only 32/64-bit Intel X86/X86-64 processors are supported.\n",\ | |
74 | __func__, __LINE__); \ | |
75 | } while (0) | |
76 | #define LOG_INFO_HEADERS_LOADED(verbose) do { \ | |
77 | if (verbose > 2) \ | |
78 | fprintf(stderr, "[%s:%d] Exe headers loaded.\n", __func__, __LINE__); \ | |
79 | } while (0) | |
80 | ||
81 | struct ld_procmaps; | |
82 | ||
83 | enum elf_bit { | |
84 | HOTPATCH_EXE_IS_NEITHER, | |
85 | HOTPATCH_EXE_IS_32BIT, | |
86 | HOTPATCH_EXE_IS_64BIT | |
87 | }; | |
88 | ||
89 | struct elf_symbol { | |
90 | char *name; /* null terminated symbol name */ | |
91 | uintptr_t address; /* address at which it is available */ | |
92 | int type; /* type of symbol */ | |
93 | size_t size; /* size of the symbol if available */ | |
94 | }; | |
95 | ||
96 | struct elf_interp { | |
97 | char *name; | |
98 | size_t length; | |
99 | uintptr_t ph_addr; | |
100 | }; | |
101 | ||
102 | struct ld_library { | |
103 | char *pathname; | |
104 | size_t length; | |
105 | ino_t inode; | |
106 | uintptr_t addr_begin; | |
107 | uintptr_t addr_end; | |
108 | }; | |
109 | ||
110 | enum { | |
111 | HOTPATCH_LIB_LD = 0, | |
112 | HOTPATCH_LIB_C, | |
113 | HOTPATCH_LIB_DL, | |
114 | HOTPATCH_LIB_PTHREAD, | |
115 | HOTPATCH_LIB_MAX | |
116 | }; | |
117 | ||
118 | enum { | |
119 | HOTPATCH_SYMBOL_IS_UNKNOWN, | |
120 | HOTPATCH_SYMBOL_IS_FUNCTION, | |
121 | HOTPATCH_SYMBOL_IS_FILENAME, | |
122 | HOTPATCH_SYMBOL_IS_SECTION, | |
123 | HOTPATCH_SYMBOL_IS_OBJECT | |
124 | }; | |
125 | ||
126 | struct hotpatch_is_opaque { | |
127 | pid_t pid; | |
128 | int verbose; | |
129 | enum elf_bit is64; | |
130 | struct elf_symbol *exe_symbols; | |
131 | size_t exe_symbols_num; | |
132 | uintptr_t exe_entry_point; | |
133 | struct elf_interp exe_interp; /* dynamic loader from .interp in the exe */ | |
134 | struct ld_procmaps *ld_maps; | |
135 | size_t ld_maps_num; | |
136 | struct ld_library libs[HOTPATCH_LIB_MAX]; | |
137 | /* addresses useful */ | |
138 | uintptr_t fn_malloc; | |
139 | uintptr_t fn_realloc; | |
140 | uintptr_t fn_free; | |
141 | uintptr_t fn_dlopen; | |
142 | uintptr_t fn_dlclose; | |
143 | uintptr_t fn_dlsym; | |
144 | uintptr_t fn_pthread_create; | |
145 | uintptr_t fn_pthread_detach; | |
146 | /* actions */ | |
147 | bool attached; | |
148 | bool inserted; | |
149 | }; | |
150 | ||
151 | struct elf_symbol *exe_load_symbols(const char *filename, int verbose, | |
152 | size_t *sym_count, | |
153 | uintptr_t *entry_point, | |
154 | struct elf_interp *interp, | |
155 | enum elf_bit *is64); | |
156 | ||
157 | struct ld_procmaps *ld_load_maps(pid_t pid, int verbose, size_t *num); | |
158 | ||
159 | void ld_free_maps(struct ld_procmaps *, size_t num); | |
160 | ||
161 | /* the full path of the library needs to be given. */ | |
162 | int ld_find_library(const struct ld_procmaps *, const size_t num, | |
163 | const char *libpath, bool inode_match, | |
164 | struct ld_library *lib, int verbose); | |
165 | ||
166 | /* finds the address of the symbol in the library if it exists */ | |
167 | uintptr_t ld_find_address(const struct ld_library *hpl, const char *symbol, | |
168 | int verbose); | |
169 | ||
170 | int elf_symbol_cmpqsort(const void *p1, const void *p2); | |
171 | #endif /* __LIBHOTPATCH_INTERNAL_H__ */ |
0 | ### hotpatch is a dll injection strategy | |
1 | ### Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
2 | ### All rights reserved. | |
3 | ### | |
4 | ### Redistribution and use in source and binary forms, with or without | |
5 | ### modification, are permitted provided that the following conditions are met: | |
6 | ### | |
7 | ### * Redistributions of source code must retain the above copyright | |
8 | ### notice, this list of conditions and the following disclaimer. | |
9 | ### | |
10 | ### * Redistributions in binary form must reproduce the above copyright | |
11 | ### notice, this list of conditions and the following disclaimer in the | |
12 | ### documentation and/or other materials provided with the distribution. | |
13 | ### | |
14 | ### * Neither the name of Selective Intellect LLC nor the | |
15 | ### names of its contributors may be used to endorse or promote products | |
16 | ### derived from this software without specific prior written permission. | |
17 | ### | |
18 | ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
19 | ### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | ### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | ### DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
22 | ### DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | ### LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | ### ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | ### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
27 | ### SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | ### | |
29 | project(hotpatch) | |
30 | ||
31 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) | |
32 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) | |
33 | ||
34 | if (USE_ASM) | |
35 | foreach (ASM_VAR call32 call64) | |
36 | set(ASM_F ${CMAKE_CURRENT_SOURCE_DIR}/${ASM_VAR}.s) | |
37 | set(ASM_F_OBJ ${CMAKE_CURRENT_BINARY_DIR}/${ASM_VAR}.o) | |
38 | set(ASM_F_HDR ${CMAKE_CURRENT_BINARY_DIR}/${ASM_VAR}.h) | |
39 | set(ASM_F_VAR hotpatch_${ASM_VAR}) | |
40 | set(ASM2HDR ${CMAKE_CURRENT_SOURCE_DIR}/asm2hdr.pl) | |
41 | if (HOTPATCH_ASM AND PERL_FOUND) | |
42 | add_custom_command(OUTPUT ${ASM_F_HDR} | |
43 | COMMAND ${HOTPATCH_ASM} ARGS ${ASM_F} -o ${ASM_F_OBJ} | |
44 | COMMAND ${PERL_EXECUTABLE} ARGS ${ASM2HDR} ${ASM_F_OBJ} | |
45 | ${ASM_F_HDR} ${ASM_F_VAR} | |
46 | DEPENDS ${ASM_F} ${ASM2HDR} | |
47 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) | |
48 | set(ASM_HEADERS ${ASM_HEADERS} ${ASM_F_HDR}) | |
49 | else (HOTPATCH_ASM AND PERL_FOUND) | |
50 | message(FATAL_ERROR "You need Perl for this") | |
51 | endif (HOTPATCH_ASM AND PERL_FOUND) | |
52 | endforeach (ASM_VAR call32 call64) | |
53 | add_custom_target(asm_header DEPENDS ${ASM_HEADERS}) | |
54 | endif (USE_ASM) | |
55 | ||
56 | add_library(hotpatch SHARED hotpatch.c exedetails.c loader.c) | |
57 | add_library(hotpatch_s STATIC hotpatch.c exedetails.c loader.c) | |
58 | if (USE_ASM) | |
59 | add_dependencies(hotpatch asm_header) | |
60 | add_dependencies(hotpatch_s asm_header) | |
61 | endif (USE_ASM) | |
62 | set_target_properties(hotpatch_s PROPERTIES OUTPUT_NAME "hotpatch" | |
63 | CLEAN_DIRECT_OUTPUT 1) | |
64 | set_target_properties(hotpatch PROPERTIES CLEAN_DIRECT_OUTPUT 1) | |
65 | install(TARGETS hotpatch LIBRARY DESTINATION lib) | |
66 | install(TARGETS hotpatch_s ARCHIVE DESTINATION lib) | |
67 | ||
68 | add_executable(hotpatcher main.c) | |
69 | target_link_libraries(hotpatcher hotpatch_s) | |
70 | install(TARGETS hotpatcher RUNTIME DESTINATION bin) |
0 | #!/usr/bin/perl | |
1 | use strict; | |
2 | use warnings; | |
3 | my $usage = "$0 <asm file> <header file> <variable>"; | |
4 | my $obj = $ARGV[0] || die $usage; | |
5 | my $hdr = $ARGV[1] || die $usage; | |
6 | my $name = $ARGV[2] || die $usage; | |
7 | my $ifdef = '__' . uc($name) . '_H__'; | |
8 | my @chars; | |
9 | do { | |
10 | local $/; | |
11 | open ASM, "<$obj" or die "Unable to open $obj: $!"; | |
12 | my $data = <ASM>; | |
13 | @chars = map(ord, split('', $data)); | |
14 | }; | |
15 | open HDR, ">$hdr" or die "Unable to open $hdr: $!"; | |
16 | print HDR "#ifndef $ifdef\n#define $ifdef\n"; | |
17 | print HDR 'const unsigned char ' . $name ."[] = {\n"; | |
18 | print HDR "\t", join(', ', @chars), "\n"; | |
19 | print HDR "};\n#endif /* $ifdef */\n"; | |
20 | close HDR; |
0 | BITS 32 | |
1 | ; in 32-bit mode the arguments are passed on the stack. Since we need only two | |
2 | ; arguments at max, we will only push 2 registers. The return value will be | |
3 | ; taken from EAX; | |
4 | ; the function pointer is placed in EBX followed by a triggered breakpoint. | |
5 | push esi | |
6 | push edi | |
7 | call [ebx] | |
8 | int 3 |
0 | BITS 64 | |
1 | ; the function pointer is placed in RBX/EBX followed by a triggered breakpoint | |
2 | ; the arguments are expected in RDI and RSI respectively. The return value will | |
3 | ; be extracted from RAX | |
4 | call [rbx] | |
5 | int 3 |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #include <hotpatch_internal.h> | |
32 | #include <hotpatch.h> | |
33 | ||
34 | #if __WORDSIZE == 64 | |
35 | typedef Elf64_Ehdr Elf_Ehdr; | |
36 | typedef Elf64_Phdr Elf_Phdr; | |
37 | typedef Elf64_Shdr Elf_Shdr; | |
38 | typedef Elf64_Sym Elf_Sym; | |
39 | #else | |
40 | typedef Elf32_Ehdr Elf_Ehdr; | |
41 | typedef Elf32_Phdr Elf_Phdr; | |
42 | typedef Elf32_Shdr Elf_Shdr; | |
43 | typedef Elf32_Sym Elf_Sym; | |
44 | #endif | |
45 | ||
46 | enum { | |
47 | HOTPATCH_SYMBOL_TYPE, | |
48 | HOTPATCH_UNKNOWN | |
49 | }; | |
50 | ||
51 | struct elf_internals { | |
52 | int fd; | |
53 | enum elf_bit is64; | |
54 | off_t proghdr_offset; | |
55 | void *proghdrs; /* program headers */ | |
56 | size_t proghdr_num; | |
57 | size_t proghdr_size; /* total buffer size */ | |
58 | off_t sechdr_offset; | |
59 | void *sechdrs; /* section headers */ | |
60 | size_t sechdr_num; | |
61 | size_t sechdr_size; /* total buffer size */ | |
62 | size_t secnametbl_idx; | |
63 | char *strsectbl; /* string table for section names */ | |
64 | size_t strsectbl_size; | |
65 | /* | |
66 | * stored here temporarily, should not be freed unless on failure. | |
67 | */ | |
68 | uintptr_t entry_point; | |
69 | struct elf_symbol *symbols; | |
70 | size_t symbols_num; | |
71 | struct elf_interp interp; | |
72 | }; | |
73 | ||
74 | /* each of the exe_* functions have to be reentrant and thread-safe */ | |
75 | static int exe_get_hotpatch_type(int info, int group) | |
76 | { | |
77 | if (group == HOTPATCH_SYMBOL_TYPE) { | |
78 | int value = ELF64_ST_TYPE(info); | |
79 | if (value == STT_FUNC) | |
80 | return HOTPATCH_SYMBOL_IS_FUNCTION; | |
81 | else if (value == STT_FILE) | |
82 | return HOTPATCH_SYMBOL_IS_FILENAME; | |
83 | else if (value == STT_SECTION) | |
84 | return HOTPATCH_SYMBOL_IS_SECTION; | |
85 | else if (value == STT_OBJECT) | |
86 | return HOTPATCH_SYMBOL_IS_OBJECT; | |
87 | else | |
88 | return HOTPATCH_SYMBOL_IS_UNKNOWN; | |
89 | } | |
90 | return -1; | |
91 | } | |
92 | ||
93 | static int exe_open_filename(const char *filename, int verbose) | |
94 | { | |
95 | int fd = -1; | |
96 | fd = open(filename, O_RDONLY); | |
97 | if (fd < 0) | |
98 | LOG_ERROR_FILE_OPEN(filename); | |
99 | if (verbose > 3) | |
100 | fprintf(stderr, "[%s:%d] Exe file descriptor: %d\n", __func__, | |
101 | __LINE__, fd); | |
102 | return fd; | |
103 | } | |
104 | ||
105 | static int exe_elf_identify(unsigned char *e_ident, size_t size, int verbose) | |
106 | { | |
107 | if (e_ident && size > 0) { | |
108 | if ((e_ident[EI_MAG0] == ELFMAG0) && | |
109 | (e_ident[EI_MAG1] == ELFMAG1) && | |
110 | (e_ident[EI_MAG2] == ELFMAG2) && | |
111 | (e_ident[EI_MAG3] == ELFMAG3)) { | |
112 | int is64 = HOTPATCH_EXE_IS_NEITHER; | |
113 | /* magic number says this is an ELF file */ | |
114 | switch (e_ident[EI_CLASS]) { | |
115 | case ELFCLASS32: | |
116 | is64 = HOTPATCH_EXE_IS_32BIT; | |
117 | if (verbose > 3) | |
118 | fprintf(stderr, "[%s:%d] File is 32-bit ELF\n", __func__, | |
119 | __LINE__); | |
120 | break; | |
121 | case ELFCLASS64: | |
122 | is64 = HOTPATCH_EXE_IS_64BIT; | |
123 | if (verbose > 3) | |
124 | fprintf(stderr, "[%s:%d] File is 64-bit ELF\n", __func__, | |
125 | __LINE__); | |
126 | break; | |
127 | case ELFCLASSNONE: | |
128 | default: | |
129 | is64 = HOTPATCH_EXE_IS_NEITHER; | |
130 | if (verbose > 3) | |
131 | fprintf(stderr, "[%s:%d] File is unsupported ELF\n", | |
132 | __func__, __LINE__); | |
133 | break; | |
134 | } | |
135 | if (is64 != HOTPATCH_EXE_IS_NEITHER) { | |
136 | int isbigendian = -1; | |
137 | int iscurrent = 0; | |
138 | int islinux = 0; | |
139 | switch (e_ident[EI_DATA]) { | |
140 | case ELFDATA2LSB: | |
141 | isbigendian = 0; | |
142 | if (verbose > 3) | |
143 | fprintf(stderr, "[%s:%d] Little endian format.\n", | |
144 | __func__, __LINE__); | |
145 | break; | |
146 | case ELFDATA2MSB: | |
147 | isbigendian = 1; | |
148 | if (verbose > 3) | |
149 | fprintf(stderr, "[%s:%d] Big endian format.\n", | |
150 | __func__, __LINE__); | |
151 | break; | |
152 | case ELFDATANONE: | |
153 | default: | |
154 | isbigendian = -1; | |
155 | if (verbose > 2) | |
156 | fprintf(stderr, "[%s:%d] Unknown endian format.\n", | |
157 | __func__, __LINE__); | |
158 | break; | |
159 | } | |
160 | if (e_ident[EI_VERSION] == EV_CURRENT) { | |
161 | iscurrent = 1; | |
162 | if (verbose > 3) | |
163 | fprintf(stderr, "[%s:%d] Current ELF format.\n", | |
164 | __func__, __LINE__); | |
165 | } | |
166 | if (verbose > 3) | |
167 | fprintf(stderr, "[%s:%d] ELFOSABI: %d\n", __func__, | |
168 | __LINE__, e_ident[EI_OSABI]); | |
169 | if (e_ident[EI_OSABI] == ELFOSABI_LINUX || | |
170 | e_ident[EI_OSABI] == ELFOSABI_SYSV) { | |
171 | islinux = 1; | |
172 | if (verbose > 3) | |
173 | fprintf(stderr, "[%s:%d] OS ABI is Linux.\n", __func__, | |
174 | __LINE__); | |
175 | } | |
176 | if (islinux && isbigendian == 0 && iscurrent) { | |
177 | return is64; | |
178 | } | |
179 | if (verbose > 1) | |
180 | fprintf(stderr, "[%s:%d] Not an acceptable header.\n", | |
181 | __func__, __LINE__); | |
182 | } | |
183 | } else { | |
184 | if (verbose > 3) | |
185 | fprintf(stderr, "[%s:%d] This is not an ELF file format.\n", | |
186 | __func__, __LINE__); | |
187 | } | |
188 | } | |
189 | return HOTPATCH_EXE_IS_NEITHER; | |
190 | } | |
191 | ||
192 | static int exe_load_symbol_table(struct elf_internals *ei, Elf_Shdr *symh, | |
193 | Elf_Shdr *strh, int verbose) | |
194 | { | |
195 | char *strsymtbl = NULL; | |
196 | size_t strsymtbl_size = 0; | |
197 | while (ei && symh && strh) { | |
198 | if (verbose > 3) | |
199 | fprintf(stderr, "[%s:%d] Retrieving symbol table.\n", __func__, | |
200 | __LINE__); | |
201 | if (lseek(ei->fd, strh->sh_offset, SEEK_SET) < 0) { | |
202 | LOG_ERROR_FILE_SEEK; | |
203 | break; | |
204 | } | |
205 | strsymtbl_size = strh->sh_size + 0; | |
206 | strsymtbl = malloc(strh->sh_size); | |
207 | if (!strsymtbl) { | |
208 | LOG_ERROR_OUT_OF_MEMORY; | |
209 | break; | |
210 | } | |
211 | if (read(ei->fd, strsymtbl, strh->sh_size) < 0) { | |
212 | LOG_ERROR_FILE_READ; | |
213 | break; | |
214 | } | |
215 | if (symh->sh_entsize > 0 && symh->sh_size > 0) { | |
216 | size_t idx; | |
217 | size_t sym_num = symh->sh_size / symh->sh_entsize; | |
218 | Elf_Sym *syms = malloc(symh->sh_size); | |
219 | if (!syms) { | |
220 | LOG_ERROR_OUT_OF_MEMORY; | |
221 | break; | |
222 | } | |
223 | if (lseek(ei->fd, symh->sh_offset, SEEK_SET) < 0) { | |
224 | LOG_ERROR_FILE_SEEK; | |
225 | free(syms); | |
226 | break; | |
227 | } | |
228 | if (read(ei->fd, syms, symh->sh_size) < 0) { | |
229 | LOG_ERROR_FILE_READ; | |
230 | free(syms); | |
231 | break; | |
232 | } | |
233 | /* there might already exist symbols from another section. | |
234 | * hence using realloc() takes care of that. | |
235 | * */ | |
236 | ei->symbols = realloc(ei->symbols, | |
237 | (sym_num + ei->symbols_num) * | |
238 | sizeof(*ei->symbols)); | |
239 | if (!ei->symbols) { | |
240 | LOG_ERROR_OUT_OF_MEMORY; | |
241 | break; | |
242 | } | |
243 | memset(&ei->symbols[ei->symbols_num], 0, sizeof(*ei->symbols) * sym_num); | |
244 | /* index 0 is always NULL */ | |
245 | for (idx = 1; idx < sym_num; ++idx) { | |
246 | const char *name = syms[idx].st_name > 0 ? | |
247 | &strsymtbl[syms[idx].st_name] : ""; | |
248 | if (name) { | |
249 | char *name2; | |
250 | int symtype = exe_get_hotpatch_type(syms[idx].st_info, | |
251 | HOTPATCH_SYMBOL_TYPE); | |
252 | if (verbose > 2) | |
253 | fprintf(stderr, | |
254 | "[%s:%d] Symbol "LU" is %s at %p type %d size "LU"\n", | |
255 | __func__, __LINE__, idx, name, | |
256 | (void *)syms[idx].st_value, symtype, | |
257 | syms[idx].st_size); | |
258 | name2 = strdup(name); | |
259 | if (!name2) { | |
260 | LOG_ERROR_OUT_OF_MEMORY; | |
261 | continue; | |
262 | } | |
263 | ei->symbols[ei->symbols_num].name = name2; | |
264 | ei->symbols[ei->symbols_num].address = (uintptr_t)syms[idx].st_value; | |
265 | ei->symbols[ei->symbols_num].size = (size_t)syms[idx].st_size; | |
266 | ei->symbols[ei->symbols_num].type = symtype; | |
267 | ei->symbols_num++; | |
268 | } | |
269 | } | |
270 | free(syms); | |
271 | if (strsymtbl) | |
272 | free(strsymtbl); | |
273 | return 0; | |
274 | } | |
275 | } | |
276 | if (strsymtbl) | |
277 | free(strsymtbl); | |
278 | return -1; | |
279 | } | |
280 | ||
281 | static int exe_load_section_headers(struct elf_internals *ei, int verbose) | |
282 | { | |
283 | Elf_Shdr *strsectblhdr = NULL; | |
284 | Elf_Shdr *sechdrs = NULL; | |
285 | size_t idx = 0; | |
286 | ssize_t symtab = -1; | |
287 | ssize_t strtab = -1; | |
288 | ||
289 | if (!ei || ei->sechdr_offset == 0 || ei->sechdr_size == 0) | |
290 | return -1; | |
291 | if (verbose > 3) | |
292 | fprintf(stderr, "[%s:%d] Retrieving section headers.\n", __func__, | |
293 | __LINE__); | |
294 | ei->sechdrs = malloc(ei->sechdr_size); | |
295 | if (!ei->sechdrs) { | |
296 | LOG_ERROR_OUT_OF_MEMORY; | |
297 | return -1; | |
298 | } | |
299 | memset(ei->sechdrs, 0, ei->sechdr_size); | |
300 | if (verbose > 3) | |
301 | fprintf(stderr, "[%s:%d] Reading section header offset at "LU"\n", | |
302 | __func__, __LINE__, (size_t)ei->sechdr_offset); | |
303 | if (lseek(ei->fd, ei->sechdr_offset, SEEK_SET) < 0) { | |
304 | LOG_ERROR_FILE_SEEK; | |
305 | return -1; | |
306 | } | |
307 | if (read(ei->fd, ei->sechdrs, ei->sechdr_size) < 0) { | |
308 | LOG_ERROR_FILE_READ; | |
309 | return -1; | |
310 | } | |
311 | sechdrs = (Elf_Shdr *)ei->sechdrs; | |
312 | strsectblhdr = &sechdrs[ei->secnametbl_idx]; | |
313 | if (lseek(ei->fd, strsectblhdr->sh_offset, SEEK_SET) < 0) { | |
314 | LOG_ERROR_FILE_SEEK; | |
315 | return -1; | |
316 | } | |
317 | ei->strsectbl = malloc(strsectblhdr->sh_size); | |
318 | if (!ei->strsectbl) { | |
319 | LOG_ERROR_OUT_OF_MEMORY; | |
320 | return -1; | |
321 | } | |
322 | ei->strsectbl_size = strsectblhdr->sh_size + 0; | |
323 | if (read(ei->fd, ei->strsectbl, strsectblhdr->sh_size) < 0) { | |
324 | LOG_ERROR_FILE_READ; | |
325 | return -1; | |
326 | } | |
327 | if (verbose > 3) | |
328 | fprintf(stderr, "[%s:%d] Number of sections: "LU"\n", __func__, __LINE__, | |
329 | ei->sechdr_num); | |
330 | for (idx = 0; idx < ei->sechdr_num; ++idx) { | |
331 | const char *name = &ei->strsectbl[sechdrs[idx].sh_name]; | |
332 | if (verbose > 2) { | |
333 | if (name) | |
334 | fprintf(stderr, "[%s:%d] Section name: %s Addr: %p Len: "LU"\n", | |
335 | __func__, __LINE__, name, (void *)sechdrs[idx].sh_offset, | |
336 | sechdrs[idx].sh_size); | |
337 | else | |
338 | fprintf(stderr, "[%s:%d] Section name: %s Addr: %p Len: "LU"\n", | |
339 | __func__, __LINE__, "N/A", (void *)sechdrs[idx].sh_offset, | |
340 | sechdrs[idx].sh_size); | |
341 | } | |
342 | switch (sechdrs[idx].sh_type) { | |
343 | case SHT_SYMTAB: | |
344 | case SHT_DYNSYM: | |
345 | symtab = idx; | |
346 | if (verbose > 3) | |
347 | fprintf(stderr, "[%s:%d] Symbol table offset: "LU" size: "LU" " | |
348 | "entsize: "LU" entries: "LU"\n", | |
349 | __func__, __LINE__, sechdrs[idx].sh_offset, | |
350 | sechdrs[idx].sh_size, sechdrs[idx].sh_entsize, | |
351 | (sechdrs[idx].sh_entsize > 0 ? sechdrs[idx].sh_size / sechdrs[idx].sh_entsize : 0)); | |
352 | break; | |
353 | case SHT_STRTAB: | |
354 | if (idx != ei->secnametbl_idx) { | |
355 | strtab = idx; | |
356 | if (verbose > 2) | |
357 | fprintf(stderr, "[%s:%d] Reading symbol table from %s\n", | |
358 | __func__, __LINE__, name); | |
359 | if (symtab >= 0 && exe_load_symbol_table(ei, &sechdrs[symtab], | |
360 | &sechdrs[strtab], verbose) < 0) { | |
361 | fprintf(stderr, "[%s:%d] Failed to retrieve symbol " | |
362 | "table.\n", __func__, __LINE__); | |
363 | } | |
364 | symtab = -1; | |
365 | } | |
366 | break; | |
367 | default: | |
368 | break; | |
369 | } | |
370 | } | |
371 | return 0; | |
372 | } | |
373 | ||
374 | static int exe_load_program_headers(struct elf_internals *ei, int verbose) | |
375 | { | |
376 | Elf_Phdr *proghdrs = NULL; | |
377 | size_t idx = 0; | |
378 | int rc = 0; | |
379 | if (!ei || ei->proghdr_offset == 0 || ei->proghdr_size == 0) | |
380 | return -1; | |
381 | ei->proghdrs = malloc(ei->proghdr_size); | |
382 | if (!ei->proghdrs) { | |
383 | LOG_ERROR_OUT_OF_MEMORY; | |
384 | return -1; | |
385 | } | |
386 | memset(ei->proghdrs, 0, ei->proghdr_size); | |
387 | if (lseek(ei->fd, ei->proghdr_offset, SEEK_SET) < 0) { | |
388 | LOG_ERROR_FILE_SEEK; | |
389 | return -1; | |
390 | } | |
391 | if (read(ei->fd, ei->proghdrs, ei->proghdr_size) < 0) { | |
392 | LOG_ERROR_FILE_READ; | |
393 | return -1; | |
394 | } | |
395 | if (verbose > 3) | |
396 | fprintf(stderr, "[%s:%d] Number of segments: "LU"\n", __func__, __LINE__, | |
397 | ei->proghdr_num); | |
398 | proghdrs = (Elf_Phdr *)ei->proghdrs; | |
399 | for (idx = 0; idx < ei->proghdr_num; ++idx) { | |
400 | rc = 0; | |
401 | if (verbose > 2) { | |
402 | fprintf(stderr, | |
403 | "[%s:%d] Prog-header "LU": Type: %d " | |
404 | "VAddr: %p PAddr: %p FileSz: "LU" MemSz: "LU"\n", | |
405 | __func__, __LINE__, idx, proghdrs[idx].p_type, | |
406 | (void *)proghdrs[idx].p_vaddr, | |
407 | (void *)proghdrs[idx].p_paddr, | |
408 | proghdrs[idx].p_filesz, proghdrs[idx].p_memsz); | |
409 | } | |
410 | if (proghdrs[idx].p_type == PT_INTERP) { | |
411 | if (verbose > 1) | |
412 | fprintf(stderr, "[%s:%d] PT_INTERP section found\n", __func__, | |
413 | __LINE__); | |
414 | if (proghdrs[idx].p_filesz == 0) | |
415 | continue; | |
416 | if (lseek(ei->fd, proghdrs[idx].p_offset, SEEK_SET) < 0) { | |
417 | LOG_ERROR_FILE_SEEK; | |
418 | rc = -1; | |
419 | break; | |
420 | } | |
421 | if (ei->interp.name) { | |
422 | free(ei->interp.name); | |
423 | memset(&ei->interp, 0, sizeof(ei->interp)); | |
424 | } | |
425 | ei->interp.name = malloc(proghdrs[idx].p_filesz); | |
426 | if (!ei->interp.name) { | |
427 | LOG_ERROR_OUT_OF_MEMORY; | |
428 | rc = -1; | |
429 | break; | |
430 | } | |
431 | if (read(ei->fd, ei->interp.name, proghdrs[idx].p_filesz) < 0) { | |
432 | LOG_ERROR_FILE_READ; | |
433 | rc = -1; | |
434 | break; | |
435 | } | |
436 | ei->interp.length = proghdrs[idx].p_filesz; | |
437 | ei->interp.ph_addr = proghdrs[idx].p_vaddr; | |
438 | if (verbose > 1) | |
439 | fprintf(stderr, "[%s:%d] Found %s at V-Addr 0x"LX"\n", | |
440 | __func__, __LINE__, ei->interp.name, | |
441 | ei->interp.ph_addr); | |
442 | } else if (proghdrs[idx].p_type == PT_DYNAMIC) { | |
443 | if (verbose > 1) | |
444 | fprintf(stderr, "[%s:%d] PT_DYNAMIC section found\n", __func__, | |
445 | __LINE__); | |
446 | } else if (proghdrs[idx].p_type == PT_LOAD) { | |
447 | if (verbose > 1) | |
448 | fprintf(stderr, "[%s:%d] PT_LOAD section found\n", __func__, | |
449 | __LINE__); | |
450 | } | |
451 | } | |
452 | return rc; | |
453 | } | |
454 | ||
455 | static int exe_load_headers(struct elf_internals *ei, int verbose) | |
456 | { | |
457 | Elf_Ehdr hdr; | |
458 | int fd = -1; | |
459 | if (!ei) { | |
460 | return -1; | |
461 | } | |
462 | fd = ei->fd; | |
463 | memset(&hdr, 0, sizeof(hdr)); | |
464 | if (lseek(fd, 0, SEEK_SET) < 0) { | |
465 | LOG_ERROR_FILE_SEEK; | |
466 | return -1; | |
467 | } | |
468 | if (read(fd, &hdr, sizeof(hdr)) < 0) { | |
469 | LOG_ERROR_FILE_READ; | |
470 | return -1; | |
471 | } | |
472 | if (verbose > 3) | |
473 | fprintf(stderr, "[%s:%d] Reading Elf header.\n", __func__, __LINE__); | |
474 | ei->is64 = exe_elf_identify(hdr.e_ident, EI_NIDENT, verbose); | |
475 | switch (ei->is64) { | |
476 | case HOTPATCH_EXE_IS_64BIT: | |
477 | if (verbose > 3) | |
478 | fprintf(stderr, "[%s:%d] 64-bit valid exe\n", __func__, __LINE__); | |
479 | break; | |
480 | case HOTPATCH_EXE_IS_32BIT: | |
481 | if (verbose > 3) | |
482 | fprintf(stderr, "[%s:%d] 32-bit valid exe\n", __func__, __LINE__); | |
483 | break; | |
484 | case HOTPATCH_EXE_IS_NEITHER: | |
485 | default: | |
486 | return -1; | |
487 | } | |
488 | if (verbose > 1) | |
489 | fprintf(stderr, "[%s:%d] Entry point %p\n", __func__, __LINE__, | |
490 | (void *)hdr.e_entry); | |
491 | ei->entry_point = (uintptr_t)hdr.e_entry; | |
492 | if (hdr.e_machine != EM_X86_64 && hdr.e_machine != EM_386) { | |
493 | LOG_ERROR_UNSUPPORTED_PROCESSOR; | |
494 | return -1; | |
495 | } | |
496 | if (hdr.e_shoff > 0) { | |
497 | ei->sechdr_offset = 0 + hdr.e_shoff; | |
498 | ei->sechdr_num = 0 + hdr.e_shnum; | |
499 | ei->sechdr_size = 0 + hdr.e_shnum * hdr.e_shentsize; | |
500 | ei->secnametbl_idx = 0 + hdr.e_shstrndx; | |
501 | } | |
502 | if (hdr.e_phoff > 0) { | |
503 | ei->proghdr_offset = 0 + hdr.e_phoff; | |
504 | ei->proghdr_num = 0 + hdr.e_phnum; | |
505 | ei->proghdr_size = 0 + hdr.e_phnum * hdr.e_phentsize; | |
506 | } | |
507 | if (exe_load_section_headers(ei, verbose) < 0) { | |
508 | fprintf(stderr, "[%s:%d] Error in loading section headers\n", | |
509 | __func__, __LINE__); | |
510 | return -1; | |
511 | } | |
512 | if (exe_load_program_headers(ei, verbose) < 0) { | |
513 | fprintf(stderr, "[%s:%d] Error in loading section headers\n", | |
514 | __func__, __LINE__); | |
515 | return -1; | |
516 | } | |
517 | return 0; | |
518 | } | |
519 | ||
520 | struct elf_symbol *exe_load_symbols(const char *filename, int verbose, | |
521 | size_t *symbols_num, | |
522 | uintptr_t *entry_point, | |
523 | struct elf_interp *interp, | |
524 | enum elf_bit *is64) | |
525 | { | |
526 | int rc = 0; | |
527 | struct elf_symbol *symbols = NULL; | |
528 | struct elf_internals ei; | |
529 | memset(&ei, 0, sizeof(ei)); | |
530 | if (entry_point) | |
531 | *entry_point = 0; | |
532 | ei.fd = exe_open_filename(filename, verbose); | |
533 | if (ei.fd < 0) { | |
534 | return NULL; | |
535 | } | |
536 | if ((rc = exe_load_headers(&ei, verbose)) < 0) { | |
537 | fprintf(stderr, "[%s:%d] Unable to load Elf details for %s\n", | |
538 | __func__, __LINE__, filename); | |
539 | } | |
540 | if (verbose > 3) | |
541 | fprintf(stderr, "[%s:%d] Freeing internal structure.\n", | |
542 | __func__, __LINE__); | |
543 | if (ei.fd >= 0) | |
544 | close(ei.fd); | |
545 | ei.fd = -1; | |
546 | ei.strsectbl_size = 0; | |
547 | if (ei.strsectbl) { | |
548 | free(ei.strsectbl); | |
549 | ei.strsectbl = NULL; | |
550 | } | |
551 | if (ei.sechdrs) { | |
552 | free(ei.sechdrs); | |
553 | ei.sechdrs = NULL; | |
554 | } | |
555 | if (ei.proghdrs) { | |
556 | free(ei.proghdrs); | |
557 | ei.proghdrs = NULL; | |
558 | } | |
559 | if (rc < 0) { | |
560 | if (ei.interp.name) | |
561 | free(ei.interp.name); | |
562 | ei.interp.name = NULL; | |
563 | if (ei.symbols) { | |
564 | size_t idx; | |
565 | for (idx = 0; idx < ei.symbols_num; ++idx) { | |
566 | free(ei.symbols[idx].name); | |
567 | ei.symbols[idx].name = NULL; | |
568 | } | |
569 | free(ei.symbols); | |
570 | } | |
571 | ei.symbols = NULL; | |
572 | ei.symbols_num = 0; | |
573 | } else { | |
574 | if (verbose > 2) | |
575 | fprintf(stderr, "[%s:%d] Readying return values.\n", | |
576 | __func__, __LINE__); | |
577 | symbols = ei.symbols; | |
578 | if (symbols_num) | |
579 | *symbols_num = ei.symbols_num; | |
580 | if (interp) { | |
581 | interp->name = ei.interp.name; | |
582 | interp->length = ei.interp.length; | |
583 | interp->ph_addr = ei.interp.ph_addr; | |
584 | } else { | |
585 | if (ei.interp.name) | |
586 | free(ei.interp.name); | |
587 | ei.interp.name = NULL; | |
588 | } | |
589 | if (is64) | |
590 | *is64 = ei.is64; | |
591 | if (entry_point) | |
592 | *entry_point = ei.entry_point; | |
593 | } | |
594 | return symbols; | |
595 | } | |
596 | ||
597 | int elf_symbol_cmpqsort(const void *p1, const void *p2) | |
598 | { | |
599 | return strcmp(((const struct elf_symbol *)p1)->name, | |
600 | ((const struct elf_symbol *)p2)->name); | |
601 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #include <hotpatch_internal.h> | |
32 | #include <hotpatch.h> | |
33 | #ifdef HOTPATCH_USE_ASM | |
34 | #include <call32.h> | |
35 | #include <call64.h> | |
36 | #endif | |
37 | ||
38 | #define LIB_LD "ld" | |
39 | #define LIB_C "libc" | |
40 | #define LIB_DL "libdl" | |
41 | #define LIB_PTHREAD "libpthread" | |
42 | ||
43 | static int hotpatch_gather_functions(hotpatch_t *hp) | |
44 | { | |
45 | int verbose = 0; | |
46 | bool ld_found = false; | |
47 | bool c_found = false; | |
48 | bool dl_found = false; | |
49 | bool pthread_found = false; | |
50 | if (!hp || !hp->libs) | |
51 | return -1; | |
52 | verbose = hp->verbose; | |
53 | if (hp->ld_maps_num <= 0) | |
54 | return -1; | |
55 | memset(hp->libs, 0, sizeof(hp->libs)); | |
56 | #undef LD_PROCMAPS_FIND_LIB | |
57 | #define LD_PROCMAPS_FIND_LIB(name,flag,index,retval) \ | |
58 | do { \ | |
59 | if (verbose > 2) \ | |
60 | fprintf(stderr, "[%s:%d] Checking if %s exists in procmaps.\n",\ | |
61 | __func__, __LINE__, name);\ | |
62 | if (ld_find_library(hp->ld_maps, hp->ld_maps_num, \ | |
63 | name, flag, &hp->libs[index], verbose) < 0) { \ | |
64 | if (verbose > 0) \ | |
65 | fprintf(stderr, "[%s:%d] %s not mapped.\n", \ | |
66 | __func__, __LINE__, name); \ | |
67 | retval = false; \ | |
68 | } else { \ | |
69 | retval = true; \ | |
70 | if (verbose > 2) \ | |
71 | fprintf(stderr, "[%s:%d] Found %s\n", \ | |
72 | __func__, __LINE__, name); \ | |
73 | } \ | |
74 | } while (0) | |
75 | #undef LD_LIB_FIND_FN_ADDR | |
76 | #define LD_LIB_FIND_FN_ADDR(fn,outfn,index) \ | |
77 | do { \ | |
78 | if (outfn) break; \ | |
79 | outfn = ld_find_address(&hp->libs[HOTPATCH_##index], fn, verbose); \ | |
80 | if (outfn != 0) { \ | |
81 | if (verbose > 0) \ | |
82 | fprintf(stderr, "[%s:%d] Found %s at 0x"LX" in %s\n", \ | |
83 | __func__, __LINE__, fn, outfn, index); \ | |
84 | } else { \ | |
85 | if (verbose > 0) \ | |
86 | fprintf(stderr, "[%s:%d] %s not found in %s.\n", \ | |
87 | __func__, __LINE__, fn, index); \ | |
88 | } \ | |
89 | } while (0) | |
90 | if (hp->exe_interp.name) { | |
91 | LD_PROCMAPS_FIND_LIB(hp->exe_interp.name, true, HOTPATCH_LIB_LD, | |
92 | ld_found); | |
93 | } | |
94 | if (!ld_found) { | |
95 | if (verbose > 1) | |
96 | fprintf(stderr, "[%s:%d] No interpreter found. Guessing.\n", | |
97 | __func__, __LINE__); | |
98 | LD_PROCMAPS_FIND_LIB(LIB_LD, false, HOTPATCH_LIB_LD, ld_found); | |
99 | } | |
100 | LD_PROCMAPS_FIND_LIB(LIB_C, false, HOTPATCH_LIB_C, c_found); | |
101 | LD_PROCMAPS_FIND_LIB(LIB_DL, false, HOTPATCH_LIB_DL, dl_found); | |
102 | LD_PROCMAPS_FIND_LIB(LIB_PTHREAD, false, HOTPATCH_LIB_PTHREAD, | |
103 | pthread_found); | |
104 | if (c_found) { | |
105 | LD_LIB_FIND_FN_ADDR("malloc", hp->fn_malloc, LIB_C); | |
106 | LD_LIB_FIND_FN_ADDR("realloc", hp->fn_realloc, LIB_C); | |
107 | LD_LIB_FIND_FN_ADDR("free", hp->fn_free, LIB_C); | |
108 | } | |
109 | if (ld_found) { | |
110 | LD_LIB_FIND_FN_ADDR("malloc", hp->fn_malloc, LIB_LD); | |
111 | LD_LIB_FIND_FN_ADDR("realloc", hp->fn_realloc, LIB_LD); | |
112 | LD_LIB_FIND_FN_ADDR("free", hp->fn_free, LIB_LD); | |
113 | } | |
114 | if (!hp->fn_malloc || !hp->fn_realloc || !hp->fn_free) { | |
115 | if (verbose > 0) | |
116 | fprintf(stderr, "[%s:%d] Some memory allocation routines are" | |
117 | " unavailable. Cannot proceed.\n", __func__, __LINE__); | |
118 | return -1; | |
119 | } | |
120 | if (dl_found) { | |
121 | LD_LIB_FIND_FN_ADDR("dlopen", hp->fn_dlopen, LIB_DL); | |
122 | LD_LIB_FIND_FN_ADDR("dlclose", hp->fn_dlclose, LIB_DL); | |
123 | LD_LIB_FIND_FN_ADDR("dlsym", hp->fn_dlsym, LIB_DL); | |
124 | } else { | |
125 | LD_LIB_FIND_FN_ADDR("__libc_dlopen_mode", hp->fn_dlopen, LIB_C); | |
126 | LD_LIB_FIND_FN_ADDR("__libc_dlclose", hp->fn_dlclose, LIB_C); | |
127 | LD_LIB_FIND_FN_ADDR("__libc_dlsym", hp->fn_dlsym, LIB_C); | |
128 | } | |
129 | if (!hp->fn_dlopen || !hp->fn_dlsym) { | |
130 | if (verbose > 0) | |
131 | fprintf(stderr, "[%s:%d] Dynamic Library loading routines were not" | |
132 | " found. Cannot proceed.\n", __func__, __LINE__); | |
133 | return -1; | |
134 | } | |
135 | if (pthread_found) { | |
136 | LD_LIB_FIND_FN_ADDR("pthread_create", hp->fn_pthread_create, | |
137 | LIB_PTHREAD); | |
138 | LD_LIB_FIND_FN_ADDR("pthread_detach", hp->fn_pthread_detach, | |
139 | LIB_PTHREAD); | |
140 | } else { | |
141 | hp->fn_pthread_create = hp->fn_pthread_detach = 0; | |
142 | } | |
143 | if (verbose > 1) { | |
144 | if (hp->fn_pthread_create && hp->fn_pthread_detach) | |
145 | fprintf(stderr, "[%s:%d] Pthread's symbol found. Do not need more" | |
146 | " magic.\n", __func__, __LINE__); | |
147 | else | |
148 | fprintf(stderr, "[%s:%d] Pthread's symbol not found. Will disable" | |
149 | " pthread usage in injection.\n", __func__, __LINE__); | |
150 | } | |
151 | #undef LD_PROCMAPS_FIND_LIB | |
152 | #undef LD_LIB_FIND_FN_ADDR | |
153 | return 0; | |
154 | } | |
155 | ||
156 | void hotpatch_version(int *major, int *minor) | |
157 | { | |
158 | if (major) | |
159 | *major = HOTPATCH_MAJOR_VERSION; | |
160 | if (minor) | |
161 | *minor = HOTPATCH_MINOR_VERSION; | |
162 | } | |
163 | ||
164 | hotpatch_t *hotpatch_create(pid_t pid, int verbose) | |
165 | { | |
166 | int rc = 0; | |
167 | hotpatch_t *hp = NULL; | |
168 | do { | |
169 | char filename[OS_MAX_BUFFER]; | |
170 | if (pid <= 0) { | |
171 | LOG_ERROR_INVALID_PID(pid); | |
172 | break; | |
173 | } | |
174 | memset(filename, 0, sizeof(filename)); | |
175 | snprintf(filename, sizeof(filename), "/proc/%d/exe", pid); | |
176 | if (verbose > 3) | |
177 | fprintf(stderr, "[%s:%d] Exe symlink for pid %d : %s\n", __func__, | |
178 | __LINE__, pid, filename); | |
179 | hp = malloc(sizeof(*hp)); | |
180 | if (!hp) { | |
181 | LOG_ERROR_OUT_OF_MEMORY; | |
182 | rc = -1; | |
183 | break; | |
184 | } | |
185 | memset(hp, 0, sizeof(*hp)); | |
186 | hp->verbose = verbose; | |
187 | hp->pid = pid; | |
188 | hp->is64 = HOTPATCH_EXE_IS_NEITHER; | |
189 | hp->exe_symbols = exe_load_symbols(filename, hp->verbose, | |
190 | &hp->exe_symbols_num, | |
191 | &hp->exe_entry_point, | |
192 | &hp->exe_interp, | |
193 | &hp->is64); | |
194 | if (!hp->exe_symbols) { | |
195 | fprintf(stderr, "[%s:%d] Unable to find any symbols in exe.\n", | |
196 | __func__, __LINE__); | |
197 | rc = -1; | |
198 | break; | |
199 | } | |
200 | if (hp->exe_entry_point == 0) { | |
201 | fprintf(stderr, "[%s:%d] Entry point is 0. Invalid.\n", | |
202 | __func__, __LINE__); | |
203 | rc = -1; | |
204 | break; | |
205 | } | |
206 | LOG_INFO_HEADERS_LOADED(verbose); | |
207 | hp->ld_maps = ld_load_maps(hp->pid, hp->verbose, &hp->ld_maps_num); | |
208 | if (!hp->ld_maps) { | |
209 | fprintf(stderr, "[%s:%d] Unable to load data in " | |
210 | "/proc/%d/maps.\n", __func__, __LINE__, pid); | |
211 | rc = -1; | |
212 | break; | |
213 | } | |
214 | if (verbose > 2) | |
215 | fprintf(stderr, "[%s:%d] /proc/%d/maps loaded.\n", | |
216 | __func__, __LINE__, pid); | |
217 | if (hp->exe_symbols && hp->exe_symbols_num > 0) { | |
218 | qsort(hp->exe_symbols, hp->exe_symbols_num, | |
219 | sizeof(*hp->exe_symbols), elf_symbol_cmpqsort); | |
220 | } | |
221 | if (hotpatch_gather_functions(hp) < 0) { | |
222 | fprintf(stderr, "[%s:%d] Unable to find all the functions" | |
223 | " needed. Cannot proceed.\n", __func__, __LINE__); | |
224 | rc = -1; | |
225 | break; | |
226 | } | |
227 | if (rc < 0) { | |
228 | hotpatch_destroy(hp); | |
229 | hp = NULL; | |
230 | } | |
231 | } while (0); | |
232 | return hp; | |
233 | } | |
234 | ||
235 | void hotpatch_destroy(hotpatch_t *hp) | |
236 | { | |
237 | if (hp) { | |
238 | size_t idx; | |
239 | if (hp->attached) | |
240 | hotpatch_detach(hp); | |
241 | if (hp->exe_symbols) { | |
242 | for (idx = 0; idx < hp->exe_symbols_num; ++idx) { | |
243 | free(hp->exe_symbols[idx].name); | |
244 | hp->exe_symbols[idx].name = NULL; | |
245 | } | |
246 | free(hp->exe_symbols); | |
247 | } | |
248 | hp->exe_symbols = NULL; | |
249 | hp->exe_symbols_num = 0; | |
250 | if (hp->exe_interp.name) { | |
251 | free(hp->exe_interp.name); | |
252 | hp->exe_interp.name = NULL; | |
253 | } | |
254 | for (idx = 0; idx < HOTPATCH_LIB_MAX; ++idx) { | |
255 | if (hp->libs[idx].pathname) | |
256 | free(hp->libs[idx].pathname); | |
257 | hp->libs[idx].pathname = NULL; | |
258 | } | |
259 | memset(hp->libs, 0, sizeof(hp->libs)); | |
260 | if (hp->ld_maps) { | |
261 | ld_free_maps(hp->ld_maps, hp->ld_maps_num); | |
262 | hp->ld_maps = NULL; | |
263 | hp->ld_maps_num = 0; | |
264 | } | |
265 | free(hp); | |
266 | hp = NULL; | |
267 | } | |
268 | } | |
269 | ||
270 | uintptr_t hotpatch_read_symbol(hotpatch_t *hp, const char *symbol, int *type, size_t *sz) | |
271 | { | |
272 | uintptr_t ptr = 0; | |
273 | size_t idx = 0; | |
274 | if (!hp || !symbol || !hp->exe_symbols) { | |
275 | if (hp->verbose > 2) | |
276 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, __LINE__); | |
277 | return (uintptr_t)0; | |
278 | } | |
279 | for (idx = 0; idx < hp->exe_symbols_num; ++idx) { | |
280 | const char *name = hp->exe_symbols[idx].name; | |
281 | if (strcmp(name, symbol) == 0) { | |
282 | if (hp->verbose > 1) | |
283 | fprintf(stderr, "[%s:%d] Found %s in symbol list at "LU"\n", | |
284 | __func__, __LINE__, symbol, idx); | |
285 | ptr = hp->exe_symbols[idx].address; | |
286 | if (type) | |
287 | *type = hp->exe_symbols[idx].type; | |
288 | if (sz) | |
289 | *sz = hp->exe_symbols[idx].size; | |
290 | break; | |
291 | } | |
292 | } | |
293 | if (hp->verbose > 2) | |
294 | fprintf(stderr, "[%s:%d] Symbol %s has address 0x"LX"\n", __func__, | |
295 | __LINE__, symbol, ptr); | |
296 | return ptr; | |
297 | } | |
298 | ||
299 | uintptr_t hotpatch_get_entry_point(hotpatch_t *hp) | |
300 | { | |
301 | return hp ? hp->exe_entry_point : 0; | |
302 | } | |
303 | ||
304 | size_t hotpatch_strnlen(const char *str, size_t maxlen) | |
305 | { | |
306 | size_t len = 0; | |
307 | /* succinct code */ | |
308 | if (str) | |
309 | while (len < maxlen && str[len++] != '\0'); | |
310 | return len; | |
311 | } | |
312 | ||
313 | int hotpatch_attach(hotpatch_t *hp) | |
314 | { | |
315 | if (!hp) | |
316 | return -1; | |
317 | if (!hp->attached) { | |
318 | hp->attached = false; | |
319 | if (hp->verbose > 3) | |
320 | fprintf(stderr, "[%s:%d] Trying to attach to PID %d\n", __func__, | |
321 | __LINE__, hp->pid); | |
322 | if (ptrace(PTRACE_ATTACH, hp->pid, NULL, NULL) < 0) { | |
323 | int err = errno; | |
324 | fprintf(stderr, "[%s:%d] Ptrace Attach failed with error %s\n", | |
325 | __func__, __LINE__, strerror(err)); | |
326 | } else { | |
327 | int status = 0; | |
328 | if (hp->verbose > 1) | |
329 | fprintf(stderr, "[%s:%d] Waiting for the child.\n", __func__, | |
330 | __LINE__); | |
331 | if (waitpid(-1, &status, 0) < 0) { | |
332 | int err = errno; | |
333 | fprintf(stderr, "[%s:%d] Waitpid failed with error: %s\n", | |
334 | __func__, __LINE__, strerror(err)); | |
335 | } else { | |
336 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | |
337 | fprintf(stderr, "[%s:%d] PID %d was terminated.\n", | |
338 | __func__, __LINE__, hp->pid); | |
339 | } else { | |
340 | hp->attached = true; | |
341 | if (hp->verbose > 0) | |
342 | fprintf(stderr, "[%s:%d] Attached to PID %d\n", | |
343 | __func__, __LINE__, hp->pid); | |
344 | } | |
345 | } | |
346 | } | |
347 | } | |
348 | return hp->attached ? 0 : -1; | |
349 | } | |
350 | ||
351 | int hotpatch_detach(hotpatch_t *hp) | |
352 | { | |
353 | int rc = -1; | |
354 | if (hp && hp->attached) { | |
355 | if (hp->verbose > 3) | |
356 | fprintf(stderr, "[%s:%d] Detaching from PID %d\n", __func__, | |
357 | __LINE__, hp->pid); | |
358 | if (ptrace(PTRACE_DETACH, hp->pid, NULL, NULL) < 0) { | |
359 | int err = errno; | |
360 | fprintf(stderr, "[%s:%d] Ptrace detach failed with error %s\n", | |
361 | __func__, __LINE__, strerror(err)); | |
362 | } else { | |
363 | rc = 0; | |
364 | if (hp->verbose > 0) | |
365 | fprintf(stderr, "[%s:%d] Detached from PID %d\n", __func__, | |
366 | __LINE__, hp->pid); | |
367 | } | |
368 | hp->attached = false; | |
369 | } | |
370 | return rc; | |
371 | } | |
372 | ||
373 | ||
374 | static int hp_attach(pid_t pid) | |
375 | { | |
376 | if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { | |
377 | int err = errno; | |
378 | fprintf(stderr, | |
379 | "[%s:%d] Ptrace Attach for PID %d failed with error: %s\n", | |
380 | __func__, __LINE__, pid, strerror(err)); | |
381 | return -1; | |
382 | } | |
383 | return 0; | |
384 | } | |
385 | ||
386 | static int hp_detach(pid_t pid) | |
387 | { | |
388 | if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) { | |
389 | int err = errno; | |
390 | fprintf(stderr, | |
391 | "[%s:%d] Ptrace Detach for PID %d failed with error: %s\n", | |
392 | __func__, __LINE__, pid, strerror(err)); | |
393 | return -1; | |
394 | } | |
395 | return 0; | |
396 | } | |
397 | ||
398 | static int hp_exec(pid_t pid) | |
399 | { | |
400 | if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { | |
401 | int err = errno; | |
402 | fprintf(stderr, | |
403 | "[%s:%d] Ptrace Continue for PID %d failed with error: %s\n", | |
404 | __func__, __LINE__, pid, strerror(err)); | |
405 | return -1; | |
406 | } | |
407 | return 0; | |
408 | } | |
409 | ||
410 | static int hp_wait(pid_t pid) | |
411 | { | |
412 | int status = 0; | |
413 | if (waitpid(pid, &status, 0) < 0) { | |
414 | int err = errno; | |
415 | fprintf(stderr, "[%s:%d] Waitpid for PID %d failed with error: %s\n", | |
416 | __func__, __LINE__, pid, strerror(err)); | |
417 | return -1; | |
418 | } | |
419 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | |
420 | fprintf(stderr, "[%s:%d] PID %d was terminated.\n", | |
421 | __func__, __LINE__, pid); | |
422 | return -1; | |
423 | } | |
424 | return 0; | |
425 | } | |
426 | ||
427 | static int hp_get_regs(pid_t pid, struct user *regs) | |
428 | { | |
429 | if (!regs) | |
430 | return -1; | |
431 | memset(regs, 0, sizeof(*regs)); | |
432 | if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) { | |
433 | int err = errno; | |
434 | fprintf(stderr, | |
435 | "[%s:%d] Ptrace Getregs for PID %d failed with error: %s\n", | |
436 | __func__, __LINE__, pid, strerror(err)); | |
437 | return -1; | |
438 | } | |
439 | return 0; | |
440 | } | |
441 | ||
442 | static int hp_set_regs(pid_t pid, const struct user *regs) | |
443 | { | |
444 | if (!regs) | |
445 | return -1; | |
446 | if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) { | |
447 | int err = errno; | |
448 | fprintf(stderr, | |
449 | "[%s:%d] Ptrace Setregs for PID %d failed with error: %s\n", | |
450 | __func__, __LINE__, pid, strerror(err)); | |
451 | return -1; | |
452 | } | |
453 | return 0; | |
454 | } | |
455 | ||
456 | static int hp_copydata(pid_t pid, uintptr_t target, | |
457 | const unsigned char *data, size_t datasz, int verbose) | |
458 | { | |
459 | size_t pos = 0; | |
460 | size_t idx = 0; | |
461 | while (pos < datasz) { | |
462 | size_t pokedata = 0, jdx = 0; | |
463 | const size_t pksz = sizeof(size_t); | |
464 | for (jdx = 0; jdx < pksz && pos < datasz; ++jdx) | |
465 | ((unsigned char *)&pokedata)[jdx] = data[pos++]; | |
466 | if (verbose > 2) | |
467 | fprintf(stderr, "[%s:%d] Pokedata: %p\n", __func__, __LINE__, | |
468 | (void *)pokedata); | |
469 | if (ptrace(PTRACE_POKEDATA, pid, target + idx, | |
470 | pokedata) < 0) { | |
471 | int err = errno; | |
472 | fprintf(stderr, | |
473 | "[%s:%d] Ptrace PokeText for PID %d failed with error: %s\n", | |
474 | __func__, __LINE__, pid, strerror(err)); | |
475 | return -1; | |
476 | } | |
477 | idx += sizeof(size_t); | |
478 | } | |
479 | return 0; | |
480 | } | |
481 | ||
482 | static int hp_peekdata(pid_t pid, uintptr_t target, uintptr_t *outpeek, | |
483 | int verbose) | |
484 | { | |
485 | int err = 0; | |
486 | long peekdata = ptrace(PTRACE_PEEKDATA, pid, target, NULL); | |
487 | err = errno; | |
488 | if (verbose > 2) | |
489 | fprintf(stderr, "[%s:%d] Peekdata: %p\n", __func__, __LINE__, | |
490 | (void *)peekdata); | |
491 | if (peekdata == -1 && err != 0) { | |
492 | fprintf(stderr, | |
493 | "[%s:%d] Ptrace PeekText for PID %d failed with error: %s\n", | |
494 | __func__, __LINE__, pid, strerror(err)); | |
495 | return -1; | |
496 | } | |
497 | if (outpeek) | |
498 | *outpeek = peekdata; | |
499 | else | |
500 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, __LINE__); | |
501 | return outpeek ? 0 : -1; | |
502 | } | |
503 | ||
504 | static int hp_pokedata(pid_t pid, uintptr_t target, uintptr_t pokedata, | |
505 | int verbose) | |
506 | { | |
507 | int err = 0; | |
508 | if (verbose > 2) | |
509 | fprintf(stderr, "[%s:%d] Pokedata: %p\n", __func__, __LINE__, | |
510 | (void *)pokedata); | |
511 | if (ptrace(PTRACE_POKEDATA, pid, target, (void *)pokedata) < 0) { | |
512 | fprintf(stderr, | |
513 | "[%s:%d] Ptrace PokeText for PID %d failed with error: %s\n", | |
514 | __func__, __LINE__, pid, strerror(err)); | |
515 | return -1; | |
516 | } | |
517 | return 0; | |
518 | } | |
519 | ||
520 | int hotpatch_set_execution_pointer(hotpatch_t *hp, uintptr_t ptr) | |
521 | { | |
522 | #undef HP_REG_IP_STR | |
523 | #undef HP_REG_IP | |
524 | #undef HP_REG_SP | |
525 | #undef HP_REG_AX | |
526 | #if __WORDSIZE == 64 | |
527 | #define HP_REG_IP_STR "RIP" | |
528 | #define HP_REG_IP(A) A.regs.rip | |
529 | #define HP_REG_SP(A) A.regs.rsp | |
530 | #define HP_REG_AX(A) A.regs.rax | |
531 | #else | |
532 | #define HP_REG_IP_STR "EIP" | |
533 | #define HP_REG_IP(A) A.regs.eip | |
534 | #define HP_REG_SP(A) A.regs.esp | |
535 | #define HP_REG_AX(A) A.regs.eax | |
536 | #endif | |
537 | int rc = -1; | |
538 | if (ptr && hp && hp->attached) { | |
539 | struct user regs; | |
540 | memset(®s, 0, sizeof(regs)); | |
541 | if (ptrace(PTRACE_GETREGS, hp->pid, NULL, ®s) < 0) { | |
542 | int err = errno; | |
543 | fprintf(stderr, "[%s:%d] Ptrace getregs failed with error %s\n", | |
544 | __func__, __LINE__, strerror(err)); | |
545 | } else { | |
546 | if (hp->verbose > 1) | |
547 | fprintf(stderr, "[%s:%d] %s is %p\n", __func__, __LINE__, | |
548 | HP_REG_IP_STR, | |
549 | (void *)HP_REG_IP(regs)); | |
550 | if (ptr == hp->exe_entry_point) | |
551 | ptr += sizeof(void *); | |
552 | HP_REG_IP(regs) = ptr; | |
553 | if (ptrace(PTRACE_SETREGS, hp->pid, NULL, ®s) < 0) { | |
554 | int err = errno; | |
555 | fprintf(stderr, "[%s:%d] Ptrace setregs failed with error %s\n", | |
556 | __func__, __LINE__, strerror(err)); | |
557 | } else { | |
558 | if (hp->verbose > 0) | |
559 | fprintf(stderr, "[%s:%d] Set %s to 0x"LX"\n", | |
560 | __func__, __LINE__, HP_REG_IP_STR, ptr); | |
561 | rc = 0; | |
562 | } | |
563 | } | |
564 | } else { | |
565 | if (!ptr) { | |
566 | fprintf(stderr, "[%s:%d] The execution pointer is null.\n", | |
567 | __func__, __LINE__); | |
568 | } | |
569 | if (!hp || !hp->attached) { | |
570 | fprintf(stderr, "[%s:%d] The process is not attached to.\n", | |
571 | __func__, __LINE__); | |
572 | } | |
573 | } | |
574 | return rc; | |
575 | } | |
576 | ||
577 | int hotpatch_inject_library(hotpatch_t *hp, const char *dll, const char *symbol, | |
578 | const unsigned char *data, size_t datalen, | |
579 | uintptr_t *outaddr, uintptr_t *outres) | |
580 | { | |
581 | size_t dllsz = 0; | |
582 | size_t symsz = 0; | |
583 | size_t datasz = 0; | |
584 | size_t tgtsz = 0; | |
585 | int rc = 0; | |
586 | unsigned char *mdata = NULL; | |
587 | if (!dll || !hp) { | |
588 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, __LINE__); | |
589 | return -1; | |
590 | } | |
591 | if (!hp->fn_malloc || !hp->fn_dlopen) { | |
592 | fprintf(stderr, "[%s:%d] No malloc/dlopen found.\n", __func__, | |
593 | __LINE__); | |
594 | return -1; | |
595 | } | |
596 | /* calculate the size to allocate */ | |
597 | dllsz = strlen(dll) + 1; | |
598 | symsz = symbol ? (strlen(symbol) + 1) : 0; | |
599 | datasz = data ? datalen : 0; | |
600 | tgtsz = dllsz + symsz + datasz + 32; /* general buffer */ | |
601 | tgtsz = (tgtsz > 1024) ? tgtsz : 1024; | |
602 | /* align the memory */ | |
603 | tgtsz += (tgtsz % sizeof(void *) == 0) ? 0 : | |
604 | (sizeof(void *) - (tgtsz % sizeof(void *))); | |
605 | mdata = calloc(sizeof(unsigned char), tgtsz); | |
606 | if (!mdata) { | |
607 | LOG_ERROR_OUT_OF_MEMORY; | |
608 | return -1; | |
609 | } | |
610 | memcpy(mdata, dll, dllsz); | |
611 | if (symbol) { | |
612 | memcpy(mdata + dllsz, symbol, symsz); | |
613 | } | |
614 | if (data) { | |
615 | memcpy(mdata + dllsz + symsz, data, datasz); | |
616 | } | |
617 | if (hp->verbose > 0) | |
618 | fprintf(stderr, "[%s:%d] Allocating "LU" bytes in the target.\n", | |
619 | __func__, __LINE__, tgtsz); | |
620 | do { | |
621 | /* The stack is read-write and not executable */ | |
622 | struct user iregs; /* intermediate registers */ | |
623 | struct user oregs; /* original registers */ | |
624 | int verbose = hp->verbose; | |
625 | uintptr_t result = 0; | |
626 | uintptr_t stack[4] = { 0, 0, 0, 0}; /* max arguments of the functions we | |
627 | are using */ | |
628 | uintptr_t heapptr = 0; | |
629 | int idx = 0; | |
630 | #undef HP_SETEXECWAITGET | |
631 | #undef HP_NULLIFYSTACK | |
632 | #undef HP_PASS_ARGS2FUNC | |
633 | #define HP_NULLIFYSTACK() \ | |
634 | do { \ | |
635 | uintptr_t nullcode = 0; \ | |
636 | if (verbose > 1) \ | |
637 | fprintf(stderr, "[%s:%d] Copying Null to stack.\n", \ | |
638 | __func__, __LINE__); \ | |
639 | if ((rc = hp_pokedata(hp->pid, HP_REG_SP(iregs), nullcode, verbose)) < 0) \ | |
640 | break; \ | |
641 | } while (0) | |
642 | ||
643 | #define HP_SETEXECWAITGET(fn) \ | |
644 | do { \ | |
645 | if (verbose > 1) \ | |
646 | fprintf(stderr, "[%s:%d] Setting registers and invoking %s.\n", \ | |
647 | __func__, __LINE__, fn); \ | |
648 | if ((rc = hp_set_regs(hp->pid, &iregs)) < 0) \ | |
649 | break; \ | |
650 | if (verbose > 1) \ | |
651 | fprintf(stderr, "[%s:%d] Executing...\n", __func__, __LINE__); \ | |
652 | if ((rc = hp_exec(hp->pid)) < 0) \ | |
653 | break; \ | |
654 | if (verbose > 1) \ | |
655 | fprintf(stderr, "[%s:%d] Waiting...\n", __func__, __LINE__); \ | |
656 | if ((rc = hp_wait(hp->pid)) < 0) \ | |
657 | break; \ | |
658 | if (verbose > 1) \ | |
659 | fprintf(stderr, "[%s:%d] Getting registers.\n", __func__, __LINE__); \ | |
660 | if ((rc = hp_get_regs(hp->pid, &iregs)) < 0) \ | |
661 | break; \ | |
662 | } while (0) | |
663 | #if __WORDSIZE == 64 | |
664 | #define HP_PASS_ARGS2FUNC(A,FN,ARG1,ARG2) \ | |
665 | do { \ | |
666 | A.regs.rsi = ARG2; \ | |
667 | A.regs.rdi = ARG1; \ | |
668 | A.regs.rip = FN; \ | |
669 | A.regs.rax = 0; \ | |
670 | } while (0) | |
671 | #else /* __WORDSIZE == 64 */ | |
672 | #define HP_PASS_ARGS2FUNC(A,FN,ARG1,ARG2) \ | |
673 | do { \ | |
674 | if (verbose > 1) \ | |
675 | fprintf(stderr, "[%s:%d] Copying Arg 1 to stack.\n", \ | |
676 | __func__, __LINE__); \ | |
677 | if ((rc = hp_pokedata(hp->pid, HP_REG_SP(iregs) + sizeof(size_t), \ | |
678 | ARG1, verbose)) < 0) \ | |
679 | break; \ | |
680 | if (verbose > 1) \ | |
681 | fprintf(stderr, "[%s:%d] Copying Arg 2 to stack.\n", \ | |
682 | __func__, __LINE__); \ | |
683 | if ((rc = hp_pokedata(hp->pid, HP_REG_SP(iregs) + 2 * sizeof(size_t), \ | |
684 | ARG2, verbose)) < 0) \ | |
685 | break; \ | |
686 | A.regs.eip = FN; \ | |
687 | A.regs.eax = 0; \ | |
688 | } while (0) | |
689 | #endif /* __WORDSIZE == 64 */ | |
690 | /* Prepare the child for injection */ | |
691 | if (verbose > 1) | |
692 | fprintf(stderr, "[%s:%d] Attaching to PID %d\n", __func__, | |
693 | __LINE__, hp->pid); | |
694 | if ((rc = hp_attach(hp->pid)) < 0) | |
695 | break; | |
696 | if (verbose > 1) | |
697 | fprintf(stderr, "[%s:%d] Waiting...\n", __func__, __LINE__); | |
698 | if ((rc = hp_wait(hp->pid)) < 0) | |
699 | break; | |
700 | if (verbose > 1) | |
701 | fprintf(stderr, "[%s:%d] Getting original registers.\n", | |
702 | __func__, __LINE__); | |
703 | if ((rc = hp_get_regs(hp->pid, &oregs)) < 0) | |
704 | break; | |
705 | memcpy(&iregs, &oregs, sizeof(oregs)); | |
706 | if (verbose > 1) | |
707 | fprintf(stderr, "[%s:%d] Copying stack out.\n", __func__, __LINE__); | |
708 | for (idx = 0; idx < sizeof(stack)/sizeof(uintptr_t); ++idx) { | |
709 | if ((rc = hp_peekdata(hp->pid, HP_REG_SP(iregs) + | |
710 | idx * sizeof(size_t), &stack[idx], verbose)) < 0) | |
711 | break; | |
712 | } | |
713 | if (rc < 0) | |
714 | break; | |
715 | /* Call malloc */ | |
716 | HP_NULLIFYSTACK(); | |
717 | HP_PASS_ARGS2FUNC(iregs, hp->fn_malloc, tgtsz, 0); | |
718 | HP_SETEXECWAITGET("malloc"); | |
719 | result = HP_REG_AX(iregs); | |
720 | heapptr = HP_REG_AX(iregs); /* keep a copy of this pointer */ | |
721 | /* Copy data to the malloced area */ | |
722 | if (verbose > 1) | |
723 | fprintf(stderr, "[%s:%d] Copying "LU" bytes to 0x"LX".\n", __func__, | |
724 | __LINE__, tgtsz, result); | |
725 | if (!result) | |
726 | break; | |
727 | if ((rc = hp_copydata(hp->pid, result, mdata, tgtsz, verbose)) < 0) | |
728 | break; | |
729 | /* Call dlopen */ | |
730 | HP_NULLIFYSTACK(); | |
731 | HP_PASS_ARGS2FUNC(iregs, hp->fn_dlopen, result /* value from malloc */, | |
732 | (RTLD_LAZY | RTLD_GLOBAL)); | |
733 | HP_SETEXECWAITGET("dlopen"); | |
734 | result = HP_REG_AX(iregs); | |
735 | if (verbose > 0) | |
736 | fprintf(stderr, "[%s:%d] Dll opened at 0x"LX"\n", __func__, __LINE__, | |
737 | result); | |
738 | if (outaddr) | |
739 | *outaddr = result; | |
740 | /* Call dlsym */ | |
741 | if (symbol && hp->fn_dlsym && result != 0) { | |
742 | HP_NULLIFYSTACK(); | |
743 | HP_PASS_ARGS2FUNC(iregs, hp->fn_dlsym, | |
744 | result /* value from dlopen */, | |
745 | (heapptr + dllsz)); | |
746 | HP_SETEXECWAITGET("dlsym"); | |
747 | result = HP_REG_AX(iregs); | |
748 | if (verbose > 0) | |
749 | fprintf(stderr, "[%s:%d] Symbol %s found at 0x"LX"\n", | |
750 | __func__, __LINE__, symbol, result); | |
751 | if (result != 0) { | |
752 | HP_NULLIFYSTACK(); | |
753 | if (datasz > 0) { | |
754 | HP_PASS_ARGS2FUNC(iregs, result /* value from dlsym */, | |
755 | (heapptr + dllsz + symsz), datasz); | |
756 | } else { | |
757 | HP_PASS_ARGS2FUNC(iregs, result /* value from dlsym */, | |
758 | 0, 0); | |
759 | } | |
760 | HP_SETEXECWAITGET(symbol); | |
761 | result = HP_REG_AX(iregs); | |
762 | if (verbose > 0) | |
763 | fprintf(stderr, "[%s:%d] Return value from invoking %s(): %p\n", | |
764 | __func__, __LINE__, symbol, (void *)result); | |
765 | if (outres) | |
766 | *outres = result; | |
767 | } else { | |
768 | if (verbose > 0) | |
769 | fprintf(stderr, "[%s:%d] Unable to find %s(). Dll might " | |
770 | "already have been injected earlier.\n", | |
771 | __func__, __LINE__, symbol); | |
772 | if (outres) | |
773 | *outres = 0; | |
774 | } | |
775 | } else { | |
776 | if (verbose > 1 && symbol) | |
777 | fprintf(stderr, "[%s:%d] %s not invoked as dlsym() wasn't " | |
778 | "found.\n", __func__, __LINE__, symbol); | |
779 | else if (verbose > 1) | |
780 | fprintf(stderr, "[%s:%d] No symbol was specified. _init() might" | |
781 | " have been invoked.\n", __func__, __LINE__); | |
782 | if (outres) | |
783 | *outres = 0; | |
784 | } | |
785 | /* Original reset */ | |
786 | if (verbose > 1) | |
787 | fprintf(stderr, "[%s:%d] Setting original registers.\n", | |
788 | __func__, __LINE__); | |
789 | if ((rc = hp_set_regs(hp->pid, &oregs)) < 0) { | |
790 | fprintf(stderr, "[%s:%d] PID %d will be unstable.\n", __func__, | |
791 | __LINE__, hp->pid); | |
792 | break; | |
793 | } | |
794 | if (verbose > 1) | |
795 | fprintf(stderr, "[%s:%d] Copying stack back.\n", | |
796 | __func__, __LINE__); | |
797 | for (idx = 0; idx < sizeof(stack)/sizeof(uintptr_t); ++idx) { | |
798 | if ((rc = hp_pokedata(hp->pid, HP_REG_SP(oregs) + | |
799 | idx * sizeof(size_t), stack[idx], verbose)) < 0) | |
800 | break; | |
801 | } | |
802 | if (rc < 0) | |
803 | break; | |
804 | if (verbose > 1) | |
805 | fprintf(stderr, "[%s:%d] Executing...\n", __func__, __LINE__); | |
806 | if ((rc = hp_exec(hp->pid)) < 0) | |
807 | break; | |
808 | } while (0); | |
809 | if (rc < 0) { | |
810 | if (hp->verbose > 1) | |
811 | fprintf(stderr, "[%s:%d] Detaching from PID %d\n", __func__, | |
812 | __LINE__, hp->pid); | |
813 | if (hp_detach(hp->pid) < 0) { | |
814 | if (hp->verbose > 0) | |
815 | fprintf(stderr, "[%s:%d] Error detaching from PID %d\n", __func__, | |
816 | __LINE__, hp->pid); | |
817 | rc = -1; | |
818 | } | |
819 | } | |
820 | if (mdata) | |
821 | free(mdata); | |
822 | mdata = NULL; | |
823 | #undef HP_PASS_ARGS2FUNC | |
824 | #undef HP_SETEXECWAITGET | |
825 | #undef HP_NULLIFYSTACK | |
826 | #undef HP_REG_IP_STR | |
827 | #undef HP_REG_IP | |
828 | #undef HP_REG_SP | |
829 | #undef HP_REG_AX | |
830 | return rc; | |
831 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #include <hotpatch_internal.h> | |
32 | ||
33 | enum { | |
34 | PROCMAPS_PERMS_NONE = 0x0, | |
35 | PROCMAPS_PERMS_READ = 0x1, | |
36 | PROCMAPS_PERMS_EXEC = 0x2, | |
37 | PROCMAPS_PERMS_WRITE = 0x4, | |
38 | PROCMAPS_PERMS_PRIVATE = 0x8, | |
39 | PROCMAPS_PERMS_SHARED = 0x10 | |
40 | }; | |
41 | ||
42 | enum { | |
43 | PROCMAPS_FILETYPE_UNKNOWN, | |
44 | PROCMAPS_FILETYPE_EXE, | |
45 | PROCMAPS_FILETYPE_LIB, | |
46 | PROCMAPS_FILETYPE_DATA, | |
47 | PROCMAPS_FILETYPE_VDSO, | |
48 | PROCMAPS_FILETYPE_HEAP, | |
49 | PROCMAPS_FILETYPE_STACK, | |
50 | PROCMAPS_FILETYPE_SYSCALL | |
51 | }; | |
52 | ||
53 | struct ld_procmaps { | |
54 | uintptr_t addr_begin; | |
55 | uintptr_t addr_end; | |
56 | bool addr_valid; | |
57 | int permissions; | |
58 | off_t offset; | |
59 | int device_major; | |
60 | int device_minor; | |
61 | ino_t inode; | |
62 | char *pathname; | |
63 | size_t pathname_sz; | |
64 | int filetype; | |
65 | }; | |
66 | ||
67 | void ld_procmaps_dump(struct ld_procmaps *pm) | |
68 | { | |
69 | if (!pm) | |
70 | return; | |
71 | fprintf(stderr, "[%s:%d] Pathname: %s\n", __func__, __LINE__, | |
72 | pm->pathname ? pm->pathname : "Unknown"); | |
73 | fprintf(stderr, "[%s:%d] Address Start: "LX" End: "LX" Valid:" | |
74 | " %d Offset: "LU"\n", __func__, __LINE__, | |
75 | pm->addr_begin, pm->addr_end, pm->addr_valid, | |
76 | (size_t)pm->offset); | |
77 | fprintf(stderr, "[%s:%d] Device Major: %d Minor: %d\n", | |
78 | __func__, __LINE__, pm->device_major, pm->device_minor); | |
79 | fprintf(stderr, "[%s:%d] Inode: "LU"\n", __func__, __LINE__, | |
80 | (size_t)pm->inode); | |
81 | fprintf(stderr, "[%s:%d] Permissions: Read(%d) Write(%d) " | |
82 | "Execute(%d) Private(%d) Shared(%d)\n", | |
83 | __func__, __LINE__, | |
84 | (pm->permissions & PROCMAPS_PERMS_READ) ? 1 : 0, | |
85 | (pm->permissions & PROCMAPS_PERMS_WRITE) ? 1 : 0, | |
86 | (pm->permissions & PROCMAPS_PERMS_EXEC) ? 1 : 0, | |
87 | (pm->permissions & PROCMAPS_PERMS_PRIVATE) ? 1 : 0, | |
88 | (pm->permissions & PROCMAPS_PERMS_SHARED) ? 1 : 0 | |
89 | ); | |
90 | fprintf(stderr, "[%s:%d] Pathname length: "LU"\n", __func__, __LINE__, | |
91 | pm->pathname_sz); | |
92 | fprintf(stderr, "[%s:%d] Filetype: %d\n", __func__, __LINE__, | |
93 | pm->filetype); | |
94 | } | |
95 | ||
96 | int ld_procmaps_parse(char *buf, size_t bufsz, struct ld_procmaps *pm, | |
97 | const char *appname, int verbose) | |
98 | { | |
99 | if (!buf || !pm) { | |
100 | if (verbose > 2) | |
101 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, __LINE__); | |
102 | return -1; | |
103 | } | |
104 | /* this is hardcoded parsing of the maps file */ | |
105 | do { | |
106 | char *token = NULL; | |
107 | char *save = NULL; | |
108 | int idx, err; | |
109 | memset(pm, 0, sizeof(*pm)); | |
110 | token = strtok_r(buf, "-", &save); | |
111 | if (!token) break; | |
112 | errno = 0; | |
113 | pm->addr_begin = (uintptr_t)strtoul(token, NULL, 16); | |
114 | err = errno; | |
115 | pm->addr_valid = (err == ERANGE || err == EINVAL) ? false : true; | |
116 | if (!pm->addr_valid) { | |
117 | if (verbose > 2) | |
118 | fprintf(stderr, "[%s:%d] Strtoul error(%s) in parsing %s\n", | |
119 | __func__, __LINE__, strerror(err), token); | |
120 | } | |
121 | token = strtok_r(NULL, " ", &save); | |
122 | if (!token) break; | |
123 | errno = 0; | |
124 | pm->addr_end = (intptr_t)strtoul(token, NULL, 16); | |
125 | err = errno; | |
126 | pm->addr_valid = (err == ERANGE || err == EINVAL) ? false : true; | |
127 | if (!pm->addr_valid) { | |
128 | if (verbose > 2) | |
129 | fprintf(stderr, "[%s:%d] Strtoul error(%s) in parsing %s\n", | |
130 | __func__, __LINE__, strerror(err), token); | |
131 | } | |
132 | token = strtok_r(NULL, " ", &save); | |
133 | if (!token) break; | |
134 | pm->permissions = PROCMAPS_PERMS_NONE; | |
135 | for (idx = strlen(token) - 1; idx >= 0; --idx) { | |
136 | switch (token[idx]) { | |
137 | case 'r': | |
138 | pm->permissions |= PROCMAPS_PERMS_READ; | |
139 | break; | |
140 | case 'w': | |
141 | pm->permissions |= PROCMAPS_PERMS_WRITE; | |
142 | break; | |
143 | case 'x': | |
144 | pm->permissions |= PROCMAPS_PERMS_EXEC; | |
145 | break; | |
146 | case 'p': | |
147 | pm->permissions |= PROCMAPS_PERMS_PRIVATE; | |
148 | break; | |
149 | case 's': | |
150 | pm->permissions |= PROCMAPS_PERMS_SHARED; | |
151 | break; | |
152 | case '-': | |
153 | break; | |
154 | default: | |
155 | if (verbose > 2) | |
156 | fprintf(stderr, "[%s:%d] Unknown flag: %c\n", __func__, | |
157 | __LINE__, token[idx]); | |
158 | break; | |
159 | } | |
160 | } | |
161 | token = strtok_r(NULL, " ", &save); | |
162 | if (!token) break; | |
163 | errno = 0; | |
164 | pm->offset = (off_t)strtoul(token, NULL, 16); | |
165 | err = errno; | |
166 | if (err == ERANGE || err == EINVAL) { | |
167 | if (verbose > 2) | |
168 | fprintf(stderr, "[%s:%d] Strtoul error(%s) in parsing %s\n", | |
169 | __func__, __LINE__, strerror(err), token); | |
170 | } | |
171 | token = strtok_r(NULL, ":", &save); | |
172 | if (!token) break; | |
173 | pm->device_major = (int)strtol(token, NULL, 10); | |
174 | token = strtok_r(NULL, " ", &save); | |
175 | if (!token) break; | |
176 | pm->device_minor = (int)strtol(token, NULL, 10); | |
177 | token = strtok_r(NULL, " ", &save); | |
178 | if (!token) break; | |
179 | pm->inode = (ino_t)strtoul(token, NULL, 10); | |
180 | token = strtok_r(NULL, "\n", &save); | |
181 | if (!token) break; | |
182 | pm->pathname_sz = strlen(token); | |
183 | pm->pathname = calloc(sizeof(char), pm->pathname_sz + 1); | |
184 | if (!pm->pathname) { | |
185 | LOG_ERROR_OUT_OF_MEMORY; | |
186 | pm->pathname = NULL; | |
187 | pm->pathname_sz = 0; | |
188 | break; | |
189 | } | |
190 | /* trim the extra spaces out */ | |
191 | save = token; | |
192 | /* find the real path names */ | |
193 | if ((token = strchr(save, '/'))) { | |
194 | memcpy(pm->pathname, token, strlen(token)); | |
195 | if (strstr(pm->pathname, ".so") || strstr(pm->pathname, ".so.")) { | |
196 | pm->filetype = PROCMAPS_FILETYPE_LIB; | |
197 | } else { | |
198 | struct stat statbuf; | |
199 | pm->filetype = PROCMAPS_FILETYPE_DATA; | |
200 | memset(&statbuf, 0, sizeof(statbuf)); | |
201 | if (stat(pm->pathname, &statbuf) >= 0) { | |
202 | ino_t inode1 = statbuf.st_ino; | |
203 | memset(&statbuf, 0, sizeof(statbuf)); | |
204 | if (stat(appname, &statbuf) >= 0) { | |
205 | if (statbuf.st_ino == inode1) | |
206 | pm->filetype = PROCMAPS_FILETYPE_EXE; | |
207 | } | |
208 | } else { | |
209 | int err = errno; | |
210 | if (verbose > 2) | |
211 | fprintf(stderr, "[%s:%d] Unable to stat file %s. Error:" | |
212 | " %s\n", __func__, __LINE__, pm->pathname, | |
213 | strerror(err)); | |
214 | } | |
215 | } | |
216 | } else if ((token = strchr(save, '['))) { | |
217 | memcpy(pm->pathname, token, strlen(token)); | |
218 | if (strstr(pm->pathname, "[heap]")) { | |
219 | pm->filetype = PROCMAPS_FILETYPE_HEAP; | |
220 | } else if (strstr(pm->pathname, "[stack]")) { | |
221 | pm->filetype = PROCMAPS_FILETYPE_STACK; | |
222 | } else if (strstr(pm->pathname, "[vdso]")) { | |
223 | pm->filetype = PROCMAPS_FILETYPE_VDSO; | |
224 | } else if (strstr(pm->pathname, "[vsyscall")) { | |
225 | pm->filetype = PROCMAPS_FILETYPE_SYSCALL; | |
226 | } else { | |
227 | if (verbose > 2) | |
228 | fprintf(stderr, "[%s:%d] Unknown memory map: %s\n", | |
229 | __func__, __LINE__, pm->pathname); | |
230 | pm->filetype = PROCMAPS_FILETYPE_UNKNOWN; | |
231 | } | |
232 | } else { | |
233 | memcpy(pm->pathname, token, strlen(token)); | |
234 | pm->filetype = PROCMAPS_FILETYPE_UNKNOWN; | |
235 | } | |
236 | } while (0); | |
237 | return 0; | |
238 | } | |
239 | ||
240 | struct ld_procmaps *ld_load_maps(pid_t pid, int verbose, size_t *num) | |
241 | { | |
242 | char filename[OS_MAX_BUFFER]; | |
243 | char appname[OS_MAX_BUFFER]; | |
244 | FILE *ff = NULL; | |
245 | const size_t bufsz = 4096; | |
246 | char *buf = NULL; | |
247 | size_t mapmax = 0; | |
248 | size_t mapnum = 0; | |
249 | struct ld_procmaps *maps = NULL; | |
250 | if (pid == 0) { | |
251 | LOG_ERROR_INVALID_PID(pid); | |
252 | return NULL; | |
253 | } | |
254 | snprintf(filename, OS_MAX_BUFFER, "/proc/%d/maps", pid); | |
255 | snprintf(appname, OS_MAX_BUFFER, "/proc/%d/exe", pid); | |
256 | if (verbose > 2) { | |
257 | fprintf(stderr, "[%s:%d] Using Proc Maps from %s\n", __func__, | |
258 | __LINE__, filename); | |
259 | fprintf(stderr, "[%s:%d] Using Proc Exe from %s\n", __func__, | |
260 | __LINE__, appname); | |
261 | } | |
262 | do { | |
263 | buf = calloc(sizeof(char), bufsz); | |
264 | if (!buf) { | |
265 | LOG_ERROR_OUT_OF_MEMORY; | |
266 | break; | |
267 | } | |
268 | ff = fopen(filename, "r"); | |
269 | if (!ff) { | |
270 | LOG_ERROR_FILE_OPEN(filename); | |
271 | break; | |
272 | } | |
273 | while (fgets(buf, bufsz, ff)) | |
274 | mapmax++; | |
275 | if (verbose > 0) | |
276 | fprintf(stderr, "[%s:%d] Max number of mappings present: "LU"\n", | |
277 | __func__, __LINE__, mapmax); | |
278 | fseek(ff, 0L, SEEK_SET); | |
279 | maps = calloc(sizeof(*maps), mapmax); | |
280 | if (!maps) { | |
281 | LOG_ERROR_OUT_OF_MEMORY; | |
282 | break; | |
283 | } | |
284 | if (verbose > 1) | |
285 | fprintf(stderr, | |
286 | "[%s:%d] Allocated memory to load proc maps.\n", | |
287 | __func__, __LINE__); | |
288 | memset(buf, 0, bufsz); | |
289 | mapnum = 0; | |
290 | while (fgets(buf, bufsz, ff)) { | |
291 | struct ld_procmaps *pm = &maps[mapnum]; | |
292 | if (verbose > 3) | |
293 | fprintf(stderr, "[%s:%d] Parsing %s\n", __func__, __LINE__, | |
294 | buf); | |
295 | if (ld_procmaps_parse(buf, bufsz, pm, appname, verbose) < 0) { | |
296 | if (verbose > 1) { | |
297 | fprintf(stderr, "[%s:%d] Parsing failure. Ignoring.\n", | |
298 | __func__, __LINE__); | |
299 | } | |
300 | continue; | |
301 | } | |
302 | if (verbose > 4) | |
303 | ld_procmaps_dump(pm); | |
304 | mapnum++; | |
305 | } | |
306 | if (num) | |
307 | *num = mapnum; | |
308 | else | |
309 | if (verbose > 3) | |
310 | fprintf(stderr, "[%s:%d] Cannot return size of maps object.\n", | |
311 | __func__, __LINE__); | |
312 | } while (0); | |
313 | if (buf) | |
314 | free(buf); | |
315 | if (ff) | |
316 | fclose(ff); | |
317 | return maps; | |
318 | } | |
319 | ||
320 | void ld_free_maps(struct ld_procmaps *maps, size_t num) | |
321 | { | |
322 | if (maps && num > 0) { | |
323 | size_t idx; | |
324 | for (idx = 0; idx < num; ++idx) { | |
325 | if (maps[idx].pathname) | |
326 | free(maps[idx].pathname); | |
327 | maps[idx].pathname = NULL; | |
328 | } | |
329 | free(maps); | |
330 | maps = NULL; | |
331 | } | |
332 | } | |
333 | ||
334 | int ld_find_library(const struct ld_procmaps *maps, const size_t mapnum, | |
335 | const char *libpath, bool inode_match, | |
336 | struct ld_library *lib, int verbose) | |
337 | { | |
338 | if (!maps && !libpath) { | |
339 | if (verbose > 3) | |
340 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, | |
341 | __LINE__); | |
342 | return -1; | |
343 | } else { | |
344 | size_t idx; | |
345 | bool found = false; | |
346 | ino_t inode = 0; | |
347 | bool nonlib_match = false; | |
348 | bool exact_match = false; | |
349 | if (inode_match) { | |
350 | struct stat statbuf = { 0 }; | |
351 | if (stat(libpath, &statbuf) < 0) { | |
352 | int err = errno; | |
353 | if (verbose > 1) | |
354 | fprintf(stderr, | |
355 | "[%s:%d] Unable to get inode for %s. Error: %s\n", | |
356 | __func__, __LINE__, libpath, strerror(err)); | |
357 | return -1; | |
358 | } | |
359 | inode = statbuf.st_ino; | |
360 | } else { | |
361 | if (verbose > 2) | |
362 | fprintf(stderr, "[%s:%d] Not doing an inode match.\n", | |
363 | __func__, __LINE__); | |
364 | nonlib_match = (strchr(libpath, '[') || strchr(libpath, ']')) ? | |
365 | true : false; | |
366 | if (verbose > 2 && nonlib_match) | |
367 | fprintf(stderr, "[%s:%d] Found '[' or ']' in %s\n", | |
368 | __func__, __LINE__, libpath); | |
369 | exact_match = (strchr(libpath, '/')) ? true : false; | |
370 | if (verbose > 2 && exact_match) | |
371 | fprintf(stderr, "[%s:%d] Found '/' in %s. Doing an exact " | |
372 | "match search\n", __func__, __LINE__, libpath); | |
373 | if (!nonlib_match && !exact_match && verbose > 0) | |
374 | fprintf(stderr, "[%s:%d] Doing best substring search for %s.\n", | |
375 | __func__, __LINE__, libpath); | |
376 | } | |
377 | for (idx = 0; idx < mapnum; ++idx) { | |
378 | const struct ld_procmaps *pm = &maps[idx]; | |
379 | if (!pm->pathname) | |
380 | continue; | |
381 | /* first try inode match. the libraries can be symlinks and | |
382 | * all that | |
383 | */ | |
384 | if (inode_match) { | |
385 | /* if it has no inode, we do not support it */ | |
386 | if (pm->inode == 0) | |
387 | continue; | |
388 | found = (pm->inode == inode) ? true : false; | |
389 | } else { | |
390 | /* Now try string match. | |
391 | * 1. if the string contains a '[' or ']' then do a substring | |
392 | * match | |
393 | * 2. if the string contains a '/' then do an exact match | |
394 | * 3. else substring search all libs and return the first one | |
395 | * with a valid inode | |
396 | */ | |
397 | if (nonlib_match) { | |
398 | /* we're looking for a non-library or a non-exe file or a | |
399 | * non-data file | |
400 | */ | |
401 | if (pm->filetype == PROCMAPS_FILETYPE_VDSO || | |
402 | pm->filetype == PROCMAPS_FILETYPE_HEAP || | |
403 | pm->filetype == PROCMAPS_FILETYPE_STACK || | |
404 | pm->filetype == PROCMAPS_FILETYPE_SYSCALL) { | |
405 | /* doing a substring match to be safe */ | |
406 | found = strstr(pm->pathname, libpath) != NULL ? | |
407 | true :false; | |
408 | } | |
409 | } else { | |
410 | if (pm->filetype != PROCMAPS_FILETYPE_LIB) | |
411 | continue; | |
412 | if (pm->inode == 0) | |
413 | continue; | |
414 | /* we're doing an exact match */ | |
415 | if (exact_match) { | |
416 | found = strcmp(libpath, pm->pathname) == 0 ? | |
417 | true : false; | |
418 | } else { | |
419 | /* do a substring match for best fit. If the string | |
420 | * matches then check if the next character is not an | |
421 | * alphabet and is a . or a - | |
422 | */ | |
423 | char *sub = strstr(pm->pathname, libpath); | |
424 | found = false; | |
425 | if (sub) { | |
426 | size_t alen = strlen(libpath); | |
427 | if (sub[alen] == '.' || sub[alen] == '-') | |
428 | found = true; | |
429 | } | |
430 | } | |
431 | } | |
432 | } | |
433 | if (found) { | |
434 | if (verbose > 2) | |
435 | fprintf(stderr, "[%s:%d] Found index ("LU") matching.\n", | |
436 | __func__, __LINE__, idx); | |
437 | if (verbose > 0) | |
438 | fprintf(stderr, "[%s:%d] Found entry %s matching %s\n", | |
439 | __func__, __LINE__, pm->pathname, libpath); | |
440 | break; | |
441 | } | |
442 | } | |
443 | if (!found) { | |
444 | if (verbose > 0) { | |
445 | fprintf(stderr, "[%s:%d] Library %s not found in procmaps\n", | |
446 | __func__, __LINE__, libpath); | |
447 | } | |
448 | return -1; | |
449 | } | |
450 | if (found && lib) { | |
451 | const struct ld_procmaps *pm = &maps[idx]; | |
452 | if (pm->addr_valid) { | |
453 | lib->addr_begin = pm->addr_begin; | |
454 | lib->addr_end = pm->addr_end; | |
455 | } else { | |
456 | if (verbose > 1) | |
457 | fprintf(stderr, "[%s:%d] Addresses are invalid for %s\n", | |
458 | __func__, __LINE__, lib->pathname); | |
459 | return -1; | |
460 | } | |
461 | lib->inode = pm->inode; | |
462 | lib->pathname = strdup(pm->pathname); | |
463 | if (!lib->pathname) { | |
464 | LOG_ERROR_OUT_OF_MEMORY; | |
465 | lib->pathname = NULL; | |
466 | lib->length = 0; | |
467 | return -1; | |
468 | } else { | |
469 | lib->length = pm->pathname_sz; | |
470 | } | |
471 | } | |
472 | } | |
473 | return 0; | |
474 | } | |
475 | ||
476 | uintptr_t ld_find_address(const struct ld_library *lib, const char *symbol, | |
477 | int verbose) | |
478 | { | |
479 | uintptr_t ptr = 0; | |
480 | if (lib && symbol && lib->pathname) { | |
481 | size_t syms_num = 0; | |
482 | struct elf_symbol *syms = exe_load_symbols(lib->pathname, verbose, | |
483 | &syms_num, NULL, NULL, NULL); | |
484 | if (syms && syms_num > 0) { | |
485 | size_t idx = 0; | |
486 | if (verbose > 1) | |
487 | fprintf(stderr, "[%s:%d] "LU" symbols found in %s\n", | |
488 | __func__, __LINE__, syms_num, lib->pathname); | |
489 | qsort(syms, syms_num, sizeof(*syms), elf_symbol_cmpqsort); | |
490 | for (idx = 0; idx < syms_num; ++idx) { | |
491 | if (strcmp(symbol, syms[idx].name) == 0) { | |
492 | if (verbose > 2) | |
493 | fprintf(stderr, "[%s:%d] Found %s in symbol list at " | |
494 | ""LU" with address offset "LX"\n", __func__, | |
495 | __LINE__, symbol, idx, syms[idx].address); | |
496 | if (syms[idx].address > lib->addr_begin) | |
497 | ptr = syms[idx].address; | |
498 | else | |
499 | ptr = syms[idx].address + lib->addr_begin; | |
500 | break; | |
501 | } | |
502 | } | |
503 | /* free memory for all to avoid mem-leaks */ | |
504 | for (idx = 0; idx < syms_num; ++idx) { | |
505 | if (syms[idx].name) | |
506 | free(syms[idx].name); | |
507 | syms[idx].name = NULL; | |
508 | } | |
509 | free(syms); | |
510 | syms_num = 0; | |
511 | } else { | |
512 | if (verbose > 0) | |
513 | fprintf(stderr, "[%s:%d] No symbols found in %s\n", | |
514 | __func__, __LINE__, lib->pathname); | |
515 | } | |
516 | } else { | |
517 | if (verbose > 3) | |
518 | fprintf(stderr, "[%s:%d] Invalid arguments.\n", __func__, | |
519 | __LINE__); | |
520 | } | |
521 | return ptr; | |
522 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #include <hotpatch.h> | |
32 | ||
33 | struct hp_options { | |
34 | pid_t pid; | |
35 | int verbose; | |
36 | bool is__start; | |
37 | char *symbol; | |
38 | bool dryrun; | |
39 | char *dll; | |
40 | }; | |
41 | ||
42 | void print_usage(const char *app) | |
43 | { | |
44 | printf("\nUsage: %s [options] <PID of process to patch>\n", app); | |
45 | printf("\nOptions:\n"); | |
46 | printf("-h This help message\n"); | |
47 | printf("-V Version number.\n"); | |
48 | printf("-v[vvvv] Enable verbose logging. Add more 'v's for more\n"); | |
49 | printf("-N Dry run. Do not modify anything in process\n"); | |
50 | printf("-l <.so> Path or name of the .so file to load. Switches off " | |
51 | "execution pointer reset\n"); | |
52 | printf("-s <name> Symbol to invoke during the dll inject. Optional.\n"); | |
53 | printf("-x <name> Set execution pointer to symbol. Cannot be set with " | |
54 | "-s option\n"); | |
55 | } | |
56 | ||
57 | void print_options(const struct hp_options *opts) | |
58 | { | |
59 | if (opts && opts->verbose > 0) { | |
60 | printf( | |
61 | "Options Given:\n" | |
62 | "Verbose Level: %d\n" | |
63 | "Process PID: %d\n" | |
64 | "Symbol name: %s\n" | |
65 | "Library name: %s\n" | |
66 | "Dry run: %s\n", | |
67 | opts->verbose, | |
68 | opts->pid, | |
69 | (opts->symbol ? opts->symbol : | |
70 | (opts->dll ? "_init" : "_start")), | |
71 | (opts->dll ? opts->dll : "N/A"), | |
72 | (opts->dryrun ? "true" : "false") | |
73 | ); | |
74 | } | |
75 | } | |
76 | ||
77 | int parse_arguments(int argc, char **argv, struct hp_options *opts) | |
78 | { | |
79 | if (argc > 0 && argv && opts) { | |
80 | int opt = 0; | |
81 | extern int optind; | |
82 | extern char *optarg; | |
83 | optind = 1; | |
84 | opts->is__start = false; | |
85 | opts->dryrun = false; | |
86 | while ((opt = getopt(argc, argv, "hNVs:x::l:v::")) != -1) { | |
87 | switch (opt) { | |
88 | case 'v': | |
89 | opts->verbose += optarg ? (int)strnlen(optarg, 5) : 1; | |
90 | break; | |
91 | case 's': | |
92 | if (opts->symbol) { | |
93 | free(opts->symbol); | |
94 | opts->symbol = NULL; | |
95 | } | |
96 | opts->symbol = strdup(optarg); | |
97 | if (!opts->symbol) { | |
98 | printf("[%s:%d] Out of memory\n", __func__, __LINE__); | |
99 | return -1; | |
100 | } | |
101 | break; | |
102 | case 'x': | |
103 | if (optarg) { | |
104 | opts->symbol = strdup(optarg); | |
105 | if (strcmp(optarg, HOTPATCH_LINUX_START) == 0) | |
106 | opts->is__start = true; | |
107 | else | |
108 | opts->is__start = false; | |
109 | } else { | |
110 | opts->symbol = strdup(HOTPATCH_LINUX_START); | |
111 | opts->is__start = true; | |
112 | } | |
113 | if (!opts->symbol) { | |
114 | printf("[%s:%d] Out of memory\n", __func__, __LINE__); | |
115 | return -1; | |
116 | } | |
117 | break; | |
118 | case 'N': | |
119 | opts->dryrun = true; | |
120 | break; | |
121 | case 'l': | |
122 | opts->dll = strdup(optarg); | |
123 | if (!opts->dll) { | |
124 | printf("[%s:%d] Out of memory\n", __func__, __LINE__); | |
125 | return -1; | |
126 | } | |
127 | break; | |
128 | case 'V': | |
129 | { | |
130 | int major = 0, minor = 0; | |
131 | hotpatch_version(&major, &minor); | |
132 | printf("Hotpatch version: %d.%d\n", major, minor); | |
133 | return 1; | |
134 | } | |
135 | break; | |
136 | case 'h': | |
137 | default: | |
138 | print_usage(argv[0]); | |
139 | return -1; | |
140 | } | |
141 | } | |
142 | if (optind >= argc) { | |
143 | printf("Expected more arguments.\n"); | |
144 | print_usage(argv[0]); | |
145 | return -1; | |
146 | } | |
147 | opts->pid = (pid_t)strtol(argv[optind], NULL, 10); | |
148 | if (opts->pid == 0) { | |
149 | printf("Process PID can't be 0. Tried parsing: %s\n", argv[optind]); | |
150 | return -1; | |
151 | } | |
152 | return 0; | |
153 | } | |
154 | return -1; | |
155 | } | |
156 | ||
157 | int main(int argc, char **argv) | |
158 | { | |
159 | struct hp_options opts = { 0 }; | |
160 | hotpatch_t *hp = NULL; | |
161 | int rc = 0; | |
162 | /* parse all arguments first */ | |
163 | if ((rc = parse_arguments(argc, argv, &opts)) != 0) { | |
164 | return rc; | |
165 | } | |
166 | print_options(&opts); | |
167 | /* break from execution whenever a step fails */ | |
168 | do { | |
169 | uintptr_t ptr = 0; | |
170 | hp = hotpatch_create(opts.pid, opts.verbose); | |
171 | if (!hp) { | |
172 | fprintf(stderr, "[%s:%d] Unable to create hotpatch for PID %d\n", | |
173 | __func__, __LINE__, opts.pid); | |
174 | rc = -1; | |
175 | break; | |
176 | } | |
177 | if (opts.dryrun) | |
178 | break; | |
179 | if (opts.dll) { | |
180 | uintptr_t dlres = 0; | |
181 | uintptr_t symres = 0; | |
182 | rc = hotpatch_inject_library(hp, opts.dll, opts.symbol, NULL, 0, | |
183 | &dlres, &symres); | |
184 | if (rc >=0) { | |
185 | printf("Dll was injected at %p\n", (void *)dlres); | |
186 | printf("Invocation of %s() returned %p\n", | |
187 | (opts.symbol ? opts.symbol : "_init"), | |
188 | (void *)symres); | |
189 | } | |
190 | } else { | |
191 | /* handles the stripped apps as well */ | |
192 | if (opts.is__start) { | |
193 | ptr = hotpatch_get_entry_point(hp); | |
194 | } else { | |
195 | ptr = hotpatch_read_symbol(hp, opts.symbol, NULL, NULL); | |
196 | } | |
197 | if (!ptr) { | |
198 | printf("Symbol %s not found. Cannot proceed\n", opts.symbol); | |
199 | break; | |
200 | } | |
201 | printf("Setting execution pointer to %s at 0x"LX"\n", opts.symbol, ptr); | |
202 | rc = hotpatch_attach(hp); | |
203 | if (rc < 0) { | |
204 | printf("Failed to attach to process. Cannot proceed\n"); | |
205 | break; | |
206 | } | |
207 | rc = hotpatch_set_execution_pointer(hp, ptr); | |
208 | if (rc < 0) { | |
209 | printf("Failed to set execution pointer to 0x"LX"\n", ptr); | |
210 | rc = hotpatch_detach(hp); | |
211 | break; | |
212 | } | |
213 | rc = hotpatch_detach(hp); | |
214 | } | |
215 | } while (0); | |
216 | hotpatch_destroy(hp); | |
217 | hp = NULL; | |
218 | if (opts.symbol) | |
219 | free(opts.symbol); | |
220 | opts.symbol = NULL; | |
221 | if (opts.dll) | |
222 | free(opts.dll); | |
223 | opts.dll = NULL; | |
224 | return rc; | |
225 | } |
0 | ### hotpatch is a dll injection strategy | |
1 | ### Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
2 | ### All rights reserved. | |
3 | ### | |
4 | ### Redistribution and use in source and binary forms, with or without | |
5 | ### modification, are permitted provided that the following conditions are met: | |
6 | ### | |
7 | ### * Redistributions of source code must retain the above copyright | |
8 | ### notice, this list of conditions and the following disclaimer. | |
9 | ### | |
10 | ### * Redistributions in binary form must reproduce the above copyright | |
11 | ### notice, this list of conditions and the following disclaimer in the | |
12 | ### documentation and/or other materials provided with the distribution. | |
13 | ### | |
14 | ### * Neither the name of Selective Intellect LLC nor the | |
15 | ### names of its contributors may be used to endorse or promote products | |
16 | ### derived from this software without specific prior written permission. | |
17 | ### | |
18 | ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
19 | ### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | ### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | ### DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
22 | ### DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | ### LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | ### ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | ### (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
27 | ### SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | ### | |
29 | project(unit_tests) | |
30 | ||
31 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) | |
32 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) | |
33 | ||
34 | if (ARCH STREQUAL "x86_64") | |
35 | add_executable(ptracetest ptrace.c) | |
36 | endif (ARCH STREQUAL "x86_64") | |
37 | ||
38 | add_executable(dummy dummy.c) | |
39 | add_executable(loadertest loader.c) | |
40 | target_link_libraries(loadertest hotpatch) | |
41 | add_test(loadertest ${CMAKE_CURRENT_BINARY_DIR}/loadertest) | |
42 | ||
43 | add_executable(compiletestcpp cpptest.cpp) | |
44 | target_link_libraries(compiletestcpp hotpatch) | |
45 | add_test(compiletestcpp ${CMAKE_CURRENT_BINARY_DIR}/compiletestcpp) | |
46 | ||
47 | add_executable(compiletestc ctest.c) | |
48 | target_link_libraries(compiletestc hotpatch) | |
49 | add_test(compiletestc ${CMAKE_CURRENT_BINARY_DIR}/compiletestc) | |
50 | ||
51 | add_library(hotpatchtest SHARED dlltest.c) | |
52 | set_target_properties(hotpatchtest PROPERTIES LINK_FLAGS "-fPIC -nostartfiles") | |
53 | install(TARGETS hotpatchtest LIBRARY DESTINATION lib) |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch.h> | |
31 | ||
32 | int main(int argc, char **argv) | |
33 | { | |
34 | hotpatch_t *hp = 0; | |
35 | if (hp) {} | |
36 | return 0; | |
37 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch.h> | |
31 | ||
32 | int main(int argc, char **argv) | |
33 | { | |
34 | hotpatch_t *hp = 0; | |
35 | if (hp) {} | |
36 | return 0; | |
37 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #ifdef HOTPATCH_HAS_ASSERT_H | |
32 | #undef NDEBUG | |
33 | #include <assert.h> | |
34 | #endif | |
35 | ||
36 | #define HP_DLLTEST(D,L) \ | |
37 | do { \ | |
38 | time_t tt = time(NULL); \ | |
39 | FILE *ff = fopen("/tmp/hotpatchtest.log", "a"); \ | |
40 | if (ff) { \ | |
41 | fprintf(ff, "Dll opened. %s\n", ctime(&tt)); \ | |
42 | if (L > 0) \ | |
43 | fprintf(ff, "Data: %s Len: "LU"\n", (char *)D, (size_t)L); \ | |
44 | fclose(ff); \ | |
45 | } \ | |
46 | } while (0) | |
47 | ||
48 | void _init() | |
49 | { | |
50 | HP_DLLTEST(__func__, 0); | |
51 | } | |
52 | ||
53 | int mysym(char *data, size_t len) | |
54 | { | |
55 | HP_DLLTEST(data, len); | |
56 | return 0xDEADBEEF; | |
57 | } |
0 | #ifndef _GNU_SOURCE | |
1 | #define _GNU_SOURCE | |
2 | #endif | |
3 | #include <stdio.h> | |
4 | #include <string.h> | |
5 | #include <stdint.h> | |
6 | #include <unistd.h> | |
7 | #include <time.h> | |
8 | #include <sys/time.h> | |
9 | #include <sys/syscall.h> | |
10 | #if __WORDSIZE == 64 | |
11 | #define LX "%lx" | |
12 | #define LU "%lu" | |
13 | #else | |
14 | #define LX "%x" | |
15 | #define LU "%u" | |
16 | #endif | |
17 | ||
18 | static int counter = 0; | |
19 | void myfun() | |
20 | { | |
21 | printf("%d: I am here in %s on %d\n", counter++, | |
22 | __func__, __LINE__); | |
23 | if (counter >= INT32_MAX) | |
24 | counter = 0; | |
25 | } | |
26 | ||
27 | int main() | |
28 | { | |
29 | intptr_t here0 = 0; | |
30 | intptr_t here1 = 0; | |
31 | const char *str = "Hello World!"; | |
32 | size_t len = strlen(str); | |
33 | here0 = (intptr_t)syscall(SYS_brk, 0); | |
34 | here1 = (intptr_t)syscall(SYS_brk, here0 + len + 1); | |
35 | printf("Starting dummy 0x"LX" 0x"LX"\n", here0, here1); | |
36 | memcpy((void *)here0, str, len + 1); | |
37 | printf("String: %s\n", (const char *)here0); | |
38 | syscall(SYS_brk, here0); | |
39 | while (1) { | |
40 | struct timeval tv = { 0 }; | |
41 | sleep(2); | |
42 | gettimeofday(&tv, NULL); | |
43 | printf("Working "LU"."LU"\n", (size_t)tv.tv_sec, (size_t)tv.tv_usec); | |
44 | myfun(); | |
45 | } | |
46 | printf("Stopping dummy\n"); | |
47 | return 0; | |
48 | } |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #include <hotpatch_internal.h> | |
32 | #ifdef HOTPATCH_HAS_ASSERT_H | |
33 | #undef NDEBUG | |
34 | #include <assert.h> | |
35 | #endif | |
36 | ||
37 | int main(int argc, char **argv) | |
38 | { | |
39 | struct ld_procmaps *maps = NULL; | |
40 | size_t mapnum = 0; | |
41 | int ret; | |
42 | pid_t pid = argc > 1 ? (pid_t)strtol(argv[1], NULL, 10) : getpid(); | |
43 | assert(pid != 0); | |
44 | maps = ld_load_maps(pid, 6 /* largest verbose */, &mapnum); | |
45 | assert(mapnum > 0); | |
46 | assert(maps != NULL); | |
47 | ret = 0; | |
48 | ret = ld_find_library(maps, mapnum, "[heap]", false, NULL, 6); | |
49 | assert(ret >= 0); | |
50 | ret = ld_find_library(maps, mapnum, "[stack", false, NULL, 6); | |
51 | assert(ret >= 0); | |
52 | ret = ld_find_library(maps, mapnum, "vdso", false, NULL, 6); | |
53 | assert(ret < 0); | |
54 | ret = ld_find_library(maps, mapnum, "libc", false, NULL, 6); | |
55 | assert(ret >= 0); | |
56 | #if __WORDSIZE == 64 | |
57 | ret = ld_find_library(maps, mapnum, "/lib64/ld-linux-x86-64.so.2", | |
58 | true, NULL, 6); | |
59 | assert(ret >= 0); | |
60 | ret = ld_find_library(maps, mapnum, "/lib/ld-linux-x86-64.so.2", | |
61 | false, NULL, 6); | |
62 | #else | |
63 | ret = ld_find_library(maps, mapnum, "/lib/ld-linux.so.2", | |
64 | true, NULL, 6); | |
65 | assert(ret >= 0); | |
66 | ret = ld_find_library(maps, mapnum, "/lib32/ld-linux.so.2", | |
67 | false, NULL, 6); | |
68 | #endif | |
69 | assert(ret < 0); | |
70 | ld_free_maps(maps, mapnum); | |
71 | return 0; | |
72 | } | |
73 |
0 | /* | |
1 | * hotpatch is a dll injection strategy. | |
2 | * Copyright (c) 2010-2011, Vikas Naresh Kumar, Selective Intellect LLC | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * | |
11 | * * Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * * Neither the name of Selective Intellect LLC nor the | |
16 | * names of its contributors may be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | #include <hotpatch_config.h> | |
31 | #ifdef HOTPATCH_HAS_ASSERT_H | |
32 | #undef NDEBUG | |
33 | #include <assert.h> | |
34 | #endif | |
35 | ||
36 | int test_child(pid_t pid) | |
37 | { | |
38 | int st = 0; | |
39 | int memfd = -1; | |
40 | char procfile[4096]; | |
41 | memset(procfile, 0, sizeof(procfile)); | |
42 | sprintf(procfile, "/proc/%d/maps", pid); | |
43 | printf("Trying to open %s\n", procfile); | |
44 | memfd = open(procfile, O_RDONLY); | |
45 | assert(memfd >= 0); | |
46 | memset(procfile, 0, sizeof(procfile)); | |
47 | while ((st = read(memfd, procfile, sizeof(procfile))) >= 0) { | |
48 | printf("st: %d\n", st); | |
49 | printf("%s\n", procfile); | |
50 | if (st == 0) break; | |
51 | } | |
52 | if (st < 0) { | |
53 | st = errno; | |
54 | printf("error: %s\n", strerror(st)); | |
55 | } | |
56 | close(memfd); | |
57 | return st; | |
58 | } | |
59 | ||
60 | int main(int argc, char **argv, char **envp) | |
61 | { | |
62 | pid_t pid = 0; | |
63 | int status = 0; | |
64 | assert(argc >= 2); | |
65 | pid = (pid_t)strtol(argv[1], NULL, 10); | |
66 | assert(pid > 0); | |
67 | ||
68 | assert(ptrace(PTRACE_ATTACH, pid, NULL, NULL) == 0); | |
69 | ||
70 | while (1) { | |
71 | struct user *cldata = NULL; | |
72 | long retval = 0; | |
73 | waitpid(-1, &status, 0); | |
74 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | |
75 | break; | |
76 | } | |
77 | cldata = malloc(sizeof(*cldata)); | |
78 | if (!cldata) { | |
79 | fprintf(stderr, "Out of memory. Tried to allocate "LU"\n", sizeof(*cldata)); | |
80 | break; | |
81 | } | |
82 | memset(cldata, 0, sizeof(*cldata)); | |
83 | if (ptrace(PTRACE_GETREGS, pid, NULL, cldata) < 0) { | |
84 | int err = errno; | |
85 | printf("[%s:%d] Error: %s\n", __func__, __LINE__, strerror(err)); | |
86 | } else { | |
87 | printf("R15: %p\n", (void *)cldata->regs.r15); | |
88 | printf("R14: %p\n", (void *)cldata->regs.r14); | |
89 | printf("R13: %p\n", (void *)cldata->regs.r13); | |
90 | printf("R12: %p\n", (void *)cldata->regs.r12); | |
91 | printf("RBP: %p\n", (void *)cldata->regs.rbp); | |
92 | printf("RBX: %p\n", (void *)cldata->regs.rbx); | |
93 | printf("R11: %p\n", (void *)cldata->regs.r11); | |
94 | printf("R10: %p\n", (void *)cldata->regs.r10); | |
95 | printf("R9: %p\n", (void *)cldata->regs.r9); | |
96 | printf("R8: %p\n", (void *)cldata->regs.r8); | |
97 | printf("RAX: %p\n", (void *)cldata->regs.rax); | |
98 | printf("RCX: %p\n", (void *)cldata->regs.rcx); | |
99 | printf("RDX: %p\n", (void *)cldata->regs.rdx); | |
100 | printf("RSI: %p\n", (void *)cldata->regs.rsi); | |
101 | printf("RDI: %p\n", (void *)cldata->regs.rdi); | |
102 | printf("ORIG_RAX: %p\n", (void *)cldata->regs.orig_rax); | |
103 | printf("RIP: %p\n", (void *)cldata->regs.rip); | |
104 | printf("CS: %p\n", (void *)cldata->regs.cs); | |
105 | printf("EFLAGS: %p\n", (void *)cldata->regs.eflags); | |
106 | printf("RSP: %p\n", (void *)cldata->regs.rsp); | |
107 | printf("SS: %p\n", (void *)cldata->regs.ss); | |
108 | printf("FS_BASE: %p\n", (void *)cldata->regs.fs_base); | |
109 | printf("GS_BASE: %p\n", (void *)cldata->regs.gs_base); | |
110 | printf("DS: %p\n", (void *)cldata->regs.ds); | |
111 | printf("ES: %p\n", (void *)cldata->regs.es); | |
112 | printf("FS: %p\n", (void *)cldata->regs.fs); | |
113 | printf("GS: %p\n", (void *)cldata->regs.gs); | |
114 | printf("FPVALID: %d\n", cldata->u_fpvalid); | |
115 | printf("TSize: "LU"\n", cldata->u_tsize); | |
116 | printf("DSize: "LU"\n", cldata->u_dsize); | |
117 | printf("SSize: "LU"\n", cldata->u_ssize); | |
118 | printf("Start code: %p\n", (void *)cldata->start_code); | |
119 | printf("Start stack: %p\n", (void *)cldata->start_stack); | |
120 | printf("Signal: "LU"\n", cldata->signal); | |
121 | printf("Reserved: %d\n", cldata->reserved); | |
122 | printf("AR0: %p\n", (void *)cldata->u_ar0); | |
123 | printf("FPSTATE: %p\n", (void *)cldata->u_fpstate); | |
124 | printf("MAGIC: "LU"\n", cldata->magic); | |
125 | printf("U_COMM: %s\n", cldata->u_comm); | |
126 | } | |
127 | cldata->regs.orig_rax++; | |
128 | ptrace(PTRACE_SETREGS, pid, NULL, cldata); | |
129 | if ((retval = ptrace(PTRACE_PEEKUSER, pid, offsetof(struct user, u_fpvalid), NULL)) < 0) { | |
130 | int err = errno; | |
131 | printf("[%s:%d] Return value: "LU" Error: %s\n", __func__, __LINE__, retval, strerror(err)); | |
132 | } else { | |
133 | cldata->start_code = retval; | |
134 | printf("Start code: %p\n", (void *)cldata->start_code); | |
135 | } | |
136 | retval = ptrace(PTRACE_PEEKTEXT, pid, cldata->regs.rip, NULL); | |
137 | printf("[%s:%d] Return value: "LU". \n", __func__, __LINE__, retval); | |
138 | if (argc > 2) { | |
139 | ptrace(PTRACE_CONT, pid, 0, 0); | |
140 | } | |
141 | printf("[%s:%d] \n", __func__, __LINE__); | |
142 | free(cldata); | |
143 | if (test_child(pid) < 0) { | |
144 | break; | |
145 | } | |
146 | } | |
147 | assert(ptrace(PTRACE_DETACH, pid, NULL, NULL) == 0); | |
148 | return 0; | |
149 | } |