hookie 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. hookie-0.1.0/ARCHITECTURE.md +360 -0
  2. hookie-0.1.0/LICENSE +21 -0
  3. hookie-0.1.0/MANIFEST.in +11 -0
  4. hookie-0.1.0/PKG-INFO +90 -0
  5. hookie-0.1.0/README.md +64 -0
  6. hookie-0.1.0/examples/README.md +36 -0
  7. hookie-0.1.0/examples/linux/getpid_payload/hook.py +27 -0
  8. hookie-0.1.0/examples/linux/puts_payload/hook.py +27 -0
  9. hookie-0.1.0/examples/windows/beep_payload/hook.py +30 -0
  10. hookie-0.1.0/examples/windows/messagebox_payload/hook.py +32 -0
  11. hookie-0.1.0/hookie/__init__.py +19 -0
  12. hookie-0.1.0/hookie/__main__.py +4 -0
  13. hookie-0.1.0/hookie/arch/__init__.py +0 -0
  14. hookie-0.1.0/hookie/arch/x64.py +42 -0
  15. hookie-0.1.0/hookie/arch/x86.py +34 -0
  16. hookie-0.1.0/hookie/core.py +300 -0
  17. hookie-0.1.0/hookie/disassembler.py +90 -0
  18. hookie-0.1.0/hookie/injector.py +153 -0
  19. hookie-0.1.0/hookie/memory.py +321 -0
  20. hookie-0.1.0/hookie/packaging.py +1006 -0
  21. hookie-0.1.0/hookie/stubs/x64-linux/hookie_stub.c +541 -0
  22. hookie-0.1.0/hookie/stubs/x64-windows/hookie_stub.c +392 -0
  23. hookie-0.1.0/hookie/stubs/x86-linux/hookie_stub.c +541 -0
  24. hookie-0.1.0/hookie/stubs/x86-windows/hookie_stub.c +392 -0
  25. hookie-0.1.0/hookie/trampoline.py +227 -0
  26. hookie-0.1.0/hookie.egg-info/PKG-INFO +90 -0
  27. hookie-0.1.0/hookie.egg-info/SOURCES.txt +53 -0
  28. hookie-0.1.0/hookie.egg-info/dependency_links.txt +1 -0
  29. hookie-0.1.0/hookie.egg-info/entry_points.txt +2 -0
  30. hookie-0.1.0/hookie.egg-info/requires.txt +1 -0
  31. hookie-0.1.0/hookie.egg-info/top_level.txt +1 -0
  32. hookie-0.1.0/pyproject.toml +55 -0
  33. hookie-0.1.0/requirements.txt +1 -0
  34. hookie-0.1.0/setup.cfg +4 -0
  35. hookie-0.1.0/tests/README.md +50 -0
  36. hookie-0.1.0/tests/linux/_common.py +26 -0
  37. hookie-0.1.0/tests/linux/test_lifecycle.py +35 -0
  38. hookie-0.1.0/tests/linux/test_loop_reloc.py +37 -0
  39. hookie-0.1.0/tests/linux/test_native_basic.py +30 -0
  40. hookie-0.1.0/tests/linux/test_native_original.py +31 -0
  41. hookie-0.1.0/tests/linux/test_native_relative.py +46 -0
  42. hookie-0.1.0/tests/linux/test_packaging_posix.py +59 -0
  43. hookie-0.1.0/tests/linux/test_relocation_matrix.py +94 -0
  44. hookie-0.1.0/tests/linux/test_safety.py +62 -0
  45. hookie-0.1.0/tests/linux/test_stub_runtime_policy.py +144 -0
  46. hookie-0.1.0/tests/linux/test_transaction.py +231 -0
  47. hookie-0.1.0/tests/run_linux.py +42 -0
  48. hookie-0.1.0/tests/run_windows.py +38 -0
  49. hookie-0.1.0/tests/test_packaging.py +177 -0
  50. hookie-0.1.0/tests/windows/test_direct_destroy.py +31 -0
  51. hookie-0.1.0/tests/windows/test_lifecycle.py +59 -0
  52. hookie-0.1.0/tests/windows/test_native_relative.py +84 -0
  53. hookie-0.1.0/tests/windows/test_stub_runtime_policy.py +113 -0
  54. hookie-0.1.0/tests/windows/test_winapi_basic.py +47 -0
  55. hookie-0.1.0/tests/windows/test_winapi_original.py +51 -0
