|
26 | 26 | 'PreHook',
|
27 | 27 | 'set_hooks_disabled',
|
28 | 28 | 'get_hooks_disabled',
|
29 |
| - 'hooks_disabled' |
| 29 | + 'hooks_disabled', |
| 30 | + 'use_pre_registers', |
30 | 31 | )
|
31 | 32 |
|
32 | 33 |
|
@@ -86,6 +87,56 @@ class PostHook(_Hook):
|
86 | 87 | # =============================================================================
|
87 | 88 | # >> FUNCTIONS
|
88 | 89 | # =============================================================================
|
| 90 | +@contextmanager |
| 91 | +def use_pre_registers(stack_data, value=True): |
| 92 | + """Temporarily set ``StackData.use_pre_registers`` to the given value. |
| 93 | + When the context ends, the previous value is restored. |
| 94 | +
|
| 95 | + Some functions overwrite CPU registers during their execution with values |
| 96 | + from their internal calculations. In a post-hook, you have access to the |
| 97 | + modified CPU registers, but in some cases you might want to access the |
| 98 | + registers that were saved before the pre-hook was called. In that case you |
| 99 | + can use this context manager to get access to the previous state of the |
| 100 | + registers. On Windows this is often required when hooking THISCALL |
| 101 | + functions, because the this-pointer is saved in the CPU register ``ECX``, |
| 102 | + but gets overwritten during the execution of the hooked function. So, in a |
| 103 | + post-hook you won't have access to the this-pointer anymore. |
| 104 | +
|
| 105 | + Example (CS:S/Windows): |
| 106 | +
|
| 107 | + .. code:: python |
| 108 | +
|
| 109 | + from entities.hooks import EntityCondition |
| 110 | + from entities.hooks import EntityPostHook |
| 111 | + from entities.hooks import EntityPreHook |
| 112 | +
|
| 113 | + from memory.hooks import use_pre_registers |
| 114 | +
|
| 115 | + @EntityPreHook(EntityCondition.is_player, 'drop_weapon') |
| 116 | + def pre_on_drop_weapon(stack_data): |
| 117 | + print(f'PRE: this = {stack_data[0].address}') |
| 118 | +
|
| 119 | + @EntityPostHook(EntityCondition.is_player, 'drop_weapon') |
| 120 | + def post_on_drop_weapon(stack_data, ret_val): |
| 121 | + print(f'POST FALSE: this = {stack_data[0].address}') |
| 122 | + with use_pre_registers(stack_data): |
| 123 | + print(f'POST CORRECT: this = {stack_data[0].address}') |
| 124 | +
|
| 125 | + Output: |
| 126 | +
|
| 127 | + .. code:: |
| 128 | +
|
| 129 | + PRE: this = 546778280 |
| 130 | + POST FALSE: this = 16439007 |
| 131 | + POST CORRECT: this = 546778280 |
| 132 | + """ |
| 133 | + old = stack_data.use_pre_registers |
| 134 | + stack_data.use_pre_registers = value |
| 135 | + try: |
| 136 | + yield |
| 137 | + finally: |
| 138 | + stack_data.use_pre_registers = old |
| 139 | + |
89 | 140 | @contextmanager
|
90 | 141 | def hooks_disabled(disabled=True):
|
91 | 142 | """Temporarily disable or enable all hook callbacks. By default hooks are
|
|
0 commit comments