三国卡牌客户端基础资源仓库
hch
2025-09-11 9e1075c83ce5dace7adce242083788bdffdf5d0c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
using DotNetDetour;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
 
namespace MonoHook
{
    public unsafe abstract class CodePatcher
    {
        public bool isValid { get; protected set; }
 
        protected void*     _pTarget, _pReplace, _pProxy;
        protected int       _jmpCodeSize;
        protected byte[]    _targetHeaderBackup;
 
        public CodePatcher(IntPtr target, IntPtr replace, IntPtr proxy, int jmpCodeSize)
        {
            _pTarget        = target.ToPointer();
            _pReplace       = replace.ToPointer();
            _pProxy         = proxy.ToPointer();
            _jmpCodeSize    = jmpCodeSize;
        }
 
        public void ApplyPatch()
        {
            BackupHeader();
            EnableAddrModifiable();
            PatchTargetMethod();
            PatchProxyMethod();
            FlushICache();
        }
 
        public void RemovePatch()
        {
            if (_targetHeaderBackup == null)
                return;
 
            EnableAddrModifiable();
            RestoreHeader();
            FlushICache();
        }
 
        protected void BackupHeader()
        {
            if (_targetHeaderBackup != null)
                return;
 
            uint requireSize    = LDasm.SizeofMinNumByte(_pTarget, _jmpCodeSize);
            _targetHeaderBackup = new byte[requireSize];
 
            fixed (void* ptr = _targetHeaderBackup)
                HookUtils.MemCpy(ptr, _pTarget, _targetHeaderBackup.Length);
        }
 
        protected void RestoreHeader()
        {
            if (_targetHeaderBackup == null)
                return;
 
            HookUtils.MemCpy_Jit(_pTarget, _targetHeaderBackup);
        }
 
        protected void PatchTargetMethod()
        {
            byte[] buff = GenJmpCode(_pTarget, _pReplace);
            HookUtils.MemCpy_Jit(_pTarget, buff);
        }
        protected void PatchProxyMethod()
        {
            if (_pProxy == null)
                return;
 
            // copy target's code to proxy
            HookUtils.MemCpy_Jit(_pProxy, _targetHeaderBackup);
 
            // jmp to target's new position
            long jmpFrom    = (long)_pProxy + _targetHeaderBackup.Length;
            long jmpTo      = (long)_pTarget + _targetHeaderBackup.Length;
 
            byte[] buff = GenJmpCode((void*)jmpFrom, (void*)jmpTo);
            HookUtils.MemCpy_Jit((void*)jmpFrom, buff);
        }
 
        protected void FlushICache()
        {
            HookUtils.FlushICache(_pTarget, _targetHeaderBackup.Length);
            HookUtils.FlushICache(_pProxy, _targetHeaderBackup.Length * 2);
        }
        protected abstract byte[] GenJmpCode(void* jmpFrom, void* jmpTo);
 
#if ENABLE_HOOK_DEBUG
        protected string PrintAddrs()
        {
            if (IntPtr.Size == 4)
                return $"target:0x{(uint)_pTarget:x}, replace:0x{(uint)_pReplace:x}, proxy:0x{(uint)_pProxy:x}";
            else
                return $"target:0x{(ulong)_pTarget:x}, replace:0x{(ulong)_pReplace:x}, proxy:0x{(ulong)_pProxy:x}";
        }
#endif
 
        private void EnableAddrModifiable()
        {
            HookUtils.SetAddrFlagsToRWX(new IntPtr(_pTarget), _targetHeaderBackup.Length);
            HookUtils.SetAddrFlagsToRWX(new IntPtr(_pProxy), _targetHeaderBackup.Length + _jmpCodeSize);
        }
    }
 
    public unsafe class CodePatcher_x86 : CodePatcher
    {
        protected static readonly byte[] s_jmpCode = new byte[] // 5 bytes
        {
            0xE9, 0x00, 0x00, 0x00, 0x00,                     // jmp $val   ; $val = $dst - $src - 5 
        };
 
        public CodePatcher_x86(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length) { }
 
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
            int val = (int)jmpTo - (int)jmpFrom - 5;
 