@@ -0,0 +1,360 @@
1
+ # Hookie Architecture
2
+
3
+ This document describes how Hookie works internally. `README.md` intentionally
4
+ stays small; this is the technical reference.
5
+
6
+ ## 1. Mental model
7
+
8
+ Hookie has two layers:
9
+
10
+ 1. **Hook engine** — patches a native function in the process where Python is
11
+ already running.
12
+ 2. **Payload bootstrap** — gets Python + Hookie into another process, then runs
13
+ the user's payload entrypoint there.
14
+
15
+ The hook engine does not know anything about DLL injection. The native bootstrap
16
+ does not implement trampoline relocation. They meet only when the bootstrap
17
+ executes the packaged Python payload and that code imports `hookie`.
18
+
19
+ ---
20
+
21
+ ## 2. Hook engine
22
+
23
+ `HookEngine.create_hook()` receives:
24
+
25
+ - a native target address,
26
+ - a Python detour callable,
27
+ - the target argument types,
28
+ - the target return type,
29
+ - optionally a ctypes prototype factory.
30
+
31
+ Hookie wraps the Python detour with `ctypes`, disassembles whole instructions
32
+ from the target prologue, builds a trampoline, and prepares a patch.
33
+
34
+ The hook starts disabled. `enable()` writes the jump into the target prologue.
35
+ `disable()` restores the original bytes.
36
+
37
+ `hook.original(...)` calls the trampoline, not the patched target entrypoint.
38
+
39
+ ### x64 patch choices
40
+
41
+ Hookie chooses among:
42
+
43
+ - a 14-byte absolute indirect jump,
44
+ - a 5-byte `rel32` jump when the destination is in range,
45
+ - a 5-byte jump to a nearby relay which then performs the absolute jump.
46
+
47
+ ### Relocation
48
+
49
+ Copied prologue instructions cannot always be copied byte-for-byte. Hookie
50
+ relocates:
51
+
52
+ - relative `call`, `jmp`, and conditional branches,
53
+ - branches whose target lies inside the stolen prologue,
54
+ - x64 RIP-relative memory operands,
55
+ - loop/jcxz-family control flow.
56
+
57
+ The trampoline ends with a jump back to the first original instruction after the
58
+ stolen region.
59
+
60
+ ---
61
+
62
+ ## 3. Memory model
63
+
64
+ Executable buffers are allocated writable, populated, then changed to executable
65
+ permissions. Hookie also supports near/range-constrained allocation where x64
66
+ RIP-relative relocation or a `rel32` relay requires it.
67
+
68
+ Hook installation/removal is not thread-safe: another thread executing the
69
+ prologue while its bytes are being replaced can observe a partial patch.
70
+
71
+ ---
72
+
73
+ ## 4. Build identity and directory layout
74
+
75
+ Every `create-dll` / `create-so` operation generates one random **6-byte id**.
76
+ It is rendered as **12 lowercase hex characters**:
77
+
78
+ ```text
79
+ 6 bytes -> a1b2c3d4e5f6
80
+ ```
81
+
82
+ That exact id is the single identity for the build.
83
+
84
+ Normal/path mode:
85
+
86
+ ```text
87
+ deps/
88
+ └── a1b2c3d4e5f6/
89
+ ├── Python runtime
90
+ ├── hookie/
91
+ ├── dependencies
92
+ └── hook.py
93
+
94
+ builds/
95
+ └── a1b2c3d4e5f6/
96
+ └── hookie_stub.dll # or .so
97
+ ```
98
+
99
+ Single-file mode extracts to the same relative name:
100
+
101
+ ```text
102
+ %TEMP%\a1b2c3d4e5f6\ # Windows
103
+ $TMPDIR/a1b2c3d4e5f6/ # Linux
104
+ ```
105
+
106
+ The entrypoint filename remains simply:
107
+
108
+ ```text
109
+ hook.py
110
+ ```
111
+
112
+ There is no separate content id and no `hookie_` prefix on the dependency/temp
113
+ directory. `deps/<id>`, `builds/<id>`, and temp extraction all expose the same
114
+ build id. The file itself stays `hook.py`; only its in-memory Python module name
115
+ uses the id (`hookie_<id>`).
116
+
117
+ ---
118
+
119
+ ## 5. Payload footer
120
+
121
+ The final DLL/SO carries a 40-byte footer:
122
+
123
+ ```c
124
+ struct HookieFooter {
125
+ char magic[17];
126
+ uint8_t mode;
127
+ uint8_t ident_len;
128
+ uint8_t reserved[5];
129
+ uint64_t raw_size;
130
+ uint64_t data_size;
131
+ };
132
+ ```
133
+
134
+ `magic` is:
135
+
136
+ ```text
137
+ HOOKIE_PAYLOAD_V1
138
+ ```
139
+
140
+ There are two modes.
141
+
142
+ ### Path mode
143
+
144
+ The data before the footer is the absolute path to `deps/<id>`.
145
+
146
+ ```text
147
+ [UTF-8 absolute deps path][footer]
148
+ ```
149
+
150
+ `ident_len` is zero because the id can be obtained from the basename of the
151
+ resolved payload directory.
152
+
153
+ ### Single-file mode
154
+
155
+ The data is:
156
+
157
+ ```text
158
+ [12-byte ASCII id][LZSS-compressed archive][footer]
159
+ ```
160
+
161
+ The native bootstrap extracts the archive to the system temporary directory
162
+ using exactly that id as the directory name.
163
+
164
+ ---
165
+
166
+ ## 6. Native bootstrap: high-level flow
167
+
168
+ Windows:
169
+
170
+ ```text
171
+ DllMain
172
+ -> create worker thread
173
+ -> resolve payload directory
174
+ -> locate versioned python3XY.dll
175
+ -> reuse matching initialized CPython if present
176
+ OR load bundled python3XY.dll
177
+ -> configure Python paths
178
+ -> execute hook.py as module hookie_<id>
179
+ ```
180
+
181
+ Linux:
182
+
183
+ ```text
184
+ constructor
185
+ -> create worker thread
186
+ -> resolve payload directory
187
+ -> read bundled libpython/runtime version
188
+ -> reuse matching process CPython when safe
189
+ OR load bundled libpython when no Python is present
190
+ -> configure Python paths
191
+ -> execute hook.py as module hookie_<id>
192
+ ```
193
+
194
+ The heavy bootstrap work is deliberately not performed directly under Windows
195
+ `DllMain` loader lock.
196
+
197
+ ---
198
+
199
+ ## 7. Single-file extraction
200
+
201
+ Packaging serializes each payload file as:
202
+
203
+ ```text
204
+ uint32 path_length
205
+ uint64 file_size
206
+ path bytes
207
+ file bytes
208
+ ```
209
+
210
+ A zero `path_length` terminates the archive.
211
+
212
+ The archive is compressed with a small LZSS codec and appended to the native
213
+ stub. At runtime the stub:
214
+
215
+ 1. reads its own final 40 bytes,
216
+ 2. validates the footer,
217
+ 3. reads the id + compressed blob,
218
+ 4. decompresses it,
219
+ 5. creates `<temp>/<id>/`,
220
+ 6. recreates the payload tree there.
221
+
222
+ The archive extractor rejects absolute/traversal paths.
223
+
224
+ ---
225
+
226
+ ## 8. Python entrypoint execution
227
+
228
+ The user writes:
229
+
230
+ ```text
231
+ hook.py
232
+ ```
233
+
234
+ Packaging keeps that filename unchanged. The bootstrap derives the id directly
235
+ from the payload directory basename and uses it only for the module name:
236
+
237
+ ```python
238
+ _hookie_id = os.path.basename(os.path.normpath(_hookie_dir))
239
+ _hookie_name = "hookie_" + _hookie_id
240
+ _hookie_file = os.path.join(_hookie_dir, "hook.py")
241
+ ```
242
+
243
+ It loads `hook.py` using `importlib.util.spec_from_file_location`, but registers
244
+ it in Python under the deterministic module name:
245
+
246
+ ```text
247
+ hookie_a1b2c3d4e5f6
248
+ ```
249
+
250
+ No path hash or second random identifier is involved.
251
+
252
+ The module is put in `sys.modules` before execution. This has two useful
253
+ properties:
254
+
255
+ - a second load of the same payload does not blindly execute the same hook setup
256
+ again;
257
+ - module globals keep `HookEngine`, `Hook`, and ctypes callback references alive.
258
+
259
+ The payload top-level is the entrypoint. There is no `install()` callback.
260
+
261
+ ---
262
+
263
+ ## 9. `sys.path` vs `os.add_dll_directory` on Windows
264
+
265
+ These solve different problems.
266
+
267
+ ### `sys.path`
268
+
269
+ ```python
270
+ sys.path.insert(0, payload_dir)
271
+ sys.path.insert(0, payload_dir / "Lib" / "site-packages")
272
+ ```
273
+
274
+ This tells **Python's importer** where to find Python modules and packages such
275
+ as `hookie` and `capstone`.
276
+
277
+ ### `os.add_dll_directory`
278
+
279
+ ```python
280
+ handle = os.add_dll_directory(payload_dir)
281
+ ```
282
+
283
+ This tells the **Windows native loader** to also search the payload directory
284
+ when a `.pyd` or another native component needs dependent DLLs.
285
+
286
+ Adding a directory to `sys.path` does not change Windows DLL dependency search.
287
+ Conversely, `add_dll_directory` does not make Python modules importable.
288
+
289
+ The returned handle owns that registration. If it is closed or garbage
290
+ collected, Windows removes the directory from the DLL search list. Hookie keeps
291
+ those handles alive in:
292
+
293
+ ```python
294
+ sys._hookie_dll_dirs
295
+ ```
296
+
297
+ for the lifetime of the embedded/reused interpreter.
298
+
299
+ ---
300
+
301
+ ## 10. Existing Python runtime policy
302
+
303
+ Hookie currently avoids subinterpreters.
304
+
305
+ ### Windows
306
+
307
+ If the payload was built for `python3XY.dll` and that same versioned DLL already
308
+ exists and is initialized in the target, Hookie reuses the target main
309
+ interpreter and acquires the GIL with `PyGILState_Ensure()`.
310
+
311
+ If a different Python minor is present but the requested one is not, Hookie may
312
+ load its own versioned bundled `python3XY.dll` because Windows symbol lookup is
313
+ performed through that explicit module handle.
314
+
315
+ ### Linux
316
+
317
+ If a matching CPython major/minor is already present, Hookie reuses it.
318
+
319
+ If a different `libpython` is already mapped, Hookie refuses to load a second
320
+ version. ELF symbol interposition makes two independent process-global CPython
321
+ runtimes substantially less predictable than the Windows versioned-DLL case.
322
+
323
+ ---
324
+
325
+ ## 11. Bundled runtime lifetime
326
+
327
+ When Hookie owns a newly initialized runtime, it does not finalize Python after
328
+ executing the payload. Native target threads may later enter ctypes callbacks
329
+ created by the hooks. Finalizing the interpreter would leave those native jump
330
+ targets pointing into a dead Python runtime.
331
+
332
+ The bootstrap releases the GIL and parks its worker for process lifetime.
333
+
334
+ ---
335
+
336
+ ## 12. Debug mode
337
+
338
+ Windows `--debug` compiles `HK_DEBUG` traces using `OutputDebugStringW`, visible
339
+ in a debugger or DebugView.
340
+
341
+ Linux `--debug` compiles `HK_DEBUG` traces to stderr.
342
+
343
+ Debug builds are compiled from source instead of silently reusing release cache
344
+ artifacts.
345
+
346
+ ---
347
+
348
+ ## 13. Tests
349
+
350
+ `examples/` contains user-facing hook payloads.
351
+
352
+ `tests/` contains regression/native tests. In particular,
353
+ `test_stub_runtime_policy` verifies the CPython bootstrap policy rather than
354
+ instruction relocation:
355
+
356
+ - same-version existing Python is reused;
357
+ - Linux refuses a mismatched already-loaded Python runtime;
358
+ - `hook.py` executes under module name `hookie_<id>`.
359
+
360
+ x86 native tests require a real 32-bit Python/process and 32-bit toolchain.
hookie-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ include LICENSE
2
+ include README.md
3
+ include ARCHITECTURE.md
4
+ include requirements.txt
5
+ recursive-include hookie/stubs *.c
6
+ recursive-include examples *.py *.md
7
+ recursive-include tests *.py *.md
8
+ global-exclude *.py[cod]
9
+ prune build
10
+ prune dist
11
+
hookie-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: hookie
3
+ Version: 0.1.0
4
+ Summary: Native x86/x64 inline hooking from pure Python on Windows and Linux
5
+ Author: Sems
6
+ License-Expression: MIT
7
+ Project-URL: Source, https://github.com/SemsYapar/hookie
8
+ Project-URL: Issues, https://github.com/SemsYapar/hookie/issues
9
+ Keywords: hooking,detour,trampoline,minhook,reverse-engineering,ctypes,x86,x64
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Topic :: Software Development :: Disassemblers
20
+ Classifier: Topic :: Security
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: capstone<6,>=5.0.8
25
+ Dynamic: license-file
26
+
27
+ # Hookie
28
+
29
+ Hookie has two useful parts that can be used together **or separately**:
30
+
31
+ 1. **Run Python code inside another process.** `create-dll` / `create-so` package a `hook.py` together with Python and the required dependencies so the native stub can execute that Python code inside the target process. You do not have to hook anything to use Hookie this way.
32
+ 2. **Hook native functions from Python.** `HookEngine` lets Python detours hook x86/x64 native functions while keeping the original function callable through a trampoline.
33
+
34
+ Put the two together and you can inject Python into a target process and install native hooks there using Python.
35
+
36
+ Windows and Linux are supported; x86 and x64 are supported.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install hookie
42
+ ```
43
+
44
+ ## Hook a function in the current process
45
+
46
+ ```python
47
+ from hookie import HookEngine
48
+
49
+ engine = HookEngine()
50
+ hook = engine.create_hook(target_addr, detour, argtypes, restype)
51
+ hook.enable()
52
+
53
+ # From the detour:
54
+ # hook.original(...)
55
+ ```
56
+
57
+ ## Run Python inside another process
58
+
59
+ Write normal top-level Python code in `hook.py`. It may install hooks, inspect the process, log information, or do any other authorized Python-side work.
60
+
61
+ Windows:
62
+
63
+ ```bash
64
+ hookie create-dll hook.py --arch x64
65
+ hookie inject-dll target.exe builds/<id>/hookie_stub.dll
66
+ ```
67
+
68
+ Linux:
69
+
70
+ ```bash
71
+ hookie create-so hook.py --arch x64
72
+ ```
73
+
74
+ Optional build modes:
75
+
76
+ ```bash
77
+ --single-file
78
+ --debug
79
+ ```
80
+
81
+ See `examples/` for examples. See `ARCHITECTURE.md` for the trampoline, payload, stub, CPython bootstrap, and runtime-reuse internals.
82
+
83
+ ## Current limitations
84
+
85
+ - Installing/removing inline hooks is not thread-safe; patch at a quiet point or stop the relevant threads yourself.
86
+ - In an existing Python process, Windows reuses the same CPython minor version and can use Hookie's bundled runtime when a different minor is present.
87
+ - On Linux, Hookie reuses the same CPython major/minor. If a different `libpython` is already mapped, Hookie refuses to load a second Python runtime.
88
+ - x86 native testing/building requires a real 32-bit Python/process and a 32-bit toolchain.
89
+
90
+ MIT licensed.
hookie-0.1.0/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Hookie
2
+
3
+ Hookie has two useful parts that can be used together **or separately**:
4
+
5
+ 1. **Run Python code inside another process.** `create-dll` / `create-so` package a `hook.py` together with Python and the required dependencies so the native stub can execute that Python code inside the target process. You do not have to hook anything to use Hookie this way.
6
+ 2. **Hook native functions from Python.** `HookEngine` lets Python detours hook x86/x64 native functions while keeping the original function callable through a trampoline.
7
+
8
+ Put the two together and you can inject Python into a target process and install native hooks there using Python.
9
+
10
+ Windows and Linux are supported; x86 and x64 are supported.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install hookie
16
+ ```
17
+
18
+ ## Hook a function in the current process
19
+
20
+ ```python
21
+ from hookie import HookEngine
22
+
23
+ engine = HookEngine()
24
+ hook = engine.create_hook(target_addr, detour, argtypes, restype)
25
+ hook.enable()
26
+
27
+ # From the detour:
28
+ # hook.original(...)
29
+ ```
30
+
31
+ ## Run Python inside another process
32
+
33
+ Write normal top-level Python code in `hook.py`. It may install hooks, inspect the process, log information, or do any other authorized Python-side work.
34
+
35
+ Windows:
36
+
37
+ ```bash
38
+ hookie create-dll hook.py --arch x64
39
+ hookie inject-dll target.exe builds/<id>/hookie_stub.dll
40
+ ```
41
+
42
+ Linux:
43
+
44
+ ```bash
45
+ hookie create-so hook.py --arch x64
46
+ ```
47
+
48
+ Optional build modes:
49
+
50
+ ```bash
51
+ --single-file
52
+ --debug
53
+ ```
54
+
55
+ See `examples/` for examples. See `ARCHITECTURE.md` for the trampoline, payload, stub, CPython bootstrap, and runtime-reuse internals.
56
+
57
+ ## Current limitations
58
+
59
+ - Installing/removing inline hooks is not thread-safe; patch at a quiet point or stop the relevant threads yourself.
60
+ - In an existing Python process, Windows reuses the same CPython minor version and can use Hookie's bundled runtime when a different minor is present.
61
+ - On Linux, Hookie reuses the same CPython major/minor. If a different `libpython` is already mapped, Hookie refuses to load a second Python runtime.
62
+ - x86 native testing/building requires a real 32-bit Python/process and a 32-bit toolchain.
63
+
64
+ MIT licensed.
@@ -0,0 +1,36 @@
1
+ # Examples
2
+
3
+ `examples/` contains user-facing payloads. The entrypoint is always `hook.py`,
4
+ and it stays named `hook.py` inside the built payload. The native stub loads it
5
+ under the in-memory module name `hookie_<build-id>`. Its module top-level runs
6
+ once and installs the hooks; there is no separate `install()` callback.
7
+
8
+ Keep the `HookEngine`, `Hook`, and Python detour objects alive at module scope for
9
+ as long as native code can enter the hook.
10
+
11
+ ## Windows
12
+
13
+ - `windows/messagebox_payload/hook.py` — rewrites `MessageBoxW` text/caption.
14
+ - `windows/beep_payload/hook.py` — rewrites `kernel32!Beep` arguments and shows
15
+ the x86 `WINFUNCTYPE` / x64 calling-convention pattern.
16
+
17
+ Build one with:
18
+
19
+ ```powershell
20
+ hookie create-dll examples\windows\messagebox_payload --arch x64
21
+ ```
22
+
23
+ ## Linux
24
+
25
+ - `linux/puts_payload/hook.py` — prefixes strings sent to `libc!puts`.
26
+ - `linux/getpid_payload/hook.py` — demonstrates return-value rewriting by
27
+ changing the observed result of `getpid()`.
28
+
29
+ Build one with:
30
+
31
+ ```bash
32
+ hookie create-so examples/linux/puts_payload --arch x64
33
+ ```
34
+
35
+ These examples deliberately modify process behavior. Use disposable test
36
+ programs while experimenting.
@@ -0,0 +1,27 @@
1
+ """Demonstrate return-value rewriting by intercepting libc getpid()."""
2
+ import ctypes
3
+ import hookie
4
+
5
+ _engine = hookie.HookEngine()
6
+ _hooks = []
7
+
8
+ libc = ctypes.CDLL(None)
9
+ libc.getpid.argtypes = []
10
+ libc.getpid.restype = ctypes.c_int
11
+ _target = ctypes.cast(libc.getpid, ctypes.c_void_p).value
12
+
13
+
14
+ def _detour():
15
+ # Deliberately obvious demo value; do not use this payload in software that
16
+ # relies on its real PID for synchronization or file naming.
17
+ return _hook.original() + 100000
18
+
19
+
20
+ _hook = _engine.create_hook(
21
+ _target,
22
+ _detour,
23
+ [],
24
+ ctypes.c_int,
25
+ )
26
+ _hook.enable()
27
+ _hooks.append(_hook)
@@ -0,0 +1,27 @@
1
+ """Prefix strings passed to libc puts()."""
2
+ import ctypes
3
+ import hookie
4
+
5
+ _engine = hookie.HookEngine()
6
+ _hooks = []
7
+
8
+ libc = ctypes.CDLL(None)
9
+ libc.puts.argtypes = [ctypes.c_char_p]
10
+ libc.puts.restype = ctypes.c_int
11
+ _target = ctypes.cast(libc.puts, ctypes.c_void_p).value
12
+
13
+
14
+ def _detour(text):
15
+ original = text or b"(null)"
16
+ rewritten = b"[hookie] " + original
17
+ return _hook.original(rewritten)
18
+
19
+
20
+ _hook = _engine.create_hook(
21
+ _target,
22
+ _detour,
23
+ libc.puts.argtypes,
24
+ libc.puts.restype,
25
+ )
26
+ _hook.enable()
27
+ _hooks.append(_hook)
@@ -0,0 +1,30 @@
1
+ """Intercept kernel32!Beep and rewrite its frequency/duration."""
2
+ import ctypes
3
+ from ctypes import wintypes
4
+ import hookie
5
+
6
+ _engine = hookie.HookEngine()
7
+ _hooks = []
8
+
9
+ kernel32 = ctypes.windll.kernel32
10
+ kernel32.Beep.argtypes = [wintypes.DWORD, wintypes.DWORD]
11
+ kernel32.Beep.restype = wintypes.BOOL
12
+ _target = ctypes.cast(kernel32.Beep, ctypes.c_void_p).value
13
+
14
+
15
+ def _detour(frequency, duration):
16
+ # Keep values in Beep's documented frequency range and shorten long beeps.
17
+ frequency = max(37, min(int(frequency) + 200, 32767))
18
+ duration = min(int(duration), 250)
19
+ return _hook.original(frequency, duration)
20
+
21
+
22
+ _hook = _engine.create_hook(
23
+ _target,
24
+ _detour,
25
+ kernel32.Beep.argtypes,
26
+ kernel32.Beep.restype,
27
+ prototype_factory=(ctypes.WINFUNCTYPE if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.CFUNCTYPE),
28
+ )
29
+ _hook.enable()
30
+ _hooks.append(_hook)
@@ -0,0 +1,32 @@
1
+ """Rewrite MessageBoxW text/caption inside the target process."""
2
+ import ctypes
3
+ from ctypes import wintypes
4
+ import hookie
5
+
6
+ _engine = hookie.HookEngine()
7
+ _hooks = []
8
+
9
+ user32 = ctypes.windll.user32
10
+ user32.MessageBoxW.argtypes = [
11
+ wintypes.HWND,
12
+ wintypes.LPCWSTR,
13
+ wintypes.LPCWSTR,
14
+ wintypes.UINT,
15
+ ]
16
+ user32.MessageBoxW.restype = ctypes.c_int
17
+ _target = ctypes.cast(user32.MessageBoxW, ctypes.c_void_p).value
18
+
19
+
20
+ def _detour(hwnd, text, caption, utype):
21
+ return _hook.original(hwnd, "hooked by hookie", "hookie", utype)
22
+
23
+
24
+ _hook = _engine.create_hook(
25
+ _target,
26
+ _detour,
27
+ user32.MessageBoxW.argtypes,
28
+ user32.MessageBoxW.restype,
29
+ prototype_factory=(ctypes.WINFUNCTYPE if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.CFUNCTYPE),
30
+ )
31
+ _hook.enable()
32
+ _hooks.append(_hook)