1
+ /* *
2
+ * =============================================================================
3
+ * DynamicHooks-x86_64
4
+ * Copyright (C) 2024 Benoist "Kenzzer" André. All rights reserved.
5
+ * Copyright (C) 2024 AlliedModders LLC. All rights reserved.
6
+ * =============================================================================
7
+ *
8
+ * This software is provided 'as-is', without any express or implied warranty.
9
+ * In no event will the authors be held liable for any damages arising from
10
+ * the use of this software.
11
+ *
12
+ * Permission is granted to anyone to use this software for any purpose,
13
+ * including commercial applications, and to alter it and redistribute it
14
+ * freely, subject to the following restrictions:
15
+ *
16
+ * 1. The origin of this software must not be misrepresented; you must not
17
+ * claim that you wrote the original software. If you use this software in a
18
+ * product, an acknowledgment in the product documentation would be
19
+ * appreciated but is not required.
20
+ *
21
+ * 2. Altered source versions must be plainly marked as such, and must not be
22
+ * misrepresented as being the original software.
23
+ *
24
+ * 3. This notice may not be removed or altered from any source distribution.
25
+ */
26
+
27
+ // ============================================================================
28
+ // >> INCLUDES
29
+ // ============================================================================
30
+ #include " x86_64MicrosoftDefault.h"
31
+ #include < smsdk_ext.h>
32
+
33
+ // ============================================================================
34
+ // >> CLASSES
35
+ // ============================================================================
36
+ x86_64MicrosoftDefault::x86_64MicrosoftDefault (std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
37
+ ICallingConvention(vecArgTypes, returnType, iAlignment),
38
+ m_stackArgs(0 )
39
+ {
40
+ const Register_t params_reg[] = { RCX, RDX, R8, R9 };
41
+ const Register_t params_floatreg[] = { XMM0, XMM1, XMM2, XMM3 };
42
+ // const char* regNames[] = { "RCX or XMM0", "RDX or XMM1", "R8 or XMM2", "R9 or XMM3"};
43
+ const std::uint8_t num_reg = sizeof (params_reg) / sizeof (Register_t);
44
+
45
+ bool used_reg[] = { false , false , false , false };
46
+
47
+ // Figure out if any register has been used
48
+ auto retreg = m_returnType.custom_register ;
49
+ used_reg[0 ] = (retreg == RCX || retreg == XMM0);
50
+ used_reg[1 ] = (retreg == RDX || retreg == XMM1);
51
+ used_reg[2 ] = (retreg == R8 || retreg == XMM2);
52
+ used_reg[3 ] = (retreg == R9 || retreg == XMM3);
53
+
54
+ for (const auto & arg : m_vecArgTypes) {
55
+ int reg_index = -1 ;
56
+ if (arg.custom_register == RCX || arg.custom_register == XMM0) {
57
+ reg_index = 0 ;
58
+ } else if (arg.custom_register == RDX || arg.custom_register == XMM1) {
59
+ reg_index = 1 ;
60
+ } else if (arg.custom_register == R8 || arg.custom_register == XMM2) {
61
+ reg_index = 2 ;
62
+ } else if (arg.custom_register == R9 || arg.custom_register == XMM3) {
63
+ reg_index = 3 ;
64
+ }
65
+
66
+ if (reg_index != -1 ) {
67
+ if (used_reg[reg_index]) {
68
+ puts (" Argument register is used twice, or shared with return" );
69
+ return ;
70
+ }
71
+ used_reg[reg_index] = true ;
72
+ }
73
+ }
74
+
75
+ // Special return type
76
+ if (m_returnType.custom_register == None && m_returnType.type == DATA_TYPE_OBJECT &&
77
+ // If size unknown, or doesn't fit on 1, 2, 4 or 8 bytes
78
+ // special place must have been allocated for it
79
+ (m_returnType.size == 0
80
+ || m_returnType.size == 3
81
+ || m_returnType.size == 5
82
+ || m_returnType.size == 6
83
+ || m_returnType.size == 7
84
+ || m_returnType.size > 8 )) {
85
+ for (std::uint8_t i = 0 ; i < num_reg && m_returnType.custom_register == None; i++) {
86
+ if (!used_reg[i]) {
87
+ m_returnType.custom_register = params_reg[i];
88
+ used_reg[i] = true ;
89
+ }
90
+ // Couldn't find a free register, this is a big problem
91
+ if (m_returnType.custom_register == None) {
92
+ puts (" Missing free register for return pointer" );
93
+ return ;
94
+ }
95
+ }
96
+ }
97
+
98
+ for (auto & arg : m_vecArgTypes) {
99
+ if (arg.custom_register == None) {
100
+ for (std::uint8_t i = 0 ; i < num_reg && arg.custom_register == None; i++) {
101
+ // Register is unused assign it
102
+ if (!used_reg[i]) {
103
+ arg.custom_register = (arg.type == DATA_TYPE_FLOAT || arg.type == DATA_TYPE_DOUBLE) ? params_floatreg[i] : params_reg[i];
104
+ used_reg[i] = true ;
105
+ }
106
+ }
107
+ // Couldn't find a free register, it's therefore a stack parameter
108
+ if (arg.custom_register == None) {
109
+ m_stackArgs++;
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ std::vector<Register_t> x86_64MicrosoftDefault::GetRegisters ()
116
+ {
117
+ std::vector<Register_t> registers;
118
+
119
+ registers.push_back (RSP);
120
+
121
+ if (m_returnType.custom_register != None)
122
+ {
123
+ registers.push_back (m_returnType.custom_register );
124
+ }
125
+ else if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
126
+ {
127
+ registers.push_back (XMM0);
128
+ }
129
+ else
130
+ {
131
+ registers.push_back (RAX);
132
+ }
133
+
134
+ for (size_t i = 0 ; i < m_vecArgTypes.size (); i++)
135
+ {
136
+ auto reg = m_vecArgTypes[i].custom_register ;
137
+ if (reg == None)
138
+ {
139
+ continue ;
140
+ }
141
+ registers.push_back (m_vecArgTypes[i].custom_register );
142
+ }
143
+
144
+ return registers;
145
+ }
146
+
147
+ int x86_64MicrosoftDefault::GetPopSize ()
148
+ {
149
+ // Clean-up is caller handled
150
+ return 0 ;
151
+ }
152
+
153
+ int x86_64MicrosoftDefault::GetArgStackSize ()
154
+ {
155
+ // Shadow space (32 bytes) + 8 bytes * amount of stack arguments
156
+ return 32 + 8 * m_stackArgs;
157
+ }
158
+
159
+ void ** x86_64MicrosoftDefault::GetStackArgumentPtr (CRegisters* registers)
160
+ {
161
+ // Skip shadow space + return address
162
+ return (void **)(registers->m_rsp ->GetValue <uintptr_t >() + 8 + 32 );
163
+ }
164
+
165
+ int x86_64MicrosoftDefault::GetArgRegisterSize ()
166
+ {
167
+ int argRegisterSize = 0 ;
168
+
169
+ for (size_t i = 0 ; i < m_vecArgTypes.size (); i++)
170
+ {
171
+ if (m_vecArgTypes[i].custom_register != None)
172
+ {
173
+ // It doesn't matter, it's always 8 bytes or less
174
+ argRegisterSize += 8 ;
175
+ }
176
+ }
177
+
178
+ return argRegisterSize;
179
+ }
180
+
181
+ void * x86_64MicrosoftDefault::GetArgumentPtr (unsigned int index, CRegisters* registers)
182
+ {
183
+ // g_pSM->LogMessage(myself, "Retrieving argument %d (max args %d) registers %p", index, m_vecArgTypes.size(), registers);
184
+ if (index >= m_vecArgTypes.size ())
185
+ {
186
+ // g_pSM->LogMessage(myself, "Not enough arguments");
187
+ return nullptr ;
188
+ }
189
+
190
+ // Check if this argument was passed in a register.
191
+ if (m_vecArgTypes[index ].custom_register != None)
192
+ {
193
+ CRegister* reg = registers->GetRegister (m_vecArgTypes[index ].custom_register );
194
+ if (!reg)
195
+ {
196
+ // g_pSM->LogMessage(myself, "Register does not exit");
197
+ return nullptr ;
198
+ }
199
+ // g_pSM->LogMessage(myself, "Register arg %d", m_vecArgTypes[index].custom_register);
200
+ return reg->m_pAddress ;
201
+ }
202
+
203
+ // Return address + shadow space
204
+ size_t offset = 8 + 32 ;
205
+ for (unsigned int i = 0 ; i < index ; i++)
206
+ {
207
+ if (m_vecArgTypes[i].custom_register == None)
208
+ {
209
+ // No matter what, the stack is allocated in slices of 8 bytes
210
+ offset += 8 ;
211
+ }
212
+ }
213
+ return (void *) (registers->m_rsp ->GetValue <uintptr_t >() + offset);
214
+ }
215
+
216
+ void x86_64MicrosoftDefault::ArgumentPtrChanged (unsigned int index, CRegisters* registers, void * argumentPtr)
217
+ {
218
+ }
219
+
220
+ void * x86_64MicrosoftDefault::GetReturnPtr (CRegisters* registers)
221
+ {
222
+ // Custom return value register
223
+ if (m_returnType.custom_register != None)
224
+ {
225
+ return registers->GetRegister (m_returnType.custom_register )->m_pAddress ;
226
+ }
227
+
228
+ if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
229
+ {
230
+ // Floating point register
231
+ return registers->m_xmm0 ->m_pAddress ;
232
+ }
233
+ return registers->m_rax ->m_pAddress ;
234
+ }
235
+
236
+ void x86_64MicrosoftDefault::ReturnPtrChanged (CRegisters* pRegisters, void * pReturnPtr)
237
+ {
238
+ }
239
+
240
+ void x86_64MicrosoftDefault::SaveReturnValue (CRegisters* registers)
241
+ {
242
+ // It doesn't matter what the return value is, it will always be fitting on 8 bytes (or less)
243
+ std::unique_ptr<uint8_t []> savedReturn = std::make_unique<uint8_t []>(8 );
244
+ memcpy (savedReturn.get (), GetReturnPtr (registers), 8 );
245
+ m_pSavedReturnBuffers.push_back (std::move (savedReturn));
246
+ }
247
+
248
+ void x86_64MicrosoftDefault::RestoreReturnValue (CRegisters* registers)
249
+ {
250
+ // Like stated in SaveReturnValue, it will always fit within 8 bytes
251
+ // the actual underlining type does not matter
252
+ uint8_t * savedReturn = m_pSavedReturnBuffers.back ().get ();
253
+ memcpy (GetReturnPtr (registers), savedReturn, 8 );
254
+ ReturnPtrChanged (registers, savedReturn);
255
+ m_pSavedReturnBuffers.pop_back ();
256
+ }
257
+
258
+ void x86_64MicrosoftDefault::SaveCallArguments (CRegisters* registers)
259
+ {
260
+ int size = GetArgStackSize () + GetArgRegisterSize ();
261
+ std::unique_ptr<uint8_t []> savedCallArguments = std::make_unique<uint8_t []>(size);
262
+ size_t offset = 0 ;
263
+ for (unsigned int i = 0 ; i < m_vecArgTypes.size (); i++) {
264
+ // Doesn't matter the type, it will always be within 8 bytes
265
+ memcpy ((void *)((uintptr_t )savedCallArguments.get () + offset), GetArgumentPtr (i, registers), 8 );
266
+ offset += 8 ;
267
+ }
268
+ m_pSavedCallArguments.push_back (std::move (savedCallArguments));
269
+ }
270
+
271
+ void x86_64MicrosoftDefault::RestoreCallArguments (CRegisters* registers)
272
+ {
273
+ uint8_t *savedCallArguments = m_pSavedCallArguments.back ().get ();
274
+ size_t offset = 0 ;
275
+ for (size_t i = 0 ; i < m_vecArgTypes.size (); i++) {
276
+ // Doesn't matter the type, it will always be within 8 bytes
277
+ memcpy (GetArgumentPtr ((unsigned int )i, registers), (void *)((uintptr_t )savedCallArguments + offset), 8 );
278
+ offset += 8 ;
279
+ }
280
+ m_pSavedCallArguments.pop_back ();
281
+ }
0 commit comments