            fixed(void * p = &ret[0])
            {
                byte* ptr = (byte*)p;
                *ptr = 0xE9;
                int* pOffset = (int*)(ptr + 1);
                *pOffset = val;
            }
            return ret;
        }
    }
 
    /// <summary>
    /// x64下2G 内的跳转
    /// </summary>
    public unsafe class CodePatcher_x64_near : CodePatcher_x86 // x64_near pathcer code is same to x86
    {
        public CodePatcher_x64_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy) { }
    }
 
    /// <summary>
    /// x64下距离超过2G的跳转
    /// </summary>
    public unsafe class CodePatcher_x64_far : CodePatcher
    {
        protected static readonly byte[] s_jmpCode = new byte[] // 12 bytes
        {
            // 由于 rax 会被函数作为返回值修改,并且不会被做为参数使用,因此修改是安全的
            0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,         // mov rax, <jmpTo>
            0x50,                                                               // push rax
            0xC3                                                                // ret
        };
 
        //protected static readonly byte[] s_jmpCode2 = new byte[] // 14 bytes
        //{
        //    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,       // <jmpTo>
        //    0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF                    // jmp [rip - 0xe]
        //};
 
        public CodePatcher_x64_far(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length) { }
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
 
            fixed (void* p = &ret[0])
            {
                byte* ptr = (byte*)p;
                *ptr++ = 0x48;
                *ptr++ = 0xB8;
                *(long*)ptr = (long)jmpTo;
                ptr += 8;
                *ptr++ = 0x50;
                *ptr++ = 0xC3;
            }
            return ret;
        }
    }
 
    public unsafe class CodePatcher_arm32_near : CodePatcher
    {
        private static readonly byte[] s_jmpCode = new byte[]    // 4 bytes
        {
            0x00, 0x00, 0x00, 0xEA,                         // B $val   ; $val = (($dst - $src) / 4 - 2) & 0x1FFFFFF
        };
 
        public CodePatcher_arm32_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
        {
            if (Math.Abs((long)target - (long)replace) >= ((1 << 25) - 1))
                throw new ArgumentException("address offset of target and replace must less than ((1 << 25) - 1)");
 
#if ENABLE_HOOK_DEBUG
            Debug.Log($"CodePatcher_arm32_near: {PrintAddrs()}");
#endif
        }
 
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
            int val = ((int)jmpTo - (int)jmpFrom) / 4 - 2;
 
            fixed (void* p = &ret[0])
            {
                byte* ptr = (byte*)p;
                *ptr++ = (byte)val;
                *ptr++ = (byte)(val >> 8);
                *ptr++ = (byte)(val >> 16);
                *ptr++ = 0xEA;
            }
            return ret;
        }
    }
 
    public unsafe class CodePatcher_arm32_far : CodePatcher
    {
        private static readonly byte[] s_jmpCode = new byte[]    // 8 bytes
        {
            0x04, 0xF0, 0x1F, 0xE5,                         // LDR PC, [PC, #-4]
            0x00, 0x00, 0x00, 0x00,                         // $val
        };
 
        public CodePatcher_arm32_far(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
        {
            if (Math.Abs((long)target - (long)replace) < ((1 << 25) - 1))
                throw new ArgumentException("address offset of target and replace must larger than ((1 << 25) - 1), please use InstructionModifier_arm32_near instead");
 
#if ENABLE_HOOK_DEBUG
            Debug.Log($"CodePatcher_arm32_far: {PrintAddrs()}");
#endif
        }
 
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
 
            fixed (void* p = &ret[0])
            {
                uint* ptr = (uint*)p;
                *ptr++ = 0xE51FF004;
                *ptr = (uint)jmpTo;
            }
            return ret;
        }
    }
 
    /// <summary>
    /// arm64 下 ±128MB 范围内的跳转
    /// </summary>
    public unsafe class CodePatcher_arm64_near : CodePatcher
    {
        private static readonly byte[] s_jmpCode = new byte[]    // 4 bytes
        {
            /*
             * from 0x14 to 0x17 is B opcode
             * offset bits is 26
             * https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/B--Branch-
             */
            0x00, 0x00, 0x00, 0x14,                         //  B $val   ; $val = (($dst - $src)/4) & 7FFFFFF
        };
 
        public CodePatcher_arm64_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
        {
            if (Math.Abs((long)target - (long)replace) >= ((1 << 26) - 1) * 4)
                throw new ArgumentException("address offset of target and replace must less than (1 << 26) - 1) * 4");
 
#if ENABLE_HOOK_DEBUG
            Debug.Log($"CodePatcher_arm64: {PrintAddrs()}");
#endif
        }
 
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
            int val = (int)((long)jmpTo - (long)jmpFrom) / 4;
 
            fixed (void* p = &ret[0])
            {
                byte* ptr = (byte*)p;
                *ptr++ = (byte)val;
                *ptr++ = (byte)(val >> 8);
                *ptr++ = (byte)(val >> 16);
 
                byte last = (byte)(val >> 24);
                last &= 0b11;
                last |= 0x14;
 
                *ptr = last;
            }
            return ret;
        }
    }
 
    /// <summary>
    /// arm64 远距离跳转
    /// </summary>
    public unsafe class CodePatcher_arm64_far : CodePatcher
    {
        private static readonly byte[] s_jmpCode = new byte[]    // 20 bytes(字节数过多,太危险了,不建议使用)
        {
            /*
             * ADR: https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/ADR--Form-PC-relative-address-
             * BR: https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/BR--Branch-to-Register-
             */
            0x6A, 0x00, 0x00, 0x10,                         // ADR X10, #C
            0x4A, 0x01, 0x40, 0xF9,                         // LDR X10, [X10,#0]
            0x40, 0x01, 0x1F, 0xD6,                         // BR X10
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // $dst
        };
 
        public CodePatcher_arm64_far(IntPtr target, IntPtr replace, IntPtr proxy, int jmpCodeSize) : base(target, replace, proxy, jmpCodeSize)
        {
        }
 
        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            throw new NotImplementedException();
        }
    }
}